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