Skip to content

Commit 3ec30cb

Browse files
authored
feat(test-results): add --no-trim-output flag and allow 0 to disable trimming (#527)
* feat(test-results): add --no-trim-output flag and allow 0 to disable trimming * docs(test-results): add section on trimming test output options
1 parent 8a05fdf commit 3ec30cb

5 files changed

Lines changed: 190 additions & 5 deletions

File tree

test-results/DOCUMENTATION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
## Configuration & Environment
4444
- Viper auto-loads `$HOME/.test-results.yaml` when present; command flags override config.
4545
- Key env vars: `SEMAPHORE_PIPELINE_ID`, `SEMAPHORE_WORKFLOW_ID`, `SEMAPHORE_JOB_ID`, `SEMAPHORE_AGENT_MACHINE_TYPE`, etc.; parsers read them to enrich metadata.
46-
- Flags to know: `--parser`, `--ignore-missing`, `--no-compress`, `--suite-prefix`, `--omit-output-for-passed`, `--trim-output-to`, `--name`.
46+
- Flags to know: `--parser`, `--ignore-missing`, `--no-compress`, `--suite-prefix`, `--omit-output-for-passed`, `--trim-output-to`, `--no-trim-output`, `--name`.
4747

4848
## Development Workflow
4949
- Requires Go 1.24+. Use `make test.setup` once to build the `cli` Docker image and fetch module deps.

test-results/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,21 @@ The generated tests in a report will sometimes contain a prefix in the name. For
7575
test-results publish --suite-prefix "Elixir." results.xml
7676
```
7777

78+
## Trimming test output
79+
80+
By default, the CLI trims stdout/stderr fields to the last 1000 characters and prepends `...[truncated]...` when trimming occurs. You can change the limit or disable trimming entirely:
81+
82+
```bash
83+
# Keep the last 5000 characters
84+
test-results publish --trim-output-to 5000 results.xml
85+
86+
# Disable trimming
87+
test-results publish --no-trim-output results.xml
88+
89+
# Also disables trimming
90+
test-results publish --trim-output-to 0 results.xml
91+
```
92+
7893
## Multiple reports from one job
7994

8095
If your job generates multiple reports: `integration.xml`, `unit.xml` you can use this command to merge and publish them

test-results/cmd/root.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ func init() {
4444
rootCmd.PersistentFlags().StringP("suite-prefix", "S", "", "prefix for each suite")
4545
rootCmd.PersistentFlags().StringP("parser", "p", "auto", "override parser to be used")
4646
rootCmd.PersistentFlags().Bool("no-compress", false, "skip gzip compression for the output")
47-
rootCmd.PersistentFlags().IntP("trim-output-to", "s", 1000, "trim stdout/stderr to last N characters (max 10000), defaults to 1000")
47+
rootCmd.PersistentFlags().IntP("trim-output-to", "s", 1000, "trim stdout/stderr to last N characters, defaults to 1000 (use 0 or --no-trim-output to disable)")
48+
rootCmd.PersistentFlags().Bool("no-trim-output", false, "disable output trimming entirely")
4849

4950
// Cobra also supports local flags, which will only run
5051
// when this action is called directly.

test-results/pkg/cli/cli.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -466,16 +466,22 @@ func ApplyOutputTrimming(result *parser.Result, cmd *cobra.Command) {
466466
return
467467
}
468468

469+
// Check if trimming is disabled via --no-trim-output flag
470+
noTrim, err := cmd.Flags().GetBool("no-trim-output")
471+
if err == nil && noTrim {
472+
return
473+
}
474+
469475
trimTo := 1000
470-
maxTrimLength := 10000
471476

472477
trimToFlag, err := cmd.Flags().GetInt("trim-output-to")
473478
if err == nil {
474479
trimTo = trimToFlag
475480
}
476481

477-
if trimTo > maxTrimLength || trimTo <= 0 {
478-
trimTo = maxTrimLength
482+
// If trimTo is 0 or negative, disable trimming
483+
if trimTo <= 0 {
484+
return
479485
}
480486

481487
for i := range result.TestResults {

test-results/pkg/cli/cli_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99

1010
"github.com/semaphoreci/toolbox/test-results/pkg/parser"
11+
"github.com/spf13/cobra"
1112

1213
"github.com/semaphoreci/toolbox/test-results/pkg/cli"
1314
"github.com/stretchr/testify/assert"
@@ -151,6 +152,168 @@ func TestWriteToTmpFile(t *testing.T) {
151152
})
152153
}
153154

155+
func TestApplyOutputTrimming(t *testing.T) {
156+
longText := func(n int) string {
157+
s := ""
158+
for i := 0; i < n; i++ {
159+
s += "x"
160+
}
161+
return s
162+
}
163+
164+
createTestResult := func(text string) *parser.Result {
165+
return &parser.Result{
166+
TestResults: []parser.TestResults{
167+
{
168+
ID: "test-1",
169+
Name: "Test",
170+
Suites: []parser.Suite{
171+
{
172+
Name: "Suite1",
173+
SystemOut: text,
174+
SystemErr: text,
175+
Tests: []parser.Test{
176+
{
177+
Name: "Test1",
178+
SystemOut: text,
179+
SystemErr: text,
180+
Failure: &parser.Failure{
181+
Message: text,
182+
Body: text,
183+
},
184+
},
185+
},
186+
},
187+
},
188+
},
189+
},
190+
}
191+
}
192+
193+
createCmd := func(trimTo int, noTrim bool) *cobra.Command {
194+
cmd := &cobra.Command{}
195+
cmd.Flags().Int("trim-output-to", trimTo, "")
196+
cmd.Flags().Bool("no-trim-output", noTrim, "")
197+
return cmd
198+
}
199+
200+
t.Run("default trimming to 1000 characters", func(t *testing.T) {
201+
result := createTestResult(longText(2000))
202+
cmd := createCmd(1000, false)
203+
204+
cli.ApplyOutputTrimming(result, cmd)
205+
206+
suite := result.TestResults[0].Suites[0]
207+
assert.True(t, len(suite.SystemOut) <= 1000+len("...[truncated]...\n"))
208+
assert.True(t, len(suite.SystemErr) <= 1000+len("...[truncated]...\n"))
209+
assert.Contains(t, suite.SystemOut, "...[truncated]...")
210+
})
211+
212+
t.Run("custom trim length", func(t *testing.T) {
213+
result := createTestResult(longText(5000))
214+
cmd := createCmd(3000, false)
215+
216+
cli.ApplyOutputTrimming(result, cmd)
217+
218+
suite := result.TestResults[0].Suites[0]
219+
assert.True(t, len(suite.SystemOut) <= 3000+len("...[truncated]...\n"))
220+
assert.Contains(t, suite.SystemOut, "...[truncated]...")
221+
})
222+
223+
t.Run("no trimming when --no-trim-output is set", func(t *testing.T) {
224+
originalText := longText(5000)
225+
result := createTestResult(originalText)
226+
cmd := createCmd(1000, true)
227+
228+
cli.ApplyOutputTrimming(result, cmd)
229+
230+
suite := result.TestResults[0].Suites[0]
231+
assert.Equal(t, originalText, suite.SystemOut)
232+
assert.Equal(t, originalText, suite.SystemErr)
233+
assert.Equal(t, originalText, suite.Tests[0].SystemOut)
234+
assert.Equal(t, originalText, suite.Tests[0].Failure.Message)
235+
})
236+
237+
t.Run("no trimming when --trim-output-to is 0", func(t *testing.T) {
238+
originalText := longText(5000)
239+
result := createTestResult(originalText)
240+
cmd := createCmd(0, false)
241+
242+
cli.ApplyOutputTrimming(result, cmd)
243+
244+
suite := result.TestResults[0].Suites[0]
245+
assert.Equal(t, originalText, suite.SystemOut)
246+
assert.Equal(t, originalText, suite.SystemErr)
247+
})
248+
249+
t.Run("no trimming when --trim-output-to is negative", func(t *testing.T) {
250+
originalText := longText(5000)
251+
result := createTestResult(originalText)
252+
cmd := createCmd(-1, false)
253+
254+
cli.ApplyOutputTrimming(result, cmd)
255+
256+
suite := result.TestResults[0].Suites[0]
257+
assert.Equal(t, originalText, suite.SystemOut)
258+
})
259+
260+
t.Run("text shorter than trim limit is not modified", func(t *testing.T) {
261+
originalText := longText(500)
262+
result := createTestResult(originalText)
263+
cmd := createCmd(1000, false)
264+
265+
cli.ApplyOutputTrimming(result, cmd)
266+
267+
suite := result.TestResults[0].Suites[0]
268+
assert.Equal(t, originalText, suite.SystemOut)
269+
assert.NotContains(t, suite.SystemOut, "...[truncated]...")
270+
})
271+
272+
t.Run("nil result does not panic", func(t *testing.T) {
273+
cmd := createCmd(1000, false)
274+
assert.NotPanics(t, func() {
275+
cli.ApplyOutputTrimming(nil, cmd)
276+
})
277+
})
278+
279+
t.Run("trims failure and error fields", func(t *testing.T) {
280+
result := &parser.Result{
281+
TestResults: []parser.TestResults{
282+
{
283+
ID: "test-1",
284+
Suites: []parser.Suite{
285+
{
286+
Tests: []parser.Test{
287+
{
288+
Failure: &parser.Failure{
289+
Message: longText(2000),
290+
Type: longText(2000),
291+
Body: longText(2000),
292+
},
293+
Error: &parser.Error{
294+
Message: longText(2000),
295+
Type: longText(2000),
296+
Body: longText(2000),
297+
},
298+
},
299+
},
300+
},
301+
},
302+
},
303+
},
304+
}
305+
cmd := createCmd(1000, false)
306+
307+
cli.ApplyOutputTrimming(result, cmd)
308+
309+
test := result.TestResults[0].Suites[0].Tests[0]
310+
assert.Contains(t, test.Failure.Message, "...[truncated]...")
311+
assert.Contains(t, test.Failure.Body, "...[truncated]...")
312+
assert.Contains(t, test.Error.Message, "...[truncated]...")
313+
assert.Contains(t, test.Error.Body, "...[truncated]...")
314+
})
315+
}
316+
154317
func TestWriteToFilePath(t *testing.T) {
155318
tr := parser.TestResults{
156319
ID: "1234",

0 commit comments

Comments
 (0)