264 lines
6.8 KiB
Go
264 lines
6.8 KiB
Go
package reads
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/influxdata/flux/execute"
|
|
"github.com/influxdata/flux/values"
|
|
"github.com/influxdata/influxdb/v2/tsdb/cursors"
|
|
)
|
|
|
|
|
|
func TestIntegerFilterArrayCursor(t *testing.T) {
|
|
var i int
|
|
expr := MockExpression{
|
|
EvalBoolFunc: func(v Valuer) bool {
|
|
i++
|
|
return i%2 == 0
|
|
},
|
|
}
|
|
|
|
var resultN int
|
|
ac := MockIntegerArrayCursor{
|
|
CloseFunc: func() {},
|
|
ErrFunc: func() error { return nil },
|
|
StatsFunc: func() cursors.CursorStats { return cursors.CursorStats{} },
|
|
NextFunc: func() *cursors.IntegerArray {
|
|
resultN++
|
|
if resultN == 4 {
|
|
return cursors.NewIntegerArrayLen(0)
|
|
}
|
|
return cursors.NewIntegerArrayLen(900)
|
|
},
|
|
}
|
|
|
|
c := newIntegerFilterArrayCursor(&expr)
|
|
c.reset(&ac)
|
|
|
|
if got, want := len(c.Next().Timestamps), 1000; got != want {
|
|
t.Fatalf("len(Next())=%d, want %d", got, want)
|
|
} else if got, want := len(c.Next().Timestamps), 350; got != want {
|
|
t.Fatalf("len(Next())=%d, want %d", got, want)
|
|
}
|
|
}
|
|
|
|
func makeIntegerArray(n int, tsStart time.Time, tsStep time.Duration, valueFn func(i int64) int64) *cursors.IntegerArray {
|
|
ia := &cursors.IntegerArray{
|
|
Timestamps: make([]int64, n),
|
|
Values: make([]int64, n),
|
|
}
|
|
|
|
for i := 0; i < n; i++ {
|
|
ia.Timestamps[i] = tsStart.UnixNano() + int64(i)*int64(tsStep)
|
|
ia.Values[i] = valueFn(int64(i))
|
|
}
|
|
|
|
return ia
|
|
}
|
|
|
|
func makeFloatArray(n int, tsStart time.Time, tsStep time.Duration, valueFn func(i int64) float64) *cursors.FloatArray {
|
|
fa := &cursors.FloatArray{
|
|
Timestamps: make([]int64, n),
|
|
Values: make([]float64, n),
|
|
}
|
|
|
|
for i := 0; i < n; i++ {
|
|
fa.Timestamps[i] = tsStart.UnixNano() + int64(i)*int64(tsStep)
|
|
fa.Values[i] = valueFn(int64(i))
|
|
}
|
|
|
|
return fa
|
|
}
|
|
|
|
func mustParseTime(ts string) time.Time {
|
|
t, err := time.Parse(time.RFC3339, ts)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return t
|
|
}
|
|
|
|
func copyIntegerArray(src *cursors.IntegerArray) *cursors.IntegerArray {
|
|
dst := cursors.NewIntegerArrayLen(src.Len())
|
|
copy(dst.Timestamps, src.Timestamps)
|
|
copy(dst.Values, src.Values)
|
|
return dst
|
|
}
|
|
|
|
func copyFloatArray(src *cursors.FloatArray) *cursors.FloatArray {
|
|
dst := cursors.NewFloatArrayLen(src.Len())
|
|
copy(dst.Timestamps, src.Timestamps)
|
|
copy(dst.Values, src.Values)
|
|
return dst
|
|
}
|
|
|
|
type aggArrayCursorTest struct {
|
|
name string
|
|
createCursorFn func(cur cursors.IntegerArrayCursor, every, offset int64, window execute.Window) cursors.Cursor
|
|
every time.Duration
|
|
offset time.Duration
|
|
inputArrays []*cursors.IntegerArray
|
|
wantIntegers []*cursors.IntegerArray
|
|
wantFloats []*cursors.FloatArray
|
|
window execute.Window
|
|
}
|
|
|
|
func (a *aggArrayCursorTest) run(t *testing.T) {
|
|
t.Helper()
|
|
t.Run(a.name, func(t *testing.T) {
|
|
var resultN int
|
|
mc := &MockIntegerArrayCursor{
|
|
CloseFunc: func() {},
|
|
ErrFunc: func() error { return nil },
|
|
StatsFunc: func() cursors.CursorStats { return cursors.CursorStats{} },
|
|
NextFunc: func() *cursors.IntegerArray {
|
|
if resultN < len(a.inputArrays) {
|
|
a := a.inputArrays[resultN]
|
|
resultN++
|
|
return a
|
|
}
|
|
return &cursors.IntegerArray{}
|
|
},
|
|
}
|
|
c := a.createCursorFn(mc, int64(a.every), int64(a.offset), a.window)
|
|
switch cursor := c.(type) {
|
|
case cursors.IntegerArrayCursor:
|
|
got := make([]*cursors.IntegerArray, 0, len(a.wantIntegers))
|
|
for a := cursor.Next(); a.Len() != 0; a = cursor.Next() {
|
|
got = append(got, copyIntegerArray(a))
|
|
}
|
|
|
|
if diff := cmp.Diff(got, a.wantIntegers); diff != "" {
|
|
t.Fatalf("did not get expected result from count array cursor; -got/+want:\n%v", diff)
|
|
}
|
|
case cursors.FloatArrayCursor:
|
|
got := make([]*cursors.FloatArray, 0, len(a.wantFloats))
|
|
for a := cursor.Next(); a.Len() != 0; a = cursor.Next() {
|
|
got = append(got, copyFloatArray(a))
|
|
}
|
|
|
|
if diff := cmp.Diff(got, a.wantFloats); diff != "" {
|
|
t.Fatalf("did not get expected result from count array cursor; -got/+want:\n%v", diff)
|
|
}
|
|
default:
|
|
t.Fatalf("unsupported cursor type: %T", cursor)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestWindowMeanArrayCursor(t *testing.T) {
|
|
maxTimestamp := time.Unix(0, math.MaxInt64)
|
|
|
|
testcases := []aggArrayCursorTest{
|
|
{
|
|
name: "no window",
|
|
every: 0,
|
|
inputArrays: []*cursors.IntegerArray{
|
|
makeIntegerArray(
|
|
5,
|
|
mustParseTime("2010-01-01T00:00:00Z"), time.Minute,
|
|
func(i int64) int64 { return i + 1 },
|
|
),
|
|
},
|
|
wantFloats: []*cursors.FloatArray{
|
|
makeFloatArray(1, maxTimestamp, 0, func(int64) float64 { return 3.0 }),
|
|
},
|
|
},
|
|
{
|
|
name: "no window fraction result",
|
|
every: 0,
|
|
inputArrays: []*cursors.IntegerArray{
|
|
makeIntegerArray(
|
|
6,
|
|
mustParseTime("2010-01-01T00:00:00Z"), time.Minute,
|
|
func(i int64) int64 { return i + 1 },
|
|
),
|
|
},
|
|
wantFloats: []*cursors.FloatArray{
|
|
makeFloatArray(1, maxTimestamp, 0, func(int64) float64 { return 3.5 }),
|
|
},
|
|
},
|
|
{
|
|
name: "no window empty",
|
|
every: 0,
|
|
inputArrays: []*cursors.IntegerArray{},
|
|
wantFloats: []*cursors.FloatArray{},
|
|
},
|
|
{
|
|
name: "window",
|
|
every: 30 * time.Minute,
|
|
inputArrays: []*cursors.IntegerArray{
|
|
makeIntegerArray(
|
|
8,
|
|
mustParseTime("2010-01-01T00:00:00Z"), 15*time.Minute,
|
|
func(i int64) int64 {
|
|
return i
|
|
},
|
|
),
|
|
},
|
|
wantFloats: []*cursors.FloatArray{
|
|
makeFloatArray(4, mustParseTime("2010-01-01T00:30:00Z"), 30*time.Minute,
|
|
func(i int64) float64 { return 0.5 + float64(i)*2 }),
|
|
},
|
|
},
|
|
{
|
|
name: "window offset",
|
|
every: 30 * time.Minute,
|
|
offset: 5 * time.Minute,
|
|
inputArrays: []*cursors.IntegerArray{
|
|
makeIntegerArray(
|
|
8,
|
|
mustParseTime("2010-01-01T00:00:00Z"), 15*time.Minute,
|
|
func(i int64) int64 {
|
|
return i
|
|
},
|
|
),
|
|
},
|
|
wantFloats: []*cursors.FloatArray{
|
|
makeFloatArray(5, mustParseTime("2010-01-01T00:05:00Z"), 30*time.Minute,
|
|
func(i int64) float64 { return []float64{0, 1.5, 3.5, 5.5, 7}[i] }),
|
|
},
|
|
},
|
|
{
|
|
name: "empty window",
|
|
every: 15 * time.Minute,
|
|
inputArrays: []*cursors.IntegerArray{
|
|
makeIntegerArray(
|
|
2,
|
|
mustParseTime("2010-01-01T00:05:00Z"), 30*time.Minute,
|
|
func(i int64) int64 {
|
|
return 100 + i
|
|
},
|
|
),
|
|
},
|
|
wantFloats: []*cursors.FloatArray{
|
|
makeFloatArray(2, mustParseTime("2010-01-01T00:15:00Z"), 30*time.Minute,
|
|
func(i int64) float64 { return 100 + float64(i) }),
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range testcases {
|
|
tc.createCursorFn = func(cur cursors.IntegerArrayCursor, every, offset int64, window execute.Window) cursors.Cursor {
|
|
if every != 0 || offset != 0 {
|
|
everyDur := values.MakeDuration(every, 0, false)
|
|
offsetDur := values.MakeDuration(offset, 0, false)
|
|
window = execute.Window{
|
|
Every: everyDur,
|
|
Offset: offsetDur,
|
|
}
|
|
}
|
|
return newIntegerWindowMeanArrayCursor(cur, window)
|
|
}
|
|
tc.run(t)
|
|
}
|
|
}
|
|
|
|
type MockExpression struct {
|
|
EvalBoolFunc func(v Valuer) bool
|
|
}
|
|
|
|
func (e *MockExpression) EvalBool(v Valuer) bool { return e.EvalBoolFunc(v) }
|