Black Lives Matter. Support the Equal Justice Initiative.

Source file src/path/filepath/path_test.go

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

View as plain text