diff --git a/pkg/tsdb/testdata/scenarios.go b/pkg/tsdb/testdata/scenarios.go index 421a907b5e9..4780dd6e662 100644 --- a/pkg/tsdb/testdata/scenarios.go +++ b/pkg/tsdb/testdata/scenarios.go @@ -2,6 +2,7 @@ package testdata import ( "encoding/json" + "math" "math/rand" "strconv" "strings" @@ -100,6 +101,15 @@ func init() { }, }) + registerScenario(&Scenario{ + Id: "random_walk_table", + Name: "Random Walk Table", + + Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult { + return getRandomWalkTable(query, context) + }, + }) + registerScenario(&Scenario{ Id: "slow_query", Name: "Slow Query", @@ -251,7 +261,7 @@ func getRandomWalk(query *tsdb.Query, tsdbQuery *tsdb.TsdbQuery) *tsdb.QueryResu series := newSeriesForQuery(query) points := make(tsdb.TimeSeriesPoints, 0) - walker := rand.Float64() * 100 + walker := query.Model.Get("startValue").MustFloat64(rand.Float64() * 100) for i := int64(0); i < 10000 && timeWalkerMs < to; i++ { points = append(points, tsdb.NewTimePoint(null.FloatFrom(walker), float64(timeWalkerMs))) @@ -267,6 +277,64 @@ func getRandomWalk(query *tsdb.Query, tsdbQuery *tsdb.TsdbQuery) *tsdb.QueryResu return queryRes } +func getRandomWalkTable(query *tsdb.Query, tsdbQuery *tsdb.TsdbQuery) *tsdb.QueryResult { + timeWalkerMs := tsdbQuery.TimeRange.GetFromAsMsEpoch() + to := tsdbQuery.TimeRange.GetToAsMsEpoch() + + table := tsdb.Table{ + Columns: []tsdb.TableColumn{ + {Text: "Time"}, + {Text: "Value"}, + {Text: "Min"}, + {Text: "Max"}, + {Text: "Info"}, + }, + Rows: []tsdb.RowValues{}, + } + + withNil := query.Model.Get("withNil").MustBool(false) + walker := query.Model.Get("startValue").MustFloat64(rand.Float64() * 100) + spread := 2.5 + var info strings.Builder + + for i := int64(0); i < query.MaxDataPoints && timeWalkerMs < to; i++ { + delta := rand.Float64() - 0.5 + walker += delta + + info.Reset() + if delta > 0 { + info.WriteString("up") + } else { + info.WriteString("down") + } + if math.Abs(delta) > .4 { + info.WriteString(" fast") + } + row := tsdb.RowValues{ + float64(timeWalkerMs), + walker, + walker - ((rand.Float64() * spread) + 0.01), // Min + walker + ((rand.Float64() * spread) + 0.01), // Max + info.String(), + } + + // Add some random null values + if withNil && rand.Float64() > 0.8 { + for i := 1; i < 4; i++ { + if rand.Float64() > .2 { + row[i] = nil + } + } + } + + table.Rows = append(table.Rows, row) + timeWalkerMs += query.IntervalMs + } + queryRes := tsdb.NewQueryResult() + queryRes.Tables = append(queryRes.Tables, &table) + return queryRes +} + func registerScenario(scenario *Scenario) { ScenarioRegistry[scenario.Id] = scenario } diff --git a/pkg/tsdb/testdata/scenarios_test.go b/pkg/tsdb/testdata/scenarios_test.go new file mode 100644 index 00000000000..307f5e4affe --- /dev/null +++ b/pkg/tsdb/testdata/scenarios_test.go @@ -0,0 +1,94 @@ +package testdata + +import ( + "testing" + "time" + + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/tsdb" + . "github.com/smartystreets/goconvey/convey" +) + +func TestTestdataScenarios(t *testing.T) { + Convey("random walk ", t, func() { + scenario, _ := ScenarioRegistry["random_walk"] + + Convey("Should start at the requested value", func() { + req := &tsdb.TsdbQuery{ + TimeRange: tsdb.NewFakeTimeRange("5m", "now", time.Now()), + Queries: []*tsdb.Query{ + {RefId: "A", IntervalMs: 100, MaxDataPoints: 100, Model: simplejson.New()}, + }, + } + query := req.Queries[0] + query.Model.Set("startValue", 1.234) + + result := scenario.Handler(req.Queries[0], req) + points := result.Series[0].Points + + So(result.Series, ShouldNotBeNil) + So(points[0][0].Float64, ShouldEqual, 1.234) + }) + }) + + Convey("random walk table", t, func() { + scenario, _ := ScenarioRegistry["random_walk_table"] + + Convey("Should return a table that looks like value/min/max", func() { + req := &tsdb.TsdbQuery{ + TimeRange: tsdb.NewFakeTimeRange("5m", "now", time.Now()), + Queries: []*tsdb.Query{ + {RefId: "A", IntervalMs: 100, MaxDataPoints: 100, Model: simplejson.New()}, + }, + } + + result := scenario.Handler(req.Queries[0], req) + table := result.Tables[0] + + So(len(table.Rows), ShouldBeGreaterThan, 50) + for _, row := range table.Rows { + value := row[1] + min := row[2] + max := row[3] + + So(min, ShouldBeLessThan, value) + So(max, ShouldBeGreaterThan, value) + } + }) + + Convey("Should return a table with some nil values", func() { + req := &tsdb.TsdbQuery{ + TimeRange: tsdb.NewFakeTimeRange("5m", "now", time.Now()), + Queries: []*tsdb.Query{ + {RefId: "A", IntervalMs: 100, MaxDataPoints: 100, Model: simplejson.New()}, + }, + } + query := req.Queries[0] + query.Model.Set("withNil", true) + + result := scenario.Handler(req.Queries[0], req) + table := result.Tables[0] + + nil1 := false + nil2 := false + nil3 := false + + So(len(table.Rows), ShouldBeGreaterThan, 50) + for _, row := range table.Rows { + if row[1] == nil { + nil1 = true + } + if row[2] == nil { + nil2 = true + } + if row[3] == nil { + nil3 = true + } + } + + So(nil1, ShouldBeTrue) + So(nil2, ShouldBeTrue) + So(nil3, ShouldBeTrue) + }) + }) +} diff --git a/public/app/features/explore/LogLabelStats.tsx b/public/app/features/explore/LogLabelStats.tsx index d689b6e70b2..466cc050e43 100644 --- a/public/app/features/explore/LogLabelStats.tsx +++ b/public/app/features/explore/LogLabelStats.tsx @@ -11,7 +11,9 @@ function LogLabelStatsRow(logLabelStatsModel: LogLabelStatsModel) { return (