go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/nthsectionsnapshot/snapshot_test.go (about) 1 // Copyright 2023 The LUCI Authors. 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 15 package nthsectionsnapshot 16 17 import ( 18 "testing" 19 20 pb "go.chromium.org/luci/bisection/proto/v1" 21 "go.chromium.org/luci/bisection/util/testutil" 22 23 . "github.com/smartystreets/goconvey/convey" 24 ) 25 26 func TestChunking(t *testing.T) { 27 t.Parallel() 28 29 Convey("One chunk", t, func() { 30 chunks := []*NthSectionSnapshotChunk{ 31 { 32 Begin: 0, 33 End: 9, 34 }, 35 } 36 alloc, chunkSize := chunking(chunks, 0, 3, 10) 37 So(alloc, ShouldResemble, []int{3}) 38 So(chunkSize, ShouldEqual, 2) 39 }) 40 41 Convey("One chunk, no divider", t, func() { 42 chunks := []*NthSectionSnapshotChunk{ 43 { 44 Begin: 0, 45 End: 9, 46 }, 47 } 48 alloc, chunkSize := chunking(chunks, 0, 0, 10) 49 So(alloc, ShouldResemble, []int{0}) 50 So(chunkSize, ShouldEqual, 10) 51 }) 52 53 Convey("One chunk, one divider", t, func() { 54 chunks := []*NthSectionSnapshotChunk{ 55 { 56 Begin: 0, 57 End: 9, 58 }, 59 } 60 alloc, chunkSize := chunking(chunks, 0, 1, 1) 61 So(alloc, ShouldResemble, []int{1}) 62 So(chunkSize, ShouldEqual, 5) 63 }) 64 65 Convey("One chunk, many dividers", t, func() { 66 chunks := []*NthSectionSnapshotChunk{ 67 { 68 Begin: 0, 69 End: 9, 70 }, 71 } 72 alloc, chunkSize := chunking(chunks, 0, 100, 100) 73 So(alloc, ShouldResemble, []int{100}) 74 So(chunkSize, ShouldEqual, 0) 75 }) 76 77 Convey("Two equal chunks", t, func() { 78 chunks := []*NthSectionSnapshotChunk{ 79 { 80 Begin: 0, 81 End: 9, 82 }, 83 { 84 Begin: 10, 85 End: 19, 86 }, 87 } 88 alloc, chunkSize := chunking(chunks, 0, 1, 1) 89 So(alloc, ShouldResemble, []int{1, 0}) 90 So(chunkSize, ShouldEqual, 10) 91 }) 92 93 Convey("Two equal chunks, two dividers", t, func() { 94 chunks := []*NthSectionSnapshotChunk{ 95 { 96 Begin: 0, 97 End: 9, 98 }, 99 { 100 Begin: 10, 101 End: 19, 102 }, 103 } 104 alloc, chunkSize := chunking(chunks, 0, 2, 2) 105 So(alloc, ShouldResemble, []int{1, 1}) 106 So(chunkSize, ShouldEqual, 5) 107 }) 108 109 Convey("Two unequal chunks, two dividers", t, func() { 110 chunks := []*NthSectionSnapshotChunk{ 111 { 112 Begin: 0, 113 End: 9, 114 }, 115 { 116 Begin: 10, 117 End: 11, 118 }, 119 } 120 alloc, chunkSize := chunking(chunks, 0, 2, 2) 121 So(alloc, ShouldResemble, []int{2, 0}) 122 So(chunkSize, ShouldEqual, 3) 123 }) 124 125 Convey("Two unequal chunks, many dividers", t, func() { 126 chunks := []*NthSectionSnapshotChunk{ 127 { 128 Begin: 0, 129 End: 9, 130 }, 131 { 132 Begin: 10, 133 End: 11, 134 }, 135 } 136 alloc, chunkSize := chunking(chunks, 0, 6, 6) 137 So(alloc, ShouldResemble, []int{5, 1}) 138 So(chunkSize, ShouldEqual, 1) 139 }) 140 141 Convey("Three chunks", t, func() { 142 chunks := []*NthSectionSnapshotChunk{ 143 { 144 Begin: 0, 145 End: 9, 146 }, 147 { 148 Begin: 10, 149 End: 15, 150 }, 151 { 152 Begin: 16, 153 End: 19, 154 }, 155 } 156 alloc, chunkSize := chunking(chunks, 0, 4, 4) 157 So(alloc, ShouldResemble, []int{2, 1, 1}) 158 So(chunkSize, ShouldEqual, 3) 159 }) 160 } 161 162 func TestGetRegressionRange(t *testing.T) { 163 t.Parallel() 164 165 Convey("GetRegressionRangeNoRun", t, func() { 166 // Create a blamelist with 100 commit 167 blamelist := testutil.CreateBlamelist(100) 168 snapshot := &Snapshot{ 169 BlameList: blamelist, 170 Runs: []*Run{}, 171 } 172 ff, lp, err := snapshot.GetCurrentRegressionRange() 173 So(err, ShouldBeNil) 174 So(ff, ShouldEqual, 0) 175 So(lp, ShouldEqual, 99) 176 }) 177 178 Convey("GetRegressionRangeOK", t, func() { 179 // Create a blamelist with 100 commit 180 blamelist := testutil.CreateBlamelist(100) 181 snapshot := &Snapshot{ 182 BlameList: blamelist, 183 Runs: []*Run{ 184 { 185 Index: 10, 186 Status: pb.RerunStatus_RERUN_STATUS_FAILED, 187 }, 188 { 189 Index: 15, 190 Status: pb.RerunStatus_RERUN_STATUS_FAILED, 191 }, 192 { 193 Index: 40, 194 Status: pb.RerunStatus_RERUN_STATUS_PASSED, 195 }, 196 { 197 Index: 50, 198 Status: pb.RerunStatus_RERUN_STATUS_PASSED, 199 }, 200 }, 201 } 202 ff, lp, err := snapshot.GetCurrentRegressionRange() 203 So(err, ShouldBeNil) 204 So(ff, ShouldEqual, 15) 205 So(lp, ShouldEqual, 39) 206 }) 207 208 Convey("GetRegressionRangeError", t, func() { 209 // Create a blamelist with 100 commit 210 blamelist := testutil.CreateBlamelist(100) 211 snapshot := &Snapshot{ 212 BlameList: blamelist, 213 Runs: []*Run{ 214 { 215 Index: 17, 216 Status: pb.RerunStatus_RERUN_STATUS_FAILED, 217 }, 218 { 219 Index: 10, 220 Status: pb.RerunStatus_RERUN_STATUS_PASSED, 221 }, 222 }, 223 } 224 _, _, err := snapshot.GetCurrentRegressionRange() 225 So(err, ShouldNotBeNil) 226 }) 227 228 Convey("GetRegressionRangeErrorFirstFailedEqualsLastPass", t, func() { 229 // Create a blamelist with 100 commit 230 blamelist := testutil.CreateBlamelist(100) 231 snapshot := &Snapshot{ 232 BlameList: blamelist, 233 Runs: []*Run{ 234 { 235 Index: 0, 236 Status: pb.RerunStatus_RERUN_STATUS_PASSED, 237 }, 238 }, 239 } 240 _, _, err := snapshot.GetCurrentRegressionRange() 241 So(err, ShouldNotBeNil) 242 }) 243 } 244 245 func TestGetCulprit(t *testing.T) { 246 t.Parallel() 247 248 Convey("GetCulpritOK", t, func() { 249 // Create a blamelist with 100 commit 250 blamelist := testutil.CreateBlamelist(100) 251 snapshot := &Snapshot{ 252 BlameList: blamelist, 253 Runs: []*Run{ 254 { 255 Index: 15, 256 Status: pb.RerunStatus_RERUN_STATUS_FAILED, 257 }, 258 { 259 Index: 16, 260 Status: pb.RerunStatus_RERUN_STATUS_PASSED, 261 }, 262 }, 263 } 264 ok, cul := snapshot.GetCulprit() 265 So(ok, ShouldBeTrue) 266 So(cul, ShouldEqual, 15) 267 }) 268 269 Convey("GetCulpritFailed", t, func() { 270 // Create a blamelist with 100 commit 271 blamelist := testutil.CreateBlamelist(100) 272 snapshot := &Snapshot{ 273 BlameList: blamelist, 274 Runs: []*Run{}, 275 } 276 ok, _ := snapshot.GetCulprit() 277 So(ok, ShouldBeFalse) 278 }) 279 280 Convey("GetCulpritError", t, func() { 281 // Create a blamelist with 100 commit 282 blamelist := testutil.CreateBlamelist(100) 283 snapshot := &Snapshot{ 284 BlameList: blamelist, 285 Runs: []*Run{ 286 { 287 Index: 10, 288 Status: pb.RerunStatus_RERUN_STATUS_FAILED, 289 }, 290 { 291 Index: 2, 292 Status: pb.RerunStatus_RERUN_STATUS_PASSED, 293 }, 294 }, 295 } 296 ok, _ := snapshot.GetCulprit() 297 So(ok, ShouldBeFalse) 298 }) 299 300 } 301 302 func TestFindRegressionChunks(t *testing.T) { 303 t.Parallel() 304 305 Convey("findRegressionChunks", t, func() { 306 blamelist := testutil.CreateBlamelist(100) 307 snapshot := &Snapshot{ 308 BlameList: blamelist, 309 Runs: []*Run{ 310 { 311 Index: 15, 312 Status: pb.RerunStatus_RERUN_STATUS_FAILED, 313 }, 314 { 315 Index: 19, 316 Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS, 317 }, 318 { 319 Index: 26, 320 Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS, 321 }, 322 { 323 Index: 35, 324 Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS, 325 }, 326 { 327 Index: 39, 328 Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS, 329 }, 330 { 331 Index: 40, 332 Status: pb.RerunStatus_RERUN_STATUS_PASSED, 333 }, 334 }, 335 } 336 chunks, err := snapshot.findRegressionChunks() 337 So(err, ShouldBeNil) 338 So(chunks, ShouldResemble, []*NthSectionSnapshotChunk{ 339 { 340 Begin: 27, 341 End: 34, 342 }, 343 { 344 Begin: 20, 345 End: 25, 346 }, 347 { 348 Begin: 16, 349 End: 18, 350 }, 351 { 352 Begin: 36, 353 End: 38, 354 }, 355 }) 356 }) 357 } 358 359 func TestBreakIntoSmallerChunks(t *testing.T) { 360 t.Parallel() 361 362 Convey("break", t, func() { 363 chunk := &NthSectionSnapshotChunk{ 364 Begin: 10, 365 End: 19, 366 } 367 So(breakToSmallerChunks(chunk, 0), ShouldResemble, []int{}) 368 So(breakToSmallerChunks(chunk, 1), ShouldResemble, []int{15}) 369 So(breakToSmallerChunks(chunk, 2), ShouldResemble, []int{13, 16}) 370 So(breakToSmallerChunks(chunk, 3), ShouldResemble, []int{12, 15, 17}) 371 So(breakToSmallerChunks(chunk, 4), ShouldResemble, []int{11, 13, 16, 18}) 372 So(breakToSmallerChunks(chunk, 5), ShouldResemble, []int{11, 13, 15, 16, 18}) 373 So(breakToSmallerChunks(chunk, 6), ShouldResemble, []int{11, 12, 14, 15, 17, 18}) 374 So(breakToSmallerChunks(chunk, 10), ShouldResemble, []int{10, 11, 12, 13, 14, 15, 16, 17, 18, 19}) 375 So(breakToSmallerChunks(chunk, 100), ShouldResemble, []int{10, 11, 12, 13, 14, 15, 16, 17, 18, 19}) 376 }) 377 } 378 379 func TestFindNextIndicesToRun(t *testing.T) { 380 t.Parallel() 381 382 Convey("FindNextIndicesToRun", t, func() { 383 blamelist := testutil.CreateBlamelist(10) 384 snapshot := &Snapshot{ 385 BlameList: blamelist, 386 Runs: []*Run{ 387 { 388 Index: 5, 389 Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS, 390 }, 391 }, 392 } 393 indices, err := snapshot.FindNextIndicesToRun(2) 394 So(err, ShouldBeNil) 395 So(indices, ShouldResemble, []int{2, 8}) 396 }) 397 398 Convey("FindNextIndicesToRun with a single commit should not return anything", t, func() { 399 blamelist := testutil.CreateBlamelist(1) 400 snapshot := &Snapshot{ 401 BlameList: blamelist, 402 Runs: []*Run{}, 403 } 404 indices, err := snapshot.FindNextIndicesToRun(2) 405 So(err, ShouldBeNil) 406 So(indices, ShouldResemble, []int{}) 407 }) 408 409 Convey("FindNextIndicesToRun already found culprit", t, func() { 410 blamelist := testutil.CreateBlamelist(10) 411 snapshot := &Snapshot{ 412 BlameList: blamelist, 413 Runs: []*Run{ 414 { 415 Index: 5, 416 Status: pb.RerunStatus_RERUN_STATUS_PASSED, 417 }, 418 { 419 Index: 4, 420 Status: pb.RerunStatus_RERUN_STATUS_FAILED, 421 }, 422 }, 423 } 424 indices, err := snapshot.FindNextIndicesToRun(2) 425 So(err, ShouldBeNil) 426 So(indices, ShouldResemble, []int{}) 427 }) 428 429 Convey("FindNextIndicesToRunAllRerunsAreRunning", t, func() { 430 blamelist := testutil.CreateBlamelist(3) 431 snapshot := &Snapshot{ 432 BlameList: blamelist, 433 Runs: []*Run{ 434 { 435 Index: 0, 436 Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS, 437 }, 438 { 439 Index: 1, 440 Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS, 441 }, 442 { 443 Index: 2, 444 Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS, 445 }, 446 }, 447 } 448 indices, err := snapshot.FindNextIndicesToRun(2) 449 So(err, ShouldBeNil) 450 So(indices, ShouldResemble, []int{}) 451 }) 452 } 453 454 func TestFindNextCommitsToRun(t *testing.T) { 455 t.Parallel() 456 457 Convey("FindNextIndicesToRun", t, func() { 458 blamelist := testutil.CreateBlamelist(10) 459 snapshot := &Snapshot{ 460 BlameList: blamelist, 461 Runs: []*Run{ 462 { 463 Index: 5, 464 Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS, 465 }, 466 }, 467 } 468 indices, err := snapshot.FindNextCommitsToRun(2) 469 So(err, ShouldBeNil) 470 So(indices, ShouldResemble, []string{"commit2", "commit8"}) 471 }) 472 } 473 474 func TestCalculateChunkSize(t *testing.T) { 475 t.Parallel() 476 477 Convey("CalculateChunkSize", t, func() { 478 So(calculateChunkSize(5, 10), ShouldEqual, 0) 479 So(calculateChunkSize(5, 5), ShouldEqual, 0) 480 So(calculateChunkSize(10, 3), ShouldEqual, 2) 481 So(calculateChunkSize(10, 0), ShouldEqual, 10) 482 So(calculateChunkSize(3, 1), ShouldEqual, 1) 483 }) 484 }