github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/lib/file/mkdir_windows_test.go (about)

     1  //go:build windows
     2  
     3  package file
     4  
     5  import (
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  // Basic test from golang's os/path_test.go
    16  func TestMkdirAll(t *testing.T) {
    17  	tmpDir := t.TempDir()
    18  
    19  	path := tmpDir + "/dir/./dir2"
    20  	err := MkdirAll(path, 0777)
    21  	if err != nil {
    22  		t.Fatalf("MkdirAll %q: %s", path, err)
    23  	}
    24  
    25  	// Already exists, should succeed.
    26  	err = MkdirAll(path, 0777)
    27  	if err != nil {
    28  		t.Fatalf("MkdirAll %q (second time): %s", path, err)
    29  	}
    30  
    31  	// Make file.
    32  	fpath := path + "/file"
    33  	f, err := Create(fpath)
    34  	if err != nil {
    35  		t.Fatalf("create %q: %s", fpath, err)
    36  	}
    37  	defer func() {
    38  		if err := f.Close(); err != nil {
    39  			t.Fatalf("Close %q: %s", fpath, err)
    40  		}
    41  	}()
    42  
    43  	// Can't make directory named after file.
    44  	err = MkdirAll(fpath, 0777)
    45  	if err == nil {
    46  		t.Fatalf("MkdirAll %q: no error", fpath)
    47  	}
    48  	perr, ok := err.(*os.PathError)
    49  	if !ok {
    50  		t.Fatalf("MkdirAll %q returned %T, not *PathError", fpath, err)
    51  	}
    52  	if filepath.Clean(perr.Path) != filepath.Clean(fpath) {
    53  		t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", fpath, filepath.Clean(perr.Path), filepath.Clean(fpath))
    54  	}
    55  
    56  	// Can't make subdirectory of file.
    57  	ffpath := fpath + "/subdir"
    58  	err = MkdirAll(ffpath, 0777)
    59  	if err == nil {
    60  		t.Fatalf("MkdirAll %q: no error", ffpath)
    61  	}
    62  	perr, ok = err.(*os.PathError)
    63  	if !ok {
    64  		t.Fatalf("MkdirAll %q returned %T, not *PathError", ffpath, err)
    65  	}
    66  	if filepath.Clean(perr.Path) != filepath.Clean(fpath) {
    67  		t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", ffpath, filepath.Clean(perr.Path), filepath.Clean(fpath))
    68  	}
    69  
    70  	path = tmpDir + `\dir\.\dir2\`
    71  	err = MkdirAll(path, 0777)
    72  	if err != nil {
    73  		t.Fatalf("MkdirAll %q: %s", path, err)
    74  	}
    75  }
    76  
    77  func unusedDrive(t *testing.T) string {
    78  	letter := FindUnusedDriveLetter()
    79  	require.NotEqual(t, letter, 0)
    80  	return string(letter) + ":"
    81  }
    82  
    83  func checkMkdirAll(t *testing.T, path string, valid bool, errormsgs ...string) {
    84  	if valid {
    85  		assert.NoError(t, MkdirAll(path, 0777))
    86  	} else {
    87  		err := MkdirAll(path, 0777)
    88  		assert.Error(t, err)
    89  		ok := false
    90  		for _, msg := range errormsgs {
    91  			if err.Error() == msg {
    92  				ok = true
    93  			}
    94  		}
    95  		assert.True(t, ok, fmt.Sprintf("Error message '%v' didn't match any of %v", err, errormsgs))
    96  	}
    97  }
    98  
    99  func checkMkdirAllSubdirs(t *testing.T, path string, valid bool, errormsgs ...string) {
   100  	checkMkdirAll(t, path, valid, errormsgs...)
   101  	checkMkdirAll(t, path+`\`, valid, errormsgs...)
   102  	checkMkdirAll(t, path+`\parent`, valid, errormsgs...)
   103  	checkMkdirAll(t, path+`\parent\`, valid, errormsgs...)
   104  	checkMkdirAll(t, path+`\parent\child`, valid, errormsgs...)
   105  	checkMkdirAll(t, path+`\parent\child\`, valid, errormsgs...)
   106  }
   107  
   108  // Testing paths on existing drive
   109  func TestMkdirAllOnDrive(t *testing.T) {
   110  	path := t.TempDir()
   111  
   112  	dir, err := os.Stat(path)
   113  	require.NoError(t, err)
   114  	require.True(t, dir.IsDir())
   115  
   116  	drive := filepath.VolumeName(path)
   117  
   118  	checkMkdirAll(t, drive, true, "")
   119  	checkMkdirAll(t, drive+`\`, true, "")
   120  	// checkMkdirAll(t, `\\?\`+drive, true, "") - this isn't actually a Valid Windows path - this test used to work under go1.21.3 but fails under go1.21.4
   121  	checkMkdirAll(t, `\\?\`+drive+`\`, true, "")
   122  	checkMkdirAllSubdirs(t, path, true, "")
   123  	checkMkdirAllSubdirs(t, `\\?\`+path, true, "")
   124  }
   125  
   126  // Testing paths on unused drive
   127  // This is where there is a difference from golang's os.MkdirAll. It would
   128  // recurse extended-length paths down to the "\\?" prefix and return the
   129  // noninformative error:
   130  // "mkdir \\?: The filename, directory name, or volume label syntax is incorrect."
   131  // Our version stops the recursion at drive's root directory, and reports:
   132  // "mkdir \\?\A:\: The system cannot find the path specified."
   133  func TestMkdirAllOnUnusedDrive(t *testing.T) {
   134  	path := unusedDrive(t)
   135  	errormsg := fmt.Sprintf(`mkdir %s\: The system cannot find the path specified.`, path)
   136  	checkMkdirAllSubdirs(t, path, false, errormsg)
   137  	errormsg1 := fmt.Sprintf(`mkdir \\?\%s\: The system cannot find the path specified.`, path) // pre go1.21.4
   138  	errormsg2 := fmt.Sprintf(`mkdir \\?\%s: The system cannot find the file specified.`, path)  // go1.21.4 and after
   139  	checkMkdirAllSubdirs(t, `\\?\`+path, false, errormsg1, errormsg2)
   140  }
   141  
   142  // Testing paths on unknown network host
   143  // This is an additional difference from golang's os.MkdirAll. With our
   144  // first fix, stopping it from recursing extended-length paths down to
   145  // the "\\?" prefix, it would now stop at `\\?\UNC`, because that is what
   146  // filepath.VolumeName returns (which is wrong, that is not a volume name!),
   147  // and still return a nonifnromative error:
   148  // "mkdir \\?\UNC\\: The filename, directory name, or volume label syntax is incorrect."
   149  // Our version stops the recursion at level before this, and reports:
   150  // "mkdir \\?\UNC\0.0.0.0: The specified path is invalid."
   151  func TestMkdirAllOnUnusedNetworkHost(t *testing.T) {
   152  	path := `\\0.0.0.0\share`
   153  	errormsg := fmt.Sprintf("mkdir %s\\: The format of the specified network name is invalid.", path)
   154  	checkMkdirAllSubdirs(t, path, false, errormsg)
   155  	path = `\\?\UNC\0.0.0.0\share`
   156  	checkMkdirAllSubdirs(t, path, false,
   157  		`mkdir \\?\UNC\0.0.0.0: The specified path is invalid.`, // pre go1.20
   158  		`mkdir \\?\UNC\0.0.0.0\share\: The format of the specified network name is invalid.`,
   159  	)
   160  
   161  }