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

Side by Side Diff: go/src/infra/appengine/test-results/model/aggregate_result.go

Issue 2252623002: test-results: package model: Add type TestList (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@cl-ing_upload
Patch Set: Created 4 years, 4 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
« no previous file with comments | « no previous file | go/src/infra/appengine/test-results/model/aggregate_result_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | go/src/infra/appengine/test-results/model/aggregate_result_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698