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: ×tamppb.Timestamp{Seconds: 100}, 220 StartTime: ×tamppb.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 }