Home | History | Annotate | Download | only in filepath
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package filepath_test
      6 
      7 import (
      8 	"errors"
      9 	"io/ioutil"
     10 	"os"
     11 	"path/filepath"
     12 	"reflect"
     13 	"runtime"
     14 	"strings"
     15 	"testing"
     16 )
     17 
     18 var supportsSymlinks = true
     19 
     20 type PathTest struct {
     21 	path, result string
     22 }
     23 
     24 var cleantests = []PathTest{
     25 	// Already clean
     26 	{"abc", "abc"},
     27 	{"abc/def", "abc/def"},
     28 	{"a/b/c", "a/b/c"},
     29 	{".", "."},
     30 	{"..", ".."},
     31 	{"../..", "../.."},
     32 	{"../../abc", "../../abc"},
     33 	{"/abc", "/abc"},
     34 	{"/", "/"},
     35 
     36 	// Empty is current dir
     37 	{"", "."},
     38 
     39 	// Remove trailing slash
     40 	{"abc/", "abc"},
     41 	{"abc/def/", "abc/def"},
     42 	{"a/b/c/", "a/b/c"},
     43 	{"./", "."},
     44 	{"../", ".."},
     45 	{"../../", "../.."},
     46 	{"/abc/", "/abc"},
     47 
     48 	// Remove doubled slash
     49 	{"abc//def//ghi", "abc/def/ghi"},
     50 	{"//abc", "/abc"},
     51 	{"///abc", "/abc"},
     52 	{"//abc//", "/abc"},
     53 	{"abc//", "abc"},
     54 
     55 	// Remove . elements
     56 	{"abc/./def", "abc/def"},
     57 	{"/./abc/def", "/abc/def"},
     58 	{"abc/.", "abc"},
     59 
     60 	// Remove .. elements
     61 	{"abc/def/ghi/../jkl", "abc/def/jkl"},
     62 	{"abc/def/../ghi/../jkl", "abc/jkl"},
     63 	{"abc/def/..", "abc"},
     64 	{"abc/def/../..", "."},
     65 	{"/abc/def/../..", "/"},
     66 	{"abc/def/../../..", ".."},
     67 	{"/abc/def/../../..", "/"},
     68 	{"abc/def/../../../ghi/jkl/../../../mno", "../../mno"},
     69 	{"/../abc", "/abc"},
     70 
     71 	// Combinations
     72 	{"abc/./../def", "def"},
     73 	{"abc//./../def", "def"},
     74 	{"abc/../../././../def", "../../def"},
     75 }
     76 
     77 var wincleantests = []PathTest{
     78 	{`c:`, `c:.`},
     79 	{`c:\`, `c:\`},
     80 	{`c:\abc`, `c:\abc`},
     81 	{`c:abc\..\..\.\.\..\def`, `c:..\..\def`},
     82 	{`c:\abc\def\..\..`, `c:\`},
     83 	{`c:\..\abc`, `c:\abc`},
     84 	{`c:..\abc`, `c:..\abc`},
     85 	{`\`, `\`},
     86 	{`/`, `\`},
     87 	{`\\i\..\c$`, `\c$`},
     88 	{`\\i\..\i\c$`, `\i\c$`},
     89 	{`\\i\..\I\c$`, `\I\c$`},
     90 	{`\\host\share\foo\..\bar`, `\\host\share\bar`},
     91 	{`//host/share/foo/../baz`, `\\host\share\baz`},
     92 	{`\\a\b\..\c`, `\\a\b\c`},
     93 	{`\\a\b`, `\\a\b`},
     94 }
     95 
     96 func TestClean(t *testing.T) {
     97 	tests := cleantests
     98 	if runtime.GOOS == "windows" {
     99 		for i := range tests {
    100 			tests[i].result = filepath.FromSlash(tests[i].result)
    101 		}
    102 		tests = append(tests, wincleantests...)
    103 	}
    104 	for _, test := range tests {
    105 		if s := filepath.Clean(test.path); s != test.result {
    106 			t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
    107 		}
    108 		if s := filepath.Clean(test.result); s != test.result {
    109 			t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result)
    110 		}
    111 	}
    112 
    113 	if testing.Short() {
    114 		t.Skip("skipping malloc count in short mode")
    115 	}
    116 	if runtime.GOMAXPROCS(0) > 1 {
    117 		t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1")
    118 		return
    119 	}
    120 
    121 	for _, test := range tests {
    122 		allocs := testing.AllocsPerRun(100, func() { filepath.Clean(test.result) })
    123 		if allocs > 0 {
    124 			t.Errorf("Clean(%q): %v allocs, want zero", test.result, allocs)
    125 		}
    126 	}
    127 }
    128 
    129 const sep = filepath.Separator
    130 
    131 var slashtests = []PathTest{
    132 	{"", ""},
    133 	{"/", string(sep)},
    134 	{"/a/b", string([]byte{sep, 'a', sep, 'b'})},
    135 	{"a//b", string([]byte{'a', sep, sep, 'b'})},
    136 }
    137 
    138 func TestFromAndToSlash(t *testing.T) {
    139 	for _, test := range slashtests {
    140 		if s := filepath.FromSlash(test.path); s != test.result {
    141 			t.Errorf("FromSlash(%q) = %q, want %q", test.path, s, test.result)
    142 		}
    143 		if s := filepath.ToSlash(test.result); s != test.path {
    144 			t.Errorf("ToSlash(%q) = %q, want %q", test.result, s, test.path)
    145 		}
    146 	}
    147 }
    148 
    149 type SplitListTest struct {
    150 	list   string
    151 	result []string
    152 }
    153 
    154 const lsep = filepath.ListSeparator
    155 
    156 var splitlisttests = []SplitListTest{
    157 	{"", []string{}},
    158 	{string([]byte{'a', lsep, 'b'}), []string{"a", "b"}},
    159 	{string([]byte{lsep, 'a', lsep, 'b'}), []string{"", "a", "b"}},
    160 }
    161 
    162 var winsplitlisttests = []SplitListTest{
    163 	// quoted
    164 	{`"a"`, []string{`a`}},
    165 
    166 	// semicolon
    167 	{`";"`, []string{`;`}},
    168 	{`"a;b"`, []string{`a;b`}},
    169 	{`";";`, []string{`;`, ``}},
    170 	{`;";"`, []string{``, `;`}},
    171 
    172 	// partially quoted
    173 	{`a";"b`, []string{`a;b`}},
    174 	{`a; ""b`, []string{`a`, ` b`}},
    175 	{`"a;b`, []string{`a;b`}},
    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";c`, []string{`a`, `b;c`}},
    181 	{`"a";b";c`, []string{`a`, `b;c`}},
    182 }
    183 
    184 func TestSplitList(t *testing.T) {
    185 	tests := splitlisttests
    186 	if runtime.GOOS == "windows" {
    187 		tests = append(tests, winsplitlisttests...)
    188 	}
    189 	for _, test := range tests {
    190 		if l := filepath.SplitList(test.list); !reflect.DeepEqual(l, test.result) {
    191 			t.Errorf("SplitList(%#q) = %#q, want %#q", test.list, l, test.result)
    192 		}
    193 	}
    194 }
    195 
    196 type SplitTest struct {
    197 	path, dir, file string
    198 }
    199 
    200 var unixsplittests = []SplitTest{
    201 	{"a/b", "a/", "b"},
    202 	{"a/b/", "a/b/", ""},
    203 	{"a/", "a/", ""},
    204 	{"a", "", "a"},
    205 	{"/", "/", ""},
    206 }
    207 
    208 var winsplittests = []SplitTest{
    209 	{`c:`, `c:`, ``},
    210 	{`c:/`, `c:/`, ``},
    211 	{`c:/foo`, `c:/`, `foo`},
    212 	{`c:/foo/bar`, `c:/foo/`, `bar`},
    213 	{`//host/share`, `//host/share`, ``},
    214 	{`//host/share/`, `//host/share/`, ``},
    215 	{`//host/share/foo`, `//host/share/`, `foo`},
    216 	{`\\host\share`, `\\host\share`, ``},
    217 	{`\\host\share\`, `\\host\share\`, ``},
    218 	{`\\host\share\foo`, `\\host\share\`, `foo`},
    219 }
    220 
    221 func TestSplit(t *testing.T) {
    222 	var splittests []SplitTest
    223 	splittests = unixsplittests
    224 	if runtime.GOOS == "windows" {
    225 		splittests = append(splittests, winsplittests...)
    226 	}
    227 	for _, test := range splittests {
    228 		if d, f := filepath.Split(test.path); d != test.dir || f != test.file {
    229 			t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file)
    230 		}
    231 	}
    232 }
    233 
    234 type JoinTest struct {
    235 	elem []string
    236 	path string
    237 }
    238 
    239 var jointests = []JoinTest{
    240 	// zero parameters
    241 	{[]string{}, ""},
    242 
    243 	// one parameter
    244 	{[]string{""}, ""},
    245 	{[]string{"/"}, "/"},
    246 	{[]string{"a"}, "a"},
    247 
    248 	// two parameters
    249 	{[]string{"a", "b"}, "a/b"},
    250 	{[]string{"a", ""}, "a"},
    251 	{[]string{"", "b"}, "b"},
    252 	{[]string{"/", "a"}, "/a"},
    253 	{[]string{"/", "a/b"}, "/a/b"},
    254 	{[]string{"/", ""}, "/"},
    255 	{[]string{"//", "a"}, "/a"},
    256 	{[]string{"/a", "b"}, "/a/b"},
    257 	{[]string{"a/", "b"}, "a/b"},
    258 	{[]string{"a/", ""}, "a"},
    259 	{[]string{"", ""}, ""},
    260 
    261 	// three parameters
    262 	{[]string{"/", "a", "b"}, "/a/b"},
    263 }
    264 
    265 var winjointests = []JoinTest{
    266 	{[]string{`directory`, `file`}, `directory\file`},
    267 	{[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`},
    268 	{[]string{`C:\Windows\`, ``}, `C:\Windows`},
    269 	{[]string{`C:\`, `Windows`}, `C:\Windows`},
    270 	{[]string{`C:`, `Windows`}, `C:\Windows`},
    271 	{[]string{`\\host\share`, `foo`}, `\\host\share\foo`},
    272 	{[]string{`\\host\share\foo`}, `\\host\share\foo`},
    273 	{[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`},
    274 	{[]string{`\`}, `\`},
    275 	{[]string{`\`, ``}, `\`},
    276 	{[]string{`\`, `a`}, `\a`},
    277 	{[]string{`\\`, `a`}, `\a`},
    278 	{[]string{`\`, `a`, `b`}, `\a\b`},
    279 	{[]string{`\\`, `a`, `b`}, `\a\b`},
    280 	{[]string{`\`, `\\a\b`, `c`}, `\a\b\c`},
    281 	{[]string{`\\a`, `b`, `c`}, `\a\b\c`},
    282 	{[]string{`\\a\`, `b`, `c`}, `\a\b\c`},
    283 }
    284 
    285 func TestJoin(t *testing.T) {
    286 	if runtime.GOOS == "windows" {
    287 		jointests = append(jointests, winjointests...)
    288 	}
    289 	for _, test := range jointests {
    290 		expected := filepath.FromSlash(test.path)
    291 		if p := filepath.Join(test.elem...); p != expected {
    292 			t.Errorf("join(%q) = %q, want %q", test.elem, p, expected)
    293 		}
    294 	}
    295 }
    296 
    297 type ExtTest struct {
    298 	path, ext string
    299 }
    300 
    301 var exttests = []ExtTest{
    302 	{"path.go", ".go"},
    303 	{"path.pb.go", ".go"},
    304 	{"a.dir/b", ""},
    305 	{"a.dir/b.go", ".go"},
    306 	{"a.dir/", ""},
    307 }
    308 
    309 func TestExt(t *testing.T) {
    310 	for _, test := range exttests {
    311 		if x := filepath.Ext(test.path); x != test.ext {
    312 			t.Errorf("Ext(%q) = %q, want %q", test.path, x, test.ext)
    313 		}
    314 	}
    315 }
    316 
    317 type Node struct {
    318 	name    string
    319 	entries []*Node // nil if the entry is a file
    320 	mark    int
    321 }
    322 
    323 var tree = &Node{
    324 	"testdata",
    325 	[]*Node{
    326 		{"a", nil, 0},
    327 		{"b", []*Node{}, 0},
    328 		{"c", nil, 0},
    329 		{
    330 			"d",
    331 			[]*Node{
    332 				{"x", nil, 0},
    333 				{"y", []*Node{}, 0},
    334 				{
    335 					"z",
    336 					[]*Node{
    337 						{"u", nil, 0},
    338 						{"v", nil, 0},
    339 					},
    340 					0,
    341 				},
    342 			},
    343 			0,
    344 		},
    345 	},
    346 	0,
    347 }
    348 
    349 func walkTree(n *Node, path string, f func(path string, n *Node)) {
    350 	f(path, n)
    351 	for _, e := range n.entries {
    352 		walkTree(e, filepath.Join(path, e.name), f)
    353 	}
    354 }
    355 
    356 func makeTree(t *testing.T) {
    357 	walkTree(tree, tree.name, func(path string, n *Node) {
    358 		if n.entries == nil {
    359 			fd, err := os.Create(path)
    360 			if err != nil {
    361 				t.Errorf("makeTree: %v", err)
    362 				return
    363 			}
    364 			fd.Close()
    365 		} else {
    366 			os.Mkdir(path, 0770)
    367 		}
    368 	})
    369 }
    370 
    371 func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) }
    372 
    373 func checkMarks(t *testing.T, report bool) {
    374 	walkTree(tree, tree.name, func(path string, n *Node) {
    375 		if n.mark != 1 && report {
    376 			t.Errorf("node %s mark = %d; expected 1", path, n.mark)
    377 		}
    378 		n.mark = 0
    379 	})
    380 }
    381 
    382 // Assumes that each node name is unique. Good enough for a test.
    383 // If clear is true, any incoming error is cleared before return. The errors
    384 // are always accumulated, though.
    385 func mark(path string, info os.FileInfo, err error, errors *[]error, clear bool) error {
    386 	if err != nil {
    387 		*errors = append(*errors, err)
    388 		if clear {
    389 			return nil
    390 		}
    391 		return err
    392 	}
    393 	name := info.Name()
    394 	walkTree(tree, tree.name, func(path string, n *Node) {
    395 		if n.name == name {
    396 			n.mark++
    397 		}
    398 	})
    399 	return nil
    400 }
    401 
    402 func chtmpdir(t *testing.T) (restore func()) {
    403 	oldwd, err := os.Getwd()
    404 	if err != nil {
    405 		t.Fatal("chtmpdir: %v", err)
    406 	}
    407 	d, err := ioutil.TempDir("", "test")
    408 	if err != nil {
    409 		t.Fatal("chtmpdir: %v", err)
    410 	}
    411 	if err := os.Chdir(d); err != nil {
    412 		t.Fatal("chtmpdir: %v", err)
    413 	}
    414 	return func() {
    415 		if err := os.Chdir(oldwd); err != nil {
    416 			t.Fatal("chtmpdir: %v", err)
    417 		}
    418 		os.RemoveAll(d)
    419 	}
    420 }
    421 
    422 func TestWalk(t *testing.T) {
    423 	if runtime.GOOS == "darwin" {
    424 		switch runtime.GOARCH {
    425 		case "arm", "arm64":
    426 			restore := chtmpdir(t)
    427 			defer restore()
    428 		}
    429 	}
    430 	makeTree(t)
    431 	errors := make([]error, 0, 10)
    432 	clear := true
    433 	markFn := func(path string, info os.FileInfo, err error) error {
    434 		return mark(path, info, err, &errors, clear)
    435 	}
    436 	// Expect no errors.
    437 	err := filepath.Walk(tree.name, markFn)
    438 	if err != nil {
    439 		t.Fatalf("no error expected, found: %s", err)
    440 	}
    441 	if len(errors) != 0 {
    442 		t.Fatalf("unexpected errors: %s", errors)
    443 	}
    444 	checkMarks(t, true)
    445 	errors = errors[0:0]
    446 
    447 	// Test permission errors.  Only possible if we're not root
    448 	// and only on some file systems (AFS, FAT).  To avoid errors during
    449 	// all.bash on those file systems, skip during go test -short.
    450 	if os.Getuid() > 0 && !testing.Short() {
    451 		// introduce 2 errors: chmod top-level directories to 0
    452 		os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0)
    453 		os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0)
    454 
    455 		// 3) capture errors, expect two.
    456 		// mark respective subtrees manually
    457 		markTree(tree.entries[1])
    458 		markTree(tree.entries[3])
    459 		// correct double-marking of directory itself
    460 		tree.entries[1].mark--
    461 		tree.entries[3].mark--
    462 		err := filepath.Walk(tree.name, markFn)
    463 		if err != nil {
    464 			t.Fatalf("expected no error return from Walk, got %s", err)
    465 		}
    466 		if len(errors) != 2 {
    467 			t.Errorf("expected 2 errors, got %d: %s", len(errors), errors)
    468 		}
    469 		// the inaccessible subtrees were marked manually
    470 		checkMarks(t, true)
    471 		errors = errors[0:0]
    472 
    473 		// 4) capture errors, stop after first error.
    474 		// mark respective subtrees manually
    475 		markTree(tree.entries[1])
    476 		markTree(tree.entries[3])
    477 		// correct double-marking of directory itself
    478 		tree.entries[1].mark--
    479 		tree.entries[3].mark--
    480 		clear = false // error will stop processing
    481 		err = filepath.Walk(tree.name, markFn)
    482 		if err == nil {
    483 			t.Fatalf("expected error return from Walk")
    484 		}
    485 		if len(errors) != 1 {
    486 			t.Errorf("expected 1 error, got %d: %s", len(errors), errors)
    487 		}
    488 		// the inaccessible subtrees were marked manually
    489 		checkMarks(t, false)
    490 		errors = errors[0:0]
    491 
    492 		// restore permissions
    493 		os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770)
    494 		os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770)
    495 	}
    496 
    497 	// cleanup
    498 	if err := os.RemoveAll(tree.name); err != nil {
    499 		t.Errorf("removeTree: %v", err)
    500 	}
    501 }
    502 
    503 func touch(t *testing.T, name string) {
    504 	f, err := os.Create(name)
    505 	if err != nil {
    506 		t.Fatal(err)
    507 	}
    508 	if err := f.Close(); err != nil {
    509 		t.Fatal(err)
    510 	}
    511 }
    512 
    513 func TestWalkSkipDirOnFile(t *testing.T) {
    514 	td, err := ioutil.TempDir("", "walktest")
    515 	if err != nil {
    516 		t.Fatal(err)
    517 	}
    518 	defer os.RemoveAll(td)
    519 
    520 	if err := os.MkdirAll(filepath.Join(td, "dir"), 0755); err != nil {
    521 		t.Fatal(err)
    522 	}
    523 	touch(t, filepath.Join(td, "dir/foo1"))
    524 	touch(t, filepath.Join(td, "dir/foo2"))
    525 
    526 	sawFoo2 := false
    527 	filepath.Walk(td, func(path string, info os.FileInfo, err error) error {
    528 		if strings.HasSuffix(path, "foo2") {
    529 			sawFoo2 = true
    530 		}
    531 		if strings.HasSuffix(path, "foo1") {
    532 			return filepath.SkipDir
    533 		}
    534 		return nil
    535 	})
    536 
    537 	if sawFoo2 {
    538 		t.Errorf("SkipDir on file foo1 did not block processing of foo2")
    539 	}
    540 }
    541 
    542 func TestWalkFileError(t *testing.T) {
    543 	td, err := ioutil.TempDir("", "walktest")
    544 	if err != nil {
    545 		t.Fatal(err)
    546 	}
    547 	defer os.RemoveAll(td)
    548 
    549 	touch(t, filepath.Join(td, "foo"))
    550 	touch(t, filepath.Join(td, "bar"))
    551 	dir := filepath.Join(td, "dir")
    552 	if err := os.MkdirAll(filepath.Join(td, "dir"), 0755); err != nil {
    553 		t.Fatal(err)
    554 	}
    555 	touch(t, filepath.Join(dir, "baz"))
    556 	touch(t, filepath.Join(dir, "stat-error"))
    557 	defer func() {
    558 		*filepath.LstatP = os.Lstat
    559 	}()
    560 	statErr := errors.New("some stat error")
    561 	*filepath.LstatP = func(path string) (os.FileInfo, error) {
    562 		if strings.HasSuffix(path, "stat-error") {
    563 			return nil, statErr
    564 		}
    565 		return os.Lstat(path)
    566 	}
    567 	got := map[string]error{}
    568 	err = filepath.Walk(td, func(path string, fi os.FileInfo, err error) error {
    569 		rel, _ := filepath.Rel(td, path)
    570 		got[filepath.ToSlash(rel)] = err
    571 		return nil
    572 	})
    573 	if err != nil {
    574 		t.Errorf("Walk error: %v", err)
    575 	}
    576 	want := map[string]error{
    577 		".":              nil,
    578 		"foo":            nil,
    579 		"bar":            nil,
    580 		"dir":            nil,
    581 		"dir/baz":        nil,
    582 		"dir/stat-error": statErr,
    583 	}
    584 	if !reflect.DeepEqual(got, want) {
    585 		t.Errorf("Walked %#v; want %#v", got, want)
    586 	}
    587 }
    588 
    589 var basetests = []PathTest{
    590 	{"", "."},
    591 	{".", "."},
    592 	{"/.", "."},
    593 	{"/", "/"},
    594 	{"////", "/"},
    595 	{"x/", "x"},
    596 	{"abc", "abc"},
    597 	{"abc/def", "def"},
    598 	{"a/b/.x", ".x"},
    599 	{"a/b/c.", "c."},
    600 	{"a/b/c.x", "c.x"},
    601 }
    602 
    603 var winbasetests = []PathTest{
    604 	{`c:\`, `\`},
    605 	{`c:.`, `.`},
    606 	{`c:\a\b`, `b`},
    607 	{`c:a\b`, `b`},
    608 	{`c:a\b\c`, `c`},
    609 	{`\\host\share\`, `\`},
    610 	{`\\host\share\a`, `a`},
    611 	{`\\host\share\a\b`, `b`},
    612 }
    613 
    614 func TestBase(t *testing.T) {
    615 	tests := basetests
    616 	if runtime.GOOS == "windows" {
    617 		// make unix tests work on windows
    618 		for i := range tests {
    619 			tests[i].result = filepath.Clean(tests[i].result)
    620 		}
    621 		// add windows specific tests
    622 		tests = append(tests, winbasetests...)
    623 	}
    624 	for _, test := range tests {
    625 		if s := filepath.Base(test.path); s != test.result {
    626 			t.Errorf("Base(%q) = %q, want %q", test.path, s, test.result)
    627 		}
    628 	}
    629 }
    630 
    631 var dirtests = []PathTest{
    632 	{"", "."},
    633 	{".", "."},
    634 	{"/.", "/"},
    635 	{"/", "/"},
    636 	{"////", "/"},
    637 	{"/foo", "/"},
    638 	{"x/", "x"},
    639 	{"abc", "."},
    640 	{"abc/def", "abc"},
    641 	{"a/b/.x", "a/b"},
    642 	{"a/b/c.", "a/b"},
    643 	{"a/b/c.x", "a/b"},
    644 }
    645 
    646 var windirtests = []PathTest{
    647 	{`c:\`, `c:\`},
    648 	{`c:.`, `c:.`},
    649 	{`c:\a\b`, `c:\a`},
    650 	{`c:a\b`, `c:a`},
    651 	{`c:a\b\c`, `c:a\b`},
    652 	{`\\host\share\`, `\\host\share\`},
    653 	{`\\host\share\a`, `\\host\share\`},
    654 	{`\\host\share\a\b`, `\\host\share\a`},
    655 }
    656 
    657 func TestDir(t *testing.T) {
    658 	tests := dirtests
    659 	if runtime.GOOS == "windows" {
    660 		// make unix tests work on windows
    661 		for i := range tests {
    662 			tests[i].result = filepath.Clean(tests[i].result)
    663 		}
    664 		// add windows specific tests
    665 		tests = append(tests, windirtests...)
    666 	}
    667 	for _, test := range tests {
    668 		if s := filepath.Dir(test.path); s != test.result {
    669 			t.Errorf("Dir(%q) = %q, want %q", test.path, s, test.result)
    670 		}
    671 	}
    672 }
    673 
    674 type IsAbsTest struct {
    675 	path  string
    676 	isAbs bool
    677 }
    678 
    679 var isabstests = []IsAbsTest{
    680 	{"", false},
    681 	{"/", true},
    682 	{"/usr/bin/gcc", true},
    683 	{"..", false},
    684 	{"/a/../bb", true},
    685 	{".", false},
    686 	{"./", false},
    687 	{"lala", false},
    688 }
    689 
    690 var winisabstests = []IsAbsTest{
    691 	{`C:\`, true},
    692 	{`c\`, false},
    693 	{`c::`, false},
    694 	{`c:`, false},
    695 	{`/`, false},
    696 	{`\`, false},
    697 	{`\Windows`, false},
    698 	{`c:a\b`, false},
    699 	{`c:\a\b`, true},
    700 	{`c:/a/b`, true},
    701 	{`\\host\share\foo`, true},
    702 	{`//host/share/foo/bar`, true},
    703 }
    704 
    705 func TestIsAbs(t *testing.T) {
    706 	var tests []IsAbsTest
    707 	if runtime.GOOS == "windows" {
    708 		tests = append(tests, winisabstests...)
    709 		// All non-windows tests should fail, because they have no volume letter.
    710 		for _, test := range isabstests {
    711 			tests = append(tests, IsAbsTest{test.path, false})
    712 		}
    713 		// All non-windows test should work as intended if prefixed with volume letter.
    714 		for _, test := range isabstests {
    715 			tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs})
    716 		}
    717 	} else {
    718 		tests = isabstests
    719 	}
    720 
    721 	for _, test := range tests {
    722 		if r := filepath.IsAbs(test.path); r != test.isAbs {
    723 			t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs)
    724 		}
    725 	}
    726 }
    727 
    728 type EvalSymlinksTest struct {
    729 	// If dest is empty, the path is created; otherwise the dest is symlinked to the path.
    730 	path, dest string
    731 }
    732 
    733 var EvalSymlinksTestDirs = []EvalSymlinksTest{
    734 	{"test", ""},
    735 	{"test/dir", ""},
    736 	{"test/dir/link3", "../../"},
    737 	{"test/link1", "../test"},
    738 	{"test/link2", "dir"},
    739 	{"test/linkabs", "/"},
    740 }
    741 
    742 var EvalSymlinksTests = []EvalSymlinksTest{
    743 	{"test", "test"},
    744 	{"test/dir", "test/dir"},
    745 	{"test/dir/../..", "."},
    746 	{"test/link1", "test"},
    747 	{"test/link2", "test/dir"},
    748 	{"test/link1/dir", "test/dir"},
    749 	{"test/link2/..", "test"},
    750 	{"test/dir/link3", "."},
    751 	{"test/link2/link3/test", "test"},
    752 	{"test/linkabs", "/"},
    753 }
    754 
    755 var EvalSymlinksAbsWindowsTests = []EvalSymlinksTest{
    756 	{`c:\`, `c:\`},
    757 }
    758 
    759 // simpleJoin builds a file name from the directory and path.
    760 // It does not use Join because we don't want ".." to be evaluated.
    761 func simpleJoin(dir, path string) string {
    762 	return dir + string(filepath.Separator) + path
    763 }
    764 
    765 func TestEvalSymlinks(t *testing.T) {
    766 	switch runtime.GOOS {
    767 	case "nacl", "plan9":
    768 		t.Skipf("skipping on %s", runtime.GOOS)
    769 	}
    770 
    771 	tmpDir, err := ioutil.TempDir("", "evalsymlink")
    772 	if err != nil {
    773 		t.Fatal("creating temp dir:", err)
    774 	}
    775 	defer os.RemoveAll(tmpDir)
    776 
    777 	// /tmp may itself be a symlink! Avoid the confusion, although
    778 	// it means trusting the thing we're testing.
    779 	tmpDir, err = filepath.EvalSymlinks(tmpDir)
    780 	if err != nil {
    781 		t.Fatal("eval symlink for tmp dir:", err)
    782 	}
    783 
    784 	// Create the symlink farm using relative paths.
    785 	for _, d := range EvalSymlinksTestDirs {
    786 		var err error
    787 		path := simpleJoin(tmpDir, d.path)
    788 		if d.dest == "" {
    789 			err = os.Mkdir(path, 0755)
    790 		} else {
    791 			if supportsSymlinks {
    792 				err = os.Symlink(d.dest, path)
    793 			}
    794 		}
    795 		if err != nil {
    796 			t.Fatal(err)
    797 		}
    798 	}
    799 
    800 	var tests []EvalSymlinksTest
    801 	if supportsSymlinks {
    802 		tests = EvalSymlinksTests
    803 	} else {
    804 		for _, d := range EvalSymlinksTests {
    805 			if d.path == d.dest {
    806 				// will test only real files and directories
    807 				tests = append(tests, d)
    808 				// test "canonical" names
    809 				d2 := EvalSymlinksTest{
    810 					path: strings.ToUpper(d.path),
    811 					dest: d.dest,
    812 				}
    813 				tests = append(tests, d2)
    814 			}
    815 		}
    816 	}
    817 
    818 	// Evaluate the symlink farm.
    819 	for _, d := range tests {
    820 		path := simpleJoin(tmpDir, d.path)
    821 		dest := simpleJoin(tmpDir, d.dest)
    822 		if filepath.IsAbs(d.dest) || os.IsPathSeparator(d.dest[0]) {
    823 			dest = d.dest
    824 		}
    825 		if p, err := filepath.EvalSymlinks(path); err != nil {
    826 			t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
    827 		} else if filepath.Clean(p) != filepath.Clean(dest) {
    828 			t.Errorf("Clean(%q)=%q, want %q", path, p, dest)
    829 		}
    830 	}
    831 }
    832 
    833 // Test directories relative to temporary directory.
    834 // The tests are run in absTestDirs[0].
    835 var absTestDirs = []string{
    836 	"a",
    837 	"a/b",
    838 	"a/b/c",
    839 }
    840 
    841 // Test paths relative to temporary directory. $ expands to the directory.
    842 // The tests are run in absTestDirs[0].
    843 // We create absTestDirs first.
    844 var absTests = []string{
    845 	".",
    846 	"b",
    847 	"../a",
    848 	"../a/b",
    849 	"../a/b/./c/../../.././a",
    850 	"$",
    851 	"$/.",
    852 	"$/a/../a/b",
    853 	"$/a/b/c/../../.././a",
    854 }
    855 
    856 func TestAbs(t *testing.T) {
    857 	root, err := ioutil.TempDir("", "TestAbs")
    858 	if err != nil {
    859 		t.Fatal("TempDir failed: ", err)
    860 	}
    861 	defer os.RemoveAll(root)
    862 
    863 	wd, err := os.Getwd()
    864 	if err != nil {
    865 		t.Fatal("getwd failed: ", err)
    866 	}
    867 	err = os.Chdir(root)
    868 	if err != nil {
    869 		t.Fatal("chdir failed: ", err)
    870 	}
    871 	defer os.Chdir(wd)
    872 
    873 	for _, dir := range absTestDirs {
    874 		err = os.Mkdir(dir, 0777)
    875 		if err != nil {
    876 			t.Fatal("Mkdir failed: ", err)
    877 		}
    878 	}
    879 
    880 	if runtime.GOOS == "windows" {
    881 		vol := filepath.VolumeName(root)
    882 		var extra []string
    883 		for _, path := range absTests {
    884 			if strings.Index(path, "$") != -1 {
    885 				continue
    886 			}
    887 			path = vol + path
    888 			extra = append(extra, path)
    889 		}
    890 		absTests = append(absTests, extra...)
    891 	}
    892 
    893 	err = os.Chdir(absTestDirs[0])
    894 	if err != nil {
    895 		t.Fatal("chdir failed: ", err)
    896 	}
    897 
    898 	for _, path := range absTests {
    899 		path = strings.Replace(path, "$", root, -1)
    900 		info, err := os.Stat(path)
    901 		if err != nil {
    902 			t.Errorf("%s: %s", path, err)
    903 			continue
    904 		}
    905 
    906 		abspath, err := filepath.Abs(path)
    907 		if err != nil {
    908 			t.Errorf("Abs(%q) error: %v", path, err)
    909 			continue
    910 		}
    911 		absinfo, err := os.Stat(abspath)
    912 		if err != nil || !os.SameFile(absinfo, info) {
    913 			t.Errorf("Abs(%q)=%q, not the same file", path, abspath)
    914 		}
    915 		if !filepath.IsAbs(abspath) {
    916 			t.Errorf("Abs(%q)=%q, not an absolute path", path, abspath)
    917 		}
    918 		if filepath.IsAbs(path) && abspath != filepath.Clean(path) {
    919 			t.Errorf("Abs(%q)=%q, isn't clean", path, abspath)
    920 		}
    921 	}
    922 }
    923 
    924 type RelTests struct {
    925 	root, path, want string
    926 }
    927 
    928 var reltests = []RelTests{
    929 	{"a/b", "a/b", "."},
    930 	{"a/b/.", "a/b", "."},
    931 	{"a/b", "a/b/.", "."},
    932 	{"./a/b", "a/b", "."},
    933 	{"a/b", "./a/b", "."},
    934 	{"ab/cd", "ab/cde", "../cde"},
    935 	{"ab/cd", "ab/c", "../c"},
    936 	{"a/b", "a/b/c/d", "c/d"},
    937 	{"a/b", "a/b/../c", "../c"},
    938 	{"a/b/../c", "a/b", "../b"},
    939 	{"a/b/c", "a/c/d", "../../c/d"},
    940 	{"a/b", "c/d", "../../c/d"},
    941 	{"a/b/c/d", "a/b", "../.."},
    942 	{"a/b/c/d", "a/b/", "../.."},
    943 	{"a/b/c/d/", "a/b", "../.."},
    944 	{"a/b/c/d/", "a/b/", "../.."},
    945 	{"../../a/b", "../../a/b/c/d", "c/d"},
    946 	{"/a/b", "/a/b", "."},
    947 	{"/a/b/.", "/a/b", "."},
    948 	{"/a/b", "/a/b/.", "."},
    949 	{"/ab/cd", "/ab/cde", "../cde"},
    950 	{"/ab/cd", "/ab/c", "../c"},
    951 	{"/a/b", "/a/b/c/d", "c/d"},
    952 	{"/a/b", "/a/b/../c", "../c"},
    953 	{"/a/b/../c", "/a/b", "../b"},
    954 	{"/a/b/c", "/a/c/d", "../../c/d"},
    955 	{"/a/b", "/c/d", "../../c/d"},
    956 	{"/a/b/c/d", "/a/b", "../.."},
    957 	{"/a/b/c/d", "/a/b/", "../.."},
    958 	{"/a/b/c/d/", "/a/b", "../.."},
    959 	{"/a/b/c/d/", "/a/b/", "../.."},
    960 	{"/../../a/b", "/../../a/b/c/d", "c/d"},
    961 	{".", "a/b", "a/b"},
    962 	{".", "..", ".."},
    963 
    964 	// can't do purely lexically
    965 	{"..", ".", "err"},
    966 	{"..", "a", "err"},
    967 	{"../..", "..", "err"},
    968 	{"a", "/a", "err"},
    969 	{"/a", "a", "err"},
    970 }
    971 
    972 var winreltests = []RelTests{
    973 	{`C:a\b\c`, `C:a/b/d`, `..\d`},
    974 	{`C:\`, `D:\`, `err`},
    975 	{`C:`, `D:`, `err`},
    976 }
    977 
    978 func TestRel(t *testing.T) {
    979 	tests := append([]RelTests{}, reltests...)
    980 	if runtime.GOOS == "windows" {
    981 		for i := range tests {
    982 			tests[i].want = filepath.FromSlash(tests[i].want)
    983 		}
    984 		tests = append(tests, winreltests...)
    985 	}
    986 	for _, test := range tests {
    987 		got, err := filepath.Rel(test.root, test.path)
    988 		if test.want == "err" {
    989 			if err == nil {
    990 				t.Errorf("Rel(%q, %q)=%q, want error", test.root, test.path, got)
    991 			}
    992 			continue
    993 		}
    994 		if err != nil {
    995 			t.Errorf("Rel(%q, %q): want %q, got error: %s", test.root, test.path, test.want, err)
    996 		}
    997 		if got != test.want {
    998 			t.Errorf("Rel(%q, %q)=%q, want %q", test.root, test.path, got, test.want)
    999 		}
   1000 	}
   1001 }
   1002 
   1003 type VolumeNameTest struct {
   1004 	path string
   1005 	vol  string
   1006 }
   1007 
   1008 var volumenametests = []VolumeNameTest{
   1009 	{`c:/foo/bar`, `c:`},
   1010 	{`c:`, `c:`},
   1011 	{`2:`, ``},
   1012 	{``, ``},
   1013 	{`\\\host`, ``},
   1014 	{`\\\host\`, ``},
   1015 	{`\\\host\share`, ``},
   1016 	{`\\\host\\share`, ``},
   1017 	{`\\host`, ``},
   1018 	{`//host`, ``},
   1019 	{`\\host\`, ``},
   1020 	{`//host/`, ``},
   1021 	{`\\host\share`, `\\host\share`},
   1022 	{`//host/share`, `//host/share`},
   1023 	{`\\host\share\`, `\\host\share`},
   1024 	{`//host/share/`, `//host/share`},
   1025 	{`\\host\share\foo`, `\\host\share`},
   1026 	{`//host/share/foo`, `//host/share`},
   1027 	{`\\host\share\\foo\\\bar\\\\baz`, `\\host\share`},
   1028 	{`//host/share//foo///bar////baz`, `//host/share`},
   1029 	{`\\host\share\foo\..\bar`, `\\host\share`},
   1030 	{`//host/share/foo/../bar`, `//host/share`},
   1031 }
   1032 
   1033 func TestVolumeName(t *testing.T) {
   1034 	if runtime.GOOS != "windows" {
   1035 		return
   1036 	}
   1037 	for _, v := range volumenametests {
   1038 		if vol := filepath.VolumeName(v.path); vol != v.vol {
   1039 			t.Errorf("VolumeName(%q)=%q, want %q", v.path, vol, v.vol)
   1040 		}
   1041 	}
   1042 }
   1043 
   1044 func TestDriveLetterInEvalSymlinks(t *testing.T) {
   1045 	if runtime.GOOS != "windows" {
   1046 		return
   1047 	}
   1048 	wd, _ := os.Getwd()
   1049 	if len(wd) < 3 {
   1050 		t.Errorf("Current directory path %q is too short", wd)
   1051 	}
   1052 	lp := strings.ToLower(wd)
   1053 	up := strings.ToUpper(wd)
   1054 	flp, err := filepath.EvalSymlinks(lp)
   1055 	if err != nil {
   1056 		t.Fatalf("EvalSymlinks(%q) failed: %q", lp, err)
   1057 	}
   1058 	fup, err := filepath.EvalSymlinks(up)
   1059 	if err != nil {
   1060 		t.Fatalf("EvalSymlinks(%q) failed: %q", up, err)
   1061 	}
   1062 	if flp != fup {
   1063 		t.Errorf("Results of EvalSymlinks do not match: %q and %q", flp, fup)
   1064 	}
   1065 }
   1066 
   1067 func TestBug3486(t *testing.T) { // https://golang.org/issue/3486
   1068 	if runtime.GOOS == "darwin" {
   1069 		switch runtime.GOARCH {
   1070 		case "arm", "arm64":
   1071 			t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
   1072 		}
   1073 	}
   1074 	root, err := filepath.EvalSymlinks(runtime.GOROOT() + "/test")
   1075 	if err != nil {
   1076 		t.Fatal(err)
   1077 	}
   1078 	bugs := filepath.Join(root, "bugs")
   1079 	ken := filepath.Join(root, "ken")
   1080 	seenBugs := false
   1081 	seenKen := false
   1082 	filepath.Walk(root, func(pth string, info os.FileInfo, err error) error {
   1083 		if err != nil {
   1084 			t.Fatal(err)
   1085 		}
   1086 
   1087 		switch pth {
   1088 		case bugs:
   1089 			seenBugs = true
   1090 			return filepath.SkipDir
   1091 		case ken:
   1092 			if !seenBugs {
   1093 				t.Fatal("filepath.Walk out of order - ken before bugs")
   1094 			}
   1095 			seenKen = true
   1096 		}
   1097 		return nil
   1098 	})
   1099 	if !seenKen {
   1100 		t.Fatalf("%q not seen", ken)
   1101 	}
   1102 }
   1103