go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/culpritverification/verify_culprit_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 culpritverification 16 17 import ( 18 "context" 19 "encoding/json" 20 "testing" 21 "time" 22 23 "github.com/golang/mock/gomock" 24 . "github.com/smartystreets/goconvey/convey" 25 bbpb "go.chromium.org/luci/buildbucket/proto" 26 "go.chromium.org/luci/common/clock" 27 "go.chromium.org/luci/common/clock/testclock" 28 "go.chromium.org/luci/gae/impl/memory" 29 "go.chromium.org/luci/gae/service/datastore" 30 "google.golang.org/protobuf/types/known/timestamppb" 31 32 "go.chromium.org/luci/bisection/internal/buildbucket" 33 "go.chromium.org/luci/bisection/internal/config" 34 "go.chromium.org/luci/bisection/internal/gitiles" 35 "go.chromium.org/luci/bisection/model" 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 41 func TestVerifySuspect(t *testing.T) { 42 t.Parallel() 43 44 Convey("Verify Suspect", t, func() { 45 c := memory.Use(context.Background()) 46 testutil.UpdateIndices(c) 47 48 cl := testclock.New(testclock.TestTimeUTC) 49 c = clock.Set(c, cl) 50 51 // Setup config. 52 projectCfg := config.CreatePlaceholderProjectConfig() 53 cfg := map[string]*configpb.ProjectConfig{"chromium": projectCfg} 54 So(config.SetTestProjectConfig(c, cfg), ShouldBeNil) 55 56 Convey("Verify Suspect triggers rerun", func() { 57 // Setup mock for buildbucket 58 ctl := gomock.NewController(t) 59 defer ctl.Finish() 60 mc := buildbucket.NewMockedClient(c, ctl) 61 c = mc.Ctx 62 res1 := &bbpb.Build{ 63 Builder: &bbpb.BuilderID{ 64 Project: "chromium", 65 Bucket: "findit", 66 Builder: "single-revision", 67 }, 68 Input: &bbpb.Build_Input{ 69 GitilesCommit: &bbpb.GitilesCommit{ 70 Host: "host", 71 Project: "proj", 72 Id: "id1", 73 Ref: "ref", 74 }, 75 }, 76 Id: 123, 77 Status: bbpb.Status_STARTED, 78 CreateTime: ×tamppb.Timestamp{Seconds: 100}, 79 StartTime: ×tamppb.Timestamp{Seconds: 101}, 80 } 81 mc.Client.EXPECT().ScheduleBuild(gomock.Any(), gomock.Any(), gomock.Any()).Return(res1, nil).Times(1) 82 83 res2 := &bbpb.Build{ 84 Builder: &bbpb.BuilderID{ 85 Project: "chromium", 86 Bucket: "findit", 87 Builder: "single-revision", 88 }, 89 Input: &bbpb.Build_Input{ 90 GitilesCommit: &bbpb.GitilesCommit{ 91 Host: "host", 92 Project: "proj", 93 Id: "id2", 94 Ref: "ref", 95 }, 96 }, 97 Id: 456, 98 Status: bbpb.Status_STARTED, 99 CreateTime: ×tamppb.Timestamp{Seconds: 200}, 100 StartTime: ×tamppb.Timestamp{Seconds: 201}, 101 } 102 mc.Client.EXPECT().ScheduleBuild(gomock.Any(), gomock.Any(), gomock.Any()).Return(res2, nil).Times(1) 103 mc.Client.EXPECT().GetBuild(gomock.Any(), gomock.Any(), gomock.Any()).Return(&bbpb.Build{}, nil).AnyTimes() 104 gitilesResponse := model.ChangeLogResponse{ 105 Log: []*model.ChangeLog{ 106 { 107 Commit: "3424", 108 }, 109 }, 110 } 111 112 // Set up gitiles. 113 gitilesResponseStr, _ := json.Marshal(gitilesResponse) 114 c = gitiles.MockedGitilesClientContext(c, map[string]string{ 115 "https://chromium.googlesource.com/chromium/src/+log/3425~2..3425^": string(gitilesResponseStr), 116 }) 117 118 fb := &model.LuciFailedBuild{} 119 So(datastore.Put(c, fb), ShouldBeNil) 120 datastore.GetTestable(c).CatchupIndexes() 121 122 compileFailure := &model.CompileFailure{ 123 Id: 111, 124 Build: datastore.KeyForObj(c, fb), 125 OutputTargets: []string{"target1"}, 126 } 127 So(datastore.Put(c, compileFailure), ShouldBeNil) 128 datastore.GetTestable(c).CatchupIndexes() 129 130 analysis := &model.CompileFailureAnalysis{ 131 Id: 444, 132 CompileFailure: datastore.KeyForObj(c, compileFailure), 133 FirstFailedBuildId: 1000, 134 } 135 So(datastore.Put(c, analysis), ShouldBeNil) 136 datastore.GetTestable(c).CatchupIndexes() 137 138 heuristicAnalysis := &model.CompileHeuristicAnalysis{ 139 ParentAnalysis: datastore.KeyForObj(c, analysis), 140 } 141 So(datastore.Put(c, heuristicAnalysis), ShouldBeNil) 142 datastore.GetTestable(c).CatchupIndexes() 143 144 suspect := &model.Suspect{ 145 Score: 10, 146 ParentAnalysis: datastore.KeyForObj(c, heuristicAnalysis), 147 GitilesCommit: bbpb.GitilesCommit{ 148 Host: "chromium.googlesource.com", 149 Project: "chromium/src", 150 Id: "3425", 151 }, 152 } 153 So(datastore.Put(c, suspect), ShouldBeNil) 154 datastore.GetTestable(c).CatchupIndexes() 155 156 err := processCulpritVerificationTask(c, 444, suspect.Id, suspect.ParentAnalysis.Encode()) 157 So(err, ShouldBeNil) 158 So(datastore.Get(c, suspect), ShouldBeNil) 159 So(suspect.VerificationStatus, ShouldEqual, model.SuspectVerificationStatus_UnderVerification) 160 datastore.GetTestable(c).CatchupIndexes() 161 162 // Check that 2 rerun builds were created, and linked to suspect 163 rerun1 := &model.CompileRerunBuild{ 164 Id: suspect.SuspectRerunBuild.IntID(), 165 } 166 err = datastore.Get(c, rerun1) 167 So(err, ShouldBeNil) 168 So(rerun1, ShouldResemble, &model.CompileRerunBuild{ 169 Id: 123, 170 LuciBuild: model.LuciBuild{ 171 BuildId: 123, 172 Project: "chromium", 173 Bucket: "findit", 174 Builder: "single-revision", 175 Status: bbpb.Status_STARTED, 176 GitilesCommit: bbpb.GitilesCommit{ 177 Host: res1.Input.GitilesCommit.Host, 178 Project: res1.Input.GitilesCommit.Project, 179 Id: res1.Input.GitilesCommit.Id, 180 Ref: res1.Input.GitilesCommit.Ref, 181 }, 182 CreateTime: res1.CreateTime.AsTime(), 183 StartTime: res1.StartTime.AsTime(), 184 }, 185 }) 186 187 rerun2 := &model.CompileRerunBuild{ 188 Id: suspect.ParentRerunBuild.IntID(), 189 } 190 err = datastore.Get(c, rerun2) 191 So(err, ShouldBeNil) 192 So(rerun2, ShouldResemble, &model.CompileRerunBuild{ 193 Id: 456, 194 LuciBuild: model.LuciBuild{ 195 BuildId: 456, 196 Project: "chromium", 197 Bucket: "findit", 198 Builder: "single-revision", 199 Status: bbpb.Status_STARTED, 200 GitilesCommit: bbpb.GitilesCommit{ 201 Host: res2.Input.GitilesCommit.Host, 202 Project: res2.Input.GitilesCommit.Project, 203 Id: res2.Input.GitilesCommit.Id, 204 Ref: res2.Input.GitilesCommit.Ref, 205 }, 206 CreateTime: res2.CreateTime.AsTime(), 207 StartTime: res2.StartTime.AsTime(), 208 }, 209 }) 210 211 // Check that 2 SingleRerun model was created 212 q := datastore.NewQuery("SingleRerun").Eq("rerun_build", datastore.KeyForObj(c, rerun1)) 213 singleReruns := []*model.SingleRerun{} 214 err = datastore.GetAll(c, q, &singleReruns) 215 So(err, ShouldBeNil) 216 So(len(singleReruns), ShouldEqual, 1) 217 218 q = datastore.NewQuery("SingleRerun").Eq("rerun_build", datastore.KeyForObj(c, rerun2)) 219 singleReruns = []*model.SingleRerun{} 220 err = datastore.GetAll(c, q, &singleReruns) 221 So(err, ShouldBeNil) 222 So(len(singleReruns), ShouldEqual, 1) 223 }) 224 225 Convey("Verify Suspect should not trigger any rerun if culprit found", func() { 226 _, _, cfa := testutil.CreateCompileFailureAnalysisAnalysisChain(c, 8001, "chromium", 555) 227 228 suspect := &model.Suspect{ 229 VerificationStatus: model.SuspectVerificationStatus_VerificationScheduled, 230 } 231 So(datastore.Put(c, suspect), ShouldBeNil) 232 datastore.GetTestable(c).CatchupIndexes() 233 cfa.VerifiedCulprits = []*datastore.Key{ 234 datastore.KeyForObj(c, suspect), 235 } 236 So(datastore.Put(c, cfa), ShouldBeNil) 237 datastore.GetTestable(c).CatchupIndexes() 238 err := VerifySuspect(c, suspect, 8001, 555) 239 So(err, ShouldBeNil) 240 datastore.GetTestable(c).CatchupIndexes() 241 242 // Check that no rerun was created 243 q := datastore.NewQuery("SingleRerun").Eq("analysis", datastore.KeyForObj(c, cfa)) 244 singleReruns := []*model.SingleRerun{} 245 err = datastore.GetAll(c, q, &singleReruns) 246 So(err, ShouldBeNil) 247 So(len(singleReruns), ShouldEqual, 0) 248 So(suspect.VerificationStatus, ShouldEqual, model.SuspectVerificationStatus_Unverified) 249 }) 250 251 Convey("Verify Suspect should also update analysis status", func() { 252 _, _, cfa := testutil.CreateCompileFailureAnalysisAnalysisChain(c, 8001, "chromium", 666) 253 cfa.Status = pb.AnalysisStatus_SUSPECTFOUND 254 cfa.RunStatus = pb.AnalysisRunStatus_STARTED 255 So(datastore.Put(c, cfa), ShouldBeNil) 256 datastore.GetTestable(c).CatchupIndexes() 257 258 nsa := testutil.CreateNthSectionAnalysis(c, cfa) 259 nsa.Status = pb.AnalysisStatus_SUSPECTFOUND 260 nsa.RunStatus = pb.AnalysisRunStatus_ENDED 261 So(datastore.Put(c, nsa), ShouldBeNil) 262 datastore.GetTestable(c).CatchupIndexes() 263 264 suspect := &model.Suspect{ 265 VerificationStatus: model.SuspectVerificationStatus_VerificationScheduled, 266 ParentAnalysis: datastore.KeyForObj(c, nsa), 267 GitilesCommit: bbpb.GitilesCommit{ 268 Host: "host", 269 Project: "proj", 270 Ref: "ref", 271 Id: "id", 272 }, 273 } 274 // Create another suspect with same gitiles commit, so that no rerun build 275 // is triggered 276 suspect1 := &model.Suspect{ 277 VerificationStatus: model.SuspectVerificationStatus_Vindicated, 278 ParentAnalysis: datastore.KeyForObj(c, nsa), 279 GitilesCommit: bbpb.GitilesCommit{ 280 Host: "host", 281 Project: "proj", 282 Ref: "ref", 283 Id: "id", 284 }, 285 } 286 So(datastore.Put(c, suspect), ShouldBeNil) 287 So(datastore.Put(c, suspect1), ShouldBeNil) 288 datastore.GetTestable(c).CatchupIndexes() 289 290 err := VerifySuspect(c, suspect, 8001, 666) 291 So(err, ShouldBeNil) 292 datastore.GetTestable(c).CatchupIndexes() 293 294 // Check that no rerun was created 295 q := datastore.NewQuery("SingleRerun").Eq("analysis", datastore.KeyForObj(c, cfa)) 296 singleReruns := []*model.SingleRerun{} 297 err = datastore.GetAll(c, q, &singleReruns) 298 So(err, ShouldBeNil) 299 So(len(singleReruns), ShouldEqual, 0) 300 So(datastore.Get(c, suspect), ShouldBeNil) 301 So(suspect.VerificationStatus, ShouldEqual, model.SuspectVerificationStatus_Unverified) 302 303 // Verify the status is updated 304 So(datastore.Get(c, cfa), ShouldBeNil) 305 So(cfa.Status, ShouldEqual, pb.AnalysisStatus_SUSPECTFOUND) 306 So(cfa.RunStatus, ShouldEqual, pb.AnalysisRunStatus_ENDED) 307 }) 308 }) 309 } 310 311 func TestHasNewTargets(t *testing.T) { 312 cls := &model.ChangeLog{ 313 ChangeLogDiffs: []model.ChangeLogDiff{ 314 { 315 Type: model.ChangeType_ADD, 316 NewPath: "src/device/bluetooth/floss/bluetooth_gatt_service_floss.h", 317 }, 318 { 319 Type: model.ChangeType_RENAME, 320 NewPath: "src/device/bluetooth/floss/bluetooth_gatt_service_floss_1.h", 321 }, 322 { 323 Type: model.ChangeType_COPY, 324 NewPath: "src/device/bluetooth/floss/bluetooth_gatt_service_floss_2.h", 325 }, 326 { 327 Type: model.ChangeType_MODIFY, 328 NewPath: "src/device/bluetooth/floss/bluetooth_gatt_service_floss_3.h", 329 }, 330 }, 331 } 332 333 c := context.Background() 334 335 Convey("Has New Targets", t, func() { 336 So(hasNewTarget(c, []string{"device/bluetooth/floss/bluetooth_gatt_service_floss.h"}, cls), ShouldBeTrue) 337 So(hasNewTarget(c, []string{"device/bluetooth/floss/bluetooth_gatt_service_floss_1.h"}, cls), ShouldBeTrue) 338 So(hasNewTarget(c, []string{"device/bluetooth/floss/bluetooth_gatt_service_floss_2.h"}, cls), ShouldBeTrue) 339 So(hasNewTarget(c, []string{"device/bluetooth/floss/bluetooth_gatt_service_floss_3.h"}, cls), ShouldBeFalse) 340 }) 341 } 342 343 func TestGetPriority(t *testing.T) { 344 t.Parallel() 345 c := memory.Use(context.Background()) 346 cl := testclock.New(testclock.TestTimeUTC) 347 c = clock.Set(c, cl) 348 349 Convey("GetPriority", t, func() { 350 now := clock.Now(c) 351 fb := &model.LuciFailedBuild{ 352 Id: 123, 353 LuciBuild: model.LuciBuild{ 354 StartTime: now, 355 EndTime: now.Add(9 * time.Minute), 356 }, 357 } 358 So(datastore.Put(c, fb), ShouldBeNil) 359 datastore.GetTestable(c).CatchupIndexes() 360 361 cf := &model.CompileFailure{ 362 Build: datastore.KeyForObj(c, fb), 363 } 364 So(datastore.Put(c, cf), ShouldBeNil) 365 datastore.GetTestable(c).CatchupIndexes() 366 367 cfa := &model.CompileFailureAnalysis{ 368 CompileFailure: datastore.KeyForObj(c, cf), 369 } 370 So(datastore.Put(c, cfa), ShouldBeNil) 371 datastore.GetTestable(c).CatchupIndexes() 372 373 ha := &model.CompileHeuristicAnalysis{ 374 ParentAnalysis: datastore.KeyForObj(c, cfa), 375 } 376 So(datastore.Put(c, ha), ShouldBeNil) 377 datastore.GetTestable(c).CatchupIndexes() 378 379 suspect := &model.Suspect{ 380 ParentAnalysis: datastore.KeyForObj(c, ha), 381 Score: 1, 382 Id: 123, 383 ReviewUrl: "reviewUrl", 384 } 385 So(datastore.Put(c, suspect), ShouldBeNil) 386 datastore.GetTestable(c).CatchupIndexes() 387 pri, err := getSuspectPriority(c, suspect) 388 So(err, ShouldBeNil) 389 So(pri, ShouldEqual, 120) 390 suspect.Score = 5 391 pri, err = getSuspectPriority(c, suspect) 392 So(err, ShouldBeNil) 393 So(pri, ShouldEqual, 100) 394 suspect.Score = 15 395 pri, err = getSuspectPriority(c, suspect) 396 So(err, ShouldBeNil) 397 So(pri, ShouldEqual, 80) 398 399 // Add another suspect 400 suspect1 := &model.Suspect{ 401 Score: 1, 402 Id: 124, 403 ReviewUrl: "reviewUrl", 404 VerificationStatus: model.SuspectVerificationStatus_UnderVerification, 405 } 406 So(datastore.Put(c, suspect1), ShouldBeNil) 407 datastore.GetTestable(c).CatchupIndexes() 408 pri, err = getSuspectPriority(c, suspect) 409 So(err, ShouldBeNil) 410 So(pri, ShouldEqual, 100) 411 412 cfa.IsTreeCloser = true 413 So(datastore.Put(c, cfa), ShouldBeNil) 414 datastore.GetTestable(c).CatchupIndexes() 415 pri, err = getSuspectPriority(c, suspect) 416 So(err, ShouldBeNil) 417 So(pri, ShouldEqual, 30) 418 }) 419 } 420 421 func TestCheckSuspectWithSameCommitExist(t *testing.T) { 422 t.Parallel() 423 c := memory.Use(context.Background()) 424 425 Convey("CheckSuspectWithSameCommitExist", t, func() { 426 _, _, cfa := testutil.CreateCompileFailureAnalysisAnalysisChain(c, 8000, "chromium", 555) 427 nsa := testutil.CreateNthSectionAnalysis(c, cfa) 428 suspect := testutil.CreateNthSectionSuspect(c, nsa) 429 430 exist, err := checkSuspectWithSameCommitExist(c, cfa, suspect) 431 So(err, ShouldBeNil) 432 So(exist, ShouldBeFalse) 433 434 ha := testutil.CreateHeuristicAnalysis(c, cfa) 435 s1 := testutil.CreateHeuristicSuspect(c, ha, model.SuspectVerificationStatus_Unverified) 436 437 exist, err = checkSuspectWithSameCommitExist(c, cfa, suspect) 438 So(err, ShouldBeNil) 439 So(exist, ShouldBeFalse) 440 441 s1.VerificationStatus = model.SuspectVerificationStatus_UnderVerification 442 So(datastore.Put(c, s1), ShouldBeNil) 443 datastore.GetTestable(c).CatchupIndexes() 444 exist, err = checkSuspectWithSameCommitExist(c, cfa, suspect) 445 So(err, ShouldBeNil) 446 So(exist, ShouldBeTrue) 447 }) 448 }