mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed?
<!-- Describe what has changed in this PR -->
Remove license header from every file. Because it is really hard to
follow in this PR here is the summary:
1. License header is removed from all `.go` and `.proto` files
:fireworks::fireworks:🎆.
2. `LICENSE` file in the root directory has only Temporal and Uber
copyrights.
3. 5 other `LICENSE` files added to the packages which have copyrights
different from Temporal and Uber: Datadog, Xargin, "Mat Ryer, Tyler
Bunnell and contributors".
4. `license_file` flag is removed from all code generation tools.
5. `copyright_file` flag is removed from `go:generate mockgen`
directive.
6. All copyright related targets are removed from `Makefile`.
7. Updated Temporal copyright year to 2025 everywhere.
## Why?
<!-- Tell your future self why have you made these changes -->
I double checked with legal department that it is not needed to have
license header in every file. One file per repo is enough. I put all
copyrights to the root `LICENSE` file and removed header from all other
files. Also updated tools and `Makefile`.
192 lines
4.6 KiB
Go
192 lines
4.6 KiB
Go
package testrunner
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"errors"
|
|
"fmt"
|
|
"iter"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/jstemmer/go-junit-report/v2/junit"
|
|
)
|
|
|
|
type junitReport struct {
|
|
path string
|
|
junit.Testsuites
|
|
}
|
|
|
|
func (j *junitReport) read() error {
|
|
f, err := os.Open(j.path)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open junit report file: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
decoder := xml.NewDecoder(f)
|
|
if err = decoder.Decode(&j.Testsuites); err != nil {
|
|
return fmt.Errorf("failed to read junit report file: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func generateForTimedoutTests(timedoutTests []string) *junitReport {
|
|
var testcases []junit.Testcase
|
|
for _, name := range timedoutTests {
|
|
testcases = append(testcases, junit.Testcase{
|
|
Name: name + " (timeout)",
|
|
Failure: &junit.Result{Message: "Timeout"},
|
|
})
|
|
}
|
|
return &junitReport{
|
|
Testsuites: junit.Testsuites{
|
|
Suites: []junit.Testsuite{
|
|
{
|
|
Name: "suite",
|
|
Testcases: testcases,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (j *junitReport) write() error {
|
|
f, err := os.Create(j.path)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open junit report file: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
encoder := xml.NewEncoder(f)
|
|
encoder.Indent("", " ")
|
|
if err = encoder.Encode(j.Testsuites); err != nil {
|
|
return fmt.Errorf("failed to write junit report file: %w", err)
|
|
}
|
|
log.Printf("wrote junit report to %s", j.path)
|
|
return nil
|
|
}
|
|
|
|
func (j *junitReport) collectTestCases() map[string]struct{} {
|
|
cases := make(map[string]struct{})
|
|
for _, suite := range j.Testsuites.Suites {
|
|
for _, tc := range suite.Testcases {
|
|
cases[tc.Name] = struct{}{}
|
|
}
|
|
}
|
|
return cases
|
|
}
|
|
|
|
func (j *junitReport) collectTestCaseFailures() []string {
|
|
root := node{children: make(map[string]node)}
|
|
|
|
for _, suite := range j.Testsuites.Suites {
|
|
if suite.Failures == 0 {
|
|
continue
|
|
}
|
|
for _, tc := range suite.Testcases {
|
|
if tc.Failure == nil {
|
|
continue
|
|
}
|
|
n := root
|
|
for _, part := range strings.Split(tc.Name, "/") {
|
|
child, ok := n.children[part]
|
|
if !ok {
|
|
child = node{children: make(map[string]node)}
|
|
n.children[part] = child
|
|
}
|
|
n = child
|
|
}
|
|
}
|
|
}
|
|
|
|
leafFailures := make([]string, 0)
|
|
|
|
// Walk the tree and find all leaf failures. The way Go test failures are reported in junit is that there's a
|
|
// test case per suite and per test in that suite. Filter out any nodes that have children to find the most
|
|
// specific failures to rerun.
|
|
for path, n := range root.walk() {
|
|
if len(n.children) > 0 {
|
|
continue
|
|
}
|
|
leafFailures = append(leafFailures, path)
|
|
}
|
|
|
|
return leafFailures
|
|
}
|
|
|
|
func mergeReports(reports []*junitReport) (*junitReport, error) {
|
|
if len(reports) == 0 {
|
|
return nil, errors.New("no reports to merge")
|
|
}
|
|
if len(reports) == 1 {
|
|
return reports[0], nil
|
|
}
|
|
|
|
var combined junit.Testsuites
|
|
combined.XMLName = reports[0].Testsuites.XMLName
|
|
combined.Name = reports[0].Testsuites.Name
|
|
combined.Suites = reports[0].Testsuites.Suites
|
|
|
|
for i, report := range reports {
|
|
combined.Tests += report.Testsuites.Tests
|
|
combined.Errors += report.Testsuites.Errors
|
|
combined.Failures += report.Testsuites.Failures
|
|
combined.Skipped += report.Testsuites.Skipped
|
|
combined.Disabled += report.Testsuites.Disabled
|
|
combined.Time += report.Testsuites.Time
|
|
|
|
// If the report is for a retry ...
|
|
if i > 0 {
|
|
// Run sanity check to make sure we rerun what we expect.
|
|
casesTested := report.collectTestCases()
|
|
casesMissing := make([]string, 0)
|
|
previousReport := reports[i-1]
|
|
for _, f := range previousReport.collectTestCaseFailures() {
|
|
if _, ok := casesTested[f]; !ok {
|
|
casesMissing = append(casesMissing, f)
|
|
}
|
|
}
|
|
if len(casesMissing) > 0 {
|
|
return nil,
|
|
fmt.Errorf("expected a rerun of all failures from the previous attempt, missing: %v", casesMissing)
|
|
}
|
|
|
|
// Append the test cases from the retry.
|
|
for _, suite := range report.Testsuites.Suites {
|
|
cpy := suite
|
|
cpy.Name += fmt.Sprintf(" (retry %d)", i)
|
|
cpy.Testcases = make([]junit.Testcase, 0, len(suite.Testcases))
|
|
for _, test := range suite.Testcases {
|
|
tcpy := test
|
|
tcpy.Name += fmt.Sprintf(" (retry %d)", i)
|
|
cpy.Testcases = append(cpy.Testcases, tcpy)
|
|
}
|
|
combined.Suites = append(combined.Suites, cpy)
|
|
}
|
|
}
|
|
}
|
|
|
|
return &junitReport{Testsuites: combined}, nil
|
|
}
|
|
|
|
type node struct {
|
|
children map[string]node
|
|
}
|
|
|
|
func (n node) visitor(path ...string) func(yield func(string, node) bool) {
|
|
return func(yield func(string, node) bool) {
|
|
for name, child := range n.children {
|
|
path := append(path, name)
|
|
if !yield(strings.Join(path, "/"), child) {
|
|
return
|
|
}
|
|
child.visitor(path...)(yield)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (n node) walk() iter.Seq2[string, node] {
|
|
return n.visitor()
|
|
}
|