go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/compilefailureanalysis/nthsection/nth_section_analysis_test.go (about)

     1  // Copyright 2022 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 nthsection
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"testing"
    21  
    22  	"github.com/golang/mock/gomock"
    23  	bbpb "go.chromium.org/luci/buildbucket/proto"
    24  	"go.chromium.org/luci/common/clock"
    25  	"go.chromium.org/luci/common/clock/testclock"
    26  	"go.chromium.org/luci/gae/impl/memory"
    27  	"go.chromium.org/luci/gae/service/datastore"
    28  	"go.chromium.org/luci/server/tq"
    29  	"google.golang.org/protobuf/types/known/timestamppb"
    30  
    31  	"go.chromium.org/luci/bisection/internal/buildbucket"
    32  	"go.chromium.org/luci/bisection/internal/config"
    33  	"go.chromium.org/luci/bisection/internal/gitiles"
    34  	"go.chromium.org/luci/bisection/model"
    35  	"go.chromium.org/luci/bisection/nthsectionsnapshot"
    36  	configpb "go.chromium.org/luci/bisection/proto/config"
    37  	pb "go.chromium.org/luci/bisection/proto/v1"
    38  	"go.chromium.org/luci/bisection/util/testutil"
    39  
    40  	. "github.com/smartystreets/goconvey/convey"
    41  	tpb "go.chromium.org/luci/bisection/task/proto"
    42  	. "go.chromium.org/luci/common/testing/assertions"
    43  )
    44  
    45  func TestCreateSnapshot(t *testing.T) {
    46  	t.Parallel()
    47  	c := memory.Use(context.Background())
    48  	testutil.UpdateIndices(c)
    49  
    50  	Convey("Create Snapshot", t, func() {
    51  		analysis := &model.CompileFailureAnalysis{}
    52  		So(datastore.Put(c, analysis), ShouldBeNil)
    53  		datastore.GetTestable(c).CatchupIndexes()
    54  		blamelist := testutil.CreateBlamelist(4)
    55  		nthSectionAnalysis := &model.CompileNthSectionAnalysis{
    56  			BlameList:      blamelist,
    57  			ParentAnalysis: datastore.KeyForObj(c, analysis),
    58  		}
    59  		So(datastore.Put(c, nthSectionAnalysis), ShouldBeNil)
    60  
    61  		rerun1 := &model.SingleRerun{
    62  			Type:   model.RerunBuildType_CulpritVerification,
    63  			Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS,
    64  			GitilesCommit: bbpb.GitilesCommit{
    65  				Id: "commit1",
    66  			},
    67  			Analysis: datastore.KeyForObj(c, analysis),
    68  		}
    69  
    70  		So(datastore.Put(c, rerun1), ShouldBeNil)
    71  
    72  		rerun2 := &model.SingleRerun{
    73  			Type:   model.RerunBuildType_NthSection,
    74  			Status: pb.RerunStatus_RERUN_STATUS_FAILED,
    75  			GitilesCommit: bbpb.GitilesCommit{
    76  				Id: "commit3",
    77  			},
    78  			Analysis: datastore.KeyForObj(c, analysis),
    79  		}
    80  		So(datastore.Put(c, rerun2), ShouldBeNil)
    81  
    82  		rerun3 := &model.SingleRerun{
    83  			Type:   model.RerunBuildType_NthSection,
    84  			Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS,
    85  			GitilesCommit: bbpb.GitilesCommit{
    86  				Id: "commit0",
    87  			},
    88  			Analysis: datastore.KeyForObj(c, analysis),
    89  		}
    90  
    91  		So(datastore.Put(c, rerun3), ShouldBeNil)
    92  
    93  		rerun4 := &model.SingleRerun{
    94  			Type:   model.RerunBuildType_NthSection,
    95  			Status: pb.RerunStatus_RERUN_STATUS_INFRA_FAILED,
    96  			GitilesCommit: bbpb.GitilesCommit{
    97  				Id: "commit2",
    98  			},
    99  			Analysis: datastore.KeyForObj(c, analysis),
   100  		}
   101  
   102  		So(datastore.Put(c, rerun4), ShouldBeNil)
   103  
   104  		datastore.GetTestable(c).CatchupIndexes()
   105  
   106  		snapshot, err := CreateSnapshot(c, nthSectionAnalysis)
   107  		So(err, ShouldBeNil)
   108  		So(snapshot.BlameList, ShouldResembleProto, blamelist)
   109  
   110  		So(snapshot.NumInProgress, ShouldEqual, 2)
   111  		So(snapshot.NumInfraFailed, ShouldEqual, 1)
   112  		So(snapshot.Runs, ShouldResemble, []*nthsectionsnapshot.Run{
   113  			{
   114  				Index:  0,
   115  				Commit: "commit0",
   116  				Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS,
   117  				Type:   model.RerunBuildType_NthSection,
   118  			},
   119  			{
   120  				Index:  1,
   121  				Commit: "commit1",
   122  				Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS,
   123  				Type:   model.RerunBuildType_CulpritVerification,
   124  			},
   125  			{
   126  				Index:  2,
   127  				Commit: "commit2",
   128  				Status: pb.RerunStatus_RERUN_STATUS_INFRA_FAILED,
   129  				Type:   model.RerunBuildType_NthSection,
   130  			},
   131  			{
   132  				Index:  3,
   133  				Commit: "commit3",
   134  				Status: pb.RerunStatus_RERUN_STATUS_FAILED,
   135  				Type:   model.RerunBuildType_NthSection,
   136  			},
   137  		})
   138  	})
   139  }
   140  
   141  func TestAnalyze(t *testing.T) {
   142  	t.Parallel()
   143  	Convey("TestAnalyze", t, func() {
   144  		c := memory.Use(context.Background())
   145  		testutil.UpdateIndices(c)
   146  		cl := testclock.New(testclock.TestTimeUTC)
   147  		c = clock.Set(c, cl)
   148  
   149  		// Set up the config
   150  		projectCfg := config.CreatePlaceholderProjectConfig()
   151  		cfg := map[string]*configpb.ProjectConfig{"chromium": projectCfg}
   152  		So(config.SetTestProjectConfig(c, cfg), ShouldBeNil)
   153  
   154  		gitilesResponse := model.ChangeLogResponse{
   155  			Log: []*model.ChangeLog{
   156  				{
   157  					Commit:  "3426",
   158  					Message: "Use TestActivationManager for all page activations\n\nblah blah\n\nChange-Id: blah\nBug: blah\nReviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3472131\nReviewed-by: blah blah\n",
   159  				},
   160  				{
   161  					Commit:  "3425",
   162  					Message: "Second Commit\n\nblah blah\n\nChange-Id: blah\nBug: blah\nReviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3472130\nReviewed-by: blah blah\n",
   163  				},
   164  				{
   165  					Commit:  "3424",
   166  					Message: "Third Commit\n\nblah blah\n\nChange-Id: blah\nBug: blah\nReviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3472129\nReviewed-by: blah blah\n",
   167  				},
   168  			},
   169  		}
   170  		gitilesResponseStr, err := json.Marshal(gitilesResponse)
   171  		if err != nil {
   172  			panic(err.Error())
   173  		}
   174  
   175  		// To test the case there is only 1 commit in blame list.
   176  		gitilesResponseSingleCommit := model.ChangeLogResponse{
   177  			Log: []*model.ChangeLog{
   178  				{
   179  					Commit:  "3426",
   180  					Message: "Use TestActivationManager for all page activations\n\nblah blah\n\nChange-Id: blah\nBug: blah\nReviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3472131\nReviewed-by: blah blah\n",
   181  				},
   182  				{
   183  					Commit:  "3425",
   184  					Message: "Second Commit\n\nblah blah\n\nChange-Id: blah\nBug: blah\nReviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3472130\nReviewed-by: blah blah\n",
   185  				},
   186  			},
   187  		}
   188  		gitilesResponseSingleCommitStr, err := json.Marshal(gitilesResponseSingleCommit)
   189  		if err != nil {
   190  			panic(err.Error())
   191  		}
   192  
   193  		c = gitiles.MockedGitilesClientContext(c, map[string]string{
   194  			"https://chromium.googlesource.com/chromium/src/+log/12345^1..23456": string(gitilesResponseStr),
   195  			"https://chromium.googlesource.com/chromium/src/+log/12346^1..23457": string(gitilesResponseSingleCommitStr),
   196  		})
   197  
   198  		// Setup mock for buildbucket
   199  		ctl := gomock.NewController(t)
   200  		defer ctl.Finish()
   201  		mc := buildbucket.NewMockedClient(c, ctl)
   202  		c = mc.Ctx
   203  		res := &bbpb.Build{
   204  			Builder: &bbpb.BuilderID{
   205  				Project: "chromium",
   206  				Bucket:  "findit",
   207  				Builder: "single-revision",
   208  			},
   209  			Input: &bbpb.Build_Input{
   210  				GitilesCommit: &bbpb.GitilesCommit{
   211  					Host:    "host",
   212  					Project: "proj",
   213  					Id:      "id1",
   214  					Ref:     "ref",
   215  				},
   216  			},
   217  			Id:         123,
   218  			Status:     bbpb.Status_STARTED,
   219  			CreateTime: &timestamppb.Timestamp{Seconds: 100},
   220  			StartTime:  &timestamppb.Timestamp{Seconds: 101},
   221  		}
   222  		mc.Client.EXPECT().ScheduleBuild(gomock.Any(), gomock.Any(), gomock.Any()).Return(res, nil).AnyTimes()
   223  		mc.Client.EXPECT().GetBuild(gomock.Any(), gomock.Any(), gomock.Any()).Return(&bbpb.Build{}, nil).AnyTimes()
   224  
   225  		Convey("CheckBlameList", func() {
   226  			rr := &pb.RegressionRange{
   227  				LastPassed: &bbpb.GitilesCommit{
   228  					Host:    "chromium.googlesource.com",
   229  					Project: "chromium/src",
   230  					Id:      "12345",
   231  				},
   232  				FirstFailed: &bbpb.GitilesCommit{
   233  					Host:    "chromium.googlesource.com",
   234  					Project: "chromium/src",
   235  					Id:      "23456",
   236  				},
   237  			}
   238  
   239  			fb := &model.LuciFailedBuild{
   240  				LuciBuild: model.LuciBuild{Project: "chromium"},
   241  			}
   242  			So(datastore.Put(c, fb), ShouldBeNil)
   243  			datastore.GetTestable(c).CatchupIndexes()
   244  
   245  			cf := &model.CompileFailure{
   246  				Build:         datastore.KeyForObj(c, fb),
   247  				OutputTargets: []string{"abc.xyz"},
   248  			}
   249  			So(datastore.Put(c, cf), ShouldBeNil)
   250  			datastore.GetTestable(c).CatchupIndexes()
   251  
   252  			cfa := &model.CompileFailureAnalysis{
   253  				Id:                     123,
   254  				CompileFailure:         datastore.KeyForObj(c, cf),
   255  				InitialRegressionRange: rr,
   256  				FirstFailedBuildId:     1000,
   257  			}
   258  			So(datastore.Put(c, cfa), ShouldBeNil)
   259  			datastore.GetTestable(c).CatchupIndexes()
   260  
   261  			nsa, err := Analyze(c, cfa)
   262  			So(err, ShouldBeNil)
   263  			So(nsa, ShouldNotBeNil)
   264  			datastore.GetTestable(c).CatchupIndexes()
   265  
   266  			// Fetch the nth section analysis
   267  			q := datastore.NewQuery("CompileNthSectionAnalysis")
   268  			nthsectionAnalyses := []*model.CompileNthSectionAnalysis{}
   269  			err = datastore.GetAll(c, q, &nthsectionAnalyses)
   270  			So(err, ShouldBeNil)
   271  			So(len(nthsectionAnalyses), ShouldEqual, 1)
   272  			nsa = nthsectionAnalyses[0]
   273  
   274  			So(nsa.BlameList, ShouldResembleProto, &pb.BlameList{
   275  				Commits: []*pb.BlameListSingleCommit{
   276  					{
   277  						Commit:      "3426",
   278  						ReviewTitle: "Use TestActivationManager for all page activations",
   279  						ReviewUrl:   "https://chromium-review.googlesource.com/c/chromium/src/+/3472131",
   280  					},
   281  					{
   282  						Commit:      "3425",
   283  						ReviewTitle: "Second Commit",
   284  						ReviewUrl:   "https://chromium-review.googlesource.com/c/chromium/src/+/3472130",
   285  					},
   286  				},
   287  				LastPassCommit: &pb.BlameListSingleCommit{
   288  					Commit:      "3424",
   289  					ReviewTitle: "Third Commit",
   290  					ReviewUrl:   "https://chromium-review.googlesource.com/c/chromium/src/+/3472129",
   291  				},
   292  			})
   293  		})
   294  
   295  		Convey("Only 1 commit in blamelist", func() {
   296  			c, skdr := tq.TestingContext(c, nil)
   297  			rr := &pb.RegressionRange{
   298  				LastPassed: &bbpb.GitilesCommit{
   299  					Host:    "chromium.googlesource.com",
   300  					Project: "chromium/src",
   301  					Ref:     "refs/heads/main",
   302  					Id:      "12346",
   303  				},
   304  				FirstFailed: &bbpb.GitilesCommit{
   305  					Host:    "chromium.googlesource.com",
   306  					Project: "chromium/src",
   307  					Ref:     "refs/heads/main",
   308  					Id:      "23457",
   309  				},
   310  			}
   311  
   312  			fb := &model.LuciFailedBuild{
   313  				LuciBuild: model.LuciBuild{Project: "chromium"},
   314  			}
   315  			So(datastore.Put(c, fb), ShouldBeNil)
   316  			datastore.GetTestable(c).CatchupIndexes()
   317  
   318  			cf := &model.CompileFailure{
   319  				Build:         datastore.KeyForObj(c, fb),
   320  				OutputTargets: []string{"abc.xyz"},
   321  			}
   322  			So(datastore.Put(c, cf), ShouldBeNil)
   323  			datastore.GetTestable(c).CatchupIndexes()
   324  
   325  			cfa := &model.CompileFailureAnalysis{
   326  				Id:                     124,
   327  				CompileFailure:         datastore.KeyForObj(c, cf),
   328  				InitialRegressionRange: rr,
   329  				FirstFailedBuildId:     1000,
   330  			}
   331  			So(datastore.Put(c, cfa), ShouldBeNil)
   332  			datastore.GetTestable(c).CatchupIndexes()
   333  
   334  			nsa, err := Analyze(c, cfa)
   335  			So(err, ShouldBeNil)
   336  			So(nsa, ShouldNotBeNil)
   337  			datastore.GetTestable(c).CatchupIndexes()
   338  
   339  			// Fetch the nth section analysis
   340  			q := datastore.NewQuery("CompileNthSectionAnalysis").Ancestor(datastore.KeyForObj(c, cfa))
   341  			nthsectionAnalyses := []*model.CompileNthSectionAnalysis{}
   342  			err = datastore.GetAll(c, q, &nthsectionAnalyses)
   343  			So(err, ShouldBeNil)
   344  			So(len(nthsectionAnalyses), ShouldEqual, 1)
   345  			nsa = nthsectionAnalyses[0]
   346  			So(nsa.Status, ShouldEqual, pb.AnalysisStatus_SUSPECTFOUND)
   347  			So(nsa.RunStatus, ShouldEqual, pb.AnalysisRunStatus_ENDED)
   348  			So(cfa.Status, ShouldEqual, pb.AnalysisStatus_SUSPECTFOUND)
   349  			So(cfa.RunStatus, ShouldEqual, pb.AnalysisRunStatus_STARTED)
   350  
   351  			// Check that suspect was created.
   352  			q = datastore.NewQuery("Suspect")
   353  			suspects := []*model.Suspect{}
   354  			err = datastore.GetAll(c, q, &suspects)
   355  			So(err, ShouldBeNil)
   356  			So(len(suspects), ShouldEqual, 1)
   357  			suspect := suspects[0]
   358  			So(suspect, ShouldResemble, &model.Suspect{
   359  				Id:             suspect.Id,
   360  				Type:           model.SuspectType_NthSection,
   361  				ParentAnalysis: datastore.KeyForObj(c, nsa),
   362  				ReviewUrl:      "https://chromium-review.googlesource.com/c/chromium/src/+/3472131",
   363  				ReviewTitle:    "Use TestActivationManager for all page activations",
   364  				GitilesCommit: bbpb.GitilesCommit{
   365  					Host:    "chromium.googlesource.com",
   366  					Project: "chromium/src",
   367  					Id:      "3426",
   368  					Ref:     "refs/heads/main",
   369  				},
   370  				AnalysisType:       pb.AnalysisType_COMPILE_FAILURE_ANALYSIS,
   371  				VerificationStatus: model.SuspectVerificationStatus_VerificationScheduled,
   372  			})
   373  
   374  			// Check that a task was created.
   375  			So(len(skdr.Tasks().Payloads()), ShouldEqual, 1)
   376  			resultsTask := skdr.Tasks().Payloads()[0].(*tpb.CulpritVerificationTask)
   377  			So(resultsTask, ShouldResembleProto, &tpb.CulpritVerificationTask{
   378  				AnalysisId: cfa.Id,
   379  				SuspectId:  suspect.Id,
   380  				ParentKey:  nsa.Suspect.Parent().Encode(),
   381  			})
   382  		})
   383  	})
   384  }
   385  
   386  func TestGetPriority(t *testing.T) {
   387  	t.Parallel()
   388  	c := memory.Use(context.Background())
   389  	Convey("Get Priority", t, func() {
   390  		fb := &model.LuciFailedBuild{}
   391  		So(datastore.Put(c, fb), ShouldBeNil)
   392  		datastore.GetTestable(c).CatchupIndexes()
   393  
   394  		cf := &model.CompileFailure{
   395  			Build: datastore.KeyForObj(c, fb),
   396  		}
   397  		So(datastore.Put(c, cf), ShouldBeNil)
   398  		datastore.GetTestable(c).CatchupIndexes()
   399  
   400  		cfa := &model.CompileFailureAnalysis{
   401  			CompileFailure: datastore.KeyForObj(c, cf),
   402  		}
   403  		So(datastore.Put(c, cfa), ShouldBeNil)
   404  		datastore.GetTestable(c).CatchupIndexes()
   405  
   406  		nsa := &model.CompileNthSectionAnalysis{
   407  			ParentAnalysis: datastore.KeyForObj(c, cfa),
   408  		}
   409  		So(datastore.Put(c, nsa), ShouldBeNil)
   410  		datastore.GetTestable(c).CatchupIndexes()
   411  
   412  		pri, err := getRerunPriority(c, nsa, nil, nil)
   413  		So(err, ShouldBeNil)
   414  		So(pri, ShouldEqual, 110)
   415  		pri, err = getRerunPriority(c, nsa, nil, map[string]string{"id": "1"})
   416  		So(err, ShouldBeNil)
   417  		So(pri, ShouldEqual, 95)
   418  
   419  		cfa.IsTreeCloser = true
   420  		So(datastore.Put(c, cfa), ShouldBeNil)
   421  		datastore.GetTestable(c).CatchupIndexes()
   422  		pri, err = getRerunPriority(c, nsa, nil, nil)
   423  		So(err, ShouldBeNil)
   424  		So(pri, ShouldEqual, 40)
   425  	})
   426  }