Skip to content

Commit 50e7748

Browse files
committed
Report failing fuzz tests
1 parent dc53526 commit 50e7748

5 files changed

+82
-12
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ will run the command:
9090

9191
`go test -json -run ParseJSON`
9292

93-
You can supply a list of packages to test, or any other arguments or flags understood by `go test`. However, `gotestdox` only prints events about *tests* (ignoring benchmarks and examples). It doesn't report fuzz tests, since they don't tend to have useful names.
93+
You can supply a list of packages to test, or any other arguments or flags understood by `go test`. However, `gotestdox` only prints events about *tests* (ignoring benchmarks and examples).
94+
95+
Since fuzz test cases are autogenerated and don't tend to have useful names, these are not included in `gotestdox` output unless they are failing.
9496

9597
## Multiple packages
9698

gotestdox.go

+32-8
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func (td *TestDoxer) Filter() {
127127
})
128128
for _, r := range tests {
129129
fmt.Fprintln(td.Stdout, r.String())
130-
if r.Action == "fail" {
130+
if r.Action == ActionFail {
131131
for _, line := range outputs[r.Test] {
132132
fmt.Fprint(td.Stdout, line)
133133
}
@@ -136,10 +136,10 @@ func (td *TestDoxer) Filter() {
136136
fmt.Fprintln(td.Stdout)
137137
case event.IsOutput():
138138
outputs[event.Test] = append(outputs[event.Test], event.Output)
139-
case event.IsTestResult():
139+
case event.IsTestResult(), event.IsFuzzFail():
140140
event.Sentence = Prettify(event.Test)
141141
results[event.Package] = append(results[event.Package], event)
142-
if event.Action == "fail" {
142+
if event.Action == ActionFail {
143143
td.OK = false
144144
}
145145
}
@@ -158,6 +158,11 @@ func ParseJSON(line string) (Event, error) {
158158
return event, nil
159159
}
160160

161+
const (
162+
ActionPass = "pass"
163+
ActionFail = "fail"
164+
)
165+
161166
// Event represents a Go test event as recorded by the 'go test -json' command.
162167
// It does not attempt to unmarshal all the data, only those fields it needs to
163168
// know about. It is based on the (unexported) 'event' struct used by Go's
@@ -184,7 +189,7 @@ type Event struct {
184189
// set, check marks will be shown in green and x's in red.
185190
func (e Event) String() string {
186191
status := color.RedString("x")
187-
if e.Action == "pass" {
192+
if e.Action == ActionPass {
188193
status = color.GreenString("✔")
189194
}
190195
return fmt.Sprintf(" %s %s (%.2fs)", status, e.Sentence, e.Elapsed)
@@ -195,24 +200,43 @@ func (e Event) String() string {
195200
// (for example, examples) are ignored, and all events on tests other than pass
196201
// or fail events (for example, run or pause events) are also ignored.
197202
func (e Event) IsTestResult() bool {
198-
// Skip events on benchmarks and examples
199-
if !strings.HasPrefix(e.Test, "Test") {
203+
// Skip events on benchmarks, examples, and fuzz tests
204+
if strings.HasPrefix(e.Test, "Benchmark") {
205+
return false
206+
}
207+
if strings.HasPrefix(e.Test, "Example") {
208+
return false
209+
}
210+
if strings.HasPrefix(e.Test, "Fuzz") {
211+
return false
212+
}
213+
if e.Test == "" {
200214
return false
201215
}
202-
if e.Action == "pass" || e.Action == "fail" {
216+
if e.Action == ActionPass || e.Action == ActionFail {
203217
return true
204218
}
205219
return false
206220
}
207221

222+
func (e Event) IsFuzzFail() bool {
223+
if !strings.HasPrefix(e.Test, "Fuzz") {
224+
return false
225+
}
226+
if e.Action != ActionFail {
227+
return false
228+
}
229+
return true
230+
}
231+
208232
// IsPackageResult determines whether or not the test event is a package pass
209233
// or fail event. That is, whether it indicates the passing or failing of a
210234
// package as a whole, rather than some individual test within the package.
211235
func (e Event) IsPackageResult() bool {
212236
if e.Test != "" {
213237
return false
214238
}
215-
if e.Action == "pass" || e.Action == "fail" {
239+
if e.Action == ActionPass || e.Action == ActionFail {
216240
return true
217241
}
218242
return false

gotestdox_test.go

+34
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,36 @@ func TestEventString_FormatsPassAndFailEventsDifferently(t *testing.T) {
7070
}
7171
}
7272

73+
func TestIsFuzzFail_IsTrueForFuzzFailEvents(t *testing.T) {
74+
t.Parallel()
75+
event := gotestdox.Event{
76+
Action: "fail",
77+
Test: "FuzzBar",
78+
}
79+
if !event.IsFuzzFail() {
80+
t.Errorf("false for %q event on %q", event.Action, event.Test)
81+
}
82+
}
83+
84+
func TestIsFuzzFail_IsFalseForNonFuzzFailEvents(t *testing.T) {
85+
t.Parallel()
86+
tcs := []gotestdox.Event{
87+
{
88+
Action: "pass",
89+
Test: "FuzzBar",
90+
},
91+
{
92+
Action: "fail",
93+
Test: "TestFooDoesX",
94+
},
95+
}
96+
for _, event := range tcs {
97+
if event.IsFuzzFail() {
98+
t.Errorf("true for %q event on %q", event.Action, event.Test)
99+
}
100+
}
101+
}
102+
73103
func TestIsTestResult_IsTrueForTestPassOrFailEvents(t *testing.T) {
74104
t.Parallel()
75105
tcs := []gotestdox.Event{
@@ -108,6 +138,10 @@ func TestIsTestResult_IsFalseForNonTestPassFailEvents(t *testing.T) {
108138
Action: "fail",
109139
Test: "",
110140
},
141+
{
142+
Action: "pass",
143+
Test: "FuzzBar",
144+
},
111145
{
112146
Action: "run",
113147
Test: "TestFooDoesX",

prettifier.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,24 @@ import (
5656
// (copious) debug information to the [DebugWriter] stream, elaborating on its
5757
// decisions.
5858
func Prettify(input string) string {
59+
var prefix string
5960
p := &prettifier{
60-
input: []rune(strings.TrimPrefix(input, "Test")),
6161
words: []string{},
6262
debug: io.Discard,
6363
}
6464
if os.Getenv("GOTESTDOX_DEBUG") != "" {
6565
p.debug = DebugWriter
6666
}
6767
p.log("input:", input)
68+
if strings.HasPrefix(input, "Fuzz") {
69+
input = strings.TrimPrefix(input, "Fuzz")
70+
prefix = "[fuzz] "
71+
}
72+
p.input = []rune(strings.TrimPrefix(input, "Test"))
6873
for state := betweenWords; state != nil; {
6974
state = state(p)
7075
}
71-
result := strings.Join(p.words, " ")
76+
result := prefix + strings.Join(p.words, " ")
7277
p.log(fmt.Sprintf("result: %q", result))
7378
return result
7479
}
@@ -127,7 +132,7 @@ func (p *prettifier) emit() {
127132
word := string(p.input[p.start:p.pos])
128133
switch {
129134
case len(p.words) == 0:
130-
// This is the first word
135+
// This is the first word, capitalise it
131136
word = cases.Title(language.Und, cases.NoLower).String(word)
132137
case len(word) == 1:
133138
// Single letter word such as A

prettifier_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -234,4 +234,9 @@ var Cases = []struct {
234234
input: "TestShiftTransforms255To0",
235235
want: "Shift transforms 255 to 0",
236236
},
237+
{
238+
name: "correctly formats fuzz test names",
239+
input: "FuzzPrettify",
240+
want: "[fuzz] Prettify",
241+
},
237242
}

0 commit comments

Comments
 (0)