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