Skip to content

Commit 4de0c9f

Browse files
authored
Merge pull request #237 from dnephin/golden-vars
assert: golden variables
2 parents 814f970 + 82e8930 commit 4de0c9f

File tree

13 files changed

+373
-95
lines changed

13 files changed

+373
-95
lines changed

assert/assert_ext_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package assert_test
2+
3+
import (
4+
"go/parser"
5+
"go/token"
6+
"io/ioutil"
7+
"runtime"
8+
"strings"
9+
"testing"
10+
11+
"gotest.tools/v3/assert"
12+
"gotest.tools/v3/internal/source"
13+
)
14+
15+
func TestEqual_WithGoldenUpdate(t *testing.T) {
16+
t.Run("assert failed with -update=false", func(t *testing.T) {
17+
ft := &fakeTestingT{}
18+
actual := `not this value`
19+
assert.Equal(ft, actual, expectedOne)
20+
assert.Assert(t, ft.failNowed)
21+
})
22+
23+
t.Run("var is updated when -update=true", func(t *testing.T) {
24+
patchUpdate(t)
25+
t.Cleanup(func() {
26+
resetVariable(t, "expectedOne", "")
27+
})
28+
29+
actual := `this is the
30+
actual value
31+
that we are testing
32+
`
33+
assert.Equal(t, actual, expectedOne)
34+
35+
raw, err := ioutil.ReadFile(fileName(t))
36+
assert.NilError(t, err)
37+
38+
expected := "var expectedOne = `this is the\nactual value\nthat we are testing\n`"
39+
assert.Assert(t, strings.Contains(string(raw), expected), "actual=%v", string(raw))
40+
})
41+
42+
t.Run("const is updated when -update=true", func(t *testing.T) {
43+
patchUpdate(t)
44+
t.Cleanup(func() {
45+
resetVariable(t, "expectedTwo", "")
46+
})
47+
48+
actual := `this is the new
49+
expected value
50+
`
51+
assert.Equal(t, actual, expectedTwo)
52+
53+
raw, err := ioutil.ReadFile(fileName(t))
54+
assert.NilError(t, err)
55+
56+
expected := "const expectedTwo = `this is the new\nexpected value\n`"
57+
assert.Assert(t, strings.Contains(string(raw), expected), "actual=%v", string(raw))
58+
})
59+
}
60+
61+
// expectedOne is updated by running the tests with -update
62+
var expectedOne = ``
63+
64+
// expectedTwo is updated by running the tests with -update
65+
const expectedTwo = ``
66+
67+
func patchUpdate(t *testing.T) {
68+
source.Update = true
69+
t.Cleanup(func() {
70+
source.Update = false
71+
})
72+
}
73+
74+
func fileName(t *testing.T) string {
75+
t.Helper()
76+
_, filename, _, ok := runtime.Caller(1)
77+
assert.Assert(t, ok, "failed to get call stack")
78+
return filename
79+
}
80+
81+
func resetVariable(t *testing.T, varName string, value string) {
82+
t.Helper()
83+
_, filename, _, ok := runtime.Caller(1)
84+
assert.Assert(t, ok, "failed to get call stack")
85+
86+
fileset := token.NewFileSet()
87+
astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors|parser.ParseComments)
88+
assert.NilError(t, err)
89+
90+
err = source.UpdateVariable(filename, fileset, astFile, varName, value)
91+
assert.NilError(t, err, "failed to reset file")
92+
}
93+
94+
type fakeTestingT struct {
95+
failNowed bool
96+
failed bool
97+
msgs []string
98+
}
99+
100+
func (f *fakeTestingT) FailNow() {
101+
f.failNowed = true
102+
}
103+
104+
func (f *fakeTestingT) Fail() {
105+
f.failed = true
106+
}
107+
108+
func (f *fakeTestingT) Log(args ...interface{}) {
109+
f.msgs = append(f.msgs, args[0].(string))
110+
}
111+
112+
func (f *fakeTestingT) Helper() {}

