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  }