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  }