all repos

x @ main

go extra()

x/is/is_test.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
is: add, 6 months ago
1
package is_test
2
3
import (
4
	"errors"
5
	"fmt"
6
	"io/fs"
7
	"math/rand/v2"
8
	"reflect"
9
	"testing"
10
	"time"
11
12
	"olexsmir.xyz/x/is"
13
)
14
15
func TestEqual(t *testing.T) {
16
	t.Run("equal", func(t *testing.T) {
17
		now := time.Now()
18
		val := 420
19
20
		tests := map[string]struct{ got, expected any }{
21
			"int":          {69, 69},
22
			"string":       {"hello", "hello"},
23
			"bool":         {true, true},
24
			"struct":       {intType{1}, intType{1}},
25
			"pointer":      {&val, &val},
26
			"nil":          {nil, nil},
27
			"nil pointer":  {(*int)(nil), (*int)(nil)},
28
			"nil map":      {map[string]int(nil), map[string]int(nil)},
29
			"nil chan":     {(chan int)(nil), (chan int)(nil)},
30
			"nil slice":    {[]int(nil), []int(nil)},
31
			"byte slice":   {[]byte("abc"), []byte("abc")},
32
			"int slice":    {[]int{1, 2}, []int{1, 2}},
33
			"string slice": {[]string{"a", "b"}, []string{"a", "b"}},
34
			"empty map":    {map[string]int{}, map[string]int{}},
35
			"map":          {map[string]int{"a": 1}, map[string]int{"a": 1}},
36
			"time.Time":    {now, now},
37
		}
38
		for tname, tt := range tests {
39
			t.Run(tname, func(t *testing.T) {
40
				ft := &fakeT{}
41
				is.Equal(ft, tt.expected, tt.got)
42
				if ft.failed {
43
					t.Errorf("failed: %s", ft.msg)
44
				}
45
			})
46
		}
47
	})
48
49
	t.Run("non-equal", func(t *testing.T) {
50
		now := time.Now()
51
		val1, val2 := 69, 420
52
		tests := map[string]struct {
53
			expected, got any
54
			expectedMsg   string
55
		}{
56
			"int":             {228, 1337, `expected: 228, got: 1337`},
57
			"string":          {"hello", "world", `expected: "hello", got: "world"`},
58
			"bool":            {true, false, "expected: true, got: false"},
59
			"struct":          {intType{1}, intType{2}, `expected: is_test.intType{v:1}, got: is_test.intType{v:2}`},
60
			"pointer":         {&val1, &val2, ``},
61
			"nil/non-nil":     {nil, 2, `expected: <nil>, got: 2`},
62
			"non-nil/nil":     {2, nil, `expected: 2, got: <nil>`},
63
			"nil slice/empty": {[]int(nil), []int{}, `expected: []int(nil), got: []int{}`},
64
			"int slice":       {[]int{1, 2}, []int{2, 1}, `expected: []int{1, 2}, got: []int{2, 1}`},
65
			"byte slice":      {[]byte("abc"), []byte("def"), `expected: []byte{0x61, 0x62, 0x63}, got: []byte{0x64, 0x65, 0x66}`},
66
			"chan":            {make(chan int), make(chan int), ``},
67
			"string slice":    {[]string{"a", "b"}, []string{"b", "a"}, `expected: []string{"a", "b"}, got: []string{"b", "a"}`},
68
			"map":             {map[string]int{"a": 1}, map[string]int{"a": 2}, `expected: map[string]int{"a":1}, got: map[string]int{"a":2}`},
69
			"time.Time":       {now, now.Add(time.Second), ``},
70
		}
71
72
		for tname, tt := range tests {
73
			t.Run(tname, func(t *testing.T) {
74
				ft := &fakeT{}
75
				is.Equal(ft, tt.expected, tt.got)
76
				shouldHaveFailed(t, ft)
77
				if tt.expectedMsg != "" {
78
					expectMsg(t, ft, tt.expectedMsg)
79
				}
80
			})
81
		}
82
	})
83
84
	t.Run("implements equaler", func(t *testing.T) {
85
		ft := &fakeT{}
86
		val1, val2 := newRandomy(1), newRandomy(1)
87
		is.Equal(ft, val1, val2)
88
		if ft.failed {
89
			t.Errorf("%#v == %#v: should have passed", val1, val2)
90
		}
91
	})
92
93
	t.Run("implements equaler, not empty", func(t *testing.T) {
94
		ft := &fakeT{}
95
		val1, val2 := newRandomy(1), newRandomy(2)
96
		is.Equal(ft, val1, val2)
97
		shouldHaveFailed(t, ft)
98
	})
99
100
	t.Run("time.Time", func(t *testing.T) {
101
		ft := &fakeT{}
102
		val1 := time.Date(2022, 1, 3, 0, 0, 18, 0, time.UTC)
103
		val2 := time.Date(2022, 1, 3, 5, 0, 18, 0, time.FixedZone("UTC+5", 5*3600))
104
		is.Equal(ft, val1, val1)
105
		if ft.failed {
106
			t.Errorf("%#v == %#v: should have passed", val1, val2)
107
		}
108
	})
109
}
110
111
func TestErr(t *testing.T) {
112
	t.Run("expected nil, got nil", func(t *testing.T) {
113
		ft := &fakeT{}
114
		is.Err(ft, nil, nil)
115
		if ft.failed {
116
			t.Errorf("failed: %s", ft.msg)
117
		}
118
	})
119
120
	t.Run("expected nil, got error", func(t *testing.T) {
121
		ft := &fakeT{}
122
		is.Err(ft, errors.New("an error"), nil)
123
124
		shouldBeFatal(t, ft)
125
		shouldHaveFailed(t, ft)
126
		expectMsg(t, ft, "unexpected error: an error")
127
	})
128
129
	t.Run("expected error, got nil", func(t *testing.T) {
130
		ft := &fakeT{}
131
		is.Err(ft, nil, errors.New("err"))
132
133
		shouldHaveFailed(t, ft)
134
		expectMsg(t, ft, `got: <nil>, expected: error`)
135
	})
136
137
	t.Run("expected error, got same error", func(t *testing.T) {
138
		ft := &fakeT{}
139
		err := errors.New("an error")
140
		is.Err(ft, err, err)
141
		if ft.failed {
142
			t.Errorf("failed: %s", ft.msg)
143
		}
144
	})
145
146
	t.Run("expected error, got different error", func(t *testing.T) {
147
		ft := &fakeT{}
148
		err1 := errors.New("an error")
149
		err2 := errors.New("second error")
150
		is.Err(ft, err1, err2)
151
152
		shouldHaveFailed(t, ft)
153
		shouldBeFatal(t, ft)
154
		expectMsg(t, ft, `expected: *errors.errorString(an error), got: *errors.errorString(second error)`)
155
	})
156
157
	t.Run("expected error, got error of different type", func(t *testing.T) {
158
		ft := &fakeT{}
159
		err1 := errors.New("an error")
160
		err2 := errType("my error")
161
		is.Err(ft, err1, err2)
162
163
		shouldHaveFailed(t, ft)
164
		shouldBeFatal(t, ft)
165
		expectMsg(t, ft, `expected: *errors.errorString(an error), got: is_test.errType(my error)`)
166
	})
167
168
	t.Run("expected error, got wrapped error", func(t *testing.T) {
169
		ft := &fakeT{}
170
		err := errors.New("an error")
171
		werr := fmt.Errorf("wrapped: %w", err)
172
		is.Err(ft, werr, err)
173
		if ft.failed {
174
			t.Errorf("failed: %s", ft.msg)
175
		}
176
	})
177
178
	t.Run("got error, string", func(t *testing.T) {
179
		ft := &fakeT{}
180
		is.Err(ft, errors.New("test string"), "test string")
181
		if ft.failed {
182
			t.Errorf("failed: %s", ft.msg)
183
		}
184
	})
185
186
	t.Run("got error, partial string", func(t *testing.T) {
187
		ft := &fakeT{}
188
		is.Err(ft, errors.New("test string"), "string")
189
		if ft.failed {
190
			t.Errorf("failed: %s", ft.msg)
191
		}
192
	})
193
194
	t.Run("got error, not containing string", func(t *testing.T) {
195
		ft := &fakeT{}
196
		is.Err(ft, errors.New("test string"), "asdfasdf")
197
198
		shouldHaveFailed(t, ft)
199
		shouldBeFatal(t, ft)
200
		expectMsg(t, ft, `expected: "test string", got: "asdfasdf"`)
201
	})
202
203
	t.Run("got custom type, same types", func(t *testing.T) {
204
		ft := &fakeT{}
205
		err := errType("custom error")
206
		is.Err(ft, err, err)
207
		if ft.failed {
208
			t.Errorf("failed: %s", ft.msg)
209
		}
210
	})
211
212
	t.Run("got custom type, different types", func(t *testing.T) {
213
		ft := &fakeT{}
214
		err := errType("custom error")
215
		is.Err(ft, err, reflect.TypeFor[*fs.PathError]())
216
		shouldHaveFailed(t, ft)
217
		shouldBeFatal(t, ft)
218
		expectMsg(t, ft, `expected: *fs.PathError, got: is_test.errType`)
219
	})
220
221
	t.Run("unexpected type", func(t *testing.T) {
222
		ft := &fakeT{}
223
		is.Err(ft, errors.New("asdf"), 0)
224
		shouldHaveFailed(t, ft)
225
		shouldBeFatal(t, ft)
226
		expectMsg(t, ft, `unexpected type: int`)
227
	})
228
}
229
230
func expectMsg(t *testing.T, ft *fakeT, expectedMsg string) {
231
	t.Helper()
232
	if ft.msg != expectedMsg {
233
		t.Errorf("expected msg: %q; got: %q", expectedMsg, ft.msg)
234
	}
235
}
236
237
func shouldBeFatal(t *testing.T, ft *fakeT) {
238
	t.Helper()
239
	if !ft.fatal {
240
		t.Error("should be fatal")
241
	}
242
}
243
244
func shouldHaveFailed(t *testing.T, ft *fakeT) {
245
	t.Helper()
246
	if !ft.failed {
247
		t.Error("should have failed")
248
	}
249
}
250
251
// helper types ---
252
253
type intType struct{ v int }
254
255
type randomy struct {
256
	val int
257
	rnd float64
258
}
259
260
func newRandomy(val int) randomy           { return randomy{val, rand.Float64()} }
261
func (n randomy) Equal(other randomy) bool { return n.val == other.val }
262
263
type errType string
264
265
func (e errType) Error() string { return string(e) }
266
267
// mockT ----
268
269
type fakeT struct {
270
	testing.TB
271
	failed bool
272
	fatal  bool
273
	msg    string
274
}
275
276
func (m *fakeT) Helper() {}
277
278
func (m *fakeT) Log(a ...any) { fmt.Println(a...) }
279
280
func (m *fakeT) Fatal(args ...any) {
281
	m.fatal = true
282
	m.Error(args...)
283
}
284
285
func (m *fakeT) Fatalf(format string, args ...any) {
286
	m.fatal = true
287
	m.Errorf(format, args...)
288
}
289
290
func (m *fakeT) Error(args ...any) {
291
	m.failed = true
292
	m.msg = fmt.Sprint(args...)
293
}
294
295
func (m *fakeT) Errorf(format string, args ...any) {
296
	m.failed = true
297
	m.msg = fmt.Sprintf(format, args...)
298
}