go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/cvtesting/e2e/submit_options_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 "sort" 20 "testing" 21 "time" 22 23 "google.golang.org/protobuf/types/known/durationpb" 24 25 cfgpb "go.chromium.org/luci/cv/api/config/v2" 26 "go.chromium.org/luci/cv/internal/configs/prjcfg/prjcfgtest" 27 gf "go.chromium.org/luci/cv/internal/gerrit/gerritfake" 28 "go.chromium.org/luci/cv/internal/run" 29 "go.chromium.org/luci/cv/internal/run/runtest" 30 "go.chromium.org/luci/gae/service/datastore" 31 32 . "github.com/smartystreets/goconvey/convey" 33 ) 34 35 func TestSubmissionObeySubmitOptions(t *testing.T) { 36 t.Parallel() 37 38 Convey("Burst requests to submit", t, func() { 39 ct := Test{} 40 ctx, cancel := ct.SetUp(t) 41 defer cancel() 42 43 const lProject = "infra" 44 const gHost = "g-review" 45 const gRepo = "re/po" 46 const gRef = "refs/heads/main" 47 const burstN = 20 48 49 cfg := MakeCfgSingular("cg0", gHost, gRepo, gRef) 50 maxBurst := 2 51 burstDelay := 10 * time.Second 52 cfg.SubmitOptions = &cfgpb.SubmitOptions{ 53 MaxBurst: int32(maxBurst), 54 BurstDelay: durationpb.New(burstDelay), 55 } 56 prjcfgtest.Create(ctx, lProject, cfg) 57 So(ct.PMNotifier.UpdateConfig(ctx, lProject), ShouldBeNil) 58 59 // burstN CLs request FULL_RUN at the same time. 60 for gChange := 1; gChange <= burstN; gChange++ { 61 ct.GFake.AddFrom(gf.WithCIs(gHost, gf.ACLRestricted(lProject), gf.CI( 62 gChange, gf.Project(gRepo), gf.Ref(gRef), 63 gf.Owner("user-1"), 64 gf.CQ(+2, ct.Clock.Now(), gf.U("user-2")), 65 gf.Approve(), 66 gf.Updated(ct.Clock.Now()), 67 ))) 68 } 69 // Only a committer can trigger a FullRun for someone else' CL. 70 ct.AddCommitter("user-2") 71 ct.LogPhase(ctx, fmt.Sprintf("CV starts and creates %d Runs", burstN)) 72 ct.RunUntilT(ctx, burstN*5 /* ~5 tasks per Run */, func() bool { 73 return len(ct.LoadRunsOf(ctx, lProject)) == burstN 74 }) 75 76 ct.LogPhase(ctx, fmt.Sprintf("CV successfully submits %d Runs", burstN)) 77 remaining := ct.LoadRunsOf(ctx, lProject) 78 ct.RunUntilT(ctx, burstN*25 /* ~25 tasks per Run */, func() bool { 79 switch err := datastore.Get(ctx, remaining); { 80 case err != nil: 81 panic(err) 82 default: 83 remaining = runtest.FilterNot(run.Status_SUCCEEDED, remaining...) 84 return len(remaining) == 0 85 } 86 }) 87 88 var submittedTimes []time.Time 89 for gChange := 1; gChange <= burstN; gChange++ { 90 c := ct.GFake.GetChange(gHost, gChange) 91 // last updated time of a CL should be submitted time. 92 submittedTimes = append(submittedTimes, c.Info.GetUpdated().AsTime()) 93 } 94 95 sort.Slice(submittedTimes, func(i, j int) bool { 96 return submittedTimes[i].Before(submittedTimes[j]) 97 }) 98 for i := 0; i < burstN-maxBurst; i++ { 99 So(submittedTimes[i+maxBurst], ShouldHappenOnOrAfter, submittedTimes[i].Add(burstDelay)) 100 } 101 }) 102 }