Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(993)

Side by Side Diff: go/src/infra/monitoring/analyzer/test_failures.go

Issue 1125263004: dispatcher: fix test result parsing, build ranges for failure alerts, other fixes (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: added comments, some bounds/type assertion checks Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package analyzer 5 package analyzer
6 6
7 import ( 7 import (
8 "fmt" 8 "fmt"
9 "strings" 9 "strings"
10 10
11 "infra/monitoring/client" 11 "infra/monitoring/client"
12 ) 12 )
13 13
14 // TestFailureAnalyzer determines the reasons, if any, for a test step failure. 14 // TestFailureAnalyzer determines the reasons, if any, for a test step failure.
15 type TestFailureAnalyzer struct { 15 type TestFailureAnalyzer struct {
16 Client client.Client 16 Client client.Client
17 } 17 }
18 18
19 // Analyze returns the reasons, if any, for the step if it was a test failure. A lso returns 19 // Analyze returns the reasons, if any, for the step if it was a test failure. A lso returns
20 // whether or not the analyzer applied to the failure and any error in encounter ed while 20 // whether or not the analyzer applied to the failure and any error in encounter ed while
21 // running. 21 // running.
22 func (a *TestFailureAnalyzer) Analyze(f stepFailure) (*StepAnalyzerResult, error ) { 22 func (a *TestFailureAnalyzer) Analyze(f stepFailure) (*StepAnalyzerResult, error ) {
23 ret := &StepAnalyzerResult{} 23 ret := &StepAnalyzerResult{}
24 » if !strings.HasSuffix(f.step.Name, "tests") { 24 » // Some test steps have names like "webkit_tests iOS(dbug)" so we look a t the first
25 » // term before the space, if there is one.
26 » s := strings.Split(f.step.Name, " ")
27 » if !strings.HasSuffix(s[0], "tests") {
25 return ret, nil 28 return ret, nil
26 } 29 }
27 ret.Recognized = true 30 ret.Recognized = true
28 31
29 testResults, err := a.Client.TestResults(f.masterName, f.builderName, f. step.Name, f.build.Number) 32 testResults, err := a.Client.TestResults(f.masterName, f.builderName, f. step.Name, f.build.Number)
30 if err != nil { 33 if err != nil {
34 ret.Reasons = append(ret.Reasons, f.step.Name)
31 return ret, fmt.Errorf("Error fetching test results: %v", err) 35 return ret, fmt.Errorf("Error fetching test results: %v", err)
32 } 36 }
33 37
34 if len(testResults.Tests) == 0 { 38 if len(testResults.Tests) == 0 {
35 » » log.Errorf("No test results for %v", f) 39 » » return ret, fmt.Errorf("No test results for %v", f)
36 } 40 }
37 log.Infof("%d test results", len(testResults.Tests))
38 41
39 for testName, testResults := range testResults.Tests { 42 for testName, testResults := range testResults.Tests {
40 » » // This string splitting logic was copied from builder_alerts. 43 » » res, ok := testResults.(map[string]interface{})
41 » » // I'm not sure it's really necessary since the strings appear t o always 44 » » if !ok {
42 » » // be either "PASS" or "FAIL" in practice. 45 » » » log.Errorf("Couldn't convert test results to map: %s", t estName)
43 » » expected := strings.Split(testResults.Expected, " ") 46 » » » continue
44 » » actual := strings.Split(testResults.Actual, " ") 47 » » }
45 » » ue := unexpected(expected, actual) 48
49 » » // If res is a simple top-level test result, just check it here.
50 » » if res["expected"] != nil || res["actual"] != nil {
51 » » » expected := strings.Split(res["expected"].(string), " ")
52 » » » actual := strings.Split(res["actual"].(string), " ")
53 » » » ue := unexpected(expected, actual)
54 » » » if len(ue) > 0 && res["bugs"] == nil {
55 » » » » ret.Reasons = append(ret.Reasons, testName)
56 » » » }
57 » » » continue
58 » » }
59
60 » » // res is not a simple top-level test result, so recurse to find
61 » » // the actual results.
62 » » ue, err := traverseResults(testName, res)
63 » » if err != nil {
64 » » » return nil, err
65 » » }
46 if len(ue) > 0 { 66 if len(ue) > 0 {
67 // Don't give too many reasons, just the top level test/ suite names.
68 // TODO: figure out a way to coalesce the unexpected res ults list into
69 // something compact but still meaningful in an alerting context.
47 ret.Reasons = append(ret.Reasons, testName) 70 ret.Reasons = append(ret.Reasons, testName)
48 } 71 }
49 } 72 }
50 return ret, nil 73 return ret, nil
51 } 74 }
75
76 // testResults json is an arbitrarily deep tree, whose nodes are the actual
77 // test results, so we recurse to find them.
78 func traverseResults(parent string, testResults map[string]interface{}) ([]strin g, error) {
79 ret := []string{}
80 for testName, testResults := range testResults {
81 res, ok := testResults.(map[string]interface{})
82 if !ok {
83 return nil, fmt.Errorf("Couldn't convert test results to map: %s/%s", parent, testName)
84 }
85 // First check if results is actually results, or just another b ranch
86 // in the tree of test results
87 // Uuuuugly.
88 if res["expected"] == nil || res["actual"] == nil {
89 // Assume it's a branch.
90 res, err := traverseResults(fmt.Sprintf("%s/%s", parent, testName), res)
91 if err != nil {
92 return ret, err
93 }
94 ret = append(ret, res...)
95 continue
96 }
97
98 expected := strings.Split(res["expected"].(string), " ")
99 actual := strings.Split(res["actual"].(string), " ")
100 ue := unexpected(expected, actual)
101 if len(ue) > 0 && res["bugs"] == nil {
102 ret = append(ret, fmt.Sprintf("%s/%s: %+v vs %+v", paren t, testName, expected, actual))
103 }
104 }
105 return ret, nil
106 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698