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

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

Issue 2234353002: test-results: package model: Add full_result.go and tests (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Improve coverage 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
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698