Index: go/src/infra/appengine/test-results/model/merge.go |
diff --git a/go/src/infra/appengine/test-results/model/merge.go b/go/src/infra/appengine/test-results/model/merge.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..72d2d53742d6bc09f5af3dc37ac6d187ccdb74ec |
--- /dev/null |
+++ b/go/src/infra/appengine/test-results/model/merge.go |
@@ -0,0 +1,167 @@ |
+package model |
martiniss
2016/08/12 00:14:24
I don't see the need for a merge.go file; why don'
nishanths
2016/08/12 02:37:39
As discussed offline, will move the contents of me
nishanths
2016/08/16 01:01:53
Done in patch set 7.
|
+ |
+import "errors" |
+ |
+var ( |
+ // ErrBuildNumberConflict is returned when the build numbers |
+ // are the same when merging. |
+ ErrBuildNumberConflict = errors.New("build number conflict") |
martiniss
2016/08/12 00:14:24
It's not obvious to me from reading this file why
nishanths
2016/08/12 02:37:39
Done.
|
+ |
+ // ErrBuilderNameConflict is returned when the builder names |
+ // do not match when merging. |
+ ErrBuilderNameConflict = errors.New("builder name conflict") |
+) |
+ |
+// Merge merges x into ar. After merging, ar's fields may reference the same |
+// objects referenced by x. |
+func (ar *AggregateResult) Merge(x *AggregateResult) error { |
martiniss
2016/08/12 00:14:24
nit: "other" is a bit more descriptive than "x".
nishanths
2016/08/12 02:37:39
Done, thanks.
|
+ if ar == nil { |
+ return errors.New("model: Merge: nil *AggregateResult") |
+ } |
+ if ar.Builder != x.Builder { |
+ return ErrBuilderNameConflict |
+ } |
+ if ar.BuilderInfo == nil { |
+ ar.BuilderInfo = &BuilderInfo{} |
+ } |
+ ar.Version = ResultsVersion |
+ return ar.BuilderInfo.Merge(x.BuilderInfo) |
+} |
+ |
+// Merge merges x into info. After merging, info's fields may reference the same |
martiniss
2016/08/12 00:14:24
Does the reference comment matter? Would that ever
nishanths
2016/08/12 02:37:39
Removed, thanks.
|
+// objects referenced by x. |
+func (info *BuilderInfo) Merge(x *BuilderInfo) error { |
martiniss
2016/08/12 00:14:24
nit: "x" variable name.
nishanths
2016/08/12 02:37:39
Done: used "other".
|
+ if info == nil { |
martiniss
2016/08/12 00:14:24
Why would info ever be nil?
nishanths
2016/08/12 02:37:39
Removed the check (also removed at similar locatio
|
+ return errors.New("model: Merge: nil *BuilderInfo") |
+ } |
+ |
+ if len(info.BuildNumbers) > 0 && len(x.BuildNumbers) > 0 { |
+ if info.BuildNumbers[0] == x.BuildNumbers[0] { |
+ return ErrBuildNumberConflict |
+ } |
+ } |
+ |
+ info.SecondsEpoch = append(x.SecondsEpoch, info.SecondsEpoch...) |
+ info.BlinkRevs = append(x.BlinkRevs, info.BlinkRevs...) |
+ info.BuildNumbers = append(x.BuildNumbers, info.BuildNumbers...) |
+ info.ChromeRevs = append(x.ChromeRevs, info.ChromeRevs...) |
+ |
+ for k, v := range x.FailuresByType { |
+ if info.FailuresByType == nil { |
martiniss
2016/08/12 00:14:24
Move outside for loop.
nishanths
2016/08/12 02:37:39
Done.
|
+ info.FailuresByType = make(map[string][]int) |
+ } |
+ info.FailuresByType[k] = append(v, info.FailuresByType[k]...) |
+ } |
+ |
+ info.FailureMap = FailureLongNames |
+ |
+ if info.Tests == nil { |
+ info.Tests = AggregateTest{} |
+ } |
+ |
+ info.Tests.WalkLeaves(func(_ string, leaf *AggregateTestLeaf) { |
+ leaf.Expected = nil |
+ leaf.Bugs = nil |
+ }) |
+ |
+ return info.Tests.Merge(x.Tests) |
+} |
+ |
+// Merge merges x into at. After merging, at's fields may reference the same |
+// objects referenced by x. |
+func (at *AggregateTest) Merge(x AggregateTest) error { |
+ if at == nil { |
+ return errors.New("model: Merge: nil *AggregateTest") |
+ } |
+ |
+ // Shallow copy but OK. We take care to not modify xcopy |
+ // values; instead always create new objects |
+ // and assign to xcopy[key]. |
+ xcopy := make(AggregateTest, len(x)) |
martiniss
2016/08/12 00:14:24
shouldn't this be make([]AggregateTest, len(x)) ?
nishanths
2016/08/12 02:37:39
AggregateTest is a map[string]Node.
|
+ for k, v := range x { |
+ xcopy[k] = v |
+ } |
+ |
+ for k, v := range *at { |
+ if _, ok := xcopy[k]; !ok { |
+ switch v.(type) { |
+ case *AggregateTestLeaf: |
+ l := &AggregateTestLeaf{} |
+ l.defaultFields() |
+ xcopy[k] = l |
+ case AggregateTest: |
+ xcopy[k] = AggregateTest{} |
+ } |
+ } |
+ } |
+ |
+ for k, v := range xcopy { |
+ // Key does not exist: assign entire subtree. |
+ if _, ok := (*at)[k]; !ok { |
+ if *at == nil { |
+ *at = AggregateTest{} |
+ } |
+ (*at)[k] = v |
+ continue |
+ } |
+ |
+ // Leaf node. |
+ if leaf1, ok := (*at)[k].(*AggregateTestLeaf); ok { |
+ leaf2, ok := v.(*AggregateTestLeaf) |
+ if !ok { |
+ return errors.New("model: Merge: expected *AggregateTestLeaf") |
+ } |
+ if err := leaf1.Merge(leaf2); err != nil { |
+ return err |
+ } |
+ continue |
+ } |
+ |
+ // Not leaf node: merge subtree recursively. |
+ at1, ok := (*at)[k].(AggregateTest) |
+ if !ok { |
+ return errors.New("model: Merge: expected AggregateTest") |
+ } |
+ at2, ok := v.(AggregateTest) |
+ if !ok { |
+ return errors.New("model: Merge: expected AggregateTest") |
+ } |
+ if err := at1.Merge(at2); err != nil { |
+ return err |
+ } |
+ } |
+ |
+ return nil |
+} |
+ |
+// Merge merges x into leaf. After merging, leaf's fields may reference the same |
+// objects referenced by x. |
+func (leaf *AggregateTestLeaf) Merge(x *AggregateTestLeaf) error { |
+ if leaf == nil { |
martiniss
2016/08/12 00:14:24
Same comment; seems unnecessary
nishanths
2016/08/12 02:37:39
Done, removed check.
|
+ return errors.New("model: Merge: nil *AggregateTestLeaf") |
+ } |
+ |
+ // Bugs and Expected should come from from x only. |
+ leaf.Bugs = x.Bugs |
+ if len(x.Expected) == 1 && x.Expected[0] != "PASS" { |
+ leaf.Expected = x.Expected |
+ } |
+ |
+ for _, r := range x.Results { |
+ if len(leaf.Results) > 0 && r.Type == leaf.Results[0].Type { |
+ leaf.Results[0].Count += r.Count |
+ } else { |
+ leaf.Results = append([]ResultSummary{r}, leaf.Results...) |
+ } |
+ } |
+ |
+ for _, r := range x.Runtimes { |
+ if len(leaf.Runtimes) > 0 && r.Runtime == leaf.Runtimes[0].Runtime { |
+ leaf.Runtimes[0].Count += r.Count |
+ } else { |
+ leaf.Runtimes = append([]RuntimeSummary{r}, leaf.Runtimes...) |
+ } |
+ } |
+ |
+ return nil |
+} |