Skip to content

Commit 7bc3b8b

Browse files
committed
feat!: make Error and Fatal compatible with testing.T
Change Error and Fatal to accept args ...any instead of err error, matching testing.T and allowing shared test helpers between f1 scenarios and standard go tests. - Error, Errorf, Fatal, Fatalf log at ERROR level (not via Log/Logf) - Log/Logf use fmt.Sprintln for formatting (aligned with testing.T) - Log/Logf add iteration and vuid context to all output - Add commonTInterface for compile-time signature verification - Add tests for exact logging format (level, msg, iteration, vuid) Based on #286 by @sirockin BREAKING CHANGE: Error(err error) and Fatal(err error) are now Error(args ...any) and Fatal(args ...any). Single-error call sites remain valid: Error(err) and Fatal(err) still compile and behave as before. Closes #278 Closes #286
1 parent 7cb1b4e commit 7bc3b8b

4 files changed

Lines changed: 248 additions & 66 deletions

File tree

internal/run/run_cmd_test.go

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -831,9 +831,11 @@ func TestOutput_JSONLogging(t *testing.T) {
831831

832832
scenarioOnlyLogs := []logFieldMatchers{
833833
{
834-
"message": "setup",
835-
"level": "info",
836-
"scenario": "scenario_where_each_iteration_takes_200ms",
834+
"message": "setup",
835+
"level": "info",
836+
"scenario": "scenario_where_each_iteration_takes_200ms",
837+
"iteration": "setup",
838+
"vuid": float64(-1),
837839
},
838840
{
839841
"message": "slog - setup",
@@ -843,9 +845,11 @@ func TestOutput_JSONLogging(t *testing.T) {
843845
},
844846

845847
{
846-
"message": "first iteration",
847-
"level": "info",
848-
"scenario": "scenario_where_each_iteration_takes_200ms",
848+
"message": "first iteration",
849+
"level": "info",
850+
"scenario": "scenario_where_each_iteration_takes_200ms",
851+
"iteration": "setup",
852+
"vuid": float64(-1),
849853
},
850854
{
851855
"message": "slog - first iteration",
@@ -862,9 +866,11 @@ func TestOutput_JSONLogging(t *testing.T) {
862866
"scenario": "scenario_where_each_iteration_takes_200ms",
863867
},
864868
{
865-
"message": "setup",
866-
"level": "info",
867-
"scenario": "scenario_where_each_iteration_takes_200ms",
869+
"message": "setup",
870+
"level": "info",
871+
"scenario": "scenario_where_each_iteration_takes_200ms",
872+
"iteration": "setup",
873+
"vuid": float64(-1),
868874
},
869875
{
870876
"message": "slog - setup",
@@ -874,9 +880,11 @@ func TestOutput_JSONLogging(t *testing.T) {
874880
},
875881

876882
{
877-
"message": "first iteration",
878-
"level": "info",
879-
"scenario": "scenario_where_each_iteration_takes_200ms",
883+
"message": "first iteration",
884+
"level": "info",
885+
"scenario": "scenario_where_each_iteration_takes_200ms",
886+
"iteration": "setup",
887+
"vuid": float64(-1),
880888
},
881889
{
882890
"message": "slog - first iteration",

internal/run/run_stage_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ type parsedLogLine struct {
6464
}
6565

6666
type (
67-
logFieldMatchers map[string]string
67+
logFieldMatchers map[string]any
6868
)
6969

7070
type RunTestStage struct {
@@ -739,7 +739,7 @@ func (s *RunTestStage) assertJSONLogMatches(t *testing.T, output string, expecte
739739
matchers := expectedLogLines[lineIndex]
740740
for key, value := range matchers {
741741
if value != anyValue {
742-
s.assert.Equal(value, parsedLine.parsed[key])
742+
s.assert.Equalf(value, parsedLine.parsed[key], "field %q: expected %v, got %v in %s", key, value, parsedLine.parsed[key], parsedLine.raw)
743743
}
744744
}
745745

pkg/f1/f1testing/t.go

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"log/slog"
77
"runtime/debug"
8+
"strings"
89
"sync/atomic"
910

1011
"github.com/stretchr/testify/require"
@@ -117,40 +118,45 @@ func (t *T) Fail() {
117118
}
118119
}
119120

120-
// Errorf is equivalent to Logf followed by Fail.
121+
// Errorf is equivalent to Logf followed by Fail. Logs at Error level.
121122
func (t *T) Errorf(format string, args ...any) {
122-
t.logger.Error(fmt.Sprintf(format, args...))
123+
t.logger.With(log.IterationAttr(t.Iteration), log.VUIDAttr(t.VUID)).Error(fmt.Sprintf(format, args...))
123124
t.Fail()
124125
}
125126

126-
// Error is equivalent to Log followed by Fail.
127-
func (t *T) Error(err error) {
128-
t.logger.Error("iteration failed", log.IterationAttr(t.Iteration), log.VUIDAttr(t.VUID), log.ErrorAttr(err))
127+
// Error is equivalent to Log followed by Fail. Logs at Error level.
128+
func (t *T) Error(args ...any) {
129+
msg := strings.TrimSuffix(fmt.Sprintln(args...), "\n")
130+
t.logger.With(log.IterationAttr(t.Iteration), log.VUIDAttr(t.VUID)).Error(msg)
129131
t.Fail()
130132
}
131133

132-
// Fatalf is equivalent to Logf followed by FailNow.
134+
// Fatalf is equivalent to Logf followed by FailNow. Logs at Error level.
133135
func (t *T) Fatalf(format string, args ...any) {
134-
t.logger.Error(fmt.Sprintf(format, args...))
136+
t.logger.With(log.IterationAttr(t.Iteration), log.VUIDAttr(t.VUID)).Error(fmt.Sprintf(format, args...))
135137
t.FailNow()
136138
}
137139

138-
// Fatal is equivalent to Log followed by FailNow.
139-
func (t *T) Fatal(err error) {
140-
t.logger.Error("iteration failed", log.IterationAttr(t.Iteration), log.VUIDAttr(t.VUID), log.ErrorAttr(err))
140+
// Fatal is equivalent to Log followed by FailNow. Logs at Error level.
141+
func (t *T) Fatal(args ...any) {
142+
msg := strings.TrimSuffix(fmt.Sprintln(args...), "\n")
143+
t.logger.With(log.IterationAttr(t.Iteration), log.VUIDAttr(t.VUID)).Error(msg)
141144
t.FailNow()
142145
}
143146

144147
// Log formats its arguments using default formatting, analogous to Println, and records the text in the error log.
145148
// The text will be printed only if f1 is running in verbose mode.
149+
// Aligns with testing.T: uses fmt.Sprintln for space-separated args (trailing newline trimmed for structured logs).
146150
func (t *T) Log(args ...any) {
147-
t.logger.Info(fmt.Sprint(args...))
151+
msg := strings.TrimSuffix(fmt.Sprintln(args...), "\n")
152+
t.logger.With(log.IterationAttr(t.Iteration), log.VUIDAttr(t.VUID)).Info(msg)
148153
}
149154

150155
// Logf formats its arguments according to the format, analogous to Printf, and records the text in the error log.
151156
// A final newline is added if not provided. The text will be printed only if f1 is running in verbose mode.
157+
// Aligns with testing.T: uses fmt.Sprintf.
152158
func (t *T) Logf(format string, args ...any) {
153-
t.logger.Info(fmt.Sprintf(format, args...))
159+
t.logger.With(log.IterationAttr(t.Iteration), log.VUIDAttr(t.VUID)).Info(fmt.Sprintf(format, args...))
154160
}
155161

156162
// Failed reports whether the function has failed.

0 commit comments

Comments
 (0)