compare_test.go (10607B)
1 // Copyright 2017 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package compare
15
16 import (
17 "path"
18 "reflect"
19 "runtime"
20 "testing"
21 "time"
22
23 "github.com/gohugoio/hugo/htesting/hqt"
24
25 qt "github.com/frankban/quicktest"
26 "github.com/gohugoio/hugo/common/hugo"
27 "github.com/spf13/cast"
28 )
29
30 type T struct {
31 NonEmptyInterfaceNil I
32 NonEmptyInterfaceTypedNil I
33 }
34
35 type I interface {
36 Foo() string
37 }
38
39 func (t *T) Foo() string {
40 return "foo"
41 }
42
43 var testT = &T{
44 NonEmptyInterfaceTypedNil: (*T)(nil),
45 }
46
47 type (
48 tstEqerType1 string
49 tstEqerType2 string
50 )
51
52 func (t tstEqerType2) Eq(other any) bool {
53 return cast.ToString(t) == cast.ToString(other)
54 }
55
56 func (t tstEqerType2) String() string {
57 return string(t)
58 }
59
60 func (t tstEqerType1) Eq(other any) bool {
61 return cast.ToString(t) == cast.ToString(other)
62 }
63
64 func (t tstEqerType1) String() string {
65 return string(t)
66 }
67
68 type stringType string
69
70 type tstCompareType int
71
72 const (
73 tstEq tstCompareType = iota
74 tstNe
75 tstGt
76 tstGe
77 tstLt
78 tstLe
79 )
80
81 func tstIsEq(tp tstCompareType) bool { return tp == tstEq || tp == tstGe || tp == tstLe }
82 func tstIsGt(tp tstCompareType) bool { return tp == tstGt || tp == tstGe }
83 func tstIsLt(tp tstCompareType) bool { return tp == tstLt || tp == tstLe }
84
85 func TestDefaultFunc(t *testing.T) {
86 t.Parallel()
87 c := qt.New(t)
88
89 then := time.Now()
90 now := time.Now()
91 ns := New(time.UTC, false)
92
93 for i, test := range []struct {
94 dflt any
95 given any
96 expect any
97 }{
98 {true, false, false},
99 {"5", 0, "5"},
100
101 {"test1", "set", "set"},
102 {"test2", "", "test2"},
103 {"test3", nil, "test3"},
104
105 {[2]int{10, 20}, [2]int{1, 2}, [2]int{1, 2}},
106 {[2]int{10, 20}, [0]int{}, [2]int{10, 20}},
107 {[2]int{100, 200}, nil, [2]int{100, 200}},
108
109 {[]string{"one"}, []string{"uno"}, []string{"uno"}},
110 {[]string{"two"}, []string{}, []string{"two"}},
111 {[]string{"three"}, nil, []string{"three"}},
112
113 {map[string]int{"one": 1}, map[string]int{"uno": 1}, map[string]int{"uno": 1}},
114 {map[string]int{"one": 1}, map[string]int{}, map[string]int{"one": 1}},
115 {map[string]int{"two": 2}, nil, map[string]int{"two": 2}},
116
117 {10, 1, 1},
118 {10, 0, 10},
119 {20, nil, 20},
120
121 {float32(10), float32(1), float32(1)},
122 {float32(10), 0, float32(10)},
123 {float32(20), nil, float32(20)},
124
125 {complex(2, -2), complex(1, -1), complex(1, -1)},
126 {complex(2, -2), complex(0, 0), complex(2, -2)},
127 {complex(3, -3), nil, complex(3, -3)},
128
129 {struct{ f string }{f: "one"}, struct{}{}, struct{}{}},
130 {struct{ f string }{f: "two"}, nil, struct{ f string }{f: "two"}},
131
132 {then, now, now},
133 {then, time.Time{}, then},
134 } {
135
136 eq := qt.CmpEquals(hqt.DeepAllowUnexported(test.dflt))
137
138 errMsg := qt.Commentf("[%d] %v", i, test)
139
140 result, err := ns.Default(test.dflt, test.given)
141
142 c.Assert(err, qt.IsNil, errMsg)
143 c.Assert(result, eq, test.expect, errMsg)
144 }
145 }
146
147 func TestCompare(t *testing.T) {
148 t.Parallel()
149
150 n := New(time.UTC, false)
151
152 twoEq := func(a, b any) bool {
153 return n.Eq(a, b)
154 }
155
156 twoGt := func(a, b any) bool {
157 return n.Gt(a, b)
158 }
159
160 twoLt := func(a, b any) bool {
161 return n.Lt(a, b)
162 }
163
164 twoGe := func(a, b any) bool {
165 return n.Ge(a, b)
166 }
167
168 twoLe := func(a, b any) bool {
169 return n.Le(a, b)
170 }
171
172 twoNe := func(a, b any) bool {
173 return n.Ne(a, b)
174 }
175
176 for _, test := range []struct {
177 tstCompareType
178 funcUnderTest func(a, b any) bool
179 }{
180 {tstGt, twoGt},
181 {tstLt, twoLt},
182 {tstGe, twoGe},
183 {tstLe, twoLe},
184 {tstEq, twoEq},
185 {tstNe, twoNe},
186 } {
187 doTestCompare(t, test.tstCompareType, test.funcUnderTest)
188 }
189 }
190
191 func doTestCompare(t *testing.T, tp tstCompareType, funcUnderTest func(a, b any) bool) {
192 for i, test := range []struct {
193 left any
194 right any
195 expectIndicator int
196 }{
197 {5, 8, -1},
198 {8, 5, 1},
199 {5, 5, 0},
200 {int(5), int64(5), 0},
201 {int32(5), int(5), 0},
202 {int16(4), int(5), -1},
203 {uint(15), uint64(15), 0},
204 {-2, 1, -1},
205 {2, -5, 1},
206 {0.0, 1.23, -1},
207 {1.1, 1.1, 0},
208 {float32(1.0), float64(1.0), 0},
209 {1.23, 0.0, 1},
210 {"5", "5", 0},
211 {"8", "5", 1},
212 {"5", "0001", 1},
213 {[]int{100, 99}, []int{1, 2, 3, 4}, -1},
214 {cast.ToTime("2015-11-20"), cast.ToTime("2015-11-20"), 0},
215 {cast.ToTime("2015-11-19"), cast.ToTime("2015-11-20"), -1},
216 {cast.ToTime("2015-11-20"), cast.ToTime("2015-11-19"), 1},
217 {"a", "a", 0},
218 {"a", "b", -1},
219 {"b", "a", 1},
220 {tstEqerType1("a"), tstEqerType1("a"), 0},
221 {tstEqerType1("a"), tstEqerType2("a"), 0},
222 {tstEqerType2("a"), tstEqerType1("a"), 0},
223 {tstEqerType2("a"), tstEqerType1("b"), -1},
224 {hugo.MustParseVersion("0.32.1").Version(), hugo.MustParseVersion("0.32").Version(), 1},
225 {hugo.MustParseVersion("0.35").Version(), hugo.MustParseVersion("0.32").Version(), 1},
226 {hugo.MustParseVersion("0.36").Version(), hugo.MustParseVersion("0.36").Version(), 0},
227 {hugo.MustParseVersion("0.32").Version(), hugo.MustParseVersion("0.36").Version(), -1},
228 {hugo.MustParseVersion("0.32").Version(), "0.36", -1},
229 {"0.36", hugo.MustParseVersion("0.32").Version(), 1},
230 {"0.36", hugo.MustParseVersion("0.36").Version(), 0},
231 {"0.37", hugo.MustParseVersion("0.37-DEV").Version(), 1},
232 {"0.37-DEV", hugo.MustParseVersion("0.37").Version(), -1},
233 {"0.36", hugo.MustParseVersion("0.37-DEV").Version(), -1},
234 {"0.37-DEV", hugo.MustParseVersion("0.37-DEV").Version(), 0},
235 // https://github.com/gohugoio/hugo/issues/5905
236 {nil, nil, 0},
237 {testT.NonEmptyInterfaceNil, nil, 0},
238 {testT.NonEmptyInterfaceTypedNil, nil, 0},
239 } {
240
241 result := funcUnderTest(test.left, test.right)
242 success := false
243
244 if test.expectIndicator == 0 {
245 if tstIsEq(tp) {
246 success = result
247 } else {
248 success = !result
249 }
250 }
251
252 if test.expectIndicator < 0 {
253 success = result && (tstIsLt(tp) || tp == tstNe)
254 success = success || (!result && !tstIsLt(tp))
255 }
256
257 if test.expectIndicator > 0 {
258 success = result && (tstIsGt(tp) || tp == tstNe)
259 success = success || (!result && (!tstIsGt(tp) || tp != tstNe))
260 }
261
262 if !success {
263 t.Fatalf("[%d][%s] %v compared to %v: %t", i, path.Base(runtime.FuncForPC(reflect.ValueOf(funcUnderTest).Pointer()).Name()), test.left, test.right, result)
264 }
265 }
266 }
267
268 func TestEqualExtend(t *testing.T) {
269 t.Parallel()
270 c := qt.New(t)
271
272 ns := New(time.UTC, false)
273
274 for _, test := range []struct {
275 first any
276 others []any
277 expect bool
278 }{
279 {1, []any{1, 2}, true},
280 {1, []any{2, 1}, true},
281 {1, []any{2, 3}, false},
282 {tstEqerType1("a"), []any{tstEqerType1("a"), tstEqerType1("b")}, true},
283 {tstEqerType1("a"), []any{tstEqerType1("b"), tstEqerType1("a")}, true},
284 {tstEqerType1("a"), []any{tstEqerType1("b"), tstEqerType1("c")}, false},
285 } {
286
287 result := ns.Eq(test.first, test.others...)
288
289 c.Assert(result, qt.Equals, test.expect)
290 }
291 }
292
293 func TestNotEqualExtend(t *testing.T) {
294 t.Parallel()
295 c := qt.New(t)
296
297 ns := New(time.UTC, false)
298
299 for _, test := range []struct {
300 first any
301 others []any
302 expect bool
303 }{
304 {1, []any{2, 3}, true},
305 {1, []any{2, 1}, false},
306 {1, []any{1, 2}, false},
307 } {
308 result := ns.Ne(test.first, test.others...)
309 c.Assert(result, qt.Equals, test.expect)
310 }
311 }
312
313 func TestGreaterEqualExtend(t *testing.T) {
314 t.Parallel()
315 c := qt.New(t)
316
317 ns := New(time.UTC, false)
318
319 for _, test := range []struct {
320 first any
321 others []any
322 expect bool
323 }{
324 {5, []any{2, 3}, true},
325 {5, []any{5, 5}, true},
326 {3, []any{4, 2}, false},
327 {3, []any{2, 4}, false},
328 } {
329 result := ns.Ge(test.first, test.others...)
330 c.Assert(result, qt.Equals, test.expect)
331 }
332 }
333
334 func TestGreaterThanExtend(t *testing.T) {
335 t.Parallel()
336 c := qt.New(t)
337
338 ns := New(time.UTC, false)
339
340 for _, test := range []struct {
341 first any
342 others []any
343 expect bool
344 }{
345 {5, []any{2, 3}, true},
346 {5, []any{5, 4}, false},
347 {3, []any{4, 2}, false},
348 } {
349 result := ns.Gt(test.first, test.others...)
350 c.Assert(result, qt.Equals, test.expect)
351 }
352 }
353
354 func TestLessEqualExtend(t *testing.T) {
355 t.Parallel()
356 c := qt.New(t)
357
358 ns := New(time.UTC, false)
359
360 for _, test := range []struct {
361 first any
362 others []any
363 expect bool
364 }{
365 {1, []any{2, 3}, true},
366 {1, []any{1, 2}, true},
367 {2, []any{1, 2}, false},
368 {3, []any{2, 4}, false},
369 } {
370 result := ns.Le(test.first, test.others...)
371 c.Assert(result, qt.Equals, test.expect)
372 }
373 }
374
375 func TestLessThanExtend(t *testing.T) {
376 t.Parallel()
377 c := qt.New(t)
378
379 ns := New(time.UTC, false)
380
381 for _, test := range []struct {
382 first any
383 others []any
384 expect bool
385 }{
386 {1, []any{2, 3}, true},
387 {1, []any{1, 2}, false},
388 {2, []any{1, 2}, false},
389 {3, []any{2, 4}, false},
390 } {
391 result := ns.Lt(test.first, test.others...)
392 c.Assert(result, qt.Equals, test.expect)
393 }
394 }
395
396 func TestCase(t *testing.T) {
397 c := qt.New(t)
398 n := New(time.UTC, false)
399
400 c.Assert(n.Eq("az", "az"), qt.Equals, true)
401 c.Assert(n.Eq("az", stringType("az")), qt.Equals, true)
402 }
403
404 func TestStringType(t *testing.T) {
405 c := qt.New(t)
406 n := New(time.UTC, true)
407
408 c.Assert(n.Lt("az", "Za"), qt.Equals, true)
409 c.Assert(n.Gt("ab", "Ab"), qt.Equals, true)
410 }
411
412 func TestTimeUnix(t *testing.T) {
413 t.Parallel()
414 n := New(time.UTC, false)
415 var sec int64 = 1234567890
416 tv := reflect.ValueOf(time.Unix(sec, 0))
417 i := 1
418
419 res := n.toTimeUnix(tv)
420 if sec != res {
421 t.Errorf("[%d] timeUnix got %v but expected %v", i, res, sec)
422 }
423
424 i++
425 func(t *testing.T) {
426 defer func() {
427 if err := recover(); err == nil {
428 t.Errorf("[%d] timeUnix didn't return an expected error", i)
429 }
430 }()
431 iv := reflect.ValueOf(sec)
432 n.toTimeUnix(iv)
433 }(t)
434 }
435
436 func TestConditional(t *testing.T) {
437 c := qt.New(t)
438 n := New(time.UTC, false)
439 a, b := "a", "b"
440
441 c.Assert(n.Conditional(true, a, b), qt.Equals, a)
442 c.Assert(n.Conditional(false, a, b), qt.Equals, b)
443 }
444
445 // Issue 9462
446 func TestComparisonArgCount(t *testing.T) {
447 t.Parallel()
448 c := qt.New(t)
449
450 ns := New(time.UTC, false)
451
452 panicMsg := "missing arguments for comparison"
453
454 c.Assert(func() { ns.Eq(1) }, qt.PanicMatches, panicMsg)
455 c.Assert(func() { ns.Ge(1) }, qt.PanicMatches, panicMsg)
456 c.Assert(func() { ns.Gt(1) }, qt.PanicMatches, panicMsg)
457 c.Assert(func() { ns.Le(1) }, qt.PanicMatches, panicMsg)
458 c.Assert(func() { ns.Lt(1) }, qt.PanicMatches, panicMsg)
459 c.Assert(func() { ns.Ne(1) }, qt.PanicMatches, panicMsg)
460 }