Chromium Code Reviews| 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) { | |
|
estaab
2016/08/12 15:17:20
won't this make a copy of ft since it's not a poin
nishanths
2016/08/12 16:45:58
FullTest's underlying type is a map, so it will be
| |
| 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 = float64(round(*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 |