github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/scheduler/task_prioritizer_test.go (about) 1 package scheduler 2 3 import ( 4 "testing" 5 6 "github.com/evergreen-ci/evergreen" 7 "github.com/evergreen-ci/evergreen/db" 8 "github.com/evergreen-ci/evergreen/model/task" 9 "github.com/evergreen-ci/evergreen/testutil" 10 "github.com/mongodb/grip" 11 . "github.com/smartystreets/goconvey/convey" 12 ) 13 14 var taskComparatorTestConf = testutil.TestConfig() 15 16 func init() { 17 db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(taskComparatorTestConf)) 18 grip.CatchError(grip.SetSender(testutil.SetupTestSender(schedulerTestConf.Scheduler.LogFile))) 19 } 20 21 func TestCmpBasedTaskComparator(t *testing.T) { 22 23 var taskComparator *CmpBasedTaskComparator 24 var taskIds []string 25 var tasks []task.Task 26 27 Convey("With a CmpBasedTaskComparator", t, func() { 28 29 taskComparator = NewCmpBasedTaskComparator() 30 31 taskIds = []string{"t1", "t2"} 32 33 tasks = []task.Task{ 34 {Id: taskIds[0]}, 35 {Id: taskIds[1]}, 36 } 37 38 alwaysEqual := func(t1, t2 task.Task, p *CmpBasedTaskComparator) ( 39 int, error) { 40 return 0, nil 41 } 42 43 alwaysMoreImportant := func(t1, t2 task.Task, 44 p *CmpBasedTaskComparator) (int, error) { 45 return 1, nil 46 } 47 48 alwaysLessImportant := func(t1, t2 task.Task, 49 p *CmpBasedTaskComparator) (int, error) { 50 return -1, nil 51 } 52 53 idComparator := func(t1, t2 task.Task, p *CmpBasedTaskComparator) ( 54 int, error) { 55 if t1.Id > t2.Id { 56 return 1, nil 57 } 58 if t1.Id < t2.Id { 59 return -1, nil 60 } 61 return 0, nil 62 } 63 64 Convey("when using the comparator functions to compare two tasks", 65 func() { 66 67 Convey("a nil comparator function slice should return the"+ 68 " default (no error)", func() { 69 taskComparator.comparators = nil 70 71 moreImportant, err := taskComparator.taskMoreImportantThan( 72 tasks[0], tasks[1]) 73 So(err, ShouldBeNil) 74 So(moreImportant, ShouldBeFalse) 75 76 moreImportant, err = taskComparator.taskMoreImportantThan( 77 tasks[1], tasks[0]) 78 So(err, ShouldBeNil) 79 So(moreImportant, ShouldBeFalse) 80 }) 81 82 Convey("if there are no comparator functions, the default is"+ 83 " always returned", func() { 84 taskComparator.comparators = []taskPriorityCmp{} 85 86 moreImportant, err := taskComparator.taskMoreImportantThan( 87 tasks[0], tasks[1]) 88 So(err, ShouldBeNil) 89 So(moreImportant, ShouldBeFalse) 90 91 moreImportant, err = taskComparator.taskMoreImportantThan( 92 tasks[1], tasks[0]) 93 So(err, ShouldBeNil) 94 So(moreImportant, ShouldBeFalse) 95 }) 96 97 Convey("if there is only one comparator function, that"+ 98 " function is definitive", func() { 99 taskComparator.comparators = []taskPriorityCmp{ 100 alwaysMoreImportant, 101 } 102 103 moreImportant, err := taskComparator.taskMoreImportantThan( 104 tasks[0], tasks[1]) 105 So(err, ShouldBeNil) 106 So(moreImportant, ShouldBeTrue) 107 108 moreImportant, err = taskComparator.taskMoreImportantThan( 109 tasks[1], tasks[0]) 110 So(err, ShouldBeNil) 111 So(moreImportant, ShouldBeTrue) 112 }) 113 114 Convey("if there are multiple comparator functions, the first"+ 115 " definitive one wins", func() { 116 taskComparator.comparators = []taskPriorityCmp{ 117 alwaysEqual, 118 idComparator, 119 alwaysMoreImportant, 120 alwaysLessImportant, 121 } 122 123 moreImportant, err := taskComparator.taskMoreImportantThan( 124 tasks[0], tasks[1]) 125 So(err, ShouldBeNil) 126 So(moreImportant, ShouldBeFalse) 127 128 moreImportant, err = taskComparator.taskMoreImportantThan( 129 tasks[1], tasks[0]) 130 So(err, ShouldBeNil) 131 So(moreImportant, ShouldBeTrue) 132 133 // for the next two, the ids are the same so the id 134 // comparator func isn't definitive 135 136 moreImportant, err = taskComparator.taskMoreImportantThan( 137 tasks[0], tasks[0]) 138 So(err, ShouldBeNil) 139 So(moreImportant, ShouldBeTrue) 140 141 moreImportant, err = taskComparator.taskMoreImportantThan( 142 tasks[1], tasks[1]) 143 So(err, ShouldBeNil) 144 So(moreImportant, ShouldBeTrue) 145 146 }) 147 148 }) 149 150 }) 151 152 Convey("Splitting tasks by requester should separate tasks based on the Requester field", t, func() { 153 taskComparator = NewCmpBasedTaskComparator() 154 taskIds = []string{"t1", "t2", "t3", "t4", "t5"} 155 tasks = []task.Task{ 156 {Id: taskIds[0], Requester: evergreen.RepotrackerVersionRequester}, 157 {Id: taskIds[1], Requester: evergreen.PatchVersionRequester}, 158 {Id: taskIds[2], Requester: evergreen.PatchVersionRequester}, 159 {Id: taskIds[3], Requester: evergreen.RepotrackerVersionRequester}, 160 {Id: taskIds[4], Requester: evergreen.RepotrackerVersionRequester}, 161 } 162 163 tq := taskComparator.splitTasksByRequester(tasks) 164 So(len(tq.RepotrackerTasks), ShouldEqual, 3) 165 repoTrackerTasks := tq.RepotrackerTasks 166 So(repoTrackerTasks[0].Id, ShouldEqual, taskIds[0]) 167 So(repoTrackerTasks[1].Id, ShouldEqual, taskIds[3]) 168 So(repoTrackerTasks[2].Id, ShouldEqual, taskIds[4]) 169 So(len(tq.PatchTasks), ShouldEqual, 2) 170 patchTasks := tq.PatchTasks 171 So(patchTasks[0].Id, ShouldEqual, taskIds[1]) 172 So(patchTasks[1].Id, ShouldEqual, taskIds[2]) 173 174 }) 175 Convey("Splitting tasks with priority greater than 100 should always put those tasks in the high priority queue", t, func() { 176 taskComparator = NewCmpBasedTaskComparator() 177 taskIds = []string{"t1", "t2", "t3", "t4", "t5"} 178 tasks = []task.Task{ 179 {Id: taskIds[0], Requester: evergreen.RepotrackerVersionRequester, Priority: 101}, 180 {Id: taskIds[1], Requester: evergreen.PatchVersionRequester, Priority: 101}, 181 {Id: taskIds[2], Requester: evergreen.PatchVersionRequester}, 182 {Id: taskIds[3], Requester: evergreen.RepotrackerVersionRequester}, 183 {Id: taskIds[4], Requester: evergreen.RepotrackerVersionRequester}, 184 } 185 tq := taskComparator.splitTasksByRequester(tasks) 186 So(len(tq.RepotrackerTasks), ShouldEqual, 2) 187 repoTrackerTasks := tq.RepotrackerTasks 188 So(repoTrackerTasks[0].Id, ShouldEqual, taskIds[3]) 189 So(repoTrackerTasks[1].Id, ShouldEqual, taskIds[4]) 190 So(len(tq.PatchTasks), ShouldEqual, 1) 191 patchTasks := tq.PatchTasks 192 So(patchTasks[0].Id, ShouldEqual, taskIds[2]) 193 So(len(tq.HighPriorityTasks), ShouldEqual, 2) 194 So(tq.HighPriorityTasks[0].Id, ShouldEqual, taskIds[0]) 195 So(tq.HighPriorityTasks[1].Id, ShouldEqual, taskIds[1]) 196 }) 197 198 Convey("Merging tasks should merge the lists of repotracker and patch tasks, taking the toggle into account", t, func() { 199 200 taskIds = []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7"} 201 tasks = []task.Task{ 202 {Id: taskIds[0]}, 203 {Id: taskIds[1]}, 204 {Id: taskIds[2]}, 205 {Id: taskIds[3]}, 206 {Id: taskIds[4]}, 207 {Id: taskIds[5]}, 208 {Id: taskIds[6]}, 209 } 210 211 Convey("With no patch tasks, the list of repotracker tasks should be returned", func() { 212 tasks[0].Requester = evergreen.RepotrackerVersionRequester 213 tasks[1].Requester = evergreen.RepotrackerVersionRequester 214 tasks[2].Requester = evergreen.RepotrackerVersionRequester 215 repoTrackerTasks := []task.Task{tasks[0], tasks[1], tasks[2]} 216 patchTasks := []task.Task{} 217 218 taskQueues := CmpBasedTaskQueues{ 219 RepotrackerTasks: repoTrackerTasks, 220 PatchTasks: patchTasks, 221 } 222 mergedTasks := taskComparator.mergeTasks(taskComparatorTestConf, &taskQueues) 223 So(len(mergedTasks), ShouldEqual, 3) 224 So(mergedTasks[0].Id, ShouldEqual, taskIds[0]) 225 So(mergedTasks[1].Id, ShouldEqual, taskIds[1]) 226 So(mergedTasks[2].Id, ShouldEqual, taskIds[2]) 227 228 Convey("high priority tasks are inserted into the beginning of the queue", func() { 229 tasks[3].Requester = evergreen.RepotrackerVersionRequester 230 tasks[3].Priority = 101 231 tasks[4].Requester = evergreen.RepotrackerVersionRequester 232 tasks[4].Priority = 102 233 234 priorityTasks := []task.Task{tasks[3], tasks[4]} 235 tq := CmpBasedTaskQueues{ 236 HighPriorityTasks: priorityTasks, 237 RepotrackerTasks: repoTrackerTasks, 238 } 239 mergedTasks := taskComparator.mergeTasks(taskComparatorTestConf, &tq) 240 So(len(mergedTasks), ShouldEqual, 5) 241 So(mergedTasks[0].Id, ShouldEqual, taskIds[3]) 242 So(mergedTasks[1].Id, ShouldEqual, taskIds[4]) 243 So(mergedTasks[2].Id, ShouldEqual, taskIds[0]) 244 So(mergedTasks[3].Id, ShouldEqual, taskIds[1]) 245 So(mergedTasks[4].Id, ShouldEqual, taskIds[2]) 246 247 }) 248 }) 249 250 Convey("With no repotracker tasks, the list of patch tasks should be returned", func() { 251 tasks[0].Requester = evergreen.PatchVersionRequester 252 tasks[1].Requester = evergreen.PatchVersionRequester 253 tasks[2].Requester = evergreen.PatchVersionRequester 254 repoTrackerTasks := []task.Task{} 255 patchTasks := []task.Task{tasks[0], tasks[1], tasks[2]} 256 257 taskQueues := CmpBasedTaskQueues{ 258 RepotrackerTasks: repoTrackerTasks, 259 PatchTasks: patchTasks, 260 } 261 mergedTasks := taskComparator.mergeTasks(taskComparatorTestConf, &taskQueues) 262 So(len(mergedTasks), ShouldEqual, 3) 263 So(mergedTasks[0].Id, ShouldEqual, taskIds[0]) 264 So(mergedTasks[1].Id, ShouldEqual, taskIds[1]) 265 So(mergedTasks[2].Id, ShouldEqual, taskIds[2]) 266 }) 267 268 Convey("With both repotracker tasks and patch tasks, the toggle should determine how often a patch task is interleaved", func() { 269 270 Convey("A MergeToggle of 2 should interleave tasks evenly", func() { 271 272 taskComparatorTestConf.Scheduler.MergeToggle = 2 273 repoTrackerTasks := []task.Task{tasks[0], tasks[1], tasks[2]} 274 patchTasks := []task.Task{tasks[3], tasks[4], tasks[5]} 275 276 tqs := CmpBasedTaskQueues{ 277 RepotrackerTasks: repoTrackerTasks, 278 PatchTasks: patchTasks, 279 } 280 mergedTasks := taskComparator.mergeTasks(taskComparatorTestConf, &tqs) 281 So(len(mergedTasks), ShouldEqual, 6) 282 So(mergedTasks[0].Id, ShouldEqual, taskIds[3]) 283 So(mergedTasks[1].Id, ShouldEqual, taskIds[0]) 284 So(mergedTasks[2].Id, ShouldEqual, taskIds[4]) 285 So(mergedTasks[3].Id, ShouldEqual, taskIds[1]) 286 So(mergedTasks[4].Id, ShouldEqual, taskIds[5]) 287 So(mergedTasks[5].Id, ShouldEqual, taskIds[2]) 288 289 }) 290 291 Convey("A MergeToggle of 3 should interleave a patch task every third task", func() { 292 293 taskComparatorTestConf.Scheduler.MergeToggle = 3 294 repoTrackerTasks := []task.Task{tasks[0], tasks[1], tasks[2], tasks[3]} 295 patchTasks := []task.Task{tasks[4], tasks[5]} 296 297 tqs := CmpBasedTaskQueues{ 298 RepotrackerTasks: repoTrackerTasks, 299 PatchTasks: patchTasks, 300 } 301 mergedTasks := taskComparator.mergeTasks(taskComparatorTestConf, &tqs) 302 So(len(mergedTasks), ShouldEqual, 6) 303 So(mergedTasks[0].Id, ShouldEqual, taskIds[4]) 304 So(mergedTasks[1].Id, ShouldEqual, taskIds[5]) 305 So(mergedTasks[2].Id, ShouldEqual, taskIds[0]) 306 So(mergedTasks[3].Id, ShouldEqual, taskIds[1]) 307 So(mergedTasks[4].Id, ShouldEqual, taskIds[2]) 308 So(mergedTasks[5].Id, ShouldEqual, taskIds[3]) 309 310 }) 311 312 }) 313 314 Convey("With a lot of patch tasks, the extras should be added on the end", func() { 315 316 taskComparatorTestConf.Scheduler.MergeToggle = 2 317 repoTrackerTasks := []task.Task{tasks[0], tasks[1]} 318 patchTasks := []task.Task{tasks[2], tasks[3], tasks[4], tasks[5]} 319 tqs := CmpBasedTaskQueues{ 320 RepotrackerTasks: repoTrackerTasks, 321 PatchTasks: patchTasks, 322 } 323 324 mergedTasks := taskComparator.mergeTasks(taskComparatorTestConf, &tqs) 325 So(len(mergedTasks), ShouldEqual, 6) 326 So(mergedTasks[0].Id, ShouldEqual, taskIds[2]) 327 So(mergedTasks[1].Id, ShouldEqual, taskIds[0]) 328 So(mergedTasks[2].Id, ShouldEqual, taskIds[3]) 329 So(mergedTasks[3].Id, ShouldEqual, taskIds[1]) 330 So(mergedTasks[4].Id, ShouldEqual, taskIds[4]) 331 So(mergedTasks[5].Id, ShouldEqual, taskIds[5]) 332 }) 333 334 Convey("With a lot of repotracker tasks, the extras should be added on the end", func() { 335 336 taskComparatorTestConf.Scheduler.MergeToggle = 2 337 repoTrackerTasks := []task.Task{tasks[0], tasks[1], tasks[2], tasks[3], tasks[4]} 338 patchTasks := []task.Task{tasks[5]} 339 340 tqs := CmpBasedTaskQueues{ 341 RepotrackerTasks: repoTrackerTasks, 342 PatchTasks: patchTasks, 343 } 344 345 mergedTasks := taskComparator.mergeTasks(taskComparatorTestConf, &tqs) 346 So(len(mergedTasks), ShouldEqual, 6) 347 So(mergedTasks[0].Id, ShouldEqual, taskIds[5]) 348 So(mergedTasks[1].Id, ShouldEqual, taskIds[0]) 349 So(mergedTasks[2].Id, ShouldEqual, taskIds[1]) 350 So(mergedTasks[3].Id, ShouldEqual, taskIds[2]) 351 So(mergedTasks[4].Id, ShouldEqual, taskIds[3]) 352 So(mergedTasks[5].Id, ShouldEqual, taskIds[4]) 353 }) 354 355 }) 356 357 }