package tsdb import ( "reflect" "testing" "time" "github.com/davecgh/go-spew/spew" "github.com/influxdb/influxdb/influxql" ) import "sort" // type testPoint struct { // time int64 // value interface{} // fields map[string]interface{} // tags map[string]string // } func TestMapMeanNoValues(t *testing.T) { if got := MapMean(&MapInput{}); got != nil { t.Errorf("output mismatch: exp nil got %v", got) } } func TestMapMean(t *testing.T) { tests := []struct { input *MapInput output *meanMapOutput }{ { // Single point input: &MapInput{ Items: []MapItem{ {Timestamp: 1, Value: 1.0}, }, }, output: &meanMapOutput{1, 1, Float64Type}, }, { // Two points input: &MapInput{ Items: []MapItem{ {Timestamp: 1, Value: float64(2.0)}, {Timestamp: 2, Value: float64(8.0)}, }, }, output: &meanMapOutput{2, 10.0, Float64Type}, }, } for _, test := range tests { got := MapMean(test.input) if got == nil { t.Fatalf("MapMean(%v): output mismatch: exp %v got %v", test.input, test.output, got) } if got.(*meanMapOutput).Count != test.output.Count || got.(*meanMapOutput).Total != test.output.Total { t.Errorf("output mismatch: exp %v got %v", test.output, got) } } } func TestInitializeMapFuncDerivative(t *testing.T) { for _, fn := range []string{"derivative", "non_negative_derivative"} { // Single field arg should return MapEcho c := &influxql.Call{ Name: fn, Args: []influxql.Expr{ &influxql.VarRef{Val: " field1"}, &influxql.DurationLiteral{Val: time.Hour}, }, } _, err := initializeMapFunc(c) if err != nil { t.Errorf("InitializeMapFunc(%v) unexpected error. got %v", c, err) } // Nested Aggregate func should return the map func for the nested aggregate c = &influxql.Call{ Name: fn, Args: []influxql.Expr{ &influxql.Call{Name: "mean", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}}}, &influxql.DurationLiteral{Val: time.Hour}, }, } _, err = initializeMapFunc(c) if err != nil { t.Errorf("InitializeMapFunc(%v) unexpected error. got %v", c, err) } } } func TestReducePercentileNil(t *testing.T) { input := []interface{}{ nil, } // ReducePercentile should ignore nil values when calculating the percentile got := ReducePercentile(input, 100) if got != nil { t.Fatalf("ReducePercentile(100) returned wrong type. exp nil got %v", got) } } func TestMapDistinct(t *testing.T) { const ( // prove that we're ignoring time timeId1 = iota + 1 timeId2 timeId3 timeId4 timeId5 timeId6 ) input := &MapInput{ Items: []MapItem{ {Timestamp: timeId1, Value: uint64(1)}, {Timestamp: timeId2, Value: uint64(1)}, {Timestamp: timeId3, Value: "1"}, {Timestamp: timeId4, Value: uint64(1)}, {Timestamp: timeId5, Value: float64(1.0)}, {Timestamp: timeId6, Value: "1"}, }, } values := MapDistinct(input).(InterfaceValues) if exp, got := 3, len(values); exp != got { t.Errorf("Wrong number of values. exp %v got %v", exp, got) } sort.Sort(values) exp := InterfaceValues{ "1", uint64(1), float64(1), } if !reflect.DeepEqual(values, exp) { t.Errorf("Wrong values. exp %v got %v", spew.Sdump(exp), spew.Sdump(values)) } } func TestMapDistinctNil(t *testing.T) { values := MapDistinct(&MapInput{}) if values != nil { t.Errorf("Wrong values. exp nil got %v", spew.Sdump(values)) } } func TestReduceDistinct(t *testing.T) { v1 := InterfaceValues{ "2", "1", float64(2.0), float64(1), uint64(2), uint64(1), true, false, } expect := InterfaceValues{ "1", "2", false, true, uint64(1), float64(1), uint64(2), float64(2), } got := ReduceDistinct([]interface{}{v1, v1, expect}) if !reflect.DeepEqual(got, expect) { t.Errorf("Wrong values. exp %v got %v", spew.Sdump(expect), spew.Sdump(got)) } } func TestReduceDistinctNil(t *testing.T) { tests := []struct { name string values []interface{} }{ { name: "nil values", values: nil, }, { name: "nil mapper", values: []interface{}{nil}, }, { name: "no mappers", values: []interface{}{}, }, { name: "empty mappper (len 1)", values: []interface{}{InterfaceValues{}}, }, { name: "empty mappper (len 2)", values: []interface{}{InterfaceValues{}, InterfaceValues{}}, }, } for _, test := range tests { t.Log(test.name) got := ReduceDistinct(test.values) if got != nil { t.Errorf("Wrong values. exp nil got %v", spew.Sdump(got)) } } } func Test_distinctValues_Sort(t *testing.T) { values := InterfaceValues{ "2", "1", float64(2.0), float64(1), uint64(2), uint64(1), true, false, } expect := InterfaceValues{ "1", "2", false, true, uint64(1), float64(1), uint64(2), float64(2), } sort.Sort(values) if !reflect.DeepEqual(values, expect) { t.Errorf("Wrong values. exp %v got %v", spew.Sdump(expect), spew.Sdump(values)) } } func TestMapCountDistinct(t *testing.T) { const ( // prove that we're ignoring time timeId1 = iota + 1 timeId2 timeId3 timeId4 timeId5 timeId6 timeId7 ) input := &MapInput{ Items: []MapItem{ {Timestamp: timeId1, Value: uint64(1)}, {Timestamp: timeId2, Value: uint64(1)}, {Timestamp: timeId3, Value: "1"}, {Timestamp: timeId4, Value: uint64(1)}, {Timestamp: timeId5, Value: float64(1.0)}, {Timestamp: timeId6, Value: "1"}, {Timestamp: timeId7, Value: true}, }, } values := MapCountDistinct(input).(map[interface{}]struct{}) if exp, got := 4, len(values); exp != got { t.Errorf("Wrong number of values. exp %v got %v", exp, got) } exp := map[interface{}]struct{}{ uint64(1): struct{}{}, float64(1): struct{}{}, "1": struct{}{}, true: struct{}{}, } if !reflect.DeepEqual(values, exp) { t.Errorf("Wrong values. exp %v got %v", spew.Sdump(exp), spew.Sdump(values)) } } func TestMapCountDistinctNil(t *testing.T) { if values := MapCountDistinct(&MapInput{}); values != nil { t.Errorf("Wrong values. exp nil got %v", spew.Sdump(values)) } } func TestReduceCountDistinct(t *testing.T) { v1 := map[interface{}]struct{}{ "2": struct{}{}, "1": struct{}{}, float64(2.0): struct{}{}, float64(1): struct{}{}, uint64(2): struct{}{}, uint64(1): struct{}{}, true: struct{}{}, false: struct{}{}, } v2 := map[interface{}]struct{}{ uint64(1): struct{}{}, float64(1): struct{}{}, uint64(2): struct{}{}, float64(2): struct{}{}, false: struct{}{}, true: struct{}{}, "1": struct{}{}, "2": struct{}{}, } exp := 8 got := ReduceCountDistinct([]interface{}{v1, v1, v2}) if !reflect.DeepEqual(got, exp) { t.Errorf("Wrong values. exp %v got %v", spew.Sdump(exp), spew.Sdump(got)) } } func TestReduceCountDistinctNil(t *testing.T) { emptyResults := make(map[interface{}]struct{}) tests := []struct { name string values []interface{} }{ { name: "nil values", values: nil, }, { name: "nil mapper", values: []interface{}{nil}, }, { name: "no mappers", values: []interface{}{}, }, { name: "empty mappper (len 1)", values: []interface{}{emptyResults}, }, { name: "empty mappper (len 2)", values: []interface{}{emptyResults, emptyResults}, }, } for _, test := range tests { t.Log(test.name) got := ReduceCountDistinct(test.values) if got != 0 { t.Errorf("Wrong values. exp nil got %v", spew.Sdump(got)) } } } var getSortedRangeData = []float64{ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, } var getSortedRangeTests = []struct { name string data []float64 start int count int expected []float64 }{ {"first 5", getSortedRangeData, 0, 5, []float64{0, 1, 2, 3, 4}}, {"0 length", getSortedRangeData, 8, 0, []float64{}}, {"past end of data", getSortedRangeData, len(getSortedRangeData) - 3, 5, []float64{67, 68, 69}}, } func TestGetSortedRange(t *testing.T) { for _, tt := range getSortedRangeTests { results := getSortedRange(tt.data, tt.start, tt.count) if len(results) != len(tt.expected) { t.Errorf("Test %s error. Expected getSortedRange to return %v but got %v", tt.name, tt.expected, results) } for i, testPoint := range tt.expected { if testPoint != results[i] { t.Errorf("Test %s error. getSortedRange returned wrong result for index %v. Expected %v but got %v", tt.name, i, testPoint, results[i]) } } } } var benchGetSortedRangeResults []float64 func BenchmarkGetSortedRangeByPivot(b *testing.B) { data := make([]float64, len(getSortedRangeData)) var results []float64 for i := 0; i < b.N; i++ { copy(data, getSortedRangeData) results = getSortedRange(data, 8, 15) } benchGetSortedRangeResults = results } func BenchmarkGetSortedRangeBySort(b *testing.B) { data := make([]float64, len(getSortedRangeData)) var results []float64 for i := 0; i < b.N; i++ { copy(data, getSortedRangeData) sort.Float64s(data) results = data[8:23] } benchGetSortedRangeResults = results } func TestMapTopBottom(t *testing.T) { tests := []struct { name string skip bool input *MapInput exp positionOut call *influxql.Call }{ { name: "top int64 - basic", input: &MapInput{ TMin: -1, Items: []MapItem{ {Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}}, {Timestamp: 20, Value: int64(88), Tags: map[string]string{"host": "a"}}, }, }, exp: positionOut{ points: PositionPoints{ {20, int64(88), nil, map[string]string{"host": "a"}}, {10, int64(53), nil, map[string]string{"host": "a"}}, }, }, call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "top int64 - tie on value, resolve based on time", input: &MapInput{ TMin: -1, Items: []MapItem{ {Timestamp: 20, Value: int64(99), Tags: map[string]string{"host": "a"}}, {Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}}, {Timestamp: 10, Value: int64(99), Tags: map[string]string{"host": "a"}}, }, }, exp: positionOut{ callArgs: []string{"host"}, points: PositionPoints{ {10, int64(99), nil, map[string]string{"host": "a"}}, {20, int64(99), nil, map[string]string{"host": "a"}}, }, }, call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "top mixed numerics - ints", input: &MapInput{ TMin: -1, Items: []MapItem{ {Timestamp: 10, Value: int64(99), Tags: map[string]string{"host": "a"}}, {Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}}, {Timestamp: 20, Value: uint64(88), Tags: map[string]string{"host": "a"}}, }, }, exp: positionOut{ points: PositionPoints{ {10, int64(99), nil, map[string]string{"host": "a"}}, {20, uint64(88), nil, map[string]string{"host": "a"}}, }, }, call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "top mixed numerics - ints & floats", input: &MapInput{ TMin: -1, Items: []MapItem{ {Timestamp: 10, Value: float64(99), Tags: map[string]string{"host": "a"}}, {Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}}, {Timestamp: 20, Value: uint64(88), Tags: map[string]string{"host": "a"}}, }, }, exp: positionOut{ points: PositionPoints{ {10, float64(99), nil, map[string]string{"host": "a"}}, {20, uint64(88), nil, map[string]string{"host": "a"}}, }, }, call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "top mixed numerics - ints, floats, & strings", input: &MapInput{ TMin: -1, Items: []MapItem{ {Timestamp: 10, Value: float64(99), Tags: map[string]string{"host": "a"}}, {Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}}, {Timestamp: 20, Value: "88", Tags: map[string]string{"host": "a"}}, }, }, exp: positionOut{ points: PositionPoints{ {10, float64(99), nil, map[string]string{"host": "a"}}, {10, int64(53), nil, map[string]string{"host": "a"}}, }, }, call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "top bools", input: &MapInput{ TMin: -1, Items: []MapItem{ {Timestamp: 10, Value: true, Tags: map[string]string{"host": "a"}}, {Timestamp: 10, Value: true, Tags: map[string]string{"host": "a"}}, {Timestamp: 20, Value: false, Tags: map[string]string{"host": "a"}}, }, }, exp: positionOut{ points: PositionPoints{ {10, true, nil, map[string]string{"host": "a"}}, {10, true, nil, map[string]string{"host": "a"}}, }, }, call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "bottom int64 - basic", input: &MapInput{ TMin: -1, Items: []MapItem{ {Timestamp: 10, Value: int64(99), Tags: map[string]string{"host": "a"}}, {Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}}, {Timestamp: 20, Value: int64(88), Tags: map[string]string{"host": "a"}}, }, }, exp: positionOut{ points: PositionPoints{ {10, int64(53), nil, map[string]string{"host": "a"}}, {20, int64(88), nil, map[string]string{"host": "a"}}, }, }, call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "bottom int64 - tie on value, resolve based on time", input: &MapInput{ TMin: -1, Items: []MapItem{ {Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}}, {Timestamp: 20, Value: int64(53), Tags: map[string]string{"host": "a"}}, {Timestamp: 20, Value: int64(53), Tags: map[string]string{"host": "a"}}, }, }, exp: positionOut{ callArgs: []string{"host"}, points: PositionPoints{ {10, int64(53), nil, map[string]string{"host": "a"}}, {20, int64(53), nil, map[string]string{"host": "a"}}, }, }, call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "bottom mixed numerics - ints", input: &MapInput{ TMin: -1, Items: []MapItem{ {Timestamp: 10, Value: int64(99), Tags: map[string]string{"host": "a"}}, {Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}}, {Timestamp: 20, Value: uint64(88), Tags: map[string]string{"host": "a"}}, }, }, exp: positionOut{ points: PositionPoints{ {10, int64(53), nil, map[string]string{"host": "a"}}, {20, uint64(88), nil, map[string]string{"host": "a"}}, }, }, call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "bottom mixed numerics - ints & floats", input: &MapInput{ TMin: -1, Items: []MapItem{ {Timestamp: 10, Value: int64(99), Tags: map[string]string{"host": "a"}}, {Timestamp: 10, Value: float64(53), Tags: map[string]string{"host": "a"}}, {Timestamp: 20, Value: uint64(88), Tags: map[string]string{"host": "a"}}, }, }, exp: positionOut{ points: PositionPoints{ {10, float64(53), nil, map[string]string{"host": "a"}}, {20, uint64(88), nil, map[string]string{"host": "a"}}, }, }, call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "bottom mixed numerics - ints, floats, & strings", input: &MapInput{ TMin: -1, Items: []MapItem{ {Timestamp: 10, Value: float64(99), Tags: map[string]string{"host": "a"}}, {Timestamp: 10, Value: int64(53), Tags: map[string]string{"host": "a"}}, {Timestamp: 20, Value: "88", Tags: map[string]string{"host": "a"}}, }, }, exp: positionOut{ points: PositionPoints{ {10, int64(53), nil, map[string]string{"host": "a"}}, {10, float64(99), nil, map[string]string{"host": "a"}}, }, }, call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "bottom bools", input: &MapInput{ TMin: -1, Items: []MapItem{ {Timestamp: 10, Value: true, Tags: map[string]string{"host": "a"}}, {Timestamp: 10, Value: true, Tags: map[string]string{"host": "a"}}, {Timestamp: 20, Value: false, Tags: map[string]string{"host": "a"}}, }, }, exp: positionOut{ points: PositionPoints{ {20, false, nil, map[string]string{"host": "a"}}, {10, true, nil, map[string]string{"host": "a"}}, }, }, call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, } for _, test := range tests { if test.skip { continue } lit, _ := test.call.Args[len(test.call.Args)-1].(*influxql.NumberLiteral) limit := int(lit.Val) fields := topCallArgs(test.call) values := MapTopBottom(test.input, limit, fields, len(test.call.Args), test.call.Name).(PositionPoints) t.Logf("Test: %s", test.name) if exp, got := len(test.exp.points), len(values); exp != got { t.Errorf("Wrong number of values. exp %v got %v", exp, got) } if !reflect.DeepEqual(values, test.exp.points) { t.Errorf("Wrong values. \nexp\n %v\ngot\n %v", spew.Sdump(test.exp.points), spew.Sdump(values)) } } } func TestReduceTopBottom(t *testing.T) { tests := []struct { name string skip bool values []interface{} exp PositionPoints call *influxql.Call }{ { name: "top int64 - single map", values: []interface{}{ PositionPoints{ {10, int64(99), nil, map[string]string{"host": "a"}}, {20, int64(88), nil, map[string]string{"host": "a"}}, {10, int64(53), nil, map[string]string{"host": "b"}}, }, }, exp: PositionPoints{ PositionPoint{10, int64(99), nil, map[string]string{"host": "a"}}, PositionPoint{20, int64(88), nil, map[string]string{"host": "a"}}, }, call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "top int64 - double map", values: []interface{}{ PositionPoints{ {10, int64(99), nil, map[string]string{"host": "a"}}, }, PositionPoints{ {20, int64(88), nil, map[string]string{"host": "a"}}, {10, int64(53), nil, map[string]string{"host": "b"}}, }, }, exp: PositionPoints{ PositionPoint{10, int64(99), nil, map[string]string{"host": "a"}}, PositionPoint{20, int64(88), nil, map[string]string{"host": "a"}}, }, call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "top int64 - double map with nil", values: []interface{}{ PositionPoints{ {10, int64(99), nil, map[string]string{"host": "a"}}, {20, int64(88), nil, map[string]string{"host": "a"}}, {10, int64(53), nil, map[string]string{"host": "b"}}, }, nil, }, exp: PositionPoints{ PositionPoint{10, int64(99), nil, map[string]string{"host": "a"}}, PositionPoint{20, int64(88), nil, map[string]string{"host": "a"}}, }, call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "top int64 - double map with non-matching tags and tag selected", values: []interface{}{ PositionPoints{ {10, int64(99), nil, map[string]string{"host": "a"}}, {20, int64(88), nil, map[string]string{}}, {10, int64(53), nil, map[string]string{"host": "b"}}, }, nil, }, exp: PositionPoints{ PositionPoint{10, int64(99), nil, map[string]string{"host": "a"}}, PositionPoint{20, int64(88), nil, map[string]string{}}, }, call: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.VarRef{Val: "host"}, &influxql.NumberLiteral{Val: 2}}}, }, { skip: true, name: "top int64 - double map with non-matching tags", values: []interface{}{ PositionPoints{ {10, int64(99), nil, map[string]string{"host": "a"}}, {20, int64(88), nil, map[string]string{}}, {10, int64(53), nil, map[string]string{"host": "b"}}, }, nil, }, exp: PositionPoints{ PositionPoint{10, int64(99), nil, map[string]string{"host": "a"}}, PositionPoint{20, int64(55), nil, map[string]string{"host": "b"}}, }, call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "bottom int64 - single map", values: []interface{}{ PositionPoints{ {10, int64(53), nil, map[string]string{"host": "b"}}, {20, int64(88), nil, map[string]string{"host": "a"}}, {10, int64(99), nil, map[string]string{"host": "a"}}, }, }, exp: PositionPoints{ PositionPoint{10, int64(53), nil, map[string]string{"host": "b"}}, PositionPoint{20, int64(88), nil, map[string]string{"host": "a"}}, }, call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "bottom int64 - double map", values: []interface{}{ PositionPoints{ {10, int64(99), nil, map[string]string{"host": "a"}}, }, PositionPoints{ {10, int64(53), nil, map[string]string{"host": "b"}}, {20, int64(88), nil, map[string]string{"host": "a"}}, }, }, exp: PositionPoints{ PositionPoint{10, int64(53), nil, map[string]string{"host": "b"}}, PositionPoint{20, int64(88), nil, map[string]string{"host": "a"}}, }, call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "bottom int64 - double map with nil", values: []interface{}{ PositionPoints{ {10, int64(53), nil, map[string]string{"host": "b"}}, {20, int64(88), nil, map[string]string{"host": "a"}}, {10, int64(99), nil, map[string]string{"host": "a"}}, }, nil, }, exp: PositionPoints{ PositionPoint{10, int64(53), nil, map[string]string{"host": "b"}}, PositionPoint{20, int64(88), nil, map[string]string{"host": "a"}}, }, call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, { name: "bottom int64 - double map with non-matching tags and tag selected", values: []interface{}{ PositionPoints{ {10, int64(53), nil, map[string]string{"host": "b"}}, {20, int64(88), nil, map[string]string{}}, {10, int64(99), nil, map[string]string{"host": "a"}}, }, nil, }, exp: PositionPoints{ PositionPoint{10, int64(53), nil, map[string]string{"host": "b"}}, PositionPoint{20, int64(88), nil, map[string]string{}}, }, call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.VarRef{Val: "host"}, &influxql.NumberLiteral{Val: 2}}}, }, { skip: true, name: "bottom int64 - double map with non-matching tags", values: []interface{}{ PositionPoints{ {10, int64(53), nil, map[string]string{"host": "b"}}, {20, int64(88), nil, map[string]string{}}, {10, int64(99), nil, map[string]string{"host": "a"}}, }, nil, }, exp: PositionPoints{ PositionPoint{10, int64(99), nil, map[string]string{"host": "a"}}, PositionPoint{20, int64(55), nil, map[string]string{"host": "b"}}, }, call: &influxql.Call{Name: "bottom", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}, }, } for _, test := range tests { if test.skip { continue } lit, _ := test.call.Args[len(test.call.Args)-1].(*influxql.NumberLiteral) limit := int(lit.Val) fields := topCallArgs(test.call) values := ReduceTopBottom(test.values, limit, fields, test.call.Name) t.Logf("Test: %s", test.name) if values != nil { v, _ := values.(PositionPoints) if exp, got := len(test.exp), len(v); exp != got { t.Errorf("Wrong number of values. exp %v got %v", exp, got) } } if !reflect.DeepEqual(values, test.exp) { t.Errorf("Wrong values. \nexp\n %v\ngot\n %v", spew.Sdump(test.exp), spew.Sdump(values)) } } }