OLD | NEW |
---|---|
1 package model | 1 package model |
2 | 2 |
3 import ( | 3 import ( |
4 "bytes" | 4 "bytes" |
5 "encoding/json" | 5 "encoding/json" |
6 "errors" | 6 "errors" |
7 "fmt" | 7 "fmt" |
8 "io" | 8 "io" |
9 "io/ioutil" | 9 "io/ioutil" |
10 "strconv" | 10 "strconv" |
(...skipping 24 matching lines...) Expand all Loading... | |
35 if bytes.HasPrefix(b, CleanPrefix) && bytes.HasSuffix(b, CleanSuffix) { | 35 if bytes.HasPrefix(b, CleanPrefix) && bytes.HasSuffix(b, CleanSuffix) { |
36 result := bytes.TrimPrefix(b, CleanPrefix) | 36 result := bytes.TrimPrefix(b, CleanPrefix) |
37 result = bytes.TrimSuffix(result, CleanSuffix) | 37 result = bytes.TrimSuffix(result, CleanSuffix) |
38 return bytes.NewReader(result), nil | 38 return bytes.NewReader(result), nil |
39 } | 39 } |
40 | 40 |
41 return bytes.NewReader(b), nil | 41 return bytes.NewReader(b), nil |
42 } | 42 } |
43 | 43 |
44 // AggregateResult represents "results.json" and "results-small.json" files. | 44 // AggregateResult represents "results.json" and "results-small.json" files. |
45 // The Builder field must be set to the expected builder name before unmarshalin g. | |
46 type AggregateResult struct { | 45 type AggregateResult struct { |
47 Version int | 46 Version int |
48 Builder string | 47 Builder string |
49 *BuilderInfo | 48 *BuilderInfo |
50 } | 49 } |
51 | 50 |
52 // BuilderInfo represents aggregate information for a builder. | 51 // BuilderInfo represents aggregate information for a builder. |
53 type BuilderInfo struct { | 52 type BuilderInfo struct { |
54 // SecondsEpoch is the start time of tests expressed in seconds from | 53 // SecondsEpoch is the start time of tests expressed in seconds from |
55 // the Unix epoch. | 54 // the Unix epoch. |
(...skipping 23 matching lines...) Expand all Loading... | |
79 FailuresByType map[string][]int `json:"num_failures_by_type,omitempty"` | 78 FailuresByType map[string][]int `json:"num_failures_by_type,omitempty"` |
80 | 79 |
81 // FixableCounts represents test failures in a legacy format, | 80 // FixableCounts represents test failures in a legacy format, |
82 // and is usually nil. | 81 // and is usually nil. |
83 // | 82 // |
84 // It is included here because it may be needed to compute | 83 // It is included here because it may be needed to compute |
85 // FailuresByType when unmarshaling JSON. | 84 // FailuresByType when unmarshaling JSON. |
86 FixableCounts []map[string]int `json:"fixableCounts,omitempty"` | 85 FixableCounts []map[string]int `json:"fixableCounts,omitempty"` |
87 } | 86 } |
88 | 87 |
88 // TestList is a representation an AggregateResult in which | |
89 // the Results and Runtimes fields of all the AggregateTestLeafs | |
90 // are set to nil. | |
91 type TestList struct { | |
92 Builder string | |
93 Tests AggregateTest | |
94 } | |
95 | |
96 // MarshalJSON marshals tl into JSON. | |
97 func (tl *TestList) MarshalJSON() ([]byte, error) { | |
98 return json.Marshal(map[string]map[string]AggregateTest{ | |
99 tl.Builder: { | |
100 "tests": tl.Tests, | |
101 }, | |
102 }) | |
103 } | |
104 | |
105 // TestList returns a TestList representation of ag. | |
106 func (ag *AggregateResult) TestList() TestList { | |
107 tl := TestList{ | |
108 Builder: ag.Builder, | |
109 Tests: ag.Tests, | |
110 } | |
111 tl.Tests.WalkLeaves(func(_ string, leaf *AggregateTestLeaf) { | |
Vadim Sh.
2016/08/16 18:36:28
is it ok that function that looks like a getter (T
| |
112 leaf.Results = nil | |
113 leaf.Runtimes = nil | |
114 }) | |
115 return tl | |
116 } | |
117 | |
89 // MarshalJSON marshal ag into JSON. | 118 // MarshalJSON marshal ag into JSON. |
90 func (ag *AggregateResult) MarshalJSON() ([]byte, error) { | 119 func (ag *AggregateResult) MarshalJSON() ([]byte, error) { |
91 v, err := json.Marshal(ag.Version) | 120 v, err := json.Marshal(ag.Version) |
92 if err != nil { | 121 if err != nil { |
93 return nil, err | 122 return nil, err |
94 } | 123 } |
95 vRaw := json.RawMessage(v) | 124 vRaw := json.RawMessage(v) |
96 | 125 |
97 info := *ag.BuilderInfo | 126 info := *ag.BuilderInfo |
98 | 127 |
99 // If FailuresByType exists, do not include FixableCounts | 128 // If FailuresByType exists, do not include FixableCounts |
100 // because it is deprecated. | 129 // because it is deprecated. |
101 if info.FailuresByType != nil { | 130 if info.FailuresByType != nil { |
102 info.FixableCounts = nil | 131 info.FixableCounts = nil |
103 } | 132 } |
104 | 133 |
105 b, err := json.Marshal(&info) | 134 b, err := json.Marshal(&info) |
106 if err != nil { | 135 if err != nil { |
107 return nil, err | 136 return nil, err |
108 } | 137 } |
109 infoRaw := json.RawMessage(b) | 138 infoRaw := json.RawMessage(b) |
110 | 139 |
111 return json.Marshal(map[string]*json.RawMessage{ | 140 return json.Marshal(map[string]*json.RawMessage{ |
112 "version": &vRaw, | 141 "version": &vRaw, |
113 ag.Builder: &infoRaw, | 142 ag.Builder: &infoRaw, |
114 }) | 143 }) |
115 } | 144 } |
116 | 145 |
146 // extractBuilderName gets the builder name from the supplied map. | |
147 // This depends on the fact that AggregateResults are expected to | |
148 // only have two top-level keys: (1) "version" (2) the builder name. | |
149 func extractBuilderName(m map[string]json.RawMessage) (string, error) { | |
150 for k := range m { | |
151 if k != "version" { | |
152 return k, nil | |
153 } | |
154 } | |
155 return "", errors.New("builder name not found") | |
156 } | |
157 | |
117 // UnmarshalJSON decodes JSON data into t. | 158 // UnmarshalJSON decodes JSON data into t. |
118 // | 159 // |
119 // The expected format is a modified version of the format described in the URL | 160 // The expected format is a modified version of the format described in the URL |
120 // below. The modifications account for the structure of results.json and | 161 // below. The modifications account for the structure of results.json and |
121 // results_small.json files in the wild. | 162 // results_small.json files in the wild. |
122 // | 163 // |
123 // https://chromium.googlesource.com/chromium/src/+/c7dd0560d9544a15908239bebc 177410899851ca/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/layout_pac kage/bot_test_expectations.py#45 | 164 // https://chromium.googlesource.com/chromium/src/+/c7dd0560d9544a15908239bebc 177410899851ca/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/layout_pac kage/bot_test_expectations.py#45 |
124 func (ag *AggregateResult) UnmarshalJSON(data []byte) error { | 165 func (ag *AggregateResult) UnmarshalJSON(data []byte) error { |
125 var m map[string]json.RawMessage | 166 var m map[string]json.RawMessage |
126 if err := json.Unmarshal(data, &m); err != nil { | 167 if err := json.Unmarshal(data, &m); err != nil { |
127 return err | 168 return err |
128 } | 169 } |
129 | 170 |
130 // Version. | 171 // Version. |
131 | 172 |
132 n, err := parseVersion(m) | 173 n, err := parseVersion(m) |
133 if err != nil { | 174 if err != nil { |
134 return err | 175 return err |
135 } | 176 } |
136 ag.Version = n | 177 ag.Version = n |
137 | 178 |
179 // Builder name. | |
180 | |
181 builder, err := extractBuilderName(m) | |
182 if err != nil { | |
183 return err | |
184 } | |
185 ag.Builder = builder | |
186 | |
138 // BuilderInfo. | 187 // BuilderInfo. |
139 | 188 |
140 raw, ok := m[ag.Builder] | |
141 if !ok { | |
142 return fmt.Errorf("model: missing builder %q", ag.Builder) | |
143 } | |
144 | |
145 var info *BuilderInfo | 189 var info *BuilderInfo |
146 » if err := json.Unmarshal(raw, &info); err != nil { | 190 » if err := json.Unmarshal(m[builder], &info); err != nil { |
147 return err | 191 return err |
148 } | 192 } |
149 ag.BuilderInfo = info | 193 ag.BuilderInfo = info |
150 | 194 |
151 if err := ag.checkFields(); err != nil { | 195 if err := ag.checkFields(); err != nil { |
152 return err | 196 return err |
153 } | 197 } |
154 if err := info.computeFailuresByType(); err != nil { | 198 if err := info.computeFailuresByType(); err != nil { |
155 return err | 199 return err |
156 } | 200 } |
(...skipping 13 matching lines...) Expand all Loading... | |
170 } | 214 } |
171 return n, nil | 215 return n, nil |
172 } | 216 } |
173 | 217 |
174 type fieldError struct { | 218 type fieldError struct { |
175 Name string // Name of field. | 219 Name string // Name of field. |
176 Value interface{} // Invalid value in the field that caused error. | 220 Value interface{} // Invalid value in the field that caused error. |
177 } | 221 } |
178 | 222 |
179 func (f *fieldError) Error() string { | 223 func (f *fieldError) Error() string { |
180 » return fmt.Sprintf("model: field %q has invalid value: %v", f.Name, f.Va lue) | 224 » return fmt.Sprintf("model: field %q has invalid value: %v (%T)", f.Name, f.Value, f.Value) |
181 } | 225 } |
182 | 226 |
183 func (ag *AggregateResult) checkFields() error { | 227 func (ag *AggregateResult) checkFields() error { |
184 if ag.Version > ResultsVersion { | 228 if ag.Version > ResultsVersion { |
185 return &fieldError{"Version", ag.Version} | 229 return &fieldError{"Version", ag.Version} |
186 } | 230 } |
187 if ag.BuilderInfo == nil { | 231 if ag.BuilderInfo == nil { |
188 return &fieldError{"BuilderInfo", ag.BuilderInfo} | 232 return &fieldError{"BuilderInfo", ag.BuilderInfo} |
189 } | 233 } |
190 return nil | 234 return nil |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
250 // WalkLeaves is similar to Walk but only calls fn for | 294 // WalkLeaves is similar to Walk but only calls fn for |
251 // *AggregateTestLeaf. | 295 // *AggregateTestLeaf. |
252 func (at AggregateTest) WalkLeaves(fn func(key string, leaf *AggregateTestLeaf)) { | 296 func (at AggregateTest) WalkLeaves(fn func(key string, leaf *AggregateTestLeaf)) { |
253 at.Walk(func(key string, node Node) { | 297 at.Walk(func(key string, node Node) { |
254 if leaf, ok := node.(*AggregateTestLeaf); ok { | 298 if leaf, ok := node.(*AggregateTestLeaf); ok { |
255 fn(key, leaf) | 299 fn(key, leaf) |
256 } | 300 } |
257 }) | 301 }) |
258 } | 302 } |
259 | 303 |
260 // ToTestList set the Results and Runtimes fields of all the | |
261 // AggregateTestLeaf under the receiver AggregateTest to nil. | |
262 func (at AggregateTest) ToTestList() { | |
263 at.WalkLeaves(func(_ string, leaf *AggregateTestLeaf) { | |
264 leaf.Results = nil | |
265 leaf.Runtimes = nil | |
266 }) | |
267 } | |
268 | |
269 // MarshalJSON marshals at into JSON. | 304 // MarshalJSON marshals at into JSON. |
270 func (at *AggregateTest) MarshalJSON() ([]byte, error) { | 305 func (at *AggregateTest) MarshalJSON() ([]byte, error) { |
271 if at == nil { | 306 if at == nil { |
272 return json.Marshal(nil) | 307 return json.Marshal(nil) |
273 } | 308 } |
274 | 309 |
275 m := make(map[string]*json.RawMessage) | 310 m := make(map[string]*json.RawMessage) |
276 | 311 |
277 for k, v := range *at { | 312 for k, v := range *at { |
278 b, err := json.Marshal(&v) | 313 b, err := json.Marshal(&v) |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
327 } | 362 } |
328 if *at == nil { | 363 if *at == nil { |
329 *at = AggregateTest{} | 364 *at = AggregateTest{} |
330 } | 365 } |
331 (*at)[k] = child | 366 (*at)[k] = child |
332 } | 367 } |
333 | 368 |
334 return nil | 369 return nil |
335 } | 370 } |
336 | 371 |
337 // isAggregateTestLeaf returns true if the supplied map is likely represents a | 372 // isAggregateTestLeaf returns true if the supplied map is likely an |
338 // AggregateTestLeaf. | 373 // AggregateTestLeaf. |
339 func isAggregateTestLeaf(m map[string]interface{}) bool { | 374 func isAggregateTestLeaf(m map[string]interface{}) bool { |
340 for key, val := range m { | 375 for key, val := range m { |
341 if key == "results" { | 376 if key == "results" { |
342 if _, ok := val.([]interface{}); ok { | 377 if _, ok := val.([]interface{}); ok { |
343 return true | 378 return true |
344 } | 379 } |
345 } | 380 } |
346 } | 381 } |
347 return false | 382 return false |
(...skipping 21 matching lines...) Expand all Loading... | |
369 func (leaf *AggregateTestLeaf) node() {} | 404 func (leaf *AggregateTestLeaf) node() {} |
370 | 405 |
371 // aggregateTestLeafAux is used to marshal and unmarshal AggregateTestLeaf. | 406 // aggregateTestLeafAux is used to marshal and unmarshal AggregateTestLeaf. |
372 type aggregateTestLeafAux struct { | 407 type aggregateTestLeafAux struct { |
373 Results []ResultSummary `json:"results,omitempty"` | 408 Results []ResultSummary `json:"results,omitempty"` |
374 Runtimes []RuntimeSummary `json:"times,omitempty"` | 409 Runtimes []RuntimeSummary `json:"times,omitempty"` |
375 Expected *string `json:"expected,omitempty"` | 410 Expected *string `json:"expected,omitempty"` |
376 Bugs []string `json:"bugs,omitempty"` | 411 Bugs []string `json:"bugs,omitempty"` |
377 } | 412 } |
378 | 413 |
379 // MarshalJSON marshal l into JSON. | 414 // MarshalJSON marshals leaf into JSON. |
380 func (leaf *AggregateTestLeaf) MarshalJSON() ([]byte, error) { | 415 func (leaf *AggregateTestLeaf) MarshalJSON() ([]byte, error) { |
381 aux := aggregateTestLeafAux{ | 416 aux := aggregateTestLeafAux{ |
382 Results: leaf.Results, | 417 Results: leaf.Results, |
383 Runtimes: leaf.Runtimes, | 418 Runtimes: leaf.Runtimes, |
384 Bugs: leaf.Bugs, | 419 Bugs: leaf.Bugs, |
385 } | 420 } |
386 if s := strings.Join(leaf.Expected, " "); len(s) > 0 { | 421 if s := strings.Join(leaf.Expected, " "); len(s) > 0 { |
387 aux.Expected = &s | 422 aux.Expected = &s |
388 } | 423 } |
389 return json.Marshal(&aux) | 424 return json.Marshal(&aux) |
390 } | 425 } |
391 | 426 |
392 // UnmarshalJSON unmarshal the supplied data into l. | 427 // UnmarshalJSON unmarshals the supplied data into leaf. |
393 func (leaf *AggregateTestLeaf) UnmarshalJSON(data []byte) error { | 428 func (leaf *AggregateTestLeaf) UnmarshalJSON(data []byte) error { |
394 var aux aggregateTestLeafAux | 429 var aux aggregateTestLeafAux |
395 if err := json.Unmarshal(data, &aux); err != nil { | 430 if err := json.Unmarshal(data, &aux); err != nil { |
396 return err | 431 return err |
397 } | 432 } |
398 | 433 |
399 leaf.Results = aux.Results | 434 leaf.Results = aux.Results |
400 leaf.Runtimes = aux.Runtimes | 435 leaf.Runtimes = aux.Runtimes |
401 if aux.Expected != nil { | 436 if aux.Expected != nil { |
402 leaf.Expected = strings.Split(*aux.Expected, " ") | 437 leaf.Expected = strings.Split(*aux.Expected, " ") |
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
647 | 682 |
648 func isDebugBuilder(builder string) bool { | 683 func isDebugBuilder(builder string) bool { |
649 for _, s := range []string{"debug", "dbg"} { | 684 for _, s := range []string{"debug", "dbg"} { |
650 if strings.Contains(strings.ToLower(builder), s) { | 685 if strings.Contains(strings.ToLower(builder), s) { |
651 return true | 686 return true |
652 } | 687 } |
653 } | 688 } |
654 return false | 689 return false |
655 } | 690 } |
656 | 691 |
657 // Trim trims the leaves of Tests in ar to the specified size. | 692 // Trim trims ag's fields to the specified size. |
658 func (ag *AggregateResult) Trim(size int) error { | 693 func (ag *AggregateResult) Trim(size int) error { |
659 t := runtimeThresholdNormal | 694 t := runtimeThresholdNormal |
660 | 695 |
661 if isDebugBuilder(ag.Builder) { | 696 if isDebugBuilder(ag.Builder) { |
662 t = runtimeThresholdDebug | 697 t = runtimeThresholdDebug |
663 } | 698 } |
664 | 699 |
700 ag.SecondsEpoch = ag.SecondsEpoch[:min(size, len(ag.SecondsEpoch))] | |
701 ag.BlinkRevs = ag.BlinkRevs[:min(size, len(ag.BlinkRevs))] | |
702 ag.ChromeRevs = ag.ChromeRevs[:min(size, len(ag.ChromeRevs))] | |
703 ag.BuildNumbers = ag.BuildNumbers[:min(size, len(ag.BuildNumbers))] | |
704 | |
665 return ag.Tests.trim(size, t) | 705 return ag.Tests.trim(size, t) |
666 } | 706 } |
667 | 707 |
668 func (at AggregateTest) trim(size int, threshold float64) error { | 708 func (at AggregateTest) trim(size int, threshold float64) error { |
669 for k, v := range at { | 709 for k, v := range at { |
670 if leaf, ok := v.(*AggregateTestLeaf); ok { | 710 if leaf, ok := v.(*AggregateTestLeaf); ok { |
671 leaf.trim(size) | 711 leaf.trim(size) |
672 if leaf.shouldDelete(threshold) { | 712 if leaf.shouldDelete(threshold) { |
673 delete(at, k) | 713 delete(at, k) |
674 } | 714 } |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
736 } | 776 } |
737 } | 777 } |
738 for _, r := range leaf.Runtimes { | 778 for _, r := range leaf.Runtimes { |
739 if r.Runtime >= threshold { | 779 if r.Runtime >= threshold { |
740 return false | 780 return false |
741 } | 781 } |
742 } | 782 } |
743 | 783 |
744 return true | 784 return true |
745 } | 785 } |
OLD | NEW |