1// 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 ( 18 "fmt" 19 "reflect" 20 "testing" 21) 22 23var firstUniqueStringsTestCases = []struct { 24 in []string 25 out []string 26}{ 27 { 28 in: []string{"a"}, 29 out: []string{"a"}, 30 }, 31 { 32 in: []string{"a", "b"}, 33 out: []string{"a", "b"}, 34 }, 35 { 36 in: []string{"a", "a"}, 37 out: []string{"a"}, 38 }, 39 { 40 in: []string{"a", "b", "a"}, 41 out: []string{"a", "b"}, 42 }, 43 { 44 in: []string{"b", "a", "a"}, 45 out: []string{"b", "a"}, 46 }, 47 { 48 in: []string{"a", "a", "b"}, 49 out: []string{"a", "b"}, 50 }, 51 { 52 in: []string{"a", "b", "a", "b"}, 53 out: []string{"a", "b"}, 54 }, 55 { 56 in: []string{"liblog", "libdl", "libc++", "libdl", "libc", "libm"}, 57 out: []string{"liblog", "libdl", "libc++", "libc", "libm"}, 58 }, 59} 60 61func TestFirstUniqueStrings(t *testing.T) { 62 for _, testCase := range firstUniqueStringsTestCases { 63 out := FirstUniqueStrings(testCase.in) 64 if !reflect.DeepEqual(out, testCase.out) { 65 t.Errorf("incorrect output:") 66 t.Errorf(" input: %#v", testCase.in) 67 t.Errorf(" expected: %#v", testCase.out) 68 t.Errorf(" got: %#v", out) 69 } 70 } 71} 72 73var lastUniqueStringsTestCases = []struct { 74 in []string 75 out []string 76}{ 77 { 78 in: []string{"a"}, 79 out: []string{"a"}, 80 }, 81 { 82 in: []string{"a", "b"}, 83 out: []string{"a", "b"}, 84 }, 85 { 86 in: []string{"a", "a"}, 87 out: []string{"a"}, 88 }, 89 { 90 in: []string{"a", "b", "a"}, 91 out: []string{"b", "a"}, 92 }, 93 { 94 in: []string{"b", "a", "a"}, 95 out: []string{"b", "a"}, 96 }, 97 { 98 in: []string{"a", "a", "b"}, 99 out: []string{"a", "b"}, 100 }, 101 { 102 in: []string{"a", "b", "a", "b"}, 103 out: []string{"a", "b"}, 104 }, 105 { 106 in: []string{"liblog", "libdl", "libc++", "libdl", "libc", "libm"}, 107 out: []string{"liblog", "libc++", "libdl", "libc", "libm"}, 108 }, 109} 110 111func TestLastUniqueStrings(t *testing.T) { 112 for _, testCase := range lastUniqueStringsTestCases { 113 out := LastUniqueStrings(testCase.in) 114 if !reflect.DeepEqual(out, testCase.out) { 115 t.Errorf("incorrect output:") 116 t.Errorf(" input: %#v", testCase.in) 117 t.Errorf(" expected: %#v", testCase.out) 118 t.Errorf(" got: %#v", out) 119 } 120 } 121} 122 123func TestJoinWithPrefix(t *testing.T) { 124 testcases := []struct { 125 name string 126 input []string 127 expected string 128 }{ 129 { 130 name: "zero_inputs", 131 input: []string{}, 132 expected: "", 133 }, 134 { 135 name: "one_input", 136 input: []string{"a"}, 137 expected: "prefix:a", 138 }, 139 { 140 name: "two_inputs", 141 input: []string{"a", "b"}, 142 expected: "prefix:a prefix:b", 143 }, 144 } 145 146 prefix := "prefix:" 147 148 for _, testCase := range testcases { 149 t.Run(testCase.name, func(t *testing.T) { 150 out := JoinWithPrefix(testCase.input, prefix) 151 if out != testCase.expected { 152 t.Errorf("incorrect output:") 153 t.Errorf(" input: %#v", testCase.input) 154 t.Errorf(" prefix: %#v", prefix) 155 t.Errorf(" expected: %#v", testCase.expected) 156 t.Errorf(" got: %#v", out) 157 } 158 }) 159 } 160} 161 162func TestIndexList(t *testing.T) { 163 input := []string{"a", "b", "c"} 164 165 testcases := []struct { 166 key string 167 expected int 168 }{ 169 { 170 key: "a", 171 expected: 0, 172 }, 173 { 174 key: "b", 175 expected: 1, 176 }, 177 { 178 key: "c", 179 expected: 2, 180 }, 181 { 182 key: "X", 183 expected: -1, 184 }, 185 } 186 187 for _, testCase := range testcases { 188 t.Run(testCase.key, func(t *testing.T) { 189 out := IndexList(testCase.key, input) 190 if out != testCase.expected { 191 t.Errorf("incorrect output:") 192 t.Errorf(" key: %#v", testCase.key) 193 t.Errorf(" input: %#v", input) 194 t.Errorf(" expected: %#v", testCase.expected) 195 t.Errorf(" got: %#v", out) 196 } 197 }) 198 } 199} 200 201func TestInList(t *testing.T) { 202 input := []string{"a"} 203 204 testcases := []struct { 205 key string 206 expected bool 207 }{ 208 { 209 key: "a", 210 expected: true, 211 }, 212 { 213 key: "X", 214 expected: false, 215 }, 216 } 217 218 for _, testCase := range testcases { 219 t.Run(testCase.key, func(t *testing.T) { 220 out := InList(testCase.key, input) 221 if out != testCase.expected { 222 t.Errorf("incorrect output:") 223 t.Errorf(" key: %#v", testCase.key) 224 t.Errorf(" input: %#v", input) 225 t.Errorf(" expected: %#v", testCase.expected) 226 t.Errorf(" got: %#v", out) 227 } 228 }) 229 } 230} 231 232func TestPrefixInList(t *testing.T) { 233 prefixes := []string{"a", "b"} 234 235 testcases := []struct { 236 str string 237 expected bool 238 }{ 239 { 240 str: "a-example", 241 expected: true, 242 }, 243 { 244 str: "b-example", 245 expected: true, 246 }, 247 { 248 str: "X-example", 249 expected: false, 250 }, 251 } 252 253 for _, testCase := range testcases { 254 t.Run(testCase.str, func(t *testing.T) { 255 out := HasAnyPrefix(testCase.str, prefixes) 256 if out != testCase.expected { 257 t.Errorf("incorrect output:") 258 t.Errorf(" str: %#v", testCase.str) 259 t.Errorf(" prefixes: %#v", prefixes) 260 t.Errorf(" expected: %#v", testCase.expected) 261 t.Errorf(" got: %#v", out) 262 } 263 }) 264 } 265} 266 267func TestFilterList(t *testing.T) { 268 input := []string{"a", "b", "c", "c", "b", "d", "a"} 269 filter := []string{"a", "c"} 270 remainder, filtered := FilterList(input, filter) 271 272 expected := []string{"b", "b", "d"} 273 if !reflect.DeepEqual(remainder, expected) { 274 t.Errorf("incorrect remainder output:") 275 t.Errorf(" input: %#v", input) 276 t.Errorf(" filter: %#v", filter) 277 t.Errorf(" expected: %#v", expected) 278 t.Errorf(" got: %#v", remainder) 279 } 280 281 expected = []string{"a", "c", "c", "a"} 282 if !reflect.DeepEqual(filtered, expected) { 283 t.Errorf("incorrect filtered output:") 284 t.Errorf(" input: %#v", input) 285 t.Errorf(" filter: %#v", filter) 286 t.Errorf(" expected: %#v", expected) 287 t.Errorf(" got: %#v", filtered) 288 } 289} 290 291func TestRemoveListFromList(t *testing.T) { 292 input := []string{"a", "b", "c", "d", "a", "c", "d"} 293 filter := []string{"a", "c"} 294 expected := []string{"b", "d", "d"} 295 out := RemoveListFromList(input, filter) 296 if !reflect.DeepEqual(out, expected) { 297 t.Errorf("incorrect output:") 298 t.Errorf(" input: %#v", input) 299 t.Errorf(" filter: %#v", filter) 300 t.Errorf(" expected: %#v", expected) 301 t.Errorf(" got: %#v", out) 302 } 303} 304 305func TestRemoveFromList(t *testing.T) { 306 testcases := []struct { 307 name string 308 key string 309 input []string 310 expectedFound bool 311 expectedOut []string 312 }{ 313 { 314 name: "remove_one_match", 315 key: "a", 316 input: []string{"a", "b", "c"}, 317 expectedFound: true, 318 expectedOut: []string{"b", "c"}, 319 }, 320 { 321 name: "remove_three_matches", 322 key: "a", 323 input: []string{"a", "b", "a", "c", "a"}, 324 expectedFound: true, 325 expectedOut: []string{"b", "c"}, 326 }, 327 { 328 name: "remove_zero_matches", 329 key: "X", 330 input: []string{"a", "b", "a", "c", "a"}, 331 expectedFound: false, 332 expectedOut: []string{"a", "b", "a", "c", "a"}, 333 }, 334 { 335 name: "remove_all_matches", 336 key: "a", 337 input: []string{"a", "a", "a", "a"}, 338 expectedFound: true, 339 expectedOut: []string{}, 340 }, 341 } 342 343 for _, testCase := range testcases { 344 t.Run(testCase.name, func(t *testing.T) { 345 found, out := RemoveFromList(testCase.key, testCase.input) 346 if found != testCase.expectedFound { 347 t.Errorf("incorrect output:") 348 t.Errorf(" key: %#v", testCase.key) 349 t.Errorf(" input: %#v", testCase.input) 350 t.Errorf(" expected: %#v", testCase.expectedFound) 351 t.Errorf(" got: %#v", found) 352 } 353 if !reflect.DeepEqual(out, testCase.expectedOut) { 354 t.Errorf("incorrect output:") 355 t.Errorf(" key: %#v", testCase.key) 356 t.Errorf(" input: %#v", testCase.input) 357 t.Errorf(" expected: %#v", testCase.expectedOut) 358 t.Errorf(" got: %#v", out) 359 } 360 }) 361 } 362} 363 364func ExampleCopyOf() { 365 a := []string{"1", "2", "3"} 366 b := CopyOf(a) 367 a[0] = "-1" 368 fmt.Printf("a = %q\n", a) 369 fmt.Printf("b = %q\n", b) 370 371 // Output: 372 // a = ["-1" "2" "3"] 373 // b = ["1" "2" "3"] 374} 375 376func ExampleCopyOf_append() { 377 a := make([]string, 1, 2) 378 a[0] = "foo" 379 380 fmt.Println("Without CopyOf:") 381 b := append(a, "bar") 382 c := append(a, "baz") 383 fmt.Printf("a = %q\n", a) 384 fmt.Printf("b = %q\n", b) 385 fmt.Printf("c = %q\n", c) 386 387 a = make([]string, 1, 2) 388 a[0] = "foo" 389 390 fmt.Println("With CopyOf:") 391 b = append(CopyOf(a), "bar") 392 c = append(CopyOf(a), "baz") 393 fmt.Printf("a = %q\n", a) 394 fmt.Printf("b = %q\n", b) 395 fmt.Printf("c = %q\n", c) 396 397 // Output: 398 // Without CopyOf: 399 // a = ["foo"] 400 // b = ["foo" "baz"] 401 // c = ["foo" "baz"] 402 // With CopyOf: 403 // a = ["foo"] 404 // b = ["foo" "bar"] 405 // c = ["foo" "baz"] 406} 407 408func TestSplitFileExt(t *testing.T) { 409 t.Run("soname with version", func(t *testing.T) { 410 root, suffix, ext := SplitFileExt("libtest.so.1.0.30") 411 expected := "libtest" 412 if root != expected { 413 t.Errorf("root should be %q but got %q", expected, root) 414 } 415 expected = ".so.1.0.30" 416 if suffix != expected { 417 t.Errorf("suffix should be %q but got %q", expected, suffix) 418 } 419 expected = ".so" 420 if ext != expected { 421 t.Errorf("ext should be %q but got %q", expected, ext) 422 } 423 }) 424 425 t.Run("soname with svn version", func(t *testing.T) { 426 root, suffix, ext := SplitFileExt("libtest.so.1svn") 427 expected := "libtest" 428 if root != expected { 429 t.Errorf("root should be %q but got %q", expected, root) 430 } 431 expected = ".so.1svn" 432 if suffix != expected { 433 t.Errorf("suffix should be %q but got %q", expected, suffix) 434 } 435 expected = ".so" 436 if ext != expected { 437 t.Errorf("ext should be %q but got %q", expected, ext) 438 } 439 }) 440 441 t.Run("version numbers in the middle should be ignored", func(t *testing.T) { 442 root, suffix, ext := SplitFileExt("libtest.1.0.30.so") 443 expected := "libtest.1.0.30" 444 if root != expected { 445 t.Errorf("root should be %q but got %q", expected, root) 446 } 447 expected = ".so" 448 if suffix != expected { 449 t.Errorf("suffix should be %q but got %q", expected, suffix) 450 } 451 expected = ".so" 452 if ext != expected { 453 t.Errorf("ext should be %q but got %q", expected, ext) 454 } 455 }) 456 457 t.Run("no known file extension", func(t *testing.T) { 458 root, suffix, ext := SplitFileExt("test.exe") 459 expected := "test" 460 if root != expected { 461 t.Errorf("root should be %q but got %q", expected, root) 462 } 463 expected = ".exe" 464 if suffix != expected { 465 t.Errorf("suffix should be %q but got %q", expected, suffix) 466 } 467 if ext != expected { 468 t.Errorf("ext should be %q but got %q", expected, ext) 469 } 470 }) 471} 472 473func Test_Shard(t *testing.T) { 474 type args struct { 475 strings []string 476 shardSize int 477 } 478 tests := []struct { 479 name string 480 args args 481 want [][]string 482 }{ 483 { 484 name: "empty", 485 args: args{ 486 strings: nil, 487 shardSize: 1, 488 }, 489 want: [][]string(nil), 490 }, 491 { 492 name: "single shard", 493 args: args{ 494 strings: []string{"a", "b"}, 495 shardSize: 2, 496 }, 497 want: [][]string{{"a", "b"}}, 498 }, 499 { 500 name: "single short shard", 501 args: args{ 502 strings: []string{"a", "b"}, 503 shardSize: 3, 504 }, 505 want: [][]string{{"a", "b"}}, 506 }, 507 { 508 name: "shard per input", 509 args: args{ 510 strings: []string{"a", "b", "c"}, 511 shardSize: 1, 512 }, 513 want: [][]string{{"a"}, {"b"}, {"c"}}, 514 }, 515 { 516 name: "balanced shards", 517 args: args{ 518 strings: []string{"a", "b", "c", "d"}, 519 shardSize: 2, 520 }, 521 want: [][]string{{"a", "b"}, {"c", "d"}}, 522 }, 523 { 524 name: "unbalanced shards", 525 args: args{ 526 strings: []string{"a", "b", "c"}, 527 shardSize: 2, 528 }, 529 want: [][]string{{"a", "b"}, {"c"}}, 530 }, 531 } 532 for _, tt := range tests { 533 t.Run(tt.name, func(t *testing.T) { 534 t.Run("strings", func(t *testing.T) { 535 if got := ShardStrings(tt.args.strings, tt.args.shardSize); !reflect.DeepEqual(got, tt.want) { 536 t.Errorf("ShardStrings(%v, %v) = %v, want %v", 537 tt.args.strings, tt.args.shardSize, got, tt.want) 538 } 539 }) 540 541 t.Run("paths", func(t *testing.T) { 542 stringsToPaths := func(strings []string) Paths { 543 if strings == nil { 544 return nil 545 } 546 paths := make(Paths, len(strings)) 547 for i, s := range strings { 548 paths[i] = PathForTesting(s) 549 } 550 return paths 551 } 552 553 paths := stringsToPaths(tt.args.strings) 554 555 var want []Paths 556 if sWant := tt.want; sWant != nil { 557 want = make([]Paths, len(sWant)) 558 for i, w := range sWant { 559 want[i] = stringsToPaths(w) 560 } 561 } 562 563 if got := ShardPaths(paths, tt.args.shardSize); !reflect.DeepEqual(got, want) { 564 t.Errorf("ShardPaths(%v, %v) = %v, want %v", 565 paths, tt.args.shardSize, got, want) 566 } 567 }) 568 }) 569 } 570} 571