From ad70a4894472b5b5b66c0d76b82cd753e2ebf354 Mon Sep 17 00:00:00 2001 From: vicanso Date: Sat, 18 Jun 2022 20:46:12 +0800 Subject: [PATCH] test: add test for funnel chart --- axis.go | 2 +- axis_test.go | 46 ++++++++++++++++- bar_chart.go | 22 +++++++- bar_chart_test.go | 120 +++++++++++++++++++++++++++++++++++++++++++ chart_option.go | 7 +-- funnel_chart.go | 10 ++++ funnel_chart_test.go | 79 ++++++++++++++++++++++++++++ 7 files changed, 276 insertions(+), 10 deletions(-) create mode 100644 bar_chart_test.go create mode 100644 funnel_chart_test.go diff --git a/axis.go b/axis.go index 00a3332..53b5362 100644 --- a/axis.go +++ b/axis.go @@ -194,7 +194,7 @@ func (a *axisPainter) Render() (Box, error) { switch opt.Position { case PositionTop: - labelPaddingTop = labelMargin + labelPaddingTop = 0 x1 = p.Width() y0 = labelMargin + int(opt.FontSize) ticksPaddingTop = int(opt.FontSize) diff --git a/axis_test.go b/axis_test.go index fe7f874..17fe8d6 100644 --- a/axis_test.go +++ b/axis_test.go @@ -48,10 +48,12 @@ func TestAxis(t *testing.T) { "Sat", "Sun", }, + SplitLineShow: true, + SplitLineColor: drawing.ColorBlack, }).Render() return p.Bytes() }, - result: "\\nMonTueWedThuFriSatSun", + result: "\\nMonTueWedThuFriSatSun", }, // 底部x轴文本居左 { @@ -113,6 +115,48 @@ func TestAxis(t *testing.T) { }, result: "\\nMonTueWedThuFriSatSun", }, + // 右侧 + { + render: func(p *Painter) ([]byte, error) { + _, _ = NewAxisPainter(p, AxisOption{ + Data: []string{ + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun", + }, + Position: PositionRight, + BoundaryGap: FalseFlag(), + SplitLineShow: true, + SplitLineColor: drawing.ColorBlack, + }).Render() + return p.Bytes() + }, + result: "\\nMonTueWedThuFriSatSun", + }, + // 顶部 + { + render: func(p *Painter) ([]byte, error) { + _, _ = NewAxisPainter(p, AxisOption{ + Data: []string{ + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun", + }, + Formatter: "{value} --", + Position: PositionTop, + }).Render() + return p.Bytes() + }, + result: "\\nMon --Tue --Wed --Thu --Fri --Sat --Sun --", + }, } for _, tt := range tests { diff --git a/bar_chart.go b/bar_chart.go index 2982829..0ac9f47 100644 --- a/bar_chart.go +++ b/bar_chart.go @@ -60,6 +60,13 @@ type BarChartOption struct { Legend LegendOption } +type barChartLabelRenderOption struct { + Text string + Style Style + X int + Y int +} + func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (Box, error) { p := b.p opt := b.opt @@ -95,6 +102,7 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B markPointPainter, markLinePainter, } + labelRenderOptions := make([]barChartLabelRenderOption, 0) for index := range seriesList { series := seriesList[index] yRange := result.axisRanges[series.AxisIndex] @@ -156,9 +164,15 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B if !series.Label.Color.IsZero() { labelStyle.FontColor = series.Label.Color } - seriesPainter.OverrideTextStyle(labelStyle) + textBox := seriesPainter.MeasureText(text) - seriesPainter.Text(text, x+(barWidth-textBox.Width())>>1, barMaxHeight-h-distance) + + labelRenderOptions = append(labelRenderOptions, barChartLabelRenderOption{ + Text: text, + Style: labelStyle, + X: x + (barWidth-textBox.Width())>>1, + Y: barMaxHeight - h - distance, + }) } markPointPainter.Add(markPointRenderOption{ @@ -176,6 +190,10 @@ func (b *barChart) render(result *defaultRenderResult, seriesList SeriesList) (B Range: yRange, }) } + for _, labelOpt := range labelRenderOptions { + seriesPainter.OverrideTextStyle(labelOpt.Style) + seriesPainter.Text(labelOpt.Text, labelOpt.X, labelOpt.Y) + } // 最大、最小的mark point err := doRender(rendererList...) if err != nil { diff --git a/bar_chart_test.go b/bar_chart_test.go new file mode 100644 index 0000000..138b3ca --- /dev/null +++ b/bar_chart_test.go @@ -0,0 +1,120 @@ +// MIT License + +// Copyright (c) 2022 Tree Xie + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package charts + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBarChart(t *testing.T) { + assert := assert.New(t) + + tests := []struct { + render func(*Painter) ([]byte, error) + result string + }{ + { + render: func(p *Painter) ([]byte, error) { + seriesList := NewSeriesListDataFromValues([][]float64{ + { + 2.0, + 4.9, + 7.0, + 23.2, + 25.6, + 76.7, + 135.6, + 162.2, + 32.6, + 20.0, + 6.4, + 3.3, + }, + { + 2.6, + 5.9, + 9.0, + 26.4, + 28.7, + 70.7, + 175.6, + 182.2, + 48.7, + 18.8, + 6.0, + 2.3, + }, + }) + for index := range seriesList { + seriesList[index].Label.Show = true + } + _, err := NewBarChart(p, BarChartOption{ + Padding: Box{ + Left: 10, + Top: 10, + Right: 10, + Bottom: 10, + }, + SeriesList: seriesList, + XAxis: NewXAxisOption([]string{ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + }), + YAxisOptions: NewYAxisOptions([]string{ + "Rainfall", + "Evaporation", + }), + }).Render() + if err != nil { + return nil, err + } + return p.Bytes() + }, + result: "\\n24020016012080400JanFebMarAprMayJunJulAugSepOctNovDec24.9723.225.676.7135.6162.232.6206.43.32.65.9926.428.770.7175.6182.248.718.862.3", + }, + } + + for _, tt := range tests { + p, err := NewPainter(PainterOptions{ + Type: ChartOutputSVG, + Width: 600, + Height: 400, + }, PainterThemeOption(defaultTheme)) + assert.Nil(err) + data, err := tt.render(p) + assert.Nil(err) + assert.Equal(tt.result, string(data)) + } +} diff --git a/chart_option.go b/chart_option.go index 0bc0a34..643c4e7 100644 --- a/chart_option.go +++ b/chart_option.go @@ -336,12 +336,7 @@ func RadarRender(values [][]float64, opts ...OptionFunc) (*Painter, error) { // FunnelRender funnel chart render func FunnelRender(values []float64, opts ...OptionFunc) (*Painter, error) { - seriesList := make(SeriesList, len(values)) - for index, value := range values { - seriesList[index] = NewSeriesFromValues([]float64{ - value, - }, ChartTypeFunnel) - } + seriesList := NewFunnelSeriesList(values) return Render(ChartOption{ SeriesList: seriesList, }, opts...) diff --git a/funnel_chart.go b/funnel_chart.go index 63b3504..7c04bfe 100644 --- a/funnel_chart.go +++ b/funnel_chart.go @@ -34,6 +34,16 @@ type funnelChart struct { opt *FunnelChartOption } +func NewFunnelSeriesList(values []float64) SeriesList { + seriesList := make(SeriesList, len(values)) + for index, value := range values { + seriesList[index] = NewSeriesFromValues([]float64{ + value, + }, ChartTypeFunnel) + } + return seriesList +} + func NewFunnelChart(p *Painter, opt FunnelChartOption) *funnelChart { if opt.Theme == nil { opt.Theme = defaultTheme diff --git a/funnel_chart_test.go b/funnel_chart_test.go new file mode 100644 index 0000000..d260bfb --- /dev/null +++ b/funnel_chart_test.go @@ -0,0 +1,79 @@ +// MIT License + +// Copyright (c) 2022 Tree Xie + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package charts + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFunnelChart(t *testing.T) { + assert := assert.New(t) + + tests := []struct { + render func(*Painter) ([]byte, error) + result string + }{ + { + render: func(p *Painter) ([]byte, error) { + _, err := NewFunnelChart(p, FunnelChartOption{ + SeriesList: NewFunnelSeriesList([]float64{ + 100, + 80, + 60, + 40, + 20, + }), + Legend: NewLegendOption([]string{ + "Show", + "Click", + "Visit", + "Inquiry", + "Order", + }), + Title: TitleOption{ + Text: "Funnel", + }, + }).Render() + if err != nil { + return nil, err + } + return p.Bytes() + }, + result: "\\nShowClickVisitInquiryOrderFunnel(100%)(80%)(60%)(40%)(20%)", + }, + } + + for _, tt := range tests { + p, err := NewPainter(PainterOptions{ + Type: ChartOutputSVG, + Width: 600, + Height: 400, + }, PainterThemeOption(defaultTheme)) + assert.Nil(err) + data, err := tt.render(p) + assert.Nil(err) + assert.Equal(tt.result, string(data)) + } +}