| 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" |
| 11 "strings" | 11 "strings" |
| 12 ) | 12 ) |
| 13 | 13 |
| 14 // ResultsVersion is the latest version of the JSON format that "results.json" | 14 // ResultsVersion is the latest version of the JSON format that "results.json" |
| 15 // and "results-small.json" files are using. | 15 // and "results-small.json" files are using. |
| 16 const ResultsVersion = 4 | 16 const ResultsVersion = 4 |
| 17 | 17 |
| 18 var _ TestNode = (AggregateTest)(nil) | 18 var ( |
| 19 var _ TestNode = (*AggregateTestLeaf)(nil) | 19 » // CleanPrefix is the prefix that CleanJSON removes. |
| 20 » CleanPrefix = []byte("ADD_RESULTS(") |
| 21 » // CleanSuffix is the suffix that CleanJSON removes. |
| 22 » CleanSuffix = []byte(");") |
| 23 ) |
| 20 | 24 |
| 21 // CleanTestResultsJSON returns the result of removing a known prefix | 25 // CleanJSON returns the result of removing CleanPrefix |
| 22 // and suffix from the contents in r. If the prefix or suffix do not exist, | 26 // and CleanSuffix from the contents in r. If either |
| 23 // the returned io.Reader has the same contents as r. | 27 // CleanPrefix or CleanSuffix does not exist, the returned |
| 24 func CleanTestResultsJSON(r io.Reader) (io.Reader, error) { | 28 // io.Reader has the same contents as r. |
| 25 » pre := []byte("ADD_RESULTS(") | 29 func CleanJSON(r io.Reader) (io.Reader, error) { |
| 26 » suf := []byte(");") | |
| 27 | |
| 28 b, err := ioutil.ReadAll(r) | 30 b, err := ioutil.ReadAll(r) |
| 29 if err != nil { | 31 if err != nil { |
| 30 return nil, err | 32 return nil, err |
| 31 } | 33 } |
| 32 | 34 |
| 33 » if bytes.HasPrefix(b, pre) && bytes.HasSuffix(b, suf) { | 35 » if bytes.HasPrefix(b, CleanPrefix) && bytes.HasSuffix(b, CleanSuffix) { |
| 34 » » result := bytes.TrimPrefix(b, pre) | 36 » » result := bytes.TrimPrefix(b, CleanPrefix) |
| 35 » » result = bytes.TrimSuffix(result, suf) | 37 » » result = bytes.TrimSuffix(result, CleanSuffix) |
| 36 return bytes.NewReader(result), nil | 38 return bytes.NewReader(result), nil |
| 37 } | 39 } |
| 38 | 40 |
| 39 return bytes.NewReader(b), nil | 41 return bytes.NewReader(b), nil |
| 40 } | 42 } |
| 41 | 43 |
| 42 // AggregateResult represents "results.json" and "results-small.json" files. | 44 // AggregateResult represents "results.json" and "results-small.json" files. |
| 43 // The Builder field must be set to the expected builder name before unmarshalin
g. | 45 // The Builder field must be set to the expected builder name before unmarshalin
g. |
| 44 type AggregateResult struct { | 46 type AggregateResult struct { |
| 45 Version int | 47 Version int |
| 46 Builder string | 48 Builder string |
| 47 *BuilderInfo | 49 *BuilderInfo |
| 48 } | 50 } |
| 49 | 51 |
| 50 // BuilderInfo represents aggregate information about a builder. | 52 // BuilderInfo represents aggregate information for a builder. |
| 51 type BuilderInfo struct { | 53 type BuilderInfo struct { |
| 52 // SecondsEpoch is the start time of tests expressed in seconds from | 54 // SecondsEpoch is the start time of tests expressed in seconds from |
| 53 // the Unix epoch. | 55 // the Unix epoch. |
| 54 SecondsEpoch []int64 `json:"secondsSinceEpoch"` | 56 SecondsEpoch []int64 `json:"secondsSinceEpoch"` |
| 55 | 57 |
| 56 » BlinkRevs []number `json:"blinkRevision"` | 58 » // BlinkRevs is list of Blink revisions. |
| 57 » BuildNumbers []number `json:"buildNumbers"` | 59 » BlinkRevs []Number `json:"blinkRevision"` |
| 60 |
| 61 » // BuildNumbers is list of build numbers. |
| 62 » BuildNumbers []Number `json:"buildNumbers"` |
| 58 | 63 |
| 59 // ChromeRevs is a list of Chrome/Chromium revisions. | 64 // ChromeRevs is a list of Chrome/Chromium revisions. |
| 60 // The elements are strings because they can either be revision | 65 // The elements are strings because they can either be revision |
| 61 // numbers or commit hashes. | 66 // numbers or commit hashes. |
| 62 ChromeRevs []string `json:"chromeRevision"` | 67 ChromeRevs []string `json:"chromeRevision"` |
| 63 | 68 |
| 64 // Tests is the test trie. The leaf nodes will be of type | 69 // Tests is the test trie. The leaf nodes will be of type |
| 65 // AggregateTestLeaf. | 70 // AggregateTestLeaf. |
| 66 Tests AggregateTest `json:"tests"` | 71 Tests AggregateTest `json:"tests"` |
| 67 | 72 |
| 68 // FailureMap is a map from long failure types to short failure | 73 // FailureMap is a map from long failure types to short failure |
| 69 // types. Usually, it resembles LongFailureTypes. | 74 // types. Usually, it resembles LongFailureTypes. |
| 70 FailureMap map[string]string `json:"failure_map"` | 75 FailureMap map[string]string `json:"failure_map"` |
| 71 | 76 |
| 72 // FailuresByType is a map from long failure type to | 77 // FailuresByType is a map from long failure type to |
| 73 // number of failures. | 78 // number of failures. |
| 74 FailuresByType map[string][]int `json:"num_failures_by_type,omitempty"` | 79 FailuresByType map[string][]int `json:"num_failures_by_type,omitempty"` |
| 75 | 80 |
| 76 // FixableCounts represents test failures in a legacy format, | 81 // FixableCounts represents test failures in a legacy format, |
| 77 // and is usually nil. | 82 // and is usually nil. |
| 78 // | 83 // |
| 79 // It is included here because it may be needed to compute | 84 // It is included here because it may be needed to compute |
| 80 // FailuresByType when unmarshaling JSON. | 85 // FailuresByType when unmarshaling JSON. |
| 81 FixableCounts []map[string]int `json:"fixableCounts,omitempty"` | 86 FixableCounts []map[string]int `json:"fixableCounts,omitempty"` |
| 82 } | 87 } |
| 83 | 88 |
| 89 // MarshalJSON marshal ag into JSON. |
| 84 func (ag *AggregateResult) MarshalJSON() ([]byte, error) { | 90 func (ag *AggregateResult) MarshalJSON() ([]byte, error) { |
| 85 v, err := json.Marshal(ag.Version) | 91 v, err := json.Marshal(ag.Version) |
| 86 if err != nil { | 92 if err != nil { |
| 87 return nil, err | 93 return nil, err |
| 88 } | 94 } |
| 89 vRaw := json.RawMessage(v) | 95 vRaw := json.RawMessage(v) |
| 90 | 96 |
| 91 info := *ag.BuilderInfo | 97 info := *ag.BuilderInfo |
| 92 | 98 |
| 93 // If FailuresByType exists, do not include FixableCounts | 99 // If FailuresByType exists, do not include FixableCounts |
| 94 // because it is deprecated. | 100 // because it is deprecated. |
| 95 » if ag.FailuresByType != nil { | 101 » if info.FailuresByType != nil { |
| 96 info.FixableCounts = nil | 102 info.FixableCounts = nil |
| 97 } | 103 } |
| 98 | 104 |
| 99 b, err := json.Marshal(&info) | 105 b, err := json.Marshal(&info) |
| 100 if err != nil { | 106 if err != nil { |
| 101 return nil, err | 107 return nil, err |
| 102 } | 108 } |
| 103 infoRaw := json.RawMessage(b) | 109 infoRaw := json.RawMessage(b) |
| 104 | 110 |
| 105 return json.Marshal(map[string]*json.RawMessage{ | 111 return json.Marshal(map[string]*json.RawMessage{ |
| (...skipping 20 matching lines...) Expand all Loading... |
| 126 n, err := parseVersion(m) | 132 n, err := parseVersion(m) |
| 127 if err != nil { | 133 if err != nil { |
| 128 return err | 134 return err |
| 129 } | 135 } |
| 130 ag.Version = n | 136 ag.Version = n |
| 131 | 137 |
| 132 // BuilderInfo. | 138 // BuilderInfo. |
| 133 | 139 |
| 134 raw, ok := m[ag.Builder] | 140 raw, ok := m[ag.Builder] |
| 135 if !ok { | 141 if !ok { |
| 136 » » return fmt.Errorf("model: missing builder: %s", ag.Builder) | 142 » » return fmt.Errorf("model: missing builder %q", ag.Builder) |
| 137 } | 143 } |
| 138 | 144 |
| 139 var info *BuilderInfo | 145 var info *BuilderInfo |
| 140 if err := json.Unmarshal(raw, &info); err != nil { | 146 if err := json.Unmarshal(raw, &info); err != nil { |
| 141 return err | 147 return err |
| 142 } | 148 } |
| 143 ag.BuilderInfo = info | 149 ag.BuilderInfo = info |
| 144 | 150 |
| 145 if err := ag.checkFields(); err != nil { | 151 if err := ag.checkFields(); err != nil { |
| 146 return err | 152 return err |
| (...skipping 16 matching lines...) Expand all Loading... |
| 163 return 0, fmt.Errorf("model: version %q must be int: %v", vStr,
err) | 169 return 0, fmt.Errorf("model: version %q must be int: %v", vStr,
err) |
| 164 } | 170 } |
| 165 return n, nil | 171 return n, nil |
| 166 } | 172 } |
| 167 | 173 |
| 168 type fieldError struct { | 174 type fieldError struct { |
| 169 Name string // Name of field. | 175 Name string // Name of field. |
| 170 Value interface{} // Invalid value in the field that caused error. | 176 Value interface{} // Invalid value in the field that caused error. |
| 171 } | 177 } |
| 172 | 178 |
| 173 func (f fieldError) Error() string { | 179 func (f *fieldError) Error() string { |
| 174 » return fmt.Sprintf("model: field %s has invalid value: %v", f.Name, f.Va
lue) | 180 » return fmt.Sprintf("model: field %q has invalid value: %v", f.Name, f.Va
lue) |
| 175 } | 181 } |
| 176 | 182 |
| 177 func (ag *AggregateResult) checkFields() error { | 183 func (ag *AggregateResult) checkFields() error { |
| 178 if ag.Version > ResultsVersion { | 184 if ag.Version > ResultsVersion { |
| 179 » » return fieldError{"Version", ag.Version} | 185 » » return &fieldError{"Version", ag.Version} |
| 180 } | 186 } |
| 181 if ag.BuilderInfo == nil { | 187 if ag.BuilderInfo == nil { |
| 182 » » return fieldError{"BuilderInfo", ag.BuilderInfo} | 188 » » return &fieldError{"BuilderInfo", ag.BuilderInfo} |
| 183 } | 189 } |
| 184 return nil | 190 return nil |
| 185 } | 191 } |
| 186 | 192 |
| 187 func (info *BuilderInfo) checkFields() error { | 193 func (info *BuilderInfo) checkFields() error { |
| 188 if info.SecondsEpoch == nil { | |
| 189 return fieldError{"SecondsEpoch", info.SecondsEpoch} | |
| 190 } | |
| 191 if info.BlinkRevs == nil { | |
| 192 return fieldError{"BlinkRevs", info.BlinkRevs} | |
| 193 } | |
| 194 if info.BuildNumbers == nil { | 194 if info.BuildNumbers == nil { |
| 195 » » return fieldError{"BuildNumbers", info.BuildNumbers} | 195 » » return &fieldError{"BuildNumbers", info.BuildNumbers} |
| 196 » } | |
| 197 » if info.ChromeRevs == nil { | |
| 198 » » return fieldError{"ChromeRevs", info.ChromeRevs} | |
| 199 » } | |
| 200 » if info.Tests == nil { | |
| 201 » » return fieldError{"Tests", info.Tests} | |
| 202 » } | |
| 203 » if info.FailureMap == nil { | |
| 204 » » return fieldError{"FailureMap", info.FailureMap} | |
| 205 » } | |
| 206 » if info.FailuresByType == nil && info.FixableCounts == nil { | |
| 207 » » return fieldError{"FailuresByType", info.FailuresByType} | |
| 208 } | 196 } |
| 209 return nil | 197 return nil |
| 210 } | 198 } |
| 211 | 199 |
| 212 // computeFailuresByType computes info.FailuresByType from info.FixableCounts. | 200 // computeFailuresByType computes info.FailuresByType from info.FixableCounts. |
| 213 // The function has no effect if info.FailuresByType is already non-nil. | 201 // The function has no effect if info.FailuresByType is already non-nil. |
| 214 func (info *BuilderInfo) computeFailuresByType() error { | 202 func (info *BuilderInfo) computeFailuresByType() error { |
| 215 if info.FailuresByType != nil { | 203 if info.FailuresByType != nil { |
| 216 // Already present. | 204 // Already present. |
| 217 return nil | 205 return nil |
| 218 } | 206 } |
| 219 | 207 |
| 220 if info.FixableCounts == nil { | 208 if info.FixableCounts == nil { |
| 221 return errors.New("model: nil FixableCounts") | 209 return errors.New("model: nil FixableCounts") |
| 222 } | 210 } |
| 223 | 211 |
| 224 res := make(map[string][]int) | 212 res := make(map[string][]int) |
| 225 for _, fc := range info.FixableCounts { | 213 for _, fc := range info.FixableCounts { |
| 226 for short, count := range fc { | 214 for short, count := range fc { |
| 227 long, ok := FailureLongNames[short] | 215 long, ok := FailureLongNames[short] |
| 228 if !ok { | 216 if !ok { |
| 229 » » » » return fmt.Errorf("model: unknown key: %s", shor
t) | 217 » » » » return fmt.Errorf("model: unknown key %q", short
) |
| 230 } | 218 } |
| 231 res[long] = append(res[long], count) | 219 res[long] = append(res[long], count) |
| 232 } | 220 } |
| 233 } | 221 } |
| 234 | 222 |
| 235 info.FailuresByType = res | 223 info.FailuresByType = res |
| 236 return nil | 224 return nil |
| 237 } | 225 } |
| 238 | 226 |
| 239 // AggregateTest represents Tests in a AggregateResult. | 227 // AggregateTest represents Tests in a AggregateResult. |
| 240 type AggregateTest map[string]TestNode | 228 type AggregateTest map[string]Node |
| 241 | 229 |
| 242 func (at AggregateTest) Walk(fn func(key string, node TestNode) error) { | 230 var _ Node = (AggregateTest)(nil) |
| 243 » for k, v := range at { | |
| 244 » » if leaf, ok := v.(*AggregateTestLeaf); ok { | |
| 245 » » » if err := fn(k, leaf); err != nil { | |
| 246 » » » » return | |
| 247 » » » } | |
| 248 » » » continue | |
| 249 » » } | |
| 250 | 231 |
| 251 » » if child, ok := v.(AggregateTest); ok { | 232 func (at AggregateTest) node() {} |
| 252 » » » if err := fn(k, child); err != nil { | 233 |
| 253 » » » » return | 234 // Walk performs a depth-first traversal of the Nodes reachable |
| 254 » » » } | 235 // from the receiver, calling fn each time. The Node in fn |
| 255 » » » child.Walk(fn) | 236 // is guaranteed to be either AggregateTest or *AggregateTestLeaf. |
| 237 // The traversal order may vary across different runs. |
| 238 func (at AggregateTest) Walk(fn func(key string, node Node)) { |
| 239 » for key, node := range at { |
| 240 » » switch val := node.(type) { |
| 241 » » case *AggregateTestLeaf: |
| 242 » » » fn(key, val) |
| 243 » » case AggregateTest: |
| 244 » » » fn(key, val) |
| 245 » » » val.Walk(fn) |
| 256 } | 246 } |
| 257 } | 247 } |
| 258 } | 248 } |
| 259 | 249 |
| 250 // WalkLeaves is similar to Walk but only calls fn for |
| 251 // *AggregateTestLeaf. |
| 260 func (at AggregateTest) WalkLeaves(fn func(key string, leaf *AggregateTestLeaf))
{ | 252 func (at AggregateTest) WalkLeaves(fn func(key string, leaf *AggregateTestLeaf))
{ |
| 261 » at.Walk(func(k string, node TestNode) error { | 253 » at.Walk(func(key string, node Node) { |
| 262 if leaf, ok := node.(*AggregateTestLeaf); ok { | 254 if leaf, ok := node.(*AggregateTestLeaf); ok { |
| 263 » » » fn(k, leaf) | 255 » » » fn(key, leaf) |
| 264 } | 256 } |
| 265 return nil | |
| 266 }) | 257 }) |
| 267 } | 258 } |
| 268 | 259 |
| 269 func (at AggregateTest) Children() map[string]TestNode { return at } | 260 // ToTestList set the Results and Runtimes fields of all the |
| 270 func (at AggregateTest) testnode() {} | 261 // AggregateTestLeaf under the receiver AggregateTest to nil. |
| 271 | |
| 272 func (at AggregateTest) ToTestList() { | 262 func (at AggregateTest) ToTestList() { |
| 273 at.WalkLeaves(func(_ string, leaf *AggregateTestLeaf) { | 263 at.WalkLeaves(func(_ string, leaf *AggregateTestLeaf) { |
| 274 leaf.Results = nil | 264 leaf.Results = nil |
| 275 leaf.Runtimes = nil | 265 leaf.Runtimes = nil |
| 276 }) | 266 }) |
| 277 } | 267 } |
| 278 | 268 |
| 269 // MarshalJSON marshals at into JSON. |
| 279 func (at *AggregateTest) MarshalJSON() ([]byte, error) { | 270 func (at *AggregateTest) MarshalJSON() ([]byte, error) { |
| 280 if at == nil { | 271 if at == nil { |
| 281 return json.Marshal(nil) | 272 return json.Marshal(nil) |
| 282 } | 273 } |
| 283 | 274 |
| 284 m := make(map[string]*json.RawMessage) | 275 m := make(map[string]*json.RawMessage) |
| 285 | 276 |
| 286 for k, v := range *at { | 277 for k, v := range *at { |
| 287 b, err := json.Marshal(&v) | 278 b, err := json.Marshal(&v) |
| 288 if err != nil { | 279 if err != nil { |
| 289 return nil, err | 280 return nil, err |
| 290 } | 281 } |
| 291 raw := json.RawMessage(b) | 282 raw := json.RawMessage(b) |
| 292 m[k] = &raw | 283 m[k] = &raw |
| 293 } | 284 } |
| 294 | 285 |
| 295 return json.Marshal(m) | 286 return json.Marshal(m) |
| 296 } | 287 } |
| 297 | 288 |
| 289 // UnmarshalJSON unmarshals the supplied data into at. |
| 298 func (at *AggregateTest) UnmarshalJSON(data []byte) error { | 290 func (at *AggregateTest) UnmarshalJSON(data []byte) error { |
| 299 var m map[string]interface{} | 291 var m map[string]interface{} |
| 300 if err := json.Unmarshal(data, &m); err != nil { | 292 if err := json.Unmarshal(data, &m); err != nil { |
| 301 return err | 293 return err |
| 302 } | 294 } |
| 303 if at == nil { | 295 if at == nil { |
| 304 » » return errors.New("model: UnmarshalJSON on nil *AggregateTest") | 296 » » return errors.New("model: UnmarshalJSON: nil *AggregateTest") |
| 305 } | 297 } |
| 306 if *at == nil { | 298 if *at == nil { |
| 307 *at = AggregateTest{} | 299 *at = AggregateTest{} |
| 308 } | 300 } |
| 309 return at.constructTree(m) | 301 return at.constructTree(m) |
| 310 } | 302 } |
| 311 | 303 |
| 312 // constructTree constructs the tree of test nodes from the supplied map. | 304 // constructTree constructs the tree of Nodes from the supplied map. |
| 313 func (at *AggregateTest) constructTree(m map[string]interface{}) error { | 305 func (at *AggregateTest) constructTree(m map[string]interface{}) error { |
| 314 for k, v := range m { | 306 for k, v := range m { |
| 315 mm, ok := v.(map[string]interface{}) | 307 mm, ok := v.(map[string]interface{}) |
| 316 if !ok { | 308 if !ok { |
| 317 continue | 309 continue |
| 318 } | 310 } |
| 319 | 311 |
| 320 if isAggregateTestLeaf(mm) { | 312 if isAggregateTestLeaf(mm) { |
| 321 l, err := makeAggregateTestLeaf(mm) | 313 l, err := makeAggregateTestLeaf(mm) |
| 322 if err != nil { | 314 if err != nil { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 367 } | 359 } |
| 368 | 360 |
| 369 // AggregateTestLeaf is the summary of test results at the l of a tests trie. | 361 // AggregateTestLeaf is the summary of test results at the l of a tests trie. |
| 370 type AggregateTestLeaf struct { | 362 type AggregateTestLeaf struct { |
| 371 Results []ResultSummary | 363 Results []ResultSummary |
| 372 Runtimes []RuntimeSummary | 364 Runtimes []RuntimeSummary |
| 373 Expected []string | 365 Expected []string |
| 374 Bugs []string | 366 Bugs []string |
| 375 } | 367 } |
| 376 | 368 |
| 369 func (l *AggregateTestLeaf) node() {} |
| 370 |
| 377 // aggregateTestLeafAux is used to marshal and unmarshal AggregateTestLeaf. | 371 // aggregateTestLeafAux is used to marshal and unmarshal AggregateTestLeaf. |
| 378 type aggregateTestLeafAux struct { | 372 type aggregateTestLeafAux struct { |
| 379 Results []ResultSummary `json:"results,omitempty"` | 373 Results []ResultSummary `json:"results,omitempty"` |
| 380 Runtimes []RuntimeSummary `json:"times,omitempty"` | 374 Runtimes []RuntimeSummary `json:"times,omitempty"` |
| 381 Expected *string `json:"expected,omitempty"` | 375 Expected *string `json:"expected,omitempty"` |
| 382 Bugs []string `json:"bugs,omitempty"` | 376 Bugs []string `json:"bugs,omitempty"` |
| 383 } | 377 } |
| 384 | 378 |
| 385 func (l *AggregateTestLeaf) Children() map[string]TestNode { return nil } | 379 // MarshalJSON marshal l into JSON. |
| 386 func (l *AggregateTestLeaf) testnode() {} | |
| 387 | |
| 388 func (l *AggregateTestLeaf) MarshalJSON() ([]byte, error) { | 380 func (l *AggregateTestLeaf) MarshalJSON() ([]byte, error) { |
| 389 aux := aggregateTestLeafAux{ | 381 aux := aggregateTestLeafAux{ |
| 390 Results: l.Results, | 382 Results: l.Results, |
| 391 Runtimes: l.Runtimes, | 383 Runtimes: l.Runtimes, |
| 392 Bugs: l.Bugs, | 384 Bugs: l.Bugs, |
| 393 } | 385 } |
| 394 if s := strings.Join(l.Expected, " "); len(s) > 0 { | 386 if s := strings.Join(l.Expected, " "); len(s) > 0 { |
| 395 aux.Expected = &s | 387 aux.Expected = &s |
| 396 } | 388 } |
| 397 return json.Marshal(&aux) | 389 return json.Marshal(&aux) |
| 398 } | 390 } |
| 399 | 391 |
| 392 // UnmarshalJSON unmarshal the supplied data into l. |
| 400 func (l *AggregateTestLeaf) UnmarshalJSON(data []byte) error { | 393 func (l *AggregateTestLeaf) UnmarshalJSON(data []byte) error { |
| 401 var aux aggregateTestLeafAux | 394 var aux aggregateTestLeafAux |
| 402 if err := json.Unmarshal(data, &aux); err != nil { | 395 if err := json.Unmarshal(data, &aux); err != nil { |
| 403 return err | 396 return err |
| 404 } | 397 } |
| 405 | 398 |
| 406 l.Results = aux.Results | 399 l.Results = aux.Results |
| 407 l.Runtimes = aux.Runtimes | 400 l.Runtimes = aux.Runtimes |
| 408 if aux.Expected != nil { | 401 if aux.Expected != nil { |
| 409 l.Expected = strings.Split(*aux.Expected, " ") | 402 l.Expected = strings.Split(*aux.Expected, " ") |
| 410 } | 403 } |
| 411 l.Bugs = aux.Bugs | 404 l.Bugs = aux.Bugs |
| 412 | 405 |
| 413 return nil | 406 return nil |
| 414 } | 407 } |
| 415 | 408 |
| 416 // defaultFields sets default values for missing/invalid fields. | 409 // defaultFields sets default values for missing/invalid fields. |
| 417 func (l *AggregateTestLeaf) defaultFields() { | 410 func (l *AggregateTestLeaf) defaultFields() { |
| 418 if len(l.Results) == 0 { | 411 if len(l.Results) == 0 { |
| 419 l.Results = []ResultSummary{{1, "N"}} | 412 l.Results = []ResultSummary{{1, "N"}} |
| 420 } | 413 } |
| 421 if len(l.Runtimes) == 0 { | 414 if len(l.Runtimes) == 0 { |
| 422 l.Runtimes = []RuntimeSummary{{1, 0}} | 415 l.Runtimes = []RuntimeSummary{{1, 0}} |
| 423 } | 416 } |
| 424 } | 417 } |
| 425 | 418 |
| 419 // ResultSummary is the type of test failure and count of how many |
| 420 // times the running time occured. |
| 426 type ResultSummary struct { | 421 type ResultSummary struct { |
| 427 Count int | 422 Count int |
| 428 Type string | 423 Type string |
| 429 } | 424 } |
| 430 | 425 |
| 426 // MarshalJSON marshals rs into JSON. |
| 431 func (rs *ResultSummary) MarshalJSON() ([]byte, error) { | 427 func (rs *ResultSummary) MarshalJSON() ([]byte, error) { |
| 432 return json.Marshal([]interface{}{ | 428 return json.Marshal([]interface{}{ |
| 433 rs.Count, | 429 rs.Count, |
| 434 rs.Type, | 430 rs.Type, |
| 435 }) | 431 }) |
| 436 } | 432 } |
| 437 | 433 |
| 434 // UnmarshalJSON unmarshals the provided data into rs. |
| 438 func (rs *ResultSummary) UnmarshalJSON(data []byte) error { | 435 func (rs *ResultSummary) UnmarshalJSON(data []byte) error { |
| 439 var tmp []interface{} | 436 var tmp []interface{} |
| 440 if err := json.Unmarshal(data, &tmp); err != nil { | 437 if err := json.Unmarshal(data, &tmp); err != nil { |
| 441 return err | 438 return err |
| 442 } | 439 } |
| 443 if len(tmp) != 2 { | 440 if len(tmp) != 2 { |
| 444 » » return fmt.Errorf("ResultSummary: wrong length: %d, expect: %d",
len(tmp), 2) | 441 » » return fmt.Errorf("model: UnmarshalJSON: ResultSummary wrong len
gth: %d, expect: %d", len(tmp), 2) |
| 445 } | 442 } |
| 446 | 443 |
| 447 count, ok := tmp[0].(float64) | 444 count, ok := tmp[0].(float64) |
| 448 if !ok { | 445 if !ok { |
| 449 » » return fmt.Errorf("ResultSummary: wrong format: %v", tmp) | 446 » » return fmt.Errorf("model: UnmarshalJSON: ResultSummary wrong typ
e: %v", tmp) |
| 450 } | 447 } |
| 451 rs.Count = int(count) | 448 rs.Count = int(count) |
| 452 | 449 |
| 453 rs.Type, ok = tmp[1].(string) | 450 rs.Type, ok = tmp[1].(string) |
| 454 if !ok { | 451 if !ok { |
| 455 » » return fmt.Errorf("ResultSummary: wrong format: %v", tmp) | 452 » » return fmt.Errorf("model: UnmarshalJSON: ResultSummary wrong typ
e: %v", tmp) |
| 456 } | 453 } |
| 457 | 454 |
| 458 return nil | 455 return nil |
| 459 } | 456 } |
| 460 | 457 |
| 458 // RuntimeSummary is the running time of a test and count of how many |
| 459 // times the running time occured. |
| 461 type RuntimeSummary struct { | 460 type RuntimeSummary struct { |
| 462 Count int | 461 Count int |
| 463 Runtime float64 | 462 Runtime float64 |
| 464 } | 463 } |
| 465 | 464 |
| 465 // MarshalJSON marshals rs into JSON. |
| 466 func (rs *RuntimeSummary) MarshalJSON() ([]byte, error) { | 466 func (rs *RuntimeSummary) MarshalJSON() ([]byte, error) { |
| 467 return json.Marshal([]float64{ | 467 return json.Marshal([]float64{ |
| 468 float64(rs.Count), | 468 float64(rs.Count), |
| 469 rs.Runtime, | 469 rs.Runtime, |
| 470 }) | 470 }) |
| 471 } | 471 } |
| 472 | 472 |
| 473 // UnmarshalJSON unmarshals the provided data into rs. |
| 473 func (rs *RuntimeSummary) UnmarshalJSON(data []byte) error { | 474 func (rs *RuntimeSummary) UnmarshalJSON(data []byte) error { |
| 474 var tmp []float64 | 475 var tmp []float64 |
| 475 if err := json.Unmarshal(data, &tmp); err != nil { | 476 if err := json.Unmarshal(data, &tmp); err != nil { |
| 476 return err | 477 return err |
| 477 } | 478 } |
| 478 if len(tmp) != 2 { | 479 if len(tmp) != 2 { |
| 479 » » return fmt.Errorf("RuntimeSummary: wrong length: %d, expect: %d"
, len(tmp), 2) | 480 » » return fmt.Errorf("model: UnmarshalJSON: RuntimeSummary wrong le
ngth: %d, expect: %d", len(tmp), 2) |
| 480 } | 481 } |
| 481 | 482 |
| 482 rs.Count = int(tmp[0]) | 483 rs.Count = int(tmp[0]) |
| 483 rs.Runtime = tmp[1] | 484 rs.Runtime = tmp[1] |
| 484 return nil | 485 return nil |
| 485 } | 486 } |
| OLD | NEW |