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