/
test.go
143 lines (128 loc) · 3.12 KB
/
test.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
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
// Package test contains helper functions that are useful for writing tests.
package test
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
)
// ErrorContains checks if the error message in out contains the text in
// want.
//
// This is safe when out is nil. Use an empty string for want if you want to
// test that err is nil.
func ErrorContains(out error, want string) bool {
if out == nil {
return want == ""
}
if want == "" {
return false
}
return strings.Contains(out.Error(), want)
}
// Read data from a file.
func Read(t *testing.T, paths ...string) []byte {
t.Helper()
path := filepath.Join(paths...)
file, err := ioutil.ReadFile(path)
if err != nil {
t.Fatalf("cannot read %v: %v", path, err)
}
return file
}
// TempFile creates a new temporary file and returns the path and a clean
// function to remove it.
//
// f, clean := TempFile("some\ndata")
// defer clean()
func TempFile(t *testing.T, data string) (string, func()) {
t.Helper()
fp, err := ioutil.TempFile(os.TempDir(), "gotest")
if err != nil {
t.Fatalf("test.TempFile: could not create file in %v: %v", os.TempDir(), err)
}
defer func() {
err := fp.Close()
if err != nil {
t.Fatalf("test.TempFile: close: %v", err)
}
}()
_, err = fp.WriteString(data)
if err != nil {
t.Fatalf("test.TempFile: write: %v", err)
}
return fp.Name(), func() {
err := os.Remove(fp.Name())
if err != nil {
t.Errorf("test.TempFile: cannot remove %#v: %v", fp.Name(), err)
}
}
}
// NormalizeIndent removes tab indentation from every line.
//
// This is useful for "inline" multiline strings:
//
// cases := []struct {
// string in
// }{
// `
// Hello,
// world!
// `,
// }
//
// This is nice and readable, but the downside is that every line will now have
// two extra tabs. This will remove those two tabs from every line.
//
// The amount of tabs to remove is based only on the first line, any further
// tabs will be preserved.
func NormalizeIndent(in string) string {
indent := 0
for _, c := range strings.TrimLeft(in, "\n") {
if c != '\t' {
break
}
indent++
}
r := ""
for _, line := range strings.Split(in, "\n") {
r += strings.Replace(line, "\t", "", indent) + "\n"
}
return strings.TrimSpace(r)
}
// R recovers a panic and cals t.Fatal().
//
// This is useful especially in subtests when you want to run a top-level defer.
// Subtests are run in their own goroutine, so those aren't called on regular
// panics. For example:
//
// func TestX(t *testing.T) {
// clean := someSetup()
// defer clean()
//
// t.Run("sub", func(t *testing.T) {
// panic("oh noes")
// })
// }
//
// The defer is never called here. To fix it, call this function in all
// subtests:
//
// t.Run("sub", func(t *testing.T) {
// defer test.R(t)
// panic("oh noes")
// })
//
// See: https://github.com/golang/go/issues/20394
func R(t *testing.T) {
t.Helper()
r := recover()
if r != nil {
t.Fatalf("panic recover: %v", r)
}
}
// SP makes a new String Pointer.
func SP(s string) *string { return &s }
// I64P makes a new Int64 Pointer.
func I64P(i int64) *int64 { return &i }