diff options
Diffstat (limited to 'libgo/go/os/os_test.go')
-rw-r--r-- | libgo/go/os/os_test.go | 379 |
1 files changed, 354 insertions, 25 deletions
diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go index dcc8d762bf8..0f1617ad5d0 100644 --- a/libgo/go/os/os_test.go +++ b/libgo/go/os/os_test.go @@ -17,6 +17,7 @@ import ( "path/filepath" "reflect" "runtime" + "runtime/debug" "sort" "strings" "sync" @@ -52,15 +53,12 @@ var sysdir = func() *sysDir { case "darwin": switch runtime.GOARCH { case "arm", "arm64": - /// At this point the test harness has not had a chance - // to move us into the ./src/os directory, so the - // current working directory is the root of the app. wd, err := syscall.Getwd() if err != nil { wd = err.Error() } return &sysDir{ - wd, + filepath.Join(wd, "..", ".."), []string{ "ResourceRules.plist", "Info.plist", @@ -110,7 +108,7 @@ func size(name string, t *testing.T) int64 { break } if e != nil { - t.Fatal("read failed:", err) + t.Fatal("read failed:", e) } } return int64(len) @@ -174,6 +172,45 @@ func TestStat(t *testing.T) { } } +func TestStatError(t *testing.T) { + defer chtmpdir(t)() + + path := "no-such-file" + Remove(path) // Just in case + + fi, err := Stat(path) + if err == nil { + t.Fatal("got nil, want error") + } + if fi != nil { + t.Errorf("got %v, want nil", fi) + } + if perr, ok := err.(*PathError); !ok { + t.Errorf("got %T, want %T", err, perr) + } + + testenv.MustHaveSymlink(t) + + link := "symlink" + Remove(link) // Just in case + err = Symlink(path, link) + if err != nil { + t.Fatal(err) + } + defer Remove(link) + + fi, err = Stat(link) + if err == nil { + t.Fatal("got nil, want error") + } + if fi != nil { + t.Errorf("got %v, want nil", fi) + } + if perr, ok := err.(*PathError); !ok { + t.Errorf("got %T, want %T", err, perr) + } +} + func TestFstat(t *testing.T) { path := sfdir + "/" + sfname file, err1 := Open(path) @@ -359,6 +396,50 @@ func BenchmarkReaddir(b *testing.B) { benchmarkReaddir(".", b) } +func benchmarkStat(b *testing.B, path string) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := Stat(path) + if err != nil { + b.Fatalf("Stat(%q) failed: %v", path, err) + } + } +} + +func benchmarkLstat(b *testing.B, path string) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := Lstat(path) + if err != nil { + b.Fatalf("Lstat(%q) failed: %v", path, err) + } + } +} + +func BenchmarkStatDot(b *testing.B) { + benchmarkStat(b, ".") +} + +func BenchmarkStatFile(b *testing.B) { + benchmarkStat(b, filepath.Join(runtime.GOROOT(), "src/os/os_test.go")) +} + +func BenchmarkStatDir(b *testing.B) { + benchmarkStat(b, filepath.Join(runtime.GOROOT(), "src/os")) +} + +func BenchmarkLstatDot(b *testing.B) { + benchmarkLstat(b, ".") +} + +func BenchmarkLstatFile(b *testing.B) { + benchmarkLstat(b, filepath.Join(runtime.GOROOT(), "src/os/os_test.go")) +} + +func BenchmarkLstatDir(b *testing.B) { + benchmarkLstat(b, filepath.Join(runtime.GOROOT(), "src/os")) +} + // Read the directory one entry at a time. func smallReaddirnames(file *File, length int, t *testing.T) []string { names := make([]string, length) @@ -673,55 +754,58 @@ func TestSymlink(t *testing.T) { Remove(from) // Just in case. file, err := Create(to) if err != nil { - t.Fatalf("open %q failed: %v", to, err) + t.Fatalf("Create(%q) failed: %v", to, err) } defer Remove(to) if err = file.Close(); err != nil { - t.Errorf("close %q failed: %v", to, err) + t.Errorf("Close(%q) failed: %v", to, err) } err = Symlink(to, from) if err != nil { - t.Fatalf("symlink %q, %q failed: %v", to, from, err) + t.Fatalf("Symlink(%q, %q) failed: %v", to, from, err) } defer Remove(from) tostat, err := Lstat(to) if err != nil { - t.Fatalf("stat %q failed: %v", to, err) + t.Fatalf("Lstat(%q) failed: %v", to, err) } if tostat.Mode()&ModeSymlink != 0 { - t.Fatalf("stat %q claims to have found a symlink", to) + t.Fatalf("Lstat(%q).Mode()&ModeSymlink = %v, want 0", to, tostat.Mode()&ModeSymlink) } fromstat, err := Stat(from) if err != nil { - t.Fatalf("stat %q failed: %v", from, err) + t.Fatalf("Stat(%q) failed: %v", from, err) } if !SameFile(tostat, fromstat) { - t.Errorf("symlink %q, %q did not create symlink", to, from) + t.Errorf("Symlink(%q, %q) did not create symlink", to, from) } fromstat, err = Lstat(from) if err != nil { - t.Fatalf("lstat %q failed: %v", from, err) + t.Fatalf("Lstat(%q) failed: %v", from, err) } if fromstat.Mode()&ModeSymlink == 0 { - t.Fatalf("symlink %q, %q did not create symlink", to, from) + t.Fatalf("Lstat(%q).Mode()&ModeSymlink = 0, want %v", from, ModeSymlink) } fromstat, err = Stat(from) if err != nil { - t.Fatalf("stat %q failed: %v", from, err) + t.Fatalf("Stat(%q) failed: %v", from, err) + } + if fromstat.Name() != from { + t.Errorf("Stat(%q).Name() = %q, want %q", from, fromstat.Name(), from) } if fromstat.Mode()&ModeSymlink != 0 { - t.Fatalf("stat %q did not follow symlink", from) + t.Fatalf("Stat(%q).Mode()&ModeSymlink = %v, want 0", from, fromstat.Mode()&ModeSymlink) } s, err := Readlink(from) if err != nil { - t.Fatalf("readlink %q failed: %v", from, err) + t.Fatalf("Readlink(%q) failed: %v", from, err) } if s != to { - t.Fatalf("after symlink %q != %q", s, to) + t.Fatalf("Readlink(%q) = %q, want %q", from, s, to) } file, err = Open(from) if err != nil { - t.Fatalf("open %q failed: %v", from, err) + t.Fatalf("Open(%q) failed: %v", from, err) } file.Close() } @@ -844,6 +928,18 @@ func TestRenameFailed(t *testing.T) { } } +func TestRenameNotExisting(t *testing.T) { + defer chtmpdir(t)() + from, to := "doesnt-exist", "dest" + + Mkdir(to, 0777) + defer Remove(to) + + if err := Rename(from, to); !IsNotExist(err) { + t.Errorf("Rename(%q, %q) = %v; want an IsNotExist error", from, to, err) + } +} + func TestRenameToDirFailed(t *testing.T) { defer chtmpdir(t)() from, to := "renamefrom", "renameto" @@ -1054,14 +1150,22 @@ func testChtimes(t *testing.T, name string) { } postStat := st - /* Plan 9, NaCl: - Mtime is the time of the last change of content. Similarly, atime is set whenever the - contents are accessed; also, it is set whenever mtime is set. - */ pat := Atime(postStat) pmt := postStat.ModTime() - if !pat.Before(at) && runtime.GOOS != "plan9" && runtime.GOOS != "nacl" { - t.Errorf("AccessTime didn't go backwards; was=%d, after=%d", at, pat) + if !pat.Before(at) { + switch runtime.GOOS { + case "plan9", "nacl": + // Ignore. + // Plan 9, NaCl: + // Mtime is the time of the last change of + // content. Similarly, atime is set whenever + // the contents are accessed; also, it is set + // whenever mtime is set. + case "netbsd": + t.Logf("AccessTime didn't go backwards; was=%d, after=%d (Ignoring. See NetBSD issue golang.org/issue/19293)", at, pat) + default: + t.Errorf("AccessTime didn't go backwards; was=%d, after=%d", at, pat) + } } if !pmt.Before(mt) { @@ -1239,6 +1343,32 @@ func TestSeek(t *testing.T) { } } +func TestSeekError(t *testing.T) { + switch runtime.GOOS { + case "plan9", "nacl": + t.Skipf("skipping test on %v", runtime.GOOS) + } + + r, w, err := Pipe() + if err != nil { + t.Fatal(err) + } + _, err = r.Seek(0, 0) + if err == nil { + t.Fatal("Seek on pipe should fail") + } + if perr, ok := err.(*PathError); !ok || perr.Err != syscall.ESPIPE { + t.Errorf("Seek returned error %v, want &PathError{Err: syscall.ESPIPE}", err) + } + _, err = w.Seek(0, 0) + if err == nil { + t.Fatal("Seek on pipe should fail") + } + if perr, ok := err.(*PathError); !ok || perr.Err != syscall.ESPIPE { + t.Errorf("Seek returned error %v, want &PathError{Err: syscall.ESPIPE}", err) + } +} + type openErrorTest struct { path string mode int @@ -1443,6 +1573,26 @@ func TestReadAtOffset(t *testing.T) { } } +// Verify that ReadAt doesn't allow negative offset. +func TestReadAtNegativeOffset(t *testing.T) { + f := newFile("TestReadAtNegativeOffset", t) + defer Remove(f.Name()) + defer f.Close() + + const data = "hello, world\n" + io.WriteString(f, data) + + f.Seek(0, 0) + b := make([]byte, 5) + + n, err := f.ReadAt(b, -10) + + const wantsub = "negative offset" + if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 { + t.Errorf("ReadAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub) + } +} + func TestWriteAt(t *testing.T) { f := newFile("TestWriteAt", t) defer Remove(f.Name()) @@ -1465,6 +1615,20 @@ func TestWriteAt(t *testing.T) { } } +// Verify that WriteAt doesn't allow negative offset. +func TestWriteAtNegativeOffset(t *testing.T) { + f := newFile("TestWriteAtNegativeOffset", t) + defer Remove(f.Name()) + defer f.Close() + + n, err := f.WriteAt([]byte("WORLD"), -10) + + const wantsub = "negative offset" + if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 { + t.Errorf("WriteAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub) + } +} + func writeFile(t *testing.T, fname string, flag int, text string) string { f, err := OpenFile(fname, flag, 0666) if err != nil { @@ -1667,6 +1831,17 @@ func TestStatStdin(t *testing.T) { Exit(0) } + fi, err := Stdin.Stat() + if err != nil { + t.Fatal(err) + } + switch mode := fi.Mode(); { + case mode&ModeCharDevice != 0: + case mode&ModeNamedPipe != 0: + default: + t.Fatalf("unexpected Stdin mode (%v), want ModeCharDevice or ModeNamedPipe", mode) + } + var cmd *osexec.Cmd if runtime.GOOS == "windows" { cmd = osexec.Command("cmd", "/c", "echo output | "+Args[0]+" -test.run=TestStatStdin") @@ -1686,6 +1861,60 @@ func TestStatStdin(t *testing.T) { } } +func TestStatRelativeSymlink(t *testing.T) { + testenv.MustHaveSymlink(t) + + tmpdir, err := ioutil.TempDir("", "TestStatRelativeSymlink") + if err != nil { + t.Fatal(err) + } + defer RemoveAll(tmpdir) + + target := filepath.Join(tmpdir, "target") + f, err := Create(target) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + st, err := f.Stat() + if err != nil { + t.Fatal(err) + } + + link := filepath.Join(tmpdir, "link") + err = Symlink(filepath.Base(target), link) + if err != nil { + t.Fatal(err) + } + + st1, err := Stat(link) + if err != nil { + t.Fatal(err) + } + + if !SameFile(st, st1) { + t.Error("Stat doesn't follow relative symlink") + } + + if runtime.GOOS == "windows" { + Remove(link) + err = Symlink(target[len(filepath.VolumeName(target)):], link) + if err != nil { + t.Fatal(err) + } + + st1, err := Stat(link) + if err != nil { + t.Fatal(err) + } + + if !SameFile(st, st1) { + t.Error("Stat doesn't follow relative symlink") + } + } +} + func TestReadAtEOF(t *testing.T) { f := newFile("TestReadAtEOF", t) defer Remove(f.Name()) @@ -1759,6 +1988,10 @@ func TestLongPath(t *testing.T) { if dir.Size() != filesize || filesize != wantSize { t.Errorf("Size(%q) is %d, len(ReadFile()) is %d, want %d", path, dir.Size(), filesize, wantSize) } + err = Chmod(path, dir.Mode()) + if err != nil { + t.Fatalf("Chmod(%q) failed: %v", path, err) + } } if err := Truncate(sizedTempDir+"/bar.txt", 0); err != nil { t.Fatalf("Truncate failed: %v", err) @@ -1927,3 +2160,99 @@ func TestRemoveAllRace(t *testing.T) { close(hold) // let workers race to remove root wg.Wait() } + +// Test that reading from a pipe doesn't use up a thread. +func TestPipeThreads(t *testing.T) { + switch runtime.GOOS { + case "freebsd": + t.Skip("skipping on FreeBSD; issue 19093") + case "solaris": + t.Skip("skipping on Solaris; issue 19111") + case "windows": + t.Skip("skipping on Windows; issue 19098") + case "plan9": + t.Skip("skipping on Plan 9; does not support runtime poller") + } + + threads := 100 + + // OpenBSD has a low default for max number of files. + if runtime.GOOS == "openbsd" { + threads = 50 + } + + r := make([]*File, threads) + w := make([]*File, threads) + for i := 0; i < threads; i++ { + rp, wp, err := Pipe() + if err != nil { + for j := 0; j < i; j++ { + r[j].Close() + w[j].Close() + } + t.Fatal(err) + } + r[i] = rp + w[i] = wp + } + + defer debug.SetMaxThreads(debug.SetMaxThreads(threads / 2)) + + var wg sync.WaitGroup + wg.Add(threads) + c := make(chan bool, threads) + for i := 0; i < threads; i++ { + go func(i int) { + defer wg.Done() + var b [1]byte + c <- true + if _, err := r[i].Read(b[:]); err != nil { + t.Error(err) + } + }(i) + } + + for i := 0; i < threads; i++ { + <-c + } + + // If we are still alive, it means that the 100 goroutines did + // not require 100 threads. + + for i := 0; i < threads; i++ { + if _, err := w[i].Write([]byte{0}); err != nil { + t.Error(err) + } + if err := w[i].Close(); err != nil { + t.Error(err) + } + } + + wg.Wait() + + for i := 0; i < threads; i++ { + if err := r[i].Close(); err != nil { + t.Error(err) + } + } +} + +func TestDoubleCloseError(t *testing.T) { + path := sfdir + "/" + sfname + file, err := Open(path) + if err != nil { + t.Fatal(err) + } + if err := file.Close(); err != nil { + t.Fatalf("unexpected error from Close: %v", err) + } + if err := file.Close(); err == nil { + t.Error("second Close did not fail") + } else if pe, ok := err.(*PathError); !ok { + t.Errorf("second Close returned unexpected error type %T; expected os.PathError", pe) + } else if pe.Err != ErrClosed { + t.Errorf("second Close returned %q, wanted %q", err, ErrClosed) + } else { + t.Logf("second close returned expected error %q", err) + } +} |