github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/model/scheduler_stats_test.go (about) 1 package model 2 3 import ( 4 "math" 5 "testing" 6 "time" 7 8 "github.com/evergreen-ci/evergreen" 9 "github.com/evergreen-ci/evergreen/db" 10 "github.com/evergreen-ci/evergreen/model/distro" 11 "github.com/evergreen-ci/evergreen/model/host" 12 "github.com/evergreen-ci/evergreen/model/task" 13 "github.com/evergreen-ci/evergreen/testutil" 14 "github.com/evergreen-ci/evergreen/util" 15 . "github.com/smartystreets/goconvey/convey" 16 ) 17 18 var projectTestConfig = testutil.TestConfig() 19 20 func init() { 21 db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(projectTestConfig)) 22 } 23 24 func TestBucketResource(t *testing.T) { 25 Convey("With a start time and a bucket size of 10 and 10 buckets", t, func() { 26 frameStart := time.Now() 27 // 10 buckets * 10 bucket size = 100 28 frameEnd := frameStart.Add(time.Duration(100)) 29 bucketSize := time.Duration(10) 30 Convey("when resource start time is equal to end time should error", func() { 31 buckets := make([]Bucket, 10) 32 resourceStart := frameEnd 33 resourceEnd := frameEnd.Add(time.Duration(10)) 34 resource := ResourceInfo{ 35 Start: resourceStart, 36 End: resourceEnd, 37 } 38 _, err := bucketResource(resource, frameStart, frameEnd, bucketSize, buckets) 39 So(err, ShouldNotBeNil) 40 }) 41 Convey("when resource start time is greater than end time should error", func() { 42 buckets := make([]Bucket, 10) 43 resourceStart := frameEnd.Add(time.Duration(10)) 44 resourceEnd := frameEnd.Add(time.Duration(20)) 45 resource := ResourceInfo{ 46 Start: resourceStart, 47 End: resourceEnd, 48 } 49 _, err := bucketResource(resource, frameStart, frameEnd, bucketSize, buckets) 50 So(err, ShouldNotBeNil) 51 }) 52 Convey("when resource end time is equal to start time should error", func() { 53 buckets := make([]Bucket, 10) 54 resourceStart := frameStart.Add(time.Duration(-10)) 55 resourceEnd := frameStart 56 resource := ResourceInfo{ 57 Start: resourceStart, 58 End: resourceEnd, 59 } 60 _, err := bucketResource(resource, frameStart, frameEnd, bucketSize, buckets) 61 So(err, ShouldNotBeNil) 62 }) 63 Convey("when resource end time is less than start time should error", func() { 64 buckets := make([]Bucket, 10) 65 resourceStart := frameStart.Add(time.Duration(-30)) 66 resourceEnd := frameStart.Add(time.Duration(-10)) 67 resource := ResourceInfo{ 68 Start: resourceStart, 69 End: resourceEnd, 70 } 71 _, err := bucketResource(resource, frameStart, frameEnd, bucketSize, buckets) 72 So(err, ShouldNotBeNil) 73 }) 74 Convey("when resource end time is less than resource start time, should error", func() { 75 buckets := make([]Bucket, 10) 76 resourceStart := frameStart.Add(time.Duration(10)) 77 resourceEnd := frameStart 78 resource := ResourceInfo{ 79 Start: resourceStart, 80 End: resourceEnd, 81 } 82 _, err := bucketResource(resource, frameStart, frameEnd, bucketSize, buckets) 83 So(err, ShouldNotBeNil) 84 }) 85 Convey("when resource start is zero, errors out", func() { 86 buckets := make([]Bucket, 10) 87 resourceStart := time.Time{} 88 resourceEnd := frameStart.Add(time.Duration(1)) 89 resource := ResourceInfo{ 90 Start: resourceStart, 91 End: resourceEnd, 92 } 93 _, err := bucketResource(resource, frameStart, frameEnd, bucketSize, buckets) 94 So(err, ShouldNotBeNil) 95 }) 96 Convey("when the resource start and end time are in the same bucket, only one bucket has the difference", func() { 97 buckets := make([]Bucket, 10) 98 resourceStart := frameStart.Add(time.Duration(1)) 99 resourceEnd := frameStart.Add(time.Duration(5)) 100 resource := ResourceInfo{ 101 Start: resourceStart, 102 End: resourceEnd, 103 } 104 _, err := bucketResource(resource, frameStart, frameEnd, bucketSize, buckets) 105 So(err, ShouldBeNil) 106 So(buckets[0].TotalTime, ShouldEqual, time.Duration(4)) 107 for i := 1; i < 10; i++ { 108 So(buckets[i].TotalTime, ShouldEqual, 0) 109 } 110 }) 111 Convey("when the resourceEnd is zero, there is no error", func() { 112 buckets := make([]Bucket, 10) 113 resourceStart := frameStart.Add(time.Duration(10)) 114 resourceEnd := util.ZeroTime 115 So(util.IsZeroTime(resourceEnd), ShouldBeTrue) 116 resource := ResourceInfo{ 117 Start: resourceStart, 118 End: resourceEnd, 119 } 120 _, err := bucketResource(resource, frameStart, frameEnd, bucketSize, buckets) 121 So(err, ShouldBeNil) 122 So(buckets[0].TotalTime, ShouldEqual, 0) 123 for i := 1; i < 10; i++ { 124 So(buckets[i].TotalTime, ShouldEqual, 10) 125 } 126 }) 127 128 }) 129 } 130 131 func TestCreateHostBuckets(t *testing.T) { 132 testutil.HandleTestingErr(db.ClearCollections(host.Collection), t, "couldnt reset host") 133 Convey("With a starting time and a minute bucket size and inserting dynamic hosts with different time frames", t, func() { 134 now := time.Now() 135 bucketSize := time.Duration(10) * time.Second 136 137 // -20 -> 20 138 beforeStartHost := host.Host{Id: "beforeStartHost", CreationTime: now.Add(time.Duration(-20) * time.Second), TerminationTime: now.Add(time.Duration(20) * time.Second), Provider: "ec2"} 139 So(beforeStartHost.Insert(), ShouldBeNil) 140 141 // 80 -> 120 142 afterEndHost := host.Host{Id: "afterEndHost", CreationTime: now.Add(time.Duration(80) * time.Second), TerminationTime: now.Add(time.Duration(120) * time.Second), Provider: "ec2"} 143 So(afterEndHost.Insert(), ShouldBeNil) 144 145 // 20 -> 40 146 h1 := host.Host{Id: "h1", CreationTime: now.Add(time.Duration(20) * time.Second), TerminationTime: now.Add(time.Duration(40) * time.Second), Provider: "ec2"} 147 So(h1.Insert(), ShouldBeNil) 148 149 // 10 -> 80 150 h2 := host.Host{Id: "h2", CreationTime: now.Add(time.Duration(10) * time.Second), TerminationTime: now.Add(time.Duration(80) * time.Second), Provider: "ec2"} 151 So(h2.Insert(), ShouldBeNil) 152 153 // 20 -> 154 h3 := host.Host{Id: "h3", CreationTime: now.Add(time.Duration(20) * time.Second), TerminationTime: util.ZeroTime, Provider: "ec2", Status: evergreen.HostRunning} 155 So(h3.Insert(), ShouldBeNil) 156 157 // 5 -> 7 158 sameBucket := host.Host{Id: "sameBucket", CreationTime: now.Add(time.Duration(5) * time.Second), TerminationTime: now.Add(time.Duration(7) * time.Second), Provider: "ec2"} 159 So(sameBucket.Insert(), ShouldBeNil) 160 161 // 5 -> 30 162 h4 := host.Host{Id: "h4", CreationTime: now.Add(time.Duration(5) * time.Second), TerminationTime: now.Add(time.Duration(30) * time.Second), Provider: "ec2"} 163 So(h4.Insert(), ShouldBeNil) 164 165 Convey("for three buckets of 10 seconds, should only retrieve pertinent host docs", func() { 166 167 endTime := now.Add(time.Duration(30) * time.Second) 168 hosts, err := host.Find(host.ByDynamicWithinTime(now, endTime)) 169 So(err, ShouldBeNil) 170 So(len(hosts), ShouldEqual, 6) 171 frameBounds := FrameBounds{ 172 StartTime: now, 173 EndTime: endTime, 174 BucketSize: bucketSize, 175 NumberBuckets: 3, 176 } 177 Convey("should create the correct buckets and bucket time accordingly", func() { 178 buckets, errors := CreateHostBuckets(hosts, frameBounds) 179 So(errors, ShouldBeEmpty) 180 So(len(buckets), ShouldEqual, 3) 181 So(int(buckets[0].TotalTime.Seconds()), ShouldEqual, 17) 182 So(int(buckets[1].TotalTime.Seconds()), ShouldEqual, 30) 183 So(int(math.Ceil(buckets[2].TotalTime.Seconds())), ShouldEqual, 40) 184 }) 185 }) 186 187 }) 188 } 189 190 func TestCreateTaskBuckets(t *testing.T) { 191 testutil.HandleTestingErr(db.ClearCollections(task.Collection), t, "couldnt reset host") 192 Convey("With a starting time and a minute bucket size and inserting tasks with different start and finish", t, func() { 193 now := time.Now() 194 bucketSize := time.Duration(10) * time.Second 195 196 // -20 -> 20 197 beforeStartHost := task.Task{Id: "beforeStartTask", StartTime: now.Add(time.Duration(-20) * time.Second), FinishTime: now.Add(time.Duration(20) * time.Second), Status: evergreen.TaskSucceeded} 198 So(beforeStartHost.Insert(), ShouldBeNil) 199 200 // 80 -> 120 201 afterEndHost := task.Task{Id: "afterStartTask", StartTime: now.Add(time.Duration(80) * time.Second), FinishTime: now.Add(time.Duration(120) * time.Second), Status: evergreen.TaskFailed} 202 So(afterEndHost.Insert(), ShouldBeNil) 203 204 // 20 -> 40: shouldnt be added 205 h1 := task.Task{Id: "h1", StartTime: now.Add(time.Duration(20) * time.Second), FinishTime: now.Add(time.Duration(40) * time.Second), Status: evergreen.TaskUndispatched} 206 So(h1.Insert(), ShouldBeNil) 207 208 // 10 -> 80 209 h2 := task.Task{Id: "h2", StartTime: now.Add(time.Duration(10) * time.Second), FinishTime: now.Add(time.Duration(80) * time.Second), Status: evergreen.TaskSucceeded} 210 So(h2.Insert(), ShouldBeNil) 211 212 // 20 -> shouldnt be added 213 neverEnding := task.Task{Id: "neverEnding", StartTime: now.Add(time.Duration(20) * time.Second), Status: evergreen.TaskSucceeded} 214 So(neverEnding.Insert(), ShouldBeNil) 215 216 // 5 -> 7 217 sameBucket := task.Task{Id: "sameBucket", StartTime: now.Add(time.Duration(5) * time.Second), FinishTime: now.Add(time.Duration(7) * time.Second), Status: evergreen.TaskFailed} 218 So(sameBucket.Insert(), ShouldBeNil) 219 220 // 5 -> 30 221 h4 := task.Task{Id: "h4", StartTime: now.Add(time.Duration(5) * time.Second), FinishTime: now.Add(time.Duration(30) * time.Second), Status: evergreen.TaskFailed} 222 So(h4.Insert(), ShouldBeNil) 223 224 endTime := now.Add(time.Duration(40) * time.Second) 225 frameBounds := FrameBounds{ 226 StartTime: now, 227 EndTime: endTime, 228 NumberBuckets: 4, 229 BucketSize: bucketSize, 230 } 231 Convey("for four buckets of 10 seconds", func() { 232 tasks, err := task.Find(task.ByTimeRun(now, endTime)) 233 So(err, ShouldBeNil) 234 So(len(tasks), ShouldEqual, 4) 235 236 buckets, errors := CreateTaskBuckets(tasks, []task.Task{}, frameBounds) 237 So(errors, ShouldBeEmpty) 238 So(len(buckets), ShouldEqual, 4) 239 So(int(buckets[0].TotalTime.Seconds()), ShouldEqual, 17) 240 So(int(math.Ceil(buckets[1].TotalTime.Seconds())), ShouldEqual, 30) 241 So(int(math.Ceil(buckets[2].TotalTime.Seconds())), ShouldEqual, 20) 242 }) 243 244 }) 245 } 246 247 func TestAverageStatistics(t *testing.T) { 248 testutil.HandleTestingErr(db.ClearCollections(task.Collection), t, "couldnt reset host") 249 Convey("With a distro sampleDistro inserted", t, func() { 250 d := distro.Distro{ 251 Id: "sampleDistro", 252 } 253 err := d.Insert() 254 So(err, ShouldBeNil) 255 distroId := d.Id 256 Convey("With a set of tasks that have different scheduled -> start times over a given time period", func() { 257 now := time.Now() 258 bucketSize := 10 * time.Second 259 numberBuckets := 3 260 261 task1 := task.Task{Id: "task1", ScheduledTime: now, 262 StartTime: now.Add(time.Duration(5) * time.Second), Status: evergreen.TaskStarted, DistroId: distroId} 263 264 So(task1.Insert(), ShouldBeNil) 265 266 task2 := task.Task{Id: "task2", ScheduledTime: now, 267 StartTime: now.Add(time.Duration(20) * time.Second), Status: evergreen.TaskStarted, DistroId: distroId} 268 269 So(task2.Insert(), ShouldBeNil) 270 271 task3 := task.Task{Id: "task3", ScheduledTime: now.Add(time.Duration(10) * time.Second), 272 StartTime: now.Add(time.Duration(20) * time.Second), Status: evergreen.TaskStarted, DistroId: distroId} 273 So(task3.Insert(), ShouldBeNil) 274 275 frameBounds := FrameBounds{ 276 StartTime: now, 277 EndTime: now.Add(time.Duration(numberBuckets) * bucketSize), 278 NumberBuckets: numberBuckets, 279 BucketSize: bucketSize, 280 } 281 avgBuckets, err := AverageStatistics(distroId, frameBounds) 282 So(err, ShouldBeNil) 283 284 So(avgBuckets[0].AverageTime, ShouldEqual, 5*time.Second) 285 So(avgBuckets[1].AverageTime, ShouldEqual, 0) 286 So(avgBuckets[2].AverageTime, ShouldEqual, 15*time.Second) 287 288 Convey("if the distro id given does not exist, it shoud return an empty list", func() { 289 _, err := AverageStatistics("noId", frameBounds) 290 So(err, ShouldNotBeNil) 291 }) 292 }) 293 }) 294 295 } 296 297 func TestFindPredictedMakespan(t *testing.T) { 298 Convey("With a simple set of tasks that are dependent on each other and different times taken", t, func() { 299 300 a := task.Task{Id: "a", TimeTaken: time.Duration(5) * time.Second, DependsOn: []task.Dependency{}} 301 b := task.Task{Id: "b", TimeTaken: time.Duration(3) * time.Second, DependsOn: []task.Dependency{{"a", evergreen.TaskFailed}}} 302 c := task.Task{Id: "c", TimeTaken: time.Duration(4) * time.Second, DependsOn: []task.Dependency{{"a", evergreen.TaskFailed}}} 303 f := task.Task{Id: "f", TimeTaken: time.Duration(40) * time.Second, DependsOn: []task.Dependency{{"b", evergreen.TaskFailed}}} 304 305 d := task.Task{Id: "d", TimeTaken: time.Duration(10) * time.Second} 306 e := task.Task{Id: "e", TimeTaken: time.Duration(5) * time.Second, DependsOn: []task.Dependency{{"d", evergreen.TaskFailed}}} 307 308 Convey("with one tree of dependencies", func() { 309 allTasks := []task.Task{a, b, c} 310 depPath := FindPredictedMakespan(allTasks) 311 So(depPath.TotalTime, ShouldEqual, time.Duration(9)*time.Second) 312 So(len(depPath.Tasks), ShouldEqual, 2) 313 }) 314 Convey("with one tree and one singular longer task", func() { 315 allTasks := []task.Task{a, b, c, d} 316 depPath := FindPredictedMakespan(allTasks) 317 So(depPath.TotalTime, ShouldEqual, time.Duration(10)*time.Second) 318 So(len(depPath.Tasks), ShouldEqual, 1) 319 So(depPath.Tasks[0], ShouldEqual, "d") 320 }) 321 Convey("with two trees", func() { 322 allTasks := []task.Task{a, b, c, d, e} 323 depPath := FindPredictedMakespan(allTasks) 324 So(depPath.TotalTime, ShouldEqual, time.Duration(15)*time.Second) 325 So(len(depPath.Tasks), ShouldEqual, 2) 326 327 }) 328 Convey("with a tree with varying times taken", func() { 329 allTasks := []task.Task{a, b, c, f} 330 depPath := FindPredictedMakespan(allTasks) 331 So(depPath.TotalTime, ShouldEqual, time.Duration(48)*time.Second) 332 }) 333 334 }) 335 } 336 337 func TestCalculateActualMakespan(t *testing.T) { 338 Convey("With a simple set of tasks that are dependent on each other and different times taken", t, func() { 339 now := time.Now() 340 a := task.Task{Id: "a", StartTime: now.Add(time.Duration(-10) * time.Second), FinishTime: now.Add(10 * time.Second)} 341 b := task.Task{Id: "b", StartTime: now.Add(time.Duration(-20) * time.Second), FinishTime: now.Add(20 * time.Second)} 342 c := task.Task{Id: "c", StartTime: now, FinishTime: now.Add(10 * time.Second)} 343 d := task.Task{Id: "d", StartTime: now.Add(time.Duration(10) * time.Second), FinishTime: now.Add(40 * time.Second)} 344 345 Convey("with one tree of dependencies", func() { 346 allTasks := []task.Task{a, b, c, d} 347 makespan := CalculateActualMakespan(allTasks) 348 So(makespan, ShouldEqual, time.Duration(60)*time.Second) 349 }) 350 351 }) 352 }