OLD | NEW |
1 package model | 1 package model |
2 | 2 |
3 import ( | 3 import ( |
4 "bytes" | 4 "bytes" |
5 "encoding/json" | 5 "encoding/json" |
6 "errors" | 6 "errors" |
7 "fmt" | 7 "fmt" |
8 "io" | 8 "io" |
9 "io/ioutil" | 9 "io/ioutil" |
10 "strconv" | 10 "strconv" |
(...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 } | 359 } |
360 | 360 |
361 // AggregateTestLeaf is the summary of test results at the l of a tests trie. | 361 // AggregateTestLeaf is the summary of test results at the l of a tests trie. |
362 type AggregateTestLeaf struct { | 362 type AggregateTestLeaf struct { |
363 Results []ResultSummary | 363 Results []ResultSummary |
364 Runtimes []RuntimeSummary | 364 Runtimes []RuntimeSummary |
365 Expected []string | 365 Expected []string |
366 Bugs []string | 366 Bugs []string |
367 } | 367 } |
368 | 368 |
369 func (l *AggregateTestLeaf) node() {} | 369 func (leaf *AggregateTestLeaf) node() {} |
370 | 370 |
371 // aggregateTestLeafAux is used to marshal and unmarshal AggregateTestLeaf. | 371 // aggregateTestLeafAux is used to marshal and unmarshal AggregateTestLeaf. |
372 type aggregateTestLeafAux struct { | 372 type aggregateTestLeafAux struct { |
373 Results []ResultSummary `json:"results,omitempty"` | 373 Results []ResultSummary `json:"results,omitempty"` |
374 Runtimes []RuntimeSummary `json:"times,omitempty"` | 374 Runtimes []RuntimeSummary `json:"times,omitempty"` |
375 Expected *string `json:"expected,omitempty"` | 375 Expected *string `json:"expected,omitempty"` |
376 Bugs []string `json:"bugs,omitempty"` | 376 Bugs []string `json:"bugs,omitempty"` |
377 } | 377 } |
378 | 378 |
379 // MarshalJSON marshal l into JSON. | 379 // MarshalJSON marshal l into JSON. |
380 func (l *AggregateTestLeaf) MarshalJSON() ([]byte, error) { | 380 func (leaf *AggregateTestLeaf) MarshalJSON() ([]byte, error) { |
381 aux := aggregateTestLeafAux{ | 381 aux := aggregateTestLeafAux{ |
382 » » Results: l.Results, | 382 » » Results: leaf.Results, |
383 » » Runtimes: l.Runtimes, | 383 » » Runtimes: leaf.Runtimes, |
384 » » Bugs: l.Bugs, | 384 » » Bugs: leaf.Bugs, |
385 } | 385 } |
386 » if s := strings.Join(l.Expected, " "); len(s) > 0 { | 386 » if s := strings.Join(leaf.Expected, " "); len(s) > 0 { |
387 aux.Expected = &s | 387 aux.Expected = &s |
388 } | 388 } |
389 return json.Marshal(&aux) | 389 return json.Marshal(&aux) |
390 } | 390 } |
391 | 391 |
392 // UnmarshalJSON unmarshal the supplied data into l. | 392 // UnmarshalJSON unmarshal the supplied data into l. |
393 func (l *AggregateTestLeaf) UnmarshalJSON(data []byte) error { | 393 func (leaf *AggregateTestLeaf) UnmarshalJSON(data []byte) error { |
394 var aux aggregateTestLeafAux | 394 var aux aggregateTestLeafAux |
395 if err := json.Unmarshal(data, &aux); err != nil { | 395 if err := json.Unmarshal(data, &aux); err != nil { |
396 return err | 396 return err |
397 } | 397 } |
398 | 398 |
399 » l.Results = aux.Results | 399 » leaf.Results = aux.Results |
400 » l.Runtimes = aux.Runtimes | 400 » leaf.Runtimes = aux.Runtimes |
401 if aux.Expected != nil { | 401 if aux.Expected != nil { |
402 » » l.Expected = strings.Split(*aux.Expected, " ") | 402 » » leaf.Expected = strings.Split(*aux.Expected, " ") |
403 } | 403 } |
404 » l.Bugs = aux.Bugs | 404 » leaf.Bugs = aux.Bugs |
405 | 405 |
406 return nil | 406 return nil |
407 } | 407 } |
408 | 408 |
409 // defaultFields sets default values for missing/invalid fields. | 409 // defaultFields sets default values for missing/invalid fields. |
410 func (l *AggregateTestLeaf) defaultFields() { | 410 func (leaf *AggregateTestLeaf) defaultFields() { |
411 » if len(l.Results) == 0 { | 411 » if len(leaf.Results) == 0 { |
412 » » l.Results = []ResultSummary{{1, "N"}} | 412 » » leaf.Results = []ResultSummary{{1, "N"}} |
413 } | 413 } |
414 » if len(l.Runtimes) == 0 { | 414 » if len(leaf.Runtimes) == 0 { |
415 » » l.Runtimes = []RuntimeSummary{{1, 0}} | 415 » » leaf.Runtimes = []RuntimeSummary{{1, 0}} |
416 } | 416 } |
417 } | 417 } |
418 | 418 |
419 // ResultSummary is the type of test failure and count of how many | 419 // ResultSummary is the type of test failure and count of how many |
420 // times the running time occured. | 420 // times the running time occured. |
421 type ResultSummary struct { | 421 type ResultSummary struct { |
422 Count int | 422 Count int |
423 Type string | 423 Type string |
424 } | 424 } |
425 | 425 |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
477 return err | 477 return err |
478 } | 478 } |
479 if len(tmp) != 2 { | 479 if len(tmp) != 2 { |
480 return fmt.Errorf("model: UnmarshalJSON: RuntimeSummary wrong le
ngth: %d, expect: %d", len(tmp), 2) | 480 return fmt.Errorf("model: UnmarshalJSON: RuntimeSummary wrong le
ngth: %d, expect: %d", len(tmp), 2) |
481 } | 481 } |
482 | 482 |
483 rs.Count = int(tmp[0]) | 483 rs.Count = int(tmp[0]) |
484 rs.Runtime = tmp[1] | 484 rs.Runtime = tmp[1] |
485 return nil | 485 return nil |
486 } | 486 } |
| 487 |
| 488 var ( |
| 489 // ErrBuildNumberConflict is returned when the build numbers |
| 490 // are the same when merging. |
| 491 ErrBuildNumberConflict = errors.New("build number conflict") |
| 492 |
| 493 // ErrBuilderNameConflict is returned when the builder names |
| 494 // do not match when merging. |
| 495 ErrBuilderNameConflict = errors.New("builder name conflict") |
| 496 ) |
| 497 |
| 498 // Merge merges other into ag. |
| 499 func (ag *AggregateResult) Merge(other *AggregateResult) error { |
| 500 if ag.Builder != other.Builder { |
| 501 return ErrBuilderNameConflict |
| 502 } |
| 503 if ag.BuilderInfo == nil { |
| 504 ag.BuilderInfo = &BuilderInfo{} |
| 505 } |
| 506 ag.Version = ResultsVersion |
| 507 return ag.BuilderInfo.Merge(other.BuilderInfo) |
| 508 } |
| 509 |
| 510 // Merge merges other into info. |
| 511 // |
| 512 // The returned error is ErrBuildNumberConflict when |
| 513 // other.BuildNumbers[0] already has the latest build number. |
| 514 func (info *BuilderInfo) Merge(other *BuilderInfo) error { |
| 515 if len(info.BuildNumbers) > 0 && len(other.BuildNumbers) > 0 { |
| 516 if info.BuildNumbers[0] == other.BuildNumbers[0] { |
| 517 return ErrBuildNumberConflict |
| 518 } |
| 519 } |
| 520 |
| 521 info.SecondsEpoch = append(other.SecondsEpoch, info.SecondsEpoch...) |
| 522 info.BlinkRevs = append(other.BlinkRevs, info.BlinkRevs...) |
| 523 info.BuildNumbers = append(other.BuildNumbers, info.BuildNumbers...) |
| 524 info.ChromeRevs = append(other.ChromeRevs, info.ChromeRevs...) |
| 525 |
| 526 if info.FailuresByType == nil && other.FailuresByType != nil { |
| 527 info.FailuresByType = make(map[string][]int) |
| 528 } |
| 529 for k, v := range other.FailuresByType { |
| 530 info.FailuresByType[k] = append(v, info.FailuresByType[k]...) |
| 531 } |
| 532 |
| 533 info.FailureMap = FailureLongNames |
| 534 |
| 535 if info.Tests == nil { |
| 536 info.Tests = AggregateTest{} |
| 537 } |
| 538 |
| 539 info.Tests.WalkLeaves(func(_ string, leaf *AggregateTestLeaf) { |
| 540 leaf.Expected = nil |
| 541 leaf.Bugs = nil |
| 542 }) |
| 543 |
| 544 return info.Tests.Merge(other.Tests) |
| 545 } |
| 546 |
| 547 // Merge merges other into at. |
| 548 func (at *AggregateTest) Merge(other AggregateTest) error { |
| 549 // Shallow copy but OK. We take care to not modify otherCopy |
| 550 // values; instead always create new objects |
| 551 // and assign to otherCopy[key]. |
| 552 otherCopy := make(AggregateTest, len(other)) |
| 553 for k, v := range other { |
| 554 otherCopy[k] = v |
| 555 } |
| 556 |
| 557 for k, v := range *at { |
| 558 if _, ok := otherCopy[k]; !ok { |
| 559 switch v.(type) { |
| 560 case *AggregateTestLeaf: |
| 561 l := &AggregateTestLeaf{} |
| 562 l.defaultFields() |
| 563 otherCopy[k] = l |
| 564 case AggregateTest: |
| 565 otherCopy[k] = AggregateTest{} |
| 566 } |
| 567 } |
| 568 } |
| 569 |
| 570 for k, v := range otherCopy { |
| 571 // Key does not exist: assign entire subtree. |
| 572 if _, ok := (*at)[k]; !ok { |
| 573 if *at == nil { |
| 574 *at = AggregateTest{} |
| 575 } |
| 576 (*at)[k] = v |
| 577 continue |
| 578 } |
| 579 |
| 580 // Leaf node. |
| 581 if leaf1, ok := (*at)[k].(*AggregateTestLeaf); ok { |
| 582 leaf2, ok := v.(*AggregateTestLeaf) |
| 583 if !ok { |
| 584 return errors.New("model: Merge: expected *Aggre
gateTestLeaf") |
| 585 } |
| 586 if err := leaf1.Merge(leaf2); err != nil { |
| 587 return err |
| 588 } |
| 589 continue |
| 590 } |
| 591 |
| 592 // Not leaf node: merge subtree recursively. |
| 593 at1, ok := (*at)[k].(AggregateTest) |
| 594 if !ok { |
| 595 return errors.New("model: Merge: expected AggregateTest"
) |
| 596 } |
| 597 at2, ok := v.(AggregateTest) |
| 598 if !ok { |
| 599 return errors.New("model: Merge: expected AggregateTest"
) |
| 600 } |
| 601 if err := at1.Merge(at2); err != nil { |
| 602 return err |
| 603 } |
| 604 } |
| 605 |
| 606 return nil |
| 607 } |
| 608 |
| 609 // Merge merges other into leaf. |
| 610 func (leaf *AggregateTestLeaf) Merge(other *AggregateTestLeaf) error { |
| 611 // Bugs and Expected should come from from other only. |
| 612 leaf.Bugs = other.Bugs |
| 613 if len(other.Expected) == 1 && other.Expected[0] != "PASS" { |
| 614 leaf.Expected = other.Expected |
| 615 } |
| 616 |
| 617 for _, r := range other.Results { |
| 618 if len(leaf.Results) > 0 && r.Type == leaf.Results[0].Type { |
| 619 leaf.Results[0].Count += r.Count |
| 620 } else { |
| 621 leaf.Results = append([]ResultSummary{r}, leaf.Results..
.) |
| 622 } |
| 623 } |
| 624 |
| 625 for _, r := range other.Runtimes { |
| 626 if len(leaf.Runtimes) > 0 && r.Runtime == leaf.Runtimes[0].Runti
me { |
| 627 leaf.Runtimes[0].Count += r.Count |
| 628 } else { |
| 629 leaf.Runtimes = append([]RuntimeSummary{r}, leaf.Runtime
s...) |
| 630 } |
| 631 } |
| 632 |
| 633 return nil |
| 634 } |
| 635 |
| 636 const ( |
| 637 // ResultsSize is the size that "results.json" should be trimmed to. |
| 638 ResultsSize = 500 |
| 639 |
| 640 // ResultsSmallSize is the size that "results_small.json" should |
| 641 // be trimmed to. |
| 642 ResultsSmallSize = 100 |
| 643 |
| 644 runtimeThresholdNormal float64 = 3 // In seconds. |
| 645 runtimeThresholdDebug float64 = 9 // In seconds. |
| 646 ) |
| 647 |
| 648 func isDebugBuilder(builder string) bool { |
| 649 for _, s := range []string{"debug", "dbg"} { |
| 650 if strings.Contains(strings.ToLower(builder), s) { |
| 651 return true |
| 652 } |
| 653 } |
| 654 return false |
| 655 } |
| 656 |
| 657 // Trim trims the leaves of Tests in ar to the specified size. |
| 658 func (ag *AggregateResult) Trim(size int) error { |
| 659 t := runtimeThresholdNormal |
| 660 |
| 661 if isDebugBuilder(ag.Builder) { |
| 662 t = runtimeThresholdDebug |
| 663 } |
| 664 |
| 665 return ag.Tests.trim(size, t) |
| 666 } |
| 667 |
| 668 func (at AggregateTest) trim(size int, threshold float64) error { |
| 669 for k, v := range at { |
| 670 if leaf, ok := v.(*AggregateTestLeaf); ok { |
| 671 leaf.trim(size) |
| 672 if leaf.shouldDelete(threshold) { |
| 673 delete(at, k) |
| 674 } |
| 675 continue |
| 676 } |
| 677 |
| 678 child, ok := v.(AggregateTest) |
| 679 if !ok { |
| 680 return errors.New("model: trim: expected AggregateTest") |
| 681 } |
| 682 if err := child.trim(size, threshold); err != nil { |
| 683 return err |
| 684 } |
| 685 if len(child) == 0 { |
| 686 delete(at, k) |
| 687 } |
| 688 } |
| 689 return nil |
| 690 } |
| 691 |
| 692 func (leaf *AggregateTestLeaf) trim(size int) { |
| 693 n := 0 |
| 694 |
| 695 for i, r := range leaf.Results { |
| 696 leaf.Results[i].Count = min(r.Count, size) |
| 697 n += r.Count |
| 698 if n >= size { |
| 699 leaf.Results = leaf.Results[:i+1] |
| 700 break |
| 701 } |
| 702 } |
| 703 |
| 704 n = 0 |
| 705 |
| 706 for i, r := range leaf.Runtimes { |
| 707 leaf.Runtimes[i].Count = min(r.Count, size) |
| 708 n += r.Count |
| 709 if n >= size { |
| 710 leaf.Runtimes = leaf.Runtimes[:i+1] |
| 711 break |
| 712 } |
| 713 } |
| 714 } |
| 715 |
| 716 func min(a, b int) int { |
| 717 if a < b { |
| 718 return a |
| 719 } |
| 720 return b |
| 721 } |
| 722 |
| 723 var deletableTypes = map[string]bool{"P": true, "N": true, "Y": true} |
| 724 |
| 725 func (leaf *AggregateTestLeaf) shouldDelete(threshold float64) bool { |
| 726 if len(leaf.Expected) == 1 && leaf.Expected[0] != "PASS" { |
| 727 return false |
| 728 } |
| 729 if leaf.Bugs != nil { |
| 730 return false |
| 731 } |
| 732 |
| 733 for _, r := range leaf.Results { |
| 734 if !deletableTypes[r.Type] { |
| 735 return false |
| 736 } |
| 737 } |
| 738 for _, r := range leaf.Runtimes { |
| 739 if r.Runtime >= threshold { |
| 740 return false |
| 741 } |
| 742 } |
| 743 |
| 744 return true |
| 745 } |
OLD | NEW |