github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/scheduler/task_duration_estimator_test.go (about) 1 package scheduler 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/evergreen-ci/evergreen" 8 "github.com/evergreen-ci/evergreen/db" 9 "github.com/evergreen-ci/evergreen/model" 10 "github.com/evergreen-ci/evergreen/model/task" 11 "github.com/evergreen-ci/evergreen/testutil" 12 "github.com/mongodb/grip" 13 . "github.com/smartystreets/goconvey/convey" 14 ) 15 16 var taskDurationEstimatorTestConf = testutil.TestConfig() 17 18 func init() { 19 db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(taskDurationEstimatorTestConf)) 20 grip.CatchError(grip.SetSender(testutil.SetupTestSender(taskDurationEstimatorTestConf.Scheduler.LogFile))) 21 } 22 23 func TestDBTaskDurationEstimator(t *testing.T) { 24 var taskDurationEstimator *DBTaskDurationEstimator 25 var displayNames []string 26 var buildVariants []string 27 var projects []string 28 var taskIds []string 29 var timeTaken []time.Duration 30 var runnableTasks []task.Task 31 32 Convey("With a DBTaskDurationEstimator...", t, 33 func() { 34 taskDurationEstimator = &DBTaskDurationEstimator{} 35 taskIds = []string{"t1", "t2", "t3", "t4", "t5", "t6", 36 "t7", "t8", "t9"} 37 displayNames = []string{"dn1", "dn2", "dn3", "dn4", "dn5", "dn6"} 38 buildVariants = []string{"bv1", "bv2", "bv3", "bv4", "bv5", "bv6"} 39 projects = []string{"p1", "p2", "p3", "p4", "p5", "p6"} 40 timeTaken = []time.Duration{ 41 time.Duration(1) * time.Minute, time.Duration(2) * time.Minute, 42 time.Duration(3) * time.Minute, time.Duration(4) * time.Minute, 43 time.Duration(5) * time.Minute, time.Duration(6) * time.Minute, 44 } 45 46 Convey("the expected task duration for tasks with different "+ 47 "display names, same project and buildvariant, should be "+ 48 "distinct", 49 func() { 50 runnableTasks = []task.Task{ 51 // task 0/7 are from the same project and buildvariant 52 // and have different display names - expected duration 53 // should not be averaged between these two 54 { 55 Id: taskIds[0], 56 DisplayName: displayNames[0], 57 BuildVariant: buildVariants[0], 58 TimeTaken: timeTaken[0], 59 Project: projects[0], 60 Status: evergreen.TaskSucceeded, 61 StartTime: time.Now().Add(-timeTaken[0]), 62 FinishTime: time.Now(), 63 }, 64 { 65 Id: taskIds[7], 66 DisplayName: displayNames[1], 67 BuildVariant: buildVariants[0], 68 TimeTaken: timeTaken[4], 69 Project: projects[0], 70 Status: evergreen.TaskSucceeded, 71 StartTime: time.Now().Add(-timeTaken[4]), 72 FinishTime: time.Now(), 73 }, 74 } 75 76 So(db.Clear(task.Collection), ShouldBeNil) 77 78 // insert all the test tasks 79 for _, testTask := range runnableTasks { 80 testutil.HandleTestingErr(testTask.Insert(), t, "failed to "+ 81 "insert task") 82 } 83 84 taskDurations, err := taskDurationEstimator. 85 GetExpectedDurations(runnableTasks) 86 87 So(err, ShouldEqual, nil) 88 89 projectDurations := taskDurations.TaskDurationByProject 90 // we only have 1 project for all runnable tasks 91 So(len(projectDurations), ShouldEqual, 1) 92 93 bvDurs := projectDurations[projects[0]]. 94 TaskDurationByBuildVariant 95 // we only have 1 buildvariant for all runnable tasks 96 So(len(bvDurs), ShouldEqual, 1) 97 98 tkDurs := bvDurs[buildVariants[0]].TaskDurationByDisplayName 99 // we have 2 runnable tasks 100 So(len(tkDurs), ShouldEqual, 2) 101 102 // ensure the expected durations are as expected 103 So(tkDurs[displayNames[0]], ShouldEqual, timeTaken[0]) 104 So(tkDurs[displayNames[1]], ShouldEqual, timeTaken[4]) 105 }, 106 ) 107 108 Convey("the expected task duration for tasks with same display "+ 109 "name same project but different buildvariant should be "+ 110 "distinct", 111 func() { 112 runnableTasks = []task.Task{ 113 // task 1/4 are from the same project but from different 114 // buildvariants and have the same display names - 115 // expected duration should not be averaged between 116 // these two 117 { 118 Id: taskIds[1], 119 DisplayName: displayNames[1], 120 BuildVariant: buildVariants[3], 121 TimeTaken: timeTaken[2], 122 Project: projects[2], 123 Status: evergreen.TaskSucceeded, 124 StartTime: time.Now().Add(-timeTaken[2]), 125 FinishTime: time.Now(), 126 }, 127 { 128 Id: taskIds[4], 129 DisplayName: displayNames[1], 130 BuildVariant: buildVariants[4], 131 TimeTaken: timeTaken[0], 132 Project: projects[2], 133 Status: evergreen.TaskSucceeded, 134 StartTime: time.Now().Add(-timeTaken[0]), 135 FinishTime: time.Now(), 136 }, 137 } 138 139 So(db.Clear(task.Collection), ShouldBeNil) 140 141 // insert all the test tasks 142 for _, testTask := range runnableTasks { 143 testutil.HandleTestingErr(testTask.Insert(), t, "failed to "+ 144 " insert task") 145 } 146 147 taskDurations, err := taskDurationEstimator. 148 GetExpectedDurations(runnableTasks) 149 150 So(err, ShouldEqual, nil) 151 152 projectDurations := taskDurations.TaskDurationByProject 153 // we only have 1 project for all runnable tasks 154 So(len(projectDurations), ShouldEqual, 1) 155 156 bvDurs := projectDurations[projects[2]]. 157 TaskDurationByBuildVariant 158 // we have 2 buildvariants for project 2 159 So(len(bvDurs), ShouldEqual, 2) 160 161 tkDurs := bvDurs[buildVariants[3]].TaskDurationByDisplayName 162 // we have 1 runnable task for buildvariant 3 163 So(len(tkDurs), ShouldEqual, 1) 164 165 // ensure the expected duration is as expected 166 So(tkDurs[displayNames[1]], ShouldEqual, timeTaken[2]) 167 168 tkDurs = bvDurs[buildVariants[4]].TaskDurationByDisplayName 169 // we have 1 runnable task for buildvariant 4 170 So(len(tkDurs), ShouldEqual, 1) 171 172 // ensure the expected duration is as expected 173 So(tkDurs[displayNames[1]], ShouldEqual, timeTaken[0]) 174 }, 175 ) 176 177 Convey("the expected task duration for tasks with same display "+ 178 "name same buildvariant and from same project should be "+ 179 "averaged", 180 func() { 181 runnableTasks = []task.Task{ 182 // task 2/3 are from the same buildvariant, have the 183 // same display name amd are from same projects - 184 // expected duration should be average across those 185 // projects 186 { 187 Id: taskIds[3], 188 DisplayName: displayNames[3], 189 BuildVariant: buildVariants[3], 190 TimeTaken: timeTaken[3], 191 Project: projects[3], 192 Status: evergreen.TaskSucceeded, 193 StartTime: time.Now().Add(-timeTaken[3]), 194 FinishTime: time.Now(), 195 }, 196 { 197 Id: taskIds[4], 198 DisplayName: displayNames[3], 199 BuildVariant: buildVariants[3], 200 TimeTaken: timeTaken[4], 201 Project: projects[3], 202 Status: evergreen.TaskSucceeded, 203 StartTime: time.Now().Add(-timeTaken[4]), 204 FinishTime: time.Now(), 205 }, 206 } 207 208 So(db.Clear(task.Collection), ShouldBeNil) 209 210 // insert all the test tasks 211 for _, testTask := range runnableTasks { 212 testutil.HandleTestingErr(testTask.Insert(), t, "failed to "+ 213 "insert task") 214 } 215 216 taskDurations, err := taskDurationEstimator. 217 GetExpectedDurations(runnableTasks) 218 219 So(err, ShouldEqual, nil) 220 221 projectDurations := taskDurations.TaskDurationByProject 222 // we have 1 project for all runnable tasks 223 So(len(projectDurations), ShouldEqual, 1) 224 225 // check the results for project 4 226 bvDurs := projectDurations[projects[3]]. 227 TaskDurationByBuildVariant 228 // we have 1 buildvariant for project 3 229 So(len(bvDurs), ShouldEqual, 1) 230 231 tkDurs := bvDurs[buildVariants[3]].TaskDurationByDisplayName 232 // we have 1 task duration for display name tasks 233 // for buildvariant 3 234 So(len(tkDurs), ShouldEqual, 1) 235 236 // ensure the expected duration is as expected 237 expDur := (timeTaken[3] + timeTaken[4]) / 2 238 So(tkDurs[displayNames[3]], ShouldEqual, expDur) 239 }, 240 ) 241 242 Convey("the expected task duration for tasks with same display "+ 243 "name same buildvariant but from different project should be "+ 244 "distinct", 245 func() { 246 runnableTasks = []task.Task{ 247 // task 4/5 are from the same buildvariant, have the 248 // same display name but are from different projects - 249 // expected duration should be distinct across those 250 // projects 251 { 252 Id: taskIds[4], 253 DisplayName: displayNames[1], 254 BuildVariant: buildVariants[1], 255 TimeTaken: timeTaken[4], 256 Project: projects[4], 257 Status: evergreen.TaskSucceeded, 258 StartTime: time.Now().Add(-timeTaken[4]), 259 FinishTime: time.Now(), 260 }, 261 { 262 Id: taskIds[5], 263 DisplayName: displayNames[1], 264 BuildVariant: buildVariants[1], 265 TimeTaken: timeTaken[5], 266 Project: projects[5], 267 Status: evergreen.TaskSucceeded, 268 StartTime: time.Now().Add(-timeTaken[5]), 269 FinishTime: time.Now(), 270 }, 271 } 272 273 So(db.Clear(task.Collection), ShouldBeNil) 274 275 // insert all the test tasks 276 for _, testTask := range runnableTasks { 277 testutil.HandleTestingErr(testTask.Insert(), t, "failed to "+ 278 "insert task") 279 } 280 281 taskDurations, err := taskDurationEstimator. 282 GetExpectedDurations(runnableTasks) 283 284 So(err, ShouldEqual, nil) 285 286 projectDurations := taskDurations.TaskDurationByProject 287 // we have 2 projects for all runnable tasks 288 So(len(projectDurations), ShouldEqual, 2) 289 290 // check the results for project 4 291 bvDurs := projectDurations[projects[4]]. 292 TaskDurationByBuildVariant 293 // we have 1 buildvariant for project 4 294 So(len(bvDurs), ShouldEqual, 1) 295 296 tkDurs := bvDurs[buildVariants[1]].TaskDurationByDisplayName 297 // we have 1 runnable task for buildvariant 1 298 So(len(tkDurs), ShouldEqual, 1) 299 300 // ensure the expected duration is as expected 301 So(tkDurs[displayNames[1]], ShouldEqual, timeTaken[4]) 302 303 // check the results for project 5 304 bvDurs = projectDurations[projects[5]]. 305 TaskDurationByBuildVariant 306 // we have 1 buildvariant for project 4 307 So(len(bvDurs), ShouldEqual, 1) 308 309 tkDurs = bvDurs[buildVariants[1]].TaskDurationByDisplayName 310 // we have 1 runnable task for buildvariant 1 311 So(len(tkDurs), ShouldEqual, 1) 312 313 // ensure the expected duration is as expected 314 So(tkDurs[displayNames[1]], ShouldEqual, timeTaken[5]) 315 }, 316 ) 317 318 Convey("the expected task duration for tasks should only pick up "+ 319 "tasks that are completed", 320 func() { 321 runnableTasks = []task.Task{ 322 // task 4/5 are from the same buildvariant, have the 323 // same display name and are from the same project - 324 // however task 4 is not yet dispatched so shouldn't 325 // count toward the expected duration 326 { 327 Id: taskIds[4], 328 DisplayName: displayNames[1], 329 BuildVariant: buildVariants[1], 330 TimeTaken: timeTaken[4], 331 Project: projects[4], 332 Status: evergreen.TaskUndispatched, 333 StartTime: time.Now().Add(-timeTaken[4]), 334 FinishTime: time.Now(), 335 }, 336 { 337 Id: taskIds[5], 338 DisplayName: displayNames[1], 339 BuildVariant: buildVariants[1], 340 TimeTaken: timeTaken[5], 341 Project: projects[4], 342 Status: evergreen.TaskSucceeded, 343 StartTime: time.Now().Add(-timeTaken[5]), 344 FinishTime: time.Now(), 345 }, 346 } 347 348 So(db.Clear(task.Collection), ShouldBeNil) 349 350 // insert all the test tasks 351 for _, testTask := range runnableTasks { 352 testutil.HandleTestingErr(testTask.Insert(), t, "failed to "+ 353 "insert task") 354 } 355 356 taskDurations, err := taskDurationEstimator. 357 GetExpectedDurations(runnableTasks) 358 359 So(err, ShouldEqual, nil) 360 361 projectDurations := taskDurations.TaskDurationByProject 362 // we have 1 project for all runnable tasks 363 So(len(projectDurations), ShouldEqual, 1) 364 365 // check the results for project 4 366 bvDurs := projectDurations[projects[4]]. 367 TaskDurationByBuildVariant 368 369 // we have 1 buildvariant for project 4 370 So(len(bvDurs), ShouldEqual, 1) 371 372 tkDurs := bvDurs[buildVariants[1]].TaskDurationByDisplayName 373 // we have 1 runnable task for buildvariant 1 374 // since task 4 is undispatched 375 So(len(tkDurs), ShouldEqual, 1) 376 377 // ensure the expected duration is that of task 5 378 So(tkDurs[displayNames[1]], ShouldEqual, timeTaken[5]) 379 }, 380 ) 381 382 Convey("the expected task duration for tasks should only pick up "+ 383 "tasks that are completed within the specified window", 384 func() { 385 runnableTasks = []task.Task{ 386 // task 4/5 are from the same buildvariant, have the 387 // same display name and are from the same project - 388 // however task 4 did not finish within the specified 389 // window and should thus, not count toward the expected 390 // duration 391 { 392 Id: taskIds[4], 393 DisplayName: displayNames[1], 394 BuildVariant: buildVariants[1], 395 TimeTaken: timeTaken[4], 396 Project: projects[4], 397 Status: evergreen.TaskSucceeded, 398 StartTime: time.Now().Add(-timeTaken[4]), 399 FinishTime: time.Now(). 400 Add(-model.TaskCompletionEstimateWindow). 401 Add(-model.TaskCompletionEstimateWindow), 402 }, 403 { 404 Id: taskIds[5], 405 DisplayName: displayNames[1], 406 BuildVariant: buildVariants[1], 407 TimeTaken: timeTaken[3], 408 Project: projects[4], 409 Status: evergreen.TaskSucceeded, 410 StartTime: time.Now().Add(-timeTaken[3]), 411 FinishTime: time.Now(), 412 }, 413 } 414 415 So(db.Clear(task.Collection), ShouldBeNil) 416 417 // insert all the test tasks 418 for _, testTask := range runnableTasks { 419 testutil.HandleTestingErr(testTask.Insert(), t, "failed to "+ 420 "insert task") 421 } 422 423 taskDurations, err := taskDurationEstimator. 424 GetExpectedDurations(runnableTasks) 425 426 So(err, ShouldEqual, nil) 427 428 projectDurations := taskDurations.TaskDurationByProject 429 // we have 1 project for all runnable tasks 430 So(len(projectDurations), ShouldEqual, 1) 431 432 // check the results for project 4 433 bvDurs := projectDurations[projects[4]]. 434 TaskDurationByBuildVariant 435 436 // we have 1 buildvariant for project 4 437 So(len(bvDurs), ShouldEqual, 1) 438 439 tkDurs := bvDurs[buildVariants[1]].TaskDurationByDisplayName 440 // we have 1 task for buildvariant 1 441 // since task 4 is did not finish within the window 442 So(len(tkDurs), ShouldEqual, 1) 443 444 // ensure the expected duration is that of task 5 445 So(tkDurs[displayNames[1]], ShouldEqual, timeTaken[3]) 446 }, 447 ) 448 449 Convey("the expected task duration for tasks where several "+ 450 "projects that contain a mix of completed/uncompleted, for \n"+ 451 "several buildvariants/tasks should ignore uncompleted tasks "+ 452 "and average tasks with the same display name", 453 func() { 454 runnableTasks = []task.Task{ 455 // task 0/7 are from the same project and buildvariant 456 // and have different display names - expected duration 457 // should thus be averaged between these two - even 458 // though task 0 succeeded and 7 failed 459 { 460 Id: taskIds[0], 461 DisplayName: displayNames[0], 462 BuildVariant: buildVariants[0], 463 TimeTaken: timeTaken[0], 464 Project: projects[0], 465 Status: evergreen.TaskSucceeded, 466 StartTime: time.Now().Add(-timeTaken[0]), 467 FinishTime: time.Now(), 468 }, 469 { 470 Id: taskIds[7], 471 DisplayName: displayNames[0], 472 BuildVariant: buildVariants[0], 473 TimeTaken: timeTaken[4], 474 Project: projects[0], 475 Status: evergreen.TaskFailed, 476 StartTime: time.Now().Add(-timeTaken[4]), 477 FinishTime: time.Now(), 478 }, 479 // task 1 is the only runnable task with this project, 480 // buildvariant & display name 481 // should be exactly equal to its time taken 482 { 483 Id: taskIds[1], 484 DisplayName: displayNames[1], 485 BuildVariant: buildVariants[1], 486 TimeTaken: timeTaken[1], 487 Project: projects[1], 488 Status: evergreen.TaskSucceeded, 489 StartTime: time.Now().Add(-timeTaken[1]), 490 FinishTime: time.Now(), 491 }, 492 // task 2 is the only runnable task with this project, 493 // buildvariant & display name - so the expected 494 // duration should be exactly equal to its time taken 495 { 496 Id: taskIds[2], 497 DisplayName: displayNames[2], 498 BuildVariant: buildVariants[2], 499 TimeTaken: timeTaken[2], 500 Project: projects[2], 501 Status: evergreen.TaskFailed, 502 StartTime: time.Now().Add(-timeTaken[2]), 503 FinishTime: time.Now(), 504 }, 505 // task 3/6 are from the same project and buildvariant 506 // but have different display names - expected duration 507 // should thus not be averaged between these two 508 { 509 Id: taskIds[3], 510 DisplayName: displayNames[3], 511 BuildVariant: buildVariants[3], 512 TimeTaken: timeTaken[3], 513 Project: projects[3], 514 Status: evergreen.TaskSucceeded, 515 StartTime: time.Now().Add(-timeTaken[3]), 516 FinishTime: time.Now(), 517 }, 518 { 519 Id: taskIds[6], 520 DisplayName: displayNames[4], 521 BuildVariant: buildVariants[3], 522 TimeTaken: timeTaken[5], 523 Project: projects[3], 524 Status: evergreen.TaskSucceeded, 525 StartTime: time.Now().Add(-timeTaken[5]), 526 FinishTime: time.Now(), 527 }, 528 // task 4/5 are from the same project and buildvariant 529 // and have the same display name - expected duration 530 // should thus be averaged between these two 531 { 532 Id: taskIds[4], 533 DisplayName: displayNames[4], 534 BuildVariant: buildVariants[4], 535 TimeTaken: timeTaken[4], 536 Project: projects[4], 537 Status: evergreen.TaskFailed, 538 StartTime: time.Now().Add(-timeTaken[4]), 539 FinishTime: time.Now(), 540 }, 541 { 542 Id: taskIds[5], 543 DisplayName: displayNames[4], 544 BuildVariant: buildVariants[4], 545 TimeTaken: timeTaken[5], 546 Project: projects[4], 547 Status: evergreen.TaskFailed, 548 StartTime: time.Now().Add(-timeTaken[5]), 549 FinishTime: time.Now(), 550 }, 551 // task 9 is undispatched so shouldn't 552 // count toward the expected duration for 553 // project/buildvariant/display name 4 554 { 555 Id: taskIds[8], 556 DisplayName: displayNames[4], 557 BuildVariant: buildVariants[4], 558 TimeTaken: timeTaken[5], 559 Project: projects[4], 560 Status: evergreen.TaskUndispatched, 561 StartTime: time.Now().Add(-timeTaken[5]), 562 FinishTime: time.Now(), 563 }, 564 } 565 566 So(db.Clear(task.Collection), ShouldBeNil) 567 568 // insert all the test tasks 569 for _, testTask := range runnableTasks { 570 testutil.HandleTestingErr(testTask.Insert(), t, "failed to "+ 571 "insert task") 572 } 573 574 taskDurations, err := taskDurationEstimator. 575 GetExpectedDurations(runnableTasks) 576 testutil.HandleTestingErr(err, t, "failed to get task "+ 577 "durations") 578 projectDurations := taskDurations. 579 TaskDurationByProject 580 581 // we have five projects for all runnable tasks 582 So(len(projectDurations), ShouldEqual, 5) 583 taskDuration := projectDurations[projects[0]]. 584 TaskDurationByBuildVariant[buildVariants[0]]. 585 TaskDurationByDisplayName[displayNames[0]] 586 So(taskDuration, ShouldEqual, 587 (timeTaken[0]+timeTaken[4])/2) 588 taskDuration = projectDurations[projects[1]]. 589 TaskDurationByBuildVariant[buildVariants[1]]. 590 TaskDurationByDisplayName[displayNames[1]] 591 So(taskDuration, ShouldEqual, timeTaken[1]) 592 taskDuration = projectDurations[projects[2]]. 593 TaskDurationByBuildVariant[buildVariants[2]]. 594 TaskDurationByDisplayName[displayNames[2]] 595 So(taskDuration, ShouldEqual, timeTaken[2]) 596 taskDuration = projectDurations[projects[3]]. 597 TaskDurationByBuildVariant[buildVariants[3]]. 598 TaskDurationByDisplayName[displayNames[3]] 599 So(taskDuration, ShouldEqual, timeTaken[3]) 600 taskDuration = projectDurations[projects[4]]. 601 TaskDurationByBuildVariant[buildVariants[4]]. 602 TaskDurationByDisplayName[displayNames[4]] 603 // should be avearge of tasks 4/5 604 So(taskDuration, ShouldEqual, 605 (timeTaken[4]+timeTaken[5])/2) 606 }, 607 ) 608 }, 609 ) 610 }