go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/throttle/throttle_test.go (about) 1 // Copyright 2023 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 throttle 16 17 import ( 18 "context" 19 "testing" 20 "time" 21 22 . "github.com/smartystreets/goconvey/convey" 23 "go.chromium.org/luci/bisection/internal/config" 24 "go.chromium.org/luci/bisection/model" 25 configpb "go.chromium.org/luci/bisection/proto/config" 26 pb "go.chromium.org/luci/bisection/proto/v1" 27 tpb "go.chromium.org/luci/bisection/task/proto" 28 _ "go.chromium.org/luci/bisection/testfailuredetection" 29 "go.chromium.org/luci/bisection/util" 30 "go.chromium.org/luci/bisection/util/testutil" 31 buildbucketpb "go.chromium.org/luci/buildbucket/proto" 32 "go.chromium.org/luci/common/clock" 33 "go.chromium.org/luci/common/clock/testclock" 34 . "go.chromium.org/luci/common/testing/assertions" 35 "go.chromium.org/luci/gae/impl/memory" 36 "go.chromium.org/luci/gae/service/datastore" 37 "go.chromium.org/luci/server/tq" 38 ) 39 40 func TestCronHandler(t *testing.T) { 41 t.Parallel() 42 ctx := memory.Use(context.Background()) 43 cl := testclock.New(testclock.TestTimeUTC) 44 baseTime := time.Unix(10000, 0).UTC() 45 cl.Set(baseTime) 46 ctx = clock.Set(ctx, cl) 47 testutil.UpdateIndices(ctx) 48 49 Convey("Check daily limit", t, func() { 50 setDailyLimit(ctx, 1) 51 Convey("no analysis", func() { 52 ctx, skdr := tq.TestingContext(ctx, nil) 53 err := CronHandler(ctx) 54 So(err, ShouldBeNil) 55 So(len(skdr.Tasks().Payloads()), ShouldEqual, 2) 56 }) 57 Convey("analysis is disabled", func() { 58 ctx, skdr := tq.TestingContext(ctx, nil) 59 testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{ 60 Status: pb.AnalysisStatus_DISABLED, 61 CreateTime: clock.Now(ctx), 62 }) 63 err := CronHandler(ctx) 64 So(err, ShouldBeNil) 65 So(len(skdr.Tasks().Payloads()), ShouldEqual, 2) 66 }) 67 Convey("analysis is not supported", func() { 68 ctx, skdr := tq.TestingContext(ctx, nil) 69 testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{ 70 Status: pb.AnalysisStatus_UNSUPPORTED, 71 CreateTime: clock.Now(ctx), 72 }) 73 err := CronHandler(ctx) 74 So(err, ShouldBeNil) 75 So(len(skdr.Tasks().Payloads()), ShouldEqual, 2) 76 }) 77 Convey("analysis is not recent", func() { 78 ctx, skdr := tq.TestingContext(ctx, nil) 79 testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{ 80 Status: pb.AnalysisStatus_FOUND, 81 CreateTime: clock.Now(ctx).Add(-25 * time.Hour), 82 }) 83 err := CronHandler(ctx) 84 So(err, ShouldBeNil) 85 So(len(skdr.Tasks().Payloads()), ShouldEqual, 2) 86 }) 87 Convey("analysis is recent", func() { 88 ctx, skdr := tq.TestingContext(ctx, nil) 89 testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{ 90 Project: "chrome", 91 Status: pb.AnalysisStatus_FOUND, 92 CreateTime: clock.Now(ctx).Add(-23 * time.Hour), 93 }) 94 err := CronHandler(ctx) 95 So(err, ShouldBeNil) 96 So(len(skdr.Tasks().Payloads()), ShouldEqual, 1) 97 // Chromium 98 resultsTask := skdr.Tasks().Payloads()[0].(*tpb.TestFailureDetectionTask) 99 So(resultsTask, ShouldResembleProto, &tpb.TestFailureDetectionTask{ 100 Project: "chromium", 101 DimensionExcludes: []*pb.Dimension{}, 102 }) 103 }) 104 }) 105 106 Convey("schedule test failure detection task", t, func() { 107 ctx, skdr := tq.TestingContext(ctx, nil) 108 setDailyLimit(ctx, 100) 109 Convey("no exclude dimensions", func() { 110 testRerun := createTestSingleRerun(ctx, buildbucketpb.Status_SCHEDULED, baseTime.Add(-6*time.Minute), "") 111 // No OS dimension. 112 testRerun.Dimensions = &pb.Dimensions{ 113 Dimensions: []*pb.Dimension{{ 114 Key: "cpu", 115 Value: "8", 116 }}, 117 } 118 So(datastore.Put(ctx, testRerun), ShouldBeNil) 119 datastore.GetTestable(ctx).CatchupIndexes() 120 121 err := CronHandler(ctx) 122 So(err, ShouldBeNil) 123 So(len(skdr.Tasks().Payloads()), ShouldEqual, 2) 124 resultsTask := skdr.Tasks().Payloads()[0].(*tpb.TestFailureDetectionTask) 125 So(resultsTask, ShouldResembleProto, &tpb.TestFailureDetectionTask{ 126 Project: "chrome", 127 DimensionExcludes: []*pb.Dimension{}, 128 }) 129 }) 130 Convey("exclude dimensions", func() { 131 setDailyLimit(ctx, 100) 132 // Scheduled test rerun - os excluded. 133 createTestSingleRerun(ctx, buildbucketpb.Status_SCHEDULED, baseTime.Add(-6*time.Minute), "test_os1") 134 // Started test rerun - os not excluded. 135 createTestSingleRerun(ctx, buildbucketpb.Status_STARTED, baseTime.Add(-6*time.Minute), "test_os2") 136 // Scheduled test rerun older than 7 days - os not excluded. 137 createTestSingleRerun(ctx, buildbucketpb.Status_SCHEDULED, baseTime.Add(-time.Hour*7*24), "test_os3") 138 // Scheduled test rerun created less than 5 minutes - os not excluded. 139 createTestSingleRerun(ctx, buildbucketpb.Status_SCHEDULED, baseTime.Add(-4*time.Minute), "test_os4") 140 // Scheduled test rerun belongs to a different project - os not excluded. 141 testRerun := createTestSingleRerun(ctx, buildbucketpb.Status_SCHEDULED, baseTime.Add(-6*time.Minute), "test_os5") 142 testRerun.Project = "otherproject" 143 So(datastore.Put(ctx, testRerun), ShouldBeNil) 144 datastore.GetTestable(ctx).CatchupIndexes() 145 146 // Scheduled compile rerun - os excluded. 147 createCompileReruns(ctx, buildbucketpb.Status_SCHEDULED, baseTime.Add(-6*time.Minute), "compile_os1") 148 // Started compile rerun - os not excluded. 149 createCompileReruns(ctx, buildbucketpb.Status_STARTED, baseTime.Add(-6*time.Minute), "compile_os2") 150 // Scheduled compile rerun older than 7 days - os not excluded. 151 createCompileReruns(ctx, buildbucketpb.Status_SCHEDULED, baseTime.Add(-time.Hour*7*24), "compile_os3") 152 // Scheduled compile rerun created less than 5 minutes - os not excluded. 153 createCompileReruns(ctx, buildbucketpb.Status_SCHEDULED, baseTime.Add(-4*time.Minute), "compile_os4") 154 // Scheduled compile rerun belongs to a different project - os not excluded. 155 rerunBuild, _ := createCompileReruns(ctx, buildbucketpb.Status_SCHEDULED, baseTime.Add(-6*time.Minute), "compile_os5") 156 rerunBuild.Project = "otherproject" 157 So(datastore.Put(ctx, rerunBuild), ShouldBeNil) 158 datastore.GetTestable(ctx).CatchupIndexes() 159 160 err := CronHandler(ctx) 161 So(err, ShouldBeNil) 162 So(len(skdr.Tasks().Payloads()), ShouldEqual, 2) 163 resultsTask := skdr.Tasks().Payloads()[1].(*tpb.TestFailureDetectionTask) 164 expectedDimensionExcludes := []*pb.Dimension{{Key: "os", Value: "test_os1"}, {Key: "os", Value: "compile_os1"}} 165 util.SortDimension(expectedDimensionExcludes) 166 So(resultsTask, ShouldResembleProto, &tpb.TestFailureDetectionTask{ 167 Project: "chromium", 168 DimensionExcludes: expectedDimensionExcludes, 169 }) 170 }) 171 }) 172 } 173 174 func createTestSingleRerun(ctx context.Context, status buildbucketpb.Status, createdTime time.Time, os string) *model.TestSingleRerun { 175 tsr := &model.TestSingleRerun{ 176 ID: 0, 177 LUCIBuild: model.LUCIBuild{ 178 BuildID: 0, 179 Project: "chromium", 180 CreateTime: createdTime, 181 Status: status, 182 }, 183 Dimensions: &pb.Dimensions{ 184 Dimensions: []*pb.Dimension{{ 185 Key: "os", 186 Value: os, 187 }}, 188 }, 189 } 190 So(datastore.Put(ctx, tsr), ShouldBeNil) 191 datastore.GetTestable(ctx).CatchupIndexes() 192 return tsr 193 } 194 195 func createCompileReruns(ctx context.Context, status buildbucketpb.Status, createdTime time.Time, os string) (*model.CompileRerunBuild, *model.SingleRerun) { 196 crb := &model.CompileRerunBuild{ 197 LuciBuild: model.LuciBuild{ 198 Project: "chromium", 199 CreateTime: createdTime, 200 Status: status, 201 }, 202 } 203 So(datastore.Put(ctx, crb), ShouldBeNil) 204 sr := &model.SingleRerun{ 205 RerunBuild: datastore.KeyForObj(ctx, crb), 206 Dimensions: &pb.Dimensions{Dimensions: []*pb.Dimension{{Key: "os", Value: os}}}, 207 } 208 So(datastore.Put(ctx, sr), ShouldBeNil) 209 datastore.GetTestable(ctx).CatchupIndexes() 210 return crb, sr 211 } 212 213 func setDailyLimit(ctx context.Context, limit int) { 214 projectCfg := config.CreatePlaceholderProjectConfig() 215 projectCfg.TestAnalysisConfig.DailyLimit = uint32(limit) 216 cfg := map[string]*configpb.ProjectConfig{ 217 "chromium": projectCfg, 218 "chrome": projectCfg, 219 } 220 So(config.SetTestProjectConfig(ctx, cfg), ShouldBeNil) 221 }