hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

collections_test.go (29395B)

    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 }