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