github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/os/os_windows_test.go (about)

     1  // Copyright 2014 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 os_test
     6  
     7  import (
     8  	"fmt"
     9  	"internal/poll"
    10  	"internal/syscall/windows"
    11  	"internal/testenv"
    12  	"io"
    13  	"io/ioutil"
    14  	"os"
    15  	osexec "os/exec"
    16  	"path/filepath"
    17  	"reflect"
    18  	"runtime"
    19  	"sort"
    20  	"strings"
    21  	"syscall"
    22  	"testing"
    23  	"unicode/utf16"
    24  	"unsafe"
    25  )
    26  
    27  func TestSameWindowsFile(t *testing.T) {
    28  	temp, err := ioutil.TempDir("", "TestSameWindowsFile")
    29  	if err != nil {
    30  		t.Fatal(err)
    31  	}
    32  	defer os.RemoveAll(temp)
    33  
    34  	wd, err := os.Getwd()
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	err = os.Chdir(temp)
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  	defer os.Chdir(wd)
    43  
    44  	f, err := os.Create("a")
    45  	if err != nil {
    46  		t.Fatal(err)
    47  	}
    48  	f.Close()
    49  
    50  	ia1, err := os.Stat("a")
    51  	if err != nil {
    52  		t.Fatal(err)
    53  	}
    54  
    55  	path, err := filepath.Abs("a")
    56  	if err != nil {
    57  		t.Fatal(err)
    58  	}
    59  	ia2, err := os.Stat(path)
    60  	if err != nil {
    61  		t.Fatal(err)
    62  	}
    63  	if !os.SameFile(ia1, ia2) {
    64  		t.Errorf("files should be same")
    65  	}
    66  
    67  	p := filepath.VolumeName(path) + filepath.Base(path)
    68  	if err != nil {
    69  		t.Fatal(err)
    70  	}
    71  	ia3, err := os.Stat(p)
    72  	if err != nil {
    73  		t.Fatal(err)
    74  	}
    75  	if !os.SameFile(ia1, ia3) {
    76  		t.Errorf("files should be same")
    77  	}
    78  }
    79  
    80  type dirLinkTest struct {
    81  	name    string
    82  	mklink  func(link, target string) error
    83  	issueNo int // correspondent issue number (for broken tests)
    84  }
    85  
    86  func testDirLinks(t *testing.T, tests []dirLinkTest) {
    87  	tmpdir, err := ioutil.TempDir("", "testDirLinks")
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  	defer os.RemoveAll(tmpdir)
    92  
    93  	oldwd, err := os.Getwd()
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  	err = os.Chdir(tmpdir)
    98  	if err != nil {
    99  		t.Fatal(err)
   100  	}
   101  	defer os.Chdir(oldwd)
   102  
   103  	dir := filepath.Join(tmpdir, "dir")
   104  	err = os.Mkdir(dir, 0777)
   105  	if err != nil {
   106  		t.Fatal(err)
   107  	}
   108  	err = ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("abc"), 0644)
   109  	if err != nil {
   110  		t.Fatal(err)
   111  	}
   112  	for _, test := range tests {
   113  		link := filepath.Join(tmpdir, test.name+"_link")
   114  		err := test.mklink(link, dir)
   115  		if err != nil {
   116  			t.Errorf("creating link for %s test failed: %v", test.name, err)
   117  			continue
   118  		}
   119  
   120  		data, err := ioutil.ReadFile(filepath.Join(link, "abc"))
   121  		if err != nil {
   122  			t.Errorf("failed to read abc file: %v", err)
   123  			continue
   124  		}
   125  		if string(data) != "abc" {
   126  			t.Errorf(`abc file is expected to have "abc" in it, but has %v`, data)
   127  			continue
   128  		}
   129  
   130  		if test.issueNo > 0 {
   131  			t.Logf("skipping broken %q test: see issue %d", test.name, test.issueNo)
   132  			continue
   133  		}
   134  
   135  		fi, err := os.Stat(link)
   136  		if err != nil {
   137  			t.Errorf("failed to stat link %v: %v", link, err)
   138  			continue
   139  		}
   140  		expected := filepath.Base(dir)
   141  		got := fi.Name()
   142  		if !fi.IsDir() || expected != got {
   143  			t.Errorf("link should point to %v but points to %v instead", expected, got)
   144  			continue
   145  		}
   146  	}
   147  }
   148  
   149  // reparseData is used to build reparse buffer data required for tests.
   150  type reparseData struct {
   151  	substituteName namePosition
   152  	printName      namePosition
   153  	pathBuf        []uint16
   154  }
   155  
   156  type namePosition struct {
   157  	offset uint16
   158  	length uint16
   159  }
   160  
   161  func (rd *reparseData) addUTF16s(s []uint16) (offset uint16) {
   162  	off := len(rd.pathBuf) * 2
   163  	rd.pathBuf = append(rd.pathBuf, s...)
   164  	return uint16(off)
   165  }
   166  
   167  func (rd *reparseData) addString(s string) (offset, length uint16) {
   168  	p := syscall.StringToUTF16(s)
   169  	return rd.addUTF16s(p), uint16(len(p)-1) * 2 // do not include terminating NUL in the legth (as per PrintNameLength and SubstituteNameLength documentation)
   170  }
   171  
   172  func (rd *reparseData) addSubstituteName(name string) {
   173  	rd.substituteName.offset, rd.substituteName.length = rd.addString(name)
   174  }
   175  
   176  func (rd *reparseData) addPrintName(name string) {
   177  	rd.printName.offset, rd.printName.length = rd.addString(name)
   178  }
   179  
   180  func (rd *reparseData) addStringNoNUL(s string) (offset, length uint16) {
   181  	p := syscall.StringToUTF16(s)
   182  	p = p[:len(p)-1]
   183  	return rd.addUTF16s(p), uint16(len(p)) * 2
   184  }
   185  
   186  func (rd *reparseData) addSubstituteNameNoNUL(name string) {
   187  	rd.substituteName.offset, rd.substituteName.length = rd.addStringNoNUL(name)
   188  }
   189  
   190  func (rd *reparseData) addPrintNameNoNUL(name string) {
   191  	rd.printName.offset, rd.printName.length = rd.addStringNoNUL(name)
   192  }
   193  
   194  // pathBuffeLen returns length of rd pathBuf in bytes.
   195  func (rd *reparseData) pathBuffeLen() uint16 {
   196  	return uint16(len(rd.pathBuf)) * 2
   197  }
   198  
   199  // Windows REPARSE_DATA_BUFFER contains union member, and cannot be
   200  // translated into Go directly. _REPARSE_DATA_BUFFER type is to help
   201  // construct alternative versions of Windows REPARSE_DATA_BUFFER with
   202  // union part of SymbolicLinkReparseBuffer or MountPointReparseBuffer type.
   203  type _REPARSE_DATA_BUFFER struct {
   204  	header windows.REPARSE_DATA_BUFFER_HEADER
   205  	detail [syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
   206  }
   207  
   208  func createDirLink(link string, rdb *_REPARSE_DATA_BUFFER) error {
   209  	err := os.Mkdir(link, 0777)
   210  	if err != nil {
   211  		return err
   212  	}
   213  
   214  	linkp := syscall.StringToUTF16(link)
   215  	fd, err := syscall.CreateFile(&linkp[0], syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING,
   216  		syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
   217  	if err != nil {
   218  		return err
   219  	}
   220  	defer syscall.CloseHandle(fd)
   221  
   222  	buflen := uint32(rdb.header.ReparseDataLength) + uint32(unsafe.Sizeof(rdb.header))
   223  	var bytesReturned uint32
   224  	return syscall.DeviceIoControl(fd, windows.FSCTL_SET_REPARSE_POINT,
   225  		(*byte)(unsafe.Pointer(&rdb.header)), buflen, nil, 0, &bytesReturned, nil)
   226  }
   227  
   228  func createMountPoint(link string, target *reparseData) error {
   229  	var buf *windows.MountPointReparseBuffer
   230  	buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen() // see ReparseDataLength documentation
   231  	byteblob := make([]byte, buflen)
   232  	buf = (*windows.MountPointReparseBuffer)(unsafe.Pointer(&byteblob[0]))
   233  	buf.SubstituteNameOffset = target.substituteName.offset
   234  	buf.SubstituteNameLength = target.substituteName.length
   235  	buf.PrintNameOffset = target.printName.offset
   236  	buf.PrintNameLength = target.printName.length
   237  	copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:], target.pathBuf)
   238  
   239  	var rdb _REPARSE_DATA_BUFFER
   240  	rdb.header.ReparseTag = windows.IO_REPARSE_TAG_MOUNT_POINT
   241  	rdb.header.ReparseDataLength = buflen
   242  	copy(rdb.detail[:], byteblob)
   243  
   244  	return createDirLink(link, &rdb)
   245  }
   246  
   247  func TestDirectoryJunction(t *testing.T) {
   248  	var tests = []dirLinkTest{
   249  		{
   250  			// Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
   251  			name: "standard",
   252  			mklink: func(link, target string) error {
   253  				var t reparseData
   254  				t.addSubstituteName(`\??\` + target)
   255  				t.addPrintName(target)
   256  				return createMountPoint(link, &t)
   257  			},
   258  		},
   259  		{
   260  			// Do as junction utility https://technet.microsoft.com/en-au/sysinternals/bb896768.aspx does - set PrintNameLength to 0.
   261  			name: "have_blank_print_name",
   262  			mklink: func(link, target string) error {
   263  				var t reparseData
   264  				t.addSubstituteName(`\??\` + target)
   265  				t.addPrintName("")
   266  				return createMountPoint(link, &t)
   267  			},
   268  		},
   269  	}
   270  	output, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output()
   271  	mklinkSupportsJunctionLinks := strings.Contains(string(output), " /J ")
   272  	if mklinkSupportsJunctionLinks {
   273  		tests = append(tests,
   274  			dirLinkTest{
   275  				name: "use_mklink_cmd",
   276  				mklink: func(link, target string) error {
   277  					output, err := osexec.Command("cmd", "/c", "mklink", "/J", link, target).CombinedOutput()
   278  					if err != nil {
   279  						t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
   280  					}
   281  					return nil
   282  				},
   283  			},
   284  		)
   285  	} else {
   286  		t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory junctions`)
   287  	}
   288  	testDirLinks(t, tests)
   289  }
   290  
   291  func enableCurrentThreadPrivilege(privilegeName string) error {
   292  	ct, err := windows.GetCurrentThread()
   293  	if err != nil {
   294  		return err
   295  	}
   296  	var t syscall.Token
   297  	err = windows.OpenThreadToken(ct, syscall.TOKEN_QUERY|windows.TOKEN_ADJUST_PRIVILEGES, false, &t)
   298  	if err != nil {
   299  		return err
   300  	}
   301  	defer syscall.CloseHandle(syscall.Handle(t))
   302  
   303  	var tp windows.TOKEN_PRIVILEGES
   304  
   305  	privStr, err := syscall.UTF16PtrFromString(privilegeName)
   306  	if err != nil {
   307  		return err
   308  	}
   309  	err = windows.LookupPrivilegeValue(nil, privStr, &tp.Privileges[0].Luid)
   310  	if err != nil {
   311  		return err
   312  	}
   313  	tp.PrivilegeCount = 1
   314  	tp.Privileges[0].Attributes = windows.SE_PRIVILEGE_ENABLED
   315  	return windows.AdjustTokenPrivileges(t, false, &tp, 0, nil, nil)
   316  }
   317  
   318  func createSymbolicLink(link string, target *reparseData, isrelative bool) error {
   319  	var buf *windows.SymbolicLinkReparseBuffer
   320  	buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen() // see ReparseDataLength documentation
   321  	byteblob := make([]byte, buflen)
   322  	buf = (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&byteblob[0]))
   323  	buf.SubstituteNameOffset = target.substituteName.offset
   324  	buf.SubstituteNameLength = target.substituteName.length
   325  	buf.PrintNameOffset = target.printName.offset
   326  	buf.PrintNameLength = target.printName.length
   327  	if isrelative {
   328  		buf.Flags = windows.SYMLINK_FLAG_RELATIVE
   329  	}
   330  	copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:], target.pathBuf)
   331  
   332  	var rdb _REPARSE_DATA_BUFFER
   333  	rdb.header.ReparseTag = syscall.IO_REPARSE_TAG_SYMLINK
   334  	rdb.header.ReparseDataLength = buflen
   335  	copy(rdb.detail[:], byteblob)
   336  
   337  	return createDirLink(link, &rdb)
   338  }
   339  
   340  func TestDirectorySymbolicLink(t *testing.T) {
   341  	var tests []dirLinkTest
   342  	output, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output()
   343  	mklinkSupportsDirectorySymbolicLinks := strings.Contains(string(output), " /D ")
   344  	if mklinkSupportsDirectorySymbolicLinks {
   345  		tests = append(tests,
   346  			dirLinkTest{
   347  				name: "use_mklink_cmd",
   348  				mklink: func(link, target string) error {
   349  					output, err := osexec.Command("cmd", "/c", "mklink", "/D", link, target).CombinedOutput()
   350  					if err != nil {
   351  						t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
   352  					}
   353  					return nil
   354  				},
   355  			},
   356  		)
   357  	} else {
   358  		t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory symbolic links`)
   359  	}
   360  
   361  	// The rest of these test requires SeCreateSymbolicLinkPrivilege to be held.
   362  	runtime.LockOSThread()
   363  	defer runtime.UnlockOSThread()
   364  
   365  	err := windows.ImpersonateSelf(windows.SecurityImpersonation)
   366  	if err != nil {
   367  		t.Fatal(err)
   368  	}
   369  	defer windows.RevertToSelf()
   370  
   371  	err = enableCurrentThreadPrivilege("SeCreateSymbolicLinkPrivilege")
   372  	if err != nil {
   373  		t.Skipf(`skipping some tests, could not enable "SeCreateSymbolicLinkPrivilege": %v`, err)
   374  	}
   375  	tests = append(tests,
   376  		dirLinkTest{
   377  			name: "use_os_pkg",
   378  			mklink: func(link, target string) error {
   379  				return os.Symlink(target, link)
   380  			},
   381  		},
   382  		dirLinkTest{
   383  			// Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
   384  			name: "standard",
   385  			mklink: func(link, target string) error {
   386  				var t reparseData
   387  				t.addPrintName(target)
   388  				t.addSubstituteName(`\??\` + target)
   389  				return createSymbolicLink(link, &t, false)
   390  			},
   391  		},
   392  		dirLinkTest{
   393  			name: "relative",
   394  			mklink: func(link, target string) error {
   395  				var t reparseData
   396  				t.addSubstituteNameNoNUL(filepath.Base(target))
   397  				t.addPrintNameNoNUL(filepath.Base(target))
   398  				return createSymbolicLink(link, &t, true)
   399  			},
   400  		},
   401  	)
   402  	testDirLinks(t, tests)
   403  }
   404  
   405  func TestNetworkSymbolicLink(t *testing.T) {
   406  	testenv.MustHaveSymlink(t)
   407  
   408  	dir, err := ioutil.TempDir("", "TestNetworkSymbolicLink")
   409  	if err != nil {
   410  		t.Fatal(err)
   411  	}
   412  	defer os.RemoveAll(dir)
   413  
   414  	oldwd, err := os.Getwd()
   415  	if err != nil {
   416  		t.Fatal(err)
   417  	}
   418  	err = os.Chdir(dir)
   419  	if err != nil {
   420  		t.Fatal(err)
   421  	}
   422  	defer os.Chdir(oldwd)
   423  
   424  	shareName := "GoSymbolicLinkTestShare" // hope no conflictions
   425  	sharePath := filepath.Join(dir, shareName)
   426  	testDir := "TestDir"
   427  
   428  	err = os.MkdirAll(filepath.Join(sharePath, testDir), 0777)
   429  	if err != nil {
   430  		t.Fatal(err)
   431  	}
   432  
   433  	wShareName, err := syscall.UTF16PtrFromString(shareName)
   434  	if err != nil {
   435  		t.Fatal(err)
   436  	}
   437  	wSharePath, err := syscall.UTF16PtrFromString(sharePath)
   438  	if err != nil {
   439  		t.Fatal(err)
   440  	}
   441  
   442  	p := windows.SHARE_INFO_2{
   443  		Netname:     wShareName,
   444  		Type:        windows.STYPE_DISKTREE,
   445  		Remark:      nil,
   446  		Permissions: 0,
   447  		MaxUses:     1,
   448  		CurrentUses: 0,
   449  		Path:        wSharePath,
   450  		Passwd:      nil,
   451  	}
   452  
   453  	err = windows.NetShareAdd(nil, 2, (*byte)(unsafe.Pointer(&p)), nil)
   454  	if err != nil {
   455  		if err == syscall.ERROR_ACCESS_DENIED {
   456  			t.Skip("you don't have enough privileges to add network share")
   457  		}
   458  		t.Fatal(err)
   459  	}
   460  	defer func() {
   461  		err := windows.NetShareDel(nil, wShareName, 0)
   462  		if err != nil {
   463  			t.Fatal(err)
   464  		}
   465  	}()
   466  
   467  	UNCPath := `\\localhost\` + shareName + `\`
   468  
   469  	fi1, err := os.Stat(sharePath)
   470  	if err != nil {
   471  		t.Fatal(err)
   472  	}
   473  	fi2, err := os.Stat(UNCPath)
   474  	if err != nil {
   475  		t.Fatal(err)
   476  	}
   477  	if !os.SameFile(fi1, fi2) {
   478  		t.Fatalf("%q and %q should be the same directory, but not", sharePath, UNCPath)
   479  	}
   480  
   481  	target := filepath.Join(UNCPath, testDir)
   482  	link := "link"
   483  
   484  	err = os.Symlink(target, link)
   485  	if err != nil {
   486  		t.Fatal(err)
   487  	}
   488  	defer os.Remove(link)
   489  
   490  	got, err := os.Readlink(link)
   491  	if err != nil {
   492  		t.Fatal(err)
   493  	}
   494  
   495  	if got != target {
   496  		t.Errorf(`os.Readlink("%s"): got %v, want %v`, link, got, target)
   497  	}
   498  }
   499  
   500  func TestStartProcessAttr(t *testing.T) {
   501  	p, err := os.StartProcess(os.Getenv("COMSPEC"), []string{"/c", "cd"}, new(os.ProcAttr))
   502  	if err != nil {
   503  		return
   504  	}
   505  	defer p.Wait()
   506  	t.Fatalf("StartProcess expected to fail, but succeeded.")
   507  }
   508  
   509  func TestShareNotExistError(t *testing.T) {
   510  	if testing.Short() {
   511  		t.Skip("slow test that uses network; skipping")
   512  	}
   513  	_, err := os.Stat(`\\no_such_server\no_such_share\no_such_file`)
   514  	if err == nil {
   515  		t.Fatal("stat succeeded, but expected to fail")
   516  	}
   517  	if !os.IsNotExist(err) {
   518  		t.Fatalf("os.Stat failed with %q, but os.IsNotExist(err) is false", err)
   519  	}
   520  }
   521  
   522  func TestBadNetPathError(t *testing.T) {
   523  	const ERROR_BAD_NETPATH = syscall.Errno(53)
   524  	if !os.IsNotExist(ERROR_BAD_NETPATH) {
   525  		t.Fatal("os.IsNotExist(syscall.Errno(53)) is false, but want true")
   526  	}
   527  }
   528  
   529  func TestStatDir(t *testing.T) {
   530  	defer chtmpdir(t)()
   531  
   532  	f, err := os.Open(".")
   533  	if err != nil {
   534  		t.Fatal(err)
   535  	}
   536  	defer f.Close()
   537  
   538  	fi, err := f.Stat()
   539  	if err != nil {
   540  		t.Fatal(err)
   541  	}
   542  
   543  	err = os.Chdir("..")
   544  	if err != nil {
   545  		t.Fatal(err)
   546  	}
   547  
   548  	fi2, err := f.Stat()
   549  	if err != nil {
   550  		t.Fatal(err)
   551  	}
   552  
   553  	if !os.SameFile(fi, fi2) {
   554  		t.Fatal("race condition occurred")
   555  	}
   556  }
   557  
   558  func TestOpenVolumeName(t *testing.T) {
   559  	tmpdir, err := ioutil.TempDir("", "TestOpenVolumeName")
   560  	if err != nil {
   561  		t.Fatal(err)
   562  	}
   563  	defer os.RemoveAll(tmpdir)
   564  
   565  	wd, err := os.Getwd()
   566  	if err != nil {
   567  		t.Fatal(err)
   568  	}
   569  	err = os.Chdir(tmpdir)
   570  	if err != nil {
   571  		t.Fatal(err)
   572  	}
   573  	defer os.Chdir(wd)
   574  
   575  	want := []string{"file1", "file2", "file3", "gopher.txt"}
   576  	sort.Strings(want)
   577  	for _, name := range want {
   578  		err := ioutil.WriteFile(filepath.Join(tmpdir, name), nil, 0777)
   579  		if err != nil {
   580  			t.Fatal(err)
   581  		}
   582  	}
   583  
   584  	f, err := os.Open(filepath.VolumeName(tmpdir))
   585  	if err != nil {
   586  		t.Fatal(err)
   587  	}
   588  	defer f.Close()
   589  
   590  	have, err := f.Readdirnames(-1)
   591  	if err != nil {
   592  		t.Fatal(err)
   593  	}
   594  	sort.Strings(have)
   595  
   596  	if strings.Join(want, "/") != strings.Join(have, "/") {
   597  		t.Fatalf("unexpected file list %q, want %q", have, want)
   598  	}
   599  }
   600  
   601  func TestDeleteReadOnly(t *testing.T) {
   602  	tmpdir, err := ioutil.TempDir("", "TestDeleteReadOnly")
   603  	if err != nil {
   604  		t.Fatal(err)
   605  	}
   606  	defer os.RemoveAll(tmpdir)
   607  	p := filepath.Join(tmpdir, "a")
   608  	// This sets FILE_ATTRIBUTE_READONLY.
   609  	f, err := os.OpenFile(p, os.O_CREATE, 0400)
   610  	if err != nil {
   611  		t.Fatal(err)
   612  	}
   613  	f.Close()
   614  
   615  	if err = os.Chmod(p, 0400); err != nil {
   616  		t.Fatal(err)
   617  	}
   618  	if err = os.Remove(p); err != nil {
   619  		t.Fatal(err)
   620  	}
   621  }
   622  
   623  func TestStatSymlinkLoop(t *testing.T) {
   624  	testenv.MustHaveSymlink(t)
   625  
   626  	defer chtmpdir(t)()
   627  
   628  	err := os.Symlink("x", "y")
   629  	if err != nil {
   630  		t.Fatal(err)
   631  	}
   632  	defer os.Remove("y")
   633  
   634  	err = os.Symlink("y", "x")
   635  	if err != nil {
   636  		t.Fatal(err)
   637  	}
   638  	defer os.Remove("x")
   639  
   640  	_, err = os.Stat("x")
   641  	if perr, ok := err.(*os.PathError); !ok || perr.Err != syscall.ELOOP {
   642  		t.Errorf("expected *PathError with ELOOP, got %T: %v\n", err, err)
   643  	}
   644  }
   645  
   646  func TestReadStdin(t *testing.T) {
   647  	old := poll.ReadConsole
   648  	defer func() {
   649  		poll.ReadConsole = old
   650  	}()
   651  
   652  	testConsole := os.NewConsoleFile(syscall.Stdin, "test")
   653  
   654  	var tests = []string{
   655  		"abc",
   656  		"äöü",
   657  		"\u3042",
   658  		"“hi”™",
   659  		"hello\x1aworld",
   660  		"\U0001F648\U0001F649\U0001F64A",
   661  	}
   662  
   663  	for _, consoleSize := range []int{1, 2, 3, 10, 16, 100, 1000} {
   664  		for _, readSize := range []int{1, 2, 3, 4, 5, 8, 10, 16, 20, 50, 100} {
   665  			for _, s := range tests {
   666  				t.Run(fmt.Sprintf("c%d/r%d/%s", consoleSize, readSize, s), func(t *testing.T) {
   667  					s16 := utf16.Encode([]rune(s))
   668  					poll.ReadConsole = func(h syscall.Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) error {
   669  						if inputControl != nil {
   670  							t.Fatalf("inputControl not nil")
   671  						}
   672  						n := int(toread)
   673  						if n > consoleSize {
   674  							n = consoleSize
   675  						}
   676  						n = copy((*[10000]uint16)(unsafe.Pointer(buf))[:n], s16)
   677  						s16 = s16[n:]
   678  						*read = uint32(n)
   679  						t.Logf("read %d -> %d", toread, *read)
   680  						return nil
   681  					}
   682  
   683  					var all []string
   684  					var buf []byte
   685  					chunk := make([]byte, readSize)
   686  					for {
   687  						n, err := testConsole.Read(chunk)
   688  						buf = append(buf, chunk[:n]...)
   689  						if err == io.EOF {
   690  							all = append(all, string(buf))
   691  							if len(all) >= 5 {
   692  								break
   693  							}
   694  							buf = buf[:0]
   695  						} else if err != nil {
   696  							t.Fatalf("reading %q: error: %v", s, err)
   697  						}
   698  						if len(buf) >= 2000 {
   699  							t.Fatalf("reading %q: stuck in loop: %q", s, buf)
   700  						}
   701  					}
   702  
   703  					want := strings.Split(s, "\x1a")
   704  					for len(want) < 5 {
   705  						want = append(want, "")
   706  					}
   707  					if !reflect.DeepEqual(all, want) {
   708  						t.Errorf("reading %q:\nhave %x\nwant %x", s, all, want)
   709  					}
   710  				})
   711  			}
   712  		}
   713  	}
   714  }
   715  
   716  func TestStatPagefile(t *testing.T) {
   717  	_, err := os.Stat(`c:\pagefile.sys`)
   718  	if err == nil {
   719  		return
   720  	}
   721  	if os.IsNotExist(err) {
   722  		t.Skip(`skipping because c:\pagefile.sys is not found`)
   723  	}
   724  	t.Fatal(err)
   725  }