| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "context" |
| "fmt" |
| "path" |
| "regexp" |
| "strings" |
| |
| "go.chromium.org/luci/common/errors" |
| "go.chromium.org/luci/common/logging" |
| |
| "infra/rts" |
| "infra/rts/filegraph/git" |
| "infra/rts/presubmit/eval" |
| evalpb "infra/rts/presubmit/eval/proto" |
| ) |
| |
| // The range of the number of changed files to enable RTS. |
| // If the number of changed files is not in this range, then RTS is disabled. |
| // Note that 99.3% of developer-authored git commits change <= 100 files, see bit.ly/chromium-rts |
| const ( |
| minChangedFiles = 1 |
| maxChangedFiles = 100 |
| ) |
| |
| // mustAlwaysRunTest returns true if the test file must never be skipped. |
| func mustAlwaysRunTest(testFile string) bool { |
| switch { |
| // Always run all third-party tests (never skip them), |
| // except //third_party/blink which is actually first party. |
| case strings.Contains(testFile, "/third_party/") && !strings.HasPrefix(testFile, "//third_party/blink/"): |
| return true |
| |
| case testFile == "//third_party/blink/web_tests/wpt_internal/webgpu/cts.html": |
| // Most of cts.html commits are auto-generated. |
| // https://source.chromium.org/chromium/chromium/src/+/HEAD:third_party/blink/web_tests/wpt_internal/webgpu/cts.html;l=5;bpv=1;bpt=0 |
| // cts.html does not have meaningful edges in the file graph. |
| return true |
| |
| default: |
| return false |
| } |
| } |
| |
| var ( |
| // requireAllTests is a list of patterns of files that require running all |
| // tests. |
| requireAllTests = []string{ |
| // A CL changes the way tests run or their configurations. |
| "//testing/.+", |
| |
| // The full list of modified files is not available, and the |
| // graph does not include DEPSed file changes anyway. |
| "//DEPS", |
| |
| // MB CLs change the way tests run or their configurations. |
| "//tools/mb/.+", |
| } |
| requireAllTestsRegexp = regexp.MustCompile(fmt.Sprintf("^(%s)$", strings.Join(requireAllTests, "|"))) |
| |
| disableRTS = errors.BoolTag{Key: errors.NewTagKey("skip RTS")} |
| ) |
| |
| // selectTests calls skipFile for test files that should be skipped. |
| // May return an error annotated with disableRTS tag and the message explaining |
| // why RTS was disabled. |
| func (r *selectRun) selectTests(skipFile func(*TestFile) error) (err error) { |
| // Disable RTS if the number of files is unusual. |
| if len(r.changedFiles) < minChangedFiles || len(r.changedFiles) > maxChangedFiles { |
| return errors.Reason( |
| "%d files were changed, which is outside of [%d, %d] range", |
| len(r.changedFiles), |
| minChangedFiles, |
| maxChangedFiles, |
| ).Tag(disableRTS).Err() |
| } |
| |
| // Check if any of the changed files requires all tests. |
| for f := range r.changedFiles { |
| if requireAllTestsRegexp.MatchString(f) { |
| return errors.Reason( |
| "%q was changed, which matches regexp %s", |
| f, |
| requireAllTests, |
| ).Tag(disableRTS).Err() |
| } |
| } |
| r.strategy.Select(r.changedFiles.ToSlice(), func(fileName string) (keepGoing bool) { |
| file, ok := r.testFiles[fileName] |
| if !ok { |
| return true |
| } |
| err = skipFile(file) |
| return err == nil |
| }) |
| return |
| } |
| |
| func (r *createModelRun) evalStrategy(er *git.EdgeReader) eval.Strategy { |
| s := &git.SelectionStrategy{ |
| Graph: r.fg, |
| EdgeReader: er, |
| OnTestNotFound: func(ctx context.Context, tv *evalpb.TestVariant) { |
| if strings.Contains(path.Base(tv.FileName), "autogen") { |
| // This file is autogenerated. |
| return |
| } |
| logging.Warningf(ctx, "test file not found: %s", tv.FileName) |
| }, |
| } |
| |
| return func(ctx context.Context, in eval.Input, out *eval.Output) error { |
| for _, f := range in.ChangedFiles { |
| switch { |
| case f.Repo != "https://chromium.googlesource.com/chromium/src": |
| return errors.Reason("unexpected repo %q", f.Repo).Err() |
| case requireAllTestsRegexp.MatchString(f.Path): |
| return nil |
| } |
| } |
| |
| if err := s.SelectEval(ctx, in, out); err != nil { |
| return err |
| } |
| |
| // No matter what filegraph said, never skip certain tests. |
| for i, tv := range in.TestVariants { |
| if mustAlwaysRunTest(tv.FileName) { |
| out.TestVariantAffectedness[i] = rts.Affectedness{Distance: 0} |
| } |
| } |
| return nil |
| } |
| } |