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