Skip to content

Commit dc5ac88

Browse files
ben-claytonDawn LUCI CQ
authored andcommitted
[tools][cts] Substantial roller changes
Avoid collisions by processing 'KEEP' blocks before non-keep. The former cannot be changed by the tool, so if expectations are added before the KEEP block is seen, then we'd end up with a collision. Simplify the expectation parser. Removes the concept of an 'empty' expectation chunk. This results in the expectation file with double-newlines being folded to a single newline, but simplifies the code a bunch. Remove tag validation of `expectations.txt`. This had false-positives, as you simply cannot statically know whether two tag sets collide, as proper collision detection requires knowledge of the variants being run. We now just rely on typ's collision detection. Remove emission of 'SLOW' tests. These are now in a separate expectation file, and will fail validation if emitted into `expectations.txt`. Change the pattern matching of 'untriaged failures' to be more lenient. This is required to match the new style in the expectations file. Also wrap the comment in a horizontal `###` line. More aggressively collapse nodes with partially failing children to reduce expectation spam. Add a bunch more logging and add progress bars for the expensive operations. Change-Id: Ie14cb89790ed32f0e3f3721be5b573113611edc0 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/151921 Reviewed-by: Antonio Maiorano <[email protected]> Auto-Submit: Ben Clayton <[email protected]> Kokoro: Kokoro <[email protected]> Commit-Queue: Ben Clayton <[email protected]>
1 parent c511ffb commit dc5ac88

File tree

12 files changed

+265
-325
lines changed

12 files changed

+265
-325
lines changed

tools/src/cmd/cts/common/config.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"encoding/json"
2020
"fmt"
2121
"io/ioutil"
22-
"time"
2322

