OLD | NEW |
(Empty) | |
| 1 package model |
| 2 |
| 3 import ( |
| 4 "encoding/json" |
| 5 "errors" |
| 6 "strings" |
| 7 ) |
| 8 |
| 9 // FullResult represents "full_results.json". |
| 10 type FullResult struct { |
| 11 Version int `json:"version"` |
| 12 Builder string `json:"builder_name"` |
| 13 BuildNumber Number `json:"build_number"` |
| 14 SecondsEpoch int64 `json:"seconds_since_epoch"` |
| 15 Tests FullTest `json:"tests"` |
| 16 FailuresByType map[string]int `json:"num_failures_by_type"` |
| 17 |
| 18 // These fields are optional. |
| 19 |
| 20 ChromiumRev *string `json:"chromium_revision,omitempty"` |
| 21 PathDelim *string `json:"path_delimiter,omitempty"` |
| 22 Interrupted *bool `json:"interrupted,omitempty"` |
| 23 BlinkRev *string `json:"blink_revision,omitempty"` |
| 24 |
| 25 // These fields are layout test specific. |
| 26 |
| 27 PrettyPatch *bool `json:"has_pretty_patch,omitempty"` |
| 28 Wdiff *bool `json:"has_wdiff"` |
| 29 LayoutTestsDir *string `json:"layout_tests_dir,omitempty"` |
| 30 PixelTestsEnabled *bool `json:"pixel_tests_enabled,omitempty"` |
| 31 |
| 32 // These fields are deprecated. However, uploaders still produce them: |
| 33 // https://chromium.googlesource.com/chromium/src/+/530b8ac05b53ddc56a
3787ad69bb1a843eee2f95/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/mo
dels/test_run_results.py#172 |
| 34 |
| 35 Fixable *int `json:"fixable,omitempty"` |
| 36 NumFlaky *int `json:"num_flaky,omitempty"` |
| 37 NumPasses *int `json:"num_passes,omitempty"` |
| 38 NumRegressions *int `json:"num_regressions,omitempty"` |
| 39 Skips *int `json:"skips,omitempty"` |
| 40 } |
| 41 |
| 42 // AggregateResult converts fr to an AggregateResult. The returned |
| 43 // AggregateResult does not share references to objects referenced |
| 44 // by fr. |
| 45 func (fr *FullResult) AggregateResult() (AggregateResult, error) { |
| 46 cRev := []string{} |
| 47 if fr.ChromiumRev != nil { |
| 48 cRev = append(cRev, *fr.ChromiumRev) |
| 49 } |
| 50 |
| 51 failuresByType := make(map[string][]int) |
| 52 for k, v := range fr.FailuresByType { |
| 53 failuresByType[k] = []int{v} |
| 54 } |
| 55 |
| 56 tests, err := fr.Tests.AggregateTest() |
| 57 if err != nil { |
| 58 return AggregateResult{}, err |
| 59 } |
| 60 |
| 61 return AggregateResult{ |
| 62 Version: ResultsVersion, |
| 63 Builder: fr.Builder, |
| 64 BuilderInfo: &BuilderInfo{ |
| 65 SecondsEpoch: []int64{fr.SecondsEpoch}, |
| 66 BuildNumbers: []Number{fr.BuildNumber}, |
| 67 ChromeRevs: cRev, |
| 68 Tests: tests, |
| 69 FailureMap: FailureLongNames, |
| 70 FailuresByType: failuresByType, |
| 71 }, |
| 72 }, nil |
| 73 } |
| 74 |
| 75 // FullTest represents Tests in a FullResult. |
| 76 type FullTest map[string]Node |
| 77 |
| 78 var _ Node = (FullTest)(nil) |
| 79 |
| 80 func (ft FullTest) node() {} |
| 81 |
| 82 // AggregateTest converts ft to an AggregateTest. The returned |
| 83 // AggregateTest does not share references to objects referenced |
| 84 // by ft. |
| 85 func (ft FullTest) AggregateTest() (AggregateTest, error) { |
| 86 var aggr AggregateTest |
| 87 |
| 88 for k, v := range ft { |
| 89 if l, ok := v.(*FullTestLeaf); ok { |
| 90 aggrLeaf, err := l.AggregateTestLeaf() |
| 91 if err != nil { |
| 92 return nil, err |
| 93 } |
| 94 |
| 95 if aggr == nil { |
| 96 aggr = AggregateTest{} |
| 97 } |
| 98 aggr[k] = &aggrLeaf |
| 99 continue |
| 100 } |
| 101 |
| 102 child, ok := v.(FullTest) |
| 103 if !ok { |
| 104 return nil, errors.New("model: expected FullTest") |
| 105 } |
| 106 |
| 107 next, err := child.AggregateTest() |
| 108 if err != nil { |
| 109 return nil, err |
| 110 } |
| 111 |
| 112 if aggr == nil { |
| 113 aggr = AggregateTest{} |
| 114 } |
| 115 aggr[k] = next |
| 116 } |
| 117 |
| 118 return aggr, nil |
| 119 } |
| 120 |
| 121 // MarshalJSON marshals ft into JSON. |
| 122 func (ft *FullTest) MarshalJSON() ([]byte, error) { |
| 123 if ft == nil { |
| 124 return json.Marshal(ft) |
| 125 } |
| 126 |
| 127 m := make(map[string]*json.RawMessage) |
| 128 |
| 129 for k, v := range *ft { |
| 130 b, err := json.Marshal(&v) |
| 131 if err != nil { |
| 132 return nil, err |
| 133 } |
| 134 raw := json.RawMessage(b) |
| 135 m[k] = &raw |
| 136 } |
| 137 |
| 138 return json.Marshal(m) |
| 139 } |
| 140 |
| 141 // UnmarshalJSON unmarshals the supplied data into ft. |
| 142 func (ft *FullTest) UnmarshalJSON(data []byte) error { |
| 143 var m map[string]interface{} |
| 144 if err := json.Unmarshal(data, &m); err != nil { |
| 145 return err |
| 146 } |
| 147 if ft == nil { |
| 148 return errors.New("model: UnmarshalJSON: nil *FullTest") |
| 149 } |
| 150 if *ft == nil { |
| 151 *ft = FullTest{} |
| 152 } |
| 153 return ft.constructTree(m) |
| 154 } |
| 155 |
| 156 // constructTree constructs the tree of Nodes from the supplied map. |
| 157 // constructTree returns an error if the receiver is nil. |
| 158 func (ft *FullTest) constructTree(m map[string]interface{}) error { |
| 159 for k, v := range m { |
| 160 mm, ok := v.(map[string]interface{}) |
| 161 if !ok { |
| 162 continue |
| 163 } |
| 164 |
| 165 if isFullTestLeaf(mm) { |
| 166 l, err := makeFullTestLeaf(mm) |
| 167 if err != nil { |
| 168 return err |
| 169 } |
| 170 if *ft == nil { |
| 171 *ft = FullTest{} |
| 172 } |
| 173 (*ft)[k] = l |
| 174 continue |
| 175 } |
| 176 |
| 177 var child FullTest |
| 178 if err := child.constructTree(mm); err != nil { |
| 179 return err |
| 180 } |
| 181 if *ft == nil { |
| 182 *ft = FullTest{} |
| 183 } |
| 184 (*ft)[k] = child |
| 185 } |
| 186 |
| 187 return nil |
| 188 } |
| 189 |
| 190 // isFullTestLeaf returns true if the supplied map likely represents a |
| 191 // FullTestLeaf. |
| 192 func isFullTestLeaf(m map[string]interface{}) bool { |
| 193 for _, val := range m { |
| 194 if _, ok := val.(map[string]interface{}); ok { |
| 195 return false |
| 196 } |
| 197 } |
| 198 return true |
| 199 } |
| 200 |
| 201 // makeFullTestLeaf returns a FullTestLeaf from the supplied map. |
| 202 func makeFullTestLeaf(m map[string]interface{}) (*FullTestLeaf, error) { |
| 203 l := &FullTestLeaf{} |
| 204 b, err := json.Marshal(m) |
| 205 if err != nil { |
| 206 return nil, err |
| 207 } |
| 208 err = json.Unmarshal(b, &l) |
| 209 return l, err |
| 210 } |
| 211 |
| 212 // FullTestLeaf represents the results for a particular test name. |
| 213 type FullTestLeaf struct { |
| 214 Actual []string `json:"-"` |
| 215 Expected []string `json:"-"` |
| 216 |
| 217 // These fields are optional. |
| 218 |
| 219 Runtime *float64 `json:"time,omitempty"` // In seconds. |
| 220 Bugs []string `json:"bugs"` |
| 221 Unexpected *bool `json:"is_unexpected,omitempty"` |
| 222 |
| 223 // These fields are layout test specific. |
| 224 |
| 225 RepaintOverlay *bool `json:"has_repaint_overlay,omitempty"` |
| 226 MissingAudio *bool `json:"is_missing_audio,omitempty"` |
| 227 MissingText *bool `json:"is_missing_text,omitempty"` |
| 228 MissingVideo *bool `json:"is_missing_video,omitempty"` |
| 229 UsedTestHarness *bool `json:"is_testharness_test,omitempty"` |
| 230 ReferenceTestType *string `json:"reftest_type,omitempty"` |
| 231 } |
| 232 |
| 233 var _ Node = (*FullTestLeaf)(nil) |
| 234 |
| 235 func (l *FullTestLeaf) node() {} |
| 236 |
| 237 // fullTestLeafAlias helps unmarshal and marshal FullTestLeaf. |
| 238 type fullTestLeafAlias FullTestLeaf |
| 239 |
| 240 // testResultAux helps unmarshal and marshal FullTestLeaf. |
| 241 type testResultAux struct { |
| 242 Actual string `json:"actual"` |
| 243 Expected string `json:"expected"` |
| 244 *fullTestLeafAlias |
| 245 } |
| 246 |
| 247 // AggregateTestLeaf converts l to AggregateTestLeaf. The returned |
| 248 // AggregateTestLeaf does not share references to objects |
| 249 // referenced by l. |
| 250 // |
| 251 // The returned error is always nil, but callers should check the |
| 252 // error anyway because this behavior may change in the future. |
| 253 func (l *FullTestLeaf) AggregateTestLeaf() (AggregateTestLeaf, error) { |
| 254 var ret AggregateTestLeaf |
| 255 |
| 256 expected := strings.Join(l.Expected, " ") |
| 257 actual := strings.Join(l.Actual, " ") |
| 258 |
| 259 if expected != "PASS" && expected != "NOTRUN" { |
| 260 ret.Expected = make([]string, len(l.Expected)) |
| 261 copy(ret.Expected, l.Expected) |
| 262 } |
| 263 |
| 264 var shortFailures string |
| 265 if (expected != "SKIP" && actual == "SKIP") || expected == "NOTRUN" { |
| 266 shortFailures = "Y" |
| 267 } else { |
| 268 for _, f := range l.Actual { |
| 269 val, ok := FailureShortNames[f] |
| 270 if ok { |
| 271 shortFailures += val |
| 272 } else { |
| 273 shortFailures += "U" |
| 274 } |
| 275 } |
| 276 } |
| 277 ret.Results = []ResultSummary{{1, shortFailures}} |
| 278 |
| 279 if len(l.Bugs) > 0 { |
| 280 ret.Bugs = make([]string, len(l.Bugs)) |
| 281 copy(ret.Bugs, l.Bugs) |
| 282 } |
| 283 |
| 284 var time float64 |
| 285 if l.Runtime != nil { |
| 286 time = *l.Runtime |
| 287 } |
| 288 ret.Runtimes = []RuntimeSummary{{1, time}} |
| 289 |
| 290 return ret, nil |
| 291 } |
| 292 |
| 293 // MarshalJSON marshals l into JSON. |
| 294 func (l *FullTestLeaf) MarshalJSON() ([]byte, error) { |
| 295 aux := testResultAux{fullTestLeafAlias: (*fullTestLeafAlias)(l)} |
| 296 aux.Actual = strings.Join(l.Actual, " ") |
| 297 aux.Expected = strings.Join(l.Expected, " ") |
| 298 return json.Marshal(&aux) |
| 299 } |
| 300 |
| 301 // UnmarshalJSON unmarshals the supplied data into l. |
| 302 func (l *FullTestLeaf) UnmarshalJSON(data []byte) error { |
| 303 aux := testResultAux{fullTestLeafAlias: (*fullTestLeafAlias)(l)} |
| 304 if err := json.Unmarshal(data, &aux); err != nil { |
| 305 return err |
| 306 } |
| 307 l.Actual = strings.Split(aux.Actual, " ") |
| 308 l.Expected = strings.Split(aux.Expected, " ") |
| 309 return nil |
| 310 } |
| 311 |
| 312 // Times represents "times_ms.json". |
| 313 type Times map[string]float64 |
OLD | NEW |