github.com/mrgossett/heapster@v0.18.2/store/statstore/stat_store_test.go (about) 1 // Copyright 2015 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package statstore 15 16 import ( 17 "testing" 18 "time" 19 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 ) 23 24 // TestLast tests all flows of the Last method. 25 func TestLast(t *testing.T) { 26 // epsilon: 10, resolution: 1 minute, total: 1 hour, no percentiles 27 store := NewStatStore(10, time.Minute, 60, []float64{}) 28 assert := assert.New(t) 29 now := time.Now().Truncate(time.Minute) 30 31 // Invocation with nothing in the StatStore - no result 32 last, max, err := store.Last() 33 assert.Error(err) 34 assert.Equal(last, TimePoint{}) 35 assert.Equal(max, uint64(0)) 36 37 // Put 5 Points in the same minute. Average: 10029, Max: 50000 38 assert.NoError(store.Put(TimePoint{ 39 Timestamp: now, 40 Value: uint64(55), 41 })) 42 assert.NoError(store.Put(TimePoint{ 43 Timestamp: now.Add(time.Second), 44 Value: uint64(1), 45 })) 46 assert.NoError(store.Put(TimePoint{ 47 Timestamp: now, 48 Value: uint64(12), 49 })) 50 assert.NoError(store.Put(TimePoint{ 51 Timestamp: now.Add(1 * time.Second), 52 Value: uint64(77), 53 })) 54 assert.NoError(store.Put(TimePoint{ 55 Timestamp: now.Add(1 * time.Second), 56 Value: uint64(50000), 57 })) 58 59 // Put one point in a previous minute, should be ignored. 60 assert.Error(store.Put(TimePoint{ 61 Timestamp: now.Add(-2 * time.Minute), 62 Value: uint64(100000), 63 })) 64 65 last, max, err = store.Last() 66 assert.NoError(err) 67 assert.Equal(last.Timestamp, now) 68 assert.Equal(last.Value, uint64(10029)) 69 assert.Equal(max, uint64(50000)) 70 71 // Put one value from the next minute 72 assert.NoError(store.Put(TimePoint{ 73 Timestamp: now.Add(time.Minute), 74 Value: uint64(92), 75 })) 76 77 // Invocation where Last is a "fake" point added because of a missed resolution. 78 last, max, err = store.Last() 79 assert.NoError(err) 80 assert.Equal(last.Timestamp, now.Add(time.Minute)) 81 assert.Equal(last.Value, uint64(92)) 82 assert.Equal(max, uint64(92)) 83 84 // Put one value from two more minutes later 85 assert.NoError(store.Put(TimePoint{ 86 Timestamp: now.Add(3 * time.Minute), 87 Value: uint64(10000), 88 })) 89 90 last, max, err = store.Last() 91 assert.NoError(err) 92 assert.Equal(last.Timestamp, now.Add(3*time.Minute)) 93 assert.Equal(last.Value, uint64(10000)) 94 assert.Equal(max, uint64(10000)) 95 } 96 97 // TestMax tests all flows of the Max method. 98 func TestMax(t *testing.T) { 99 // epsilon: 50, resolution: 1 minute, total: 5 minutes, no percentiles 100 store := NewStatStore(50, time.Minute, 5, []float64{}) 101 assert := assert.New(t) 102 now := time.Now().Truncate(time.Minute) 103 104 // Invocation with nothing in the StatStore - no result 105 max, err := store.Max() 106 assert.Error(err) 107 assert.Equal(max, uint64(0)) 108 109 // Put 3 Points in the same minute. Max: 88 110 assert.NoError(store.Put(TimePoint{ 111 Timestamp: now, 112 Value: uint64(55), 113 })) 114 assert.NoError(store.Put(TimePoint{ 115 Timestamp: now.Add(time.Second), 116 Value: uint64(88), 117 })) 118 assert.NoError(store.Put(TimePoint{ 119 Timestamp: now, 120 Value: uint64(21), 121 })) 122 123 // Invocation with elements only in lastPut 124 max, err = store.Max() 125 assert.Error(err) 126 assert.Equal(max, uint64(0)) 127 128 // Put 1 Point in the next minute. 129 assert.NoError(store.Put(TimePoint{ 130 Timestamp: now.Add(time.Minute), 131 Value: uint64(199), 132 })) 133 134 // Invocation where the previous minute is now accessible 135 max, err = store.Max() 136 assert.NoError(err) 137 assert.Equal(max, uint64(199)) 138 139 // Put 1 Point in the next minute. 140 assert.NoError(store.Put(TimePoint{ 141 Timestamp: now.Add(2 * time.Minute), 142 Value: uint64(22), 143 })) 144 145 // Put one point in a previous minute, should be ignored. 146 assert.Error(store.Put(TimePoint{ 147 Timestamp: now, 148 Value: uint64(100000), 149 })) 150 151 // Put one point in the next minute, same bucket as before. 152 assert.NoError(store.Put(TimePoint{ 153 Timestamp: now.Add(2 * time.Minute), 154 Value: uint64(40), 155 })) 156 157 // Put one point in the next minute. 158 // Even though the max is greater, this minute is currently in lastPut, 159 // so it is excluded from the max. 160 assert.NoError(store.Put(TimePoint{ 161 Timestamp: now.Add(3 * time.Minute), 162 Value: uint64(511), 163 })) 164 165 // Invocation with three minutes in three different buckets 166 max, err = store.Max() 167 assert.NoError(err) 168 assert.Equal(max, uint64(511)) 169 170 // Put one value from the next minute 171 assert.NoError(store.Put(TimePoint{ 172 Timestamp: now.Add(4 * time.Minute), 173 Value: uint64(550), 174 })) 175 176 // Invocation with four minutes 177 max, err = store.Max() 178 assert.NoError(err) 179 assert.Equal(max, uint64(550)) 180 181 // Call again to assert validity of the cache 182 max, err = store.Max() 183 assert.NoError(err) 184 assert.Equal(max, uint64(550)) 185 } 186 187 // TestGet tests all flows of the Get method. 188 // Seven resolutions are stored in total, causing two rewinds. 189 func TestGet(t *testing.T) { 190 // epsilon: 100, resolution: 1 minute, total: 5 minutes, no percentiles 191 store := NewStatStore(100, time.Minute, 5, []float64{}) 192 assert := assert.New(t) 193 require := require.New(t) 194 now := time.Now().Truncate(time.Minute) 195 zeroTime := time.Time{} 196 197 // Invocation with nothing in the StatStore - empty result 198 res := store.Get(zeroTime, zeroTime) 199 assert.Len(res, 0) 200 201 // Put 3 Points in the same minute. Average: 150, Max: 190 202 assert.NoError(store.Put(TimePoint{ 203 Timestamp: now, 204 Value: uint64(120), 205 })) 206 assert.NoError(store.Put(TimePoint{ 207 Timestamp: now, 208 Value: uint64(190), 209 })) 210 assert.NoError(store.Put(TimePoint{ 211 Timestamp: now.Add(time.Second), 212 Value: uint64(140), 213 })) 214 215 // Put 1 Point in the next minute. 216 assert.NoError(store.Put(TimePoint{ 217 Timestamp: now.Add(time.Minute), 218 Value: uint64(599), 219 })) 220 221 // Invocation with one element in the StatStore 222 res = store.Get(zeroTime, zeroTime) 223 require.Len(res, 2) 224 assert.Equal(res[0], TimePoint{ 225 Timestamp: now.Add(time.Minute), 226 Value: uint64(599), 227 }) 228 assert.Equal(res[1], TimePoint{ 229 Timestamp: now, 230 Value: uint64(200), 231 }) 232 // Put 1 Point in the next minute. 233 assert.NoError(store.Put(TimePoint{ 234 Timestamp: now.Add(2 * time.Minute), 235 Value: uint64(22), 236 })) 237 238 // Put one point in a previous minute, should be ignored. 239 assert.Error(store.Put(TimePoint{ 240 Timestamp: now, 241 Value: uint64(100000), 242 })) 243 244 // Invocation with two elements in the StatStore 245 res = store.Get(zeroTime, zeroTime) 246 require.Len(res, 3) 247 assert.Equal(res[0], TimePoint{ 248 Timestamp: now.Add(2 * time.Minute), 249 Value: uint64(22), 250 }) 251 assert.Equal(res[1], TimePoint{ 252 Timestamp: now.Add(time.Minute), 253 Value: uint64(600), 254 }) 255 assert.Equal(res[2], TimePoint{ 256 Timestamp: now, 257 Value: uint64(200), 258 }) 259 260 // Put one point in the next minute, same bucket as before. 261 assert.NoError(store.Put(TimePoint{ 262 Timestamp: now.Add(2 * time.Minute), 263 Value: uint64(110), 264 })) 265 266 // Put one point in the next minute. 267 assert.NoError(store.Put(TimePoint{ 268 Timestamp: now.Add(3 * time.Minute), 269 Value: uint64(511), 270 })) 271 272 // Invocation with three elements in the StatStore 273 res = store.Get(zeroTime, zeroTime) 274 require.Len(res, 4) 275 assert.Equal(res[0], TimePoint{ 276 Timestamp: now.Add(3 * time.Minute), 277 Value: uint64(511), 278 }) 279 assert.Equal(res[1], TimePoint{ 280 Timestamp: now.Add(2 * time.Minute), 281 Value: uint64(100), 282 }) 283 assert.Equal(res[2], TimePoint{ 284 Timestamp: now.Add(time.Minute), 285 Value: uint64(600), 286 }) 287 assert.Equal(res[3], TimePoint{ 288 Timestamp: now, 289 Value: uint64(200), 290 }) 291 292 // Put one value from the next minute. Same bucket as the previous minute 293 assert.NoError(store.Put(TimePoint{ 294 Timestamp: now.Add(4 * time.Minute), 295 Value: uint64(540), 296 })) 297 298 // Put one value from the next minute. Same bucket as the previous minute 299 assert.NoError(store.Put(TimePoint{ 300 Timestamp: now.Add(5 * time.Minute), 301 Value: uint64(550), 302 })) 303 304 // Invocation with a full StatStore and a multi-resolution bucket 305 res = store.Get(zeroTime, zeroTime) 306 require.Len(res, 6) 307 assert.Equal(res[0], TimePoint{ 308 Timestamp: now.Add(5 * time.Minute), 309 Value: uint64(550), 310 }) 311 assert.Equal(res[1], TimePoint{ 312 Timestamp: now.Add(4 * time.Minute), 313 Value: uint64(600), 314 }) 315 assert.Equal(res[2], TimePoint{ 316 Timestamp: now.Add(3 * time.Minute), 317 Value: uint64(600), 318 }) 319 assert.Equal(res[3], TimePoint{ 320 Timestamp: now.Add(2 * time.Minute), 321 Value: uint64(100), 322 }) 323 assert.Equal(res[4], TimePoint{ 324 Timestamp: now.Add(time.Minute), 325 Value: uint64(600), 326 }) 327 assert.Equal(res[5], TimePoint{ 328 Timestamp: now, 329 Value: uint64(200), 330 }) 331 332 // Put one value from the next minute. Different bucket than the previous minute 333 // This Put should cause a rewind for the first minute that was stored 334 assert.NoError(store.Put(TimePoint{ 335 Timestamp: now.Add(6 * time.Minute), 336 Value: uint64(750), 337 })) 338 339 // Invocation after one rewind 340 res = store.Get(zeroTime, zeroTime) 341 require.Len(res, 6) 342 assert.Equal(res[0], TimePoint{ 343 Timestamp: now.Add(6 * time.Minute), 344 Value: uint64(750), 345 }) 346 assert.Equal(res[1], TimePoint{ 347 Timestamp: now.Add(5 * time.Minute), 348 Value: uint64(600), 349 }) 350 assert.Equal(res[2], TimePoint{ 351 Timestamp: now.Add(4 * time.Minute), 352 Value: uint64(600), 353 }) 354 assert.Equal(res[3], TimePoint{ 355 Timestamp: now.Add(3 * time.Minute), 356 Value: uint64(600), 357 }) 358 assert.Equal(res[4], TimePoint{ 359 Timestamp: now.Add(2 * time.Minute), 360 Value: uint64(100), 361 }) 362 assert.Equal(res[5], TimePoint{ 363 Timestamp: now.Add(time.Minute), 364 Value: uint64(600), 365 }) 366 367 // Cause one more rewind 368 assert.NoError(store.Put(TimePoint{ 369 Timestamp: now.Add(7 * time.Minute), 370 Value: uint64(998), 371 })) 372 373 // Invocation after second rewind 374 res = store.Get(zeroTime, zeroTime) 375 require.Len(res, 6) 376 assert.Equal(res[0], TimePoint{ 377 Timestamp: now.Add(7 * time.Minute), 378 Value: uint64(998), 379 }) 380 assert.Equal(res[1], TimePoint{ 381 Timestamp: now.Add(6 * time.Minute), 382 Value: uint64(800), 383 }) 384 assert.Equal(res[2], TimePoint{ 385 Timestamp: now.Add(5 * time.Minute), 386 Value: uint64(600), 387 }) 388 assert.Equal(res[3], TimePoint{ 389 Timestamp: now.Add(4 * time.Minute), 390 Value: uint64(600), 391 }) 392 assert.Equal(res[4], TimePoint{ 393 Timestamp: now.Add(3 * time.Minute), 394 Value: uint64(600), 395 }) 396 assert.Equal(res[5], TimePoint{ 397 Timestamp: now.Add(2 * time.Minute), 398 Value: uint64(100), 399 }) 400 401 // Invocation with start after end 402 res = store.Get(now.Add(10*time.Minute), now) 403 assert.Len(res, 0) 404 405 // Invocation with mid-length start-end range 406 res = store.Get(now.Add(3*time.Minute), now.Add(5*time.Minute)) 407 assert.Len(res, 3) 408 assert.Equal(res[0], TimePoint{ 409 Timestamp: now.Add(5 * time.Minute), 410 Value: uint64(600), 411 }) 412 assert.Equal(res[1], TimePoint{ 413 Timestamp: now.Add(4 * time.Minute), 414 Value: uint64(600), 415 }) 416 assert.Equal(res[2], TimePoint{ 417 Timestamp: now.Add(3 * time.Minute), 418 Value: uint64(600), 419 }) 420 421 // Invocation with full-length start-end range. 422 // The first TimePoint is ignored, as it is equal to start 423 res = store.Get(now.Add(2*time.Minute), now.Add(6*time.Minute)) 424 assert.Len(res, 4) 425 assert.Equal(res[0], TimePoint{ 426 Timestamp: now.Add(6 * time.Minute), 427 Value: uint64(800), 428 }) 429 assert.Equal(res[1], TimePoint{ 430 Timestamp: now.Add(5 * time.Minute), 431 Value: uint64(600), 432 }) 433 assert.Equal(res[2], TimePoint{ 434 Timestamp: now.Add(4 * time.Minute), 435 Value: uint64(600), 436 }) 437 assert.Equal(res[3], TimePoint{ 438 Timestamp: now.Add(3 * time.Minute), 439 Value: uint64(600), 440 }) 441 442 // Invocation with start-end range outside of the scope of values. 443 res = store.Get(now.Add(-2*time.Minute), now.Add(time.Minute)) 444 assert.Len(res, 0) 445 446 // Put one value from 10 minutes since the last Put. 447 // This Put should force the entire StatStore to be filled with 1000. 448 assert.NoError(store.Put(TimePoint{ 449 Timestamp: now.Add(25 * time.Minute), 450 Value: uint64(1500), 451 })) 452 453 // Invocation after a future Put. Everything in between is placed in the last bucket 454 res = store.Get(zeroTime, zeroTime) 455 require.Len(res, 6) 456 assert.Equal(res[0], TimePoint{ 457 Timestamp: now.Add(25 * time.Minute), 458 Value: uint64(1500), 459 }) 460 assert.Equal(res[1], TimePoint{ 461 Timestamp: now.Add(24 * time.Minute), 462 Value: uint64(1000), 463 }) 464 assert.Equal(res[2], TimePoint{ 465 Timestamp: now.Add(23 * time.Minute), 466 Value: uint64(1000), 467 }) 468 assert.Equal(res[3], TimePoint{ 469 Timestamp: now.Add(22 * time.Minute), 470 Value: uint64(1000), 471 }) 472 assert.Equal(res[4], TimePoint{ 473 Timestamp: now.Add(21 * time.Minute), 474 Value: uint64(1000), 475 }) 476 assert.Equal(res[5], TimePoint{ 477 Timestamp: now.Add(20 * time.Minute), 478 Value: uint64(1000), 479 }) 480 481 } 482 483 // TestAverage tests all flows of the Average method. 484 func TestAverage(t *testing.T) { 485 // epsilon: 100, resolution: 1 minute, total: 5 minutes, no percentiles 486 store := NewStatStore(100, time.Minute, 5, []float64{}) 487 assert := assert.New(t) 488 now := time.Now().Truncate(time.Minute) 489 490 // Invocation with nothing in the StatStore - error 491 avg, err := store.Average() 492 assert.Error(err) 493 assert.Equal(avg, uint64(0)) 494 495 // Populate StatStore 496 assert.NoError(store.Put(TimePoint{ 497 Timestamp: now, 498 Value: uint64(190), 499 })) 500 501 // Put 1 Point in the next minute. Same bucket (200) 502 assert.NoError(store.Put(TimePoint{ 503 Timestamp: now.Add(time.Minute), 504 Value: uint64(199), 505 })) 506 507 // Invocation with one element in the StatStore 508 avg, err = store.Average() 509 assert.NoError(err) 510 assert.Equal(avg, 200) 511 512 // Put one Point in the next minute. Same bucket (200) 513 assert.NoError(store.Put(TimePoint{ 514 Timestamp: now.Add(2 * time.Minute), 515 Value: uint64(120), 516 })) 517 518 // Put one point in the next minute. Different bucket (600) 519 assert.NoError(store.Put(TimePoint{ 520 Timestamp: now.Add(3 * time.Minute), 521 Value: uint64(511), 522 })) 523 524 // Put one point in the next minute. Same bucket (600) 525 assert.NoError(store.Put(TimePoint{ 526 Timestamp: now.Add(4 * time.Minute), 527 Value: uint64(599), 528 })) 529 530 // Put one point in the next minute. StatStore is full now 531 assert.NoError(store.Put(TimePoint{ 532 Timestamp: now.Add(5 * time.Minute), 533 Value: uint64(1081), 534 })) 535 536 // Invocation with five elements in the StatStore 537 avg, err = store.Average() 538 assert.NoError(err) 539 assert.Equal(avg, uint64(360)) 540 541 // Call again to assert validity of the cache 542 avg, err = store.Average() 543 assert.NoError(err) 544 assert.Equal(avg, uint64(360)) 545 } 546 547 // TestPercentile tests all flows of the Percentile method. 548 func TestPercentile(t *testing.T) { 549 // epsilon: 50, resolution: 1 minute, total: 5 minutes, two percentiles 550 store := NewStatStore(50, time.Minute, 5, []float64{0.5, 0.95}) 551 assert := assert.New(t) 552 now := time.Now().Truncate(time.Minute) 553 554 // Invocation with nothing in the StatStore - error 555 pc, err := store.Percentile(0.95) 556 assert.Error(err) 557 assert.Equal(pc, uint64(0)) 558 559 // Populate StatStore 560 assert.NoError(store.Put(TimePoint{ 561 Timestamp: now, 562 Value: uint64(190), 563 })) 564 565 // Put 1 Point in the next minute. Same bucket (200) 566 assert.NoError(store.Put(TimePoint{ 567 Timestamp: now.Add(time.Minute), 568 Value: uint64(199), 569 })) 570 571 // Invocation with an unsupported percentile 572 pc, err = store.Percentile(0.2) 573 assert.Error(err) 574 assert.Equal(pc, uint64(0)) 575 576 // Invocation with one element in the StatStore 577 pc, err = store.Percentile(0.5) 578 assert.NoError(err) 579 assert.Equal(pc, 200) 580 pc, err = store.Percentile(0.95) 581 assert.NoError(err) 582 assert.Equal(pc, 200) 583 584 // Put one Point in the next minute. Different bucket (550) 585 assert.NoError(store.Put(TimePoint{ 586 Timestamp: now.Add(2 * time.Minute), 587 Value: uint64(532), 588 })) 589 590 // Put one point in the next minute. Same bucket (550) 591 assert.NoError(store.Put(TimePoint{ 592 Timestamp: now.Add(3 * time.Minute), 593 Value: uint64(511), 594 })) 595 596 // Put one point in the next minute. Different bucket (50) 597 assert.NoError(store.Put(TimePoint{ 598 Timestamp: now.Add(4 * time.Minute), 599 Value: uint64(30), 600 })) 601 602 // Put one point in the next minute. StatStore is full now 603 assert.NoError(store.Put(TimePoint{ 604 Timestamp: now.Add(5 * time.Minute), 605 Value: uint64(50), 606 })) 607 608 // Invocation with five elements in the StatStore 609 pc, err = store.Percentile(0.5) 610 assert.NoError(err) 611 assert.Equal(pc, uint64(200)) 612 pc, err = store.Percentile(0.95) 613 assert.NoError(err) 614 assert.Equal(pc, uint64(550)) 615 616 // Call again to assert validity of the cache 617 pc, err = store.Percentile(0.5) 618 assert.NoError(err) 619 assert.Equal(pc, uint64(200)) 620 pc, err = store.Percentile(0.95) 621 assert.NoError(err) 622 assert.Equal(pc, uint64(550)) 623 } 624 625 // TestIsEmpty tests all flows of the IsEmpty method. 626 func TestIsEmpty(t *testing.T) { 627 // epsilon: 50, resolution: 1 minute, total: 5 minutes, no percentiles 628 store := NewStatStore(50, time.Minute, 5, []float64{}) 629 assert := assert.New(t) 630 now := time.Now().Truncate(time.Minute) 631 632 // Invocation with nothing in the StatStore 633 assert.True(store.IsEmpty()) 634 635 // Put one point in the StatStore. 636 assert.NoError(store.Put(TimePoint{ 637 Timestamp: now, 638 Value: uint64(30), 639 })) 640 641 // Invocation with values only in the lastPut field. 642 assert.True(store.IsEmpty()) 643 644 // Put one point in the next minute. StatStore is not empty now 645 assert.NoError(store.Put(TimePoint{ 646 Timestamp: now.Add(1 * time.Minute), 647 Value: uint64(50), 648 })) 649 650 // Invocation with a value in the StatStore 651 assert.False(store.IsEmpty()) 652 } 653 654 // TestMaxSize tests all flows of the MaxSize method. 655 func TestMaxSize(t *testing.T) { 656 assert := assert.New(t) 657 658 // Invocation with a StatStore of 5 minutes, 1 min resolution. 659 store := NewStatStore(50, time.Minute, 5, []float64{}) 660 assert.Equal(5*time.Minute, store.MaxSize()) 661 662 // Invocation with a StatStore of 1 hour, 5 min resolution. 663 store = NewStatStore(50, 5*time.Minute, 12, []float64{}) 664 assert.Equal(time.Hour, store.MaxSize()) 665 666 // Invocation with a StatStore of 1 hour, 1 hour resolution. 667 store = NewStatStore(50, time.Hour, 1, []float64{}) 668 assert.Equal(time.Hour, store.MaxSize()) 669 670 // Invocation with a StatStore of 1 minute, 10 second resolution. 671 store = NewStatStore(50, 10*time.Second, 6, []float64{}) 672 assert.Equal(time.Minute, store.MaxSize()) 673 }