github.com/m3db/m3@v1.5.0/src/query/functions/binary/binary_test.go (about) 1 // Copyright (c) 2018 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 binary 22 23 import ( 24 "math" 25 "testing" 26 "time" 27 28 "github.com/m3db/m3/src/query/block" 29 "github.com/m3db/m3/src/query/executor/transform" 30 "github.com/m3db/m3/src/query/functions/utils" 31 "github.com/m3db/m3/src/query/models" 32 "github.com/m3db/m3/src/query/parser" 33 "github.com/m3db/m3/src/query/test" 34 "github.com/m3db/m3/src/query/test/compare" 35 "github.com/m3db/m3/src/query/test/executor" 36 xtime "github.com/m3db/m3/src/x/time" 37 38 "github.com/stretchr/testify/assert" 39 "github.com/stretchr/testify/require" 40 ) 41 42 var scalarTests = []struct { 43 name string 44 lVal float64 45 opType string 46 rVal float64 47 expected float64 48 }{ 49 /* Arithmetic */ 50 // + 51 {"1 + 3 = 4", 1, PlusType, 3, 4}, 52 // - 53 {"1 - 3 = -2", 1, MinusType, 3, -2}, 54 {"3 - 1 = 2", 3, MinusType, 1, 2}, 55 // * 56 {"7 * 3 = 21", 7, MultiplyType, 3, 21}, 57 // / 58 {"4 / 8 = 0.5", 4, DivType, 8, 0.5}, 59 {"4 / 8 = 2", 8, DivType, 4, 2}, 60 {"8 / 8 = 1", 8, DivType, 8, 1}, 61 {"8 / 0 = +infinity", 8, DivType, 0, math.Inf(1)}, 62 {"-8/ 0 = -infinity", -8, DivType, 0, math.Inf(-1)}, 63 {"0 / 0 = NaN", 0, DivType, 0, math.NaN()}, 64 // ^ 65 {"0 ^ 0 = 1", 0, ExpType, 0, 1}, 66 {"x ^ 0 = 1", 2, ExpType, 0, 1}, 67 {"0 ^ x = 0", 0, ExpType, 2, 0}, 68 // % 69 {"8 % 0 = NaN", 8, ModType, 0, math.NaN()}, 70 {"8 % 3 = 2", 8, ModType, 3, 2}, 71 {"8 % 2 = 0", 8, ModType, 2, 0}, 72 {"8 % 1.5 = 1", 8, ModType, 1.5, 0.5}, 73 /* Comparison */ 74 // == 75 {"2 == 1 = 0", 2, EqType, 1, 0}, 76 {"2 == 2 = 1", 2, EqType, 2, 1}, 77 {"2 == 3 = 0", 2, EqType, 3, 0}, 78 // != 79 {"2 != 1 = 1", 2, NotEqType, 1, 1}, 80 {"2 != 2 = 0", 2, NotEqType, 2, 0}, 81 {"2 != 3 = 1", 2, NotEqType, 3, 1}, 82 // > 83 {"2 > 1 = 1", 2, GreaterType, 1, 1}, 84 {"2 > 2 = 0", 2, GreaterType, 2, 0}, 85 {"2 > 3 = 0", 2, GreaterType, 3, 0}, 86 // < 87 {"2 < 1 = 0", 2, LesserType, 1, 0}, 88 {"2 < 2 = 0", 2, LesserType, 2, 0}, 89 {"2 < 3 = 1", 2, LesserType, 3, 1}, 90 // >= 91 {"2 >= 1 = 1", 2, GreaterEqType, 1, 1}, 92 {"2 >= 2 = 1", 2, GreaterEqType, 2, 1}, 93 {"2 >= 3 = 0", 2, GreaterEqType, 3, 0}, 94 // <= 95 {"2 <= 1 = 0", 2, LesserEqType, 1, 0}, 96 {"2 <= 2 = 1", 2, LesserEqType, 2, 1}, 97 {"2 <= 3 = 1", 2, LesserEqType, 3, 1}, 98 } 99 100 func TestScalars(t *testing.T) { 101 _, bounds := test.GenerateValuesAndBounds(nil, nil) 102 103 for _, tt := range scalarTests { 104 t.Run(tt.name, func(t *testing.T) { 105 op, err := NewOp( 106 tt.opType, 107 NodeParams{ 108 LNode: parser.NodeID(rune(0)), 109 RNode: parser.NodeID(rune(1)), 110 ReturnBool: true, 111 VectorMatcherBuilder: emptyVectorMatcherBuilder, 112 }, 113 ) 114 require.NoError(t, err) 115 116 c, sink := executor.NewControllerWithSink(parser.NodeID(rune(2))) 117 node := op.(baseOp).Node(c, transform.Options{}) 118 119 err = node.Process( 120 models.NoopQueryContext(), 121 parser.NodeID(rune(0)), 122 block.NewScalar(tt.lVal, block.Metadata{ 123 Bounds: bounds, 124 Tags: models.EmptyTags(), 125 }), 126 ) 127 128 require.NoError(t, err) 129 err = node.Process( 130 models.NoopQueryContext(), 131 parser.NodeID(rune(1)), 132 block.NewScalar(tt.rVal, block.Metadata{ 133 Bounds: bounds, 134 Tags: models.EmptyTags(), 135 }), 136 ) 137 138 expected := [][]float64{{ 139 tt.expected, tt.expected, tt.expected, 140 tt.expected, tt.expected, 141 }} 142 143 compare.EqualsWithNans(t, expected, sink.Values) 144 145 assert.Equal(t, bounds, sink.Meta.Bounds) 146 assert.Equal(t, 0, sink.Meta.Tags.Len()) 147 148 assert.Len(t, sink.Metas, 1) 149 assert.Equal(t, []byte(nil), sink.Metas[0].Name) 150 assert.Equal(t, 0, sink.Metas[0].Tags.Len()) 151 }) 152 } 153 } 154 155 func TestScalarsReturnBoolFalse(t *testing.T) { 156 _, bounds := test.GenerateValuesAndBounds(nil, nil) 157 158 for _, tt := range scalarTests { 159 t.Run(tt.name, func(t *testing.T) { 160 op, err := NewOp( 161 tt.opType, 162 NodeParams{ 163 LNode: parser.NodeID(rune(0)), 164 RNode: parser.NodeID(rune(1)), 165 ReturnBool: false, 166 VectorMatcherBuilder: emptyVectorMatcherBuilder, 167 }, 168 ) 169 require.NoError(t, err) 170 171 c, sink := executor.NewControllerWithSink(parser.NodeID(rune(2))) 172 node := op.(baseOp).Node(c, transform.Options{}) 173 174 err = node.Process( 175 models.NoopQueryContext(), 176 parser.NodeID(rune(0)), 177 block.NewScalar(tt.lVal, block.Metadata{ 178 Bounds: bounds, 179 Tags: models.EmptyTags(), 180 }), 181 ) 182 183 require.NoError(t, err) 184 err = node.Process( 185 models.NoopQueryContext(), 186 parser.NodeID(rune(1)), 187 block.NewScalar(tt.rVal, block.Metadata{ 188 Bounds: bounds, 189 Tags: models.EmptyTags(), 190 }), 191 ) 192 193 if tt.opType == EqType || tt.opType == NotEqType || 194 tt.opType == GreaterType || tt.opType == LesserType || 195 tt.opType == GreaterEqType || tt.opType == LesserEqType { 196 require.Error(t, err, "scalar comparisons must fail without returnBool") 197 return 198 } 199 200 require.NoError(t, err, "scalar maths must succeed without returnBool") 201 202 expected := [][]float64{{ 203 tt.expected, tt.expected, tt.expected, 204 tt.expected, tt.expected, 205 }} 206 207 compare.EqualsWithNans(t, expected, sink.Values) 208 209 assert.Equal(t, bounds, sink.Meta.Bounds) 210 assert.Equal(t, 0, sink.Meta.Tags.Len()) 211 212 assert.Len(t, sink.Metas, 1) 213 assert.Equal(t, []byte(nil), sink.Metas[0].Name) 214 assert.Equal(t, 0, sink.Metas[0].Tags.Len()) 215 }) 216 } 217 } 218 219 var singleSeriesTests = []struct { 220 name string 221 seriesValues [][]float64 222 scalarVal float64 223 opType string 224 seriesLeft bool 225 expected [][]float64 226 expectedBool [][]float64 227 }{ 228 /* Arithmetic */ 229 // + 230 { 231 name: "series + scalar", 232 seriesValues: [][]float64{{1, math.NaN(), 3}, {4, 5, 6}}, 233 opType: PlusType, 234 scalarVal: 10, 235 seriesLeft: true, 236 expected: [][]float64{{11, math.NaN(), 13}, {14, 15, 16}}, 237 expectedBool: [][]float64{{11, math.NaN(), 13}, {14, 15, 16}}, 238 }, 239 { 240 name: "scalar + series", 241 scalarVal: 10, 242 opType: PlusType, 243 seriesValues: [][]float64{{1, 2, 3}, {4, 5, math.NaN()}}, 244 seriesLeft: false, 245 expected: [][]float64{{11, 12, 13}, {14, 15, math.NaN()}}, 246 expectedBool: [][]float64{{11, 12, 13}, {14, 15, math.NaN()}}, 247 }, 248 // - 249 { 250 name: "series - scalar", 251 seriesValues: [][]float64{{1, 2, 3}, {4, 5, 6}}, 252 opType: MinusType, 253 scalarVal: 10, 254 seriesLeft: true, 255 expected: [][]float64{{-9, -8, -7}, {-6, -5, -4}}, 256 expectedBool: [][]float64{{-9, -8, -7}, {-6, -5, -4}}, 257 }, 258 { 259 name: "scalar - series", 260 scalarVal: 10, 261 opType: MinusType, 262 seriesValues: [][]float64{{1, 2, 3}, {4, 5, 6}}, 263 seriesLeft: false, 264 expected: [][]float64{{9, 8, 7}, {6, 5, 4}}, 265 expectedBool: [][]float64{{9, 8, 7}, {6, 5, 4}}, 266 }, 267 // * 268 { 269 name: "series * scalar", 270 seriesValues: [][]float64{ 271 {-1, 0, math.NaN()}, 272 {math.MaxFloat64 - 1, -1 * (math.MaxFloat64 - 1), 1}, 273 }, 274 opType: MultiplyType, 275 scalarVal: 10, 276 seriesLeft: true, 277 expected: [][]float64{{-10, 0, math.NaN()}, {math.Inf(1), math.Inf(-1), 10}}, 278 expectedBool: [][]float64{{-10, 0, math.NaN()}, {math.Inf(1), math.Inf(-1), 10}}, 279 }, 280 { 281 name: "scalar * series", 282 scalarVal: 10, 283 opType: MultiplyType, 284 seriesValues: [][]float64{{-1, 0, math.NaN(), math.MaxFloat64 - 1, -1 * (math.MaxFloat64 - 1), 1}}, 285 seriesLeft: false, 286 expected: [][]float64{{-10, 0, math.NaN(), math.Inf(1), math.Inf(-1), 10}}, 287 expectedBool: [][]float64{{-10, 0, math.NaN(), math.Inf(1), math.Inf(-1), 10}}, 288 }, 289 // / 290 { 291 name: "series / scalar", 292 seriesValues: [][]float64{{10, 0, 5, math.Inf(1), math.Inf(-1), math.NaN()}}, 293 opType: DivType, 294 scalarVal: 10, 295 seriesLeft: true, 296 expected: [][]float64{{1, 0, 0.5, math.Inf(1), math.Inf(-1), math.NaN()}}, 297 expectedBool: [][]float64{{1, 0, 0.5, math.Inf(1), math.Inf(-1), math.NaN()}}, 298 }, 299 { 300 name: "scalar / series", 301 scalarVal: 10, 302 opType: DivType, 303 seriesValues: [][]float64{{10, 0, 5, math.Inf(1), math.Inf(-1), math.NaN()}}, 304 seriesLeft: false, 305 expected: [][]float64{{1, math.Inf(1), 2, 0, 0, math.NaN()}}, 306 expectedBool: [][]float64{{1, math.Inf(1), 2, 0, 0, math.NaN()}}, 307 }, 308 { 309 name: "series / 0", 310 seriesValues: [][]float64{{1, -2}, {3, -4}, {0, math.NaN()}}, 311 opType: DivType, 312 scalarVal: 0, 313 seriesLeft: true, 314 expected: [][]float64{{math.Inf(1), math.Inf(-1)}, {math.Inf(1), math.Inf(-1)}, {math.NaN(), math.NaN()}}, 315 expectedBool: [][]float64{{math.Inf(1), math.Inf(-1)}, {math.Inf(1), math.Inf(-1)}, {math.NaN(), math.NaN()}}, 316 }, 317 // ^ 318 { 319 name: "series ^ scalar", 320 seriesValues: [][]float64{{1, 2, 3}, {4, math.NaN(), math.MaxFloat64}}, 321 opType: ExpType, 322 scalarVal: 2, 323 seriesLeft: true, 324 expected: [][]float64{{1, 4, 9}, {16, math.NaN(), math.Inf(1)}}, 325 expectedBool: [][]float64{{1, 4, 9}, {16, math.NaN(), math.Inf(1)}}, 326 }, 327 { 328 name: "scalar ^ series", 329 scalarVal: 10, 330 opType: ExpType, 331 seriesValues: [][]float64{{1, 2, 3}, {4, 5, 6}}, 332 seriesLeft: false, 333 expected: [][]float64{{10, 100, 1000}, {10000, 100000, 1000000}}, 334 expectedBool: [][]float64{{10, 100, 1000}, {10000, 100000, 1000000}}, 335 }, 336 { 337 name: "series ^ 0", 338 seriesValues: [][]float64{{1, 2, 3}, {1, math.NaN(), math.MaxFloat64}}, 339 opType: ExpType, 340 scalarVal: 0, 341 seriesLeft: true, 342 expected: [][]float64{{1, 1, 1}, {1, 1, 1}}, 343 expectedBool: [][]float64{{1, 1, 1}, {1, 1, 1}}, 344 }, 345 { 346 name: "series ^ 0.5", 347 seriesValues: [][]float64{{1, 4, 9}}, 348 opType: ExpType, 349 scalarVal: 0.5, 350 seriesLeft: true, 351 expected: [][]float64{{1, 2, 3}}, 352 expectedBool: [][]float64{{1, 2, 3}}, 353 }, 354 { 355 name: "series ^ -1", 356 seriesValues: [][]float64{{1, 2, 4}}, 357 opType: ExpType, 358 scalarVal: -1, 359 seriesLeft: true, 360 expected: [][]float64{{1, .5, .25}}, 361 expectedBool: [][]float64{{1, .5, .25}}, 362 }, 363 // % 364 { 365 name: "series % scalar", 366 seriesValues: [][]float64{{1, 2, 3}, {14, -105, 60}}, 367 opType: ModType, 368 scalarVal: 10, 369 seriesLeft: true, 370 expected: [][]float64{{1, 2, 3}, {4, -5, 0}}, 371 expectedBool: [][]float64{{1, 2, 3}, {4, -5, 0}}, 372 }, 373 { 374 name: "scalar % series", 375 scalarVal: 10, 376 opType: ModType, 377 seriesValues: [][]float64{{1, 2, 3}, {4, 5, 6}}, 378 seriesLeft: false, 379 expected: [][]float64{{0, 0, 1}, {2, 0, 4}}, 380 expectedBool: [][]float64{{0, 0, 1}, {2, 0, 4}}, 381 }, 382 /* Comparison */ 383 // == 384 { 385 name: "series == scalar", 386 seriesValues: [][]float64{{-10, 0, 1, 9, 10}, 387 {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}}, 388 opType: EqType, 389 scalarVal: 10, 390 seriesLeft: true, 391 expected: [][]float64{{math.NaN(), math.NaN(), math.NaN(), math.NaN(), 10}, 392 {math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()}}, 393 expectedBool: [][]float64{{0, 0, 0, 0, 1}, {0, 0, 0, 0, 0}}, 394 }, 395 { 396 name: "scalar == series", 397 scalarVal: 10, 398 opType: EqType, 399 seriesValues: [][]float64{{-10, 0, 1, 9, 10}, 400 {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}}, 401 seriesLeft: false, 402 expected: [][]float64{{math.NaN(), math.NaN(), math.NaN(), math.NaN(), 10}, 403 {math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()}}, 404 expectedBool: [][]float64{{0, 0, 0, 0, 1}, {0, 0, 0, 0, 0}}, 405 }, 406 // != 407 { 408 name: "series != scalar", 409 seriesValues: [][]float64{{-10, 0, 1, 9, 10}, {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}}, 410 opType: NotEqType, 411 scalarVal: 10, 412 seriesLeft: true, 413 expected: [][]float64{{-10, 0, 1, 9, math.NaN()}, {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}}, 414 expectedBool: [][]float64{{1, 1, 1, 1, 0}, {1, 1, 1, 1, 1}}, 415 }, 416 { 417 name: "scalar != series", 418 scalarVal: 10, 419 opType: NotEqType, 420 seriesValues: [][]float64{{-10, 0, 1, 9, 10}, {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}}, 421 seriesLeft: false, 422 expected: [][]float64{{10, 10, 10, 10, math.NaN()}, {10, 10, 10, 10, 10}}, 423 expectedBool: [][]float64{{1, 1, 1, 1, 0}, {1, 1, 1, 1, 1}}, 424 }, 425 // > 426 { 427 name: "series > scalar", 428 seriesValues: [][]float64{{-10, 0, 1, 9, 10}, 429 {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}}, 430 opType: GreaterType, 431 scalarVal: 10, 432 seriesLeft: true, 433 expected: [][]float64{ 434 {math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()}, 435 {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.NaN()}, 436 }, 437 expectedBool: [][]float64{{0, 0, 0, 0, 0}, {1, 1, 0, 1, 0}}, 438 }, 439 { 440 name: "scalar > series", 441 scalarVal: 10, 442 opType: GreaterType, 443 seriesValues: [][]float64{ 444 {-10, 0, 1, 9, 10}, 445 {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}, 446 }, 447 seriesLeft: false, 448 expected: [][]float64{ 449 {10, 10, 10, 10, math.NaN()}, 450 {math.NaN(), math.NaN(), math.NaN(), math.NaN(), 10}, 451 }, 452 expectedBool: [][]float64{{1, 1, 1, 1, 0}, {0, 0, 0, 0, 1}}, 453 }, 454 // > 455 { 456 name: "series < scalar", 457 seriesValues: [][]float64{ 458 {-10, 0, 1, 9, 10}, 459 {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}, 460 }, 461 opType: LesserType, 462 scalarVal: 10, 463 seriesLeft: true, 464 expected: [][]float64{ 465 {-10, 0, 1, 9, math.NaN()}, 466 {math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.Inf(-1)}, 467 }, 468 expectedBool: [][]float64{{1, 1, 1, 1, 0}, {0, 0, 0, 0, 1}}, 469 }, 470 { 471 name: "scalar < series", 472 scalarVal: 10, 473 opType: LesserType, 474 seriesValues: [][]float64{{-10, 0, 1, 9, 10}, 475 {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}}, 476 seriesLeft: false, 477 expected: [][]float64{ 478 {math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()}, 479 {10, 10, math.NaN(), 10, math.NaN()}, 480 }, 481 expectedBool: [][]float64{{0, 0, 0, 0, 0}, {1, 1, 0, 1, 0}}, 482 }, 483 // >= 484 { 485 name: "series >= scalar", 486 seriesValues: [][]float64{{-10, 0, 1, 9, 10}, 487 {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}}, 488 opType: GreaterEqType, 489 scalarVal: 10, 490 seriesLeft: true, 491 expected: [][]float64{{math.NaN(), math.NaN(), math.NaN(), math.NaN(), 10}, 492 {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.NaN()}}, 493 expectedBool: [][]float64{{0, 0, 0, 0, 1}, {1, 1, 0, 1, 0}}, 494 }, 495 { 496 name: "scalar >= series", 497 scalarVal: 10, 498 opType: GreaterEqType, 499 seriesValues: [][]float64{ 500 {-10, 0, 1, 9, 10}, 501 {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}, 502 }, 503 seriesLeft: false, 504 expected: [][]float64{ 505 {10, 10, 10, 10, 10}, 506 {math.NaN(), math.NaN(), math.NaN(), math.NaN(), 10}, 507 }, 508 expectedBool: [][]float64{{1, 1, 1, 1, 1}, {0, 0, 0, 0, 1}}, 509 }, 510 // <= 511 { 512 name: "series <= scalar", 513 seriesValues: [][]float64{ 514 {-10, 0, 1, 9, 10}, 515 {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}, 516 }, 517 opType: LesserEqType, 518 scalarVal: 10, 519 seriesLeft: true, 520 expected: [][]float64{ 521 {-10, 0, 1, 9, 10}, 522 {math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.Inf(-1)}, 523 }, 524 expectedBool: [][]float64{{1, 1, 1, 1, 1}, {0, 0, 0, 0, 1}}, 525 }, 526 { 527 name: "scalar <= series", 528 scalarVal: 10, 529 opType: LesserEqType, 530 seriesValues: [][]float64{ 531 {-10, 0, 1, 9, 10}, 532 {11, math.MaxFloat64, math.NaN(), math.Inf(1), math.Inf(-1)}, 533 }, 534 seriesLeft: false, 535 expected: [][]float64{ 536 {math.NaN(), math.NaN(), math.NaN(), math.NaN(), 10}, 537 {10, 10, math.NaN(), 10, math.NaN()}, 538 }, 539 expectedBool: [][]float64{{0, 0, 0, 0, 1}, {1, 1, 0, 1, 0}}, 540 }, 541 } 542 543 func TestSingleSeriesReturnBool(t *testing.T) { 544 now := xtime.Now() 545 546 for _, tt := range singleSeriesTests { 547 t.Run(tt.name, func(t *testing.T) { 548 op, err := NewOp( 549 tt.opType, 550 NodeParams{ 551 LNode: parser.NodeID(rune(0)), 552 RNode: parser.NodeID(rune(1)), 553 ReturnBool: true, 554 VectorMatcherBuilder: emptyVectorMatcherBuilder, 555 }, 556 ) 557 require.NoError(t, err) 558 559 c, sink := executor.NewControllerWithSink(parser.NodeID(rune(2))) 560 node := op.(baseOp).Node(c, transform.Options{}) 561 562 seriesValues := tt.seriesValues 563 metas := test.NewSeriesMeta("a", len(seriesValues)) 564 bounds := models.Bounds{ 565 Start: now, 566 Duration: time.Minute * time.Duration(len(seriesValues[0])), 567 StepSize: time.Minute, 568 } 569 570 series := test.NewBlockFromValuesWithSeriesMeta(bounds, metas, seriesValues) 571 // Set the series and scalar blocks on the correct sides 572 if tt.seriesLeft { 573 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), series) 574 require.NoError(t, err) 575 576 err = node.Process( 577 models.NoopQueryContext(), 578 parser.NodeID(rune(1)), 579 block.NewScalar(tt.scalarVal, block.Metadata{ 580 Bounds: bounds, 581 Tags: models.EmptyTags(), 582 }), 583 ) 584 585 require.NoError(t, err) 586 } else { 587 err = node.Process( 588 models.NoopQueryContext(), 589 parser.NodeID(rune(0)), 590 block.NewScalar(tt.scalarVal, block.Metadata{ 591 Bounds: bounds, 592 Tags: models.EmptyTags(), 593 }), 594 ) 595 596 require.NoError(t, err) 597 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(1)), series) 598 require.NoError(t, err) 599 } 600 601 compare.EqualsWithNans(t, tt.expectedBool, sink.Values) 602 603 assert.Equal(t, bounds, sink.Meta.Bounds) 604 assert.Equal(t, 0, sink.Meta.Tags.Len()) 605 606 assert.Equal(t, metas, sink.Metas) 607 }) 608 } 609 } 610 611 func TestSingleSeriesReturnValues(t *testing.T) { 612 now := xtime.Now() 613 614 for _, tt := range singleSeriesTests { 615 t.Run(tt.name, func(t *testing.T) { 616 op, err := NewOp( 617 tt.opType, 618 NodeParams{ 619 LNode: parser.NodeID(rune(0)), 620 RNode: parser.NodeID(rune(1)), 621 ReturnBool: false, 622 VectorMatcherBuilder: emptyVectorMatcherBuilder, 623 }, 624 ) 625 626 require.NoError(t, err) 627 c, sink := executor.NewControllerWithSink(parser.NodeID(rune(2))) 628 node := op.(baseOp).Node(c, transform.Options{}) 629 630 seriesValues := tt.seriesValues 631 metas := test.NewSeriesMeta("a", len(seriesValues)) 632 bounds := models.Bounds{ 633 Start: now, 634 Duration: time.Minute * time.Duration(len(seriesValues[0])), 635 StepSize: time.Minute, 636 } 637 638 series := test.NewBlockFromValuesWithSeriesMeta(bounds, metas, seriesValues) 639 // Set the series and scalar blocks on the correct sides 640 if tt.seriesLeft { 641 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), series) 642 require.NoError(t, err) 643 644 err = node.Process( 645 models.NoopQueryContext(), 646 parser.NodeID(rune(1)), 647 block.NewScalar(tt.scalarVal, block.Metadata{ 648 Bounds: bounds, 649 Tags: models.EmptyTags(), 650 }), 651 ) 652 653 require.NoError(t, err) 654 } else { 655 err = node.Process( 656 models.NoopQueryContext(), 657 parser.NodeID(rune(0)), 658 block.NewScalar(tt.scalarVal, block.Metadata{ 659 Bounds: bounds, 660 Tags: models.EmptyTags(), 661 }), 662 ) 663 664 require.NoError(t, err) 665 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(1)), series) 666 require.NoError(t, err) 667 } 668 669 compare.EqualsWithNans(t, tt.expected, sink.Values) 670 671 assert.Equal(t, bounds, sink.Meta.Bounds) 672 assert.Equal(t, 0, sink.Meta.Tags.Len()) 673 674 assert.Equal(t, metas, sink.Metas) 675 }) 676 } 677 } 678 679 var bothSeriesTests = []struct { 680 name string 681 opType string 682 lhsMeta []block.SeriesMeta 683 lhs [][]float64 684 rhsMeta []block.SeriesMeta 685 rhs [][]float64 686 returnBool bool 687 expectedMetas []block.SeriesMeta 688 expected [][]float64 689 }{ 690 /* Arithmetic */ 691 { 692 "+, second series matches first", 693 PlusType, 694 test.NewSeriesMeta("a", 2), 695 [][]float64{{1, 2, 3}, {4, 5, 6}}, 696 test.NewSeriesMeta("a", 3)[1:], 697 [][]float64{{10, 20, 30}, {40, 50, 60}}, 698 true, 699 test.NewSeriesMeta("a", 2)[1:], 700 [][]float64{{14, 25, 36}}, 701 }, 702 { 703 "-, first two series on lhs match", 704 MinusType, 705 test.NewSeriesMeta("a", 2), 706 [][]float64{{1, 2, 3}, {4, 5, 6}}, 707 test.NewSeriesMeta("a", 3), 708 [][]float64{{10, 20, 30}, {40, 50, 60}, {700, 800, 900}}, 709 true, 710 test.NewSeriesMeta("a", 2), 711 [][]float64{{-9, -18, -27}, {-36, -45, -54}}, 712 }, 713 { 714 "*, last two series on lhs match", 715 MultiplyType, 716 test.NewSeriesMeta("a", 3), 717 [][]float64{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, 718 test.NewSeriesMeta("a", 3)[1:], 719 [][]float64{{10, 20, 30}, {40, 50, 60}}, 720 true, 721 test.NewSeriesMeta("a", 3)[1:], 722 [][]float64{{40, 100, 180}, {280, 400, 540}}, 723 }, 724 { 725 "/, both series match", 726 DivType, 727 test.NewSeriesMeta("a", 2), 728 [][]float64{{1, 2, 3}, {40, 50, 60}}, 729 test.NewSeriesMeta("a", 2), 730 [][]float64{{10, 20, 30}, {4, 5, 6}}, 731 true, 732 test.NewSeriesMeta("a", 2), 733 [][]float64{{0.1, 0.1, 0.1}, {10, 10, 10}}, 734 }, 735 { 736 "^, single matching series", 737 ExpType, 738 test.NewSeriesMeta("a", 1), 739 [][]float64{{10, math.NaN(), -1, 9, 10, 4}}, 740 test.NewSeriesMeta("a", 1), 741 [][]float64{{2, 2, 0.5, 0.5, -1, -0.5}}, 742 true, 743 test.NewSeriesMeta("a", 1), 744 [][]float64{{100, math.NaN(), math.NaN(), 3, 0.1, 0.5}}, 745 }, 746 { 747 "%, single matching series", 748 ModType, 749 test.NewSeriesMeta("a", 1), 750 [][]float64{{10, 11, 12, 13, 14, 15}}, 751 test.NewSeriesMeta("a", 1), 752 [][]float64{{2, 3, -5, 1.5, 1.5, -1.5}}, 753 true, 754 test.NewSeriesMeta("a", 1), 755 [][]float64{{0, 2, 2, 1, 0.5, 0}}, 756 }, 757 /* Comparison */ 758 { 759 "==, second series matches first", 760 EqType, 761 test.NewSeriesMeta("a", 2), 762 [][]float64{{1, 2, 3}, {3, 6, 9}}, 763 test.NewSeriesMeta("a", 3)[1:], 764 [][]float64{{10, 6, 30}, {40, 50, 60}}, 765 false, 766 test.NewSeriesMeta("a", 2)[1:], 767 [][]float64{{math.NaN(), 6, math.NaN()}}, 768 }, 769 { 770 "== BOOL, second series matches first", 771 EqType, 772 test.NewSeriesMeta("a", 2), 773 [][]float64{{1, 2, 3}, {3, 6, 9}}, 774 test.NewSeriesMeta("a", 3)[1:], 775 [][]float64{{10, 6, 30}, {40, 50, 60}}, 776 true, 777 test.NewSeriesMeta("a", 2)[1:], 778 [][]float64{{0, 1, 0}}, 779 }, 780 { 781 "=! BOOL, both series match", 782 NotEqType, 783 test.NewSeriesMeta("a", 2), 784 [][]float64{{1, 2, 3}, {3, 6, 9}}, 785 test.NewSeriesMeta("a", 2), 786 [][]float64{{1, 20, 3}, {40, 6, 60}}, 787 true, 788 test.NewSeriesMeta("a", 2), 789 [][]float64{{0, 1, 0}, {1, 0, 1}}, 790 }, 791 { 792 "=!, both series match", 793 NotEqType, 794 test.NewSeriesMeta("a", 2), 795 [][]float64{{1, 2, 3}, {3, 6, 9}}, 796 test.NewSeriesMeta("a", 2), 797 [][]float64{{1, 20, 3}, {40, 6, 60}}, 798 false, 799 test.NewSeriesMeta("a", 2), 800 [][]float64{{math.NaN(), 2, math.NaN()}, {3, math.NaN(), 9}}, 801 }, 802 { 803 "> BOOL, last two series of rhs match", 804 GreaterType, 805 test.NewSeriesMeta("a", 3)[1:], 806 [][]float64{{1, 2, 3}, {3, 6, 9}}, 807 test.NewSeriesMeta("a", 3), 808 [][]float64{{10, 10, 10}, {1, 20, -100}, {2, 4, 10}}, 809 true, 810 test.NewSeriesMeta("a", 3)[1:], 811 [][]float64{{0, 0, 1}, {1, 1, 0}}, 812 }, 813 { 814 ">, last two series of rhs match", 815 GreaterType, 816 test.NewSeriesMeta("a", 3)[1:], 817 [][]float64{{1, 2, 3}, {3, 6, 9}}, 818 test.NewSeriesMeta("a", 3), 819 [][]float64{{10, 10, 10}, {1, 20, -100}, {2, 4, 10}}, 820 false, 821 test.NewSeriesMeta("a", 3)[1:], 822 [][]float64{{math.NaN(), math.NaN(), 3}, {3, 6, math.NaN()}}, 823 }, 824 { 825 "< BOOL, single series matches", 826 LesserType, 827 test.NewSeriesMeta("a", 1), 828 [][]float64{{1, 2, 3}}, 829 test.NewSeriesMeta("a", 1), 830 [][]float64{{-1, 2, 5}}, 831 true, 832 test.NewSeriesMeta("a", 1), 833 [][]float64{{0, 0, 1}}, 834 }, 835 { 836 "<, single series matches", 837 LesserType, 838 test.NewSeriesMeta("a", 1), 839 [][]float64{{1, 2, 3}}, 840 test.NewSeriesMeta("a", 1), 841 [][]float64{{-1, 2, 5}}, 842 false, 843 test.NewSeriesMeta("a", 1), 844 [][]float64{{math.NaN(), math.NaN(), 3}}, 845 }, 846 { 847 ">= BOOL, single series matches", 848 GreaterEqType, 849 test.NewSeriesMeta("a", 1), 850 [][]float64{{1, 2, 3}}, 851 test.NewSeriesMeta("a", 1), 852 [][]float64{{-1, 2, 5}}, 853 true, 854 test.NewSeriesMeta("a", 1), 855 [][]float64{{1, 1, 0}}, 856 }, 857 { 858 ">=, single series matches", 859 GreaterEqType, 860 test.NewSeriesMeta("a", 1), 861 [][]float64{{1, 2, 3}}, 862 test.NewSeriesMeta("a", 1), 863 [][]float64{{-1, 2, 5}}, 864 false, 865 test.NewSeriesMeta("a", 1), 866 [][]float64{{1, 2, math.NaN()}}, 867 }, 868 { 869 "<= BOOL, single series matches", 870 LesserEqType, 871 test.NewSeriesMeta("a", 1), 872 [][]float64{{1, 2, 3}}, 873 test.NewSeriesMeta("a", 1), 874 [][]float64{{-1, 2, 5}}, 875 true, 876 test.NewSeriesMeta("a", 1), 877 [][]float64{{0, 1, 1}}, 878 }, 879 { 880 "<=, single series matches", 881 LesserEqType, 882 test.NewSeriesMeta("a", 1), 883 [][]float64{{1, 2, 3}}, 884 test.NewSeriesMeta("a", 1), 885 [][]float64{{-1, 2, 5}}, 886 false, 887 test.NewSeriesMeta("a", 1), 888 [][]float64{{math.NaN(), 2, 3}}, 889 }, 890 } 891 892 func TestBothSeries(t *testing.T) { 893 now := xtime.Now() 894 895 for _, tt := range bothSeriesTests { 896 t.Run(tt.name, func(t *testing.T) { 897 op, err := NewOp( 898 tt.opType, 899 NodeParams{ 900 LNode: parser.NodeID(rune(0)), 901 RNode: parser.NodeID(rune(1)), 902 ReturnBool: tt.returnBool, 903 VectorMatcherBuilder: emptyVectorMatcherBuilder, 904 }, 905 ) 906 require.NoError(t, err) 907 908 c, sink := executor.NewControllerWithSink(parser.NodeID(rune(2))) 909 node := op.(baseOp).Node(c, transform.Options{}) 910 bounds := models.Bounds{ 911 Start: now, 912 Duration: time.Minute * time.Duration(len(tt.lhs[0])), 913 StepSize: time.Minute, 914 } 915 916 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), 917 test.NewBlockFromValuesWithSeriesMeta(bounds, tt.lhsMeta, tt.lhs)) 918 require.NoError(t, err) 919 920 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(1)), 921 test.NewBlockFromValuesWithSeriesMeta(bounds, tt.rhsMeta, tt.rhs)) 922 require.NoError(t, err) 923 924 compare.EqualsWithNans(t, tt.expected, sink.Values) 925 926 // Extract duped expected metas 927 expectedMeta := block.Metadata{ 928 Bounds: bounds, 929 ResultMetadata: block.NewResultMetadata(), 930 } 931 var expectedMetas []block.SeriesMeta 932 expectedMeta.Tags, expectedMetas = utils.DedupeMetadata( 933 tt.expectedMetas, models.NewTagOptions()) 934 expectedMeta, expectedMetas = removeNameTags(expectedMeta, expectedMetas) 935 assert.Equal(t, expectedMeta, sink.Meta) 936 assert.Equal(t, expectedMetas, sink.Metas) 937 }) 938 } 939 } 940 941 func TestBinaryFunctionWithDifferentNames(t *testing.T) { 942 now := xtime.Now() 943 944 meta := func(bounds models.Bounds, name string, m block.ResultMetadata) block.Metadata { 945 return block.Metadata{ 946 Bounds: bounds, 947 Tags: models.NewTags(1, models.NewTagOptions()).SetName([]byte(name)), 948 ResultMetadata: m, 949 } 950 } 951 952 var ( 953 bounds = models.Bounds{ 954 Start: now, 955 Duration: time.Minute * 3, 956 StepSize: time.Minute, 957 } 958 959 lhsResultMeta = block.ResultMetadata{ 960 LocalOnly: true, 961 Exhaustive: false, 962 Warnings: []block.Warning{}, 963 } 964 965 lhsMeta = meta(bounds, "left", lhsResultMeta) 966 lhsMetas = test.NewSeriesMeta("a", 2) 967 lhs = [][]float64{{1, 2, 3}, {4, 5, 6}} 968 left = test.NewBlockFromValuesWithMetaAndSeriesMeta( 969 lhsMeta, lhsMetas, lhs, 970 ) 971 972 rhsResultMeta = block.ResultMetadata{ 973 LocalOnly: false, 974 Exhaustive: true, 975 Warnings: []block.Warning{block.Warning{Name: "foo", Message: "bar"}}, 976 } 977 978 rhsMeta = meta(bounds, "right", rhsResultMeta) 979 rhsMetas = test.NewSeriesMeta("a", 3)[1:] 980 rhs = [][]float64{{10, 20, 30}, {40, 50, 60}} 981 right = test.NewBlockFromValuesWithMetaAndSeriesMeta( 982 rhsMeta, rhsMetas, rhs, 983 ) 984 985 expected = [][]float64{{14, 25, 36}} 986 ) 987 988 op, err := NewOp( 989 PlusType, 990 NodeParams{ 991 LNode: parser.NodeID(rune(0)), 992 RNode: parser.NodeID(rune(1)), 993 VectorMatcherBuilder: emptyVectorMatcherBuilder, 994 }, 995 ) 996 require.NoError(t, err) 997 998 c, sink := executor.NewControllerWithSink(parser.NodeID(rune(2))) 999 node := op.(baseOp).Node(c, transform.Options{}) 1000 1001 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), left) 1002 require.NoError(t, err) 1003 1004 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(1)), right) 1005 require.NoError(t, err) 1006 1007 compare.EqualsWithNans(t, expected, sink.Values) 1008 1009 exResultMeta := block.ResultMetadata{ 1010 LocalOnly: false, 1011 Exhaustive: false, 1012 Warnings: []block.Warning{block.Warning{Name: "foo", Message: "bar"}}, 1013 } 1014 1015 // Extract duped expected metas 1016 expectedMeta := block.Metadata{ 1017 Bounds: bounds, 1018 Tags: models.NewTags(1, models.NewTagOptions()).AddTag(toTag("a1", "a1")), 1019 ResultMetadata: exResultMeta, 1020 } 1021 1022 expectedMetas := []block.SeriesMeta{ 1023 block.SeriesMeta{ 1024 Name: []byte("a1"), 1025 Tags: models.EmptyTags(), 1026 }, 1027 } 1028 1029 assert.Equal(t, expectedMeta, sink.Meta) 1030 assert.Equal(t, expectedMetas, sink.Metas) 1031 } 1032 1033 func TestOneToOneMatcher(t *testing.T) { 1034 now := xtime.Now() 1035 1036 meta := func(bounds models.Bounds, name string, m block.ResultMetadata) block.Metadata { 1037 return block.Metadata{ 1038 Bounds: bounds, 1039 Tags: models.NewTags(1, models.NewTagOptions()).SetName([]byte(name)), 1040 ResultMetadata: m, 1041 } 1042 } 1043 1044 var ( 1045 bounds = models.Bounds{ 1046 Start: now, 1047 Duration: time.Minute * 3, 1048 StepSize: time.Minute, 1049 } 1050 1051 lhsResultMeta = block.ResultMetadata{ 1052 LocalOnly: true, 1053 Exhaustive: false, 1054 Warnings: []block.Warning{}, 1055 } 1056 1057 lhsMeta = meta(bounds, "left", lhsResultMeta) 1058 lhsMetas = test.NewSeriesMeta("a", 2) 1059 lhs = [][]float64{{1, 2, 3}, {4, 5, 6}} 1060 left = test.NewBlockFromValuesWithMetaAndSeriesMeta( 1061 lhsMeta, lhsMetas, lhs, 1062 ) 1063 1064 rhsResultMeta = block.ResultMetadata{ 1065 LocalOnly: false, 1066 Exhaustive: true, 1067 Warnings: []block.Warning{{Name: "foo", Message: "bar"}}, 1068 } 1069 1070 rhsMeta = meta(bounds, "right", rhsResultMeta) 1071 rhsMetas = test.NewSeriesMeta("a", 3)[1:] 1072 rhs = [][]float64{{10, 20, 30}, {40, 50, 60}} 1073 right = test.NewBlockFromValuesWithMetaAndSeriesMeta( 1074 rhsMeta, rhsMetas, rhs, 1075 ) 1076 1077 expected = [][]float64{{41, 52, 63}, {14, 25, 36}} 1078 ) 1079 1080 op, err := NewOp( 1081 PlusType, 1082 NodeParams{ 1083 LNode: parser.NodeID(rune(0)), 1084 RNode: parser.NodeID(rune(1)), 1085 VectorMatcherBuilder: oneToOneVectorMatchingBuilder, 1086 }, 1087 ) 1088 require.NoError(t, err) 1089 1090 c, sink := executor.NewControllerWithSink(parser.NodeID(rune(2))) 1091 node := op.(baseOp).Node(c, transform.Options{}) 1092 1093 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), left) 1094 require.NoError(t, err) 1095 1096 err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(1)), right) 1097 require.NoError(t, err) 1098 1099 compare.EqualsWithNans(t, expected, sink.Values) 1100 1101 expectedMetas := []block.SeriesMeta{ 1102 { 1103 Name: []byte("a0"), 1104 Tags: models.EmptyTags(), 1105 }, 1106 { 1107 Name: []byte("a1"), 1108 Tags: models.NewTags(1, models.NewTagOptions()).AddTag(toTag("a1", "a1")), 1109 }, 1110 } 1111 1112 assert.Equal(t, expectedMetas, sink.Metas) 1113 } 1114 1115 func oneToOneVectorMatchingBuilder(_, _ block.Block) VectorMatching { 1116 return VectorMatching{ 1117 Set: true, 1118 Card: CardOneToOne, 1119 On: true, 1120 MatchingLabels: [][]byte{[]byte("a1")}, 1121 } 1122 }