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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: go/src/infra/appengine/test-results/model/full_result.go
diff --git a/go/src/infra/appengine/test-results/model/full_result.go b/go/src/infra/appengine/test-results/model/full_result.go
new file mode 100644
index 0000000000000000000000000000000000000000..0caebf7b5508369acd2c754f4691a730f201d27b
--- /dev/null
+++ b/go/src/infra/appengine/test-results/model/full_result.go
@@ -0,0 +1,313 @@
+package model
+
+import (
+ "encoding/json"
+ "errors"
+ "strings"
+)
+
+// FullResult represents "full_results.json".
+type FullResult struct {
+ Version int `json:"version"`
+ Builder string `json:"builder_name"`
+ BuildNumber Number `json:"build_number"`
+ SecondsEpoch int64 `json:"seconds_since_epoch"`
+ Tests FullTest `json:"tests"`
+ FailuresByType map[string]int `json:"num_failures_by_type"`
+
+ // These fields are optional.
+
+ ChromiumRev *string `json:"chromium_revision,omitempty"`
+ PathDelim *string `json:"path_delimiter,omitempty"`
+ Interrupted *bool `json:"interrupted,omitempty"`
+ BlinkRev *string `json:"blink_revision,omitempty"`
+
+ // These fields are layout test specific.
+
+ PrettyPatch *bool `json:"has_pretty_patch,omitempty"`
+ Wdiff *bool `json:"has_wdiff"`
+ LayoutTestsDir *string `json:"layout_tests_dir,omitempty"`
+ PixelTestsEnabled *bool `json:"pixel_tests_enabled,omitempty"`
+
+ // These fields are deprecated. However, uploaders still produce them:
+ // https://chromium.googlesource.com/chromium/src/+/530b8ac05b53ddc56a3787ad69bb1a843eee2f95/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py#172
+
+ Fixable *int `json:"fixable,omitempty"`
+ NumFlaky *int `json:"num_flaky,omitempty"`
+ NumPasses *int `json:"num_passes,omitempty"`
+ NumRegressions *int `json:"num_regressions,omitempty"`
+ Skips *int `json:"skips,omitempty"`
+}
+
+// AggregateResult converts fr to an AggregateResult. The returned
+// AggregateResult does not share references to objects referenced
+// by fr.
+func (fr *FullResult) AggregateResult() (AggregateResult, error) {
+ cRev := []string{}
+ if fr.ChromiumRev != nil {
+ cRev = append(cRev, *fr.ChromiumRev)
+ }
+
+ failuresByType := make(map[string][]int)
+ for k, v := range fr.FailuresByType {
+ failuresByType[k] = []int{v}
+ }
+
+ tests, err := fr.Tests.AggregateTest()
+ if err != nil {
+ return AggregateResult{}, err
+ }
+
+ return AggregateResult{
+ Version: ResultsVersion,
+ Builder: fr.Builder,
+ BuilderInfo: &BuilderInfo{
+ SecondsEpoch: []int64{fr.SecondsEpoch},
+ BuildNumbers: []Number{fr.BuildNumber},
+ ChromeRevs: cRev,
+ Tests: tests,
+ FailureMap: FailureLongNames,
+ FailuresByType: failuresByType,
+ },
+ }, nil
+}
+
+// FullTest represents Tests in a FullResult.
+type FullTest map[string]Node
+
+var _ Node = (FullTest)(nil)
+
+func (ft FullTest) node() {}
+
+// AggregateTest converts ft to an AggregateTest. The returned
+// AggregateTest does not share references to objects referenced
+// by ft.
+func (ft FullTest) AggregateTest() (AggregateTest, error) {
+ var aggr AggregateTest
+
+ for k, v := range ft {
+ if l, ok := v.(*FullTestLeaf); ok {
+ aggrLeaf, err := l.AggregateTestLeaf()
+ if err != nil {
+ return nil, err
+ }
+
+ if aggr == nil {
+ aggr = AggregateTest{}
+ }
+ aggr[k] = &aggrLeaf
+ continue
+ }
+
+ child, ok := v.(FullTest)
+ if !ok {
+ return nil, errors.New("model: expected FullTest")
+ }
+
+ next, err := child.AggregateTest()
+ if err != nil {
+ return nil, err
+ }
+
+ if aggr == nil {
+ aggr = AggregateTest{}
+ }
+ aggr[k] = next
+ }
+
+ return aggr, nil
+}
+
+// MarshalJSON marshals ft into JSON.
+func (ft *FullTest) MarshalJSON() ([]byte, error) {
+ if ft == nil {
+ return json.Marshal(ft)
+ }
+
+ m := make(map[string]*json.RawMessage)
+
+ for k, v := range *ft {
+ b, err := json.Marshal(&v)
+ if err != nil {
+ return nil, err
+ }
+ raw := json.RawMessage(b)
+ m[k] = &raw
+ }
+
+ return json.Marshal(m)
+}
+
+// UnmarshalJSON unmarshals the supplied data into ft.
+func (ft *FullTest) UnmarshalJSON(data []byte) error {
+ var m map[string]interface{}
+ if err := json.Unmarshal(data, &m); err != nil {
+ return err
+ }
+ if ft == nil {
+ return errors.New("model: UnmarshalJSON: nil *FullTest")
+ }
+ if *ft == nil {
+ *ft = FullTest{}
+ }
+ return ft.constructTree(m)
+}
+
+// constructTree constructs the tree of Nodes from the supplied map.
+// constructTree returns an error if the receiver is nil.
+func (ft *FullTest) constructTree(m map[string]interface{}) error {
+ for k, v := range m {
+ mm, ok := v.(map[string]interface{})
+ if !ok {
+ continue
+ }
+
+ if isFullTestLeaf(mm) {
+ l, err := makeFullTestLeaf(mm)
+ if err != nil {
+ return err
+ }
+ if *ft == nil {
+ *ft = FullTest{}
+ }
+ (*ft)[k] = l
+ continue
+ }
+
+ var child FullTest
+ if err := child.constructTree(mm); err != nil {
+ return err
+ }
+ if *ft == nil {
+ *ft = FullTest{}
+ }
+ (*ft)[k] = child
+ }
+
+ return nil
+}
+
+// isFullTestLeaf returns true if the supplied map likely represents a
+// FullTestLeaf.
+func isFullTestLeaf(m map[string]interface{}) bool {
+ for _, val := range m {
+ if _, ok := val.(map[string]interface{}); ok {
+ return false
+ }
+ }
+ return true
+}
+
+// makeFullTestLeaf returns a FullTestLeaf from the supplied map.
+func makeFullTestLeaf(m map[string]interface{}) (*FullTestLeaf, error) {
+ l := &FullTestLeaf{}
+ b, err := json.Marshal(m)
+ if err != nil {
+ return nil, err
+ }
+ err = json.Unmarshal(b, &l)
+ return l, err
+}
+
+// FullTestLeaf represents the results for a particular test name.
+type FullTestLeaf struct {
+ Actual []string `json:"-"`
+ Expected []string `json:"-"`
+
+ // These fields are optional.
+
+ Runtime *float64 `json:"time,omitempty"` // In seconds.
+ Bugs []string `json:"bugs"`
+ Unexpected *bool `json:"is_unexpected,omitempty"`
+
+ // These fields are layout test specific.
+
+ RepaintOverlay *bool `json:"has_repaint_overlay,omitempty"`
+ MissingAudio *bool `json:"is_missing_audio,omitempty"`
+ MissingText *bool `json:"is_missing_text,omitempty"`
+ MissingVideo *bool `json:"is_missing_video,omitempty"`
+ UsedTestHarness *bool `json:"is_testharness_test,omitempty"`
+ ReferenceTestType *string `json:"reftest_type,omitempty"`
+}
+
+var _ Node = (*FullTestLeaf)(nil)
+
+func (l *FullTestLeaf) node() {}
+
+// fullTestLeafAlias helps unmarshal and marshal FullTestLeaf.
+type fullTestLeafAlias FullTestLeaf
+
+// testResultAux helps unmarshal and marshal FullTestLeaf.
+type testResultAux struct {
+ Actual string `json:"actual"`
+ Expected string `json:"expected"`
+ *fullTestLeafAlias
+}
+
+// AggregateTestLeaf converts l to AggregateTestLeaf. The returned
+// AggregateTestLeaf does not share references to objects
+// referenced by l.
+//
+// The returned error is always nil, but callers should check the
+// error anyway because this behavior may change in the future.
+func (l *FullTestLeaf) AggregateTestLeaf() (AggregateTestLeaf, error) {
+ var ret AggregateTestLeaf
+
+ expected := strings.Join(l.Expected, " ")
+ actual := strings.Join(l.Actual, " ")
+
+ if expected != "PASS" && expected != "NOTRUN" {
+ ret.Expected = make([]string, len(l.Expected))
+ copy(ret.Expected, l.Expected)
+ }
+
+ var shortFailures string
+ if (expected != "SKIP" && actual == "SKIP") || expected == "NOTRUN" {
+ shortFailures = "Y"
+ } else {
+ for _, f := range l.Actual {
+ val, ok := FailureShortNames[f]
+ if ok {
+ shortFailures += val
+ } else {
+ shortFailures += "U"
+ }
+ }
+ }
+ ret.Results = []ResultSummary{{1, shortFailures}}
+
+ if len(l.Bugs) > 0 {
+ ret.Bugs = make([]string, len(l.Bugs))
+ copy(ret.Bugs, l.Bugs)
+ }
+
+ var time float64
+ if l.Runtime != nil {
+ time = *l.Runtime
+ }
+ ret.Runtimes = []RuntimeSummary{{1, time}}
+
+ return ret, nil
+}
+
+// MarshalJSON marshals l into JSON.
+func (l *FullTestLeaf) MarshalJSON() ([]byte, error) {
+ aux := testResultAux{fullTestLeafAlias: (*fullTestLeafAlias)(l)}
+ aux.Actual = strings.Join(l.Actual, " ")
+ aux.Expected = strings.Join(l.Expected, " ")
+ return json.Marshal(&aux)
+}
+
+// UnmarshalJSON unmarshals the supplied data into l.
+func (l *FullTestLeaf) UnmarshalJSON(data []byte) error {
+ aux := testResultAux{fullTestLeafAlias: (*fullTestLeafAlias)(l)}
+ if err := json.Unmarshal(data, &aux); err != nil {
+ return err
+ }
+ l.Actual = strings.Split(aux.Actual, " ")
+ l.Expected = strings.Split(aux.Expected, " ")
+ return nil
+}
+
+// Times represents "times_ms.json".
+type Times map[string]float64

Powered by Google App Engine
This is Rietveld 408576698