go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/cvtesting/e2e/config_changes_test.go (about) 1 // Copyright 2021 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 e2e 16 17 import ( 18 "fmt" 19 "testing" 20 21 "google.golang.org/protobuf/proto" 22 23 cfgpb "go.chromium.org/luci/cv/api/config/v2" 24 "go.chromium.org/luci/cv/internal/configs/prjcfg/prjcfgtest" 25 gf "go.chromium.org/luci/cv/internal/gerrit/gerritfake" 26 "go.chromium.org/luci/cv/internal/run" 27 "go.chromium.org/luci/cv/internal/run/runtest" 28 29 . "github.com/smartystreets/goconvey/convey" 30 ) 31 32 func TestConfigChangeStartsAndStopsRuns(t *testing.T) { 33 t.Parallel() 34 35 Convey("CV starts new and stops old Runs on config change as needed", t, func() { 36 ct := Test{} 37 ctx, cancel := ct.SetUp(t) 38 defer cancel() 39 40 const ( 41 lProject = "infra" 42 gHost = "g-review.example.com" 43 gRepoFirst = "repo/first" 44 gRepoSecond = "repo/second" 45 gChangeFirstSingle = 10 46 gChangeFirstCombo = 15 47 gChangeSecondCombo = 25 48 gChangeSecondSingle = 20 49 ) 50 builder := &cfgpb.Verifiers_Tryjob_Builder{ 51 Host: buildbucketHost, 52 Name: fmt.Sprintf("%s/try/test-builder", lProject), 53 } 54 cfgFirst := MakeCfgCombinable("main", gHost, gRepoFirst, "refs/heads/.+", builder) 55 ct.BuildbucketFake.EnsureBuilders(cfgFirst) 56 now := ct.Clock.Now() 57 ct.GFake.AddFrom(gf.WithCIs(gHost, gf.ACLRestricted(lProject), 58 // One CL in each repo can run standalone. 59 gf.CI( 60 gChangeFirstSingle, gf.Project(gRepoFirst), 61 gf.Owner("user-1"), 62 gf.CQ(+1, now, gf.U("user-1")), 63 gf.Updated(now), 64 ), 65 gf.CI( 66 gChangeSecondSingle, gf.Project(gRepoSecond), 67 gf.Owner("user-2"), 68 gf.CQ(+1, now, gf.U("user-2")), 69 gf.Updated(now), 70 ), 71 72 // First combo CL, when CV isn't watching gRepoSecond, can run standalone, 73 // but not the second combo which explicitly depends on the first. 74 gf.CI( 75 gChangeFirstCombo, gf.Project(gRepoFirst), 76 gf.Owner("user-12"), 77 gf.CQ(+1, now, gf.U("user-12")), 78 gf.Updated(now), 79 ), 80 gf.CI( 81 gChangeSecondCombo, gf.Project(gRepoSecond), 82 gf.Owner("user-12"), 83 gf.CQ(+1, now, gf.U("user-12")), 84 gf.Updated(now), 85 gf.Desc(fmt.Sprintf("Second Combo\n\nCq-Depend: %d", gChangeFirstCombo)), 86 ), 87 )) 88 ct.AddDryRunner("user-1") 89 ct.AddDryRunner("user-2") 90 ct.AddDryRunner("user-12") 91 92 ct.LogPhase(ctx, "CV starts 2 runs while watching first repo only") 93 prjcfgtest.Create(ctx, lProject, cfgFirst) 94 So(ct.PMNotifier.UpdateConfig(ctx, lProject), ShouldBeNil) 95 96 var runFirstSingle, runFirstCombo *run.Run 97 ct.RunUntil(ctx, func() bool { 98 runFirstSingle = ct.LatestRunWithGerritCL(ctx, gHost, gChangeFirstSingle) 99 runFirstCombo = ct.LatestRunWithGerritCL(ctx, gHost, gChangeFirstCombo) 100 return runtest.AreRunning(runFirstSingle, runFirstCombo) 101 }) 102 // Project must have no other runs. 103 So(ct.LoadRunsOf(ctx, lProject), ShouldHaveLength, 2) 104 // And combo Run must have just 1 CL. 105 So(runFirstCombo.CLs, ShouldHaveLength, 1) 106 107 ct.LogPhase(ctx, "CV watches both repos") 108 cfgBoth := proto.Clone(cfgFirst).(*cfgpb.Config) 109 g0 := cfgBoth.ConfigGroups[0].Gerrit[0] 110 g0.Projects = append(g0.Projects, &cfgpb.ConfigGroup_Gerrit_Project{ 111 Name: gRepoSecond, 112 RefRegexp: []string{"refs/heads/.+"}, 113 }) 114 prjcfgtest.Update(ctx, lProject, cfgBoth) 115 So(ct.PMNotifier.UpdateConfig(ctx, lProject), ShouldBeNil) 116 117 var runSecondSingle *run.Run 118 ct.RunUntil(ctx, func() bool { 119 runSecondSingle = ct.LatestRunWithGerritCL(ctx, gHost, gChangeSecondSingle) 120 return runtest.AreRunning(runSecondSingle) 121 }) 122 // TODO(crbug/1221535): CV should not ignore gChangeSecondCombo while 123 // runFirstCombo is running. It should either stop runFirstCombo or start a 124 // new Run. 125 So(ct.LatestRunWithGerritCL(ctx, gHost, gChangeSecondCombo), ShouldBeNil) 126 runFirstSingle = ct.LoadRun(ctx, runFirstSingle.ID) 127 runFirstCombo = ct.LoadRun(ctx, runFirstCombo.ID) 128 So(runtest.AreRunning(runFirstSingle, runFirstCombo, runSecondSingle), ShouldBeTrue) 129 130 ct.LogPhase(ctx, "CV watches only the second repo, stops Runs on CLs from the first repo, and purges second combo CL") 131 cfgSecond := MakeCfgCombinable("main", gHost, gRepoSecond, "refs/heads/.+", builder) 132 prjcfgtest.Update(ctx, lProject, cfgSecond) 133 So(ct.PMNotifier.UpdateConfig(ctx, lProject), ShouldBeNil) 134 ct.RunUntil(ctx, func() bool { 135 runFirstSingle = ct.LoadRun(ctx, runFirstSingle.ID) 136 runFirstCombo = ct.LoadRun(ctx, runFirstCombo.ID) 137 return runtest.AreEnded(runFirstSingle, runFirstCombo) && ct.MaxCQVote(ctx, gHost, gChangeSecondCombo) == 0 138 }) 139 So(ct.LastMessage(gHost, gChangeSecondCombo).GetMessage(), ShouldContainSubstring, 140 "CQ can't process the CL because its deps are not watched by the same LUCI project") 141 }) 142 }