github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/graphite/native/builtin_functions_test.go (about) 1 // Copyright (c) 2019 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package native 22 23 import ( 24 "context" 25 "fmt" 26 "math" 27 "math/rand" 28 "testing" 29 "time" 30 31 "github.com/m3db/m3/src/query/block" 32 "github.com/m3db/m3/src/query/graphite/common" 33 xctx "github.com/m3db/m3/src/query/graphite/context" 34 "github.com/m3db/m3/src/query/graphite/storage" 35 xtest "github.com/m3db/m3/src/query/graphite/testing" 36 "github.com/m3db/m3/src/query/graphite/ts" 37 querystorage "github.com/m3db/m3/src/query/storage" 38 "github.com/m3db/m3/src/query/storage/m3/consolidators" 39 xgomock "github.com/m3db/m3/src/x/test" 40 41 "github.com/golang/mock/gomock" 42 "github.com/stretchr/testify/assert" 43 "github.com/stretchr/testify/require" 44 ) 45 46 var ( 47 // testInput defines the input for various tests 48 testInput = []common.TestSeries{ 49 {"foo", []float64{0, 601, 3, 4}}, 50 {"nan", []float64{math.NaN(), math.NaN(), math.NaN()}}, 51 {"bar", []float64{500, -8}}, 52 {"baz", []float64{600, -600, 3}}, 53 {"quux", []float64{100, 50000, 888, -1, -2, math.NaN()}}, 54 } 55 56 // testSmallInput defines a small input for various tests 57 testSmallInput = []common.TestSeries{ 58 testInput[0], 59 testInput[2], 60 } 61 62 // testInputWithNaNSeries defines another input set with all-nan series 63 testInputWithNaNSeries = []common.TestSeries{ 64 testInput[0], 65 testInput[2], 66 testInput[4], 67 {"allNaN", []float64{math.NaN(), math.NaN()}}, 68 } 69 ) 70 71 func TestExclude(t *testing.T) { 72 ctx := common.NewTestContext() 73 defer func() { _ = ctx.Close() }() 74 75 now := time.Now() 76 values := ts.NewConstantValues(ctx, 10.0, 1000, 10) 77 78 g01 := ts.NewSeries(ctx, "servers.graphite01-foo.disk.bar.available_bytes", now, values) 79 g02 := ts.NewSeries(ctx, "servers.graphite02-foo.disk.bar.available_bytes", now, values) 80 g03 := ts.NewSeries(ctx, "servers.graphite03-foo.disk.bar.available_bytes", now, values) 81 82 sampleInput := []*ts.Series{g01, g02, g03} 83 sampleOutput := []*ts.Series{g01, g03} 84 tests := []struct { 85 inputs []*ts.Series 86 r string 87 n int 88 outputs []*ts.Series 89 }{ 90 { 91 sampleInput, 92 "graphite02-foo", 93 2, 94 sampleOutput, 95 }, 96 { 97 sampleInput, 98 "graphite", 99 0, 100 []*ts.Series{}, 101 }, 102 { 103 sampleInput, 104 "graphite.*-foo", 105 0, 106 []*ts.Series{}, 107 }, 108 } 109 110 for _, test := range tests { 111 results, err := exclude(nil, singlePathSpec{ 112 Values: test.inputs, 113 }, test.r) 114 require.Nil(t, err) 115 require.NotNil(t, results) 116 require.Equal(t, test.n, results.Len()) 117 for i := range results.Values { 118 assert.Equal(t, sampleOutput[i].Name(), results.Values[i].Name()) 119 } 120 } 121 } 122 123 func TestExcludeErr(t *testing.T) { 124 ctx := common.NewTestContext() 125 defer func() { _ = ctx.Close() }() 126 127 now := time.Now() 128 values := ts.NewConstantValues(ctx, 10.0, 1000, 10) 129 130 series := []*ts.Series{ 131 ts.NewSeries(ctx, "anything", now, values), 132 } 133 results, err := exclude(ctx, singlePathSpec{ 134 Values: series, 135 }, "(") 136 require.Error(t, err, "Failure is expected") 137 require.Nil(t, results.Values) 138 } 139 140 func TestGrep(t *testing.T) { 141 ctx := common.NewTestContext() 142 defer func() { _ = ctx.Close() }() 143 144 now := time.Now() 145 values := ts.NewConstantValues(ctx, 10.0, 5, 10) 146 147 series1 := ts.NewSeries(ctx, "collectd.test-db1.load.value", now, values) 148 series2 := ts.NewSeries(ctx, "collectd.test-db2.load.value", now, values) 149 series3 := ts.NewSeries(ctx, "collectd.test-db3.load.value", now, values) 150 series4 := ts.NewSeries(ctx, "collectd.test-db4.load.value", now, values) 151 152 testInputs := []*ts.Series{series1, series2, series3, series4} 153 expectedOutput := []common.TestSeries{ 154 { 155 Name: "collectd.test-db1.load.value", 156 Data: []float64{10.0, 10.0, 10.0, 10.0, 10.0}, 157 }, 158 { 159 Name: "collectd.test-db2.load.value", 160 Data: []float64{10.0, 10.0, 10.0, 10.0, 10.0}, 161 }, 162 } 163 164 results, err := grep(nil, singlePathSpec{ 165 Values: testInputs, 166 }, ".*db[12]") 167 require.Nil(t, err) 168 require.NotNil(t, results) 169 common.CompareOutputsAndExpected(t, 10, now, expectedOutput, results.Values) 170 171 // error case 172 _, err = grep(nil, singlePathSpec{ 173 Values: testInputs, 174 }, "+++++") 175 require.NotNil(t, err) 176 } 177 178 func TestSortByName(t *testing.T) { 179 ctx := common.NewTestContext() 180 defer func() { _ = ctx.Close() }() 181 182 now := time.Now() 183 values := ts.NewConstantValues(ctx, 10.0, 1000, 10) 184 185 series := []*ts.Series{ 186 ts.NewSeries(ctx, "b.d.a", now, values), 187 ts.NewSeries(ctx, "zee", now, values), 188 ts.NewSeries(ctx, "a.c.d", now, values), 189 } 190 191 // Normal. 192 results, err := sortByName(ctx, singlePathSpec{ 193 Values: series, 194 }, false, false) 195 require.Nil(t, err) 196 require.Equal(t, len(series), results.Len()) 197 assert.Equal(t, "a.c.d", results.Values[0].Name()) 198 assert.Equal(t, "b.d.a", results.Values[1].Name()) 199 assert.Equal(t, "zee", results.Values[2].Name()) 200 201 // Reverse. 202 results, err = sortByName(ctx, singlePathSpec{ 203 Values: series, 204 }, false, true) 205 require.Nil(t, err) 206 require.Equal(t, len(series), results.Len()) 207 assert.Equal(t, "zee", results.Values[0].Name()) 208 assert.Equal(t, "b.d.a", results.Values[1].Name()) 209 assert.Equal(t, "a.c.d", results.Values[2].Name()) 210 } 211 212 func TestSortByNameNatural(t *testing.T) { 213 ctx := common.NewTestContext() 214 defer func() { _ = ctx.Close() }() 215 216 now := time.Now() 217 values := ts.NewConstantValues(ctx, 10.0, 1000, 10) 218 219 series := []*ts.Series{ 220 ts.NewSeries(ctx, "server1", now, values), 221 ts.NewSeries(ctx, "server11", now, values), 222 ts.NewSeries(ctx, "server12", now, values), 223 ts.NewSeries(ctx, "server2", now, values), 224 } 225 226 results, err := sortByName(ctx, singlePathSpec{ 227 Values: series, 228 }, true, false) 229 require.Nil(t, err) 230 require.Equal(t, len(series), results.Len()) 231 assert.Equal(t, "server1", results.Values[0].Name()) 232 assert.Equal(t, "server2", results.Values[1].Name()) 233 assert.Equal(t, "server11", results.Values[2].Name()) 234 assert.Equal(t, "server12", results.Values[3].Name()) 235 } 236 237 func getTestInput(ctx *common.Context) []*ts.Series { 238 series := make([]*ts.Series, len(testInput)) 239 now := time.Now() 240 for idx, s := range testInput { 241 series[idx] = ts.NewSeries(ctx, s.Name, now, common.NewTestSeriesValues(ctx, 100, s.Data)) 242 } 243 return series 244 } 245 246 func testSortingFuncs( 247 t *testing.T, 248 f func(ctx *common.Context, series singlePathSpec) (ts.SeriesList, error), 249 resultIndexes []int, 250 ) { 251 ctx := common.NewTestContext() 252 defer func() { _ = ctx.Close() }() 253 254 input := getTestInput(ctx) 255 results, err := f(ctx, singlePathSpec{Values: input}) 256 require.Nil(t, err) 257 require.Equal(t, len(resultIndexes), results.Len()) 258 259 expected := make([]string, 0, len(input)) 260 for _, idx := range resultIndexes { 261 expected = append(expected, input[idx].Name()) 262 } 263 264 actual := make([]string, 0, len(input)) 265 for _, s := range results.Values { 266 actual = append(actual, s.Name()) 267 } 268 require.Equal(t, expected, actual) 269 270 for i, idx := range resultIndexes { 271 require.Equal(t, results.Values[i], input[idx]) 272 } 273 } 274 275 func TestSortBy(t *testing.T) { 276 for _, test := range []struct { 277 fn string 278 indices []int 279 }{ 280 {fn: "average", indices: []int{4, 2, 0, 3, 1}}, 281 {fn: "sum", indices: []int{4, 0, 2, 3, 1}}, 282 {fn: "max", indices: []int{4, 0, 3, 2, 1}}, 283 {fn: "min", indices: []int{1, 3, 2, 4, 0}}, 284 } { 285 t.Run(test.fn, func(t *testing.T) { 286 // Regular. 287 expected := test.indices 288 testSortingFuncs(t, 289 func(ctx *common.Context, series singlePathSpec) (ts.SeriesList, error) { 290 return sortBy(ctx, series, test.fn, false) 291 }, 292 expected) 293 294 // Reversed. 295 for i, j := 0, len(expected)-1; i < j; i, j = i+1, j-1 { 296 expected[i], expected[j] = expected[j], expected[i] 297 } 298 testSortingFuncs(t, 299 func(ctx *common.Context, series singlePathSpec) (ts.SeriesList, error) { 300 return sortBy(ctx, series, test.fn, true) 301 }, 302 expected) 303 }) 304 } 305 } 306 307 func TestSortByTotal(t *testing.T) { 308 testSortingFuncs(t, sortByTotal, []int{4, 0, 2, 3, 1}) 309 } 310 311 func TestSortByMaxima(t *testing.T) { 312 testSortingFuncs(t, sortByMaxima, []int{4, 0, 3, 2, 1}) 313 } 314 315 func TestSortByMinima(t *testing.T) { 316 testSortingFuncs(t, sortByMinima, []int{1, 3, 2, 4, 0}) 317 } 318 319 func TestAbsolute(t *testing.T) { 320 ctx := common.NewTestContext() 321 defer func() { _ = ctx.Close() }() 322 323 inputVals := []float64{-2, 0, 42, math.NaN()} 324 outputVals := []float64{2, 0, 42, math.NaN()} 325 start := time.Now() 326 327 input := ts.NewSeries(ctx, "foo", start, common.NewTestSeriesValues(ctx, 100, inputVals)) 328 r, err := absolute(ctx, singlePathSpec{ 329 Values: []*ts.Series{input}, 330 }) 331 require.NoError(t, err) 332 333 outputs := r.Values 334 require.Equal(t, 1, len(outputs)) 335 require.Equal(t, 100, outputs[0].MillisPerStep()) 336 require.Equal(t, len(outputVals), outputs[0].Len()) 337 require.Equal(t, start, outputs[0].StartTime()) 338 assert.Equal(t, "absolute(foo)", outputs[0].Name()) 339 340 for step := 0; step < outputs[0].Len(); step++ { 341 v := outputs[0].ValueAt(step) 342 xtest.Equalish(t, outputVals[step], v, "invalid value for %d", step) 343 } 344 } 345 346 func TestScale(t *testing.T) { 347 ctx := common.NewTestContext() 348 defer func() { _ = ctx.Close() }() 349 350 tests := []struct { 351 inputs []float64 352 scale float64 353 outputs []float64 354 }{ 355 { 356 []float64{0, 1.0, 2.0, math.NaN(), 3.0}, 357 2.5, 358 []float64{0, 2.5, 5.0, math.NaN(), 7.5}, 359 }, 360 { 361 []float64{0, 1.0, 2.0, math.NaN(), 3.0}, 362 0.5, 363 []float64{0, 0.5, 1.0, math.NaN(), 1.5}, 364 }, 365 } 366 367 start := time.Now() 368 for _, test := range tests { 369 input := ts.NewSeries(ctx, "foo", start, common.NewTestSeriesValues(ctx, 100, test.inputs)) 370 r, err := scale(ctx, singlePathSpec{ 371 Values: []*ts.Series{input}, 372 }, test.scale) 373 require.NoError(t, err) 374 375 outputs := r.Values 376 require.Equal(t, 1, len(outputs)) 377 require.Equal(t, 100, outputs[0].MillisPerStep()) 378 require.Equal(t, len(test.inputs), outputs[0].Len()) 379 require.Equal(t, start, outputs[0].StartTime()) 380 assert.Equal(t, fmt.Sprintf("scale(foo,"+common.FloatingPointFormat+")", test.scale), outputs[0].Name()) 381 382 for step := 0; step < outputs[0].Len(); step++ { 383 v := outputs[0].ValueAt(step) 384 xtest.Equalish(t, test.outputs[step], v, "invalid value for %d", step) 385 } 386 } 387 } 388 389 func TestUseSeriesAbove(t *testing.T) { 390 var ( 391 ctrl = xgomock.NewController(t) 392 store = storage.NewMockStorage(ctrl) 393 now = time.Now().Truncate(time.Hour) 394 engine = NewEngine(store, CompileOptions{}) 395 startTime = now.Add(-3 * time.Minute) 396 endTime = now.Add(-time.Minute) 397 ctx = common.NewContext(common.ContextOptions{Start: startTime, End: endTime, Engine: engine}) 398 stepSize = 60000 399 ) 400 401 defer ctrl.Finish() 402 defer func() { _ = ctx.Close() }() 403 404 store.EXPECT().FetchByQuery(gomock.Any(), "foo.bar.q.zed", gomock.Any()).DoAndReturn( 405 buildTestSeriesFn(stepSize, "foo.bar.q.zed")) 406 store.EXPECT().FetchByQuery(gomock.Any(), "foo.bar.g.zed", gomock.Any()).DoAndReturn( 407 buildTestSeriesFn(stepSize, "foo.bar.g.zed")) 408 store.EXPECT().FetchByQuery(gomock.Any(), "foo.bar.x.zed", gomock.Any()).DoAndReturn( 409 buildTestSeriesFn(stepSize, "foo.bar.x.zed")).Times(2) 410 store.EXPECT().FetchByQuery(gomock.Any(), "foo.bar.g.zed.g", gomock.Any()).Return( 411 &storage.FetchResult{SeriesList: []*ts.Series{ts.NewSeries(ctx, "foo.bar.g.zed.g", startTime, 412 common.NewTestSeriesValues(ctx, 60000, []float64{10, 20, 30}))}}, nil) 413 store.EXPECT().FetchByQuery(gomock.Any(), "foo.bar.q.zed.q", gomock.Any()).Return( 414 &storage.FetchResult{SeriesList: []*ts.Series{ts.NewSeries(ctx, "foo.bar.q.zed.q", startTime, 415 common.NewTestSeriesValues(ctx, 60000, []float64{1, 2, 3}))}}, nil) 416 417 tests := []struct { 418 target string 419 expected common.TestSeries 420 }{ 421 { 422 "useSeriesAbove(foo.bar.q.zed, -1, 'q', 'g')", 423 common.TestSeries{ 424 Name: "foo.bar.g.zed", 425 Data: []float64{1.0, 1.0}, 426 }, 427 }, 428 // two replacements 429 { 430 "useSeriesAbove(foo.bar.g.zed.g, 15, 'g', 'q')", 431 common.TestSeries{ 432 Name: "foo.bar.q.zed.q", 433 Data: []float64{1.0, 2.0, 3.0}, 434 }, 435 }, 436 // no replacments 437 { 438 "useSeriesAbove(foo.bar.x.zed, 1, 'p', 'g')", 439 common.TestSeries{ 440 Name: "foo.bar.x.zed", 441 Data: []float64{2.0, 2.0}, 442 }, 443 }, 444 } 445 446 for _, test := range tests { 447 expr, err := engine.Compile(test.target) 448 require.NoError(t, err) 449 res, err := expr.Execute(ctx) 450 require.NoError(t, err) 451 common.CompareOutputsAndExpected(t, stepSize, startTime, 452 []common.TestSeries{test.expected}, res.Values) 453 } 454 } 455 456 func TestPercentileOfSeriesErrors(t *testing.T) { 457 ctx := common.NewTestContext() 458 459 tests := []struct { 460 stepPerMillis []int 461 percentile float64 462 values [][]float64 463 expectedValues []float64 464 expectedStepPerMillis float64 465 interpolate genericInterface 466 }{ 467 { // percentile is over 100%. 468 []int{120, 120}, 469 101.0, 470 [][]float64{ 471 {60.0, 50.0, 40.0, 30.0, 20.0, 10.0}, 472 {6, 5, 4, 3, 2, 1}, 473 }, 474 []float64{5.5, 11.0, 16.5, 22.0, 27.5, 33.0}, 475 120, 476 "true", 477 }, 478 { // percentile is less than zero. 479 []int{120, 120}, 480 -10.0, 481 [][]float64{ 482 {60.0, 50.0, 40.0, 30.0, 20.0, 10.0}, 483 {6, 5, 4, 3, 2, 1}, 484 }, 485 []float64{5.5, 11.0, 16.5, 22.0, 27.5, 33.0}, 486 120, 487 "true", 488 }, 489 { // percentile input is empty. 490 []int{120, 120}, 491 10.0, 492 [][]float64{}, 493 []float64{}, 494 120, 495 "true", 496 }, 497 { // percentile series have different size millisPerStep. 498 []int{120, 320}, 499 33.0, 500 [][]float64{ 501 {60.0, 50.0, 40.0, 30.0, 20.0, 10.0}, 502 {6, 5, 4, 3, 2, 1}, 503 }, 504 []float64{5.5, 11.0, 16.5, 22.0, 27.5, 33.0}, 505 960, 506 "true", 507 }, 508 { // interpolateStr is neither "true" nor "false". 509 []int{120, 320}, 510 33.0, 511 [][]float64{ 512 {60.0, 50.0, 40.0, 30.0, 20.0, 10.0}, 513 {6, 5, 4, 3, 2, 1}, 514 }, 515 []float64{5.5, 11.0, 16.5, 22.0, 27.5, 33.0}, 516 960, 517 "random", 518 }, 519 { // types other than boolean and string are not allowed 520 []int{120, 120, 120, 120, 120}, 521 33.0, 522 [][]float64{ 523 {math.NaN(), 16, 23, math.NaN(), 75, 48, 42, 41}, 524 {math.NaN(), 36, 74, 43, 73}, 525 {math.NaN(), 61, 24, 29, math.NaN(), 62, 65, 72}, 526 {math.NaN(), 48, 94, math.NaN(), 32, 39, math.NaN(), 84}, 527 {math.NaN(), 16, math.NaN(), 85, 34, 27, 74, math.NaN(), 72}, 528 }, 529 []float64{math.NaN(), 16, 24, 43, 34}, 530 120, 531 []*ts.Series(nil), 532 }, 533 } 534 535 for _, test := range tests { 536 seriesList := make([]*ts.Series, len(test.values)) 537 for i := 0; i < len(seriesList); i++ { 538 seriesList[i] = ts.NewSeries(ctx, "<values>", time.Now(), common.NewTestSeriesValues(ctx, test.stepPerMillis[i], test.values[i])) 539 } 540 541 _, err := percentileOfSeries(ctx, singlePathSpec{ 542 Values: seriesList, 543 }, test.percentile, test.interpolate) 544 assert.NotNil(t, err) 545 } 546 } 547 548 func TestPercentileOfSeries(t *testing.T) { 549 ctx := common.NewTestContext() 550 551 tests := []struct { 552 stepPerMillis []int 553 percentile float64 554 values [][]float64 555 expectedValues []float64 556 expectedStepPerMillis float64 557 interpolate genericInterface 558 }{ 559 { // Test arrays with NaNs, multiple series, and same time step. 560 []int{120, 120, 120, 120, 120}, 561 33, 562 [][]float64{ 563 {math.NaN(), 16, 23, math.NaN(), 75, 48, 42, 41}, 564 {math.NaN(), 36, 74, 43, 73}, 565 {math.NaN(), 61, 24, 29, math.NaN(), 62, 65, 72}, 566 {math.NaN(), 48, 94, math.NaN(), 32, 39, math.NaN(), 84}, 567 {math.NaN(), 16, math.NaN(), 85, 34, 27, 74, math.NaN(), 72}, 568 }, 569 []float64{math.NaN(), 16, 24, 43, 34}, 570 120, 571 "false", 572 }, 573 { // Test arrays with NaNs, multiple series, and same time step. 574 []int{120, 120, 120, 120, 120}, 575 33, 576 [][]float64{ 577 {math.NaN(), 16, 23, math.NaN(), 75, 48, 42, 41}, 578 {math.NaN(), 36, 74, 43, 73}, 579 {math.NaN(), 61, 24, 29, math.NaN(), 62, 65, 72}, 580 {math.NaN(), 48, 94, math.NaN(), 32, 39, math.NaN(), 84}, 581 {math.NaN(), 16, math.NaN(), 85, 34, 27, 74, math.NaN(), 72}, 582 }, 583 []float64{math.NaN(), 16.0, 23.65, 33.480000000000004, 33.3}, 584 120, 585 "true", 586 }, 587 { // Test arrays with NaNs remove them and get correct percentile value 588 []int{120, 120, 120}, 589 5, 590 [][]float64{ 591 {math.NaN(), 60, 50, 40, math.NaN(), 30, 20, 10}, 592 {math.NaN(), 15, 12, 9, 6, 3, math.NaN()}, 593 {math.NaN(), 6, 5, 4, 3, 2, 1}, 594 }, 595 []float64{math.NaN(), 6, 5, 4, 3, 2, 1}, 596 120, 597 "false", 598 }, 599 { // Test non-interpolated percentile 600 []int{120, 120}, 601 42, 602 [][]float64{ 603 {60, 5, 40, 30, 20, 10}, 604 {3, 40, 4, 1, 2, 6}, 605 }, 606 []float64{60, 40, 40, 30, 20, 10}, 607 120, 608 "false", 609 }, 610 { // Test non-interpolated percentile for 100th percentile 611 []int{120, 120, 120}, 612 100, 613 [][]float64{ 614 {60, 50, 40, 30, 20, 10}, 615 {18, 15, 12, 9, 6, 3}, 616 {6, 5, 4, 3, 2, 1}, 617 }, 618 []float64{60, 50, 40, 30, 20, 10}, 619 120, 620 "false", 621 }, 622 { // Test non-interpolated percentile for 1st percentile 623 []int{120, 120}, 624 1, 625 [][]float64{ 626 {60, 50, 40, 30, 20, 10}, 627 {6, 5, 4, 3, 2, 1}, 628 }, 629 []float64{6, 5, 4, 3, 2, 1}, 630 120, 631 "false", 632 }, 633 { // Test interpolation for a percentile series 634 []int{120, 120}, 635 75, 636 [][]float64{ 637 {60, 50, 40, 30, 20, 10}, 638 {6, 5, 4, 3, 2, 1}, 639 }, 640 []float64{60, 50, 40, 30, 20, 10}, 641 120, 642 "true", 643 }, 644 } 645 646 for _, test := range tests { 647 seriesList := make([]*ts.Series, len(test.values)) 648 for i := 0; i < len(seriesList); i++ { 649 seriesList[i] = ts.NewSeries(ctx, "<values>", time.Now(), common.NewTestSeriesValues(ctx, test.stepPerMillis[i], test.values[i])) 650 } 651 652 r, err := percentileOfSeries(ctx, singlePathSpec{ 653 Values: seriesList, 654 }, test.percentile, test.interpolate) 655 require.NoError(t, err) 656 657 output := r.Values 658 name := fmt.Sprintf("percentileOfSeries(<values>,"+common.FloatingPointFormat+")", 659 test.percentile) 660 assert.Equal(t, name, output[0].Name()) 661 for step := 0; step < output[0].Len(); step++ { 662 v := output[0].ValueAt(step) 663 require.NoError(t, err) 664 665 xtest.Equalish(t, test.expectedValues[step], v) 666 } 667 xtest.Equalish(t, test.expectedStepPerMillis, output[0].MillisPerStep()) 668 } 669 } 670 671 func TestOffset(t *testing.T) { 672 ctx := common.NewTestContext() 673 defer func() { _ = ctx.Close() }() 674 675 tests := []struct { 676 inputs []float64 677 factor float64 678 outputs []float64 679 }{ 680 { 681 []float64{0, 1.0, 2.0, math.NaN(), 3.0}, 682 2.5, 683 []float64{2.5, 3.5, 4.5, math.NaN(), 5.5}, 684 }, 685 { 686 []float64{0, 1.0, 2.0, math.NaN(), 3.0}, 687 -0.5, 688 []float64{-0.5, 0.5, 1.5, math.NaN(), 2.5}, 689 }, 690 } 691 692 start := time.Now() 693 for _, test := range tests { 694 input := ts.NewSeries(ctx, "foo", start, common.NewTestSeriesValues(ctx, 100, test.inputs)) 695 r, err := offset(ctx, singlePathSpec{ 696 Values: []*ts.Series{input}, 697 }, test.factor) 698 require.NoError(t, err) 699 700 outputs := r.Values 701 require.Equal(t, 1, len(outputs)) 702 require.Equal(t, 100, outputs[0].MillisPerStep()) 703 require.Equal(t, len(test.inputs), outputs[0].Len()) 704 require.Equal(t, start, outputs[0].StartTime()) 705 assert.Equal(t, fmt.Sprintf("offset(foo,"+common.FloatingPointFormat+")", test.factor), outputs[0].Name()) 706 707 for step := 0; step < outputs[0].Len(); step++ { 708 v := outputs[0].ValueAt(step) 709 xtest.Equalish(t, test.outputs[step], v, "invalid value for %d", step) 710 } 711 } 712 } 713 714 func TestPerSecond(t *testing.T) { 715 ctx := common.NewTestContext() 716 defer func() { _ = ctx.Close() }() 717 718 tests := []struct { 719 millisPerStep int 720 input []float64 721 output []float64 722 }{ 723 // increase by 1 per 100ms == 10 per sec 724 {100, []float64{1, 2, 3, 4, 5}, []float64{math.NaN(), 10, 10, 10, 10}}, 725 726 // increase by 1 per 10s == .1 per sec 727 {10000, []float64{1, 2, 3, 4, 5}, []float64{math.NaN(), 0.1, 0.1, 0.1, 0.1}}, 728 729 // decreasing value - rate of change not applicable 730 { 731 1000, 732 []float64{5, 4, 3, 2, 1}, 733 []float64{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()}, 734 }, 735 736 // skip over missing values 737 {1000, []float64{1, 2, math.NaN(), 4, 5}, []float64{math.NaN(), 1, math.NaN(), 1, 1}}, 738 } 739 740 for _, test := range tests { 741 values := common.NewTestSeriesValues(ctx, test.millisPerStep, test.input) 742 series := ts.NewSeries(ctx, "foo", time.Now(), values) 743 r, err := perSecond(ctx, singlePathSpec{ 744 Values: []*ts.Series{series}, 745 }, math.NaN()) 746 require.NoError(t, err) 747 748 perSec := r.Values 749 require.Equal(t, 1, len(perSec)) 750 require.Equal(t, len(test.output), perSec[0].Len()) 751 assert.Equal(t, series.StartTime(), perSec[0].StartTime()) 752 assert.Equal(t, "perSecond(foo)", perSec[0].Name()) 753 for i := 0; i < perSec[0].Len(); i++ { 754 val := perSec[0].ValueAt(i) 755 xtest.Equalish(t, test.output[i], val, "invalid value for %d", i) 756 } 757 } 758 } 759 760 func TestTransformNull(t *testing.T) { 761 var ( 762 start = time.Now() 763 ctx = common.NewTestContext() 764 millisPerStep = 100 765 ) 766 defer func() { _ = ctx.Close() }() 767 768 tests := []struct { 769 inputs []*ts.Series 770 defaultValue float64 771 outputs []common.TestSeries 772 }{ 773 { 774 []*ts.Series{ 775 ts.NewSeries(ctx, "foo1", start, 776 common.NewTestSeriesValues(ctx, millisPerStep, []float64{0, math.NaN(), 2.0, math.NaN(), 3.0})), 777 ts.NewSeries(ctx, "foo2", start, 778 common.NewTestSeriesValues(ctx, millisPerStep, []float64{math.NaN(), 7, 2.0, 6.5, math.NaN()})), 779 }, 780 42.5, 781 []common.TestSeries{ 782 { 783 Name: "transformNull(foo1,42.500)", 784 Data: []float64{0, 42.5, 2.0, 42.5, 3.0}, 785 }, 786 { 787 Name: "transformNull(foo2,42.500)", 788 Data: []float64{42.5, 7, 2.0, 6.5, 42.5}, 789 }, 790 }, 791 }, 792 { 793 []*ts.Series{ 794 ts.NewSeries(ctx, "foo1", start, 795 common.NewTestSeriesValues(ctx, millisPerStep, []float64{0, 1.0, 2.0, math.NaN(), 3.0})), 796 ts.NewSeries(ctx, "foo2", start, 797 common.NewTestSeriesValues(ctx, millisPerStep, []float64{math.NaN(), 7, math.NaN(), 6.5, math.NaN()})), 798 }, 799 -0.5, 800 []common.TestSeries{ 801 { 802 Name: "transformNull(foo1,-0.500)", 803 Data: []float64{0, 1.0, 2.0, -0.5, 3.0}, 804 }, 805 { 806 Name: "transformNull(foo2,-0.500)", 807 Data: []float64{-0.5, 7, -0.5, 6.5, -0.5}, 808 }, 809 }, 810 }, 811 } 812 813 for _, test := range tests { 814 r, err := transformNull(ctx, singlePathSpec{ 815 Values: test.inputs, 816 }, test.defaultValue) 817 require.NoError(t, err) 818 819 common.CompareOutputsAndExpected(t, 100, start, 820 test.outputs, r.Values) 821 } 822 } 823 824 var ( 825 testMovingFunctionBootstrap = testMovingFunctionStart.Add(-30 * time.Second) 826 testMovingFunctionStart = time.Now().Truncate(time.Minute) 827 testMovingFunctionEnd = testMovingFunctionStart.Add(time.Minute).Add(-time.Second) 828 ) 829 830 func testMovingFunction(t *testing.T, target, expectedName string, values, bootstrap, output []float64) { 831 ctx := common.NewTestContext() 832 defer func() { _ = ctx.Close() }() 833 834 engine := NewEngine(&common.MovingFunctionStorage{ 835 StepMillis: 10000, 836 Bootstrap: bootstrap, 837 BootstrapStart: testMovingFunctionBootstrap, 838 Values: values, 839 }, CompileOptions{}) 840 phonyContext := common.NewContext(common.ContextOptions{ 841 Start: testMovingFunctionStart, 842 End: testMovingFunctionEnd, 843 Engine: engine, 844 }) 845 846 expr, err := phonyContext.Engine.(*Engine).Compile(target) 847 require.NoError(t, err) 848 res, err := expr.Execute(phonyContext) 849 require.NoError(t, err) 850 var expected []common.TestSeries 851 if output != nil { 852 expectedSeries := common.TestSeries{ 853 Name: expectedName, 854 Data: output, 855 } 856 expected = append(expected, expectedSeries) 857 } 858 common.CompareOutputsAndExpected(t, 10000, testMovingFunctionStart, 859 expected, res.Values) 860 } 861 862 var ( 863 testGeneralFunctionStart = time.Now().Add(time.Minute * -11).Truncate(time.Minute) 864 testGeneralFunctionEnd = time.Now().Add(time.Minute * -3).Truncate(time.Minute) 865 ) 866 867 // testGeneralFunction is a copy of testMovingFunction but without any logic for bootstrapping values 868 func testGeneralFunction(t *testing.T, target, expectedName string, values, output []float64) { 869 ctx := common.NewTestContext() 870 defer func() { _ = ctx.Close() }() 871 872 engine := NewEngine(&common.MovingFunctionStorage{ 873 StepMillis: 60000, 874 Values: values, 875 }, CompileOptions{}) 876 phonyContext := common.NewContext(common.ContextOptions{ 877 Start: testGeneralFunctionStart, 878 End: testGeneralFunctionEnd, 879 Engine: engine, 880 }) 881 882 expr, err := phonyContext.Engine.(*Engine).Compile(target) 883 require.NoError(t, err) 884 res, err := expr.Execute(phonyContext) 885 require.NoError(t, err) 886 var expected []common.TestSeries 887 if output != nil { 888 expectedSeries := common.TestSeries{ 889 Name: expectedName, 890 Data: output, 891 } 892 expected = append(expected, expectedSeries) 893 } 894 common.CompareOutputsAndExpected(t, 60000, testGeneralFunctionStart, expected, res.Values) 895 } 896 897 func TestCombineBootstrapWithOriginal(t *testing.T) { 898 var ( 899 contextStart = time.Date(2020, time.October, 5, 1, 15, 37, 884207922, time.UTC) 900 contextEnd = time.Date(2020, time.October, 5, 1, 18, 37, 884207922, time.UTC) 901 ctx = common.NewContext(common.ContextOptions{ 902 Start: contextStart, 903 End: contextEnd, 904 Engine: NewEngine(&common.MovingFunctionStorage{}, CompileOptions{}), 905 }) 906 907 originalStart = time.Date(2020, time.October, 5, 1, 16, 0o0, 0, time.UTC) 908 originalValues = []float64{14, 15, 16, 17, 18} 909 originalSeriesListValues = []*ts.Series{ts.NewSeries(ctx, "original", originalStart, common.NewTestSeriesValues(ctx, 30000, originalValues))} 910 originalSeriesList = singlePathSpec{Values: originalSeriesListValues} 911 912 bootstrappedStart = time.Date(2020, time.October, 5, 1, 15, 0o0, 0, time.UTC) 913 bootstrappedValues = []float64{12, 13, 14, 15, 16, 17, 18} 914 bootstrappedSeriesListValues = []*ts.Series{ts.NewSeries(ctx, "original", bootstrappedStart, common.NewTestSeriesValues(ctx, 30000, bootstrappedValues))} 915 bootstrappedSeriesList = ts.NewSeriesList() 916 917 bootstrapStartTime = time.Date(2020, time.October, 5, 1, 14, 37, 884207922, time.UTC) 918 bootstrapEndTime = time.Date(2020, time.October, 5, 1, 15, 37, 884207922, time.UTC) 919 920 expectedValues = []float64{12, 13, 14, 15, 16, 17, 18} 921 expectedSeries = ts.NewSeries(ctx, "original", bootstrapStartTime, common.NewTestSeriesValues(ctx, 30000, expectedValues)) 922 ) 923 bootstrappedSeriesList.Values = bootstrappedSeriesListValues 924 925 defer func() { _ = ctx.Close() }() 926 927 output, err := combineBootstrapWithOriginal(ctx, bootstrapStartTime, bootstrapEndTime, 928 contextStart, contextEnd, bootstrappedSeriesList, originalSeriesList) 929 assert.Equal(t, output.Values[0], expectedSeries) 930 assert.Nil(t, err) 931 } 932 933 func TestMovingAverageSuccess(t *testing.T) { 934 values := []float64{12.0, 19.0, -10.0, math.NaN(), 10.0} 935 bootstrap := []float64{3.0, 4.0, 5.0} 936 expected := []float64{4.0, 7.0, 12.0, 7.0, 4.5} 937 expectedWithXFiles := []float64{4.0, 7.0, 12.0, 7.0, math.NaN()} 938 939 testMovingFunction(t, "movingAverage(foo.bar.baz, '30s', 0.5)", "movingAverage(foo.bar.baz,\"30s\")", values, bootstrap, expected) 940 testMovingFunction(t, "movingAverage(foo.bar.baz, '30s', 0.8)", "movingAverage(foo.bar.baz,\"30s\")", values, bootstrap, expectedWithXFiles) 941 testMovingFunction(t, "movingAverage(foo.bar.baz, 3, 0.6)", "movingAverage(foo.bar.baz,3)", values, bootstrap, expected) 942 } 943 944 func TestExponentialMovingAverageSuccess(t *testing.T) { 945 tests := []struct { 946 target string 947 expectedName string 948 bootstrap []float64 949 inputs []float64 950 expected []float64 951 }{ 952 { 953 "exponentialMovingAverage(foo.bar.baz, '30s')", 954 "exponentialMovingAverage(foo.bar.baz,\"30s\")", 955 []float64{0.0, 1.0, 2.0}, 956 []float64{3.0, 4.0, 5.0, 6.0, 7.0}, 957 []float64{1, 1.193548, 1.439126, 1.733376, 2.073158}, 958 }, 959 { 960 "exponentialMovingAverage(foo.bar.baz, 3)", 961 "exponentialMovingAverage(foo.bar.baz,3)", 962 []float64{0.0, 1.0, 2.0}, 963 []float64{3.0, 4.0, 5.0, 6.0, 7.0}, 964 []float64{1, 2.5, 3.75, 4.875, 5.9375}, 965 }, 966 { 967 "exponentialMovingAverage(foo.bar.baz, 3)", 968 "exponentialMovingAverage(foo.bar.baz,3)", 969 []float64{0.0, 1.0, 2.0}, 970 []float64{3.0, 4.0, 5.0, math.NaN(), 7.0}, 971 []float64{1, 2.5, 3.75, math.NaN(), 5.375}, 972 }, 973 } 974 975 for _, test := range tests { 976 t.Run(test.target, func(t *testing.T) { 977 testMovingFunction(t, test.target, test.expectedName, test.inputs, test.bootstrap, test.expected) 978 }) 979 } 980 } 981 982 func testMovingFunctionError(t *testing.T, target string) { 983 ctx := common.NewTestContext() 984 defer func() { _ = ctx.Close() }() 985 986 engine := NewEngine(&common.MovingFunctionStorage{ 987 StepMillis: 10000, 988 Bootstrap: []float64{1.0}, 989 BootstrapStart: testMovingFunctionBootstrap, 990 Values: []float64{1.0}, 991 }, CompileOptions{}) 992 phonyContext := common.NewContext(common.ContextOptions{ 993 Start: testMovingFunctionStart, 994 End: testMovingFunctionEnd, 995 Engine: engine, 996 }) 997 998 expr, err := phonyContext.Engine.(*Engine).Compile(target) 999 require.NoError(t, err) 1000 res, err := expr.Execute(phonyContext) 1001 require.Error(t, err) 1002 require.Nil(t, res.Values) 1003 } 1004 1005 func testMovingFunctionErrorWithInput(t *testing.T, target string, values, bootstrap []float64) { 1006 ctx := common.NewTestContext() 1007 defer func() { _ = ctx.Close() }() 1008 1009 engine := NewEngine(&common.MovingFunctionStorage{ 1010 StepMillis: 10000, 1011 Bootstrap: bootstrap, 1012 BootstrapStart: testMovingFunctionBootstrap, 1013 Values: values, 1014 }, CompileOptions{}) 1015 phonyContext := common.NewContext(common.ContextOptions{ 1016 Start: testMovingFunctionStart, 1017 End: testMovingFunctionEnd, 1018 Engine: engine, 1019 }) 1020 1021 expr, err := phonyContext.Engine.(*Engine).Compile(target) 1022 require.NoError(t, err) 1023 res, err := expr.Execute(phonyContext) 1024 require.Error(t, err) 1025 require.Nil(t, res.Values) 1026 } 1027 1028 func TestMovingAverageError(t *testing.T) { 1029 testMovingFunctionError(t, "movingAverage(foo.bar.baz, '-30s')") 1030 testMovingFunctionError(t, "movingAverage(foo.bar.baz, 0)") 1031 testMovingFunctionErrorWithInput(t, "movingAverage(foo.bar.baz, 3, 0.1)", nil, nil) 1032 } 1033 1034 func TestMovingSumSuccess(t *testing.T) { 1035 values := []float64{12.0, 19.0, -10.0, math.NaN(), 10.0} 1036 bootstrap := []float64{3.0, 4.0, 5.0} 1037 expected := []float64{12.0, 21.0, 36.0, 21.0, 9.0} // (3+4+5), (4+5+12), (5+12+19), (12+19-10), (19-10+Nan) 1038 expectedXFF := []float64{12.0, 21.0, 36.0, 21.0, math.NaN()} 1039 1040 testMovingFunction(t, "movingSum(foo.bar.baz, '30s', 0.1)", "movingSum(foo.bar.baz,\"30s\")", values, bootstrap, expected) 1041 testMovingFunction(t, "movingSum(foo.bar.baz, '30s')", "movingSum(foo.bar.baz,\"30s\")", values, bootstrap, expected) 1042 testMovingFunction(t, "movingSum(foo.bar.baz, '30s', 1.0)", "movingSum(foo.bar.baz,\"30s\")", values, bootstrap, expectedXFF) 1043 testMovingFunction(t, "movingSum(foo.bar.baz, '30s')", "movingSum(foo.bar.baz,\"30s\")", values, bootstrap, expected) 1044 1045 testMovingFunction(t, "movingSum(foo.bar.baz, '30s')", "movingSum(foo.bar.baz,3)", nil, nil, nil) 1046 } 1047 1048 // TestMovingSumOfMovingSum tests that expansion of the time window 1049 // fetched is stacked when child contexts are created, otherwise the child 1050 // functions do not have enough data to work with to satisfy the parent call. 1051 func TestMovingSumOfMovingSum(t *testing.T) { 1052 var ( 1053 ctrl = xgomock.NewController(t) 1054 store = storage.NewMockStorage(ctrl) 1055 engine = NewEngine(store, CompileOptions{}) 1056 end = time.Now().Truncate(time.Minute) 1057 start = end.Add(-5 * time.Minute) 1058 ctx = common.NewContext(common.ContextOptions{ 1059 Start: start, 1060 End: end, 1061 Engine: engine, 1062 }) 1063 millisPerStep = 60000 1064 data = []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} 1065 ) 1066 1067 defer ctrl.Finish() 1068 defer func() { _ = ctx.Close() }() 1069 1070 store.EXPECT(). 1071 FetchByQuery(gomock.Any(), "foo.bar", gomock.Any()). 1072 DoAndReturn(func( 1073 _ *common.Context, 1074 query string, 1075 opts storage.FetchOptions, 1076 ) (*storage.FetchResult, error) { 1077 if !opts.EndTime.Equal(end) { 1078 return nil, fmt.Errorf("unexpected end") 1079 } 1080 length := int(opts.EndTime.Sub(opts.StartTime) / time.Minute) 1081 values := data[len(data)-length:] 1082 return &storage.FetchResult{ 1083 SeriesList: []*ts.Series{ 1084 ts.NewSeries(ctx, query, opts.StartTime, 1085 common.NewTestSeriesValues(ctx, millisPerStep, values)), 1086 }, 1087 }, nil 1088 }). 1089 AnyTimes() 1090 1091 target := `movingSum(movingSum(foo.bar,"2min"),"5min")` 1092 1093 phonyContext := common.NewContext(common.ContextOptions{ 1094 Start: start, 1095 End: end, 1096 Engine: engine, 1097 }) 1098 1099 expr, err := phonyContext.Engine.(*Engine).Compile(target) 1100 require.NoError(t, err) 1101 res, err := expr.Execute(phonyContext) 1102 require.NoError(t, err) 1103 1104 expected := []common.TestSeries{ 1105 { 1106 Name: target, 1107 Data: []float64{65, 75, 85, 95, 105}, 1108 }, 1109 } 1110 1111 common.CompareOutputsAndExpected(t, millisPerStep, start, 1112 expected, res.Values) 1113 } 1114 1115 func TestMovingSumError(t *testing.T) { 1116 testMovingFunctionError(t, "movingSum(foo.bar.baz, '-30s')") 1117 testMovingFunctionError(t, "movingSum(foo.bar.baz, 0)") 1118 } 1119 1120 // TestMovingSumOriginalIDsMissingFromBootstrapIDs tests the case for the 1121 // "moving" function families where the bootstrap of the time range that 1122 // expands back returns timeseries not present from the original series list 1123 // which can happen when using a temporal index (i.e. latest time window 1124 // does not include the same timeseries as the time window from the bootstrap 1125 // list, say using movingSum 1h but the query is only for the last few minutes 1126 // and in the last few minutes data for a series from an hour ago exists 1127 // but is not present in the last few minutes; for this case the results 1128 // from the preceding hour should still be evaluated as part of the movingSum 1129 // calculation). 1130 func TestMovingSumOriginalIDsMissingFromBootstrapIDs(t *testing.T) { 1131 ctx := common.NewTestContext() 1132 defer func() { _ = ctx.Close() }() 1133 1134 end := time.Now().Truncate(time.Minute) 1135 start := end.Add(-3 * time.Minute) 1136 end = end.Add(time.Second) // Extend so three values are calculated. 1137 bootstrapStart := start.Add(-10 * time.Minute) 1138 1139 engine := NewEngine(&common.MovingFunctionStorage{ 1140 StepMillis: 60000, 1141 OriginalValues: []common.SeriesNameAndValues{ 1142 {Name: "foo.bar", Values: []float64{3, 3, 3}}, 1143 }, 1144 ExplicitBootstraps: []common.ExplicitBootstrap{ 1145 { 1146 Start: bootstrapStart, 1147 Values: []common.SeriesNameAndValues{ 1148 {Name: "foo.bar", Values: []float64{1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3}}, 1149 {Name: "foo.baz", Values: []float64{1, 1, 1, 1, 1, 2, 2, 2, 2, 2, math.NaN(), math.NaN(), math.NaN()}}, 1150 }, 1151 }, 1152 }, 1153 }, CompileOptions{}) 1154 phonyContext := common.NewContext(common.ContextOptions{ 1155 Start: start, 1156 End: end, 1157 Engine: engine, 1158 }) 1159 1160 target := "movingSum(foo.*, '10min')" 1161 expr, err := phonyContext.Engine.(*Engine).Compile(target) 1162 require.NoError(t, err) 1163 res, err := expr.Execute(phonyContext) 1164 require.NoError(t, err) 1165 expected := []common.TestSeries{ 1166 { 1167 Name: "movingSum(foo.bar,\"10min\")", 1168 Data: []float64{15, 17, 19}, 1169 }, 1170 { 1171 Name: "movingSum(foo.baz,\"10min\")", 1172 Data: []float64{15, 14, 13}, 1173 }, 1174 } 1175 common.CompareOutputsAndExpected(t, 60000, start, 1176 expected, res.Values) 1177 } 1178 1179 // TestMovingSumAllOriginalIDsMissingFromBootstrapIDs tests the case for the 1180 // "moving" function families where the bootstrap of the time range that 1181 // expands back returns timeseries and the original series list is empty 1182 // which can also happen when using a temporal index. 1183 func TestMovingSumAllOriginalIDsMissingFromBootstrapIDs(t *testing.T) { 1184 ctx := common.NewTestContext() 1185 defer func() { _ = ctx.Close() }() 1186 1187 end := time.Now().Truncate(time.Minute) 1188 start := end.Add(-3 * time.Minute) 1189 end = end.Add(time.Second) // Extend so three values are calculated. 1190 bootstrapStart := start.Add(-10 * time.Minute) 1191 1192 engine := NewEngine(&common.MovingFunctionStorage{ 1193 StepMillis: 60000, 1194 ExplicitBootstraps: []common.ExplicitBootstrap{ 1195 { 1196 Start: bootstrapStart, 1197 Values: []common.SeriesNameAndValues{ 1198 {Name: "foo.bar", Values: []float64{1, 1, 1, 1, 1, 2, 2, 2, 2, 2, math.NaN(), math.NaN(), math.NaN()}}, 1199 {Name: "foo.baz", Values: []float64{1, 1, 1, 1, 1, 2, 2, 2, 2, 2, math.NaN(), math.NaN(), math.NaN()}}, 1200 }, 1201 }, 1202 }, 1203 }, CompileOptions{}) 1204 phonyContext := common.NewContext(common.ContextOptions{ 1205 Start: start, 1206 End: end, 1207 Engine: engine, 1208 }) 1209 1210 target := "movingSum(foo.*, '10min')" 1211 expr, err := phonyContext.Engine.(*Engine).Compile(target) 1212 require.NoError(t, err) 1213 res, err := expr.Execute(phonyContext) 1214 require.NoError(t, err) 1215 expected := []common.TestSeries{ 1216 { 1217 Name: "movingSum(foo.bar,\"10min\")", 1218 Data: []float64{15, 14, 13}, 1219 }, 1220 { 1221 Name: "movingSum(foo.baz,\"10min\")", 1222 Data: []float64{15, 14, 13}, 1223 }, 1224 } 1225 common.CompareOutputsAndExpected(t, 60000, start, 1226 expected, res.Values) 1227 } 1228 1229 // TestMovingSumOriginalIDsDifferentResolutionFromBootstrapIDs tests the case 1230 // where when a "moving" function fetches the bootstrapped time range but that 1231 // ends up returning a different resolution of data from a different namespace 1232 // and as such requires an adjusted context shift. 1233 func TestMovingSumOriginalIDsDifferentResolutionFromBootstrapIDs(t *testing.T) { 1234 ctx := common.NewTestContext() 1235 defer func() { _ = ctx.Close() }() 1236 1237 end := time.Now().Truncate(time.Minute) 1238 start := end.Add(-3 * time.Minute) 1239 end = end.Add(time.Second) // Extend so three values are calculated. 1240 1241 engine := NewEngine(&common.MovingFunctionStorage{ 1242 StepMillis: int(time.Minute / time.Millisecond), 1243 OriginalValues: []common.SeriesNameAndValues{ 1244 {Name: "foo.bar", Values: []float64{1, 1, 1}}, 1245 }, 1246 ExplicitBootstraps: []common.ExplicitBootstrap{ 1247 { 1248 Start: start.Add(-3 * time.Minute), 1249 Values: []common.SeriesNameAndValues{ 1250 // Two values for a 3min query + 3min bootstrap (6min window) at 5min resolution. 1251 {Name: "foo.bar", Values: []float64{2, 2}}, 1252 {Name: "foo.baz", Values: []float64{4, 4}}, 1253 }, 1254 StepMillis: int((5 * time.Minute) / time.Millisecond), 1255 }, 1256 { 1257 Start: start.Add(-15 * time.Minute), 1258 Values: []common.SeriesNameAndValues{ 1259 // Four values for a 3min query + 15 bootstrap (18min window) at 5min resolution. 1260 {Name: "foo.bar", Values: []float64{3, 3, 3, 3}}, 1261 {Name: "foo.baz", Values: []float64{6, 6, 6, 6}}, 1262 }, 1263 StepMillis: int((5 * time.Minute) / time.Millisecond), 1264 }, 1265 }, 1266 }, CompileOptions{}) 1267 phonyContext := common.NewContext(common.ContextOptions{ 1268 Start: start, 1269 End: end, 1270 Engine: engine, 1271 }) 1272 1273 target := "movingSum(foo.*, 3)" 1274 expr, err := phonyContext.Engine.(*Engine).Compile(target) 1275 require.NoError(t, err) 1276 res, err := expr.Execute(phonyContext) 1277 require.NoError(t, err) 1278 expected := []common.TestSeries{ 1279 { 1280 Name: "movingSum(foo.bar,3)", 1281 Data: []float64{9}, 1282 }, 1283 { 1284 Name: "movingSum(foo.baz,3)", 1285 Data: []float64{18}, 1286 }, 1287 } 1288 common.CompareOutputsAndExpected(t, int((5*time.Minute)/time.Millisecond), 1289 start, expected, res.Values) 1290 } 1291 1292 func TestMovingMaxSuccess(t *testing.T) { 1293 values := []float64{12.0, 19.0, -10.0, math.NaN(), 10.0} 1294 bootstrap := []float64{3.0, 4.0, 5.0} 1295 expected := []float64{5.0, 12.0, 19.0, 19.0, 19.0} // max(3,4,5), max(4,5,12), max(5,12,19), max(12,19,10), max(19,-10,NaN) 1296 1297 testMovingFunction(t, "movingMax(foo.bar.baz, '30s')", "movingMax(foo.bar.baz,\"30s\")", values, bootstrap, expected) 1298 testMovingFunction(t, "movingMax(foo.bar.baz, '30s')", "movingMax(foo.bar.baz,3)", nil, nil, nil) 1299 } 1300 1301 func TestMovingMaxError(t *testing.T) { 1302 testMovingFunctionError(t, "movingMax(foo.bar.baz, '-30s')") 1303 testMovingFunctionError(t, "movingMax(foo.bar.baz, 0)") 1304 } 1305 1306 func TestMovingMinSuccess(t *testing.T) { 1307 values := []float64{12.0, 19.0, -10.0, math.NaN(), 10.0} 1308 bootstrap := []float64{3.0, 4.0, 5.0} 1309 expected := []float64{3.0, 4.0, 5.0, -10.0, -10.0} // min(3,4,5), min(4,5,12), min(5,12,19), min(12,19,-10), min(19,-10,NaN) 1310 1311 testMovingFunction(t, "movingMin(foo.bar.baz, '30s')", "movingMin(foo.bar.baz,\"30s\")", values, bootstrap, expected) 1312 testMovingFunction(t, "movingMin(foo.bar.baz, '30s')", "movingMin(foo.bar.baz,3)", nil, nil, nil) 1313 } 1314 1315 func TestMovingMinError(t *testing.T) { 1316 testMovingFunctionError(t, "movingMin(foo.bar.baz, '-30s')") 1317 testMovingFunctionError(t, "movingMin(foo.bar.baz, 0)") 1318 } 1319 1320 func TestIsNonNull(t *testing.T) { 1321 ctx := common.NewTestContext() 1322 defer func() { _ = ctx.Close() }() 1323 1324 tests := []struct { 1325 inputs []float64 1326 outputs []float64 1327 }{ 1328 { 1329 []float64{0, math.NaN(), 2.0, math.NaN(), 3.0}, 1330 []float64{1, 0, 1, 0, 1}, 1331 }, 1332 { 1333 []float64{0, 1.0, 2.0, math.NaN(), 3.0}, 1334 []float64{1, 1, 1, 0, 1}, 1335 }, 1336 } 1337 1338 start := time.Now() 1339 for _, test := range tests { 1340 input := ts.NewSeries(ctx, "foo", start, common.NewTestSeriesValues(ctx, 100, test.inputs)) 1341 r, err := isNonNull(ctx, singlePathSpec{ 1342 Values: []*ts.Series{input}, 1343 }) 1344 require.NoError(t, err) 1345 1346 outputs := r.Values 1347 require.Equal(t, 1, len(outputs)) 1348 require.Equal(t, 100, outputs[0].MillisPerStep()) 1349 require.Equal(t, len(test.inputs), outputs[0].Len()) 1350 require.Equal(t, start, outputs[0].StartTime()) 1351 assert.Equal(t, "isNonNull(foo)", outputs[0].Name()) 1352 1353 for step := 0; step < outputs[0].Len(); step++ { 1354 v := outputs[0].ValueAt(step) 1355 assert.Equal(t, test.outputs[step], v, "invalid value for %d", step) 1356 } 1357 } 1358 } 1359 1360 func TestKeepLastValue(t *testing.T) { 1361 ctx := common.NewTestContext() 1362 defer func() { _ = ctx.Close() }() 1363 1364 tests := []struct { 1365 inputs []float64 1366 outputs []float64 1367 limit int 1368 }{ 1369 { 1370 []float64{0, math.NaN(), 2.0, math.NaN(), 3.0}, 1371 []float64{0, 0, 2.0, 2.0, 3.0}, 1372 -1, 1373 }, 1374 { 1375 []float64{math.NaN(), 1.0, 2.0, math.NaN(), 3.0}, 1376 []float64{math.NaN(), 1.0, 2.0, 2.0, 3.0}, 1377 -1, 1378 }, 1379 { 1380 []float64{1.0, math.NaN(), math.NaN(), math.NaN(), 3.0, math.NaN(), math.NaN(), 2.0}, 1381 []float64{1.0, math.NaN(), math.NaN(), math.NaN(), 3.0, 3.0, 3.0, 2.0}, 1382 2, 1383 }, 1384 } 1385 1386 start := time.Now() 1387 for _, test := range tests { 1388 input := ts.NewSeries(ctx, "foo", start, common.NewTestSeriesValues(ctx, 100, test.inputs)) 1389 outputs, err := keepLastValue(ctx, singlePathSpec{ 1390 Values: []*ts.Series{input}, 1391 }, test.limit) 1392 expected := common.TestSeries{Name: "keepLastValue(foo)", Data: test.outputs} 1393 require.NoError(t, err) 1394 common.CompareOutputsAndExpected(t, 100, start, 1395 []common.TestSeries{expected}, outputs.Values) 1396 } 1397 } 1398 1399 func TestRoundFunction(t *testing.T) { 1400 ctx := common.NewTestContext() 1401 defer func() { _ = ctx.Close() }() 1402 1403 tests := []struct { 1404 name string 1405 inputs []float64 1406 outputs []float64 1407 outputName string 1408 precision int 1409 }{ 1410 { 1411 "foo", 1412 []float64{111.1, math.NaN(), 111.11, math.NaN(), 111.111}, 1413 []float64{110, math.NaN(), 110, math.NaN(), 110}, 1414 "roundFunction(foo,-1)", 1415 -1, 1416 }, 1417 { 1418 "foo", 1419 []float64{1.1, math.NaN(), 1.11, math.NaN(), 1.111}, 1420 []float64{1, math.NaN(), 1, math.NaN(), 1}, 1421 "roundFunction(foo)", 1422 0, 1423 }, 1424 { 1425 "foo", 1426 []float64{1.1, math.NaN(), 1.11, math.NaN(), 1.111}, 1427 []float64{1.1, math.NaN(), 1.1, math.NaN(), 1.1}, 1428 "roundFunction(foo,1)", 1429 1, 1430 }, 1431 { 1432 "foo", 1433 []float64{1.1, math.NaN(), 1.11, math.NaN(), 1.111}, 1434 []float64{1.10, math.NaN(), 1.11, math.NaN(), 1.11}, 1435 "roundFunction(foo,2)", 1436 2, 1437 }, 1438 } 1439 1440 start := time.Now() 1441 for _, test := range tests { 1442 input := ts.NewSeries(ctx, test.name, start, common.NewTestSeriesValues(ctx, 100, test.inputs)) 1443 outputs, err := roundFunction(ctx, singlePathSpec{ 1444 Values: []*ts.Series{input}, 1445 }, test.precision) 1446 expected := common.TestSeries{Name: test.outputName, Data: test.outputs} 1447 require.NoError(t, err) 1448 common.CompareOutputsAndExpected(t, 100, start, 1449 []common.TestSeries{expected}, outputs.Values) 1450 } 1451 } 1452 1453 func TestSustainedAbove(t *testing.T) { 1454 ctx := common.NewTestContext() 1455 defer func() { _ = ctx.Close() }() 1456 1457 tests := []struct { 1458 inputs []float64 1459 outputs []float64 1460 threshold float64 1461 interval string 1462 }{ 1463 { 1464 []float64{0, 0, 3, 3, 4, 0, 0}, 1465 []float64{0, 0, 3, 3, 4, 0, 0}, 1466 2, 1467 "10s", 1468 }, 1469 { 1470 []float64{0, 0, 3, 3, 4, 0, 0}, 1471 []float64{0, 0, 0, 3, 4, 0, 0}, 1472 2, 1473 "20s", 1474 }, 1475 { 1476 []float64{0, 0, 3, 3, 4, 0, 0}, 1477 []float64{0, 0, 0, 0, 4, 0, 0}, 1478 2, 1479 "30s", 1480 }, 1481 { 1482 []float64{0, 0, 3, 3, 4, 0, 0}, 1483 []float64{0, 0, 0, 0, 0, 0, 0}, 1484 2, 1485 "40s", 1486 }, 1487 { 1488 []float64{0, 3, 3, 4, 4, 2, 0}, 1489 []float64{0, 0, 0, 0, 4, 0, 0}, 1490 4, 1491 "20s", 1492 }, 1493 { 1494 []float64{1, 2, 3, 4, 9, 9, 9, 9, 9, 3}, 1495 []float64{0, 0, 0, 0, 0, 0, 9, 9, 9, 0}, 1496 8, 1497 "30s", 1498 }, 1499 { 1500 []float64{1, 2, 3, 4, 5, 5, 5, 5, 5, 3}, 1501 []float64{0, 0, 0, 4, 5, 5, 5, 5, 5, 0}, 1502 4, 1503 "10s", 1504 }, 1505 { 1506 []float64{-3, -4, -1, 3, 0, -1, -5, -6, -3}, 1507 []float64{-4, -4, -4, 3, 0, -1, -4, -4, -4}, 1508 -2, 1509 "20s", 1510 }, 1511 } 1512 1513 start := time.Now() 1514 for _, test := range tests { 1515 input := ts.NewSeries(ctx, "foo", start, common.NewTestSeriesValues(ctx, 10000, test.inputs)) 1516 r, err := sustainedAbove(ctx, singlePathSpec{ 1517 Values: []*ts.Series{input}, 1518 }, test.threshold, test.interval) 1519 require.NoError(t, err) 1520 1521 outputs := r.Values 1522 require.Equal(t, 1, len(outputs)) 1523 require.Equal(t, 10000, outputs[0].MillisPerStep()) 1524 require.Equal(t, len(test.inputs), outputs[0].Len()) 1525 require.Equal(t, start, outputs[0].StartTime()) 1526 1527 str := fmt.Sprintf("sustainedAbove(foo, %f, '%s')", test.threshold, test.interval) 1528 1529 assert.Equal(t, str, outputs[0].Name()) 1530 1531 for step := 0; step < outputs[0].Len(); step++ { 1532 v := outputs[0].ValueAt(step) 1533 1534 assert.Equal(t, test.outputs[step], v, "invalid value for %d", step) 1535 } 1536 } 1537 } 1538 1539 func TestSustainedAboveFail(t *testing.T) { 1540 ctx := common.NewTestContext() 1541 defer func() { _ = ctx.Close() }() 1542 1543 input := ts.NewSeries(ctx, "foo", time.Now(), common.NewTestSeriesValues(ctx, 10000, []float64{0})) 1544 outputs, err := sustainedAbove(ctx, singlePathSpec{ 1545 Values: []*ts.Series{input}, 1546 }, 10, "wat") 1547 require.Error(t, err) 1548 require.Equal(t, 0, outputs.Len()) 1549 } 1550 1551 func TestSustainedBelow(t *testing.T) { 1552 ctx := common.NewTestContext() 1553 defer func() { _ = ctx.Close() }() 1554 1555 tests := []struct { 1556 inputs []float64 1557 outputs []float64 1558 threshold float64 1559 interval string 1560 }{ 1561 { 1562 []float64{4, 4, 1, 1, 1, 4, 4}, 1563 []float64{4, 4, 1, 1, 1, 4, 4}, 1564 2, 1565 "10s", 1566 }, 1567 { 1568 []float64{7, 8, 3, 3, 2, 6, 7}, 1569 []float64{6, 6, 6, 3, 2, 6, 6}, 1570 3, 1571 "20s", 1572 }, 1573 { 1574 []float64{9, 7, 3, 3, 2, 5, 6}, 1575 []float64{6, 6, 6, 6, 2, 6, 6}, 1576 3, 1577 "30s", 1578 }, 1579 { 1580 []float64{8, 5, 3, 3, 2, 5, 8}, 1581 []float64{6, 6, 6, 6, 6, 6, 6}, 1582 3, 1583 "40s", 1584 }, 1585 { 1586 []float64{4, 3, 3, 1, 1, 2, 4}, 1587 []float64{2, 2, 2, 2, 1, 2, 2}, 1588 1, 1589 "20s", 1590 }, 1591 { 1592 []float64{7, 8, 9, 2, 2, 4, 2, 5, 3, 2}, 1593 []float64{8, 8, 8, 8, 8, 8, 2, 8, 8, 8}, 1594 4, 1595 "40s", 1596 }, 1597 { 1598 []float64{1, 2, 3, 4, 9, 9, 9, 9, 9, 3}, 1599 []float64{8, 2, 3, 4, 8, 8, 8, 8, 8, 8}, 1600 4, 1601 "20s", 1602 }, 1603 { 1604 []float64{-3, -4, -3, -1, 3, 2, -5, -4, -3, -3}, 1605 []float64{0, -4, -3, 0, 0, 0, 0, -4, -3, -3}, 1606 -2, 1607 "20s", 1608 }, 1609 } 1610 1611 start := time.Now() 1612 for _, test := range tests { 1613 input := ts.NewSeries(ctx, "foo", start, common.NewTestSeriesValues(ctx, 10000, test.inputs)) 1614 r, err := sustainedBelow(ctx, singlePathSpec{ 1615 Values: []*ts.Series{input}, 1616 }, test.threshold, test.interval) 1617 require.NoError(t, err) 1618 1619 outputs := r.Values 1620 require.Equal(t, 1, len(outputs)) 1621 require.Equal(t, 10000, outputs[0].MillisPerStep()) 1622 require.Equal(t, len(test.inputs), outputs[0].Len()) 1623 require.Equal(t, start, outputs[0].StartTime()) 1624 1625 str := fmt.Sprintf("sustainedBelow(foo, %f, '%s')", test.threshold, test.interval) 1626 1627 assert.Equal(t, str, outputs[0].Name()) 1628 for step := 0; step < outputs[0].Len(); step++ { 1629 v := outputs[0].ValueAt(step) 1630 1631 assert.Equal(t, test.outputs[step], v, "invalid value for %d", step) 1632 } 1633 } 1634 } 1635 1636 func TestSustainedBelowFail(t *testing.T) { 1637 ctx := common.NewTestContext() 1638 defer func() { _ = ctx.Close() }() 1639 1640 input := ts.NewSeries(ctx, "foo", time.Now(), common.NewTestSeriesValues(ctx, 10000, []float64{0})) 1641 outputs, err := sustainedBelow(ctx, singlePathSpec{ 1642 Values: []*ts.Series{input}, 1643 }, 10, "wat") 1644 require.Error(t, err) 1645 require.Equal(t, 0, outputs.Len()) 1646 } 1647 1648 // nIntParamGoldenData holds test data for functions that take an additional "n" int parameter 1649 type nIntParamGoldenData struct { 1650 inputs []common.TestSeries 1651 n int 1652 outputs []common.TestSeries 1653 } 1654 1655 // nIntParamGoldenDataWithAgg holds test data for functions that take an additional "n" int parameter 1656 // It also holds an aggregation function 1657 type nIntParamGoldenDataWithAgg struct { 1658 nIntParamGoldenData 1659 aggFunc string 1660 } 1661 1662 // rankingFunc selects the n lowest or highest series based on certain metric of the 1663 // series (e.g., maximum, minimum, average). 1664 type rankingFunc func(ctx *common.Context, input singlePathSpec, n int) (ts.SeriesList, error) 1665 1666 // testRanking can be used to test the ranking alias functions 1667 // (e.g. lowestAverage, highestMax, highestAverage, lowestCurrent) 1668 // these functions are all aliases of the "meta-ranking" functions (i.e. highest and lowest) 1669 func testRanking(t *testing.T, ctx *common.Context, tests []nIntParamGoldenData, f rankingFunc) { 1670 start := time.Now() 1671 step := 100 1672 for _, test := range tests { 1673 outputs, err := f(ctx, singlePathSpec{ 1674 Values: generateSeriesList(ctx, start, test.inputs, step), 1675 }, test.n) 1676 if test.n < 0 { 1677 require.NotNil(t, err) 1678 require.Equal(t, "n must be positive", err.Error()) 1679 assert.Nil(t, outputs.Values, "Nil timeseries should be returned") 1680 continue 1681 } 1682 require.NoError(t, err) 1683 common.CompareOutputsAndExpected(t, step, start, 1684 test.outputs, outputs.Values) 1685 } 1686 } 1687 1688 // testOrderedAggregationFunc is a helper function for testing lowest and highest 1689 func testOrderedAggregationFunc(t *testing.T, ctx *common.Context, tests []nIntParamGoldenDataWithAgg, isLowest bool) { 1690 f := highest 1691 if isLowest { 1692 f = lowest 1693 } 1694 1695 start := time.Now() 1696 step := 100 1697 for _, test := range tests { 1698 input := singlePathSpec{Values: generateSeriesList(ctx, start, test.inputs, step)} 1699 outputs, err := f(ctx, input, test.n, test.aggFunc) 1700 1701 if test.n < 0 { 1702 require.NotNil(t, err) 1703 require.Equal(t, "n must be positive", err.Error()) 1704 assert.Nil(t, outputs.Values, "Nil timeseries should be returned") 1705 continue 1706 } 1707 1708 require.NoError(t, err) 1709 common.CompareOutputsAndExpected(t, step, start, 1710 test.outputs, outputs.Values) 1711 } 1712 } 1713 1714 func TestHighest(t *testing.T) { 1715 ctx := common.NewTestContext() 1716 defer func() { _ = ctx.Close() }() 1717 1718 tests := []nIntParamGoldenDataWithAgg{ 1719 { 1720 nIntParamGoldenData{ 1721 testInput, 1722 0, 1723 nil, 1724 }, 1725 "sum", 1726 }, 1727 { 1728 nIntParamGoldenData{ 1729 testInput, 1730 1, 1731 []common.TestSeries{testInput[0]}, 1732 }, 1733 "current", 1734 }, 1735 { 1736 nIntParamGoldenData{ 1737 testInput, 1738 2, 1739 []common.TestSeries{testInput[4], testInput[2]}, 1740 }, 1741 "average", 1742 }, 1743 { 1744 nIntParamGoldenData{ 1745 testInput, 1746 len(testInput) + 10, // force sort 1747 []common.TestSeries{testInput[0], testInput[3], testInput[4], testInput[2], testInput[1]}, 1748 }, 1749 "last", 1750 }, 1751 } 1752 testOrderedAggregationFunc(t, ctx, tests, false) 1753 } 1754 1755 func TestHighestCurrent(t *testing.T) { 1756 ctx := common.NewTestContext() 1757 defer func() { _ = ctx.Close() }() 1758 1759 tests := []nIntParamGoldenData{ 1760 { 1761 testInput, 1762 0, 1763 nil, 1764 }, 1765 { 1766 testInput, 1767 1, 1768 []common.TestSeries{testInput[0]}, 1769 }, 1770 { 1771 testInput, 1772 2, 1773 []common.TestSeries{testInput[0], testInput[3]}, 1774 }, 1775 { 1776 testInput, 1777 len(testInput) + 10, // force sort 1778 []common.TestSeries{testInput[0], testInput[3], testInput[4], testInput[2], testInput[1]}, 1779 }, 1780 } 1781 testRanking(t, ctx, tests, highestCurrent) 1782 } 1783 1784 func TestHighestCurrentWithNaNSeries(t *testing.T) { 1785 ctx := common.NewTestContext() 1786 defer func() { _ = ctx.Close() }() 1787 1788 tests := []nIntParamGoldenData{ 1789 { 1790 testInputWithNaNSeries, 1791 0, 1792 nil, 1793 }, 1794 { 1795 testInputWithNaNSeries, 1796 1, 1797 []common.TestSeries{testInputWithNaNSeries[0]}, 1798 }, 1799 { 1800 testInputWithNaNSeries, 1801 2, 1802 []common.TestSeries{testInputWithNaNSeries[0], testInputWithNaNSeries[2]}, 1803 }, 1804 { 1805 testInputWithNaNSeries, 1806 3, 1807 []common.TestSeries{testInputWithNaNSeries[0], testInputWithNaNSeries[2], testInputWithNaNSeries[1]}, 1808 }, 1809 { 1810 testInputWithNaNSeries, 1811 4, 1812 []common.TestSeries{testInputWithNaNSeries[0], testInputWithNaNSeries[2], testInputWithNaNSeries[1], testInputWithNaNSeries[3]}, 1813 }, 1814 } 1815 testRanking(t, ctx, tests, highestCurrent) 1816 } 1817 1818 func TestHighestAverage(t *testing.T) { 1819 ctx := common.NewTestContext() 1820 defer func() { _ = ctx.Close() }() 1821 1822 tests := []nIntParamGoldenData{ 1823 { 1824 testInput, 1825 1, 1826 []common.TestSeries{testInput[4]}, 1827 }, 1828 { 1829 testInput, 1830 2, 1831 []common.TestSeries{testInput[4], testInput[2]}, 1832 }, 1833 } 1834 testRanking(t, ctx, tests, highestAverage) 1835 } 1836 1837 func TestHighestMax(t *testing.T) { 1838 ctx := common.NewTestContext() 1839 defer func() { _ = ctx.Close() }() 1840 1841 tests := []nIntParamGoldenData{ 1842 { 1843 testInput, 1844 1, 1845 []common.TestSeries{testInput[4]}, 1846 }, 1847 { 1848 testInput, 1849 2, 1850 []common.TestSeries{testInput[4], testInput[0]}, 1851 }, 1852 } 1853 testRanking(t, ctx, tests, highestMax) 1854 } 1855 1856 //nolint:govet 1857 func TestFallbackSeries(t *testing.T) { 1858 ctx := common.NewTestContext() 1859 defer func() { _ = ctx.Close() }() 1860 1861 tests := []struct { 1862 input []common.TestSeries 1863 fallback []common.TestSeries 1864 output []common.TestSeries 1865 }{ 1866 { 1867 nil, 1868 []common.TestSeries{{Name: "output", Data: []float64{0, 1.0}}}, 1869 []common.TestSeries{{Name: "output", Data: []float64{0, 1.0}}}, 1870 }, 1871 { 1872 []common.TestSeries{}, 1873 []common.TestSeries{{Name: "output", Data: []float64{0, 1.0}}}, 1874 []common.TestSeries{{Name: "output", Data: []float64{0, 1.0}}}, 1875 }, 1876 { 1877 []common.TestSeries{{Name: "output", Data: []float64{0, 2.0}}}, 1878 []common.TestSeries{{Name: "fallback", Data: []float64{0, 1.0}}}, 1879 []common.TestSeries{{Name: "output", Data: []float64{0, 2.0}}}, 1880 }, 1881 } 1882 1883 start := time.Now() 1884 step := 100 1885 for _, test := range tests { 1886 1887 inputs := generateSeriesList(ctx, start, test.input, step) 1888 fallbacks := generateSeriesList(ctx, start, test.fallback, step) 1889 1890 outputs, err := fallbackSeries(ctx, singlePathSpec{ 1891 Values: inputs, 1892 }, singlePathSpec{ 1893 Values: fallbacks, 1894 }) 1895 require.NoError(t, err) 1896 1897 common.CompareOutputsAndExpected(t, step, start, 1898 test.output, outputs.Values) 1899 } 1900 } 1901 1902 func TestMostDeviant(t *testing.T) { 1903 ctx := common.NewTestContext() 1904 defer func() { _ = ctx.Close() }() 1905 1906 tests := []nIntParamGoldenData{ 1907 { 1908 testInput, 1909 -2, 1910 nil, 1911 }, 1912 { 1913 testInput, 1914 1, 1915 []common.TestSeries{testInput[4]}, 1916 }, 1917 { 1918 testInput, 1919 2, 1920 []common.TestSeries{testInput[4], testInput[3]}, 1921 }, 1922 } 1923 testRanking(t, ctx, tests, mostDeviant) 1924 } 1925 1926 func TestLowest(t *testing.T) { 1927 ctx := common.NewTestContext() 1928 defer func() { _ = ctx.Close() }() 1929 1930 tests := []nIntParamGoldenDataWithAgg{ 1931 { 1932 nIntParamGoldenData{ 1933 testInput, 1934 0, 1935 nil, 1936 }, 1937 "max", 1938 }, 1939 { 1940 nIntParamGoldenData{ 1941 testInput, 1942 2, 1943 []common.TestSeries{testInput[1], testInput[3]}, 1944 }, 1945 "sum", 1946 }, 1947 { 1948 nIntParamGoldenData{ 1949 testInput, 1950 2, 1951 []common.TestSeries{testInput[1], testInput[2]}, 1952 }, 1953 "current", 1954 }, 1955 { 1956 nIntParamGoldenData{ 1957 testInput, 1958 3, 1959 []common.TestSeries{testInput[1], testInput[3], testInput[0]}, 1960 }, 1961 "average", 1962 }, 1963 } 1964 testOrderedAggregationFunc(t, ctx, tests, true) 1965 } 1966 1967 func TestLowestAverage(t *testing.T) { 1968 ctx := common.NewTestContext() 1969 defer func() { _ = ctx.Close() }() 1970 1971 tests := []nIntParamGoldenData{ 1972 { 1973 testInput, 1974 0, 1975 nil, 1976 }, 1977 { 1978 testInput, 1979 1, 1980 []common.TestSeries{testInput[1]}, 1981 }, 1982 { 1983 testInput, 1984 2, 1985 []common.TestSeries{testInput[1], testInput[3]}, 1986 }, 1987 { 1988 testInput, 1989 3, 1990 []common.TestSeries{testInput[1], testInput[3], testInput[0]}, 1991 }, 1992 } 1993 testRanking(t, ctx, tests, lowestAverage) 1994 } 1995 1996 func TestLowestCurrent(t *testing.T) { 1997 ctx := common.NewTestContext() 1998 defer func() { _ = ctx.Close() }() 1999 2000 tests := []nIntParamGoldenData{ 2001 { 2002 testInput, 2003 0, 2004 nil, 2005 }, 2006 { 2007 testInput, 2008 1, 2009 []common.TestSeries{testInput[1]}, 2010 }, 2011 { 2012 testInput, 2013 2, 2014 []common.TestSeries{testInput[1], testInput[2]}, 2015 }, 2016 { 2017 testInput, 2018 3, 2019 []common.TestSeries{testInput[1], testInput[2], testInput[4]}, 2020 }, 2021 } 2022 testRanking(t, ctx, tests, lowestCurrent) 2023 } 2024 2025 type comparatorFunc func(ctx *common.Context, series singlePathSpec, n float64) (ts.SeriesList, error) 2026 2027 func testComparatorFunc( 2028 t *testing.T, 2029 f comparatorFunc, 2030 n float64, 2031 resultIndexes []int, 2032 ) { 2033 ctx := common.NewTestContext() 2034 defer func() { _ = ctx.Close() }() 2035 2036 input := getTestInput(ctx) 2037 results, err := f(ctx, singlePathSpec{ 2038 Values: input, 2039 }, n) 2040 require.Nil(t, err) 2041 require.Equal(t, len(resultIndexes), results.Len()) 2042 for i, idx := range resultIndexes { 2043 require.Equal(t, input[idx], results.Values[i]) 2044 } 2045 } 2046 2047 func TestMaximumAbove(t *testing.T) { 2048 testComparatorFunc(t, maximumAbove, -10, []int{0, 2, 3, 4}) 2049 testComparatorFunc(t, maximumAbove, 600, []int{0, 4}) 2050 testComparatorFunc(t, maximumAbove, 100000, nil) 2051 } 2052 2053 func TestMinimumAbove(t *testing.T) { 2054 testComparatorFunc(t, minimumAbove, -1000, []int{0, 2, 3, 4}) 2055 testComparatorFunc(t, minimumAbove, -100, []int{0, 2, 4}) 2056 testComparatorFunc(t, minimumAbove, 1, nil) 2057 } 2058 2059 func TestAverageAbove(t *testing.T) { 2060 testComparatorFunc(t, averageAbove, 0, []int{0, 2, 3, 4}) 2061 testComparatorFunc(t, averageAbove, 1, []int{0, 2, 4}) 2062 testComparatorFunc(t, averageAbove, 12000, nil) 2063 } 2064 2065 func TestAverageBelow(t *testing.T) { 2066 testComparatorFunc(t, averageBelow, 0, nil) 2067 testComparatorFunc(t, averageBelow, 600, []int{0, 2, 3}) 2068 testComparatorFunc(t, averageBelow, 12000, []int{0, 2, 3, 4}) 2069 } 2070 2071 func TestCurrentAbove(t *testing.T) { 2072 testComparatorFunc(t, currentAbove, -10, []int{0, 2, 3, 4}) 2073 testComparatorFunc(t, currentAbove, -5, []int{0, 3, 4}) 2074 testComparatorFunc(t, currentAbove, 5, nil) 2075 } 2076 2077 func TestCurrentBelow(t *testing.T) { 2078 testComparatorFunc(t, currentBelow, 5, []int{0, 2, 3, 4}) 2079 testComparatorFunc(t, currentBelow, 0, []int{2, 4}) 2080 testComparatorFunc(t, currentBelow, -5, []int{2}) 2081 testComparatorFunc(t, currentBelow, -10, nil) 2082 } 2083 2084 func TestRemoveBelowValue(t *testing.T) { 2085 ctx := common.NewTestContext() 2086 defer func() { _ = ctx.Close() }() 2087 2088 nan := math.NaN() 2089 tests := []struct { 2090 inputs []common.TestSeries 2091 n float64 2092 outputs []common.TestSeries 2093 }{ 2094 { 2095 testSmallInput, 2096 500, 2097 []common.TestSeries{ 2098 {"foo", []float64{nan, 601, nan, nan}}, 2099 {"bar", []float64{500, nan}}, 2100 }, 2101 }, 2102 { 2103 testSmallInput, 2104 4, 2105 []common.TestSeries{ 2106 {"foo", []float64{nan, 601, nan, 4}}, 2107 {"bar", []float64{500, nan}}, 2108 }, 2109 }, 2110 } 2111 start := time.Now() 2112 step := 100 2113 for _, test := range tests { 2114 outputs, err := removeBelowValue(ctx, singlePathSpec{ 2115 Values: generateSeriesList(ctx, start, test.inputs, step), 2116 }, test.n) 2117 require.NoError(t, err) 2118 for i := range test.outputs { // overwrite series names 2119 name := fmt.Sprintf("removeBelowValue(%s, "+common.FloatingPointFormat+")", 2120 test.outputs[i].Name, test.n) 2121 test.outputs[i].Name = name 2122 } 2123 common.CompareOutputsAndExpected(t, step, start, 2124 test.outputs, outputs.Values) 2125 } 2126 } 2127 2128 func TestRemoveAboveValue(t *testing.T) { 2129 ctx := common.NewTestContext() 2130 defer func() { _ = ctx.Close() }() 2131 2132 nan := math.NaN() 2133 tests := []struct { 2134 inputs []common.TestSeries 2135 n float64 2136 outputs []common.TestSeries 2137 }{ 2138 { 2139 testSmallInput, 2140 500, 2141 []common.TestSeries{ 2142 {"foo", []float64{0, nan, 3, 4}}, 2143 {"bar", []float64{500, -8}}, 2144 }, 2145 }, 2146 { 2147 testSmallInput, 2148 3, 2149 []common.TestSeries{ 2150 {"foo", []float64{0, nan, 3, nan}}, 2151 {"bar", []float64{nan, -8}}, 2152 }, 2153 }, 2154 } 2155 start := time.Now() 2156 step := 100 2157 for _, test := range tests { 2158 outputs, err := removeAboveValue(ctx, singlePathSpec{ 2159 Values: generateSeriesList(ctx, start, test.inputs, step), 2160 }, test.n) 2161 require.NoError(t, err) 2162 for i := range test.outputs { // overwrite series names 2163 test.outputs[i].Name = fmt.Sprintf( 2164 "removeAboveValue(%s, "+common.FloatingPointFormat+")", 2165 test.outputs[i].Name, 2166 test.n, 2167 ) 2168 } 2169 common.CompareOutputsAndExpected(t, step, start, 2170 test.outputs, outputs.Values) 2171 } 2172 } 2173 2174 func TestRemoveEmptySeries(t *testing.T) { 2175 ctx := common.NewTestContext() 2176 defer func() { _ = ctx.Close() }() 2177 2178 nan := math.NaN() 2179 tests := []struct { 2180 inputs []common.TestSeries 2181 xFilesFactor float64 2182 outputs []common.TestSeries 2183 }{ 2184 { 2185 []common.TestSeries{ 2186 {Name: "foo", Data: []float64{500, 600, 700}}, 2187 {Name: "bar", Data: []float64{500, 600, nan}}, 2188 {Name: "baz", Data: []float64{500, nan, nan}}, 2189 {Name: "qux", Data: []float64{nan, nan, nan}}, 2190 }, 2191 0, 2192 []common.TestSeries{ 2193 {Name: "foo", Data: []float64{500, 600, 700}}, 2194 {Name: "bar", Data: []float64{500, 600, nan}}, 2195 {Name: "baz", Data: []float64{500, nan, nan}}, 2196 }, 2197 }, 2198 { 2199 []common.TestSeries{ 2200 {Name: "foo", Data: []float64{500, 600, 700}}, 2201 {Name: "bar", Data: []float64{500, 600, nan}}, 2202 {Name: "baz", Data: []float64{500, nan, nan}}, 2203 {Name: "qux", Data: []float64{nan, nan, nan}}, 2204 }, 2205 0.5, 2206 []common.TestSeries{ 2207 {Name: "foo", Data: []float64{500, 600, 700}}, 2208 {Name: "bar", Data: []float64{500, 600, nan}}, 2209 }, 2210 }, 2211 { 2212 []common.TestSeries{ 2213 {Name: "foo", Data: []float64{500, 600, 700}}, 2214 {Name: "bar", Data: []float64{500, 600, nan}}, 2215 {Name: "baz", Data: []float64{500, nan, nan}}, 2216 {Name: "qux", Data: []float64{nan, nan, nan}}, 2217 }, 2218 1, 2219 []common.TestSeries{ 2220 {Name: "foo", Data: []float64{500, 600, 700}}, 2221 }, 2222 }, 2223 } 2224 start := time.Now() 2225 step := 100 2226 for _, test := range tests { 2227 outputs, err := removeEmptySeries(ctx, 2228 singlePathSpec{Values: generateSeriesList(ctx, start, test.inputs, step)}, 2229 test.xFilesFactor) 2230 require.NoError(t, err) 2231 common.CompareOutputsAndExpected(t, step, start, 2232 test.outputs, outputs.Values) 2233 } 2234 } 2235 2236 func TestFilterSeries(t *testing.T) { 2237 ctx := common.NewTestContext() 2238 defer func() { _ = ctx.Close() }() 2239 2240 nan := math.NaN() 2241 tests := []struct { 2242 inputs []common.TestSeries 2243 aggregationFn string 2244 comparator string 2245 threashold float64 2246 outputs []common.TestSeries 2247 }{ 2248 { 2249 []common.TestSeries{ 2250 {Name: "foo", Data: []float64{500, 600, 700}}, 2251 {Name: "bar", Data: []float64{500, 600, nan}}, 2252 {Name: "baz", Data: []float64{500, nan, nan}}, 2253 {Name: "qux", Data: []float64{nan, nan, nan}}, 2254 }, 2255 "max", 2256 ">", 2257 600, 2258 []common.TestSeries{ 2259 {Name: "foo", Data: []float64{500, 600, 700}}, 2260 }, 2261 }, 2262 { 2263 []common.TestSeries{ 2264 {Name: "foo", Data: []float64{500, 600, 700}}, 2265 {Name: "bar", Data: []float64{500, 600, nan}}, 2266 {Name: "baz", Data: []float64{500, nan, nan}}, 2267 {Name: "qux", Data: []float64{nan, nan, nan}}, 2268 }, 2269 "max", 2270 ">=", 2271 600, 2272 []common.TestSeries{ 2273 {Name: "foo", Data: []float64{500, 600, 700}}, 2274 {Name: "bar", Data: []float64{500, 600, nan}}, 2275 }, 2276 }, 2277 } 2278 start := time.Now() 2279 step := 100 2280 for _, test := range tests { 2281 outputs, err := filterSeries(ctx, 2282 singlePathSpec{Values: generateSeriesList(ctx, start, test.inputs, step)}, 2283 test.aggregationFn, test.comparator, test.threashold) 2284 require.NoError(t, err) 2285 common.CompareOutputsAndExpected(t, step, start, 2286 test.outputs, outputs.Values) 2287 } 2288 } 2289 2290 func generateSeriesList(ctx *common.Context, start time.Time, inputs []common.TestSeries, step int) []*ts.Series { 2291 tSeriesList := make([]*ts.Series, 0, len(inputs)) 2292 for _, in := range inputs { 2293 tSeries := ts.NewSeries(ctx, in.Name, start, common.NewTestSeriesValues(ctx, step, in.Data)) 2294 tSeriesList = append(tSeriesList, tSeries) 2295 } 2296 return tSeriesList 2297 } 2298 2299 func TestScaleToSeconds(t *testing.T) { 2300 ctx := common.NewTestContext() 2301 defer func() { _ = ctx.Close() }() 2302 2303 tests := []struct { 2304 millisPerStep int 2305 values []float64 2306 expected []float64 2307 seconds int 2308 }{ 2309 { 2310 1000, 2311 []float64{1000.0, 2000.0, 3000.0, 4000.0, 5000.0}, 2312 []float64{2000.0, 4000.0, 6000.0, 8000.0, 10000.0}, 2313 2, 2314 }, 2315 // expected values should double when step is halved 2316 // relative to the original expected values 2317 { 2318 500, 2319 []float64{1000.0, 2000.0, 3000.0, 4000.0, 5000.0}, 2320 []float64{4000.0, 8000.0, 12000.0, 16000.0, 20000.0}, 2321 2, 2322 }, 2323 // expected values should drop by a factor of 1/5 when step is multiplied by 5 2324 // relative to the original expected values 2325 { 2326 5000, 2327 []float64{1000.0, 2000.0, 3000.0, 4000.0, 5000.0}, 2328 []float64{400.0, 800.0, 1200.0, 1600.0, 2000.0}, 2329 2, 2330 }, 2331 } 2332 2333 for _, test := range tests { 2334 timeSeries := ts.NewSeries(ctx, "<values>", ctx.StartTime, 2335 common.NewTestSeriesValues(ctx, test.millisPerStep, test.values)) 2336 2337 r, err := scaleToSeconds(ctx, singlePathSpec{ 2338 Values: []*ts.Series{timeSeries}, 2339 }, test.seconds) 2340 require.NoError(t, err) 2341 2342 output := r.Values 2343 require.Equal(t, 1, len(output)) 2344 assert.Equal(t, "scaleToSeconds(<values>,2)", output[0].Name()) 2345 for step := 0; step < output[0].Len(); step++ { 2346 v := output[0].ValueAt(step) 2347 assert.Equal(t, test.expected[step], v) 2348 } 2349 } 2350 } 2351 2352 func TestAsPercentWithSeriesTotal(t *testing.T) { 2353 ctx := common.NewTestContext() 2354 defer func() { _ = ctx.Close() }() 2355 2356 tests := []struct { 2357 valuesStep int 2358 values []float64 2359 totalsStep int 2360 totals []float64 2361 outputStep int 2362 output []float64 2363 }{ 2364 { 2365 100, 2366 []float64{10.0, 20.0, 30.0, 40.0, 50.0}, 2367 100, 2368 []float64{1000.0, 1000.0, 1000.0, 1000.0, 1000.0}, 2369 100, 2370 []float64{1.0, 2.0, 3.0, 4.0, 5.0}, 2371 }, 2372 { 2373 100, 2374 []float64{12.0, 14.0, 16.0, math.NaN(), 20.0}, 2375 150, 2376 []float64{50.0, 50.0, 25.0, 50.0, 50.0}, 2377 300, 2378 []float64{28.0, 53.0}, 2379 }, 2380 } 2381 2382 for _, test := range tests { 2383 timeSeries := ts.NewSeries(ctx, "<values>", ctx.StartTime, 2384 common.NewTestSeriesValues(ctx, test.valuesStep, test.values)) 2385 totalSeries := ts.NewSeries(ctx, "<totals>", ctx.StartTime, 2386 common.NewTestSeriesValues(ctx, test.totalsStep, test.totals)) 2387 2388 r, err := asPercent(ctx, singlePathSpec{ 2389 Values: []*ts.Series{timeSeries}, 2390 }, ts.SeriesList{ 2391 Values: []*ts.Series{totalSeries}, 2392 }) 2393 require.NoError(t, err, fmt.Sprintf("err: %v", err)) 2394 2395 output := r.Values 2396 require.Equal(t, 1, len(output)) 2397 require.Equal(t, output[0].MillisPerStep(), test.outputStep) 2398 assert.Equal(t, "asPercent(<values>,<totals>)", output[0].Name()) 2399 2400 for step := 0; step < output[0].Len(); step++ { 2401 v := output[0].ValueAt(step) 2402 assert.Equal(t, math.Trunc(v), test.output[step]) 2403 } 2404 } 2405 } 2406 2407 func TestAsPercentWithFloatTotal(t *testing.T) { 2408 ctx := common.NewTestContext() 2409 defer func() { _ = ctx.Close() }() 2410 2411 nan := math.NaN() 2412 tests := []struct { 2413 valuesStep int 2414 values []float64 2415 total float64 2416 outputStep int 2417 output []float64 2418 }{ 2419 { 2420 100, 2421 []float64{12.0, 14.0, 16.0, nan, 20.0}, 2422 20.0, 2423 100, 2424 []float64{60, 70, 80, nan, 100}, 2425 }, 2426 { 2427 100, 2428 []float64{12.0, 14.0, 16.0, nan, 20.0}, 2429 0, 2430 100, 2431 []float64{nan, nan, nan, nan, nan}, 2432 }, 2433 } 2434 2435 for _, test := range tests { 2436 timeSeries := ts.NewSeries(ctx, "<values>", ctx.StartTime, 2437 common.NewTestSeriesValues(ctx, test.valuesStep, test.values)) 2438 r, err := asPercent(ctx, singlePathSpec{ 2439 Values: []*ts.Series{timeSeries}, 2440 }, test.total) 2441 require.NoError(t, err) 2442 2443 output := r.Values 2444 require.Equal(t, 1, len(output)) 2445 require.Equal(t, output[0].MillisPerStep(), test.outputStep) 2446 expectedName := fmt.Sprintf("asPercent(<values>,"+common.FloatingPointFormat+")", 2447 test.total) 2448 assert.Equal(t, expectedName, output[0].Name()) 2449 2450 for step := 0; step < output[0].Len(); step++ { 2451 v := output[0].ValueAt(step) 2452 xtest.Equalish(t, math.Trunc(v), test.output[step]) 2453 } 2454 } 2455 } 2456 2457 func TestAsPercentWithNilTotal(t *testing.T) { 2458 ctx := common.NewTestContext() 2459 defer func() { _ = ctx.Close() }() 2460 2461 nan := math.NaN() 2462 tests := []struct { 2463 valuesStep int 2464 values []float64 2465 outputStep int 2466 output []float64 2467 }{ 2468 { 2469 60, 2470 []float64{12.0, 14.0, 16.0, nan, 20.0}, 2471 60, 2472 []float64{100, 100, 100, nan, 100}, 2473 }, 2474 } 2475 2476 for _, test := range tests { 2477 timeSeries := ts.NewSeries(ctx, "<values>", ctx.StartTime, 2478 common.NewTestSeriesValues(ctx, test.valuesStep, test.values)) 2479 r, err := asPercent(ctx, singlePathSpec{ 2480 Values: []*ts.Series{timeSeries}, 2481 }, nil) 2482 require.NoError(t, err) 2483 2484 output := r.Values 2485 require.Equal(t, 1, len(output)) 2486 require.Equal(t, output[0].MillisPerStep(), test.outputStep) 2487 expectedName := "asPercent(<values>,sumSeries(<values>))" 2488 assert.Equal(t, expectedName, output[0].Name()) 2489 2490 for step := 0; step < output[0].Len(); step++ { 2491 v := output[0].ValueAt(step) 2492 xtest.Equalish(t, math.Trunc(v), test.output[step]) 2493 } 2494 } 2495 } 2496 2497 func TestAsPercentWithSeriesList(t *testing.T) { 2498 ctx := common.NewTestContext() 2499 defer func() { _ = ctx.Close() }() 2500 2501 nan := math.NaN() 2502 inputs := []struct { 2503 name string 2504 step int 2505 values []float64 2506 }{ 2507 { 2508 "foo", 2509 100, 2510 []float64{12.0, 14.0, 16.0, nan, 20.0, 30.0}, 2511 }, 2512 { 2513 "bar", 2514 200, 2515 []float64{7.0, nan, 25.0}, 2516 }, 2517 } 2518 outputs := []struct { 2519 name string 2520 step int 2521 values []float64 2522 }{ 2523 { 2524 "asPercent(foo,sumSeries(foo,bar))", 2525 200, 2526 []float64{65.0, 100.0, 50.0}, 2527 }, 2528 { 2529 "asPercent(bar,sumSeries(foo,bar))", 2530 200, 2531 []float64{35.0, nan, 50.0}, 2532 }, 2533 } 2534 2535 inputSeries := make([]*ts.Series, 0, len(inputs)) 2536 for _, input := range inputs { 2537 timeSeries := ts.NewSeries( 2538 ctx, 2539 input.name, 2540 ctx.StartTime, 2541 common.NewTestSeriesValues(ctx, input.step, input.values), 2542 ) 2543 inputSeries = append(inputSeries, timeSeries) 2544 } 2545 2546 expected := make([]*ts.Series, 0, len(outputs)) 2547 for _, output := range outputs { 2548 timeSeries := ts.NewSeries( 2549 ctx, 2550 output.name, 2551 ctx.StartTime, 2552 common.NewTestSeriesValues(ctx, output.step, output.values), 2553 ) 2554 expected = append(expected, timeSeries) 2555 } 2556 2557 r, err := asPercent(ctx, singlePathSpec{ 2558 Values: inputSeries, 2559 }, nil) 2560 require.NoError(t, err) 2561 requireEqual(t, expected, r.Values) 2562 } 2563 2564 func requireEqual(t *testing.T, expected, results []*ts.Series) { 2565 require.Equal(t, len(expected), len(results)) 2566 for i := 0; i < len(results); i++ { 2567 require.Equal(t, expected[i].MillisPerStep(), results[i].MillisPerStep()) 2568 require.Equal(t, expected[i].Len(), results[i].Len()) 2569 require.Equal(t, expected[i].Name(), results[i].Name()) 2570 for step := 0; step < results[i].Len(); step++ { 2571 xtest.Equalish(t, expected[i].ValueAt(step), results[i].ValueAt(step)) 2572 } 2573 } 2574 } 2575 2576 func TestAsPercentWithSeriesListAndTotalSeriesList(t *testing.T) { 2577 ctx := common.NewTestContext() 2578 defer func() { _ = ctx.Close() }() 2579 2580 nan := math.NaN() 2581 inputs := []struct { 2582 name string 2583 step int 2584 values []float64 2585 }{ 2586 { 2587 "foo.value", 2588 100, 2589 []float64{12.0, 14.0, 16.0, nan, 20.0, 30.0}, 2590 }, 2591 { 2592 "bar.value", 2593 200, 2594 []float64{7.0, nan, 25.0}, 2595 }, 2596 } 2597 totals := []struct { 2598 name string 2599 step int 2600 values []float64 2601 }{ 2602 { 2603 "foo.total", 2604 100, 2605 []float64{24.0, 28.0, 48.0, nan, 40.0, 60.0}, 2606 }, 2607 { 2608 "bar.total", 2609 200, 2610 []float64{14.0, nan, 75.0}, 2611 }, 2612 } 2613 outputs := []struct { 2614 name string 2615 step int 2616 values []float64 2617 }{ 2618 { 2619 "asPercent(bar.value,bar.total)", 2620 200, 2621 []float64{50.0, nan, 33.33333333333333}, 2622 }, 2623 { 2624 "asPercent(foo.value,foo.total)", 2625 100, 2626 []float64{50.0, 50.0, 33.33333333333333, nan, 50.0, 50.0}, 2627 }, 2628 } 2629 2630 var inputSeries []*ts.Series // nolint: prealloc 2631 for _, input := range inputs { 2632 timeSeries := ts.NewSeries( 2633 ctx, 2634 input.name, 2635 ctx.StartTime, 2636 common.NewTestSeriesValues(ctx, input.step, input.values), 2637 ) 2638 inputSeries = append(inputSeries, timeSeries) 2639 } 2640 2641 var totalSeries []*ts.Series // nolint: prealloc 2642 for _, input := range totals { 2643 timeSeries := ts.NewSeries( 2644 ctx, 2645 input.name, 2646 ctx.StartTime, 2647 common.NewTestSeriesValues(ctx, input.step, input.values), 2648 ) 2649 totalSeries = append(totalSeries, timeSeries) 2650 } 2651 2652 var expected []*ts.Series // nolint: prealloc 2653 for _, output := range outputs { 2654 timeSeries := ts.NewSeries( 2655 ctx, 2656 output.name, 2657 ctx.StartTime, 2658 common.NewTestSeriesValues(ctx, output.step, output.values), 2659 ) 2660 expected = append(expected, timeSeries) 2661 } 2662 2663 r, err := asPercent(ctx, singlePathSpec{ 2664 Values: inputSeries, 2665 }, singlePathSpec{ 2666 Values: totalSeries, 2667 }) 2668 require.NoError(t, err) 2669 requireEqual(t, expected, r.Values) 2670 } 2671 2672 func TestAsPercentWithSeriesListAndEmptyTotalSeriesList(t *testing.T) { 2673 ctx := common.NewTestContext() 2674 defer func() { _ = ctx.Close() }() 2675 2676 inputs := []struct { 2677 name string 2678 step int 2679 values []float64 2680 }{ 2681 { 2682 "foo.bar", 2683 100, 2684 []float64{2.5, 5, 7.5, 10}, 2685 }, 2686 { 2687 "foo.baz", 2688 100, 2689 []float64{10, 20, 30, 40}, 2690 }, 2691 } 2692 outputs := []struct { 2693 name string 2694 step int 2695 values []float64 2696 }{ 2697 { 2698 "asPercent(foo.bar,sumSeries(foo.*))", 2699 100, 2700 []float64{20, 20, 20, 20}, 2701 }, 2702 { 2703 "asPercent(foo.baz,sumSeries(foo.*))", 2704 100, 2705 []float64{80, 80, 80, 80}, 2706 }, 2707 } 2708 2709 var inputSeries []*ts.Series // nolint: prealloc 2710 for _, input := range inputs { 2711 timeSeries := ts.NewSeries( 2712 ctx, 2713 input.name, 2714 ctx.StartTime, 2715 common.NewTestSeriesValues(ctx, input.step, input.values), 2716 ) 2717 timeSeries.Specification = "foo.*" 2718 inputSeries = append(inputSeries, timeSeries) 2719 } 2720 2721 var expected []*ts.Series // nolint: prealloc 2722 for _, output := range outputs { 2723 timeSeries := ts.NewSeries( 2724 ctx, 2725 output.name, 2726 ctx.StartTime, 2727 common.NewTestSeriesValues(ctx, output.step, output.values), 2728 ) 2729 expected = append(expected, timeSeries) 2730 } 2731 2732 r, err := asPercent(ctx, singlePathSpec{ 2733 Values: inputSeries, 2734 }, nil) 2735 require.NoError(t, err) 2736 requireEqual(t, expected, r.Values) 2737 } 2738 2739 func TestAsPercentWithNodesAndTotalNil(t *testing.T) { 2740 ctx := common.NewTestContext() 2741 defer func() { _ = ctx.Close() }() 2742 2743 inputs := []struct { 2744 name string 2745 step int 2746 values []float64 2747 }{ 2748 { 2749 "cpu.foo.core1", 2750 200, 2751 []float64{12.0, 5.0, 48.0}, 2752 }, 2753 { 2754 "cpu.foo.core2", 2755 200, 2756 []float64{12.0, 15.0, 16.0}, 2757 }, 2758 { 2759 "cpu.bar.core1", 2760 200, 2761 []float64{12.0, 14.0, 16.0}, 2762 }, 2763 } 2764 outputs := []struct { 2765 name string 2766 step int 2767 values []float64 2768 }{ 2769 { 2770 "asPercent(cpu.bar.core1,cpu.bar.core1)", 2771 200, 2772 []float64{100.0, 100.0, 100.0}, 2773 }, 2774 { 2775 "asPercent(cpu.foo.core1,sumSeries(cpu.foo.core1,cpu.foo.core2))", 2776 200, 2777 []float64{50.0, 25.0, 75.0}, 2778 }, 2779 { 2780 "asPercent(cpu.foo.core2,sumSeries(cpu.foo.core1,cpu.foo.core2))", 2781 200, 2782 []float64{50.0, 75.0, 25.0}, 2783 }, 2784 } 2785 2786 var inputSeries []*ts.Series // nolint: prealloc 2787 for _, input := range inputs { 2788 timeSeries := ts.NewSeries( 2789 ctx, 2790 input.name, 2791 ctx.StartTime, 2792 common.NewTestSeriesValues(ctx, input.step, input.values), 2793 ) 2794 inputSeries = append(inputSeries, timeSeries) 2795 } 2796 2797 var expected []*ts.Series // nolint: prealloc 2798 for _, output := range outputs { 2799 timeSeries := ts.NewSeries( 2800 ctx, 2801 output.name, 2802 ctx.StartTime, 2803 common.NewTestSeriesValues(ctx, output.step, output.values), 2804 ) 2805 expected = append(expected, timeSeries) 2806 } 2807 2808 r, err := asPercent(ctx, singlePathSpec{ 2809 Values: inputSeries, 2810 }, nil, 1) 2811 require.NoError(t, err) 2812 requireEqual(t, expected, r.Values) 2813 } 2814 2815 func TestAsPercentWithNodesAndTotalSeriesList(t *testing.T) { 2816 ctx := common.NewTestContext() 2817 defer func() { _ = ctx.Close() }() 2818 2819 nan := math.NaN() 2820 inputs := []struct { 2821 name string 2822 step int 2823 values []float64 2824 }{ 2825 { 2826 "cpu.foo.core1", 2827 200, 2828 []float64{12.0, 5.0, 48.0}, 2829 }, 2830 { 2831 "cpu.foo.core2", 2832 200, 2833 []float64{12.0, 15.0, 16.0}, 2834 }, 2835 { 2836 "cpu.bar.core1", 2837 200, 2838 []float64{12.0, 14.0, 16.0}, 2839 }, 2840 { 2841 "cpu.qux.core1", 2842 200, 2843 []float64{12.0, 14.0, 16.0}, 2844 }, 2845 } 2846 totals := []struct { 2847 name string 2848 step int 2849 values []float64 2850 }{ 2851 { 2852 "cpu_cluster.foo.zone-a", 2853 200, 2854 []float64{24.0, 40.0, 256.0}, 2855 }, 2856 { 2857 "cpu_cluster.foo.zone-b", 2858 200, 2859 []float64{24.0, 40.0, 256.0}, 2860 }, 2861 { 2862 "cpu_cluster.bar", 2863 200, 2864 []float64{48.0, 14.0, 16.0}, 2865 }, 2866 { 2867 "cpu_cluster.baz", 2868 200, 2869 []float64{12.0, 14.0, 16.0}, 2870 }, 2871 } 2872 outputs := []struct { 2873 name string 2874 step int 2875 values []float64 2876 }{ 2877 { 2878 "asPercent(cpu.bar.core1,cpu_cluster.bar)", 2879 200, 2880 []float64{25.0, 100.0, 100.0}, 2881 }, 2882 { 2883 "asPercent(MISSING,cpu_cluster.baz)", 2884 200, 2885 []float64{nan, nan, nan}, 2886 }, 2887 { 2888 "asPercent(cpu.foo.core1,sumSeries(cpu_cluster.foo.zone-a,cpu_cluster.foo.zone-b))", 2889 200, 2890 []float64{25.0, 6.25, 9.375}, 2891 }, 2892 { 2893 "asPercent(cpu.foo.core2,sumSeries(cpu_cluster.foo.zone-a,cpu_cluster.foo.zone-b))", 2894 200, 2895 []float64{25.0, 18.75, 3.125}, 2896 }, 2897 { 2898 "asPercent(cpu.qux.core1,MISSING)", 2899 200, 2900 []float64{nan, nan, nan}, 2901 }, 2902 } 2903 2904 var inputSeries []*ts.Series // nolint: prealloc 2905 for _, input := range inputs { 2906 timeSeries := ts.NewSeries( 2907 ctx, 2908 input.name, 2909 ctx.StartTime, 2910 common.NewTestSeriesValues(ctx, input.step, input.values), 2911 ) 2912 inputSeries = append(inputSeries, timeSeries) 2913 } 2914 2915 var totalSeries []*ts.Series // nolint: prealloc 2916 for _, input := range totals { 2917 timeSeries := ts.NewSeries( 2918 ctx, 2919 input.name, 2920 ctx.StartTime, 2921 common.NewTestSeriesValues(ctx, input.step, input.values), 2922 ) 2923 totalSeries = append(totalSeries, timeSeries) 2924 } 2925 2926 var expected []*ts.Series // nolint: prealloc 2927 for _, output := range outputs { 2928 timeSeries := ts.NewSeries( 2929 ctx, 2930 output.name, 2931 ctx.StartTime, 2932 common.NewTestSeriesValues(ctx, output.step, output.values), 2933 ) 2934 expected = append(expected, timeSeries) 2935 } 2936 2937 r, err := asPercent(ctx, singlePathSpec{ 2938 Values: inputSeries, 2939 }, singlePathSpec{ 2940 Values: totalSeries, 2941 }, 1) 2942 require.NoError(t, err) 2943 requireEqual(t, expected, r.Values) 2944 } 2945 2946 func testLogarithm(t *testing.T, base float64, asserts func(*ts.Series)) { 2947 ctx := common.NewTestContext() 2948 defer func() { _ = ctx.Close() }() 2949 2950 invals := make([]float64, 101) 2951 for i := range invals { 2952 invals[i] = float64(i) 2953 } 2954 2955 series := ts.NewSeries(ctx, "hello", time.Now(), 2956 common.NewTestSeriesValues(ctx, 10000, invals)) 2957 2958 r, err := logarithm(ctx, singlePathSpec{ 2959 Values: []*ts.Series{series}, 2960 }, base) 2961 require.NoError(t, err) 2962 2963 output := r.Values 2964 require.Equal(t, 1, len(output)) 2965 assert.Equal(t, fmt.Sprintf("log(hello, %f)", base), output[0].Name()) 2966 assert.Equal(t, series.StartTime(), output[0].StartTime()) 2967 require.Equal(t, len(invals), output[0].Len()) 2968 xtest.Equalish(t, math.NaN(), output[0].ValueAt(0)) 2969 asserts(output[0]) 2970 } 2971 2972 func TestLogarithm(t *testing.T) { 2973 testLogarithm(t, 10, func(output *ts.Series) { 2974 xtest.Equalish(t, 0, output.ValueAt(1)) 2975 xtest.Equalish(t, 1, output.ValueAt(10)) 2976 xtest.Equalish(t, 2, output.ValueAt(100)) 2977 }) 2978 testLogarithm(t, 2, func(output *ts.Series) { 2979 xtest.Equalish(t, 0, output.ValueAt(1)) 2980 xtest.Equalish(t, 1, output.ValueAt(2)) 2981 xtest.Equalish(t, 2, output.ValueAt(4)) 2982 }) 2983 testLogarithm(t, 3.142, func(output *ts.Series) { 2984 xtest.Equalish(t, 0, output.ValueAt(1)) 2985 xtest.Equalish(t, 0.6054429879326457, output.ValueAt(2)) 2986 xtest.Equalish(t, 0.9596044321978149, output.ValueAt(3)) 2987 }) 2988 2989 _, err := logarithm(nil, singlePathSpec{}, -1) 2990 require.NotNil(t, err) 2991 } 2992 2993 func TestIntegral(t *testing.T) { 2994 ctx := common.NewTestContext() 2995 defer func() { _ = ctx.Close() }() 2996 2997 invals := []float64{ 2998 0, 1, 2, 3, 4, 5, 6, math.NaN(), 8, math.NaN(), 2999 } 3000 3001 outvals := []float64{ 3002 0, 1, 3, 6, 10, 15, 21, math.NaN(), 29, math.NaN(), 3003 } 3004 3005 series := ts.NewSeries(ctx, "hello", time.Now(), 3006 common.NewTestSeriesValues(ctx, 10000, invals)) 3007 3008 r, err := integral(ctx, singlePathSpec{ 3009 Values: []*ts.Series{series}, 3010 }) 3011 require.NoError(t, err) 3012 3013 output := r.Values 3014 require.Equal(t, 1, len(output)) 3015 assert.Equal(t, "integral(hello)", output[0].Name()) 3016 assert.Equal(t, series.StartTime(), output[0].StartTime()) 3017 require.Equal(t, len(outvals), output[0].Len()) 3018 for i, expected := range outvals { 3019 xtest.Equalish(t, expected, output[0].ValueAt(i), "incorrect value at %d", i) 3020 } 3021 } 3022 3023 func TestInterpolate(t *testing.T) { 3024 ctx := common.NewTestContext() 3025 defer func() { _ = ctx.Close() }() 3026 3027 tests := []struct { 3028 values []float64 3029 output []float64 3030 limit int 3031 }{ 3032 { 3033 []float64{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0}, 3034 []float64{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0}, 3035 -1, 3036 }, 3037 { 3038 []float64{math.NaN(), 2.0, math.NaN(), 4.0, math.NaN(), 6.0, math.NaN(), 8.0, math.NaN(), 10.0, math.NaN(), 12.0, math.NaN(), 14.0, math.NaN(), 16.0, math.NaN(), 18.0, math.NaN(), 20.0}, 3039 []float64{math.NaN(), 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0}, 3040 -1, 3041 }, 3042 { 3043 []float64{1.0, 2.0, math.NaN(), math.NaN(), math.NaN(), 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, math.NaN(), math.NaN(), math.NaN()}, 3044 []float64{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, math.NaN(), math.NaN(), math.NaN()}, 3045 -1, 3046 }, 3047 { 3048 []float64{1.0, 2.0, 3.0, 4.0, math.NaN(), 6.0, math.NaN(), math.NaN(), 9.0, 10.0, 11.0, math.NaN(), 13.0, math.NaN(), math.NaN(), math.NaN(), math.NaN(), 18.0, 19.0, 20.0}, 3049 []float64{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0}, 3050 -1, 3051 }, 3052 { 3053 []float64{1.0, 2.0, math.NaN(), math.NaN(), math.NaN(), 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, math.NaN(), math.NaN()}, 3054 []float64{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, math.NaN(), math.NaN()}, 3055 -1, 3056 }, 3057 { 3058 []float64{1.0, 2.0, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, math.NaN(), math.NaN()}, 3059 []float64{1.0, 2.0, math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, math.NaN(), math.NaN()}, 3060 3, 3061 }, 3062 { 3063 []float64{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0}, 3064 []float64{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0}, 3065 -1, 3066 }, 3067 } 3068 3069 start := time.Now() 3070 step := 100 3071 for _, test := range tests { 3072 input := []common.TestSeries{{Name: "foo", Data: test.values}} 3073 expected := []common.TestSeries{{Name: "interpolate(foo)", Data: test.output}} 3074 timeSeries := generateSeriesList(ctx, start, input, step) 3075 output, err := interpolate(ctx, singlePathSpec{ 3076 Values: timeSeries, 3077 }, test.limit) 3078 require.NoError(t, err) 3079 common.CompareOutputsAndExpected(t, step, start, 3080 expected, output.Values) 3081 } 3082 } 3083 3084 func TestIntegralByInterval(t *testing.T) { 3085 ctx := common.NewTestContext() 3086 defer func() { _ = ctx.Close() }() 3087 3088 invals := []float64{ 3089 math.NaN(), 1, 2, 3, 4, 5, math.NaN(), 6, 7, 8, 3090 } 3091 3092 outvals := []float64{ 3093 0, 1, 2, 5, 4, 9, 0, 6, 7, 15, 3094 } 3095 3096 series := ts.NewSeries(ctx, "hello", time.Now(), 3097 common.NewTestSeriesValues(ctx, 60000, invals)) 3098 3099 r, err := integralByInterval(ctx, singlePathSpec{ 3100 Values: []*ts.Series{series}, 3101 }, "2min") 3102 require.NoError(t, err) 3103 3104 output := r.Values 3105 require.Equal(t, 1, len(output)) 3106 assert.Equal(t, "integralByInterval(hello, 2min)", output[0].Name()) 3107 assert.Equal(t, series.StartTime(), output[0].StartTime()) 3108 require.Equal(t, len(outvals), output[0].Len()) 3109 for i, expected := range outvals { 3110 xtest.Equalish(t, expected, output[0].ValueAt(i), "incorrect value at %d", i) 3111 } 3112 } 3113 3114 func TestDerivative(t *testing.T) { 3115 ctx := common.NewTestContext() 3116 defer func() { _ = ctx.Close() }() 3117 3118 tests := []struct { 3119 values []float64 3120 output []float64 3121 }{ 3122 { 3123 []float64{10.0, 20.0, 30.0, 5.0, 5.0}, 3124 []float64{math.NaN(), 10.0, 10.0, -25.0, 0.0}, 3125 }, 3126 { 3127 []float64{50.0, 50.0, 25.0, 250.0, 350.0}, 3128 []float64{math.NaN(), 0.0, -25.0, 225.0, 100.0}, 3129 }, 3130 } 3131 3132 start := time.Now() 3133 step := 100 3134 for _, test := range tests { 3135 input := []common.TestSeries{{Name: "foo", Data: test.values}} 3136 expected := []common.TestSeries{{Name: "derivative(foo)", Data: test.output}} 3137 timeSeries := generateSeriesList(ctx, start, input, step) 3138 output, err := derivative(ctx, singlePathSpec{ 3139 Values: timeSeries, 3140 }) 3141 require.NoError(t, err) 3142 common.CompareOutputsAndExpected(t, step, start, 3143 expected, output.Values) 3144 } 3145 } 3146 3147 func TestNonNegativeDerivative(t *testing.T) { 3148 ctx := common.NewTestContext() 3149 defer func() { _ = ctx.Close() }() 3150 3151 tests := []struct { 3152 values []float64 3153 maxValue float64 3154 output []float64 3155 }{ 3156 { 3157 []float64{10.0, 20.0, 30.0, 5.0, 5.0}, 3158 math.NaN(), 3159 []float64{math.NaN(), 10.0, 10.0, math.NaN(), 0.0}, 3160 }, 3161 { 3162 []float64{50.0, 50.0, 25.0, 250.0, 350.0}, 3163 100.0, 3164 []float64{math.NaN(), 0.0, 76.0, 225.0, 100.0}, 3165 }, 3166 } 3167 3168 start := time.Now() 3169 step := 100 3170 for _, test := range tests { 3171 input := []common.TestSeries{{Name: "foo", Data: test.values}} 3172 expected := []common.TestSeries{{Name: "nonNegativeDerivative(foo)", Data: test.output}} 3173 timeSeries := generateSeriesList(ctx, start, input, step) 3174 output, err := nonNegativeDerivative(ctx, singlePathSpec{ 3175 Values: timeSeries, 3176 }, test.maxValue) 3177 require.NoError(t, err) 3178 common.CompareOutputsAndExpected(t, step, start, expected, output.Values) 3179 } 3180 } 3181 3182 type TimeSeriesPtrVector []*ts.Series 3183 3184 func (o TimeSeriesPtrVector) Len() int { return len(o) } 3185 func (o TimeSeriesPtrVector) Less(i, j int) bool { return o[i].Name() < o[j].Name() } 3186 func (o TimeSeriesPtrVector) Swap(i, j int) { o[i], o[j] = o[j], o[i] } 3187 3188 func TestConstantLine(t *testing.T) { 3189 ctx := common.NewTestContext() 3190 defer func() { _ = ctx.Close() }() 3191 3192 testValue := 5.0 3193 r, err := constantLine(ctx, testValue) 3194 require.Nil(t, err) 3195 3196 testSeries := r.Values 3197 require.Equal(t, 1, len(testSeries)) 3198 require.Equal(t, 3, testSeries[0].Len()) 3199 expectedName := fmt.Sprintf(common.FloatingPointFormat, testValue) 3200 require.Equal(t, expectedName, testSeries[0].Name()) 3201 for i := 0; i < testSeries[0].Len(); i++ { 3202 require.Equal(t, float64(testValue), testSeries[0].ValueAt(i)) 3203 } 3204 } 3205 3206 func TestIdentity(t *testing.T) { 3207 ctx := common.NewTestContext() 3208 defer func() { _ = ctx.Close() }() 3209 3210 testName := "testName.mytest" 3211 r, err := identity(ctx, testName) 3212 require.Nil(t, err) 3213 3214 testSeries := r.Values 3215 require.Equal(t, 1, len(testSeries)) 3216 require.Equal(t, testName, testSeries[0].Name()) 3217 require.Equal(t, 60, testSeries[0].Len()) 3218 expectedValue := ctx.StartTime.Unix() 3219 for i := 0; i < testSeries[0].Len(); i++ { 3220 require.Equal(t, float64(expectedValue), testSeries[0].ValueAt(i)) 3221 expectedValue += 60 3222 } 3223 } 3224 3225 func TestLimit(t *testing.T) { 3226 ctx := common.NewTestContext() 3227 defer func() { _ = ctx.Close() }() 3228 3229 // invalid input 3230 testInput := getTestInput(ctx) 3231 testSeries, err := limit(ctx, singlePathSpec{ 3232 Values: testInput, 3233 }, -1) 3234 require.NotNil(t, err) 3235 3236 // valid input 3237 testSeries, err = limit(ctx, singlePathSpec{ 3238 Values: testInput, 3239 }, 1) 3240 require.Nil(t, err) 3241 require.Equal(t, 1, testSeries.Len()) 3242 3243 // input bigger than length of series 3244 testSeries, err = limit(ctx, singlePathSpec{ 3245 Values: testInput, 3246 }, 10) 3247 require.Nil(t, err) 3248 require.Equal(t, len(testInput), testSeries.Len()) 3249 } 3250 3251 func TestLimitSortStable(t *testing.T) { 3252 ctx := common.NewTestContext() 3253 defer func() { _ = ctx.Close() }() 3254 3255 constValues := common.NewTestSeriesValues(ctx, 1000, []float64{1, 2, 3, 4}) 3256 series := []*ts.Series{ 3257 ts.NewSeries(ctx, "qux", time.Now(), constValues), 3258 ts.NewSeries(ctx, "bar", time.Now(), constValues), 3259 ts.NewSeries(ctx, "foo", time.Now(), constValues), 3260 ts.NewSeries(ctx, "baz", time.Now(), constValues), 3261 } 3262 3263 // Check that if input order is random that the same first 3264 // series is chosen deterministically each time if the results weren't 3265 // already ordered. 3266 var lastOrder []string 3267 for i := 0; i < 100; i++ { 3268 rand.Shuffle(len(series), func(i, j int) { 3269 series[i], series[j] = series[j], series[i] 3270 }) 3271 3272 result, err := limit(ctx, singlePathSpec(ts.SeriesList{ 3273 Values: series, 3274 SortApplied: false, 3275 }), 2) 3276 require.NoError(t, err) 3277 3278 order := make([]string, 0, len(result.Values)) 3279 for _, series := range result.Values { 3280 order = append(order, series.Name()) 3281 } 3282 3283 expectedOrder := lastOrder 3284 lastOrder = order 3285 if expectedOrder == nil { 3286 continue 3287 } 3288 3289 require.Equal(t, expectedOrder, order) 3290 } 3291 } 3292 3293 func TestHitcount(t *testing.T) { 3294 now := time.Now() 3295 tests := []struct { 3296 name string 3297 startTime time.Time 3298 stepInMilli int 3299 values []float64 3300 intervalString string 3301 newStartTime time.Time 3302 newStep int 3303 output []float64 3304 }{ 3305 { 3306 "foo", 3307 now, 3308 1000, 3309 []float64{1.0, 2.0, 3.0, 4.0, 5.0, math.NaN(), 6.0}, 3310 "2s", 3311 now.Add(-time.Second), 3312 2000, 3313 []float64{1.0, 5.0, 9.0, 6.0}, 3314 }, 3315 { 3316 "bar", 3317 now, 3318 1000, 3319 []float64{1.0, 2.0, 3.0, 4.0, 5.0, math.NaN(), 6.0}, 3320 "10s", 3321 now.Add(-3 * time.Second), 3322 10000, 3323 []float64{21.0}, 3324 }, 3325 } 3326 3327 for i, input := range tests { 3328 input := input 3329 t.Run(fmt.Sprintf("test_%d_%s", i, input.name), func(t *testing.T) { 3330 ctrl := xgomock.NewController(t) 3331 defer ctrl.Finish() 3332 3333 store := storage.NewMockStorage(ctrl) 3334 engine := NewEngine(store, CompileOptions{}) 3335 3336 ctx := common.NewContext(common.ContextOptions{ 3337 Start: input.startTime, 3338 End: input.startTime.Add(time.Second * 10), 3339 Engine: engine, 3340 }) 3341 defer func() { _ = ctx.Close() }() 3342 3343 series := ts.NewSeries(ctx, input.name, input.startTime, 3344 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values)) 3345 3346 target := fmt.Sprintf("hitcount(%s, %q, false)", input.name, input.intervalString) 3347 testSeriesFn := func( 3348 *common.Context, 3349 string, 3350 storage.FetchOptions, 3351 ) (*storage.FetchResult, error) { 3352 return &storage.FetchResult{SeriesList: []*ts.Series{series}}, nil 3353 } 3354 3355 store.EXPECT(). 3356 FetchByQuery(gomock.Any(), gomock.Any(), gomock.Any()). 3357 DoAndReturn(testSeriesFn). 3358 AnyTimes() 3359 expr, err := engine.Compile(target) 3360 require.NoError(t, err) 3361 res, err := expr.Execute(ctx) 3362 require.NoError(t, err) 3363 expected := common.TestSeries{ 3364 Name: fmt.Sprintf("hitcount(%s, %q)", input.name, input.intervalString), 3365 Data: input.output, 3366 } 3367 require.NoError(t, err) 3368 common.CompareOutputsAndExpected(t, input.newStep, input.newStartTime, 3369 []common.TestSeries{expected}, res.Values) 3370 }) 3371 } 3372 } 3373 3374 func TestSubstr(t *testing.T) { 3375 ctx := common.NewTestContext() 3376 defer func() { _ = ctx.Close() }() 3377 3378 now := ctx.StartTime 3379 input := struct { 3380 name string 3381 startTime time.Time 3382 stepInMilli int 3383 values []float64 3384 }{ 3385 "aliasByName(foo.bar,baz)", 3386 now, 3387 1000, 3388 []float64{1.0, 2.0, 3.0}, 3389 } 3390 3391 series := ts.NewSeries( 3392 ctx, 3393 input.name, 3394 input.startTime, 3395 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values), 3396 ) 3397 results, err := substr(ctx, singlePathSpec{ 3398 Values: []*ts.Series{series}, 3399 }, 1, 0) 3400 expected := common.TestSeries{Name: "bar", Data: input.values} 3401 require.NoError(t, err) 3402 common.CompareOutputsAndExpected(t, input.stepInMilli, input.startTime, 3403 []common.TestSeries{expected}, results.Values) 3404 3405 results, err = substr(ctx, singlePathSpec{ 3406 Values: []*ts.Series{series}, 3407 }, 0, 2) 3408 expected = common.TestSeries{Name: "foo.bar", Data: input.values} 3409 require.NoError(t, err) 3410 common.CompareOutputsAndExpected(t, input.stepInMilli, input.startTime, 3411 []common.TestSeries{expected}, results.Values) 3412 3413 results, err = substr(ctx, singlePathSpec{ 3414 Values: []*ts.Series{series}, 3415 }, 0, 0) 3416 expected = common.TestSeries{Name: "foo.bar", Data: input.values} 3417 require.NoError(t, err) 3418 common.CompareOutputsAndExpected(t, input.stepInMilli, input.startTime, 3419 []common.TestSeries{expected}, results.Values) 3420 3421 // Negative support -1, 0. 3422 results, err = substr(ctx, singlePathSpec{ 3423 Values: []*ts.Series{series}, 3424 }, -1, 0) 3425 expected = common.TestSeries{Name: "bar", Data: input.values} 3426 require.NoError(t, err) 3427 common.CompareOutputsAndExpected(t, input.stepInMilli, input.startTime, 3428 []common.TestSeries{expected}, results.Values) 3429 3430 // Negative support -3, 0. 3431 results, err = substr(ctx, singlePathSpec{ 3432 Values: []*ts.Series{series}, 3433 }, -3, 0) 3434 expected = common.TestSeries{Name: "bar", Data: input.values} 3435 require.NoError(t, err) 3436 common.CompareOutputsAndExpected(t, input.stepInMilli, input.startTime, 3437 []common.TestSeries{expected}, results.Values) 3438 3439 results, err = substr(ctx, singlePathSpec{ 3440 Values: []*ts.Series{series}, 3441 }, 2, 1) 3442 require.Error(t, err) 3443 3444 results, err = substr(ctx, singlePathSpec{ 3445 Values: []*ts.Series{series}, 3446 }, 3, 4) 3447 require.Error(t, err) 3448 } 3449 3450 type mockStorage struct{} 3451 3452 func (*mockStorage) FetchByQuery( 3453 ctx xctx.Context, 3454 query string, 3455 opts storage.FetchOptions, 3456 ) (*storage.FetchResult, error) { 3457 return storage.NewFetchResult(ctx, nil, block.NewResultMetadata()), nil 3458 } 3459 3460 func (*mockStorage) CompleteTags( 3461 ctx context.Context, 3462 query *querystorage.CompleteTagsQuery, 3463 opts *querystorage.FetchOptions, 3464 ) (*consolidators.CompleteTagsResult, error) { 3465 return nil, fmt.Errorf("not implemented") 3466 } 3467 3468 func TestHoltWintersForecast(t *testing.T) { 3469 ctx := common.NewTestContext() 3470 ctx.Engine = NewEngine(&mockStorage{}, CompileOptions{}) 3471 defer func() { _ = ctx.Close() }() 3472 3473 now := ctx.StartTime 3474 tests := []struct { 3475 name string 3476 startTime time.Time 3477 stepInMilli int 3478 values []float64 3479 duration time.Duration 3480 newStartTime time.Time 3481 newStep int 3482 output []float64 3483 }{ 3484 { 3485 "foo", 3486 now, 3487 1000, 3488 []float64{4, 5.0, 6.0}, 3489 3 * time.Second, 3490 now, 3491 1000, 3492 []float64{math.NaN(), 4.0, 4.10035}, 3493 }, 3494 } 3495 3496 for _, input := range tests { 3497 series := ts.NewSeries( 3498 ctx, 3499 input.name, 3500 input.startTime, 3501 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values), 3502 ) 3503 3504 results, err := holtWintersForecastInternal(ctx, singlePathSpec{ 3505 Values: []*ts.Series{series}, 3506 }, input.duration) 3507 expected := common.TestSeries{ 3508 Name: fmt.Sprintf(`holtWintersForecast(%s)`, input.name), 3509 Data: input.output, 3510 } 3511 require.Nil(t, err) 3512 3513 common.CompareOutputsAndExpected(t, input.newStep, input.newStartTime, 3514 []common.TestSeries{expected}, results.Values) 3515 } 3516 } 3517 3518 func TestHoltWintersConfidenceBands(t *testing.T) { 3519 ctx := common.NewTestContext() 3520 ctx.Engine = NewEngine(&mockStorage{}, CompileOptions{}) 3521 defer func() { _ = ctx.Close() }() 3522 3523 now := ctx.StartTime 3524 tests := []struct { 3525 name string 3526 startTime time.Time 3527 stepInMilli int 3528 values []float64 3529 duration time.Duration 3530 lowerStartTime time.Time 3531 lowerStep int 3532 lowerOutput []float64 3533 upperStartTime time.Time 3534 upperStep int 3535 upperOutput []float64 3536 }{ 3537 { 3538 "foo", 3539 now, 3540 1000, 3541 []float64{4.0, 5.0, 6.0}, 3542 3 * time.Second, 3543 now, 3544 1000, 3545 []float64{math.NaN(), 3.7, 3.5305}, 3546 now, 3547 1000, 3548 []float64{math.NaN(), 4.3, 4.6702}, 3549 }, 3550 } 3551 3552 for _, input := range tests { 3553 series := ts.NewSeries( 3554 ctx, 3555 input.name, 3556 input.startTime, 3557 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values), 3558 ) 3559 results, err := holtWintersConfidenceBandsInternal(ctx, singlePathSpec{ 3560 Values: []*ts.Series{series}, 3561 }, 3, input.duration) 3562 lowerExpected := common.TestSeries{ 3563 Name: fmt.Sprintf(`holtWintersConfidenceLower(%s)`, input.name), 3564 Data: input.lowerOutput, 3565 } 3566 upperExpected := common.TestSeries{ 3567 Name: fmt.Sprintf(`holtWintersConfidenceUpper(%s)`, input.name), 3568 Data: input.upperOutput, 3569 } 3570 require.Nil(t, err) 3571 common.CompareOutputsAndExpected(t, input.lowerStep, input.lowerStartTime, 3572 []common.TestSeries{lowerExpected}, []*ts.Series{results.Values[0]}) 3573 common.CompareOutputsAndExpected(t, input.upperStep, input.upperStartTime, 3574 []common.TestSeries{upperExpected}, []*ts.Series{results.Values[1]}) 3575 } 3576 } 3577 3578 func TestHoltWintersAberration(t *testing.T) { 3579 ctx := common.NewTestContext() 3580 ctx.Engine = NewEngine(&mockStorage{}, CompileOptions{}) 3581 defer func() { _ = ctx.Close() }() 3582 3583 now := ctx.StartTime 3584 tests := []struct { 3585 name string 3586 startTime time.Time 3587 stepInMilli int 3588 values []float64 3589 duration time.Duration 3590 aberrationStartTime time.Time 3591 aberrationStep int 3592 aberrationOutput []float64 3593 }{ 3594 { 3595 "foo", 3596 now, 3597 1000, 3598 []float64{4.0, 5.0, 6.0}, 3599 3 * time.Second, 3600 now, 3601 1000, 3602 []float64{0, 0.7, 1.3298}, 3603 }, 3604 } 3605 3606 for _, input := range tests { 3607 series := ts.NewSeries( 3608 ctx, 3609 input.name, 3610 input.startTime, 3611 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values), 3612 ) 3613 results, err := holtWintersAberrationInternal(ctx, singlePathSpec{ 3614 Values: []*ts.Series{series}, 3615 }, 3, input.duration) 3616 expected := common.TestSeries{ 3617 Name: fmt.Sprintf(`holtWintersAberration(%s)`, input.name), 3618 Data: input.aberrationOutput, 3619 } 3620 require.Nil(t, err) 3621 common.CompareOutputsAndExpected(t, input.aberrationStep, input.aberrationStartTime, 3622 []common.TestSeries{expected}, results.Values) 3623 } 3624 } 3625 3626 func TestSquareRoot(t *testing.T) { 3627 ctx := common.NewTestContext() 3628 defer func() { _ = ctx.Close() }() 3629 3630 nan := math.NaN() 3631 startTime := ctx.StartTime 3632 stepSize := 10000 3633 inputs := []struct { 3634 name string 3635 startTime time.Time 3636 stepInMilli int 3637 values []float64 3638 }{ 3639 { 3640 "foo", 3641 startTime, 3642 stepSize, 3643 []float64{1.0, -2.0, 3.0, nan}, 3644 }, 3645 { 3646 "bar", 3647 startTime, 3648 stepSize, 3649 []float64{4.0}, 3650 }, 3651 } 3652 3653 inputSeries := make([]*ts.Series, 0, len(inputs)) 3654 for _, input := range inputs { 3655 series := ts.NewSeries( 3656 ctx, 3657 input.name, 3658 input.startTime, 3659 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values), 3660 ) 3661 inputSeries = append(inputSeries, series) 3662 } 3663 expected := []common.TestSeries{ 3664 {Name: "squareRoot(foo)", Data: []float64{1.0, nan, 1.73205, nan}}, 3665 {Name: "squareRoot(bar)", Data: []float64{2.0}}, 3666 } 3667 results, err := squareRoot(ctx, singlePathSpec{ 3668 Values: inputSeries, 3669 }) 3670 require.Nil(t, err) 3671 common.CompareOutputsAndExpected(t, stepSize, startTime, 3672 expected, results.Values) 3673 } 3674 3675 func TestStdev(t *testing.T) { 3676 ctx := common.NewTestContext() 3677 defer func() { _ = ctx.Close() }() 3678 3679 nan := math.NaN() 3680 startTime := ctx.StartTime 3681 stepSize := 10000 3682 inputs := []struct { 3683 name string 3684 startTime time.Time 3685 stepInMilli int 3686 values []float64 3687 }{ 3688 { 3689 "foo", 3690 startTime, 3691 stepSize, 3692 []float64{1.0, 2.0, 3.0, 4.0, nan, nan, nan, 5.0, 6.0, nan, nan}, 3693 }, 3694 } 3695 3696 inputSeries := make([]*ts.Series, 0, len(inputs)) 3697 for _, input := range inputs { 3698 series := ts.NewSeries( 3699 ctx, 3700 input.name, 3701 input.startTime, 3702 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values), 3703 ) 3704 inputSeries = append(inputSeries, series) 3705 } 3706 expected := []common.TestSeries{ 3707 {Name: "stddev(foo,3)", Data: []float64{0.0, 0.5, 0.8165, 0.8165, 0.5, 0.0, nan, 0.0, 0.5, 0.5, 0.0}}, 3708 } 3709 results, err := stdev(ctx, singlePathSpec{ 3710 Values: inputSeries, 3711 }, 3, 0.1) 3712 require.Nil(t, err) 3713 common.CompareOutputsAndExpected(t, stepSize, startTime, 3714 expected, results.Values) 3715 } 3716 3717 func TestRangeOfSeries(t *testing.T) { 3718 ctx, input := newConsolidationTestSeries() 3719 defer func() { _ = ctx.Close() }() 3720 3721 expectedStart := ctx.StartTime.Add(-30 * time.Second) 3722 expectedStep := 10000 3723 rangeSeries, err := rangeOfSeries(ctx, singlePathSpec{ 3724 Values: input, 3725 }) 3726 require.Nil(t, err) 3727 expected := common.TestSeries{ 3728 Name: "rangeOfSeries(a,b,c,d)", 3729 Data: []float64{0, 0, 0, 12, 12, 12, 14, 14, 14, 0, 0, 0}, 3730 } 3731 common.CompareOutputsAndExpected(t, expectedStep, expectedStart, 3732 []common.TestSeries{expected}, rangeSeries.Values) 3733 } 3734 3735 type percentileFunction func(ctx *common.Context, seriesList singlePathSpec, percentile float64) (ts.SeriesList, error) 3736 3737 func testPercentileFunction(t *testing.T, f percentileFunction, expected []common.TestSeries) { 3738 ctx := common.NewTestContext() 3739 defer func() { _ = ctx.Close() }() 3740 3741 nan := math.NaN() 3742 startTime := ctx.StartTime 3743 stepSize := 10000 3744 inputs := []struct { 3745 name string 3746 startTime time.Time 3747 stepInMilli int 3748 values []float64 3749 }{ 3750 { 3751 "foo", 3752 startTime, 3753 stepSize, 3754 []float64{nan, nan, nan, nan, nan}, 3755 }, 3756 { 3757 "bar", 3758 startTime, 3759 stepSize, 3760 []float64{3.0, 2.0, 4.0, nan, 1.0, 6.0, nan, 5.0}, 3761 }, 3762 { 3763 "baz", 3764 startTime, 3765 stepSize, 3766 []float64{1.0}, 3767 }, 3768 } 3769 3770 inputSeries := make([]*ts.Series, 0, len(inputs)) 3771 for _, input := range inputs { 3772 series := ts.NewSeries( 3773 ctx, 3774 input.name, 3775 input.startTime, 3776 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values), 3777 ) 3778 inputSeries = append(inputSeries, series) 3779 } 3780 percentile := 40.123 3781 results, err := f(ctx, singlePathSpec{ 3782 Values: inputSeries, 3783 }, percentile) 3784 require.Nil(t, err) 3785 common.CompareOutputsAndExpected(t, stepSize, startTime, 3786 expected, results.Values) 3787 } 3788 3789 func TestNPercentile(t *testing.T) { 3790 expected := []common.TestSeries{ 3791 { 3792 Name: "nPercentile(bar, 40.123)", 3793 Data: []float64{3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0}, 3794 }, 3795 { 3796 Name: "nPercentile(baz, 40.123)", 3797 Data: []float64{1.0}, 3798 }, 3799 } 3800 testPercentileFunction(t, nPercentile, expected) 3801 } 3802 3803 func TestRemoveAbovePercentile(t *testing.T) { 3804 nan := math.NaN() 3805 expected := []common.TestSeries{ 3806 { 3807 Name: "removeAbovePercentile(foo, 40.123)", 3808 Data: []float64{nan, nan, nan, nan, nan}, 3809 }, 3810 { 3811 Name: "removeAbovePercentile(bar, 40.123)", 3812 Data: []float64{3.0, 2.0, nan, nan, 1.0, nan, nan, nan}, 3813 }, 3814 { 3815 Name: "removeAbovePercentile(baz, 40.123)", 3816 Data: []float64{1.0}, 3817 }, 3818 } 3819 3820 testPercentileFunction(t, removeAbovePercentile, expected) 3821 } 3822 3823 func TestRemoveBelowPercentile(t *testing.T) { 3824 nan := math.NaN() 3825 3826 expected := []common.TestSeries{ 3827 { 3828 Name: "removeBelowPercentile(foo, 40.123)", 3829 Data: []float64{nan, nan, nan, nan, nan}, 3830 }, 3831 { 3832 Name: "removeBelowPercentile(bar, 40.123)", 3833 Data: []float64{3.0, nan, 4.0, nan, nan, 6.0, nan, 5.0}, 3834 }, 3835 { 3836 Name: "removeBelowPercentile(baz, 40.123)", 3837 Data: []float64{1.0}, 3838 }, 3839 } 3840 3841 testPercentileFunction(t, removeBelowPercentile, expected) 3842 } 3843 3844 func testRandomWalkFunctionInternal(t *testing.T, ctx *common.Context, stepSize, expectedLen int) { 3845 r, err := randomWalkFunction(ctx, "foo", stepSize) 3846 require.Nil(t, err) 3847 3848 results := r.Values 3849 require.Equal(t, 1, len(results)) 3850 require.Equal(t, expectedLen, results[0].Len()) 3851 for i := 0; i < expectedLen; i++ { 3852 v := results[0].ValueAt(i) 3853 require.True(t, v >= -0.5 && v < 0.5) 3854 } 3855 } 3856 3857 func TestRandomWalkFunction(t *testing.T) { 3858 ctx := common.NewTestContext() 3859 defer func() { _ = ctx.Close() }() 3860 3861 ctx.EndTime = ctx.StartTime.Add(1100 * time.Millisecond) 3862 testRandomWalkFunctionInternal(t, ctx, 1, 2) 3863 3864 ctx.EndTime = ctx.StartTime.Add(1600 * time.Millisecond) 3865 testRandomWalkFunctionInternal(t, ctx, 1, 2) 3866 } 3867 3868 func testAggregateLineInternal(t *testing.T, f string, expectedName string, expectedVal float64) { 3869 ctx := common.NewTestContext() 3870 defer func() { _ = ctx.Close() }() 3871 3872 input := struct { 3873 name string 3874 startTime time.Time 3875 stepInMilli int 3876 values []float64 3877 }{ 3878 "foo", 3879 ctx.StartTime, 3880 10000, 3881 []float64{1.0, 2.0, 3.0, 4.0}, 3882 } 3883 3884 series := ts.NewSeries( 3885 ctx, 3886 input.name, 3887 input.startTime, 3888 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values), 3889 ) 3890 3891 r, err := aggregateLine(ctx, singlePathSpec{ 3892 Values: []*ts.Series{series}, 3893 }, f) 3894 require.Nil(t, err) 3895 3896 results := r.Values 3897 require.Equal(t, 1, len(results)) 3898 require.Equal(t, expectedName, results[0].Name()) 3899 require.Equal(t, 3, results[0].Len()) 3900 for i := 0; i < 2; i++ { 3901 require.Equal(t, expectedVal, results[0].ValueAt(i)) 3902 } 3903 } 3904 3905 func TestAggregateLine(t *testing.T) { 3906 testAggregateLineInternal(t, "avg", "aggregateLine(foo,2.500)", 2.5) 3907 testAggregateLineInternal(t, "max", "aggregateLine(foo,4.000)", 4.0) 3908 testAggregateLineInternal(t, "min", "aggregateLine(foo,1.000)", 1.0) 3909 } 3910 3911 func TestChanged(t *testing.T) { 3912 ctx := common.NewTestContext() 3913 defer func() { _ = ctx.Close() }() 3914 3915 nan := math.NaN() 3916 startTime := ctx.StartTime 3917 stepSize := 10000 3918 input := struct { 3919 name string 3920 startTime time.Time 3921 stepInMilli int 3922 values []float64 3923 }{ 3924 "foo", 3925 startTime, 3926 stepSize, 3927 []float64{1.0, 1.0, 2.0, 3.0, nan, 3.0, nan, 4.0, nan}, 3928 } 3929 3930 series := ts.NewSeries( 3931 ctx, 3932 input.name, 3933 input.startTime, 3934 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values), 3935 ) 3936 3937 expected := []common.TestSeries{ 3938 { 3939 Name: "changed(foo)", 3940 Data: []float64{0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0}, 3941 }, 3942 } 3943 results, err := changed(ctx, singlePathSpec{ 3944 Values: []*ts.Series{series}, 3945 }) 3946 require.Nil(t, err) 3947 common.CompareOutputsAndExpected(t, stepSize, startTime, 3948 expected, results.Values) 3949 } 3950 3951 func TestEffectiveXFilesFactor(t *testing.T) { 3952 require.True(t, effectiveXFF(10, 9, 0)) 3953 require.True(t, effectiveXFF(10, 4, 0.5)) 3954 require.True(t, effectiveXFF(10, 0, 1.0)) 3955 3956 require.False(t, effectiveXFF(10, 10, 0.1)) 3957 require.False(t, effectiveXFF(10, 6, 0.5)) 3958 require.False(t, effectiveXFF(10, 1, 1.0)) 3959 } 3960 3961 func TestMovingMedian(t *testing.T) { 3962 ctrl := xgomock.NewController(t) 3963 defer ctrl.Finish() 3964 3965 store := storage.NewMockStorage(ctrl) 3966 now := time.Now().Truncate(time.Hour) 3967 engine := NewEngine(store, CompileOptions{}) 3968 startTime := now.Add(-3 * time.Minute) 3969 // Make sure two full steps considered. 3970 endTime := now.Add(-time.Minute) 3971 ctx := common.NewContext(common.ContextOptions{Start: startTime, End: endTime, Engine: engine}) 3972 defer func() { _ = ctx.Close() }() 3973 3974 stepSize := 60000 3975 target := "movingMedian(foo.bar.q.zed, '1min')" 3976 store.EXPECT().FetchByQuery(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( 3977 buildTestSeriesFn(stepSize, "foo.bar.q.zed")).Times(2) 3978 expr, err := engine.Compile(target) 3979 require.NoError(t, err) 3980 res, err := expr.Execute(ctx) 3981 require.NoError(t, err) 3982 expected := common.TestSeries{ 3983 Name: "movingMedian(foo.bar.q.zed,\"1min\")", 3984 Data: []float64{0.0, 0.0}, 3985 } 3986 common.CompareOutputsAndExpected(t, stepSize, startTime, 3987 []common.TestSeries{expected}, res.Values) 3988 } 3989 3990 func TestMovingAverage(t *testing.T) { 3991 ctrl := xgomock.NewController(t) 3992 defer ctrl.Finish() 3993 3994 store := storage.NewMockStorage(ctrl) 3995 now := time.Now().Truncate(time.Hour) 3996 engine := NewEngine(store, CompileOptions{}) 3997 startTime := now.Add(-3 * time.Minute) 3998 endTime := now.Add(-1 * time.Minute) 3999 ctx := common.NewContext(common.ContextOptions{Start: startTime, End: endTime, Engine: engine}) 4000 defer func() { _ = ctx.Close() }() 4001 4002 stepSize := 60000 4003 target := `movingAverage(timeShift(foo.bar.g.zed,'-1d'), '1min', 0.7)` 4004 store.EXPECT().FetchByQuery(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( 4005 buildTestSeriesFn(stepSize, "foo.bar.g.zed")).AnyTimes() 4006 expr, err := engine.Compile(target) 4007 require.NoError(t, err) 4008 res, err := expr.Execute(ctx) 4009 require.NoError(t, err) 4010 expected := common.TestSeries{ 4011 Name: `movingAverage(timeShift(foo.bar.g.zed,"-1d"),"1min")`, 4012 Data: []float64{1, 1}, 4013 } 4014 common.CompareOutputsAndExpected(t, stepSize, startTime, 4015 []common.TestSeries{expected}, res.Values) 4016 } 4017 4018 // nolint: dupl 4019 func TestMovingWindow(t *testing.T) { 4020 ctrl := xgomock.NewController(t) 4021 defer ctrl.Finish() 4022 4023 store := storage.NewMockStorage(ctrl) 4024 now := time.Now().Truncate(time.Hour) 4025 engine := NewEngine(store, CompileOptions{}) 4026 startTime := now.Add(-3 * time.Minute) 4027 endTime := now.Add(-1 * time.Minute) 4028 ctx := common.NewContext(common.ContextOptions{Start: startTime, End: endTime, Engine: engine}) 4029 defer func() { _ = ctx.Close() }() 4030 4031 stepSize := 60000 4032 target := `movingWindow(timeShift(foo.bar.g.zed,'-1d'), '1min', 'avg', 0.7)` 4033 store.EXPECT().FetchByQuery(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( 4034 buildTestSeriesFn(stepSize, "foo.bar.g.zed")).AnyTimes() 4035 expr, err := engine.Compile(target) 4036 require.NoError(t, err) 4037 res, err := expr.Execute(ctx) 4038 require.NoError(t, err) 4039 expected := common.TestSeries{ 4040 Name: `movingAverage(timeShift(foo.bar.g.zed,"-1d"),"1min")`, 4041 Data: []float64{1, 1}, 4042 } 4043 common.CompareOutputsAndExpected(t, stepSize, startTime, 4044 []common.TestSeries{expected}, res.Values) 4045 } 4046 4047 func TestLegendValue(t *testing.T) { 4048 ctx := common.NewTestContext() 4049 defer func() { _ = ctx.Close() }() 4050 4051 vals := []float64{1.0, 2.0, 3.0, 4.0, math.NaN()} 4052 input := struct { 4053 name string 4054 startTime time.Time 4055 stepInMilli int 4056 values []float64 4057 }{ 4058 "foo", 4059 ctx.StartTime, 4060 10000, 4061 vals, 4062 } 4063 4064 series := ts.NewSeries( 4065 ctx, 4066 input.name, 4067 input.startTime, 4068 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values), 4069 ) 4070 4071 results, err := legendValue(ctx, singlePathSpec{ 4072 Values: []*ts.Series{series}, 4073 }, "avg") 4074 expected := common.TestSeries{Name: "foo (avg: 2.500)", Data: vals} 4075 require.Nil(t, err) 4076 common.CompareOutputsAndExpected(t, input.stepInMilli, input.startTime, 4077 []common.TestSeries{expected}, results.Values) 4078 4079 results, err = legendValue(ctx, singlePathSpec{ 4080 Values: []*ts.Series{series}, 4081 }, "last") 4082 expected = common.TestSeries{Name: "foo (last: 4.000)", Data: vals} 4083 require.Nil(t, err) 4084 common.CompareOutputsAndExpected(t, input.stepInMilli, input.startTime, 4085 []common.TestSeries{expected}, results.Values) 4086 4087 results, err = legendValue(ctx, singlePathSpec{ 4088 Values: []*ts.Series{series}, 4089 }, "unknown") 4090 require.NotNil(t, err) 4091 } 4092 4093 func TestCactiStyle(t *testing.T) { 4094 ctx := common.NewTestContext() 4095 defer func() { _ = ctx.Close() }() 4096 4097 stepSize := 10000 4098 inputs := []struct { 4099 name string 4100 startTime time.Time 4101 stepInMilli int 4102 values []float64 4103 }{ 4104 { 4105 "foo", 4106 ctx.StartTime, 4107 stepSize, 4108 []float64{1.0, 2.0, 3.0, 4.0, math.NaN()}, 4109 }, 4110 { 4111 "barbaz", 4112 ctx.StartTime, 4113 stepSize, 4114 []float64{10.0, -5.0, 80.0, 100.0, math.NaN()}, 4115 }, 4116 { 4117 "test", 4118 ctx.StartTime, 4119 stepSize, 4120 []float64{math.NaN()}, 4121 }, 4122 } 4123 4124 inputSeries := make([]*ts.Series, 0, len(inputs)) 4125 for _, input := range inputs { 4126 series := ts.NewSeries( 4127 ctx, 4128 input.name, 4129 input.startTime, 4130 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values), 4131 ) 4132 inputSeries = append(inputSeries, series) 4133 } 4134 4135 results, err := cactiStyle(ctx, singlePathSpec{ 4136 Values: inputSeries, 4137 }) 4138 expected := []common.TestSeries{ 4139 {Name: "foo Current:4.00 Max:4.00 Min:1.00 ", Data: inputs[0].values}, 4140 {Name: "barbaz Current:100.00 Max:100.00 Min:-5.00 ", Data: inputs[1].values}, 4141 {Name: "test Current:nan Max:nan Min:nan ", Data: inputs[2].values}, 4142 } 4143 require.Nil(t, err) 4144 common.CompareOutputsAndExpected(t, stepSize, ctx.StartTime, 4145 expected, results.Values) 4146 } 4147 4148 func TestConsolidateBy(t *testing.T) { 4149 start := common.NewTestContext().StartTime 4150 stepSize := 10000 4151 for i, test := range []struct { 4152 name string 4153 fn string 4154 startTime time.Time 4155 stepMillis int 4156 maxDataPoints int64 4157 values []float64 4158 expectedValues []float64 4159 expectedStepMillis int 4160 expectedErr bool 4161 }{ 4162 { 4163 name: "foo", 4164 fn: "min", 4165 startTime: start, 4166 stepMillis: stepSize, 4167 values: []float64{1.0, 2.0, 3.0, 4.0, 5.0, math.NaN()}, 4168 expectedStepMillis: stepSize, 4169 expectedValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0, math.NaN()}, 4170 }, 4171 { 4172 name: "foo", 4173 fn: "min", 4174 startTime: start, 4175 stepMillis: stepSize, 4176 maxDataPoints: 2, 4177 values: []float64{1.0, 2.0, 3.0, 4.0, 5.0, math.NaN()}, 4178 expectedStepMillis: 3 * stepSize, 4179 expectedValues: []float64{1.0, 4.0}, 4180 }, 4181 { 4182 name: "foo", 4183 fn: "last", 4184 startTime: start, 4185 stepMillis: stepSize, 4186 maxDataPoints: 2, 4187 values: []float64{1.0, 2.0, 3.0, 4.0, 5.0, math.NaN()}, 4188 expectedStepMillis: 3 * stepSize, 4189 expectedValues: []float64{3.0, 5.0}, 4190 }, 4191 { 4192 name: "foo", 4193 fn: "nonexistent", 4194 startTime: start, 4195 stepMillis: stepSize, 4196 values: []float64{1.0, 2.0, 3.0, 4.0, math.NaN(), 5.0}, 4197 expectedErr: true, 4198 }, 4199 } { 4200 input := test 4201 t.Run(fmt.Sprintf("%d-%s", i, input.name), func(t *testing.T) { 4202 ctx := common.NewTestContext() 4203 ctx.MaxDataPoints = input.maxDataPoints 4204 defer func() { _ = ctx.Close() }() 4205 4206 series := ts.NewSeries( 4207 ctx, 4208 input.name, 4209 input.startTime, 4210 common.NewTestSeriesValues(ctx, input.stepMillis, input.values), 4211 ) 4212 4213 results, err := consolidateBy(ctx, singlePathSpec{ 4214 Values: []*ts.Series{series}, 4215 }, input.fn) 4216 if input.expectedErr { 4217 require.Error(t, err) 4218 return 4219 } 4220 4221 expected := common.TestSeries{ 4222 Name: fmt.Sprintf(`consolidateBy(%s,"%s")`, input.name, input.fn), 4223 Data: input.expectedValues, 4224 } 4225 require.NoError(t, err) 4226 common.CompareOutputsAndExpected(t, input.expectedStepMillis, input.startTime, 4227 []common.TestSeries{expected}, results.Values) 4228 }) 4229 } 4230 } 4231 4232 func TestPow(t *testing.T) { 4233 var ( 4234 ctx = common.NewTestContext() 4235 millisPerStep = 10000 4236 ) 4237 4238 defer func() { _ = ctx.Close() }() 4239 4240 inputs := []struct { 4241 name string 4242 values []float64 4243 pow float64 4244 expected []float64 4245 }{ 4246 { 4247 "foo", 4248 []float64{1.0, 2.0, 3.0, 4.0, 5.0}, 4249 2, 4250 []float64{1.0, 4.0, 9.0, 16.0, 25.0}, 4251 }, 4252 { 4253 "bar", 4254 []float64{0.0, 2.0, 4.0, 6.0, 8.0}, 4255 2, 4256 []float64{0.0, 4.0, 16.0, 36.0, 64.0}, 4257 }, 4258 } 4259 4260 for _, input := range inputs { 4261 series := ts.NewSeries( 4262 ctx, 4263 input.name, 4264 ctx.StartTime, 4265 common.NewTestSeriesValues(ctx, millisPerStep, input.values), 4266 ) 4267 results, err := pow(ctx, singlePathSpec{ 4268 Values: []*ts.Series{series}, 4269 }, input.pow) 4270 require.NoError(t, err) 4271 expected := common.TestSeries{ 4272 Name: fmt.Sprintf("pow(%s, %f)", input.name, input.pow), 4273 Data: input.expected, 4274 } 4275 common.CompareOutputsAndExpected(t, millisPerStep, ctx.StartTime, 4276 []common.TestSeries{expected}, results.Values) 4277 } 4278 } 4279 4280 func TestInvert(t *testing.T) { 4281 var ( 4282 ctx = common.NewTestContext() 4283 millisPerStep = 10000 4284 ) 4285 4286 defer func() { _ = ctx.Close() }() 4287 4288 inputs := []struct { 4289 name string 4290 values []float64 4291 expected []float64 4292 }{ 4293 { 4294 "foo", 4295 []float64{1.0, 2.0, 4.0}, 4296 []float64{1.0, 1 / 2.0, 1 / 4.0}, 4297 }, 4298 } 4299 4300 for _, input := range inputs { 4301 series := ts.NewSeries( 4302 ctx, 4303 input.name, 4304 ctx.StartTime, 4305 common.NewTestSeriesValues(ctx, millisPerStep, input.values), 4306 ) 4307 results, err := invert(ctx, singlePathSpec{ 4308 Values: []*ts.Series{series}, 4309 }) 4310 require.NoError(t, err) 4311 expected := common.TestSeries{ 4312 Name: fmt.Sprintf("invert(%s)", input.name), 4313 Data: input.expected, 4314 } 4315 common.CompareOutputsAndExpected(t, millisPerStep, ctx.StartTime, 4316 []common.TestSeries{expected}, results.Values) 4317 } 4318 } 4319 4320 func TestCumulative(t *testing.T) { 4321 ctx := common.NewTestContext() 4322 defer func() { _ = ctx.Close() }() 4323 4324 stepSize := 10000 4325 input := struct { 4326 name string 4327 startTime time.Time 4328 stepInMilli int 4329 values []float64 4330 }{ 4331 "foo", 4332 ctx.StartTime, 4333 stepSize, 4334 []float64{1.0, 2.0, 3.0, 4.0, math.NaN()}, 4335 } 4336 4337 series := ts.NewSeries( 4338 ctx, 4339 input.name, 4340 input.startTime, 4341 common.NewTestSeriesValues(ctx, input.stepInMilli, input.values), 4342 ) 4343 4344 results, err := cumulative(ctx, singlePathSpec{ 4345 Values: []*ts.Series{series}, 4346 }) 4347 expected := common.TestSeries{Name: `consolidateBy(foo,"sum")`, Data: input.values} 4348 require.Nil(t, err) 4349 common.CompareOutputsAndExpected(t, input.stepInMilli, input.startTime, 4350 []common.TestSeries{expected}, results.Values) 4351 } 4352 4353 func TestOffsetToZero(t *testing.T) { 4354 ctx := common.NewTestContext() 4355 defer func() { _ = ctx.Close() }() 4356 4357 nan := math.NaN() 4358 startTime := ctx.StartTime 4359 stepSize := 10000 4360 inputs := []struct { 4361 name string 4362 values []float64 4363 expected []float64 4364 }{ 4365 { 4366 "foo", 4367 []float64{nan, nan, nan, nan, nan}, 4368 []float64{nan, nan, nan, nan, nan}, 4369 }, 4370 { 4371 "bar", 4372 []float64{3.0, 2.0, 4.0, nan, 1.0, 6.0, nan, 5.0}, 4373 []float64{2.0, 1.0, 3.0, nan, 0.0, 5.0, nan, 4.0}, 4374 }, 4375 { 4376 "baz", 4377 []float64{1.0}, 4378 []float64{0.0}, 4379 }, 4380 } 4381 4382 for _, input := range inputs { 4383 series := ts.NewSeries( 4384 ctx, 4385 input.name, 4386 startTime, 4387 common.NewTestSeriesValues(ctx, stepSize, input.values), 4388 ) 4389 results, err := offsetToZero(ctx, singlePathSpec{ 4390 Values: []*ts.Series{series}, 4391 }) 4392 require.NoError(t, err) 4393 expected := common.TestSeries{ 4394 Name: fmt.Sprintf("offsetToZero(%s)", input.name), 4395 Data: input.expected, 4396 } 4397 common.CompareOutputsAndExpected(t, stepSize, startTime, 4398 []common.TestSeries{expected}, results.Values) 4399 } 4400 } 4401 4402 func TestTimeFunction(t *testing.T) { 4403 ctx := common.NewTestContext() 4404 now := time.Now() 4405 truncatedNow := float64(now.Truncate(time.Second).Unix()) 4406 ctx.StartTime = now 4407 ctx.EndTime = now.Add(2 * time.Minute) 4408 defer func() { _ = ctx.Close() }() 4409 4410 results, err := timeFunction(ctx, "foo", 30) 4411 require.NoError(t, err) 4412 expected := common.TestSeries{ 4413 Name: "foo", 4414 Data: []float64{truncatedNow, truncatedNow + 30, truncatedNow + 60, truncatedNow + 90}, 4415 } 4416 common.CompareOutputsAndExpected(t, 30000, now.Truncate(time.Second), 4417 []common.TestSeries{expected}, results.Values) 4418 } 4419 4420 func TestTimeShift(t *testing.T) { 4421 ctrl := xgomock.NewController(t) 4422 defer ctrl.Finish() 4423 4424 store := storage.NewMockStorage(ctrl) 4425 now := time.Now().Truncate(time.Hour) 4426 engine := NewEngine(store, CompileOptions{}) 4427 startTime := now.Add(-3 * time.Minute) 4428 endTime := now.Add(-time.Minute) 4429 ctx := common.NewContext(common.ContextOptions{ 4430 Start: startTime, 4431 End: endTime, 4432 Engine: engine, 4433 }) 4434 defer func() { _ = ctx.Close() }() 4435 4436 stepSize := 60000 4437 target := "timeShift(foo.bar.q.zed, '1min', false)" 4438 4439 store.EXPECT().FetchByQuery(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( 4440 buildTestSeriesFn(stepSize, "foo.bar.q.zed")).AnyTimes() 4441 4442 expr, err := engine.Compile(target) 4443 require.NoError(t, err) 4444 res, err := expr.Execute(ctx) 4445 require.NoError(t, err) 4446 expected := common.TestSeries{ 4447 Name: `timeShift(foo.bar.q.zed,"-1min")`, 4448 Data: []float64{0.0, 0.0}, 4449 } 4450 common.CompareOutputsAndExpected(t, stepSize, startTime, 4451 []common.TestSeries{expected}, res.Values) 4452 } 4453 4454 func TestDelay(t *testing.T) { 4455 values := [3][]float64{ 4456 {54.0, 48.0, 92.0, 54.0, 14.0, 1.2}, 4457 {4.0, 5.0, math.NaN(), 6.4, 7.2, math.NaN()}, 4458 {math.NaN(), 8.0, 9.0, 10.6, 11.2, 12.2}, 4459 } 4460 expected := [3][]float64{ 4461 {math.NaN(), math.NaN(), math.NaN(), 54.0, 48.0, 92.0}, 4462 {math.NaN(), math.NaN(), math.NaN(), 4.0, 5.0, math.NaN()}, 4463 {math.NaN(), math.NaN(), math.NaN(), math.NaN(), 8.0, 9.0}, 4464 } 4465 4466 for index, value := range values { 4467 e := expected[index] 4468 testDelay(t, "delay(foo.bar.baz, 3)", "delay(foo.bar.baz,3)", value, e) 4469 } 4470 } 4471 4472 var ( 4473 testDelayStart = time.Now().Truncate(time.Minute) 4474 testDelayEnd = testMovingFunctionEnd.Add(time.Minute) 4475 ) 4476 4477 func testDelay(t *testing.T, target, expectedName string, values, output []float64) { 4478 ctx := common.NewTestContext() 4479 defer func() { _ = ctx.Close() }() 4480 4481 engine := NewEngine(&common.MovingFunctionStorage{ 4482 StepMillis: 10000, 4483 Values: values, 4484 }, CompileOptions{}) 4485 phonyContext := common.NewContext(common.ContextOptions{ 4486 Start: testDelayStart, 4487 End: testDelayEnd, 4488 Engine: engine, 4489 }) 4490 4491 expr, err := phonyContext.Engine.(*Engine).Compile(target) 4492 require.NoError(t, err) 4493 res, err := expr.Execute(phonyContext) 4494 require.NoError(t, err) 4495 var expected []common.TestSeries 4496 4497 if output != nil { 4498 expectedSeries := common.TestSeries{ 4499 Name: expectedName, 4500 Data: output, 4501 } 4502 expected = append(expected, expectedSeries) 4503 } 4504 common.CompareOutputsAndExpected(t, 10000, testDelayStart, expected, res.Values) 4505 } 4506 4507 func TestTimeSlice(t *testing.T) { 4508 values := []float64{math.NaN(), 1.0, 2.0, 3.0, math.NaN(), 5.0, 6.0, math.NaN(), 7.0, 8.0, 9.0} 4509 expected := []float64{math.NaN(), math.NaN(), math.NaN(), 3.0, math.NaN(), 5.0, 6.0, math.NaN(), 7.0, math.NaN(), math.NaN()} 4510 4511 testGeneralFunction(t, 4512 "timeSlice(foo.bar.baz, '-9min','-3min')", 4513 `timeSlice(foo.bar.baz, "-9min", "-3min")`, 4514 values, expected) 4515 } 4516 4517 func TestDashed(t *testing.T) { 4518 ctx := common.NewTestContext() 4519 defer func() { _ = ctx.Close() }() 4520 4521 nan := math.NaN() 4522 startTime := ctx.StartTime 4523 stepSize := 10000 4524 inputs := []struct { 4525 name string 4526 values []float64 4527 expected []float64 4528 }{ 4529 { 4530 "foo", 4531 []float64{nan, nan, nan, nan, nan}, 4532 []float64{nan, nan, nan, nan, nan}, 4533 }, 4534 } 4535 4536 for _, input := range inputs { 4537 series := ts.NewSeries( 4538 ctx, 4539 input.name, 4540 startTime, 4541 common.NewTestSeriesValues(ctx, stepSize, input.values), 4542 ) 4543 results, err := dashed(ctx, singlePathSpec{ 4544 Values: []*ts.Series{series}, 4545 }, 3.0) 4546 require.NoError(t, err) 4547 expected := common.TestSeries{ 4548 Name: fmt.Sprintf("dashed(%s, 3.000)", input.name), 4549 Data: input.expected, 4550 } 4551 common.CompareOutputsAndExpected(t, stepSize, startTime, 4552 []common.TestSeries{expected}, results.Values) 4553 } 4554 } 4555 4556 func TestThreshold(t *testing.T) { 4557 ctx := common.NewTestContext() 4558 defer func() { _ = ctx.Close() }() 4559 4560 r, err := threshold(ctx, 1.0, "bar", "yellow") 4561 require.NoError(t, err) 4562 4563 results := r.Values 4564 require.Equal(t, 1, len(results)) 4565 require.Equal(t, "bar", results[0].Name()) 4566 4567 r, err = threshold(ctx, 1.0, "", "red") 4568 require.NoError(t, err) 4569 4570 results = r.Values 4571 require.Equal(t, 1, len(results)) 4572 require.Equal(t, "1.000", results[0].Name()) 4573 4574 r, err = threshold(ctx, 1.0, "", "") 4575 require.NoError(t, err) 4576 4577 results = r.Values 4578 require.Equal(t, 1, len(results)) 4579 require.Equal(t, "1.000", results[0].Name()) 4580 } 4581 4582 func TestFunctionsRegistered(t *testing.T) { 4583 fnames := []string{ 4584 "abs", 4585 "absolute", 4586 "aggregate", 4587 "aggregateLine", 4588 "alias", 4589 "aliasByMetric", 4590 "aliasByNode", 4591 "aliasByTags", 4592 "aliasSub", 4593 "asPercent", 4594 "averageAbove", 4595 "averageSeries", 4596 "averageSeriesWithWildcards", 4597 "avg", 4598 "cactiStyle", 4599 "changed", 4600 "consolidateBy", 4601 "constantLine", 4602 "countSeries", 4603 "cumulative", 4604 "currentAbove", 4605 "currentBelow", 4606 "dashed", 4607 "delay", 4608 "derivative", 4609 "diffSeries", 4610 "divideSeries", 4611 "divideSeriesLists", 4612 "exclude", 4613 "exponentialMovingAverage", 4614 "fallbackSeries", 4615 "grep", 4616 "group", 4617 "groupByNode", 4618 "groupByNodes", 4619 "highest", 4620 "highestAverage", 4621 "highestCurrent", 4622 "highestMax", 4623 "hitcount", 4624 "holtWintersAberration", 4625 "holtWintersConfidenceBands", 4626 "holtWintersForecast", 4627 "identity", 4628 "integral", 4629 "integralByInterval", 4630 "interpolate", 4631 "isNonNull", 4632 "keepLastValue", 4633 "legendValue", 4634 "limit", 4635 "log", 4636 "logarithm", 4637 "lowest", 4638 "lowestAverage", 4639 "lowestCurrent", 4640 "max", 4641 "maxSeries", 4642 "maximumAbove", 4643 "min", 4644 "minSeries", 4645 "minimumAbove", 4646 "mostDeviant", 4647 "movingAverage", 4648 "movingMedian", 4649 "movingSum", 4650 "movingMax", 4651 "movingMin", 4652 "multiplySeries", 4653 "nonNegativeDerivative", 4654 "nPercentile", 4655 "offset", 4656 "offsetToZero", 4657 "perSecond", 4658 "pow", 4659 "powSeries", 4660 "randomWalk", 4661 "randomWalkFunction", 4662 "rangeOfSeries", 4663 "removeAbovePercentile", 4664 "removeAboveValue", 4665 "removeBelowPercentile", 4666 "removeBelowValue", 4667 "removeEmptySeries", 4668 "scale", 4669 "scaleToSeconds", 4670 "smartSummarize", 4671 "sortByMaxima", 4672 "sortByMinima", 4673 "sortByName", 4674 "sortByTotal", 4675 "squareRoot", 4676 "stdev", 4677 "stddevSeries", 4678 "substr", 4679 "sum", 4680 "sumSeries", 4681 "summarize", 4682 "threshold", 4683 "time", 4684 "timeFunction", 4685 "timeShift", 4686 "timeSlice", 4687 "transformNull", 4688 "useSeriesAbove", 4689 "weightedAverage", 4690 } 4691 4692 for _, fname := range fnames { 4693 assert.NotNil(t, findFunction(fname), "could not find function: %s", fname) 4694 } 4695 }