-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexpect.go
More file actions
152 lines (138 loc) · 5.36 KB
/
Copy pathexpect.go
File metadata and controls
152 lines (138 loc) · 5.36 KB
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package be
// expect.go provides a native, dependency-free assertion runner so `be` matchers
// can be used with the standard library `testing` package alone — no ginkgo,
// gomega or testify import required. It preserves the brand sentence:
//
// be.Expect(t, actual).To(be.GreaterThan(3)) // "expect ... to be greater than 3"
//
// The matcher core stays gomega-backed internally; only the failure message is
// reshaped to read natively (see internal/beformat).
import (
"fmt"
"github.com/expectto/be/internal/beformat"
"github.com/expectto/be/internal/psi"
)
// TestingT is the minimal subset of *testing.T the native driver needs.
// *testing.T satisfies it; tests can supply a fake. Mirrors testify's approach
// so the runner never imports the heavyweight `testing` package contract.
type TestingT interface {
Helper()
Errorf(format string, args ...any)
Fatalf(format string, args ...any)
}
// Expectation is a TestingT-bound assertion produced by Expect or Require.
type Expectation struct {
t TestingT
actual any
fatal bool
}
// Expect begins a soft assertion: a failure is reported via Errorf and the test
// continues (assert-style).
func Expect(t TestingT, actual any) *Expectation {
return &Expectation{t: t, actual: actual}
}
// Require begins a hard assertion: the first failure stops the test via Fatalf
// (require-style).
func Require(t TestingT, actual any) *Expectation {
return &Expectation{t: t, actual: actual, fatal: true}
}
// AssertThat is the flat, testify-style spelling of Expect(t, actual).To(matcher):
// a soft assertion that reports via Errorf and lets the test continue. It is the
// drop-in for testify's assert when you want a be matcher:
//
// assert.Equal(t, want, got) // testify
// be.AssertThat(t, got, be.Eq(want)) // be — and now `got` can face any matcher
//
// The subject (actual) comes first and the expected value lives inside the
// matcher, so unlike testify's Equal there is no want/got order to get wrong.
// An optional message provides failure context (see To). Returns true on success.
func AssertThat(t TestingT, actual, matcher any, msgAndArgs ...any) bool {
t.Helper()
return Expect(t, actual).To(matcher, msgAndArgs...)
}
// RequireThat is the flat, testify-style spelling of Require(t, actual).To(matcher):
// a hard assertion that stops the test on the first failure via Fatalf. See
// AssertThat for the argument-order rationale. Returns true on success.
func RequireThat(t TestingT, actual, matcher any, msgAndArgs ...any) bool {
t.Helper()
return Require(t, actual).To(matcher, msgAndArgs...)
}
// To asserts that actual satisfies the matcher. The matcher may be a be/gomega/
// gomock matcher or a raw value (wrapped via Psi, like the rest of be). An
// optional message — a format string plus args, or plain values — is prepended to
// the failure output for context. Returns true on success.
func (e *Expectation) To(matcher any, msgAndArgs ...any) bool {
e.t.Helper()
m := psi.Psi(matcher)
ok, err := m.Match(e.actual)
if err != nil {
return e.fail(err.Error(), msgAndArgs...)
}
if !ok {
return e.fail(beformat.Compact(m.FailureMessage(e.actual)), msgAndArgs...)
}
return true
}
// NotTo asserts that actual does NOT satisfy the matcher. An optional message
// provides failure context (see To).
func (e *Expectation) NotTo(matcher any, msgAndArgs ...any) bool {
e.t.Helper()
m := psi.Psi(matcher)
ok, err := m.Match(e.actual)
if err != nil {
return e.fail(err.Error(), msgAndArgs...)
}
if ok {
return e.fail(beformat.Compact(m.NegatedFailureMessage(e.actual)), msgAndArgs...)
}
return true
}
// ToNot is an alias for NotTo.
func (e *Expectation) ToNot(matcher any, msgAndArgs ...any) bool {
e.t.Helper()
return e.NotTo(matcher, msgAndArgs...)
}
func (e *Expectation) fail(msg string, msgAndArgs ...any) bool {
e.t.Helper()
if ctx := formatMsgAndArgs(msgAndArgs...); ctx != "" {
msg = ctx + ": " + msg
}
if e.fatal {
e.t.Fatalf("%s", msg)
} else {
e.t.Errorf("%s", msg)
}
return false
}
// formatMsgAndArgs renders an optional assertion message: a leading format string
// is applied to the remaining args (testify-style), otherwise the values are
// concatenated.
//
// The message contract for every assertion func taking msgAndArgs: the first
// value MAY be a Printf format string for the rest — but go vet cannot validate
// it (vet's printf checker only understands an explicit `format string` param).
// To keep vet from misclassifying the whole assertion chain as fmt.Sprint-style
// "print wrappers" — which makes any message containing a '%' directive fail
// `go vet` (and therefore `go test`) with "possible Printf formatting directive"
// — msgAndArgs must never be ellipsis-forwarded straight into a fmt print
// function from here or anywhere up the chain. The `vals` indirection below is
// what breaks vet's forwarding heuristic; the directive-bearing messages in
// expect_shortcuts_test.go are the regression guard (go test runs vet, so a
// regression here fails the build).
func formatMsgAndArgs(msgAndArgs ...any) string {
switch len(msgAndArgs) {
case 0:
return ""
case 1:
if s, ok := msgAndArgs[0].(string); ok {
return s
}
return fmt.Sprint(msgAndArgs[0])
default:
if format, ok := msgAndArgs[0].(string); ok {
return fmt.Sprintf(format, msgAndArgs[1:]...)
}
vals := msgAndArgs // see doc-comment: intentionally not forwarded directly
return fmt.Sprint(vals...)
}
}