-
Notifications
You must be signed in to change notification settings - Fork 1
/
xfail.go
89 lines (72 loc) · 1.96 KB
/
xfail.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// Package xfail provides testing helpers for expected tests failures.
package xfail
import (
"sync/atomic"
"testing"
)
// XFail returns a new [testing.TB] instance that expects the test to fail for the given reason.
//
// At the end of the test, if it was marked as failed using non-fatal methods Fail, Error, or Errorf,
// it will pass instead.
// If it wasn't marked as failed, it will fail so that XFail call can be removed.
// Fatal methods FailNow, Fatal, or Fatalf will skip the rest of the test instead.
func XFail(tb testing.TB, reason string) testing.TB {
tb.Helper()
if reason == "" {
tb.Fatal("XFail reason can't be empty")
}
x := &xfail{
TB: tb,
}
x.TB.Cleanup(func() {
if x.failed.Load() {
x.TB.Logf("Test failed as expected: %s", reason)
return
}
x.TB.Fatalf("Test passed unexpectedly: %s", reason)
})
return x
}
// xfail wraps [testing.TB] with expected failure logic.
type xfail struct {
testing.TB
failed atomic.Bool // we can't access testing.common.failed/skipped/etc fields
}
// Failed reports whether the function has failed.
func (x *xfail) Failed() bool {
return x.failed.Load()
}
// Fail marks the function as having failed but continues execution.
func (x *xfail) Fail() {
x.failed.Store(true)
}
// Error is equivalent to Log followed by Fail.
func (x *xfail) Error(args ...any) {
x.Log(args...)
x.Fail()
}
// Errorf is equivalent to Logf followed by Fail.
func (x *xfail) Errorf(format string, args ...any) {
x.Logf(format, args...)
x.Fail()
}
// FailNow marks the function as having failed and stops its execution.
func (x *xfail) FailNow() {
x.Fail()
// runtime.Goexit would not work
x.SkipNow()
}
// Fatal is equivalent to Log followed by FailNow.
func (x *xfail) Fatal(args ...any) {
x.Log(args...)
x.FailNow()
}
// Fatalf is equivalent to Logf followed by FailNow.
func (x *xfail) Fatalf(format string, args ...any) {
x.Logf(format, args...)
x.FailNow()
}
// check interfaces
var (
_ testing.TB = (*xfail)(nil)
)