cuelang.org/go@v0.10.1/pkg/path/path_test.go (about) 1 // Copyright 2020 CUE Authors 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 15 // Copyright 2009 The Go Authors. All rights reserved. 16 // Use of this source code is governed by a BSD-style 17 // license that can be found in the LICENSE file. 18 19 package path 20 21 import ( 22 "fmt" 23 "reflect" 24 "runtime" 25 "slices" 26 "testing" 27 ) 28 29 func testEachOS(t *testing.T, list []OS, fn func(t *testing.T, os OS)) { 30 for _, os := range list { 31 t.Run(fmt.Sprintf("OS=%s", os), func(t *testing.T) { 32 fn(t, os) 33 }) 34 } 35 } 36 37 type PathTest struct { 38 path, result string 39 } 40 41 var cleantests = []PathTest{ 42 // Already clean 43 {"abc", "abc"}, 44 {"abc/def", "abc/def"}, 45 {"a/b/c", "a/b/c"}, 46 {".", "."}, 47 {"..", ".."}, 48 {"../..", "../.."}, 49 {"../../abc", "../../abc"}, 50 {"/abc", "/abc"}, 51 {"/", "/"}, 52 53 // Empty is current dir 54 {"", "."}, 55 56 // Remove trailing slash 57 {"abc/", "abc"}, 58 {"abc/def/", "abc/def"}, 59 {"a/b/c/", "a/b/c"}, 60 {"./", "."}, 61 {"../", ".."}, 62 {"../../", "../.."}, 63 {"/abc/", "/abc"}, 64 65 // Remove doubled slash 66 {"abc//def//ghi", "abc/def/ghi"}, 67 {"//abc", "/abc"}, 68 {"///abc", "/abc"}, 69 {"//abc//", "/abc"}, 70 {"abc//", "abc"}, 71 72 // Remove . elements 73 {"abc/./def", "abc/def"}, 74 {"/./abc/def", "/abc/def"}, 75 {"abc/.", "abc"}, 76 77 // Remove .. elements 78 {"abc/def/ghi/../jkl", "abc/def/jkl"}, 79 {"abc/def/../ghi/../jkl", "abc/jkl"}, 80 {"abc/def/..", "abc"}, 81 {"abc/def/../..", "."}, 82 {"/abc/def/../..", "/"}, 83 {"abc/def/../../..", ".."}, 84 {"/abc/def/../../..", "/"}, 85 {"abc/def/../../../ghi/jkl/../../../mno", "../../mno"}, 86 {"/../abc", "/abc"}, 87 88 // Combinations 89 {"abc/./../def", "def"}, 90 {"abc//./../def", "def"}, 91 {"abc/../../././../def", "../../def"}, 92 } 93 94 var wincleantests = []PathTest{ 95 {`c:`, `c:.`}, 96 {`c:\`, `c:\`}, 97 {`c:\abc`, `c:\abc`}, 98 {`c:abc\..\..\.\.\..\def`, `c:..\..\def`}, 99 {`c:\abc\def\..\..`, `c:\`}, 100 {`c:\..\abc`, `c:\abc`}, 101 {`c:..\abc`, `c:..\abc`}, 102 {`\`, `\`}, 103 {`/`, `\`}, 104 {`\\i\..\c$`, `\c$`}, 105 {`\\i\..\i\c$`, `\i\c$`}, 106 {`\\i\..\I\c$`, `\I\c$`}, 107 {`\\host\share\foo\..\bar`, `\\host\share\bar`}, 108 {`//host/share/foo/../baz`, `\\host\share\baz`}, 109 {`\\a\b\..\c`, `\\a\b\c`}, 110 {`\\a\b`, `\\a\b`}, 111 } 112 113 func TestClean(t *testing.T) { 114 testEachOS(t, []OS{Unix, Windows, Plan9}, func(t *testing.T, os OS) { 115 tests := slices.Clone(cleantests) 116 if os == Windows { 117 for i := range tests { 118 tests[i].result = FromSlash(tests[i].result, os) 119 } 120 tests = append(tests, wincleantests...) 121 } 122 for _, test := range tests { 123 if s := Clean(test.path, os); s != test.result { 124 t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result) 125 } 126 if s := Clean(test.result, os); s != test.result { 127 t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result) 128 } 129 } 130 131 if testing.Short() { 132 t.Skip("skipping malloc count in short mode") 133 } 134 if runtime.GOMAXPROCS(0) > 1 { 135 t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1") 136 return 137 } 138 139 for _, test := range tests { 140 allocs := testing.AllocsPerRun(100, func() { Clean(test.result, os) }) 141 if allocs > 0 { 142 t.Errorf("Clean(%q): %v allocs, want zero", test.result, allocs) 143 } 144 } 145 }) 146 } 147 148 func TestFromAndToSlash(t *testing.T) { 149 for _, o := range []OS{Unix, Windows, Plan9} { 150 sep := getOS(o).Separator 151 152 var slashtests = []PathTest{ 153 {"", ""}, 154 {"/", string(sep)}, 155 {"/a/b", string([]byte{sep, 'a', sep, 'b'})}, 156 {"a//b", string([]byte{'a', sep, sep, 'b'})}, 157 } 158 159 for _, test := range slashtests { 160 if s := FromSlash(test.path, o); s != test.result { 161 t.Errorf("FromSlash(%q) = %q, want %q", test.path, s, test.result) 162 } 163 if s := ToSlash(test.result, o); s != test.path { 164 t.Errorf("ToSlash(%q) = %q, want %q", test.result, s, test.path) 165 } 166 } 167 } 168 } 169 170 type SplitListTest struct { 171 list string 172 result []string 173 } 174 175 var winsplitlisttests = []SplitListTest{ 176 // quoted 177 {`"a"`, []string{`a`}}, 178 179 // semicolon 180 {`";"`, []string{`;`}}, 181 {`"a;b"`, []string{`a;b`}}, 182 {`";";`, []string{`;`, ``}}, 183 {`;";"`, []string{``, `;`}}, 184 185 // partially quoted 186 {`a";"b`, []string{`a;b`}}, 187 {`a; ""b`, []string{`a`, ` b`}}, 188 {`"a;b`, []string{`a;b`}}, 189 {`""a;b`, []string{`a`, `b`}}, 190 {`"""a;b`, []string{`a;b`}}, 191 {`""""a;b`, []string{`a`, `b`}}, 192 {`a";b`, []string{`a;b`}}, 193 {`a;b";c`, []string{`a`, `b;c`}}, 194 {`"a";b";c`, []string{`a`, `b;c`}}, 195 } 196 197 func TestSplitList(t *testing.T) { 198 testEachOS(t, []OS{Unix, Windows, Plan9}, func(t *testing.T, os OS) { 199 sep := getOS(os).ListSeparator 200 201 tests := []SplitListTest{ 202 {"", []string{}}, 203 {string([]byte{'a', sep, 'b'}), []string{"a", "b"}}, 204 {string([]byte{sep, 'a', sep, 'b'}), []string{"", "a", "b"}}, 205 } 206 if os == Windows { 207 tests = append(tests, winsplitlisttests...) 208 } 209 for _, test := range tests { 210 if l := SplitList(test.list, os); !reflect.DeepEqual(l, test.result) { 211 t.Errorf("SplitList(%#q, %q) = %#q, want %#q", test.list, os, l, test.result) 212 } 213 } 214 }) 215 } 216 217 type SplitTest struct { 218 path, dir, file string 219 } 220 221 var unixsplittests = []SplitTest{ 222 {"a/b", "a/", "b"}, 223 {"a/b/", "a/b/", ""}, 224 {"a/", "a/", ""}, 225 {"a", "", "a"}, 226 {"/", "/", ""}, 227 } 228 229 var winsplittests = []SplitTest{ 230 {`c:`, `c:`, ``}, 231 {`c:/`, `c:/`, ``}, 232 {`c:/foo`, `c:/`, `foo`}, 233 {`c:/foo/bar`, `c:/foo/`, `bar`}, 234 {`//host/share`, `//host/share`, ``}, 235 {`//host/share/`, `//host/share/`, ``}, 236 {`//host/share/foo`, `//host/share/`, `foo`}, 237 {`\\host\share`, `\\host\share`, ``}, 238 {`\\host\share\`, `\\host\share\`, ``}, 239 {`\\host\share\foo`, `\\host\share\`, `foo`}, 240 } 241 242 func TestSplit(t *testing.T) { 243 testEachOS(t, []OS{Unix, Windows}, func(t *testing.T, os OS) { 244 tests := unixsplittests 245 if os == Windows { 246 tests = append(tests, winsplittests...) 247 } 248 for _, test := range tests { 249 pair := Split(test.path, os) 250 d, f := pair[0], pair[1] 251 if d != test.dir || f != test.file { 252 t.Errorf("Split(%q, %q) = %q, %q, want %q, %q", 253 test.path, os, d, f, test.dir, test.file) 254 } 255 } 256 }) 257 } 258 259 type JoinTest struct { 260 elem []string 261 path string 262 } 263 264 var jointests = []JoinTest{ 265 // zero parameters 266 {[]string{}, ""}, 267 268 // one parameter 269 {[]string{""}, ""}, 270 {[]string{"/"}, "/"}, 271 {[]string{"a"}, "a"}, 272 273 // two parameters 274 {[]string{"a", "b"}, "a/b"}, 275 {[]string{"a", ""}, "a"}, 276 {[]string{"", "b"}, "b"}, 277 {[]string{"/", "a"}, "/a"}, 278 {[]string{"/", "a/b"}, "/a/b"}, 279 {[]string{"/", ""}, "/"}, 280 {[]string{"//", "a"}, "/a"}, 281 {[]string{"/a", "b"}, "/a/b"}, 282 {[]string{"a/", "b"}, "a/b"}, 283 {[]string{"a/", ""}, "a"}, 284 {[]string{"", ""}, ""}, 285 286 // three parameters 287 {[]string{"/", "a", "b"}, "/a/b"}, 288 } 289 290 var winjointests = []JoinTest{ 291 {[]string{`directory`, `file`}, `directory\file`}, 292 {[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`}, 293 {[]string{`C:\Windows\`, ``}, `C:\Windows`}, 294 {[]string{`C:\`, `Windows`}, `C:\Windows`}, 295 {[]string{`C:`, `a`}, `C:a`}, 296 {[]string{`C:`, `a\b`}, `C:a\b`}, 297 {[]string{`C:`, `a`, `b`}, `C:a\b`}, 298 {[]string{`C:`, ``, `b`}, `C:b`}, 299 {[]string{`C:`, ``, ``, `b`}, `C:b`}, 300 {[]string{`C:`, ``}, `C:.`}, 301 {[]string{`C:`, ``, ``}, `C:.`}, 302 {[]string{`C:.`, `a`}, `C:a`}, 303 {[]string{`C:a`, `b`}, `C:a\b`}, 304 {[]string{`C:a`, `b`, `d`}, `C:a\b\d`}, 305 {[]string{`\\host\share`, `foo`}, `\\host\share\foo`}, 306 {[]string{`\\host\share\foo`}, `\\host\share\foo`}, 307 {[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`}, 308 {[]string{`\`}, `\`}, 309 {[]string{`\`, ``}, `\`}, 310 {[]string{`\`, `a`}, `\a`}, 311 {[]string{`\\`, `a`}, `\a`}, 312 {[]string{`\`, `a`, `b`}, `\a\b`}, 313 {[]string{`\\`, `a`, `b`}, `\a\b`}, 314 {[]string{`\`, `\\a\b`, `c`}, `\a\b\c`}, 315 {[]string{`\\a`, `b`, `c`}, `\a\b\c`}, 316 {[]string{`\\a\`, `b`, `c`}, `\a\b\c`}, 317 } 318 319 func TestJoin(t *testing.T) { 320 testEachOS(t, []OS{Unix, Windows}, func(t *testing.T, os OS) { 321 tests := jointests 322 if os == Windows { 323 tests = append(tests, winjointests...) 324 } 325 for _, test := range tests { 326 expected := FromSlash(test.path, os) 327 if p := Join(test.elem, os); p != expected { 328 t.Errorf("join(%q, %q) = %q, want %q", test.elem, os, p, expected) 329 } 330 } 331 }) 332 } 333 334 type ExtTest struct { 335 path, ext string 336 } 337 338 var exttests = []ExtTest{ 339 {"path.go", ".go"}, 340 {"path.pb.go", ".go"}, 341 {"a.dir/b", ""}, 342 {"a.dir/b.go", ".go"}, 343 {"a.dir/", ""}, 344 } 345 346 func TestExt(t *testing.T) { 347 testEachOS(t, []OS{Unix, Windows}, func(t *testing.T, os OS) { 348 for _, test := range exttests { 349 if x := Ext(test.path, os); x != test.ext { 350 t.Errorf("Ext(%q, %q) = %q, want %q", test.path, os, x, test.ext) 351 } 352 } 353 }) 354 } 355 356 var basetests = []PathTest{ 357 {"", "."}, 358 {".", "."}, 359 {"/.", "."}, 360 {"/", "/"}, 361 {"////", "/"}, 362 {"x/", "x"}, 363 {"abc", "abc"}, 364 {"abc/def", "def"}, 365 {"a/b/.x", ".x"}, 366 {"a/b/c.", "c."}, 367 {"a/b/c.x", "c.x"}, 368 } 369 370 var winbasetests = []PathTest{ 371 {`c:\`, `\`}, 372 {`c:.`, `.`}, 373 {`c:\a\b`, `b`}, 374 {`c:a\b`, `b`}, 375 {`c:a\b\c`, `c`}, 376 {`\\host\share\`, `\`}, 377 {`\\host\share\a`, `a`}, 378 {`\\host\share\a\b`, `b`}, 379 } 380 381 func TestBase(t *testing.T) { 382 tests := basetests 383 testEachOS(t, []OS{Unix, Windows}, func(t *testing.T, os OS) { 384 if os == Windows { 385 // make unix tests work on windows 386 for i := range tests { 387 tests[i].result = Clean(tests[i].result, os) 388 } 389 // add windows specific tests 390 tests = append(tests, winbasetests...) 391 } 392 for _, test := range tests { 393 if s := Base(test.path, os); s != test.result { 394 t.Errorf("Base(%q, %q) = %q, want %q", test.path, os, s, test.result) 395 } 396 } 397 }) 398 } 399 400 var dirtests = []PathTest{ 401 {"", "."}, 402 {".", "."}, 403 {"/.", "/"}, 404 {"/", "/"}, 405 {"////", "/"}, 406 {"/foo", "/"}, 407 {"x/", "x"}, 408 {"abc", "."}, 409 {"abc/def", "abc"}, 410 {"a/b/.x", "a/b"}, 411 {"a/b/c.", "a/b"}, 412 {"a/b/c.x", "a/b"}, 413 } 414 415 var windirtests = []PathTest{ 416 {`c:\`, `c:\`}, 417 {`c:.`, `c:.`}, 418 {`c:\a\b`, `c:\a`}, 419 {`c:a\b`, `c:a`}, 420 {`c:a\b\c`, `c:a\b`}, 421 {`\\host\share`, `\\host\share`}, 422 {`\\host\share\`, `\\host\share\`}, 423 {`\\host\share\a`, `\\host\share\`}, 424 {`\\host\share\a\b`, `\\host\share\a`}, 425 } 426 427 func TestDir(t *testing.T) { 428 testEachOS(t, []OS{Unix, Windows}, func(t *testing.T, os OS) { 429 tests := dirtests 430 if os == Windows { 431 // make unix tests work on windows 432 for i := range tests { 433 tests[i].result = Clean(tests[i].result, os) 434 } 435 // add windows specific tests 436 tests = append(tests, windirtests...) 437 } 438 for _, test := range tests { 439 if s := Dir(test.path, os); s != test.result { 440 t.Errorf("Dir(%q, %q) = %q, want %q", test.path, os, s, test.result) 441 } 442 } 443 }) 444 } 445 446 type IsAbsTest struct { 447 path string 448 isAbs bool 449 } 450 451 var isabstests = []IsAbsTest{ 452 {"", false}, 453 {"/", true}, 454 {"/usr/bin/gcc", true}, 455 {"..", false}, 456 {"/a/../bb", true}, 457 {".", false}, 458 {"./", false}, 459 {"lala", false}, 460 } 461 462 var winisabstests = []IsAbsTest{ 463 {`C:\`, true}, 464 {`c\`, false}, 465 {`c::`, false}, 466 {`c:`, false}, 467 {`/`, false}, 468 {`\`, false}, 469 {`\Windows`, false}, 470 {`c:a\b`, false}, 471 {`c:\a\b`, true}, 472 {`c:/a/b`, true}, 473 {`\\host\share\foo`, true}, 474 {`//host/share/foo/bar`, true}, 475 } 476 477 func TestIsAbs(t *testing.T) { 478 testEachOS(t, []OS{Unix, Windows}, func(t *testing.T, os OS) { 479 var tests []IsAbsTest 480 if os == Windows { 481 tests = append(tests, winisabstests...) 482 // All non-windows tests should fail, because they have no volume letter. 483 for _, test := range isabstests { 484 tests = append(tests, IsAbsTest{test.path, false}) 485 } 486 // All non-windows test should work as intended if prefixed with volume letter. 487 for _, test := range isabstests { 488 tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs}) 489 } 490 // Test reserved names. 491 // tests = append(tests, IsAbsTest{"/dev/null", true}) 492 tests = append(tests, IsAbsTest{"NUL", true}) 493 tests = append(tests, IsAbsTest{"nul", true}) 494 tests = append(tests, IsAbsTest{"CON", true}) 495 } else { 496 tests = isabstests 497 } 498 499 for _, test := range tests { 500 if r := IsAbs(test.path, os); r != test.isAbs { 501 t.Errorf("IsAbs(%q, %q) = %v, want %v", test.path, os, r, test.isAbs) 502 } 503 } 504 }) 505 } 506 507 type RelTests struct { 508 root, path, want string 509 } 510 511 var reltests = []RelTests{ 512 {"a/b", "a/b", "."}, 513 {"a/b/.", "a/b", "."}, 514 {"a/b", "a/b/.", "."}, 515 {"./a/b", "a/b", "."}, 516 {"a/b", "./a/b", "."}, 517 {"ab/cd", "ab/cde", "../cde"}, 518 {"ab/cd", "ab/c", "../c"}, 519 {"a/b", "a/b/c/d", "c/d"}, 520 {"a/b", "a/b/../c", "../c"}, 521 {"a/b/../c", "a/b", "../b"}, 522 {"a/b/c", "a/c/d", "../../c/d"}, 523 {"a/b", "c/d", "../../c/d"}, 524 {"a/b/c/d", "a/b", "../.."}, 525 {"a/b/c/d", "a/b/", "../.."}, 526 {"a/b/c/d/", "a/b", "../.."}, 527 {"a/b/c/d/", "a/b/", "../.."}, 528 {"../../a/b", "../../a/b/c/d", "c/d"}, 529 {"/a/b", "/a/b", "."}, 530 {"/a/b/.", "/a/b", "."}, 531 {"/a/b", "/a/b/.", "."}, 532 {"/ab/cd", "/ab/cde", "../cde"}, 533 {"/ab/cd", "/ab/c", "../c"}, 534 {"/a/b", "/a/b/c/d", "c/d"}, 535 {"/a/b", "/a/b/../c", "../c"}, 536 {"/a/b/../c", "/a/b", "../b"}, 537 {"/a/b/c", "/a/c/d", "../../c/d"}, 538 {"/a/b", "/c/d", "../../c/d"}, 539 {"/a/b/c/d", "/a/b", "../.."}, 540 {"/a/b/c/d", "/a/b/", "../.."}, 541 {"/a/b/c/d/", "/a/b", "../.."}, 542 {"/a/b/c/d/", "/a/b/", "../.."}, 543 {"/../../a/b", "/../../a/b/c/d", "c/d"}, 544 {".", "a/b", "a/b"}, 545 {".", "..", ".."}, 546 547 // can't do purely lexically 548 {"..", ".", "err"}, 549 {"..", "a", "err"}, 550 {"../..", "..", "err"}, 551 {"a", "/a", "err"}, 552 {"/a", "a", "err"}, 553 } 554 555 var winreltests = []RelTests{ 556 {`C:a\b\c`, `C:a/b/d`, `..\d`}, 557 {`C:\`, `D:\`, `err`}, 558 {`C:`, `D:`, `err`}, 559 {`C:\Projects`, `c:\projects\src`, `src`}, 560 {`C:\Projects`, `c:\projects`, `.`}, 561 {`C:\Projects\a\..`, `c:\projects`, `.`}, 562 } 563 564 func TestRel(t *testing.T) { 565 testEachOS(t, []OS{Unix, Windows}, func(t *testing.T, os OS) { 566 tests := slices.Clone(reltests) 567 if os == Windows { 568 for i := range tests { 569 tests[i].want = FromSlash(tests[i].want, Windows) 570 } 571 tests = append(tests, winreltests...) 572 } 573 for _, test := range tests { 574 got, err := Rel(test.root, test.path, os) 575 if test.want == "err" { 576 if err == nil { 577 t.Errorf("Rel(%q, %q, %q)=%q, want error", test.root, test.path, os, got) 578 } 579 continue 580 } 581 if err != nil { 582 t.Errorf("Rel(%q, %q, %q): want %q, got error: %s", test.root, test.path, os, test.want, err) 583 } 584 if got != test.want { 585 t.Errorf("Rel(%q, %q, %q)=%q, want %q", test.root, test.path, os, got, test.want) 586 } 587 } 588 }) 589 } 590 591 type VolumeNameTest struct { 592 path string 593 vol string 594 } 595 596 var volumenametests = []VolumeNameTest{ 597 {`c:/foo/bar`, `c:`}, 598 {`c:`, `c:`}, 599 {`2:`, ``}, 600 {``, ``}, 601 {`\\\host`, ``}, 602 {`\\\host\`, ``}, 603 {`\\\host\share`, ``}, 604 {`\\\host\\share`, ``}, 605 {`\\host`, ``}, 606 {`//host`, ``}, 607 {`\\host\`, ``}, 608 {`//host/`, ``}, 609 {`\\host\share`, `\\host\share`}, 610 {`//host/share`, `//host/share`}, 611 {`\\host\share\`, `\\host\share`}, 612 {`//host/share/`, `//host/share`}, 613 {`\\host\share\foo`, `\\host\share`}, 614 {`//host/share/foo`, `//host/share`}, 615 {`\\host\share\\foo\\\bar\\\\baz`, `\\host\share`}, 616 {`//host/share//foo///bar////baz`, `//host/share`}, 617 {`\\host\share\foo\..\bar`, `\\host\share`}, 618 {`//host/share/foo/../bar`, `//host/share`}, 619 } 620 621 func TestVolumeName(t *testing.T) { 622 os := Windows 623 for _, v := range volumenametests { 624 if vol := VolumeName(v.path, os); vol != v.vol { 625 t.Errorf("VolumeName(%q, %q)=%q, want %q", v.path, os, vol, v.vol) 626 } 627 } 628 }