From bd87b46b1555929ba84acfc9dba5b9a37dc03e71 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Fri, 11 Nov 2022 09:27:35 +0000 Subject: [PATCH] Alerting: Improve test coverage for ConditionsCmd (#58603) --- pkg/expr/classic/classic_test.go | 533 +++++++++++++++++++---------- pkg/expr/classic/evaluator_test.go | 28 +- pkg/expr/classic/reduce_test.go | 279 +++++++-------- pkg/expr/mathexp/types.go | 4 - 4 files changed, 514 insertions(+), 330 deletions(-) diff --git a/pkg/expr/classic/classic_test.go b/pkg/expr/classic/classic_test.go index 19ff3a3d589..e5d85f9dd78 100644 --- a/pkg/expr/classic/classic_test.go +++ b/pkg/expr/classic/classic_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/stretchr/testify/require" ptr "github.com/xorcare/pointer" + "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/expr/mathexp" ) @@ -20,181 +20,61 @@ func TestConditionsCmd(t *testing.T) { vars mathexp.Vars expected func() mathexp.Results }{{ - name: "single query and single condition", + // This test asserts that a single query with condition returns 0 and no matches as the condition + // is not met + name: "single query with condition when condition is not met", vars: mathexp.Vars{ "A": mathexp.Results{ Values: []mathexp.Value{ - valBasedSeries(ptr.Float64(30), ptr.Float64(40)), + newSeries(ptr.Float64(1), ptr.Float64(5)), }, }, }, cmd: &ConditionsCmd{ Conditions: []condition{ - { - InputRefID: "A", - Reducer: reducer("avg"), - Operator: "and", - Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 34}, - }, - }}, - expected: func() mathexp.Results { - v := valBasedNumber(ptr.Float64(1)) - v.SetMeta([]EvalMatch{{Value: ptr.Float64(35)}}) - return mathexp.NewResults(v) - }, - }, { - name: "single query and single condition - empty series", - vars: mathexp.Vars{ - "A": mathexp.Results{ - Values: []mathexp.Value{ - valBasedSeries(), - }, - }, - }, - cmd: &ConditionsCmd{ - Conditions: []condition{ - { - InputRefID: "A", - Reducer: reducer("avg"), - Operator: "and", - Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 34}, - }, - }}, - expected: func() mathexp.Results { - v := valBasedNumber(nil) - v.SetMeta([]EvalMatch{{Metric: "NoData"}}) - return mathexp.NewResults(v) - }, - }, { - name: "single query and single condition - empty series and not empty series", - vars: mathexp.Vars{ - "A": mathexp.Results{ - Values: []mathexp.Value{ - valBasedSeries(), - valBasedSeries(ptr.Float64(3)), - }, - }, - }, - cmd: &ConditionsCmd{ - Conditions: []condition{ - { - InputRefID: "A", - Reducer: reducer("avg"), - Operator: "and", - Evaluator: &thresholdEvaluator{Type: "gt", Threshold: .5}, - }, - }}, - expected: func() mathexp.Results { - v := valBasedNumber(ptr.Float64(1)) - v.SetMeta([]EvalMatch{{Value: ptr.Float64(3)}}) - return mathexp.NewResults(v) - }, - }, { - name: "single query and two conditions", - vars: mathexp.Vars{ - "A": mathexp.Results{ - Values: []mathexp.Value{ - valBasedSeries(ptr.Float64(30), ptr.Float64(40)), - }, - }, - }, - cmd: &ConditionsCmd{ - Conditions: []condition{ - { - InputRefID: "A", - Reducer: reducer("max"), - Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 34}, - }, { InputRefID: "A", Reducer: reducer("min"), - Operator: "or", - Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 12}, - }, - }}, - expected: func() mathexp.Results { - v := valBasedNumber(ptr.Float64(1)) - v.SetMeta([]EvalMatch{{Value: ptr.Float64(40)}, {Value: ptr.Float64(30)}}) - return mathexp.NewResults(v) - }, - }, { - name: "single query and single condition - multiple series (one true, one not == true)", - vars: mathexp.Vars{ - "A": mathexp.Results{ - Values: []mathexp.Value{ - valBasedSeriesWithLabels(data.Labels{"h": "1"}, ptr.Float64(30), ptr.Float64(40)), - valBasedSeries(ptr.Float64(0), ptr.Float64(10)), - }, - }, - }, - cmd: &ConditionsCmd{ - Conditions: []condition{ - { - InputRefID: "A", - Reducer: reducer("avg"), Operator: "and", - Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 34}, + Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 2}, }, }}, expected: func() mathexp.Results { - v := valBasedNumber(ptr.Float64(1)) - v.SetMeta([]EvalMatch{{Value: ptr.Float64(35), Labels: data.Labels{"h": "1"}}}) - return mathexp.NewResults(v) - }, - }, { - name: "single query and single condition - multiple series (one not true, one true == true)", - vars: mathexp.Vars{ - "A": mathexp.Results{ - Values: []mathexp.Value{ - valBasedSeries(ptr.Float64(0), ptr.Float64(10)), - valBasedSeries(ptr.Float64(30), ptr.Float64(40)), - }, - }, - }, - cmd: &ConditionsCmd{ - Conditions: []condition{ - { - InputRefID: "A", - Reducer: reducer("avg"), - Operator: "and", - Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 34}, - }, - }}, - expected: func() mathexp.Results { - v := valBasedNumber(ptr.Float64(1)) - v.SetMeta([]EvalMatch{{Value: ptr.Float64(35)}}) - return mathexp.NewResults(v) - }, - }, { - name: "single query and single condition - multiple series (2 not true == false)", - vars: mathexp.Vars{ - "A": mathexp.Results{ - Values: []mathexp.Value{ - valBasedSeries(ptr.Float64(0), ptr.Float64(10)), - valBasedSeries(ptr.Float64(20), ptr.Float64(30)), - }, - }, - }, - cmd: &ConditionsCmd{ - Conditions: []condition{ - { - InputRefID: "A", - Reducer: reducer("avg"), - Operator: "and", - Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 34}, - }, - }}, - expected: func() mathexp.Results { - v := valBasedNumber(ptr.Float64(0)) + v := newNumber(ptr.Float64(0)) v.SetMeta([]EvalMatch{}) - return mathexp.Results{Values: mathexp.Values{v}} + return newResults(v) }, }, { - name: "single query and single ranged condition", + // This test asserts that a single query with condition returns 1 and the average in the meta as + // the condition is met + name: "single query with condition when condition is met", vars: mathexp.Vars{ "A": mathexp.Results{ Values: []mathexp.Value{ - valBasedSeries(ptr.Float64(30), ptr.Float64(40)), + newSeries(ptr.Float64(1), ptr.Float64(5)), + }, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("avg"), + Operator: "and", + Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 2}, + }, + }}, + expected: func() mathexp.Results { + v := newNumber(ptr.Float64(1)) + v.SetMeta([]EvalMatch{{Value: ptr.Float64(3)}}) + return newResults(v) + }, + }, { + name: "single query with ranged condition when condition is not met", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{ + newSeries(ptr.Float64(1), ptr.Float64(5)), }, }, }, @@ -204,17 +84,41 @@ func TestConditionsCmd(t *testing.T) { InputRefID: "A", Reducer: reducer("diff"), Operator: "and", - Evaluator: &rangedEvaluator{Type: "within_range", Lower: 2, Upper: 3}, + Evaluator: &rangedEvaluator{Type: "within_range", Lower: 2, Upper: 4}, }, }, }, expected: func() mathexp.Results { - v := valBasedNumber(ptr.Float64(0)) + v := newNumber(ptr.Float64(0)) v.SetMeta([]EvalMatch{}) - return mathexp.NewResults(v) + return newResults(v) }, }, { - name: "single query with no data", + name: "single query with ranged condition when condition is met", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{ + newSeries(ptr.Float64(1), ptr.Float64(5)), + }, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("diff"), + Operator: "and", + Evaluator: &rangedEvaluator{Type: "within_range", Lower: 0, Upper: 10}, + }, + }, + }, + expected: func() mathexp.Results { + v := newNumber(ptr.Float64(1)) + v.SetMeta([]EvalMatch{{Value: ptr.Float64(4)}}) + return newResults(v) + }, + }, { + name: "single no data query with condition is No Data", vars: mathexp.Vars{ "A": mathexp.Results{ Values: []mathexp.Value{mathexp.NoData{}.New()}, @@ -224,19 +128,19 @@ func TestConditionsCmd(t *testing.T) { Conditions: []condition{ { InputRefID: "A", - Reducer: reducer("avg"), + Reducer: reducer("min"), Operator: "and", Evaluator: &thresholdEvaluator{"gt", 1}, }, }, }, expected: func() mathexp.Results { - v := valBasedNumber(nil) + v := newNumber(nil) v.SetMeta([]EvalMatch{{Metric: "NoData"}}) - return mathexp.NewResults(v) + return newResults(v) }, }, { - name: "single query with no values", + name: "single no values query with condition is No Data", vars: mathexp.Vars{ "A": mathexp.Results{ Values: []mathexp.Value{}, @@ -246,25 +150,307 @@ func TestConditionsCmd(t *testing.T) { Conditions: []condition{ { InputRefID: "A", - Reducer: reducer("avg"), + Reducer: reducer("min"), Operator: "and", Evaluator: &thresholdEvaluator{"gt", 1}, }, }, }, expected: func() mathexp.Results { - v := valBasedNumber(nil) + v := newNumber(nil) v.SetMeta([]EvalMatch{{Metric: "NoData"}}) - return mathexp.NewResults(v) + return newResults(v) }, }, { - name: "should accept numbers", + name: "single series no points query with condition returns No Data", vars: mathexp.Vars{ "A": mathexp.Results{ Values: []mathexp.Value{ - valBasedNumber(ptr.Float64(5)), - valBasedNumber(ptr.Float64(10)), - valBasedNumber(ptr.Float64(15)), + newSeries(nil), + }, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("min"), + Operator: "and", + Evaluator: &thresholdEvaluator{"gt", 1}, + }, + }, + }, + expected: func() mathexp.Results { + v := newNumber(nil) + v.SetMeta([]EvalMatch{{Metric: "NoData"}}) + return newResults(v) + }, + }, { + name: "single no data query with condition is met has no value", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{mathexp.NoData{}.New()}, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("min"), + Operator: "and", + Evaluator: &noValueEvaluator{}, + }, + }, + }, + expected: func() mathexp.Results { + v := newNumber(nil) + // This seems incorrect + v.SetMeta([]EvalMatch{{}, {Metric: "NoData"}}) + return newResults(v) + }, + }, { + name: "single no values query with condition is met has no value", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{}, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("min"), + Operator: "and", + Evaluator: &noValueEvaluator{}, + }, + }, + }, + expected: func() mathexp.Results { + v := newNumber(nil) + // This too seems incorrect, looks like we don't call the evaluator + v.SetMeta([]EvalMatch{{Metric: "NoData"}}) + return newResults(v) + }, + }, { + name: "single series no points query with condition is met has no value", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{ + newSeries(nil), + }, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("min"), + Operator: "and", + Evaluator: &noValueEvaluator{}, + }, + }, + }, + expected: func() mathexp.Results { + v := newNumber(nil) + // This seems incorrect + v.SetMeta([]EvalMatch{{}, {Metric: "NoData"}}) + return newResults(v) + }, + }, { + // This test asserts that a single query with condition returns 1 and the average of the second + // series in the meta because while the first series is No Data the second series contains valid points + name: "single query with condition returns average when one series is no data and the other contains valid points", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{ + newSeries(), + newSeries(ptr.Float64(2)), + }, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("min"), + Operator: "and", + Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 1}, + }, + }}, + expected: func() mathexp.Results { + v := newNumber(ptr.Float64(1)) + v.SetMeta([]EvalMatch{{Value: ptr.Float64(2)}}) + return newResults(v) + }, + }, { + name: "single query with condition and no series matches condition", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{ + newSeries(ptr.Float64(1), ptr.Float64(5)), + newSeries(ptr.Float64(2), ptr.Float64(10)), + }, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("min"), + Operator: "and", + Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 15}, + }, + }}, + expected: func() mathexp.Results { + v := newNumber(ptr.Float64(0)) + v.SetMeta([]EvalMatch{}) + return mathexp.Results{Values: mathexp.Values{v}} + }, + }, { + name: "single query with condition and one of two series matches condition", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{ + newSeries(ptr.Float64(1), ptr.Float64(5)), + newSeriesWithLabels(data.Labels{"foo": "bar"}, ptr.Float64(2), ptr.Float64(10)), + }, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("min"), + Operator: "and", + Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 1}, + }, + }}, + expected: func() mathexp.Results { + v := newNumber(ptr.Float64(1)) + v.SetMeta([]EvalMatch{{Value: ptr.Float64(2), Labels: data.Labels{"foo": "bar"}}}) + return newResults(v) + }, + }, { + name: "single query with condition and both series matches condition", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{ + newSeries(ptr.Float64(1), ptr.Float64(5)), + newSeriesWithLabels(data.Labels{"foo": "bar"}, ptr.Float64(2), ptr.Float64(10)), + }, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("min"), + Operator: "and", + Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 0}, + }, + }}, + expected: func() mathexp.Results { + v := newNumber(ptr.Float64(1)) + v.SetMeta([]EvalMatch{{ + Value: ptr.Float64(1), + }, { + Value: ptr.Float64(2), + Labels: data.Labels{"foo": "bar"}, + }}) + return newResults(v) + }, + }, { + name: "single query with two conditions where left hand side is met", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{ + newSeries(ptr.Float64(1), ptr.Float64(5)), + }, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("max"), + Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 2}, + }, + { + InputRefID: "A", + Reducer: reducer("min"), + Operator: "or", + Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 1}, + }, + }}, + expected: func() mathexp.Results { + v := newNumber(ptr.Float64(1)) + v.SetMeta([]EvalMatch{{Value: ptr.Float64(5)}}) + return newResults(v) + }, + }, { + name: "single query with two conditions where right hand side is met", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{ + newSeries(ptr.Float64(1), ptr.Float64(5)), + }, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("max"), + Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 10}, + }, + { + InputRefID: "A", + Reducer: reducer("min"), + Operator: "or", + Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 0}, + }, + }}, + expected: func() mathexp.Results { + v := newNumber(ptr.Float64(1)) + v.SetMeta([]EvalMatch{{Value: ptr.Float64(1)}}) + return newResults(v) + }, + }, { + name: "single query with two conditions where both are met", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{ + newSeries(ptr.Float64(1), ptr.Float64(5)), + }, + }, + }, + cmd: &ConditionsCmd{ + Conditions: []condition{ + { + InputRefID: "A", + Reducer: reducer("max"), + Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 2}, + }, + { + InputRefID: "A", + Reducer: reducer("min"), + Operator: "or", + Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 0}, + }, + }}, + expected: func() mathexp.Results { + v := newNumber(ptr.Float64(1)) + v.SetMeta([]EvalMatch{{Value: ptr.Float64(5)}, {Value: ptr.Float64(1)}}) + return newResults(v) + }, + }, { + name: "single instant query with condition where condition is met", + vars: mathexp.Vars{ + "A": mathexp.Results{ + Values: []mathexp.Value{ + newNumber(ptr.Float64(5)), + newNumber(ptr.Float64(10)), + newNumber(ptr.Float64(15)), }, }, }, @@ -279,13 +465,13 @@ func TestConditionsCmd(t *testing.T) { }, }, expected: func() mathexp.Results { - v := valBasedNumber(ptr.Float64(1)) + v := newNumber(ptr.Float64(1)) v.SetMeta([]EvalMatch{ {Value: ptr.Float64(5)}, {Value: ptr.Float64(10)}, {Value: ptr.Float64(15)}, }) - return mathexp.NewResults(v) + return newResults(v) }, }} @@ -293,9 +479,6 @@ func TestConditionsCmd(t *testing.T) { t.Run(tt.name, func(t *testing.T) { res, err := tt.cmd.Execute(context.Background(), time.Now(), tt.vars) require.NoError(t, err) - - require.Equal(t, 1, len(res.Values)) - require.Equal(t, tt.expected(), res) }) } diff --git a/pkg/expr/classic/evaluator_test.go b/pkg/expr/classic/evaluator_test.go index b4a61e98441..4deb92491ad 100644 --- a/pkg/expr/classic/evaluator_test.go +++ b/pkg/expr/classic/evaluator_test.go @@ -18,25 +18,25 @@ func TestThresholdEvaluator(t *testing.T) { { name: "value 3 is gt 1: true", evaluator: &thresholdEvaluator{"gt", 1}, - inputNumber: valBasedNumber(ptr.Float64(3)), + inputNumber: newNumber(ptr.Float64(3)), expected: true, }, { name: "value 1 is gt 3: false", evaluator: &thresholdEvaluator{"gt", 3}, - inputNumber: valBasedNumber(ptr.Float64(1)), + inputNumber: newNumber(ptr.Float64(1)), expected: false, }, { name: "value 3 is lt 1: true", evaluator: &thresholdEvaluator{"lt", 1}, - inputNumber: valBasedNumber(ptr.Float64(3)), + inputNumber: newNumber(ptr.Float64(3)), expected: false, }, { name: "value 1 is lt 3: false", evaluator: &thresholdEvaluator{"lt", 3}, - inputNumber: valBasedNumber(ptr.Float64(1)), + inputNumber: newNumber(ptr.Float64(1)), expected: true, }, } @@ -59,50 +59,50 @@ func TestRangedEvaluator(t *testing.T) { { name: "value 3 is within range 1, 100: true", evaluator: &rangedEvaluator{"within_range", 1, 100}, - inputNumber: valBasedNumber(ptr.Float64(3)), + inputNumber: newNumber(ptr.Float64(3)), expected: true, }, { name: "value 300 is within range 1, 100: false", evaluator: &rangedEvaluator{"within_range", 1, 100}, - inputNumber: valBasedNumber(ptr.Float64(300)), + inputNumber: newNumber(ptr.Float64(300)), expected: false, }, { name: "value 3 is within range 100, 1: true", evaluator: &rangedEvaluator{"within_range", 100, 1}, - inputNumber: valBasedNumber(ptr.Float64(3)), + inputNumber: newNumber(ptr.Float64(3)), expected: true, }, { name: "value 300 is within range 100, 1: false", evaluator: &rangedEvaluator{"within_range", 100, 1}, - inputNumber: valBasedNumber(ptr.Float64(300)), + inputNumber: newNumber(ptr.Float64(300)), expected: false, }, // outside { name: "value 1000 is outside range 1, 100: true", evaluator: &rangedEvaluator{"outside_range", 1, 100}, - inputNumber: valBasedNumber(ptr.Float64(1000)), + inputNumber: newNumber(ptr.Float64(1000)), expected: true, }, { name: "value 50 is outside range 1, 100: false", evaluator: &rangedEvaluator{"outside_range", 1, 100}, - inputNumber: valBasedNumber(ptr.Float64(50)), + inputNumber: newNumber(ptr.Float64(50)), expected: false, }, { name: "value 1000 is outside range 100, 1: true", evaluator: &rangedEvaluator{"outside_range", 100, 1}, - inputNumber: valBasedNumber(ptr.Float64(1000)), + inputNumber: newNumber(ptr.Float64(1000)), expected: true, }, { name: "value 50 is outside range 100, 1: false", evaluator: &rangedEvaluator{"outside_range", 100, 1}, - inputNumber: valBasedNumber(ptr.Float64(50)), + inputNumber: newNumber(ptr.Float64(50)), expected: false, }, } @@ -124,13 +124,13 @@ func TestNoValueEvaluator(t *testing.T) { { name: "value 50 is no_value: false", evaluator: &noValueEvaluator{}, - inputNumber: valBasedNumber(ptr.Float64(50)), + inputNumber: newNumber(ptr.Float64(50)), expected: false, }, { name: "value nil is no_value: true", evaluator: &noValueEvaluator{}, - inputNumber: valBasedNumber(nil), + inputNumber: newNumber(nil), expected: true, }, } diff --git a/pkg/expr/classic/reduce_test.go b/pkg/expr/classic/reduce_test.go index 8e146215a2b..98cb5e63718 100644 --- a/pkg/expr/classic/reduce_test.go +++ b/pkg/expr/classic/reduce_test.go @@ -5,10 +5,11 @@ import ( "testing" "time" - "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana/pkg/expr/mathexp" "github.com/stretchr/testify/require" ptr "github.com/xorcare/pointer" + + "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/grafana/grafana/pkg/expr/mathexp" ) func TestReducer(t *testing.T) { @@ -21,98 +22,98 @@ func TestReducer(t *testing.T) { { name: "sum", reducer: reducer("sum"), - inputSeries: valBasedSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(3)), - expectedNumber: valBasedNumber(ptr.Float64(6)), + inputSeries: newSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(3)), + expectedNumber: newNumber(ptr.Float64(6)), }, { name: "min", reducer: reducer("min"), - inputSeries: valBasedSeries(ptr.Float64(3), ptr.Float64(2), ptr.Float64(1)), - expectedNumber: valBasedNumber(ptr.Float64(1)), + inputSeries: newSeries(ptr.Float64(3), ptr.Float64(2), ptr.Float64(1)), + expectedNumber: newNumber(ptr.Float64(1)), }, { name: "min with NaNs only", reducer: reducer("min"), - inputSeries: valBasedSeries(ptr.Float64(math.NaN()), ptr.Float64(math.NaN()), ptr.Float64(math.NaN())), - expectedNumber: valBasedNumber(nil), + inputSeries: newSeries(ptr.Float64(math.NaN()), ptr.Float64(math.NaN()), ptr.Float64(math.NaN())), + expectedNumber: newNumber(nil), }, { name: "max", reducer: reducer("max"), - inputSeries: valBasedSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(3)), - expectedNumber: valBasedNumber(ptr.Float64(3)), + inputSeries: newSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(3)), + expectedNumber: newNumber(ptr.Float64(3)), }, { name: "count", reducer: reducer("count"), - inputSeries: valBasedSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(3000)), - expectedNumber: valBasedNumber(ptr.Float64(3)), + inputSeries: newSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(3000)), + expectedNumber: newNumber(ptr.Float64(3)), }, { name: "last", reducer: reducer("last"), - inputSeries: valBasedSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(3000)), - expectedNumber: valBasedNumber(ptr.Float64(3000)), + inputSeries: newSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(3000)), + expectedNumber: newNumber(ptr.Float64(3000)), }, { name: "median with odd amount of numbers", reducer: reducer("median"), - inputSeries: valBasedSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(3000)), - expectedNumber: valBasedNumber(ptr.Float64(2)), + inputSeries: newSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(3000)), + expectedNumber: newNumber(ptr.Float64(2)), }, { name: "median with even amount of numbers", reducer: reducer("median"), - inputSeries: valBasedSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(4), ptr.Float64(3000)), - expectedNumber: valBasedNumber(ptr.Float64(3)), + inputSeries: newSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(4), ptr.Float64(3000)), + expectedNumber: newNumber(ptr.Float64(3)), }, { name: "median with one value", reducer: reducer("median"), - inputSeries: valBasedSeries(ptr.Float64(1)), - expectedNumber: valBasedNumber(ptr.Float64(1)), + inputSeries: newSeries(ptr.Float64(1)), + expectedNumber: newNumber(ptr.Float64(1)), }, { name: "median should ignore null values", reducer: reducer("median"), - inputSeries: valBasedSeries(nil, nil, nil, ptr.Float64(1), ptr.Float64(2), ptr.Float64(3)), - expectedNumber: valBasedNumber(ptr.Float64(2)), + inputSeries: newSeries(nil, nil, nil, ptr.Float64(1), ptr.Float64(2), ptr.Float64(3)), + expectedNumber: newNumber(ptr.Float64(2)), }, { name: "avg", reducer: reducer("avg"), - inputSeries: valBasedSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(3)), - expectedNumber: valBasedNumber(ptr.Float64(2)), + inputSeries: newSeries(ptr.Float64(1), ptr.Float64(2), ptr.Float64(3)), + expectedNumber: newNumber(ptr.Float64(2)), }, { name: "avg with only nulls", reducer: reducer("avg"), - inputSeries: valBasedSeries(nil), - expectedNumber: valBasedNumber(nil), + inputSeries: newSeries(nil), + expectedNumber: newNumber(nil), }, { name: "avg of number values and null values should ignore nulls", reducer: reducer("avg"), - inputSeries: valBasedSeries(ptr.Float64(3), nil, nil, ptr.Float64(3)), - expectedNumber: valBasedNumber(ptr.Float64(3)), + inputSeries: newSeries(ptr.Float64(3), nil, nil, ptr.Float64(3)), + expectedNumber: newNumber(ptr.Float64(3)), }, { name: "count_non_null with mixed null/real values", reducer: reducer("count_non_null"), - inputSeries: valBasedSeries(nil, nil, ptr.Float64(3), ptr.Float64(4)), - expectedNumber: valBasedNumber(ptr.Float64(2)), + inputSeries: newSeries(nil, nil, ptr.Float64(3), ptr.Float64(4)), + expectedNumber: newNumber(ptr.Float64(2)), }, { name: "count_non_null with mixed null/real values", reducer: reducer("count_non_null"), - inputSeries: valBasedSeries(nil, nil, ptr.Float64(3), ptr.Float64(4)), - expectedNumber: valBasedNumber(ptr.Float64(2)), + inputSeries: newSeries(nil, nil, ptr.Float64(3), ptr.Float64(4)), + expectedNumber: newNumber(ptr.Float64(2)), }, { name: "count_non_null with no values", reducer: reducer("count_non_null"), - inputSeries: valBasedSeries(nil, nil), - expectedNumber: valBasedNumber(nil), + inputSeries: newSeries(nil, nil), + expectedNumber: newNumber(nil), }, } @@ -133,58 +134,58 @@ func TestDiffReducer(t *testing.T) { }{ { name: "diff of one positive point", - inputSeries: valBasedSeries(ptr.Float64(30)), - expectedNumber: valBasedNumber(ptr.Float64(0)), + inputSeries: newSeries(ptr.Float64(30)), + expectedNumber: newNumber(ptr.Float64(0)), }, { name: "diff of one negative point", - inputSeries: valBasedSeries(ptr.Float64(-30)), - expectedNumber: valBasedNumber(ptr.Float64(0)), + inputSeries: newSeries(ptr.Float64(-30)), + expectedNumber: newNumber(ptr.Float64(0)), }, { name: "diff two positive points [1]", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(40)), - expectedNumber: valBasedNumber(ptr.Float64(10)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(40)), + expectedNumber: newNumber(ptr.Float64(10)), }, { name: "diff two positive points [2]", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(20)), - expectedNumber: valBasedNumber(ptr.Float64(-10)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(20)), + expectedNumber: newNumber(ptr.Float64(-10)), }, { name: "diff two negative points [1]", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(-40)), - expectedNumber: valBasedNumber(ptr.Float64(-10)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(-40)), + expectedNumber: newNumber(ptr.Float64(-10)), }, { name: "diff two negative points [2]", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(-10)), - expectedNumber: valBasedNumber(ptr.Float64(20)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(-10)), + expectedNumber: newNumber(ptr.Float64(20)), }, { name: "diff of one positive and one negative point", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(-40)), - expectedNumber: valBasedNumber(ptr.Float64(-70)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(-40)), + expectedNumber: newNumber(ptr.Float64(-70)), }, { name: "diff of one negative and one positive point", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(40)), - expectedNumber: valBasedNumber(ptr.Float64(70)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(40)), + expectedNumber: newNumber(ptr.Float64(70)), }, { name: "diff of three positive points", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(40), ptr.Float64(50)), - expectedNumber: valBasedNumber(ptr.Float64(20)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(40), ptr.Float64(50)), + expectedNumber: newNumber(ptr.Float64(20)), }, { name: "diff of three negative points", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(-40), ptr.Float64(-50)), - expectedNumber: valBasedNumber(ptr.Float64(-20)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(-40), ptr.Float64(-50)), + expectedNumber: newNumber(ptr.Float64(-20)), }, { name: "diff with only nulls", - inputSeries: valBasedSeries(nil, nil), - expectedNumber: valBasedNumber(nil), + inputSeries: newSeries(nil, nil), + expectedNumber: newNumber(nil), }, } for _, tt := range tests { @@ -203,58 +204,58 @@ func TestDiffAbsReducer(t *testing.T) { }{ { name: "diff_abs of one positive point", - inputSeries: valBasedSeries(ptr.Float64(30)), - expectedNumber: valBasedNumber(ptr.Float64(0)), + inputSeries: newSeries(ptr.Float64(30)), + expectedNumber: newNumber(ptr.Float64(0)), }, { name: "diff_abs of one negative point", - inputSeries: valBasedSeries(ptr.Float64(-30)), - expectedNumber: valBasedNumber(ptr.Float64(0)), + inputSeries: newSeries(ptr.Float64(-30)), + expectedNumber: newNumber(ptr.Float64(0)), }, { name: "diff_abs two positive points [1]", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(40)), - expectedNumber: valBasedNumber(ptr.Float64(10)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(40)), + expectedNumber: newNumber(ptr.Float64(10)), }, { name: "diff_abs two positive points [2]", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(20)), - expectedNumber: valBasedNumber(ptr.Float64(10)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(20)), + expectedNumber: newNumber(ptr.Float64(10)), }, { name: "diff_abs two negative points [1]", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(-40)), - expectedNumber: valBasedNumber(ptr.Float64(10)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(-40)), + expectedNumber: newNumber(ptr.Float64(10)), }, { name: "diff_abs two negative points [2]", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(-10)), - expectedNumber: valBasedNumber(ptr.Float64(20)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(-10)), + expectedNumber: newNumber(ptr.Float64(20)), }, { name: "diff_abs of one positive and one negative point", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(-40)), - expectedNumber: valBasedNumber(ptr.Float64(70)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(-40)), + expectedNumber: newNumber(ptr.Float64(70)), }, { name: "diff_abs of one negative and one positive point", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(40)), - expectedNumber: valBasedNumber(ptr.Float64(70)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(40)), + expectedNumber: newNumber(ptr.Float64(70)), }, { name: "diff_abs of three positive points", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(40), ptr.Float64(50)), - expectedNumber: valBasedNumber(ptr.Float64(20)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(40), ptr.Float64(50)), + expectedNumber: newNumber(ptr.Float64(20)), }, { name: "diff_abs of three negative points", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(-40), ptr.Float64(-50)), - expectedNumber: valBasedNumber(ptr.Float64(20)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(-40), ptr.Float64(-50)), + expectedNumber: newNumber(ptr.Float64(20)), }, { name: "diff_abs with only nulls", - inputSeries: valBasedSeries(nil, nil), - expectedNumber: valBasedNumber(nil), + inputSeries: newSeries(nil, nil), + expectedNumber: newNumber(nil), }, } for _, tt := range tests { @@ -273,58 +274,58 @@ func TestPercentDiffReducer(t *testing.T) { }{ { name: "percent_diff of one positive point", - inputSeries: valBasedSeries(ptr.Float64(30)), - expectedNumber: valBasedNumber(ptr.Float64(0)), + inputSeries: newSeries(ptr.Float64(30)), + expectedNumber: newNumber(ptr.Float64(0)), }, { name: "percent_diff of one negative point", - inputSeries: valBasedSeries(ptr.Float64(-30)), - expectedNumber: valBasedNumber(ptr.Float64(0)), + inputSeries: newSeries(ptr.Float64(-30)), + expectedNumber: newNumber(ptr.Float64(0)), }, { name: "percent_diff two positive points [1]", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(40)), - expectedNumber: valBasedNumber(ptr.Float64(33.33333333333333)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(40)), + expectedNumber: newNumber(ptr.Float64(33.33333333333333)), }, { name: "percent_diff two positive points [2]", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(20)), - expectedNumber: valBasedNumber(ptr.Float64(-33.33333333333333)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(20)), + expectedNumber: newNumber(ptr.Float64(-33.33333333333333)), }, { name: "percent_diff two negative points [1]", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(-40)), - expectedNumber: valBasedNumber(ptr.Float64(-33.33333333333333)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(-40)), + expectedNumber: newNumber(ptr.Float64(-33.33333333333333)), }, { name: "percent_diff two negative points [2]", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(-10)), - expectedNumber: valBasedNumber(ptr.Float64(66.66666666666666)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(-10)), + expectedNumber: newNumber(ptr.Float64(66.66666666666666)), }, { name: "percent_diff of one positive and one negative point", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(-40)), - expectedNumber: valBasedNumber(ptr.Float64(-233.33333333333334)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(-40)), + expectedNumber: newNumber(ptr.Float64(-233.33333333333334)), }, { name: "percent_diff of one negative and one positive point", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(40)), - expectedNumber: valBasedNumber(ptr.Float64(233.33333333333334)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(40)), + expectedNumber: newNumber(ptr.Float64(233.33333333333334)), }, { name: "percent_diff of three positive points", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(40), ptr.Float64(50)), - expectedNumber: valBasedNumber(ptr.Float64(66.66666666666666)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(40), ptr.Float64(50)), + expectedNumber: newNumber(ptr.Float64(66.66666666666666)), }, { name: "percent_diff of three negative points", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(-40), ptr.Float64(-50)), - expectedNumber: valBasedNumber(ptr.Float64(-66.66666666666666)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(-40), ptr.Float64(-50)), + expectedNumber: newNumber(ptr.Float64(-66.66666666666666)), }, { name: "percent_diff with only nulls", - inputSeries: valBasedSeries(nil, nil), - expectedNumber: valBasedNumber(nil), + inputSeries: newSeries(nil, nil), + expectedNumber: newNumber(nil), }, } for _, tt := range tests { @@ -343,58 +344,58 @@ func TestPercentDiffAbsReducer(t *testing.T) { }{ { name: "percent_diff_abs of one positive point", - inputSeries: valBasedSeries(ptr.Float64(30)), - expectedNumber: valBasedNumber(ptr.Float64(0)), + inputSeries: newSeries(ptr.Float64(30)), + expectedNumber: newNumber(ptr.Float64(0)), }, { name: "percent_diff_abs of one negative point", - inputSeries: valBasedSeries(ptr.Float64(-30)), - expectedNumber: valBasedNumber(ptr.Float64(0)), + inputSeries: newSeries(ptr.Float64(-30)), + expectedNumber: newNumber(ptr.Float64(0)), }, { name: "percent_diff_abs two positive points [1]", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(40)), - expectedNumber: valBasedNumber(ptr.Float64(33.33333333333333)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(40)), + expectedNumber: newNumber(ptr.Float64(33.33333333333333)), }, { name: "percent_diff_abs two positive points [2]", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(20)), - expectedNumber: valBasedNumber(ptr.Float64(33.33333333333333)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(20)), + expectedNumber: newNumber(ptr.Float64(33.33333333333333)), }, { name: "percent_diff_abs two negative points [1]", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(-40)), - expectedNumber: valBasedNumber(ptr.Float64(33.33333333333333)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(-40)), + expectedNumber: newNumber(ptr.Float64(33.33333333333333)), }, { name: "percent_diff_abs two negative points [2]", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(-10)), - expectedNumber: valBasedNumber(ptr.Float64(66.66666666666666)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(-10)), + expectedNumber: newNumber(ptr.Float64(66.66666666666666)), }, { name: "percent_diff_abs of one positive and one negative point", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(-40)), - expectedNumber: valBasedNumber(ptr.Float64(233.33333333333334)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(-40)), + expectedNumber: newNumber(ptr.Float64(233.33333333333334)), }, { name: "percent_diff_abs of one negative and one positive point", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(40)), - expectedNumber: valBasedNumber(ptr.Float64(233.33333333333334)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(40)), + expectedNumber: newNumber(ptr.Float64(233.33333333333334)), }, { name: "percent_diff_abs of three positive points", - inputSeries: valBasedSeries(ptr.Float64(30), ptr.Float64(40), ptr.Float64(50)), - expectedNumber: valBasedNumber(ptr.Float64(66.66666666666666)), + inputSeries: newSeries(ptr.Float64(30), ptr.Float64(40), ptr.Float64(50)), + expectedNumber: newNumber(ptr.Float64(66.66666666666666)), }, { name: "percent_diff_abs of three negative points", - inputSeries: valBasedSeries(ptr.Float64(-30), ptr.Float64(-40), ptr.Float64(-50)), - expectedNumber: valBasedNumber(ptr.Float64(66.66666666666666)), + inputSeries: newSeries(ptr.Float64(-30), ptr.Float64(-40), ptr.Float64(-50)), + expectedNumber: newNumber(ptr.Float64(66.66666666666666)), }, { name: "percent_diff_abs with only nulls", - inputSeries: valBasedSeries(nil, nil), - expectedNumber: valBasedNumber(nil), + inputSeries: newSeries(nil, nil), + expectedNumber: newNumber(nil), }, } for _, tt := range tests { @@ -405,24 +406,28 @@ func TestPercentDiffAbsReducer(t *testing.T) { } } -func valBasedSeries(vals ...*float64) mathexp.Series { - newSeries := mathexp.NewSeries("", nil, len(vals)) - for idx, f := range vals { - newSeries.SetPoint(idx, time.Unix(int64(idx), 0), f) - } - return newSeries +func newNumber(f *float64) mathexp.Number { + num := mathexp.NewNumber("", nil) + num.SetValue(f) + return num } -func valBasedSeriesWithLabels(l data.Labels, vals ...*float64) mathexp.Series { - newSeries := mathexp.NewSeries("", l, len(vals)) - for idx, f := range vals { - newSeries.SetPoint(idx, time.Unix(int64(idx), 0), f) +func newSeries(points ...*float64) mathexp.Series { + series := mathexp.NewSeries("", nil, len(points)) + for idx, point := range points { + series.SetPoint(idx, time.Unix(int64(idx), 0), point) } - return newSeries + return series } -func valBasedNumber(f *float64) mathexp.Number { - newNumber := mathexp.NewNumber("", nil) - newNumber.SetValue(f) - return newNumber +func newSeriesWithLabels(labels data.Labels, values ...*float64) mathexp.Series { + series := mathexp.NewSeries("", labels, len(values)) + for idx, value := range values { + series.SetPoint(idx, time.Unix(int64(idx), 0), value) + } + return series +} + +func newResults(values ...mathexp.Value) mathexp.Results { + return mathexp.Results{Values: values} } diff --git a/pkg/expr/mathexp/types.go b/pkg/expr/mathexp/types.go index 8c668e8b8ee..3e020d85d5a 100644 --- a/pkg/expr/mathexp/types.go +++ b/pkg/expr/mathexp/types.go @@ -11,10 +11,6 @@ type Results struct { Values Values } -func NewResults(values ...Value) Results { - return Results{Values: values} -} - // Values is a slice of Value interfaces type Values []Value