github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/tpl/compare/compare_test.go (about)

     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  		{"infinity", "infinity", 0},
   221  		{"nan", "nan", 0},
   222  		{tstEqerType1("a"), tstEqerType1("a"), 0},
   223  		{tstEqerType1("a"), tstEqerType2("a"), 0},
   224  		{tstEqerType2("a"), tstEqerType1("a"), 0},
   225  		{tstEqerType2("a"), tstEqerType1("b"), -1},
   226  		{hugo.MustParseVersion("0.32.1").Version(), hugo.MustParseVersion("0.32").Version(), 1},
   227  		{hugo.MustParseVersion("0.35").Version(), hugo.MustParseVersion("0.32").Version(), 1},
   228  		{hugo.MustParseVersion("0.36").Version(), hugo.MustParseVersion("0.36").Version(), 0},
   229  		{hugo.MustParseVersion("0.32").Version(), hugo.MustParseVersion("0.36").Version(), -1},
   230  		{hugo.MustParseVersion("0.32").Version(), "0.36", -1},
   231  		{"0.36", hugo.MustParseVersion("0.32").Version(), 1},
   232  		{"0.36", hugo.MustParseVersion("0.36").Version(), 0},
   233  		{"0.37", hugo.MustParseVersion("0.37-DEV").Version(), 1},
   234  		{"0.37-DEV", hugo.MustParseVersion("0.37").Version(), -1},
   235  		{"0.36", hugo.MustParseVersion("0.37-DEV").Version(), -1},
   236  		{"0.37-DEV", hugo.MustParseVersion("0.37-DEV").Version(), 0},
   237  		// https://github.com/gohugoio/hugo/issues/5905
   238  		{nil, nil, 0},
   239  		{testT.NonEmptyInterfaceNil, nil, 0},
   240  		{testT.NonEmptyInterfaceTypedNil, nil, 0},
   241  	} {
   242  
   243  		result := funcUnderTest(test.left, test.right)
   244  		success := false
   245  
   246  		if test.expectIndicator == 0 {
   247  			if tstIsEq(tp) {
   248  				success = result
   249  			} else {
   250  				success = !result
   251  			}
   252  		}
   253  
   254  		if test.expectIndicator < 0 {
   255  			success = result && (tstIsLt(tp) || tp == tstNe)
   256  			success = success || (!result && !tstIsLt(tp))
   257  		}
   258  
   259  		if test.expectIndicator > 0 {
   260  			success = result && (tstIsGt(tp) || tp == tstNe)
   261  			success = success || (!result && (!tstIsGt(tp) || tp != tstNe))
   262  		}
   263  
   264  		if !success {
   265  			t.Fatalf("[%d][%s] %v compared to %v: %t", i, path.Base(runtime.FuncForPC(reflect.ValueOf(funcUnderTest).Pointer()).Name()), test.left, test.right, result)
   266  		}
   267  	}
   268  }
   269  
   270  func TestEqualExtend(t *testing.T) {
   271  	t.Parallel()
   272  	c := qt.New(t)
   273  
   274  	ns := New(time.UTC, false)
   275  
   276  	for _, test := range []struct {
   277  		first  any
   278  		others []any
   279  		expect bool
   280  	}{
   281  		{1, []any{1, 2}, true},
   282  		{1, []any{2, 1}, true},
   283  		{1, []any{2, 3}, false},
   284  		{tstEqerType1("a"), []any{tstEqerType1("a"), tstEqerType1("b")}, true},
   285  		{tstEqerType1("a"), []any{tstEqerType1("b"), tstEqerType1("a")}, true},
   286  		{tstEqerType1("a"), []any{tstEqerType1("b"), tstEqerType1("c")}, false},
   287  	} {
   288  
   289  		result := ns.Eq(test.first, test.others...)
   290  
   291  		c.Assert(result, qt.Equals, test.expect)
   292  	}
   293  }
   294  
   295  func TestNotEqualExtend(t *testing.T) {
   296  	t.Parallel()
   297  	c := qt.New(t)
   298  
   299  	ns := New(time.UTC, false)
   300  
   301  	for _, test := range []struct {
   302  		first  any
   303  		others []any
   304  		expect bool
   305  	}{
   306  		{1, []any{2, 3}, true},
   307  		{1, []any{2, 1}, false},
   308  		{1, []any{1, 2}, false},
   309  	} {
   310  		result := ns.Ne(test.first, test.others...)
   311  		c.Assert(result, qt.Equals, test.expect)
   312  	}
   313  }
   314  
   315  func TestGreaterEqualExtend(t *testing.T) {
   316  	t.Parallel()
   317  	c := qt.New(t)
   318  
   319  	ns := New(time.UTC, false)
   320  
   321  	for _, test := range []struct {
   322  		first  any
   323  		others []any
   324  		expect bool
   325  	}{
   326  		{5, []any{2, 3}, true},
   327  		{5, []any{5, 5}, true},
   328  		{3, []any{4, 2}, false},
   329  		{3, []any{2, 4}, false},
   330  	} {
   331  		result := ns.Ge(test.first, test.others...)
   332  		c.Assert(result, qt.Equals, test.expect)
   333  	}
   334  }
   335  
   336  func TestGreaterThanExtend(t *testing.T) {
   337  	t.Parallel()
   338  	c := qt.New(t)
   339  
   340  	ns := New(time.UTC, false)
   341  
   342  	for _, test := range []struct {
   343  		first  any
   344  		others []any
   345  		expect bool
   346  	}{
   347  		{5, []any{2, 3}, true},
   348  		{5, []any{5, 4}, false},
   349  		{3, []any{4, 2}, false},
   350  	} {
   351  		result := ns.Gt(test.first, test.others...)
   352  		c.Assert(result, qt.Equals, test.expect)
   353  	}
   354  }
   355  
   356  func TestLessEqualExtend(t *testing.T) {
   357  	t.Parallel()
   358  	c := qt.New(t)
   359  
   360  	ns := New(time.UTC, false)
   361  
   362  	for _, test := range []struct {
   363  		first  any
   364  		others []any
   365  		expect bool
   366  	}{
   367  		{1, []any{2, 3}, true},
   368  		{1, []any{1, 2}, true},
   369  		{2, []any{1, 2}, false},
   370  		{3, []any{2, 4}, false},
   371  	} {
   372  		result := ns.Le(test.first, test.others...)
   373  		c.Assert(result, qt.Equals, test.expect)
   374  	}
   375  }
   376  
   377  func TestLessThanExtend(t *testing.T) {
   378  	t.Parallel()
   379  	c := qt.New(t)
   380  
   381  	ns := New(time.UTC, false)
   382  
   383  	for _, test := range []struct {
   384  		first  any
   385  		others []any
   386  		expect bool
   387  	}{
   388  		{1, []any{2, 3}, true},
   389  		{1, []any{1, 2}, false},
   390  		{2, []any{1, 2}, false},
   391  		{3, []any{2, 4}, false},
   392  	} {
   393  		result := ns.Lt(test.first, test.others...)
   394  		c.Assert(result, qt.Equals, test.expect)
   395  	}
   396  }
   397  
   398  func TestCase(t *testing.T) {
   399  	c := qt.New(t)
   400  	n := New(time.UTC, false)
   401  
   402  	c.Assert(n.Eq("az", "az"), qt.Equals, true)
   403  	c.Assert(n.Eq("az", stringType("az")), qt.Equals, true)
   404  }
   405  
   406  func TestStringType(t *testing.T) {
   407  	c := qt.New(t)
   408  	n := New(time.UTC, true)
   409  
   410  	c.Assert(n.Lt("az", "Za"), qt.Equals, true)
   411  	c.Assert(n.Gt("ab", "Ab"), qt.Equals, true)
   412  }
   413  
   414  func TestTimeUnix(t *testing.T) {
   415  	t.Parallel()
   416  	n := New(time.UTC, false)
   417  	var sec int64 = 1234567890
   418  	tv := reflect.ValueOf(time.Unix(sec, 0))
   419  	i := 1
   420  
   421  	res := n.toTimeUnix(tv)
   422  	if sec != res {
   423  		t.Errorf("[%d] timeUnix got %v but expected %v", i, res, sec)
   424  	}
   425  
   426  	i++
   427  	func(t *testing.T) {
   428  		defer func() {
   429  			if err := recover(); err == nil {
   430  				t.Errorf("[%d] timeUnix didn't return an expected error", i)
   431  			}
   432  		}()
   433  		iv := reflect.ValueOf(sec)
   434  		n.toTimeUnix(iv)
   435  	}(t)
   436  }
   437  
   438  func TestConditional(t *testing.T) {
   439  	c := qt.New(t)
   440  	n := New(time.UTC, false)
   441  	a, b := "a", "b"
   442  
   443  	c.Assert(n.Conditional(true, a, b), qt.Equals, a)
   444  	c.Assert(n.Conditional(false, a, b), qt.Equals, b)
   445  }
   446  
   447  // Issue 9462
   448  func TestComparisonArgCount(t *testing.T) {
   449  	t.Parallel()
   450  	c := qt.New(t)
   451  
   452  	ns := New(time.UTC, false)
   453  
   454  	panicMsg := "missing arguments for comparison"
   455  
   456  	c.Assert(func() { ns.Eq(1) }, qt.PanicMatches, panicMsg)
   457  	c.Assert(func() { ns.Ge(1) }, qt.PanicMatches, panicMsg)
   458  	c.Assert(func() { ns.Gt(1) }, qt.PanicMatches, panicMsg)
   459  	c.Assert(func() { ns.Le(1) }, qt.PanicMatches, panicMsg)
   460  	c.Assert(func() { ns.Lt(1) }, qt.PanicMatches, panicMsg)
   461  	c.Assert(func() { ns.Ne(1) }, qt.PanicMatches, panicMsg)
   462  }