blob: 20161e52d8228d4bc864f3569f5fdc82d2535348 [file] [log] [blame]
Colin Crossb6715442017-10-24 11:13:31 -07001// Copyright 2017 Google Inc. 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//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
Colin Cross454c0872019-02-15 23:03:34 -080018 "fmt"
Colin Crossb6715442017-10-24 11:13:31 -070019 "reflect"
Colin Cross27027c72020-02-28 15:34:17 -080020 "strconv"
Martin Stjernholm1461c4d2021-03-27 19:04:05 +000021 "strings"
Colin Crossb6715442017-10-24 11:13:31 -070022 "testing"
Colin Crossb5e3f7d2023-07-06 15:37:53 -070023 "unsafe"
Colin Crossb6715442017-10-24 11:13:31 -070024)
25
26var firstUniqueStringsTestCases = []struct {
27 in []string
28 out []string
29}{
30 {
31 in: []string{"a"},
32 out: []string{"a"},
33 },
34 {
35 in: []string{"a", "b"},
36 out: []string{"a", "b"},
37 },
38 {
39 in: []string{"a", "a"},
40 out: []string{"a"},
41 },
42 {
43 in: []string{"a", "b", "a"},
44 out: []string{"a", "b"},
45 },
46 {
47 in: []string{"b", "a", "a"},
48 out: []string{"b", "a"},
49 },
50 {
51 in: []string{"a", "a", "b"},
52 out: []string{"a", "b"},
53 },
54 {
55 in: []string{"a", "b", "a", "b"},
56 out: []string{"a", "b"},
57 },
58 {
59 in: []string{"liblog", "libdl", "libc++", "libdl", "libc", "libm"},
60 out: []string{"liblog", "libdl", "libc++", "libc", "libm"},
61 },
62}
63
64func TestFirstUniqueStrings(t *testing.T) {
Colin Cross27027c72020-02-28 15:34:17 -080065 f := func(t *testing.T, imp func([]string) []string, in, want []string) {
66 t.Helper()
67 out := imp(in)
68 if !reflect.DeepEqual(out, want) {
Colin Crossb6715442017-10-24 11:13:31 -070069 t.Errorf("incorrect output:")
Colin Cross27027c72020-02-28 15:34:17 -080070 t.Errorf(" input: %#v", in)
71 t.Errorf(" expected: %#v", want)
Colin Crossb6715442017-10-24 11:13:31 -070072 t.Errorf(" got: %#v", out)
73 }
74 }
Colin Cross27027c72020-02-28 15:34:17 -080075
76 for _, testCase := range firstUniqueStringsTestCases {
77 t.Run("list", func(t *testing.T) {
Colin Crossc85750b2022-04-21 12:50:51 -070078 f(t, firstUniqueList[string], testCase.in, testCase.out)
Colin Cross27027c72020-02-28 15:34:17 -080079 })
80 t.Run("map", func(t *testing.T) {
Colin Crossc85750b2022-04-21 12:50:51 -070081 f(t, firstUniqueMap[string], testCase.in, testCase.out)
Colin Cross27027c72020-02-28 15:34:17 -080082 })
83 }
Colin Crossb6715442017-10-24 11:13:31 -070084}
85
86var lastUniqueStringsTestCases = []struct {
87 in []string
88 out []string
89}{
90 {
91 in: []string{"a"},
92 out: []string{"a"},
93 },
94 {
95 in: []string{"a", "b"},
96 out: []string{"a", "b"},
97 },
98 {
99 in: []string{"a", "a"},
100 out: []string{"a"},
101 },
102 {
103 in: []string{"a", "b", "a"},
104 out: []string{"b", "a"},
105 },
106 {
107 in: []string{"b", "a", "a"},
108 out: []string{"b", "a"},
109 },
110 {
111 in: []string{"a", "a", "b"},
112 out: []string{"a", "b"},
113 },
114 {
115 in: []string{"a", "b", "a", "b"},
116 out: []string{"a", "b"},
117 },
118 {
119 in: []string{"liblog", "libdl", "libc++", "libdl", "libc", "libm"},
120 out: []string{"liblog", "libc++", "libdl", "libc", "libm"},
121 },
122}
123
124func TestLastUniqueStrings(t *testing.T) {
125 for _, testCase := range lastUniqueStringsTestCases {
126 out := LastUniqueStrings(testCase.in)
127 if !reflect.DeepEqual(out, testCase.out) {
128 t.Errorf("incorrect output:")
129 t.Errorf(" input: %#v", testCase.in)
130 t.Errorf(" expected: %#v", testCase.out)
131 t.Errorf(" got: %#v", out)
132 }
133 }
134}
Logan Chien5e877b12018-03-08 18:14:19 +0800135
136func TestJoinWithPrefix(t *testing.T) {
137 testcases := []struct {
138 name string
139 input []string
140 expected string
141 }{
142 {
143 name: "zero_inputs",
144 input: []string{},
145 expected: "",
146 },
147 {
148 name: "one_input",
149 input: []string{"a"},
150 expected: "prefix:a",
151 },
152 {
153 name: "two_inputs",
154 input: []string{"a", "b"},
155 expected: "prefix:a prefix:b",
156 },
157 }
158
159 prefix := "prefix:"
160
161 for _, testCase := range testcases {
162 t.Run(testCase.name, func(t *testing.T) {
163 out := JoinWithPrefix(testCase.input, prefix)
164 if out != testCase.expected {
165 t.Errorf("incorrect output:")
166 t.Errorf(" input: %#v", testCase.input)
167 t.Errorf(" prefix: %#v", prefix)
168 t.Errorf(" expected: %#v", testCase.expected)
169 t.Errorf(" got: %#v", out)
170 }
171 })
172 }
173}
174
175func TestIndexList(t *testing.T) {
176 input := []string{"a", "b", "c"}
177
178 testcases := []struct {
179 key string
180 expected int
181 }{
182 {
183 key: "a",
184 expected: 0,
185 },
186 {
187 key: "b",
188 expected: 1,
189 },
190 {
191 key: "c",
192 expected: 2,
193 },
194 {
195 key: "X",
196 expected: -1,
197 },
198 }
199
200 for _, testCase := range testcases {
201 t.Run(testCase.key, func(t *testing.T) {
202 out := IndexList(testCase.key, input)
203 if out != testCase.expected {
204 t.Errorf("incorrect output:")
205 t.Errorf(" key: %#v", testCase.key)
206 t.Errorf(" input: %#v", input)
207 t.Errorf(" expected: %#v", testCase.expected)
208 t.Errorf(" got: %#v", out)
209 }
210 })
211 }
212}
213
214func TestInList(t *testing.T) {
215 input := []string{"a"}
216
217 testcases := []struct {
218 key string
219 expected bool
220 }{
221 {
222 key: "a",
223 expected: true,
224 },
225 {
226 key: "X",
227 expected: false,
228 },
229 }
230
231 for _, testCase := range testcases {
232 t.Run(testCase.key, func(t *testing.T) {
233 out := InList(testCase.key, input)
234 if out != testCase.expected {
235 t.Errorf("incorrect output:")
236 t.Errorf(" key: %#v", testCase.key)
237 t.Errorf(" input: %#v", input)
238 t.Errorf(" expected: %#v", testCase.expected)
239 t.Errorf(" got: %#v", out)
240 }
241 })
242 }
243}
244
245func TestPrefixInList(t *testing.T) {
246 prefixes := []string{"a", "b"}
247
248 testcases := []struct {
249 str string
250 expected bool
251 }{
252 {
253 str: "a-example",
254 expected: true,
255 },
256 {
257 str: "b-example",
258 expected: true,
259 },
260 {
261 str: "X-example",
262 expected: false,
263 },
264 }
265
266 for _, testCase := range testcases {
267 t.Run(testCase.str, func(t *testing.T) {
Jaewoong Jung3aff5782020-02-11 07:54:35 -0800268 out := HasAnyPrefix(testCase.str, prefixes)
Logan Chien5e877b12018-03-08 18:14:19 +0800269 if out != testCase.expected {
270 t.Errorf("incorrect output:")
271 t.Errorf(" str: %#v", testCase.str)
272 t.Errorf(" prefixes: %#v", prefixes)
273 t.Errorf(" expected: %#v", testCase.expected)
274 t.Errorf(" got: %#v", out)
275 }
276 })
277 }
278}
279
280func TestFilterList(t *testing.T) {
281 input := []string{"a", "b", "c", "c", "b", "d", "a"}
282 filter := []string{"a", "c"}
283 remainder, filtered := FilterList(input, filter)
284
285 expected := []string{"b", "b", "d"}
286 if !reflect.DeepEqual(remainder, expected) {
287 t.Errorf("incorrect remainder output:")
288 t.Errorf(" input: %#v", input)
289 t.Errorf(" filter: %#v", filter)
290 t.Errorf(" expected: %#v", expected)
291 t.Errorf(" got: %#v", remainder)
292 }
293
294 expected = []string{"a", "c", "c", "a"}
295 if !reflect.DeepEqual(filtered, expected) {
296 t.Errorf("incorrect filtered output:")
297 t.Errorf(" input: %#v", input)
298 t.Errorf(" filter: %#v", filter)
299 t.Errorf(" expected: %#v", expected)
300 t.Errorf(" got: %#v", filtered)
301 }
302}
303
Martin Stjernholm1461c4d2021-03-27 19:04:05 +0000304func TestFilterListPred(t *testing.T) {
305 pred := func(s string) bool { return strings.HasPrefix(s, "a/") }
306 AssertArrayString(t, "filter", FilterListPred([]string{"a/c", "b/a", "a/b"}, pred), []string{"a/c", "a/b"})
307 AssertArrayString(t, "filter", FilterListPred([]string{"b/c", "a/a", "b/b"}, pred), []string{"a/a"})
308 AssertArrayString(t, "filter", FilterListPred([]string{"c/c", "b/a", "c/b"}, pred), []string{})
309 AssertArrayString(t, "filter", FilterListPred([]string{"a/c", "a/a", "a/b"}, pred), []string{"a/c", "a/a", "a/b"})
310}
311
Logan Chien5e877b12018-03-08 18:14:19 +0800312func TestRemoveListFromList(t *testing.T) {
313 input := []string{"a", "b", "c", "d", "a", "c", "d"}
314 filter := []string{"a", "c"}
315 expected := []string{"b", "d", "d"}
316 out := RemoveListFromList(input, filter)
317 if !reflect.DeepEqual(out, expected) {
318 t.Errorf("incorrect output:")
319 t.Errorf(" input: %#v", input)
320 t.Errorf(" filter: %#v", filter)
321 t.Errorf(" expected: %#v", expected)
322 t.Errorf(" got: %#v", out)
323 }
324}
Logan Chien7922ab82018-03-06 18:29:27 +0800325
326func TestRemoveFromList(t *testing.T) {
327 testcases := []struct {
328 name string
329 key string
330 input []string
331 expectedFound bool
332 expectedOut []string
333 }{
334 {
335 name: "remove_one_match",
336 key: "a",
337 input: []string{"a", "b", "c"},
338 expectedFound: true,
339 expectedOut: []string{"b", "c"},
340 },
341 {
342 name: "remove_three_matches",
343 key: "a",
344 input: []string{"a", "b", "a", "c", "a"},
345 expectedFound: true,
346 expectedOut: []string{"b", "c"},
347 },
348 {
349 name: "remove_zero_matches",
350 key: "X",
351 input: []string{"a", "b", "a", "c", "a"},
352 expectedFound: false,
353 expectedOut: []string{"a", "b", "a", "c", "a"},
354 },
355 {
356 name: "remove_all_matches",
357 key: "a",
358 input: []string{"a", "a", "a", "a"},
359 expectedFound: true,
360 expectedOut: []string{},
361 },
362 }
363
364 for _, testCase := range testcases {
365 t.Run(testCase.name, func(t *testing.T) {
366 found, out := RemoveFromList(testCase.key, testCase.input)
367 if found != testCase.expectedFound {
368 t.Errorf("incorrect output:")
369 t.Errorf(" key: %#v", testCase.key)
370 t.Errorf(" input: %#v", testCase.input)
371 t.Errorf(" expected: %#v", testCase.expectedFound)
372 t.Errorf(" got: %#v", found)
373 }
374 if !reflect.DeepEqual(out, testCase.expectedOut) {
375 t.Errorf("incorrect output:")
376 t.Errorf(" key: %#v", testCase.key)
377 t.Errorf(" input: %#v", testCase.input)
378 t.Errorf(" expected: %#v", testCase.expectedOut)
379 t.Errorf(" got: %#v", out)
380 }
381 })
382 }
383}
Colin Cross454c0872019-02-15 23:03:34 -0800384
Spandan Dascc4da762023-04-27 19:34:08 +0000385func TestCopyOfEmptyAndNil(t *testing.T) {
386 emptyList := []string{}
387 copyOfEmptyList := CopyOf(emptyList)
388 AssertBoolEquals(t, "Copy of an empty list should be an empty list and not nil", true, copyOfEmptyList != nil)
Colin Cross13aeb682023-07-06 15:02:56 -0700389 copyOfNilList := CopyOf([]string(nil))
Spandan Dascc4da762023-04-27 19:34:08 +0000390 AssertBoolEquals(t, "Copy of a nil list should be a nil list and not an empty list", true, copyOfNilList == nil)
391}
392
Colin Cross454c0872019-02-15 23:03:34 -0800393func ExampleCopyOf() {
394 a := []string{"1", "2", "3"}
395 b := CopyOf(a)
396 a[0] = "-1"
397 fmt.Printf("a = %q\n", a)
398 fmt.Printf("b = %q\n", b)
399
400 // Output:
401 // a = ["-1" "2" "3"]
402 // b = ["1" "2" "3"]
403}
404
405func ExampleCopyOf_append() {
406 a := make([]string, 1, 2)
407 a[0] = "foo"
408
409 fmt.Println("Without CopyOf:")
410 b := append(a, "bar")
411 c := append(a, "baz")
412 fmt.Printf("a = %q\n", a)
413 fmt.Printf("b = %q\n", b)
414 fmt.Printf("c = %q\n", c)
415
416 a = make([]string, 1, 2)
417 a[0] = "foo"
418
419 fmt.Println("With CopyOf:")
420 b = append(CopyOf(a), "bar")
421 c = append(CopyOf(a), "baz")
422 fmt.Printf("a = %q\n", a)
423 fmt.Printf("b = %q\n", b)
424 fmt.Printf("c = %q\n", c)
425
426 // Output:
427 // Without CopyOf:
428 // a = ["foo"]
429 // b = ["foo" "baz"]
430 // c = ["foo" "baz"]
431 // With CopyOf:
432 // a = ["foo"]
433 // b = ["foo" "bar"]
434 // c = ["foo" "baz"]
435}
Ivan Lozano022a73b2019-09-09 20:29:31 -0700436
437func TestSplitFileExt(t *testing.T) {
438 t.Run("soname with version", func(t *testing.T) {
439 root, suffix, ext := SplitFileExt("libtest.so.1.0.30")
440 expected := "libtest"
441 if root != expected {
442 t.Errorf("root should be %q but got %q", expected, root)
443 }
444 expected = ".so.1.0.30"
445 if suffix != expected {
446 t.Errorf("suffix should be %q but got %q", expected, suffix)
447 }
448 expected = ".so"
449 if ext != expected {
450 t.Errorf("ext should be %q but got %q", expected, ext)
451 }
452 })
453
454 t.Run("soname with svn version", func(t *testing.T) {
455 root, suffix, ext := SplitFileExt("libtest.so.1svn")
456 expected := "libtest"
457 if root != expected {
458 t.Errorf("root should be %q but got %q", expected, root)
459 }
460 expected = ".so.1svn"
461 if suffix != expected {
462 t.Errorf("suffix should be %q but got %q", expected, suffix)
463 }
464 expected = ".so"
465 if ext != expected {
466 t.Errorf("ext should be %q but got %q", expected, ext)
467 }
468 })
469
470 t.Run("version numbers in the middle should be ignored", func(t *testing.T) {
471 root, suffix, ext := SplitFileExt("libtest.1.0.30.so")
472 expected := "libtest.1.0.30"
473 if root != expected {
474 t.Errorf("root should be %q but got %q", expected, root)
475 }
476 expected = ".so"
477 if suffix != expected {
478 t.Errorf("suffix should be %q but got %q", expected, suffix)
479 }
480 expected = ".so"
481 if ext != expected {
482 t.Errorf("ext should be %q but got %q", expected, ext)
483 }
484 })
485
486 t.Run("no known file extension", func(t *testing.T) {
487 root, suffix, ext := SplitFileExt("test.exe")
488 expected := "test"
489 if root != expected {
490 t.Errorf("root should be %q but got %q", expected, root)
491 }
492 expected = ".exe"
493 if suffix != expected {
494 t.Errorf("suffix should be %q but got %q", expected, suffix)
495 }
496 if ext != expected {
497 t.Errorf("ext should be %q but got %q", expected, ext)
498 }
499 })
500}
Colin Cross0a2f7192019-09-23 14:33:09 -0700501
502func Test_Shard(t *testing.T) {
503 type args struct {
504 strings []string
505 shardSize int
506 }
507 tests := []struct {
508 name string
509 args args
510 want [][]string
511 }{
512 {
513 name: "empty",
514 args: args{
515 strings: nil,
516 shardSize: 1,
517 },
518 want: [][]string(nil),
519 },
520 {
521 name: "single shard",
522 args: args{
523 strings: []string{"a", "b"},
524 shardSize: 2,
525 },
526 want: [][]string{{"a", "b"}},
527 },
528 {
529 name: "single short shard",
530 args: args{
531 strings: []string{"a", "b"},
532 shardSize: 3,
533 },
534 want: [][]string{{"a", "b"}},
535 },
536 {
537 name: "shard per input",
538 args: args{
539 strings: []string{"a", "b", "c"},
540 shardSize: 1,
541 },
542 want: [][]string{{"a"}, {"b"}, {"c"}},
543 },
544 {
545 name: "balanced shards",
546 args: args{
547 strings: []string{"a", "b", "c", "d"},
548 shardSize: 2,
549 },
550 want: [][]string{{"a", "b"}, {"c", "d"}},
551 },
552 {
553 name: "unbalanced shards",
554 args: args{
555 strings: []string{"a", "b", "c"},
556 shardSize: 2,
557 },
558 want: [][]string{{"a", "b"}, {"c"}},
559 },
560 }
561 for _, tt := range tests {
562 t.Run(tt.name, func(t *testing.T) {
563 t.Run("strings", func(t *testing.T) {
564 if got := ShardStrings(tt.args.strings, tt.args.shardSize); !reflect.DeepEqual(got, tt.want) {
565 t.Errorf("ShardStrings(%v, %v) = %v, want %v",
566 tt.args.strings, tt.args.shardSize, got, tt.want)
567 }
568 })
569
570 t.Run("paths", func(t *testing.T) {
571 stringsToPaths := func(strings []string) Paths {
572 if strings == nil {
573 return nil
574 }
575 paths := make(Paths, len(strings))
576 for i, s := range strings {
577 paths[i] = PathForTesting(s)
578 }
579 return paths
580 }
581
582 paths := stringsToPaths(tt.args.strings)
583
584 var want []Paths
585 if sWant := tt.want; sWant != nil {
586 want = make([]Paths, len(sWant))
587 for i, w := range sWant {
588 want[i] = stringsToPaths(w)
589 }
590 }
591
592 if got := ShardPaths(paths, tt.args.shardSize); !reflect.DeepEqual(got, want) {
593 t.Errorf("ShardPaths(%v, %v) = %v, want %v",
594 paths, tt.args.shardSize, got, want)
595 }
596 })
597 })
598 }
599}
Colin Cross27027c72020-02-28 15:34:17 -0800600
601func BenchmarkFirstUniqueStrings(b *testing.B) {
602 implementations := []struct {
603 name string
604 f func([]string) []string
605 }{
606 {
607 name: "list",
Colin Crossc85750b2022-04-21 12:50:51 -0700608 f: firstUniqueList[string],
Colin Cross27027c72020-02-28 15:34:17 -0800609 },
610 {
611 name: "map",
Colin Crossc85750b2022-04-21 12:50:51 -0700612 f: firstUniqueMap[string],
Colin Cross27027c72020-02-28 15:34:17 -0800613 },
Colin Cross96c44122020-11-25 14:29:50 -0800614 {
615 name: "optimal",
616 f: FirstUniqueStrings,
617 },
Colin Cross27027c72020-02-28 15:34:17 -0800618 }
619 const maxSize = 1024
620 uniqueStrings := make([]string, maxSize)
621 for i := range uniqueStrings {
622 uniqueStrings[i] = strconv.Itoa(i)
623 }
624 sameString := make([]string, maxSize)
625 for i := range sameString {
626 sameString[i] = uniqueStrings[0]
627 }
628
629 f := func(b *testing.B, imp func([]string) []string, s []string) {
630 for i := 0; i < b.N; i++ {
631 b.ReportAllocs()
632 s = append([]string(nil), s...)
633 imp(s)
634 }
635 }
636
637 for n := 1; n <= maxSize; n <<= 1 {
638 b.Run(strconv.Itoa(n), func(b *testing.B) {
639 for _, implementation := range implementations {
640 b.Run(implementation.name, func(b *testing.B) {
641 b.Run("same", func(b *testing.B) {
642 f(b, implementation.f, sameString[:n])
643 })
644 b.Run("unique", func(b *testing.B) {
645 f(b, implementation.f, uniqueStrings[:n])
646 })
647 })
648 }
649 })
650 }
651}
Colin Cross9eb853b2022-02-17 11:13:37 -0800652
Cole Faust18994c72023-02-28 16:02:16 -0800653func testSortedKeysHelper[K Ordered, V any](t *testing.T, name string, input map[K]V, expected []K) {
654 t.Helper()
655 t.Run(name, func(t *testing.T) {
656 actual := SortedKeys(input)
657 if !reflect.DeepEqual(actual, expected) {
Spandan Dasc52e2c02023-03-02 23:45:10 +0000658 t.Errorf("expected %v, got %v", expected, actual)
Cole Faust18994c72023-02-28 16:02:16 -0800659 }
660 })
661}
662
663func TestSortedKeys(t *testing.T) {
664 testSortedKeysHelper(t, "simple", map[string]string{
665 "b": "bar",
666 "a": "foo",
667 }, []string{
668 "a",
669 "b",
670 })
671 testSortedKeysHelper(t, "ints", map[int]interface{}{
672 10: nil,
673 5: nil,
674 }, []int{
675 5,
676 10,
677 })
678
679 testSortedKeysHelper(t, "nil", map[string]string(nil), nil)
680 testSortedKeysHelper(t, "empty", map[string]string{}, nil)
681}
682
Colin Cross9eb853b2022-02-17 11:13:37 -0800683func TestSortedStringValues(t *testing.T) {
684 testCases := []struct {
685 name string
686 in interface{}
687 expected []string
688 }{
689 {
690 name: "nil",
691 in: map[string]string(nil),
692 expected: nil,
693 },
694 {
695 name: "empty",
696 in: map[string]string{},
697 expected: nil,
698 },
699 {
700 name: "simple",
701 in: map[string]string{"foo": "a", "bar": "b"},
702 expected: []string{"a", "b"},
703 },
704 {
705 name: "duplicates",
706 in: map[string]string{"foo": "a", "bar": "b", "baz": "b"},
707 expected: []string{"a", "b", "b"},
708 },
709 }
710
711 for _, tt := range testCases {
712 t.Run(tt.name, func(t *testing.T) {
713 got := SortedStringValues(tt.in)
714 if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
715 t.Errorf("wanted %q, got %q", w, g)
716 }
717 })
718 }
719}
720
721func TestSortedUniqueStringValues(t *testing.T) {
722 testCases := []struct {
723 name string
724 in interface{}
725 expected []string
726 }{
727 {
728 name: "nil",
729 in: map[string]string(nil),
730 expected: nil,
731 },
732 {
733 name: "empty",
734 in: map[string]string{},
735 expected: nil,
736 },
737 {
738 name: "simple",
739 in: map[string]string{"foo": "a", "bar": "b"},
740 expected: []string{"a", "b"},
741 },
742 {
743 name: "duplicates",
744 in: map[string]string{"foo": "a", "bar": "b", "baz": "b"},
745 expected: []string{"a", "b"},
746 },
747 }
748
749 for _, tt := range testCases {
750 t.Run(tt.name, func(t *testing.T) {
751 got := SortedUniqueStringValues(tt.in)
752 if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
753 t.Errorf("wanted %q, got %q", w, g)
754 }
755 })
756 }
757}
Colin Crossb5e3f7d2023-07-06 15:37:53 -0700758
759var reverseTestCases = []struct {
760 name string
761 in []string
762 expected []string
763}{
764 {
765 name: "nil",
766 in: nil,
767 expected: nil,
768 },
769 {
770 name: "empty",
771 in: []string{},
772 expected: []string{},
773 },
774 {
775 name: "one",
776 in: []string{"one"},
777 expected: []string{"one"},
778 },
779 {
780 name: "even",
781 in: []string{"one", "two"},
782 expected: []string{"two", "one"},
783 },
784 {
785 name: "odd",
786 in: []string{"one", "two", "three"},
787 expected: []string{"three", "two", "one"},
788 },
789}
790
791func TestReverseSliceInPlace(t *testing.T) {
792 for _, testCase := range reverseTestCases {
793 t.Run(testCase.name, func(t *testing.T) {
794 slice := CopyOf(testCase.in)
795 slice2 := slice
796 ReverseSliceInPlace(slice)
797 if !reflect.DeepEqual(slice, testCase.expected) {
798 t.Errorf("expected %#v, got %#v", testCase.expected, slice)
799 }
800 if unsafe.SliceData(slice) != unsafe.SliceData(slice2) {
801 t.Errorf("expected slices to share backing array")
802 }
803 })
804 }
805}
806
807func TestReverseSlice(t *testing.T) {
808 for _, testCase := range reverseTestCases {
809 t.Run(testCase.name, func(t *testing.T) {
810 slice := ReverseSlice(testCase.in)
811 if !reflect.DeepEqual(slice, testCase.expected) {
812 t.Errorf("expected %#v, got %#v", testCase.expected, slice)
813 }
814 if slice != nil && unsafe.SliceData(testCase.in) == unsafe.SliceData(slice) {
815 t.Errorf("expected slices to have different backing arrays")
816 }
817 })
818 }
819}