assert/cmp/compare.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison {
3535
if diff == "" {
3636
return ResultSuccess
3737
}
38-
return multiLineDiffResult(diff)
38+
return multiLineDiffResult(diff, x, y)
3939
}
4040
}
4141

@@ -102,7 +102,7 @@ func Equal(x, y interface{}) Comparison {
102102
return ResultSuccess
103103
case isMultiLineStringCompare(x, y):
104104
diff := format.UnifiedDiff(format.DiffConfig{A: x.(string), B: y.(string)})
105-
return multiLineDiffResult(diff)
105+
return multiLineDiffResult(diff, x, y)
106106
}
107107
return ResultFailureTemplate(`
108108
{{- printf "%v" .Data.x}} (
@@ -128,12 +128,12 @@ func isMultiLineStringCompare(x, y interface{}) bool {
128128
return strings.Contains(strX, "\n") || strings.Contains(strY, "\n")
129129
}
130130

131-
func multiLineDiffResult(diff string) Result {
131+
func multiLineDiffResult(diff string, x, y interface{}) Result {
132132
return ResultFailureTemplate(`
133133
--- {{ with callArg 0 }}{{ formatNode . }}{{else}}←{{end}}
134134
+++ {{ with callArg 1 }}{{ formatNode . }}{{else}}→{{end}}
135135
{{ .Data.diff }}`,
136-
map[string]interface{}{"diff": diff})
136+
map[string]interface{}{"diff": diff, "x": x, "y": y})
137137
}
138138

139139
// Len succeeds if the sequence has the expected length.

assert/cmp/result.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ func (r templatedResult) FailureMessage(args []ast.Expr) string {
6969
return msg
7070
}
7171

72+
func (r templatedResult) UpdatedExpected(stackIndex int) error {
73+
// TODO: would be nice to have structured data instead of a map
74+
return source.UpdateExpectedValue(stackIndex+1, r.data["x"], r.data["y"])
75+
}
76+
7277
// ResultFailureTemplate returns a Result with a template string and data which
7378
// can be used to format a failure message. The template may access data from .Data,
7479
// the comparison args with the callArg function, and the formatNode function may

golden/golden.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@ import (
1818
"gotest.tools/v3/assert"
1919
"gotest.tools/v3/assert/cmp"
2020
"gotest.tools/v3/internal/format"
21+
"gotest.tools/v3/internal/source"
2122
)
2223

23-
var flagUpdate bool
24-
2524
func init() {
26-
flag.BoolVar(&flagUpdate, "update", false, "update golden files")
27-
flag.BoolVar(&flagUpdate, "test.update-golden", false, "deprecated flag")
25+
flag.BoolVar(&source.Update, "test.update-golden", false, "deprecated flag")
2826
}
2927

3028
type helperT interface {
@@ -46,7 +44,7 @@ var NormalizeCRLFToLF = os.Getenv("GOTESTTOOLS_GOLDEN_NormalizeCRLFToLF") != "fa
4644

4745
// FlagUpdate returns true when the -update flag has been set.
4846
func FlagUpdate() bool {
49-
return flagUpdate
47+
return source.Update
5048
}
5149

5250
// Open opens the file in ./testdata
@@ -180,7 +178,7 @@ func compare(actual []byte, filename string) (cmp.Result, []byte) {
180178
}
181179

182180
func update(filename string, actual []byte) error {
183-
if !flagUpdate {
181+
if !source.Update {
184182
return nil
185183
}
186184
if dir := filepath.Dir(Path(filename)); dir != "." {

golden/golden_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"gotest.tools/v3/assert"
1010
"gotest.tools/v3/assert/cmp"
1111
"gotest.tools/v3/fs"
12+
"gotest.tools/v3/internal/source"
1213
)
1314

1415
type fakeT struct {
@@ -190,10 +191,10 @@ func TestGoldenAssertBytes(t *testing.T) {
190191
}
191192

192193
func setUpdateFlag(t *testing.T) func() {
193-
orig := flagUpdate
194-
flagUpdate = true
194+
orig := source.Update
195+
source.Update = true
195196
undo := func() {
196-
flagUpdate = orig
197+
source.Update = orig
197198
}
198199
t.Cleanup(undo)
199200
return undo

icmd/command_test.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
exec "golang.org/x/sys/execabs"
1313
"gotest.tools/v3/assert"
1414
"gotest.tools/v3/fs"
15-
"gotest.tools/v3/golden"
1615
"gotest.tools/v3/internal/maint"
1716
)
1817

@@ -120,9 +119,22 @@ func TestResult_Match_NotMatched(t *testing.T) {
120119
}
121120
err := result.match(exp)
122121
assert.ErrorContains(t, err, "Failures")
123-
golden.Assert(t, err.Error(), "result-match-no-match.golden")
122+
assert.Equal(t, err.Error(), expectedMatch)
124123
}
125124

125+
var expectedMatch = `
126+
Command: binary arg1
127+
ExitCode: 99 (timeout)
128+
Error: exit code 99
129+
Stdout: the output
130+
Stderr: the stderr
131+
132+
Failures:
133+
ExitCode was 99 expected 101
134+
Expected command to finish, but it hit the timeout
135+
Expected stdout to contain "Something else"
136+
Expected stderr to contain "[NOTHING]"`
137+
126138
func newLockedBuffer(s string) *lockedBuffer {
127139
return &lockedBuffer{buf: *bytes.NewBufferString(s)}
128140
}
@@ -140,9 +152,20 @@ func TestResult_Match_NotMatchedNoError(t *testing.T) {
140152
}
141153
err := result.match(exp)
142154
assert.ErrorContains(t, err, "Failures")
143-
golden.Assert(t, err.Error(), "result-match-no-match-no-error.golden")
155+
assert.Equal(t, err.Error(), expectedResultMatchNoMatch)
144156
}
145157

158+
var expectedResultMatchNoMatch = `
159+
Command: binary arg1
160+
ExitCode: 0
161+
Stdout: the output
162+
Stderr: the stderr
163+
164+
Failures:
165+
ExitCode was 0 expected 101
166+
Expected stdout to contain "Something else"
167+
Expected stderr to contain "[NOTHING]"`
168+
146169
func TestResult_Match_Match(t *testing.T) {
147170
result := &Result{
148171
Cmd: exec.Command("binary", "arg1"),

icmd/testdata/result-match-no-match-no-error.golden

Lines changed: 0 additions & 10 deletions
This file was deleted.

icmd/testdata/result-match-no-match.golden

Lines changed: 0 additions & 12 deletions
This file was deleted.

internal/assert/result.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package assert
22

33
import (
4+
"errors"
45
"fmt"
56
"go/ast"
67

@@ -25,6 +26,22 @@ func RunComparison(
2526
return true
2627
}
2728

29+
if source.Update {
30+
if updater, ok := result.(updateExpected); ok {
31+
const stackIndex = 3 // Assert/Check, assert, RunComparison
32+
err := updater.UpdatedExpected(stackIndex)
33+
switch {
34+
case err == nil:
35+
return true
36+
case errors.Is(err, source.ErrNotFound):
37+
// do nothing, fallthrough to regular failure message
38+
default:
39+
t.Log("failed to update source", err)
40+
return false
41+
}
42+
}
43+
}
44+
2845
var message string
2946
switch typed := result.(type) {
3047
case resultWithComparisonArgs:
@@ -52,6 +69,10 @@ type resultBasic interface {
5269
FailureMessage() string
5370
}
5471

72+
type updateExpected interface {
73+
UpdatedExpected(stackIndex int) error
74+
}
75+
5576
// filterPrintableExpr filters the ast.Expr slice to only include Expr that are
5677
// easy to read when printed and contain relevant information to an assertion.
5778
//

internal/source/defers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func guessDefer(node ast.Node) (ast.Node, error) {
2828
defers := collectDefers(node)
2929
switch len(defers) {
3030
case 0:
31-
return nil, fmt.Errorf("failed to expression in defer")
31+
return nil, fmt.Errorf("failed to find expression in defer")
3232
case 1:
3333
return defers[0].Call, nil
3434
default:

0 commit comments

Comments
 (0)