mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 04:59:15 -06:00
4093fae99a
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
425 lines
9.2 KiB
Go
425 lines
9.2 KiB
Go
package mathexp
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestNaN(t *testing.T) {
|
|
var tests = []struct {
|
|
name string
|
|
expr string
|
|
vars Vars
|
|
newErrIs assert.ErrorAssertionFunc
|
|
execErrIs assert.ErrorAssertionFunc
|
|
results Results
|
|
willPanic bool
|
|
}{
|
|
{
|
|
name: "unary !: Op Number(NaN) is NaN",
|
|
expr: "! $A",
|
|
vars: Vars{"A": Results{[]Value{makeNumber("", nil, NaN)}}},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{[]Value{makeNumber("", nil, NaN)}},
|
|
},
|
|
{
|
|
name: "unary -: Op Number(NaN) is NaN",
|
|
expr: "-$A",
|
|
vars: Vars{"A": Results{[]Value{makeNumber("", nil, NaN)}}},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{[]Value{makeNumber("", nil, NaN)}},
|
|
},
|
|
{
|
|
name: "binary: Scalar Op(Non-AND/OR) Number(NaN) is NaN",
|
|
expr: "1 * $A",
|
|
vars: Vars{"A": Results{[]Value{makeNumber("", nil, NaN)}}},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{[]Value{makeNumber("", nil, NaN)}},
|
|
},
|
|
{
|
|
name: "binary: Scalar Op(AND/OR) Number(NaN) is 0/1",
|
|
expr: "1 || $A",
|
|
vars: Vars{"A": Results{[]Value{makeNumber("", nil, NaN)}}},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{[]Value{makeNumber("", nil, float64Pointer(1))}},
|
|
},
|
|
{
|
|
name: "binary: Scalar Op(Non-AND/OR) Series(with NaN value) is NaN)",
|
|
expr: "1 - $A",
|
|
vars: Vars{
|
|
"A": Results{
|
|
[]Value{
|
|
makeSeries("temp", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(2),
|
|
}, tp{
|
|
time.Unix(10, 0), NaN,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(-1),
|
|
}, tp{
|
|
time.Unix(10, 0), NaN,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "binary: Number Op(Non-AND/OR) Series(with NaN value) is Series with NaN",
|
|
expr: "$A == $B",
|
|
vars: Vars{
|
|
"A": Results{
|
|
[]Value{
|
|
makeSeries("temp", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(2),
|
|
}, tp{
|
|
time.Unix(10, 0), NaN,
|
|
}),
|
|
},
|
|
},
|
|
"B": Results{[]Value{makeNumber("", nil, float64Pointer(0))}},
|
|
},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(0),
|
|
}, tp{
|
|
time.Unix(10, 0), NaN,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "binary: Number(NaN) Op Series(with NaN value) is Series with NaN",
|
|
expr: "$A + $B",
|
|
vars: Vars{
|
|
"A": Results{
|
|
[]Value{
|
|
makeSeries("temp", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(2),
|
|
}, tp{
|
|
time.Unix(10, 0), NaN,
|
|
}),
|
|
},
|
|
},
|
|
"B": Results{[]Value{makeNumber("", nil, NaN)}},
|
|
},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), NaN,
|
|
}, tp{
|
|
time.Unix(10, 0), NaN,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
opt := cmp.Comparer(func(x, y float64) bool {
|
|
return (math.IsNaN(x) && math.IsNaN(y)) || x == y
|
|
})
|
|
options := append([]cmp.Option{opt}, data.FrameTestCompareOptions()...)
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
testBlock := func() {
|
|
e, err := New(tt.expr)
|
|
tt.newErrIs(t, err)
|
|
if e != nil {
|
|
res, err := e.Execute("", tt.vars)
|
|
tt.execErrIs(t, err)
|
|
if diff := cmp.Diff(res, tt.results, options...); diff != "" {
|
|
assert.FailNow(t, tt.name, diff)
|
|
}
|
|
}
|
|
}
|
|
if tt.willPanic {
|
|
assert.Panics(t, testBlock)
|
|
} else {
|
|
assert.NotPanics(t, testBlock)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNullValues(t *testing.T) {
|
|
var tests = []struct {
|
|
name string
|
|
expr string
|
|
vars Vars
|
|
newErrIs assert.ErrorAssertionFunc
|
|
execErrIs assert.ErrorAssertionFunc
|
|
results Results
|
|
willPanic bool
|
|
}{
|
|
{
|
|
name: "scalar: unary ! null(): is null",
|
|
expr: "! null()",
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: NewScalarResults("", nil),
|
|
},
|
|
{
|
|
name: "scalar: binary null() + null(): is null",
|
|
expr: "null() + null()",
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: NewScalarResults("", nil),
|
|
},
|
|
{
|
|
name: "scalar: binary 1 + null(): is null",
|
|
expr: "1 + null()",
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: NewScalarResults("", nil),
|
|
},
|
|
{
|
|
name: "series: unary with a null value in it has a null value in result",
|
|
expr: "- $A",
|
|
vars: Vars{
|
|
"A": Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(1),
|
|
}, tp{
|
|
time.Unix(10, 0), nil,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(-1),
|
|
}, tp{
|
|
time.Unix(10, 0), nil,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "series: binary with a null value in it has a null value in result",
|
|
expr: "$A - $A",
|
|
vars: Vars{
|
|
"A": Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(1),
|
|
}, tp{
|
|
time.Unix(10, 0), nil,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(0),
|
|
}, tp{
|
|
time.Unix(10, 0), nil,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "series and scalar: binary with a null value in it has a nil value in result",
|
|
expr: "$A - 1",
|
|
vars: Vars{
|
|
"A": Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(1),
|
|
}, tp{
|
|
time.Unix(10, 0), nil,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(0),
|
|
}, tp{
|
|
time.Unix(10, 0), nil,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "number: unary ! null number: is null",
|
|
expr: "! $A",
|
|
vars: Vars{
|
|
"A": Results{
|
|
[]Value{
|
|
makeNumber("", nil, nil),
|
|
},
|
|
},
|
|
},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{
|
|
[]Value{
|
|
makeNumber("", nil, nil),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "number: binary null number and null number: is null",
|
|
expr: "$A + $A",
|
|
vars: Vars{
|
|
"A": Results{
|
|
[]Value{
|
|
makeNumber("", nil, nil),
|
|
},
|
|
},
|
|
},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{
|
|
[]Value{
|
|
makeNumber("", nil, nil),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "number: binary non-null number and null number: is null",
|
|
expr: "$A * $B",
|
|
vars: Vars{
|
|
"A": Results{
|
|
[]Value{
|
|
makeNumber("", nil, nil),
|
|
},
|
|
},
|
|
"B": Results{
|
|
[]Value{
|
|
makeNumber("", nil, float64Pointer(1)),
|
|
},
|
|
},
|
|
},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{
|
|
[]Value{
|
|
makeNumber("", nil, nil),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "number and series: binary non-null number and series with a null: is null",
|
|
expr: "$A * $B",
|
|
vars: Vars{
|
|
"A": Results{
|
|
[]Value{
|
|
makeNumber("", nil, float64Pointer(1)),
|
|
},
|
|
},
|
|
"B": Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(1),
|
|
}, tp{
|
|
time.Unix(10, 0), nil,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(1),
|
|
}, tp{
|
|
time.Unix(10, 0), nil,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "number and series: binary null number and series with non-null and null: is null and null",
|
|
expr: "$A * $B",
|
|
vars: Vars{
|
|
"A": Results{
|
|
[]Value{
|
|
makeNumber("", nil, nil),
|
|
},
|
|
},
|
|
"B": Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), float64Pointer(1),
|
|
}, tp{
|
|
time.Unix(10, 0), nil,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
newErrIs: assert.NoError,
|
|
execErrIs: assert.NoError,
|
|
results: Results{
|
|
[]Value{
|
|
makeSeries("", nil, tp{
|
|
time.Unix(5, 0), nil,
|
|
}, tp{
|
|
time.Unix(10, 0), nil,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// go-cmp instead of testify assert is used to compare results here
|
|
// because it supports an option for NaN equality.
|
|
// https://github.com/stretchr/testify/pull/691#issuecomment-528457166
|
|
opt := cmp.Comparer(func(x, y float64) bool {
|
|
return (math.IsNaN(x) && math.IsNaN(y)) || x == y
|
|
})
|
|
options := append([]cmp.Option{opt}, data.FrameTestCompareOptions()...)
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
testBlock := func() {
|
|
e, err := New(tt.expr)
|
|
tt.newErrIs(t, err)
|
|
if e != nil {
|
|
res, err := e.Execute("", tt.vars)
|
|
tt.execErrIs(t, err)
|
|
if diff := cmp.Diff(tt.results, res, options...); diff != "" {
|
|
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
|
}
|
|
}
|
|
}
|
|
if tt.willPanic {
|
|
assert.Panics(t, testBlock)
|
|
} else {
|
|
testBlock()
|
|
}
|
|
})
|
|
}
|
|
}
|