Atlas › Test
TestRunnerPrintSummary
Exact test identity: go.temporal.io/server/tools/testrunner/TestRunnerPrintSummary
- Package
go.temporal.io/server/tools/testrunner
- Suite / test hierarchy
TestRunnerPrintSummary
- Test
TestRunnerPrintSummary
- Introduced at
- testrunner.go ×5 Frontier kind: Joint frontier
- Covered ranges
- 48
- Covered lines
- 120
- Covered files
- 3
Covered source
Expand a file to inspect source; the > gutter marks covered lines.
go.temporal.io/server/tools/testrunner/summary.go 58 covered LOC · 18 ranges
Open complete file
17
}
18
19
>
func newSummaryFromReports(reports []*junitReport) summary {
summary.go
20
>
return summary{
21
>
Rows: newSummaryRowsFromReports(reports),
22
>
}
23
>
}
24
25
// Markdown renders the GitHub step summary HTML and enforces both the total
26
// summary budget and per-row detail truncation.
27
>
func (s summary) Markdown() string {
summary.go
28
>
if len(s.Rows) == 0 {
29
return ""
30
}
31
33
>
sb.WriteString("<table>\n<tr><th>Kind</th><th>Test</th></tr>\n")
34
>
35
>
// Reserve bytes for the closing tag so we can always finish the table.
36
>
const tableClose = "</table>\n"
37
>
budget := summaryMarkdownMaxBytes - sb.Len() - len(tableClose)
38
>
39
>
written := 0
40
>
for _, row := range s.Rows {
41
>
rendered := row.Markdown()
42
>
if len(rendered) > budget {
43
omitted := len(s.Rows) - written
44
fmt.Fprintf(&sb, "<tr><td colspan=\"2\">… %d failure(s) not shown — see full output in job logs</td></tr>\n", omitted)
45
break
46
}
48
>
budget -= len(rendered)
49
>
written++
50
}
51
53
>
return sb.String()
54
}
55
56
>
func (s summary) JSON() ([]byte, error) {
summary.go
57
>
return json.MarshalIndent(s, "", " ")
58
>
}
59
60
// summaryRow is a single failure entry rendered in both Markdown and JSON summaries.
66
}
67
68
>
func newSummaryRowsFromReports(reports []*junitReport) []summaryRow {
summary.go
69
>
var rows []summaryRow
70
>
for _, report := range reports {
71
>
for _, suite := range report.Suites {
72
>
for _, tc := range suite.Testcases {
summary.go
73
>
if tc.Failure == nil {
74
continue
75
}
76
>
rows = append(rows, newSummaryRow(failureType(tc.Failure.Type), tc.Name, tc.Failure.Data))
summary.go
77
}
78
}
79
}
80
>
slices.SortFunc(rows, func(a, b summaryRow) int {
summary.go
81
>
if byName := strings.Compare(a.Name, b.Name); byName != 0 {
summary.go
82
>
return byName
83
>
}
84
if byKind := strings.Compare(string(a.Kind), string(b.Kind)); byKind != 0 {
85
return byKind
87
return strings.Compare(a.Details, b.Details)
88
})
90
}
91
92
>
func newSummaryRow(kind failureType, name string, details string) summaryRow {
summary.go
93
>
if len(details) > summaryMaxDetailBytes {
94
headBytes := (summaryMaxDetailBytes - len(summaryTruncatedMarker)) / 2
95
tailBytes := summaryMaxDetailBytes - len(summaryTruncatedMarker) - headBytes
96
details = details[:headBytes] + summaryTruncatedMarker + details[len(details)-tailBytes:]
97
}
99
>
Kind: kind,
100
>
Name: name,
101
>
Details: details,
102
>
Final: strings.Contains(name, "(final)"),
103
>
}
104
}
105
106
// Markdown renders one summary table row.
107
>
func (row summaryRow) Markdown() string {
summary.go
108
>
kind := string(row.Kind)
109
>
if row.Final {
110
kind = "❌ " + kind
111
}
112
114
>
fmt.Fprintf(&sb, "<tr><td>%s</td><td>", html.EscapeString(kind))
115
>
if row.Details != "" {
116
>
escaped := html.EscapeString(row.Details)
117
>
if strings.Contains(row.Details, summaryTruncatedMarker) {
118
escaped += "\n… (truncated — see full output in job logs)"
119
}
120
>
fmt.Fprintf(&sb, "<details><summary>%s</summary><pre>%s</pre></details>",
summary.go
121
>
html.EscapeString(row.Name), escaped)
122
} else {
123
sb.WriteString(html.EscapeString(row.Name))
124
}
126
>
return sb.String()
127
}
go.temporal.io/server/tools/testrunner/testrunner.go 44 covered LOC · 24 ranges
Open complete file
81
}
82
84
>
return &runner{
85
>
attempts: make([]*attempt, 0),
86
>
maxAttempts: 1,
87
>
}
88
>
}
89
90
// nolint:revive,cognitive-complexity
91
>
func (r *runner) sanitizeAndParseArgs(command string, args []string) ([]string, error) {
testrunner.go
92
>
var sanitizedArgs []string
93
>
for _, arg := range args {
95
var err error
96
r.maxAttempts, err = strconv.Atoi(strings.Split(arg, "=")[1])
104
}
105
106
>
if strings.HasPrefix(arg, totalTimeoutFlag) {
testrunner.go
107
var err error
108
r.totalTimeout, err = time.ParseDuration(strings.TrimPrefix(arg, totalTimeoutFlag))
116
}
117
118
>
if strings.HasPrefix(arg, gotestsumPathFlag) {
testrunner.go
119
r.gotestsumPath = strings.Split(arg, "=")[1]
120
continue
121
}
122
123
>
if strings.HasPrefix(arg, crashReportNameFlag) {
testrunner.go
124
r.crashName = strings.Split(arg, "=")[1]
125
if r.crashName == "" {
132
}
133
136
>
continue
137
}
138
>
if strings.HasPrefix(arg, summaryOutputDirFlag) {
testrunner.go
139
>
r.summaryOutputDir = strings.Split(arg, "=")[1]
testrunner.go
140
>
if command != summaryCommand {
141
return nil, fmt.Errorf("argument %q is only valid for command %q", summaryOutputDirFlag, summaryCommand)
142
}
144
}
145
if strings.HasPrefix(arg, coverProfileFlag) {
153
}
154
156
case testCommand:
157
if r.coverProfilePath == "" {
171
return nil, fmt.Errorf("missing required argument %q", crashReportNameFlag)
172
}
174
>
if r.junitGlob == "" {
175
return nil, fmt.Errorf("missing required argument %q", junitGlobFlag)
176
}
178
return nil, fmt.Errorf("missing required argument %q", summaryOutputDirFlag)
179
}
256
}
257
259
>
paths, err := filepath.Glob(r.junitGlob)
260
>
if err != nil {
261
return fmt.Errorf("failed to expand junit glob %q: %w", r.junitGlob, err)
262
}
264
>
265
>
reports := make([]*junitReport, 0, len(paths))
266
>
for _, path := range paths {
267
>
report := &junitReport{path: path}
268
>
if err := report.read(); err != nil {
269
return fmt.Errorf("failed to read junit report %q: %w", path, err)
270
}
272
}
273
275
>
if len(summary.Rows) == 0 {
276
fmt.Println("no failed tests found in junit reports; skipping test summary")
277
return nil
278
}
279
281
>
if err := os.MkdirAll(r.summaryOutputDir, 0o755); err != nil {
282
return fmt.Errorf("failed to create summary output directory: %w", err)
283
}
284
>
if err := os.WriteFile(filepath.Join(r.summaryOutputDir, "test-summary.md"), []byte(markdown), 0o644); err != nil {
testrunner.go
285
return fmt.Errorf("failed to write summary markdown: %w", err)
286
}
287
289
>
if err != nil {
290
return fmt.Errorf("failed to render summary json: %w", err)
291
}
292
>
if err := os.WriteFile(filepath.Join(r.summaryOutputDir, "test-summary.json"), append(content, '\n'), 0o644); err != nil {
testrunner.go
293
return fmt.Errorf("failed to write summary json: %w", err)
294
}
296
}
297
go.temporal.io/server/tools/testrunner/junit.go 18 covered LOC · 6 ranges
Open complete file
38
}
39
40
>
func (j *junitReport) read() error {
junit.go
41
>
f, err := os.Open(j.path)
42
>
if err != nil {
43
return fmt.Errorf("failed to open junit report file: %w", err)
44
}
46
>
47
>
decoder := xml.NewDecoder(f)
48
>
if err = decoder.Decode(&j.Testsuites); err != nil {
49
return fmt.Errorf("failed to read junit report file: %w", err)
50
}
52
}
53
84
}
85
86
>
func (j *junitReport) write() error {
junit.go
87
>
f, err := os.Create(j.path)
88
>
if err != nil {
89
return fmt.Errorf("failed to open junit report file: %w", err)
90
}
92
>
93
>
encoder := xml.NewEncoder(f)
94
>
encoder.Indent("", " ")
95
>
if err = encoder.Encode(j.Testsuites); err != nil {
96
return fmt.Errorf("failed to write junit report file: %w", err)
97
}
98
>
log.Printf("wrote junit report to %s", j.path)
junit.go
99
>
return nil
100
}
101