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 }