From 95d0bcca4b2efc1ace95a80fcd275b79475fd07d Mon Sep 17 00:00:00 2001 From: Will Charczuk Date: Fri, 5 Aug 2016 20:08:24 -0700 Subject: [PATCH] tests --- bar_chart.go | 13 ++-- bar_chart_test.go | 144 +++++++++++++++++++++++++++++++++++++ defaults.go | 5 ++ examples/bar_chart/main.go | 14 ++-- images/bar_chart.png | Bin 0 -> 16830 bytes 5 files changed, 161 insertions(+), 15 deletions(-) create mode 100644 bar_chart_test.go create mode 100644 images/bar_chart.png diff --git a/bar_chart.go b/bar_chart.go index 9dc59a3..86bb61f 100644 --- a/bar_chart.go +++ b/bar_chart.go @@ -35,11 +35,8 @@ type BarChart struct { } // GetDPI returns the dpi for the chart. -func (bc BarChart) GetDPI(defaults ...float64) float64 { +func (bc BarChart) GetDPI() float64 { if bc.DPI == 0 { - if len(defaults) > 0 { - return defaults[0] - } return DefaultDPI } return bc.DPI @@ -64,7 +61,7 @@ func (bc BarChart) GetWidth() int { // GetHeight returns the chart height or the default value. func (bc BarChart) GetHeight() int { if bc.Height == 0 { - return DefaultChartWidth + return DefaultChartHeight } return bc.Height } @@ -72,7 +69,7 @@ func (bc BarChart) GetHeight() int { // GetBarSpacing returns the spacing between bars. func (bc BarChart) GetBarSpacing() int { if bc.BarSpacing == 0 { - return 100 + return DefaultBarSpacing } return bc.BarSpacing } @@ -80,7 +77,7 @@ func (bc BarChart) GetBarSpacing() int { // GetBarWidth returns the default bar width. func (bc BarChart) GetBarWidth() int { if bc.BarWidth == 0 { - return 40 + return DefaultBarWidth } return bc.BarWidth } @@ -103,7 +100,7 @@ func (bc BarChart) Render(rp RendererProvider, w io.Writer) error { } bc.defaultFont = defaultFont } - r.SetDPI(bc.GetDPI(DefaultDPI)) + r.SetDPI(bc.GetDPI()) bc.drawBackground(r) diff --git a/bar_chart_test.go b/bar_chart_test.go new file mode 100644 index 0000000..40e4c98 --- /dev/null +++ b/bar_chart_test.go @@ -0,0 +1,144 @@ +package chart + +import ( + "bytes" + "math" + "testing" + + assert "github.com/blendlabs/go-assert" +) + +func TestBarChartProps(t *testing.T) { + assert := assert.New(t) + + bc := BarChart{} + + assert.Equal(DefaultDPI, bc.GetDPI()) + bc.DPI = 100 + assert.Equal(100, bc.GetDPI()) + + assert.Nil(bc.GetFont()) + f, err := GetDefaultFont() + assert.Nil(err) + bc.Font = f + assert.NotNil(bc.GetFont()) + + assert.Equal(DefaultChartWidth, bc.GetWidth()) + bc.Width = DefaultChartWidth - 1 + assert.Equal(DefaultChartWidth-1, bc.GetWidth()) + + assert.Equal(DefaultChartHeight, bc.GetHeight()) + bc.Height = DefaultChartHeight - 1 + assert.Equal(DefaultChartHeight-1, bc.GetHeight()) + + assert.Equal(DefaultBarSpacing, bc.GetBarSpacing()) + bc.BarSpacing = 150 + assert.Equal(150, bc.GetBarSpacing()) + + assert.Equal(DefaultBarWidth, bc.GetBarWidth()) + bc.BarWidth = 75 + assert.Equal(75, bc.GetBarWidth()) +} + +func TestBarChartRenderNoBars(t *testing.T) { + assert := assert.New(t) + + bc := BarChart{} + err := bc.Render(PNG, bytes.NewBuffer([]byte{})) + assert.NotNil(err) +} + +func TestBarChartGetRanges(t *testing.T) { + assert := assert.New(t) + + bc := BarChart{} + + yr := bc.getRanges() + assert.NotNil(yr) + assert.False(yr.IsZero()) + + assert.Equal(-math.MaxFloat64, yr.GetMax()) + assert.Equal(math.MaxFloat64, yr.GetMin()) +} + +func TestBarChartGetRangesBarsMinMax(t *testing.T) { + assert := assert.New(t) + + bc := BarChart{ + Bars: []Value{ + {Value: 1.0}, + {Value: 10.0}, + }, + } + + yr := bc.getRanges() + assert.NotNil(yr) + assert.False(yr.IsZero()) + + assert.Equal(10, yr.GetMax()) + assert.Equal(1, yr.GetMin()) +} + +func TestBarChartGetRangesMinMax(t *testing.T) { + assert := assert.New(t) + + bc := BarChart{ + YAxis: YAxis{ + Range: &ContinuousRange{ + Min: 5.0, + Max: 15.0, + }, + Ticks: []Tick{ + {Value: 7.0, Label: "Foo"}, + {Value: 11.0, Label: "Foo2"}, + }, + }, + Bars: []Value{ + {Value: 1.0}, + {Value: 10.0}, + }, + } + + yr := bc.getRanges() + assert.NotNil(yr) + assert.False(yr.IsZero()) + + assert.Equal(15, yr.GetMax()) + assert.Equal(5, yr.GetMin()) +} + +func TestBarChartGetRangesTicksMinMax(t *testing.T) { + assert := assert.New(t) + + bc := BarChart{ + YAxis: YAxis{ + Ticks: []Tick{ + {Value: 7.0, Label: "Foo"}, + {Value: 11.0, Label: "Foo2"}, + }, + }, + Bars: []Value{ + {Value: 1.0}, + {Value: 10.0}, + }, + } + + yr := bc.getRanges() + assert.NotNil(yr) + assert.False(yr.IsZero()) + + assert.Equal(11, yr.GetMax()) + assert.Equal(7, yr.GetMin()) +} + +func TestBarChartHasAxes(t *testing.T) { + assert := assert.New(t) + + bc := BarChart{} + assert.False(bc.hasAxes()) + bc.YAxis = YAxis{ + Style: StyleShow(), + } + + assert.True(bc.hasAxes()) +} diff --git a/defaults.go b/defaults.go index 4c9fd1f..482d489 100644 --- a/defaults.go +++ b/defaults.go @@ -66,6 +66,11 @@ const ( DefaultFloatFormat = "%.2f" // DefaultPercentValueFormat is the default percent format. DefaultPercentValueFormat = "%0.2f%%" + + // DefaultBarSpacing is the default pixel spacing between bars. + DefaultBarSpacing = 100 + // DefaultBarWidth is the default pixel width of bars in a bar chart. + DefaultBarWidth = 50 ) var ( diff --git a/examples/bar_chart/main.go b/examples/bar_chart/main.go index 613deac..54619bb 100644 --- a/examples/bar_chart/main.go +++ b/examples/bar_chart/main.go @@ -14,20 +14,20 @@ func drawChart(res http.ResponseWriter, req *http.Request) { Height: 512, BarWidth: 60, XAxis: chart.Style{ - Show: true, + Show: false, }, YAxis: chart.YAxis{ Style: chart.Style{ - Show: true, + Show: false, }, }, Bars: []chart.Value{ - {Value: 5, Label: "Blue"}, - {Value: 5, Label: "Green"}, - {Value: 4, Label: "Gray"}, - {Value: 3, Label: "Orange"}, + {Value: 5.25, Label: "Blue"}, + {Value: 4.88, Label: "Green"}, + {Value: 4.74, Label: "Gray"}, + {Value: 3.22, Label: "Orange"}, {Value: 3, Label: "Test"}, - {Value: 2, Label: "??"}, + {Value: 2.27, Label: "??"}, {Value: 1, Label: "!!"}, }, } diff --git a/images/bar_chart.png b/images/bar_chart.png new file mode 100644 index 0000000000000000000000000000000000000000..21c8fddf2e6f3b5144aeceb79180591536d4015d GIT binary patch literal 16830 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 literal 0 HcmV?d00001