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

     1  // Copyright 2019 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 collections
    15  
    16  import (
    17  	"errors"
    18  	"fmt"
    19  	"html/template"
    20  	"math/rand"
    21  	"reflect"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/gohugoio/hugo/common/maps"
    26  
    27  	qt "github.com/frankban/quicktest"
    28  	"github.com/gohugoio/hugo/common/loggers"
    29  	"github.com/gohugoio/hugo/config"
    30  	"github.com/gohugoio/hugo/deps"
    31  	"github.com/gohugoio/hugo/helpers"
    32  	"github.com/gohugoio/hugo/hugofs"
    33  	"github.com/gohugoio/hugo/langs"
    34  	"github.com/spf13/afero"
    35  )
    36  
    37  type tstNoStringer struct{}
    38  
    39  func TestAfter(t *testing.T) {
    40  	t.Parallel()
    41  	c := qt.New(t)
    42  
    43  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
    44  
    45  	for i, test := range []struct {
    46  		index  any
    47  		seq    any
    48  		expect any
    49  	}{
    50  		{int(2), []string{"a", "b", "c", "d"}, []string{"c", "d"}},
    51  		{int32(3), []string{"a", "b"}, []string{}},
    52  		{int64(2), []int{100, 200, 300}, []int{300}},
    53  		{100, []int{100, 200}, []int{}},
    54  		{"1", []int{100, 200, 300}, []int{200, 300}},
    55  		{0, []int{100, 200, 300, 400, 500}, []int{100, 200, 300, 400, 500}},
    56  		{0, []string{"a", "b", "c", "d", "e"}, []string{"a", "b", "c", "d", "e"}},
    57  		{int64(-1), []int{100, 200, 300}, false},
    58  		{"noint", []int{100, 200, 300}, false},
    59  		{2, []string{}, []string{}},
    60  		{1, nil, false},
    61  		{nil, []int{100}, false},
    62  		{1, t, false},
    63  		{1, (*string)(nil), false},
    64  	} {
    65  		errMsg := qt.Commentf("[%d] %v", i, test)
    66  
    67  		result, err := ns.After(test.index, test.seq)
    68  
    69  		if b, ok := test.expect.(bool); ok && !b {
    70  			c.Assert(err, qt.Not(qt.IsNil), errMsg)
    71  			continue
    72  		}
    73  
    74  		c.Assert(err, qt.IsNil, errMsg)
    75  		c.Assert(result, qt.DeepEquals, test.expect, errMsg)
    76  	}
    77  }
    78  
    79  type tstGrouper struct {
    80  }
    81  
    82  type tstGroupers []*tstGrouper
    83  
    84  func (g tstGrouper) Group(key any, items any) (any, error) {
    85  	ilen := reflect.ValueOf(items).Len()
    86  	return fmt.Sprintf("%v(%d)", key, ilen), nil
    87  }
    88  
    89  type tstGrouper2 struct {
    90  }
    91  
    92  func (g *tstGrouper2) Group(key any, items any) (any, error) {
    93  	ilen := reflect.ValueOf(items).Len()
    94  	return fmt.Sprintf("%v(%d)", key, ilen), nil
    95  }
    96  
    97  func TestGroup(t *testing.T) {
    98  	t.Parallel()
    99  	c := qt.New(t)
   100  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   101  
   102  	for i, test := range []struct {
   103  		key    any
   104  		items  any
   105  		expect any
   106  	}{
   107  		{"a", []*tstGrouper{{}, {}}, "a(2)"},
   108  		{"b", tstGroupers{&tstGrouper{}, &tstGrouper{}}, "b(2)"},
   109  		{"a", []tstGrouper{{}, {}}, "a(2)"},
   110  		{"a", []*tstGrouper2{{}, {}}, "a(2)"},
   111  		{"b", []tstGrouper2{{}, {}}, "b(2)"},
   112  		{"a", []*tstGrouper{}, "a(0)"},
   113  		{"a", []string{"a", "b"}, false},
   114  		{"a", "asdf", false},
   115  		{"a", nil, false},
   116  		{nil, []*tstGrouper{{}, {}}, false},
   117  	} {
   118  		errMsg := qt.Commentf("[%d] %v", i, test)
   119  
   120  		result, err := ns.Group(test.key, test.items)
   121  
   122  		if b, ok := test.expect.(bool); ok && !b {
   123  			c.Assert(err, qt.Not(qt.IsNil), errMsg)
   124  			continue
   125  		}
   126  
   127  		c.Assert(err, qt.IsNil, errMsg)
   128  		c.Assert(result, qt.Equals, test.expect, errMsg)
   129  	}
   130  }
   131  
   132  func TestDelimit(t *testing.T) {
   133  	t.Parallel()
   134  	c := qt.New(t)
   135  
   136  	ns := New(&deps.Deps{
   137  		Language: langs.NewDefaultLanguage(config.New()),
   138  	})
   139  
   140  	for i, test := range []struct {
   141  		seq       any
   142  		delimiter any
   143  		last      any
   144  		expect    template.HTML
   145  	}{
   146  		{[]string{"class1", "class2", "class3"}, " ", nil, "class1 class2 class3"},
   147  		{[]int{1, 2, 3, 4, 5}, ",", nil, "1,2,3,4,5"},
   148  		{[]int{1, 2, 3, 4, 5}, ", ", nil, "1, 2, 3, 4, 5"},
   149  		{[]string{"class1", "class2", "class3"}, " ", " and ", "class1 class2 and class3"},
   150  		{[]int{1, 2, 3, 4, 5}, ",", ",", "1,2,3,4,5"},
   151  		{[]int{1, 2, 3, 4, 5}, ", ", ", and ", "1, 2, 3, 4, and 5"},
   152  		// test maps with and without sorting required
   153  		{map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, "--", nil, "10--20--30--40--50"},
   154  		{map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, "--", nil, "30--20--10--40--50"},
   155  		{map[string]string{"1": "10", "2": "20", "3": "30", "4": "40", "5": "50"}, "--", nil, "10--20--30--40--50"},
   156  		{map[string]string{"3": "10", "2": "20", "1": "30", "4": "40", "5": "50"}, "--", nil, "30--20--10--40--50"},
   157  		{map[string]string{"one": "10", "two": "20", "three": "30", "four": "40", "five": "50"}, "--", nil, "50--40--10--30--20"},
   158  		{map[int]string{1: "10", 2: "20", 3: "30", 4: "40", 5: "50"}, "--", nil, "10--20--30--40--50"},
   159  		{map[int]string{3: "10", 2: "20", 1: "30", 4: "40", 5: "50"}, "--", nil, "30--20--10--40--50"},
   160  		{map[float64]string{3.3: "10", 2.3: "20", 1.3: "30", 4.3: "40", 5.3: "50"}, "--", nil, "30--20--10--40--50"},
   161  		// test maps with a last delimiter
   162  		{map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, "--", "--and--", "10--20--30--40--and--50"},
   163  		{map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, "--", "--and--", "30--20--10--40--and--50"},
   164  		{map[string]string{"1": "10", "2": "20", "3": "30", "4": "40", "5": "50"}, "--", "--and--", "10--20--30--40--and--50"},
   165  		{map[string]string{"3": "10", "2": "20", "1": "30", "4": "40", "5": "50"}, "--", "--and--", "30--20--10--40--and--50"},
   166  		{map[string]string{"one": "10", "two": "20", "three": "30", "four": "40", "five": "50"}, "--", "--and--", "50--40--10--30--and--20"},
   167  		{map[int]string{1: "10", 2: "20", 3: "30", 4: "40", 5: "50"}, "--", "--and--", "10--20--30--40--and--50"},
   168  		{map[int]string{3: "10", 2: "20", 1: "30", 4: "40", 5: "50"}, "--", "--and--", "30--20--10--40--and--50"},
   169  		{map[float64]string{3.5: "10", 2.5: "20", 1.5: "30", 4.5: "40", 5.5: "50"}, "--", "--and--", "30--20--10--40--and--50"},
   170  	} {
   171  		errMsg := qt.Commentf("[%d] %v", i, test)
   172  
   173  		var result template.HTML
   174  		var err error
   175  
   176  		if test.last == nil {
   177  			result, err = ns.Delimit(test.seq, test.delimiter)
   178  		} else {
   179  			result, err = ns.Delimit(test.seq, test.delimiter, test.last)
   180  		}
   181  
   182  		c.Assert(err, qt.IsNil, errMsg)
   183  		c.Assert(result, qt.Equals, test.expect, errMsg)
   184  	}
   185  }
   186  
   187  func TestDictionary(t *testing.T) {
   188  	c := qt.New(t)
   189  
   190  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   191  
   192  	for i, test := range []struct {
   193  		values []any
   194  		expect any
   195  	}{
   196  		{[]any{"a", "b"}, map[string]any{"a": "b"}},
   197  		{[]any{[]string{"a", "b"}, "c"}, map[string]any{"a": map[string]any{"b": "c"}}},
   198  		{
   199  			[]any{[]string{"a", "b"}, "c", []string{"a", "b2"}, "c2", "b", "c"},
   200  			map[string]any{"a": map[string]any{"b": "c", "b2": "c2"}, "b": "c"},
   201  		},
   202  		{[]any{"a", 12, "b", []int{4}}, map[string]any{"a": 12, "b": []int{4}}},
   203  		// errors
   204  		{[]any{5, "b"}, false},
   205  		{[]any{"a", "b", "c"}, false},
   206  	} {
   207  		i := i
   208  		test := test
   209  		c.Run(fmt.Sprint(i), func(c *qt.C) {
   210  			c.Parallel()
   211  			errMsg := qt.Commentf("[%d] %v", i, test.values)
   212  
   213  			result, err := ns.Dictionary(test.values...)
   214  
   215  			if b, ok := test.expect.(bool); ok && !b {
   216  				c.Assert(err, qt.Not(qt.IsNil), errMsg)
   217  				return
   218  			}
   219  
   220  			c.Assert(err, qt.IsNil, errMsg)
   221  			c.Assert(result, qt.DeepEquals, test.expect, qt.Commentf(fmt.Sprint(result)))
   222  		})
   223  	}
   224  }
   225  
   226  func TestReverse(t *testing.T) {
   227  	t.Parallel()
   228  	c := qt.New(t)
   229  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   230  
   231  	s := []string{"a", "b", "c"}
   232  	reversed, err := ns.Reverse(s)
   233  	c.Assert(err, qt.IsNil)
   234  	c.Assert(reversed, qt.DeepEquals, []string{"c", "b", "a"}, qt.Commentf(fmt.Sprint(reversed)))
   235  	c.Assert(s, qt.DeepEquals, []string{"a", "b", "c"})
   236  
   237  	reversed, err = ns.Reverse(nil)
   238  	c.Assert(err, qt.IsNil)
   239  	c.Assert(reversed, qt.IsNil)
   240  	_, err = ns.Reverse(43)
   241  	c.Assert(err, qt.Not(qt.IsNil))
   242  }
   243  
   244  func TestEchoParam(t *testing.T) {
   245  	t.Parallel()
   246  	c := qt.New(t)
   247  
   248  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   249  
   250  	for i, test := range []struct {
   251  		a      any
   252  		key    any
   253  		expect any
   254  	}{
   255  		{[]int{1, 2, 3}, 1, int64(2)},
   256  		{[]uint{1, 2, 3}, 1, uint64(2)},
   257  		{[]float64{1.1, 2.2, 3.3}, 1, float64(2.2)},
   258  		{[]string{"foo", "bar", "baz"}, 1, "bar"},
   259  		{[]TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}}, 1, ""},
   260  		{map[string]int{"foo": 1, "bar": 2, "baz": 3}, "bar", int64(2)},
   261  		{map[string]uint{"foo": 1, "bar": 2, "baz": 3}, "bar", uint64(2)},
   262  		{map[string]float64{"foo": 1.1, "bar": 2.2, "baz": 3.3}, "bar", float64(2.2)},
   263  		{map[string]string{"foo": "FOO", "bar": "BAR", "baz": "BAZ"}, "bar", "BAR"},
   264  		{map[string]TstX{"foo": {A: "a", B: "b"}, "bar": {A: "c", B: "d"}, "baz": {A: "e", B: "f"}}, "bar", ""},
   265  		{map[string]any{"foo": nil}, "foo", ""},
   266  		{(*[]string)(nil), "bar", ""},
   267  	} {
   268  		errMsg := qt.Commentf("[%d] %v", i, test)
   269  
   270  		result := ns.EchoParam(test.a, test.key)
   271  
   272  		c.Assert(result, qt.Equals, test.expect, errMsg)
   273  	}
   274  }
   275  
   276  func TestFirst(t *testing.T) {
   277  	t.Parallel()
   278  	c := qt.New(t)
   279  
   280  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   281  
   282  	for i, test := range []struct {
   283  		limit  any
   284  		seq    any
   285  		expect any
   286  	}{
   287  		{int(2), []string{"a", "b", "c"}, []string{"a", "b"}},
   288  		{int32(3), []string{"a", "b"}, []string{"a", "b"}},
   289  		{int64(2), []int{100, 200, 300}, []int{100, 200}},
   290  		{100, []int{100, 200}, []int{100, 200}},
   291  		{"1", []int{100, 200, 300}, []int{100}},
   292  		{0, []string{"h", "u", "g", "o"}, []string{}},
   293  		{int64(-1), []int{100, 200, 300}, false},
   294  		{"noint", []int{100, 200, 300}, false},
   295  		{1, nil, false},
   296  		{nil, []int{100}, false},
   297  		{1, t, false},
   298  		{1, (*string)(nil), false},
   299  	} {
   300  		errMsg := qt.Commentf("[%d] %v", i, test)
   301  
   302  		result, err := ns.First(test.limit, test.seq)
   303  
   304  		if b, ok := test.expect.(bool); ok && !b {
   305  			c.Assert(err, qt.Not(qt.IsNil), errMsg)
   306  			continue
   307  		}
   308  
   309  		c.Assert(err, qt.IsNil)
   310  		c.Assert(result, qt.DeepEquals, test.expect, errMsg)
   311  	}
   312  }
   313  
   314  func TestIn(t *testing.T) {
   315  	t.Parallel()
   316  	c := qt.New(t)
   317  
   318  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   319  
   320  	for i, test := range []struct {
   321  		l1     any
   322  		l2     any
   323  		expect bool
   324  	}{
   325  		{[]string{"a", "b", "c"}, "b", true},
   326  		{[]any{"a", "b", "c"}, "b", true},
   327  		{[]any{"a", "b", "c"}, "d", false},
   328  		{[]string{"a", "b", "c"}, "d", false},
   329  		{[]string{"a", "12", "c"}, 12, false},
   330  		{[]string{"a", "b", "c"}, nil, false},
   331  		{[]int{1, 2, 4}, 2, true},
   332  		{[]any{1, 2, 4}, 2, true},
   333  		{[]any{1, 2, 4}, nil, false},
   334  		{[]any{nil}, nil, false},
   335  		{[]int{1, 2, 4}, 3, false},
   336  		{[]float64{1.23, 2.45, 4.67}, 1.23, true},
   337  		{[]float64{1.234567, 2.45, 4.67}, 1.234568, false},
   338  		{[]float64{1, 2, 3}, 1, true},
   339  		{[]float32{1, 2, 3}, 1, true},
   340  		{"this substring should be found", "substring", true},
   341  		{"this substring should not be found", "subseastring", false},
   342  		{nil, "foo", false},
   343  		// Pointers
   344  		{pagesPtr{p1, p2, p3, p2}, p2, true},
   345  		{pagesPtr{p1, p2, p3, p2}, p4, false},
   346  		// Structs
   347  		{pagesVals{p3v, p2v, p3v, p2v}, p2v, true},
   348  		{pagesVals{p3v, p2v, p3v, p2v}, p4v, false},
   349  		// template.HTML
   350  		{template.HTML("this substring should be found"), "substring", true},
   351  		{template.HTML("this substring should not be found"), "subseastring", false},
   352  		// Uncomparable, use hashstructure
   353  		{[]string{"a", "b"}, []string{"a", "b"}, false},
   354  		{[][]string{{"a", "b"}}, []string{"a", "b"}, true},
   355  	} {
   356  
   357  		errMsg := qt.Commentf("[%d] %v", i, test)
   358  
   359  		result, err := ns.In(test.l1, test.l2)
   360  		c.Assert(err, qt.IsNil)
   361  		c.Assert(result, qt.Equals, test.expect, errMsg)
   362  	}
   363  }
   364  
   365  type testPage struct {
   366  	Title string
   367  }
   368  
   369  func (p testPage) String() string {
   370  	return "p-" + p.Title
   371  }
   372  
   373  type (
   374  	pagesPtr  []*testPage
   375  	pagesVals []testPage
   376  )
   377  
   378  var (
   379  	p1 = &testPage{"A"}
   380  	p2 = &testPage{"B"}
   381  	p3 = &testPage{"C"}
   382  	p4 = &testPage{"D"}
   383  
   384  	p1v = testPage{"A"}
   385  	p2v = testPage{"B"}
   386  	p3v = testPage{"C"}
   387  	p4v = testPage{"D"}
   388  )
   389  
   390  func TestIntersect(t *testing.T) {
   391  	t.Parallel()
   392  	c := qt.New(t)
   393  
   394  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   395  
   396  	for i, test := range []struct {
   397  		l1, l2 any
   398  		expect any
   399  	}{
   400  		{[]string{"a", "b", "c", "c"}, []string{"a", "b", "b"}, []string{"a", "b"}},
   401  		{[]string{"a", "b"}, []string{"a", "b", "c"}, []string{"a", "b"}},
   402  		{[]string{"a", "b", "c"}, []string{"d", "e"}, []string{}},
   403  		{[]string{}, []string{}, []string{}},
   404  		{[]string{"a", "b"}, nil, []any{}},
   405  		{nil, []string{"a", "b"}, []any{}},
   406  		{nil, nil, []any{}},
   407  		{[]string{"1", "2"}, []int{1, 2}, []string{}},
   408  		{[]int{1, 2}, []string{"1", "2"}, []int{}},
   409  		{[]int{1, 2, 4}, []int{2, 4}, []int{2, 4}},
   410  		{[]int{2, 4}, []int{1, 2, 4}, []int{2, 4}},
   411  		{[]int{1, 2, 4}, []int{3, 6}, []int{}},
   412  		{[]float64{2.2, 4.4}, []float64{1.1, 2.2, 4.4}, []float64{2.2, 4.4}},
   413  
   414  		// []interface{} ∩ []interface{}
   415  		{[]any{"a", "b", "c"}, []any{"a", "b", "b"}, []any{"a", "b"}},
   416  		{[]any{1, 2, 3}, []any{1, 2, 2}, []any{1, 2}},
   417  		{[]any{int8(1), int8(2), int8(3)}, []any{int8(1), int8(2), int8(2)}, []any{int8(1), int8(2)}},
   418  		{[]any{int16(1), int16(2), int16(3)}, []any{int16(1), int16(2), int16(2)}, []any{int16(1), int16(2)}},
   419  		{[]any{int32(1), int32(2), int32(3)}, []any{int32(1), int32(2), int32(2)}, []any{int32(1), int32(2)}},
   420  		{[]any{int64(1), int64(2), int64(3)}, []any{int64(1), int64(2), int64(2)}, []any{int64(1), int64(2)}},
   421  		{[]any{float32(1), float32(2), float32(3)}, []any{float32(1), float32(2), float32(2)}, []any{float32(1), float32(2)}},
   422  		{[]any{float64(1), float64(2), float64(3)}, []any{float64(1), float64(2), float64(2)}, []any{float64(1), float64(2)}},
   423  
   424  		// []interface{} ∩ []T
   425  		{[]any{"a", "b", "c"}, []string{"a", "b", "b"}, []any{"a", "b"}},
   426  		{[]any{1, 2, 3}, []int{1, 2, 2}, []any{1, 2}},
   427  		{[]any{int8(1), int8(2), int8(3)}, []int8{1, 2, 2}, []any{int8(1), int8(2)}},
   428  		{[]any{int16(1), int16(2), int16(3)}, []int16{1, 2, 2}, []any{int16(1), int16(2)}},
   429  		{[]any{int32(1), int32(2), int32(3)}, []int32{1, 2, 2}, []any{int32(1), int32(2)}},
   430  		{[]any{int64(1), int64(2), int64(3)}, []int64{1, 2, 2}, []any{int64(1), int64(2)}},
   431  		{[]any{uint(1), uint(2), uint(3)}, []uint{1, 2, 2}, []any{uint(1), uint(2)}},
   432  		{[]any{float32(1), float32(2), float32(3)}, []float32{1, 2, 2}, []any{float32(1), float32(2)}},
   433  		{[]any{float64(1), float64(2), float64(3)}, []float64{1, 2, 2}, []any{float64(1), float64(2)}},
   434  
   435  		// []T ∩ []interface{}
   436  		{[]string{"a", "b", "c"}, []any{"a", "b", "b"}, []string{"a", "b"}},
   437  		{[]int{1, 2, 3}, []any{1, 2, 2}, []int{1, 2}},
   438  		{[]int8{1, 2, 3}, []any{int8(1), int8(2), int8(2)}, []int8{1, 2}},
   439  		{[]int16{1, 2, 3}, []any{int16(1), int16(2), int16(2)}, []int16{1, 2}},
   440  		{[]int32{1, 2, 3}, []any{int32(1), int32(2), int32(2)}, []int32{1, 2}},
   441  		{[]int64{1, 2, 3}, []any{int64(1), int64(2), int64(2)}, []int64{1, 2}},
   442  		{[]float32{1, 2, 3}, []any{float32(1), float32(2), float32(2)}, []float32{1, 2}},
   443  		{[]float64{1, 2, 3}, []any{float64(1), float64(2), float64(2)}, []float64{1, 2}},
   444  
   445  		// Structs
   446  		{pagesPtr{p1, p4, p2, p3}, pagesPtr{p4, p2, p2}, pagesPtr{p4, p2}},
   447  		{pagesVals{p1v, p4v, p2v, p3v}, pagesVals{p1v, p3v, p3v}, pagesVals{p1v, p3v}},
   448  		{[]any{p1, p4, p2, p3}, []any{p4, p2, p2}, []any{p4, p2}},
   449  		{[]any{p1v, p4v, p2v, p3v}, []any{p1v, p3v, p3v}, []any{p1v, p3v}},
   450  		{pagesPtr{p1, p4, p2, p3}, pagesPtr{}, pagesPtr{}},
   451  		{pagesVals{}, pagesVals{p1v, p3v, p3v}, pagesVals{}},
   452  		{[]any{p1, p4, p2, p3}, []any{}, []any{}},
   453  		{[]any{}, []any{p1v, p3v, p3v}, []any{}},
   454  
   455  		// errors
   456  		{"not array or slice", []string{"a"}, false},
   457  		{[]string{"a"}, "not array or slice", false},
   458  
   459  		// uncomparable types - #3820
   460  		{[]map[int]int{{1: 1}, {2: 2}}, []map[int]int{{2: 2}, {3: 3}}, false},
   461  		{[][]int{{1, 1}, {1, 2}}, [][]int{{1, 2}, {1, 2}, {1, 3}}, false},
   462  		{[]int{1, 1}, [][]int{{1, 2}, {1, 2}, {1, 3}}, false},
   463  	} {
   464  
   465  		errMsg := qt.Commentf("[%d] %v", i, test)
   466  
   467  		result, err := ns.Intersect(test.l1, test.l2)
   468  
   469  		if b, ok := test.expect.(bool); ok && !b {
   470  			c.Assert(err, qt.Not(qt.IsNil), errMsg)
   471  			continue
   472  		}
   473  
   474  		c.Assert(err, qt.IsNil, errMsg)
   475  		if !reflect.DeepEqual(result, test.expect) {
   476  			t.Fatalf("[%d] Got\n%v expected\n%v", i, result, test.expect)
   477  		}
   478  	}
   479  }
   480  
   481  func TestIsSet(t *testing.T) {
   482  	t.Parallel()
   483  	c := qt.New(t)
   484  	ns := newTestNs()
   485  
   486  	for i, test := range []struct {
   487  		a      any
   488  		key    any
   489  		expect bool
   490  		isErr  bool
   491  	}{
   492  		{[]any{1, 2, 3, 5}, 2, true, false},
   493  		{[]any{1, 2, 3, 5}, "2", true, false},
   494  		{[]any{1, 2, 3, 5}, 2.0, true, false},
   495  
   496  		{[]any{1, 2, 3, 5}, 22, false, false},
   497  
   498  		{map[string]any{"a": 1, "b": 2}, "b", true, false},
   499  		{map[string]any{"a": 1, "b": 2}, "bc", false, false},
   500  
   501  		{time.Now(), "Day", false, false},
   502  		{nil, "nil", false, false},
   503  		{[]any{1, 2, 3, 5}, TstX{}, false, true},
   504  	} {
   505  		errMsg := qt.Commentf("[%d] %v", i, test)
   506  
   507  		result, err := ns.IsSet(test.a, test.key)
   508  		if test.isErr {
   509  			continue
   510  		}
   511  
   512  		c.Assert(err, qt.IsNil, errMsg)
   513  		c.Assert(result, qt.Equals, test.expect, errMsg)
   514  	}
   515  }
   516  
   517  func TestLast(t *testing.T) {
   518  	t.Parallel()
   519  	c := qt.New(t)
   520  
   521  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   522  
   523  	for i, test := range []struct {
   524  		limit  any
   525  		seq    any
   526  		expect any
   527  	}{
   528  		{int(2), []string{"a", "b", "c"}, []string{"b", "c"}},
   529  		{int32(3), []string{"a", "b"}, []string{"a", "b"}},
   530  		{int64(2), []int{100, 200, 300}, []int{200, 300}},
   531  		{100, []int{100, 200}, []int{100, 200}},
   532  		{"1", []int{100, 200, 300}, []int{300}},
   533  		{"0", []int{100, 200, 300}, []int{}},
   534  		{"0", []string{"a", "b", "c"}, []string{}},
   535  		// errors
   536  		{int64(-1), []int{100, 200, 300}, false},
   537  		{"noint", []int{100, 200, 300}, false},
   538  		{1, nil, false},
   539  		{nil, []int{100}, false},
   540  		{1, t, false},
   541  		{1, (*string)(nil), false},
   542  	} {
   543  		errMsg := qt.Commentf("[%d] %v", i, test)
   544  
   545  		result, err := ns.Last(test.limit, test.seq)
   546  
   547  		if b, ok := test.expect.(bool); ok && !b {
   548  			c.Assert(err, qt.Not(qt.IsNil), errMsg)
   549  			continue
   550  		}
   551  
   552  		c.Assert(err, qt.IsNil, errMsg)
   553  		c.Assert(result, qt.DeepEquals, test.expect, errMsg)
   554  	}
   555  }
   556  
   557  func TestQuerify(t *testing.T) {
   558  	t.Parallel()
   559  	c := qt.New(t)
   560  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   561  
   562  	for i, test := range []struct {
   563  		params []any
   564  		expect any
   565  	}{
   566  		{[]any{"a", "b"}, "a=b"},
   567  		{[]any{"a", "b", "c", "d", "f", " &"}, `a=b&c=d&f=+%26`},
   568  		{[]any{[]string{"a", "b"}}, "a=b"},
   569  		{[]any{[]string{"a", "b", "c", "d", "f", " &"}}, `a=b&c=d&f=+%26`},
   570  		{[]any{[]any{"x", "y"}}, `x=y`},
   571  		{[]any{[]any{"x", 5}}, `x=5`},
   572  		// errors
   573  		{[]any{5, "b"}, false},
   574  		{[]any{"a", "b", "c"}, false},
   575  		{[]any{[]string{"a", "b", "c"}}, false},
   576  		{[]any{[]string{"a", "b"}, "c"}, false},
   577  		{[]any{[]any{"c", "d", "e"}}, false},
   578  	} {
   579  		errMsg := qt.Commentf("[%d] %v", i, test.params)
   580  
   581  		result, err := ns.Querify(test.params...)
   582  
   583  		if b, ok := test.expect.(bool); ok && !b {
   584  			c.Assert(err, qt.Not(qt.IsNil), errMsg)
   585  			continue
   586  		}
   587  
   588  		c.Assert(err, qt.IsNil, errMsg)
   589  		c.Assert(result, qt.Equals, test.expect, errMsg)
   590  	}
   591  }
   592  
   593  func BenchmarkQuerify(b *testing.B) {
   594  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   595  	params := []any{"a", "b", "c", "d", "f", " &"}
   596  
   597  	b.ResetTimer()
   598  	for i := 0; i < b.N; i++ {
   599  		_, err := ns.Querify(params...)
   600  		if err != nil {
   601  			b.Fatal(err)
   602  		}
   603  	}
   604  }
   605  
   606  func BenchmarkQuerifySlice(b *testing.B) {
   607  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   608  	params := []string{"a", "b", "c", "d", "f", " &"}
   609  
   610  	b.ResetTimer()
   611  	for i := 0; i < b.N; i++ {
   612  		_, err := ns.Querify(params)
   613  		if err != nil {
   614  			b.Fatal(err)
   615  		}
   616  	}
   617  }
   618  
   619  func TestSeq(t *testing.T) {
   620  	t.Parallel()
   621  	c := qt.New(t)
   622  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   623  
   624  	for i, test := range []struct {
   625  		args   []any
   626  		expect any
   627  	}{
   628  		{[]any{-2, 5}, []int{-2, -1, 0, 1, 2, 3, 4, 5}},
   629  		{[]any{1, 2, 4}, []int{1, 3}},
   630  		{[]any{1}, []int{1}},
   631  		{[]any{3}, []int{1, 2, 3}},
   632  		{[]any{3.2}, []int{1, 2, 3}},
   633  		{[]any{0}, []int{}},
   634  		{[]any{-1}, []int{-1}},
   635  		{[]any{-3}, []int{-1, -2, -3}},
   636  		{[]any{3, -2}, []int{3, 2, 1, 0, -1, -2}},
   637  		{[]any{6, -2, 2}, []int{6, 4, 2}},
   638  		// errors
   639  		{[]any{1, 0, 2}, false},
   640  		{[]any{1, -1, 2}, false},
   641  		{[]any{2, 1, 1}, false},
   642  		{[]any{2, 1, 1, 1}, false},
   643  		{[]any{2001}, false},
   644  		{[]any{}, false},
   645  		{[]any{0, -1000000}, false},
   646  		{[]any{tstNoStringer{}}, false},
   647  		{nil, false},
   648  	} {
   649  		errMsg := qt.Commentf("[%d] %v", i, test)
   650  
   651  		result, err := ns.Seq(test.args...)
   652  
   653  		if b, ok := test.expect.(bool); ok && !b {
   654  			c.Assert(err, qt.Not(qt.IsNil), errMsg)
   655  			continue
   656  		}
   657  
   658  		c.Assert(err, qt.IsNil, errMsg)
   659  		c.Assert(result, qt.DeepEquals, test.expect, errMsg)
   660  	}
   661  }
   662  
   663  func TestShuffle(t *testing.T) {
   664  	t.Parallel()
   665  	c := qt.New(t)
   666  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   667  
   668  	for i, test := range []struct {
   669  		seq     any
   670  		success bool
   671  	}{
   672  		{[]string{"a", "b", "c", "d"}, true},
   673  		{[]int{100, 200, 300}, true},
   674  		{[]int{100, 200, 300}, true},
   675  		{[]int{100, 200}, true},
   676  		{[]string{"a", "b"}, true},
   677  		{[]int{100, 200, 300}, true},
   678  		{[]int{100, 200, 300}, true},
   679  		{[]int{100}, true},
   680  		// errors
   681  		{nil, false},
   682  		{t, false},
   683  		{(*string)(nil), false},
   684  	} {
   685  		errMsg := qt.Commentf("[%d] %v", i, test)
   686  
   687  		result, err := ns.Shuffle(test.seq)
   688  
   689  		if !test.success {
   690  			c.Assert(err, qt.Not(qt.IsNil), errMsg)
   691  			continue
   692  		}
   693  
   694  		c.Assert(err, qt.IsNil, errMsg)
   695  
   696  		resultv := reflect.ValueOf(result)
   697  		seqv := reflect.ValueOf(test.seq)
   698  
   699  		c.Assert(seqv.Len(), qt.Equals, resultv.Len(), errMsg)
   700  	}
   701  }
   702  
   703  func TestShuffleRandomising(t *testing.T) {
   704  	t.Parallel()
   705  	c := qt.New(t)
   706  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   707  
   708  	// Note that this test can fail with false negative result if the shuffle
   709  	// of the sequence happens to be the same as the original sequence. However
   710  	// the probability of the event is 10^-158 which is negligible.
   711  	seqLen := 100
   712  	rand.Seed(time.Now().UTC().UnixNano())
   713  
   714  	for _, test := range []struct {
   715  		seq []int
   716  	}{
   717  		{rand.Perm(seqLen)},
   718  	} {
   719  		result, err := ns.Shuffle(test.seq)
   720  		resultv := reflect.ValueOf(result)
   721  
   722  		c.Assert(err, qt.IsNil)
   723  
   724  		allSame := true
   725  		for i, v := range test.seq {
   726  			allSame = allSame && (resultv.Index(i).Interface() == v)
   727  		}
   728  
   729  		c.Assert(allSame, qt.Equals, false)
   730  	}
   731  }
   732  
   733  // Also see tests in commons/collection.
   734  func TestSlice(t *testing.T) {
   735  	t.Parallel()
   736  	c := qt.New(t)
   737  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   738  
   739  	for i, test := range []struct {
   740  		args     []any
   741  		expected any
   742  	}{
   743  		{[]any{"a", "b"}, []string{"a", "b"}},
   744  		{[]any{}, []any{}},
   745  		{[]any{nil}, []any{nil}},
   746  		{[]any{5, "b"}, []any{5, "b"}},
   747  		{[]any{tstNoStringer{}}, []tstNoStringer{{}}},
   748  	} {
   749  		errMsg := qt.Commentf("[%d] %v", i, test.args)
   750  
   751  		result := ns.Slice(test.args...)
   752  
   753  		c.Assert(result, qt.DeepEquals, test.expected, errMsg)
   754  	}
   755  }
   756  
   757  func TestUnion(t *testing.T) {
   758  	t.Parallel()
   759  	c := qt.New(t)
   760  
   761  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   762  
   763  	for i, test := range []struct {
   764  		l1     any
   765  		l2     any
   766  		expect any
   767  		isErr  bool
   768  	}{
   769  		{nil, nil, []any{}, false},
   770  		{nil, []string{"a", "b"}, []string{"a", "b"}, false},
   771  		{[]string{"a", "b"}, nil, []string{"a", "b"}, false},
   772  
   773  		// []A ∪ []B
   774  		{[]string{"1", "2"}, []int{3}, []string{}, false},
   775  		{[]int{1, 2}, []string{"1", "2"}, []int{}, false},
   776  
   777  		// []T ∪ []T
   778  		{[]string{"a", "b", "c", "c"}, []string{"a", "b", "b"}, []string{"a", "b", "c"}, false},
   779  		{[]string{"a", "b"}, []string{"a", "b", "c"}, []string{"a", "b", "c"}, false},
   780  		{[]string{"a", "b", "c"}, []string{"d", "e"}, []string{"a", "b", "c", "d", "e"}, false},
   781  		{[]string{}, []string{}, []string{}, false},
   782  		{[]int{1, 2, 3}, []int{3, 4, 5}, []int{1, 2, 3, 4, 5}, false},
   783  		{[]int{1, 2, 3}, []int{1, 2, 3}, []int{1, 2, 3}, false},
   784  		{[]int{1, 2, 4}, []int{2, 4}, []int{1, 2, 4}, false},
   785  		{[]int{2, 4}, []int{1, 2, 4}, []int{2, 4, 1}, false},
   786  		{[]int{1, 2, 4}, []int{3, 6}, []int{1, 2, 4, 3, 6}, false},
   787  		{[]float64{2.2, 4.4}, []float64{1.1, 2.2, 4.4}, []float64{2.2, 4.4, 1.1}, false},
   788  		{[]any{"a", "b", "c", "c"}, []any{"a", "b", "b"}, []any{"a", "b", "c"}, false},
   789  
   790  		// []T ∪ []interface{}
   791  		{[]string{"1", "2"}, []any{"9"}, []string{"1", "2", "9"}, false},
   792  		{[]int{2, 4}, []any{1, 2, 4}, []int{2, 4, 1}, false},
   793  		{[]int8{2, 4}, []any{int8(1), int8(2), int8(4)}, []int8{2, 4, 1}, false},
   794  		{[]int8{2, 4}, []any{1, 2, 4}, []int8{2, 4, 1}, false},
   795  		{[]int16{2, 4}, []any{1, 2, 4}, []int16{2, 4, 1}, false},
   796  		{[]int32{2, 4}, []any{1, 2, 4}, []int32{2, 4, 1}, false},
   797  		{[]int64{2, 4}, []any{1, 2, 4}, []int64{2, 4, 1}, false},
   798  
   799  		{[]float64{2.2, 4.4}, []any{1.1, 2.2, 4.4}, []float64{2.2, 4.4, 1.1}, false},
   800  		{[]float32{2.2, 4.4}, []any{1.1, 2.2, 4.4}, []float32{2.2, 4.4, 1.1}, false},
   801  
   802  		// []interface{} ∪ []T
   803  		{[]any{"a", "b", "c", "c"}, []string{"a", "b", "d"}, []any{"a", "b", "c", "d"}, false},
   804  		{[]any{}, []string{}, []any{}, false},
   805  		{[]any{1, 2}, []int{2, 3}, []any{1, 2, 3}, false},
   806  		{[]any{1, 2}, []int8{2, 3}, []any{1, 2, 3}, false}, // 28
   807  		{[]any{uint(1), uint(2)}, []uint{2, 3}, []any{uint(1), uint(2), uint(3)}, false},
   808  		{[]any{1.1, 2.2}, []float64{2.2, 3.3}, []any{1.1, 2.2, 3.3}, false},
   809  
   810  		// Structs
   811  		{pagesPtr{p1, p4}, pagesPtr{p4, p2, p2}, pagesPtr{p1, p4, p2}, false},
   812  		{pagesVals{p1v}, pagesVals{p3v, p3v}, pagesVals{p1v, p3v}, false},
   813  		{[]any{p1, p4}, []any{p4, p2, p2}, []any{p1, p4, p2}, false},
   814  		{[]any{p1v}, []any{p3v, p3v}, []any{p1v, p3v}, false},
   815  		// #3686
   816  		{[]any{p1v}, []any{}, []any{p1v}, false},
   817  		{[]any{}, []any{p1v}, []any{p1v}, false},
   818  		{pagesPtr{p1}, pagesPtr{}, pagesPtr{p1}, false},
   819  		{pagesVals{p1v}, pagesVals{}, pagesVals{p1v}, false},
   820  		{pagesPtr{}, pagesPtr{p1}, pagesPtr{p1}, false},
   821  		{pagesVals{}, pagesVals{p1v}, pagesVals{p1v}, false},
   822  
   823  		// errors
   824  		{"not array or slice", []string{"a"}, false, true},
   825  		{[]string{"a"}, "not array or slice", false, true},
   826  
   827  		// uncomparable types - #3820
   828  		{[]map[string]int{{"K1": 1}}, []map[string]int{{"K2": 2}, {"K2": 2}}, false, true},
   829  		{[][]int{{1, 1}, {1, 2}}, [][]int{{2, 1}, {2, 2}}, false, true},
   830  	} {
   831  
   832  		errMsg := qt.Commentf("[%d] %v", i, test)
   833  
   834  		result, err := ns.Union(test.l1, test.l2)
   835  		if test.isErr {
   836  			c.Assert(err, qt.Not(qt.IsNil), errMsg)
   837  			continue
   838  		}
   839  
   840  		c.Assert(err, qt.IsNil, errMsg)
   841  		if !reflect.DeepEqual(result, test.expect) {
   842  			t.Fatalf("[%d] Got\n%v expected\n%v", i, result, test.expect)
   843  		}
   844  	}
   845  }
   846  
   847  func TestUniq(t *testing.T) {
   848  	t.Parallel()
   849  	c := qt.New(t)
   850  	ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())})
   851  	for i, test := range []struct {
   852  		l      any
   853  		expect any
   854  		isErr  bool
   855  	}{
   856  		{[]string{"a", "b", "c"}, []string{"a", "b", "c"}, false},
   857  		{[]string{"a", "b", "c", "c"}, []string{"a", "b", "c"}, false},
   858  		{[]string{"a", "b", "b", "c"}, []string{"a", "b", "c"}, false},
   859  		{[]string{"a", "b", "c", "b"}, []string{"a", "b", "c"}, false},
   860  		{[]int{1, 2, 3}, []int{1, 2, 3}, false},
   861  		{[]int{1, 2, 3, 3}, []int{1, 2, 3}, false},
   862  		{[]int{1, 2, 2, 3}, []int{1, 2, 3}, false},
   863  		{[]int{1, 2, 3, 2}, []int{1, 2, 3}, false},
   864  		{[4]int{1, 2, 3, 2}, []int{1, 2, 3}, false},
   865  		{nil, make([]any, 0), false},
   866  		// Pointers
   867  		{pagesPtr{p1, p2, p3, p2}, pagesPtr{p1, p2, p3}, false},
   868  		{pagesPtr{}, pagesPtr{}, false},
   869  		// Structs
   870  		{pagesVals{p3v, p2v, p3v, p2v}, pagesVals{p3v, p2v}, false},
   871  
   872  		// not Comparable(), use hashstructure
   873  		{[]map[string]int{
   874  			{"K1": 1}, {"K2": 2}, {"K1": 1}, {"K2": 1},
   875  		}, []map[string]int{
   876  			{"K1": 1}, {"K2": 2}, {"K2": 1},
   877  		}, false},
   878  
   879  		// should fail
   880  		{1, 1, true},
   881  		{"foo", "fo", true},
   882  	} {
   883  		errMsg := qt.Commentf("[%d] %v", i, test)
   884  
   885  		result, err := ns.Uniq(test.l)
   886  		if test.isErr {
   887  			c.Assert(err, qt.Not(qt.IsNil), errMsg)
   888  			continue
   889  		}
   890  
   891  		c.Assert(err, qt.IsNil, errMsg)
   892  		c.Assert(result, qt.DeepEquals, test.expect, errMsg)
   893  	}
   894  }
   895  
   896  func (x *TstX) TstRp() string {
   897  	return "r" + x.A
   898  }
   899  
   900  func (x TstX) TstRv() string {
   901  	return "r" + x.B
   902  }
   903  
   904  func (x TstX) TstRv2() string {
   905  	return "r" + x.B
   906  }
   907  
   908  func (x TstX) unexportedMethod() string {
   909  	return x.unexported
   910  }
   911  
   912  func (x TstX) MethodWithArg(s string) string {
   913  	return s
   914  }
   915  
   916  func (x TstX) MethodReturnNothing() {}
   917  
   918  func (x TstX) MethodReturnErrorOnly() error {
   919  	return errors.New("some error occurred")
   920  }
   921  
   922  func (x TstX) MethodReturnTwoValues() (string, string) {
   923  	return "foo", "bar"
   924  }
   925  
   926  func (x TstX) MethodReturnValueWithError() (string, error) {
   927  	return "", errors.New("some error occurred")
   928  }
   929  
   930  func (x TstX) String() string {
   931  	return fmt.Sprintf("A: %s, B: %s", x.A, x.B)
   932  }
   933  
   934  type TstX struct {
   935  	A, B       string
   936  	unexported string
   937  }
   938  
   939  type TstParams struct {
   940  	params maps.Params
   941  }
   942  
   943  func (x TstParams) Params() maps.Params {
   944  	return x.params
   945  }
   946  
   947  type TstXIHolder struct {
   948  	XI TstXI
   949  }
   950  
   951  // Partially implemented by the TstX struct.
   952  type TstXI interface {
   953  	TstRv2() string
   954  }
   955  
   956  func ToTstXIs(slice any) []TstXI {
   957  	s := reflect.ValueOf(slice)
   958  	if s.Kind() != reflect.Slice {
   959  		return nil
   960  	}
   961  	tis := make([]TstXI, s.Len())
   962  
   963  	for i := 0; i < s.Len(); i++ {
   964  		tsti, ok := s.Index(i).Interface().(TstXI)
   965  		if !ok {
   966  			return nil
   967  		}
   968  		tis[i] = tsti
   969  	}
   970  
   971  	return tis
   972  }
   973  
   974  func newDeps(cfg config.Provider) *deps.Deps {
   975  	l := langs.NewLanguage("en", cfg)
   976  	l.Set("i18nDir", "i18n")
   977  	cs, err := helpers.NewContentSpec(l, loggers.NewErrorLogger(), afero.NewMemMapFs(), nil)
   978  	if err != nil {
   979  		panic(err)
   980  	}
   981  	return &deps.Deps{
   982  		Language:    l,
   983  		Cfg:         cfg,
   984  		Fs:          hugofs.NewMem(l),
   985  		ContentSpec: cs,
   986  		Log:         loggers.NewErrorLogger(),
   987  	}
   988  }
   989  
   990  func newTestNs() *Namespace {
   991  	return New(newDeps(config.NewWithTestDefaults()))
   992  }