github.com/euank/go@v0.0.0-20160829210321-495514729181/src/path/filepath/path_windows_test.go (about)

     1  // Copyright 2013 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  	"flag"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"reflect"
    15  	"strings"
    16  	"testing"
    17  )
    18  
    19  func TestWinSplitListTestsAreValid(t *testing.T) {
    20  	comspec := os.Getenv("ComSpec")
    21  	if comspec == "" {
    22  		t.Fatal("%ComSpec% must be set")
    23  	}
    24  
    25  	for ti, tt := range winsplitlisttests {
    26  		testWinSplitListTestIsValid(t, ti, tt, comspec)
    27  	}
    28  }
    29  
    30  func testWinSplitListTestIsValid(t *testing.T, ti int, tt SplitListTest,
    31  	comspec string) {
    32  
    33  	const (
    34  		cmdfile             = `printdir.cmd`
    35  		perm    os.FileMode = 0700
    36  	)
    37  
    38  	tmp, err := ioutil.TempDir("", "testWinSplitListTestIsValid")
    39  	if err != nil {
    40  		t.Fatalf("TempDir failed: %v", err)
    41  	}
    42  	defer os.RemoveAll(tmp)
    43  
    44  	for i, d := range tt.result {
    45  		if d == "" {
    46  			continue
    47  		}
    48  		if cd := filepath.Clean(d); filepath.VolumeName(cd) != "" ||
    49  			cd[0] == '\\' || cd == ".." || (len(cd) >= 3 && cd[0:3] == `..\`) {
    50  			t.Errorf("%d,%d: %#q refers outside working directory", ti, i, d)
    51  			return
    52  		}
    53  		dd := filepath.Join(tmp, d)
    54  		if _, err := os.Stat(dd); err == nil {
    55  			t.Errorf("%d,%d: %#q already exists", ti, i, d)
    56  			return
    57  		}
    58  		if err = os.MkdirAll(dd, perm); err != nil {
    59  			t.Errorf("%d,%d: MkdirAll(%#q) failed: %v", ti, i, dd, err)
    60  			return
    61  		}
    62  		fn, data := filepath.Join(dd, cmdfile), []byte("@echo "+d+"\r\n")
    63  		if err = ioutil.WriteFile(fn, data, perm); err != nil {
    64  			t.Errorf("%d,%d: WriteFile(%#q) failed: %v", ti, i, fn, err)
    65  			return
    66  		}
    67  	}
    68  
    69  	for i, d := range tt.result {
    70  		if d == "" {
    71  			continue
    72  		}
    73  		exp := []byte(d + "\r\n")
    74  		cmd := &exec.Cmd{
    75  			Path: comspec,
    76  			Args: []string{`/c`, cmdfile},
    77  			Env:  []string{`Path=` + tt.list},
    78  			Dir:  tmp,
    79  		}
    80  		out, err := cmd.CombinedOutput()
    81  		switch {
    82  		case err != nil:
    83  			t.Errorf("%d,%d: execution error %v\n%q", ti, i, err, out)
    84  			return
    85  		case !reflect.DeepEqual(out, exp):
    86  			t.Errorf("%d,%d: expected %#q, got %#q", ti, i, exp, out)
    87  			return
    88  		default:
    89  			// unshadow cmdfile in next directory
    90  			err = os.Remove(filepath.Join(tmp, d, cmdfile))
    91  			if err != nil {
    92  				t.Fatalf("Remove test command failed: %v", err)
    93  			}
    94  		}
    95  	}
    96  }
    97  
    98  // TestEvalSymlinksCanonicalNames verify that EvalSymlinks
    99  // returns "canonical" path names on windows.
   100  func TestEvalSymlinksCanonicalNames(t *testing.T) {
   101  	tmp, err := ioutil.TempDir("", "evalsymlinkcanonical")
   102  	if err != nil {
   103  		t.Fatal("creating temp dir:", err)
   104  	}
   105  	defer os.RemoveAll(tmp)
   106  
   107  	// ioutil.TempDir might return "non-canonical" name.
   108  	cTmpName, err := filepath.EvalSymlinks(tmp)
   109  	if err != nil {
   110  		t.Errorf("EvalSymlinks(%q) error: %v", tmp, err)
   111  	}
   112  
   113  	dirs := []string{
   114  		"test",
   115  		"test/dir",
   116  		"testing_long_dir",
   117  		"TEST2",
   118  	}
   119  
   120  	for _, d := range dirs {
   121  		dir := filepath.Join(cTmpName, d)
   122  		err := os.Mkdir(dir, 0755)
   123  		if err != nil {
   124  			t.Fatal(err)
   125  		}
   126  		cname, err := filepath.EvalSymlinks(dir)
   127  		if err != nil {
   128  			t.Errorf("EvalSymlinks(%q) error: %v", dir, err)
   129  			continue
   130  		}
   131  		if dir != cname {
   132  			t.Errorf("EvalSymlinks(%q) returns %q, but should return %q", dir, cname, dir)
   133  			continue
   134  		}
   135  		// test non-canonical names
   136  		test := strings.ToUpper(dir)
   137  		p, err := filepath.EvalSymlinks(test)
   138  		if err != nil {
   139  			t.Errorf("EvalSymlinks(%q) error: %v", test, err)
   140  			continue
   141  		}
   142  		if p != cname {
   143  			t.Errorf("EvalSymlinks(%q) returns %q, but should return %q", test, p, cname)
   144  			continue
   145  		}
   146  		// another test
   147  		test = strings.ToLower(dir)
   148  		p, err = filepath.EvalSymlinks(test)
   149  		if err != nil {
   150  			t.Errorf("EvalSymlinks(%q) error: %v", test, err)
   151  			continue
   152  		}
   153  		if p != cname {
   154  			t.Errorf("EvalSymlinks(%q) returns %q, but should return %q", test, p, cname)
   155  			continue
   156  		}
   157  	}
   158  }
   159  
   160  // checkVolume8dot3Setting runs "fsutil 8dot3name query c:" command
   161  // (where c: is vol parameter) to discover "8dot3 name creation state".
   162  // The state is combination of 2 flags. The global flag controls if it
   163  // is per volume or global setting:
   164  //   0 - Enable 8dot3 name creation on all volumes on the system
   165  //   1 - Disable 8dot3 name creation on all volumes on the system
   166  //   2 - Set 8dot3 name creation on a per volume basis
   167  //   3 - Disable 8dot3 name creation on all volumes except the system volume
   168  // If global flag is set to 2, then per-volume flag needs to be examined:
   169  //   0 - Enable 8dot3 name creation on this volume
   170  //   1 - Disable 8dot3 name creation on this volume
   171  // checkVolume8dot3Setting verifies that "8dot3 name creation" flags
   172  // are set to 2 and 0, if enabled parameter is true, or 2 and 1, if enabled
   173  // is false. Otherwise checkVolume8dot3Setting returns error.
   174  func checkVolume8dot3Setting(vol string, enabled bool) error {
   175  	// It appears, on some systems "fsutil 8dot3name query ..." command always
   176  	// exits with error. Ignore exit code, and look at fsutil output instead.
   177  	out, _ := exec.Command("fsutil", "8dot3name", "query", vol).CombinedOutput()
   178  	// Check that system has "Volume level setting" set.
   179  	expected := "The registry state of NtfsDisable8dot3NameCreation is 2, the default (Volume level setting)"
   180  	if !strings.Contains(string(out), expected) {
   181  		// Windows 10 version of fsutil has different output message.
   182  		expectedWindow10 := "The registry state is: 2 (Per volume setting - the default)"
   183  		if !strings.Contains(string(out), expectedWindow10) {
   184  			return fmt.Errorf("fsutil output should contain %q, but is %q", expected, string(out))
   185  		}
   186  	}
   187  	// Now check the volume setting.
   188  	expected = "Based on the above two settings, 8dot3 name creation is %s on %s"
   189  	if enabled {
   190  		expected = fmt.Sprintf(expected, "enabled", vol)
   191  	} else {
   192  		expected = fmt.Sprintf(expected, "disabled", vol)
   193  	}
   194  	if !strings.Contains(string(out), expected) {
   195  		return fmt.Errorf("unexpected fsutil output: %q", string(out))
   196  	}
   197  	return nil
   198  }
   199  
   200  func setVolume8dot3Setting(vol string, enabled bool) error {
   201  	cmd := []string{"fsutil", "8dot3name", "set", vol}
   202  	if enabled {
   203  		cmd = append(cmd, "0")
   204  	} else {
   205  		cmd = append(cmd, "1")
   206  	}
   207  	// It appears, on some systems "fsutil 8dot3name set ..." command always
   208  	// exits with error. Ignore exit code, and look at fsutil output instead.
   209  	out, _ := exec.Command(cmd[0], cmd[1:]...).CombinedOutput()
   210  	if string(out) != "\r\nSuccessfully set 8dot3name behavior.\r\n" {
   211  		// Windows 10 version of fsutil has different output message.
   212  		expectedWindow10 := "Successfully %s 8dot3name generation on %s\r\n"
   213  		if enabled {
   214  			expectedWindow10 = fmt.Sprintf(expectedWindow10, "enabled", vol)
   215  		} else {
   216  			expectedWindow10 = fmt.Sprintf(expectedWindow10, "disabled", vol)
   217  		}
   218  		if string(out) != expectedWindow10 {
   219  			return fmt.Errorf("%v command failed: %q", cmd, string(out))
   220  		}
   221  	}
   222  	return nil
   223  }
   224  
   225  var runFSModifyTests = flag.Bool("run_fs_modify_tests", false, "run tests which modify filesystem parameters")
   226  
   227  // This test assumes registry state of NtfsDisable8dot3NameCreation is 2,
   228  // the default (Volume level setting).
   229  func TestEvalSymlinksCanonicalNamesWith8dot3Disabled(t *testing.T) {
   230  	if !*runFSModifyTests {
   231  		t.Skip("skipping test that modifies file system setting; enable with -run_fs_modify_tests")
   232  	}
   233  	tempVol := filepath.VolumeName(os.TempDir())
   234  	if len(tempVol) != 2 {
   235  		t.Fatalf("unexpected temp volume name %q", tempVol)
   236  	}
   237  
   238  	err := checkVolume8dot3Setting(tempVol, true)
   239  	if err != nil {
   240  		t.Fatal(err)
   241  	}
   242  	err = setVolume8dot3Setting(tempVol, false)
   243  	if err != nil {
   244  		t.Fatal(err)
   245  	}
   246  	defer func() {
   247  		err := setVolume8dot3Setting(tempVol, true)
   248  		if err != nil {
   249  			t.Fatal(err)
   250  		}
   251  		err = checkVolume8dot3Setting(tempVol, true)
   252  		if err != nil {
   253  			t.Fatal(err)
   254  		}
   255  	}()
   256  	err = checkVolume8dot3Setting(tempVol, false)
   257  	if err != nil {
   258  		t.Fatal(err)
   259  	}
   260  	TestEvalSymlinksCanonicalNames(t)
   261  }
   262  
   263  func TestToNorm(t *testing.T) {
   264  	stubBase := func(path string) (string, error) {
   265  		vol := filepath.VolumeName(path)
   266  		path = path[len(vol):]
   267  
   268  		if strings.Contains(path, "/") {
   269  			return "", fmt.Errorf("invalid path is given to base: %s", vol+path)
   270  		}
   271  
   272  		if path == "" || path == "." || path == `\` {
   273  			return "", fmt.Errorf("invalid path is given to base: %s", vol+path)
   274  		}
   275  
   276  		i := strings.LastIndexByte(path, filepath.Separator)
   277  		if i == len(path)-1 { // trailing '\' is invalid
   278  			return "", fmt.Errorf("invalid path is given to base: %s", vol+path)
   279  		}
   280  		if i == -1 {
   281  			return strings.ToUpper(path), nil
   282  		}
   283  
   284  		return strings.ToUpper(path[i+1:]), nil
   285  	}
   286  
   287  	// On this test, toNorm should be same as string.ToUpper(filepath.Clean(path)) except empty string.
   288  	tests := []struct {
   289  		arg  string
   290  		want string
   291  	}{
   292  		{"", ""},
   293  		{".", "."},
   294  		{"./foo/bar", `FOO\BAR`},
   295  		{"/", `\`},
   296  		{"/foo/bar", `\FOO\BAR`},
   297  		{"/foo/bar/baz/qux", `\FOO\BAR\BAZ\QUX`},
   298  		{"foo/bar", `FOO\BAR`},
   299  		{"C:/foo/bar", `C:\FOO\BAR`},
   300  		{"C:foo/bar", `C:FOO\BAR`},
   301  		{"c:/foo/bar", `C:\FOO\BAR`},
   302  		{"C:/foo/bar", `C:\FOO\BAR`},
   303  		{"C:/foo/bar/", `C:\FOO\BAR`},
   304  		{`C:\foo\bar`, `C:\FOO\BAR`},
   305  		{`C:\foo/bar\`, `C:\FOO\BAR`},
   306  		{"C:/ふー/バー", `C:\ふー\バー`},
   307  	}
   308  
   309  	for _, test := range tests {
   310  		got, err := filepath.ToNorm(test.arg, stubBase)
   311  		if err != nil {
   312  			t.Errorf("unexpected toNorm error, arg: %s, err: %v\n", test.arg, err)
   313  		} else if got != test.want {
   314  			t.Errorf("toNorm error, arg: %s, want: %s, got: %s\n", test.arg, test.want, got)
   315  		}
   316  	}
   317  }