Skip to content

Commit ddca10c

Browse files
authored
feat(op-acceptor): better error logging for failing tests. (#254)
1 parent ef1f296 commit ddca10c

File tree

4 files changed

+232
-97
lines changed

4 files changed

+232
-97
lines changed

op-acceptor/nat.go

+26-16
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,6 @@ func New(ctx context.Context, config *Config, version string, shutdownCallback f
4545
return nil, errors.New("config is required")
4646
}
4747

48-
config.Log.Debug("Creating NAT with config",
49-
"testDir", config.TestDir,
50-
"validatorConfig", config.ValidatorConfig,
51-
"runInterval", config.RunInterval,
52-
"runOnce", config.RunOnce,
53-
"allowSkips", config.AllowSkips)
54-
5548
reg, err := registry.NewRegistry(registry.Config{
5649
Log: config.Log,
5750
ValidatorConfigFile: config.ValidatorConfig,
@@ -72,7 +65,16 @@ func New(ctx context.Context, config *Config, version string, shutdownCallback f
7265
if err != nil {
7366
return nil, fmt.Errorf("failed to create test runner: %w", err)
7467
}
75-
config.Log.Info("nat.New: created registry and test runner")
68+
69+
config.Log.Debug("Created NAT with config",
70+
"testDir", config.TestDir,
71+
"validatorConfig", config.ValidatorConfig,
72+
"targetGate", config.TargetGate,
73+
"runInterval", config.RunInterval,
74+
"runOnce", config.RunOnce,
75+
"allowSkips", config.AllowSkips,
76+
"goBinary", config.GoBinary,
77+
)
7678

7779
return &nat{
7880
ctx: ctx,
@@ -124,9 +126,8 @@ func (n *nat) Start(ctx context.Context) error {
124126

125127
// Check if any tests failed and return appropriate exit code
126128
if n.result != nil && n.result.Status == types.TestStatusFail {
127-
n.config.Log.Warn("Run-once test run completed with failures, returning exit code 1")
128-
// Return exit code 1 for test failures (assertions failed)
129-
return NewTestFailureError(n.result.String())
129+
n.config.Log.Warn("Run-once test run completed with failures")
130+
return NewTestFailureError("Run-once test run completed with failures")
130131
}
131132

132133
// Only need to call this when we're in run-once mode and all tests passed
@@ -260,6 +261,7 @@ func (n *nat) printResultsTable(runID string) {
260261
gate.Stats.Skipped,
261262
getResultString(gate.Status),
262263
"",
264+
"", // No stdout for gates
263265
})
264266

265267
// Print suites in this gate
@@ -274,6 +276,7 @@ func (n *nat) printResultsTable(runID string) {
274276
suite.Stats.Skipped,
275277
getResultString(suite.Status),
276278
"",
279+
"", // No stdout for suites
277280
})
278281

279282
// Print tests in this suite
@@ -297,7 +300,7 @@ func (n *nat) printResultsTable(runID string) {
297300
boolToInt(test.Status == types.TestStatusFail),
298301
boolToInt(test.Status == types.TestStatusSkip),
299302
getResultString(test.Status),
300-
test.Error,
303+
getErrorMessage(test.Status, test.Error),
301304
})
302305

303306
// Display individual sub-tests if present (for package tests)
@@ -318,7 +321,7 @@ func (n *nat) printResultsTable(runID string) {
318321
boolToInt(subTest.Status == types.TestStatusFail),
319322
boolToInt(subTest.Status == types.TestStatusSkip),
320323
getResultString(subTest.Status),
321-
subTest.Error,
324+
getErrorMessage(subTest.Status, subTest.Error),
322325
})
323326
j++
324327
}
@@ -349,7 +352,7 @@ func (n *nat) printResultsTable(runID string) {
349352
boolToInt(test.Status == types.TestStatusFail),
350353
boolToInt(test.Status == types.TestStatusSkip),
351354
getResultString(test.Status),
352-
test.Error,
355+
getErrorMessage(test.Status, test.Error),
353356
})
354357

355358
// Display individual sub-tests if present (for package tests)
@@ -370,12 +373,11 @@ func (n *nat) printResultsTable(runID string) {
370373
boolToInt(subTest.Status == types.TestStatusFail),
371374
boolToInt(subTest.Status == types.TestStatusSkip),
372375
getResultString(subTest.Status),
373-
subTest.Error,
376+
getErrorMessage(subTest.Status, subTest.Error),
374377
})
375378
j++
376379
}
377380
}
378-
379381
i++
380382
}
381383

@@ -443,6 +445,14 @@ func formatDuration(d time.Duration) string {
443445
return fmt.Sprintf("%.1fs", d.Seconds())
444446
}
445447

448+
// getErrorMessage returns the error message if test failed, empty string otherwise
449+
func getErrorMessage(status types.TestStatus, err error) string {
450+
if status == types.TestStatusFail && err != nil {
451+
return err.Error()
452+
}
453+
return ""
454+
}
455+
446456
// WaitForShutdown blocks until all goroutines have terminated.
447457
// This is useful in tests to ensure complete cleanup before moving to the next test.
448458
func (n *nat) WaitForShutdown(ctx context.Context) error {

op-acceptor/runner/logging_test.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package runner
2+
3+
import (
4+
"testing"
5+
6+
"github.com/ethereum-optimism/infra/op-acceptor/types"
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
// TestFailingTestStdoutLogging verifies that stdout is captured when tests fail
12+
func TestFailingTestStdoutLogging(t *testing.T) {
13+
// Setup test with a failing test that outputs to stdout
14+
testContent := []byte(`
15+
package feature_test
16+
17+
import (
18+
"fmt"
19+
"testing"
20+
)
21+
22+
func TestWithStdout(t *testing.T) {
23+
fmt.Println("This is some stdout output that should be captured")
24+
fmt.Println("This is a second line of output")
25+
t.Error("This test deliberately fails")
26+
}
27+
`)
28+
29+
configContent := []byte(`
30+
gates:
31+
- id: logging-gate
32+
description: "Gate with a test that outputs to stdout"
33+
suites:
34+
logging-suite:
35+
description: "Suite with a failing test that outputs to stdout"
36+
tests:
37+
- name: TestWithStdout
38+
package: "./feature"
39+
`)
40+
41+
// Setup the test runner
42+
r := setupTestRunner(t, testContent, configContent)
43+
44+
// Run the test
45+
result, err := r.RunAllTests()
46+
require.NoError(t, err)
47+
assert.Equal(t, types.TestStatusFail, result.Status)
48+
49+
// Verify the structure
50+
require.Contains(t, result.Gates, "logging-gate")
51+
gate := result.Gates["logging-gate"]
52+
require.Contains(t, gate.Suites, "logging-suite")
53+
suite := gate.Suites["logging-suite"]
54+
55+
// Get the failing test
56+
var failingTest *types.TestResult
57+
for _, test := range suite.Tests {
58+
failingTest = test
59+
break
60+
}
61+
require.NotNil(t, failingTest)
62+
63+
// Verify the test failed and has stdout captured
64+
assert.Equal(t, types.TestStatusFail, failingTest.Status)
65+
assert.NotNil(t, failingTest.Error)
66+
assert.NotEmpty(t, failingTest.Stdout)
67+
assert.Contains(t, failingTest.Stdout, "This is some stdout output that should be captured")
68+
assert.Contains(t, failingTest.Stdout, "This is a second line of output")
69+
}

0 commit comments

Comments
 (0)