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  }