2423
"dawn.googlesource.com/dawn/tools/src/buildbucket"
2524
"github.com/tidwall/jsonc"
@@ -33,8 +32,6 @@ type Config struct {
3332
Test struct {
3433
// The ResultDB string prefix for CTS tests.
3534
Prefixes []string
36-
// The time threshold used to classify tests as slow.
37-
SlowThreshold time.Duration
3835
}
3936
// Gerrit holds configuration for Dawn's Gerrit server.
4037
Gerrit struct {

tools/src/cmd/cts/common/results.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ func (r *ResultSource) GetResults(ctx context.Context, cfg Config, auth auth.Opt
126126

127127
// Obtain the patchset's results, kicking a build if there are no results
128128
// already available.
129-
log.Printf("fetching results from cl %v ps %v...", ps.Change, ps.Patchset)
130129
builds, err := GetOrStartBuildsAndWait(ctx, cfg, *ps, bb, "", false)
131130
if err != nil {
132131
return nil, err
@@ -157,11 +156,13 @@ func CacheResults(
157156
dir := fileutils.ExpandHome(cacheDir)
158157
path := filepath.Join(dir, strconv.Itoa(ps.Change), fmt.Sprintf("ps-%v.txt", ps.Patchset))
159158
if _, err := os.Stat(path); err == nil {
159+
log.Printf("loading cached results from cl %v ps %v...", ps.Change, ps.Patchset)
160160
return result.Load(path)
161161
}
162162
cachePath = path
163163
}
164164

165+
log.Printf("fetching results from cl %v ps %v...", ps.Change, ps.Patchset)
165166
results, err := GetResults(ctx, cfg, rdb, builds)
166167
if err != nil {
167168
return nil, err
@@ -241,10 +242,6 @@ func GetResults(
241242
}
242243
}
243244

244-
if status == result.Pass && duration > cfg.Test.SlowThreshold {
245-
status = result.Slow
246-
}
247-
248245
results = append(results, result.Result{
249246
Query: query.Parse(testName),
250247
Status: status,

tools/src/cmd/cts/roll/roll.go

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ type rollerFlags struct {
7777
rebuild bool // Rebuild the expectations file from scratch
7878
preserve bool // If false, abandon past roll changes
7979
sendToGardener bool // If true, automatically send to the gardener for review
80-
parentSwarmingRunId string
80+
parentSwarmingRunID string
81+
maxAttempts int
8182
}
8283

8384
type cmd struct {
@@ -105,7 +106,8 @@ func (c *cmd) RegisterFlags(ctx context.Context, cfg common.Config) ([]string, e
105106
flag.BoolVar(&c.flags.rebuild, "rebuild", false, "rebuild the expectation file from scratch")
106107
flag.BoolVar(&c.flags.preserve, "preserve", false, "do not abandon existing rolls")
107108
flag.BoolVar(&c.flags.sendToGardener, "send-to-gardener", false, "send the CL to the WebGPU gardener for review")
108-
flag.StringVar(&c.flags.parentSwarmingRunId, "parent-swarming-run-id", "", "parent swarming run id. All triggered tasks will be children of this task and will be canceled if the parent is canceled.")
109+
flag.StringVar(&c.flags.parentSwarmingRunID, "parent-swarming-run-id", "", "parent swarming run id. All triggered tasks will be children of this task and will be canceled if the parent is canceled.")
110+
flag.IntVar(&c.flags.maxAttempts, "max-attempts", 3, "number of update attempts before giving up")
109111
return nil, nil
110112
}
111113

@@ -169,7 +171,7 @@ func (c *cmd) Run(ctx context.Context, cfg common.Config) error {
169171
flags: c.flags,
170172
auth: auth,
171173
bb: bb,
172-
parentSwarmingRunId: c.flags.parentSwarmingRunId,
174+
parentSwarmingRunID: c.flags.parentSwarmingRunID,
173175
rdb: rdb,
174176
git: git,
175177
gerrit: gerrit,
@@ -185,7 +187,7 @@ type roller struct {
185187
flags rollerFlags
186188
auth auth.Options
187189
bb *buildbucket.Buildbucket
188-
parentSwarmingRunId string
190+
parentSwarmingRunID string
189191
rdb *resultsdb.ResultsDB
190192
git *git.Git
191193
gerrit *gerrit.Gerrit
@@ -237,16 +239,13 @@ func (r *roller) roll(ctx context.Context) error {
237239
return fmt.Errorf("failed to load expectations: %v", err)
238240
}
239241

240-
// If the user requested a full rebuild of the expecations, strip out
242+
// If the user requested a full rebuild of the expectations, strip out
241243
// everything but comment chunks.
242244
if r.flags.rebuild {
243245
rebuilt := ex.Clone()
244246
rebuilt.Chunks = rebuilt.Chunks[:0]
245247
for _, c := range ex.Chunks {
246-
switch {
247-
case c.IsBlankLine():
248-
rebuilt.MaybeAddBlankLine()
249-
case c.IsCommentOnly():
248+
if c.IsCommentOnly() {
250249
rebuilt.Chunks = append(rebuilt.Chunks, c)
251250
}
252251
}
@@ -333,12 +332,11 @@ func (r *roller) roll(ctx context.Context) error {
333332
}
334333

335334
// Begin main roll loop
336-
const maxAttempts = 3
337335
results := result.List{}
338336
for attempt := 0; ; attempt++ {
339337
// Kick builds
340338
log.Printf("building (attempt %v)...\n", attempt)
341-
builds, err := common.GetOrStartBuildsAndWait(ctx, r.cfg, ps, r.bb, r.parentSwarmingRunId, false)
339+
builds, err := common.GetOrStartBuildsAndWait(ctx, r.cfg, ps, r.bb, r.parentSwarmingRunID, false)
342340
if err != nil {
343341
return err
344342
}
@@ -397,7 +395,7 @@ func (r *roller) roll(ctx context.Context) error {
397395
return fmt.Errorf("failed to update change '%v': %v", changeID, err)
398396
}
399397

400-
if attempt >= maxAttempts {
398+
if attempt >= r.flags.maxAttempts {
401399
err := fmt.Errorf("CTS failed after %v attempts.\nGiving up", attempt)
402400
r.gerrit.Comment(ps, err.Error(), nil)
403401
return err
@@ -417,10 +415,10 @@ func (r *roller) roll(ctx context.Context) error {
417415
return err
418416
}
419417

420-
type StructuredJsonResponse struct {
418+
type StructuredJSONResponse struct {
421419
Emails []string
422420
}
423-
var jsonRes StructuredJsonResponse
421+
var jsonRes StructuredJSONResponse
424422
if err := json.Unmarshal(jsonResponse, &jsonRes); err != nil {
425423
return err
426424
}

tools/src/cmd/cts/update/update.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"flag"
2020
"fmt"
2121
"io/ioutil"
22+
"log"
2223
"os"
2324
"strings"
2425

@@ -79,31 +80,37 @@ func (c *cmd) Run(ctx context.Context, cfg common.Config) error {
7980
}
8081

8182
// Fetch the results
83+
log.Println("fetching results...")
8284
results, err := c.flags.results.GetResults(ctx, cfg, auth)
8385
if err != nil {
8486
return err
8587
}
8688

8789
// Merge to remove duplicates
90+
log.Println("removing duplicate results...")
8891
results = result.Merge(results)
8992

9093
// Load the expectations file
94+
log.Println("loading expectations...")
9195
ex, err := expectations.Load(c.flags.expectations)
9296
if err != nil {
9397
return err
9498
}
9599

100+
log.Println("loading test list...")
96101
testlist, err := loadTestList(common.DefaultTestListPath())
97102
if err != nil {
98103
return err
99104
}
100105

106+
log.Println("validating...")
101107
if diag := ex.Validate(); diag.NumErrors() > 0 {
102108
diag.Print(os.Stdout, c.flags.expectations)
103109
return fmt.Errorf("validation failed")
104110
}
105111

106112
// Update the expectations file with the results
113+
log.Println("updating expectations...")
107114
diag, err := ex.Update(results, testlist)
108115
if err != nil {
109116
return err

tools/src/cts/expectations/expectations.go

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ package expectations
2121
import (
2222
"fmt"
2323
"io"
24-
"io/ioutil"
2524
"os"
2625
"sort"
2726
"strings"
@@ -86,7 +85,7 @@ type Expectations []Expectation
8685

8786
// Load loads the expectation file at 'path', returning a Content.
8887
func Load(path string) (Content, error) {
89-
content, err := ioutil.ReadFile(path)
88+
content, err := os.ReadFile(path)
9089
if err != nil {
9190
return Content{}, err
9291
}
@@ -122,27 +121,13 @@ func (c Content) Empty() bool {
122121
return len(c.Chunks) == 0
123122
}
124123

125-
// EndsInBlankLine returns true if the Content ends with a blank line
126-
func (c Content) EndsInBlankLine() bool {
127-
return !c.Empty() && c.Chunks[len(c.Chunks)-1].IsBlankLine()
128-
}
129-
130-
// MaybeAddBlankLine appends a new blank line to the content, if the content
131-
// does not already end in a blank line.
132-
func (c *Content) MaybeAddBlankLine() {
133-
if !c.Empty() && !c.EndsInBlankLine() {
134-
c.Chunks = append(c.Chunks, Chunk{})
135-
}
136-
}
137-
138124
// Write writes the Content, in textual form, to the writer w.
139125
func (c Content) Write(w io.Writer) error {
140-
for _, chunk := range c.Chunks {
141-
if len(chunk.Comments) == 0 && len(chunk.Expectations) == 0 {
126+
for i, chunk := range c.Chunks {
127+
if i > 0 {
142128
if _, err := fmt.Fprintln(w); err != nil {
143129
return err
144130
}
145-
continue
146131
}
147132
for _, comment := range chunk.Comments {
148133
if _, err := fmt.Fprintln(w, comment); err != nil {
@@ -182,11 +167,6 @@ func (c Chunk) IsCommentOnly() bool {
182167
return len(c.Comments) > 0 && len(c.Expectations) == 0
183168
}
184169

185-
// IsBlankLine returns true if the Chunk has no comments or expectations.
186-
func (c Chunk) IsBlankLine() bool {
187-
return len(c.Comments) == 0 && len(c.Expectations) == 0
188-
}
189-
190170
// Clone returns a deep-copy of the Chunk
191171
func (c Chunk) Clone() Chunk {
192172
comments := make([]string, len(c.Comments))

tools/src/cts/expectations/parse.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ func Parse(path, body string) (Content, error) {
7373
if i > 0 {
7474
switch {
7575
case
76-
lastLineType == blank && lineType != blank, // blank -> !blank
7776
lastLineType != blank && lineType == blank, // !blank -> blank
7877
lastLineType == expectation && lineType != expectation: // expectation -> comment
7978
flush()

tools/src/cts/expectations/parse_test.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ func TestParse(t *testing.T) {
6060
# a comment`,
6161
expect: expectations.Content{
6262
Chunks: []expectations.Chunk{
63-
{},
6463
{Comments: []string{`# a comment`}},
6564
},
6665
},
@@ -70,7 +69,6 @@ func TestParse(t *testing.T) {
7069
in: "\r\n# a comment",
7170
expect: expectations.Content{
7271
Chunks: []expectations.Chunk{
73-
{},
7472
{Comments: []string{`# a comment`}},
7573
},
7674
},
@@ -98,7 +96,6 @@ func TestParse(t *testing.T) {
9896
expect: expectations.Content{
9997
Chunks: []expectations.Chunk{
10098
{Comments: []string{`# comment 1`}},
101-
{},
10299
{Comments: []string{`# comment 2`}},
103100
},
104101
},
@@ -113,7 +110,6 @@ func TestParse(t *testing.T) {
113110
expect: expectations.Content{
114111
Chunks: []expectations.Chunk{
115112
{Comments: []string{`# comment 1`}},
116-
{},
117113
{Comments: []string{`# comment 2`}},
118114
},
119115
},
@@ -334,7 +330,6 @@ crbug.com/123 abc_def [ Skip ]
334330
},
335331
},
336332
},
337-
{},
338333
{Comments: []string{`### comment 2`}},
339334
},
340335
},
@@ -358,9 +353,7 @@ crbug.com/456 [ Mac ] ghi_jkl [ PASS ]
358353
expect: expectations.Content{
359354
Chunks: []expectations.Chunk{
360355
{Comments: []string{`# comment 1`}},
361-
{},
362356
{Comments: []string{`# comment 2`, `# comment 3`}},
363-
{},
364357
{
365358
Expectations: []expectations.Expectation{
366359
{
@@ -372,7 +365,6 @@ crbug.com/456 [ Mac ] ghi_jkl [ PASS ]
372365
},
373366
},
374367
},
375-
{},
376368
{
377369
Comments: []string{`# comment 4`, `# comment 5`},
378370
Expectations: []expectations.Expectation{
@@ -386,7 +378,6 @@ crbug.com/456 [ Mac ] ghi_jkl [ PASS ]
386378
},
387379
},
388380
{Comments: []string{`# comment 6`}},
389-
{},
390381
{Comments: []string{`# comment 7`}},
391382
},
392383
},
@@ -409,7 +400,6 @@ crbug.com/456 [ Mac ] ghi_jkl [ PASS ]
409400
`,
410401
expect: expectations.Content{
411402
Chunks: []expectations.Chunk{
412-
{},
413403
{Comments: []string{
414404
`# BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)`,
415405
`# Devices`,

0 commit comments

Comments
 (0)