go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/cvtesting/e2e/large_cl_stack_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 "testing" 19 20 gerritpb "go.chromium.org/luci/common/proto/gerrit" 21 22 "go.chromium.org/luci/cv/internal/configs/prjcfg/prjcfgtest" 23 gf "go.chromium.org/luci/cv/internal/gerrit/gerritfake" 24 "go.chromium.org/luci/cv/internal/run" 25 26 . "github.com/smartystreets/goconvey/convey" 27 ) 28 29 func TestHandleLargeCLStack(t *testing.T) { 30 t.Parallel() 31 32 Convey("CV full runs and submits a large CL stack.", t, func() { 33 ct := Test{} 34 ctx, cancel := ct.SetUp(t) 35 defer cancel() 36 37 const lProject = "infra" 38 const gHost = "g-review" 39 const gRepo = "re/po" 40 const gRef = "refs/heads/main" 41 const gChangeFirst = 1001 42 // TODO(tandrii): bump max entities limit in datastore in-memory emulation 43 // to from 25 to ~500 and then bump this limit to 200. 44 // NOTE: current datastore in-memory emulates classic Datastore, not the 45 // Firestore used by CV, and as such its limit of 25 counts entity *groups*, 46 // thus Run and all its CLs count as 1 such group. 47 const N = 15 48 49 cfg := MakeCfgCombinable("cg0", gHost, gRepo, gRef) 50 prjcfgtest.Create(ctx, lProject, cfg) 51 So(ct.PMNotifier.UpdateConfig(ctx, lProject), ShouldBeNil) 52 53 cis := make([]*gerritpb.ChangeInfo, N) 54 for i := range cis { 55 cis[i] = gf.CI( 56 gChangeFirst+i, 57 gf.Project(gRepo), gf.Ref(gRef), 58 gf.Owner("user-1"), gf.PS(1), 59 gf.CQ(+2, ct.Clock.Now(), gf.U("user-1")), 60 gf.Approve(), 61 gf.Updated(ct.Clock.Now())) 62 } 63 // A DryRunner can trigger a FullRun w/ an approval. 64 ct.AddDryRunner("user-1") 65 ct.GFake.AddFrom(gf.WithCIs(gHost, gf.ACLRestricted(lProject), cis...)) 66 for i, child := range cis { 67 for _, parent := range cis[:i] { 68 ct.GFake.SetDependsOn(gHost, child, parent) 69 } 70 } 71 72 ct.LogPhase(ctx, "CV creates a Run") 73 ct.RunUntil(ctx, func() bool { 74 return len(ct.LoadRunsOf(ctx, lProject)) > 0 75 }) 76 r := ct.EarliestCreatedRunOf(ctx, lProject) 77 So(r.CLs, ShouldHaveLength, N) 78 79 ct.LogPhase(ctx, "CV submits all CLs and finishes the Run") 80 ct.RunUntil(ctx, func() bool { 81 r = ct.LoadRun(ctx, r.ID) 82 return run.IsEnded(r.Status) 83 }) 84 85 So(r.Status, ShouldEqual, run.Status_SUCCEEDED) 86 So(r.Submission.GetCls(), ShouldHaveLength, N) 87 So(r.Submission.GetSubmittedCls(), ShouldHaveLength, N) 88 var actual, expected []int 89 for i := range cis { 90 gChange := gChangeFirst + i 91 expected = append(expected, gChange) 92 if ct.GFake.GetChange(gHost, gChangeFirst+i).Info.GetStatus() == gerritpb.ChangeStatus_MERGED { 93 actual = append(actual, gChange) 94 } 95 } 96 So(actual, ShouldResemble, expected) 97 }) 98 }