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  }