os: add a few upstream tests for Read and ReadAt, fix problems they exposed.
There are more upstream tests to pull in, but this is plenty for today.
Этот коммит содержится в:
родитель
1e2791b216
коммит
61be9189f1
4 изменённых файлов: 148 добавлений и 4 удалений
|
@ -101,6 +101,7 @@ func Create(name string) (*File, error) {
|
|||
// read and any error encountered. At end of file, Read returns 0, io.EOF.
|
||||
func (f *File) Read(b []byte) (n int, err error) {
|
||||
n, err = f.handle.Read(b)
|
||||
// TODO: want to always wrap, like upstream, but ReadFile() compares against exactly io.EOF?
|
||||
if err != nil && err != io.EOF {
|
||||
err = &PathError{"read", f.name, err}
|
||||
}
|
||||
|
@ -120,8 +121,11 @@ func (f *File) ReadAt(b []byte, offset int64) (n int, err error) {
|
|||
for len(b) > 0 {
|
||||
m, e := f.handle.ReadAt(b, offset)
|
||||
if e != nil {
|
||||
if err != io.EOF {
|
||||
err = &PathError{"readat", f.name, err}
|
||||
// TODO: want to always wrap, like upstream, but TestReadAtEOF compares against exactly io.EOF?
|
||||
if e != io.EOF {
|
||||
err = &PathError{"readat", f.name, e}
|
||||
} else {
|
||||
err = e
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ type unixFileHandle uintptr
|
|||
func (f unixFileHandle) Read(b []byte) (n int, err error) {
|
||||
n, err = syscall.Read(syscallFd(f), b)
|
||||
err = handleSyscallError(err)
|
||||
if n == 0 && err == nil {
|
||||
if n == 0 && len(b) > 0 && err == nil {
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
|
|
|
@ -50,7 +50,7 @@ func tempDir() string {
|
|||
func (f unixFileHandle) ReadAt(b []byte, offset int64) (n int, err error) {
|
||||
n, err = syscall.Pread(syscallFd(f), b, offset)
|
||||
err = handleSyscallError(err)
|
||||
if n == 0 && err == nil {
|
||||
if n == 0 && len(b) > 0 && err == nil {
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"io"
|
||||
. "os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -24,6 +25,57 @@ func newFile(testName string, t *testing.T) (f *File) {
|
|||
return
|
||||
}
|
||||
|
||||
// Read with length 0 should not return EOF.
|
||||
func TestRead0(t *testing.T) {
|
||||
f := newFile("TestRead0", t)
|
||||
defer Remove(f.Name())
|
||||
defer f.Close()
|
||||
|
||||
const data = "hello, world\n"
|
||||
io.WriteString(f, data)
|
||||
f.Close()
|
||||
f, err := Open(f.Name())
|
||||
if err != nil {
|
||||
t.Errorf("failed to reopen")
|
||||
}
|
||||
|
||||
b := make([]byte, 0)
|
||||
n, err := f.Read(b)
|
||||
if n != 0 || err != nil {
|
||||
t.Errorf("Read(0) = %d, %v, want 0, nil", n, err)
|
||||
}
|
||||
b = make([]byte, 5)
|
||||
n, err = f.Read(b)
|
||||
if n <= 0 || err != nil {
|
||||
t.Errorf("Read(5) = %d, %v, want >0, nil", n, err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadAt with length 0 should not return EOF.
|
||||
func TestReadAt0(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Log("TODO: implement Pread for Windows")
|
||||
return
|
||||
}
|
||||
f := newFile("TestReadAt0", t)
|
||||
defer Remove(f.Name())
|
||||
defer f.Close()
|
||||
|
||||
const data = "hello, world\n"
|
||||
io.WriteString(f, data)
|
||||
|
||||
b := make([]byte, 0)
|
||||
n, err := f.ReadAt(b, 0)
|
||||
if n != 0 || err != nil {
|
||||
t.Errorf("ReadAt(0,0) = %d, %v, want 0, nil", n, err)
|
||||
}
|
||||
b = make([]byte, 5)
|
||||
n, err = f.ReadAt(b, 0)
|
||||
if n <= 0 || err != nil {
|
||||
t.Errorf("ReadAt(5,0) = %d, %v, want >0, nil", n, err)
|
||||
}
|
||||
}
|
||||
|
||||
func checkMode(t *testing.T, path string, mode FileMode) {
|
||||
dir, err := Stat(path)
|
||||
if err != nil {
|
||||
|
@ -55,3 +107,91 @@ func TestReadAt(t *testing.T) {
|
|||
t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that ReadAt doesn't affect seek offset.
|
||||
// In the Plan 9 kernel, there used to be a bug in the implementation of
|
||||
// the pread syscall, where the channel offset was erroneously updated after
|
||||
// calling pread on a file.
|
||||
func TestReadAtOffset(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Log("TODO: implement Pread for Windows")
|
||||
return
|
||||
}
|
||||
f := newFile("TestReadAtOffset", t)
|
||||
defer Remove(f.Name())
|
||||
defer f.Close()
|
||||
|
||||
const data = "hello, world\n"
|
||||
io.WriteString(f, data)
|
||||
f.Close()
|
||||
f, err := Open(f.Name())
|
||||
if err != nil {
|
||||
t.Errorf("failed to reopen")
|
||||
}
|
||||
|
||||
b := make([]byte, 5)
|
||||
|
||||
n, err := f.ReadAt(b, 7)
|
||||
if err != nil || n != len(b) {
|
||||
t.Fatalf("ReadAt 7: %d, %v", n, err)
|
||||
}
|
||||
if string(b) != "world" {
|
||||
t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
|
||||
}
|
||||
|
||||
n, err = f.Read(b)
|
||||
if err != nil || n != len(b) {
|
||||
t.Fatalf("Read: %d, %v", n, err)
|
||||
}
|
||||
if string(b) != "hello" {
|
||||
t.Fatalf("Read: have %q want %q", string(b), "hello")
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that ReadAt doesn't allow negative offset.
|
||||
func TestReadAtNegativeOffset(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Log("TODO: implement Pread for Windows")
|
||||
return
|
||||
}
|
||||
f := newFile("TestReadAtNegativeOffset", t)
|
||||
defer Remove(f.Name())
|
||||
defer f.Close()
|
||||
|
||||
const data = "hello, world\n"
|
||||
io.WriteString(f, data)
|
||||
f.Close()
|
||||
f, err := Open(f.Name())
|
||||
if err != nil {
|
||||
t.Errorf("failed to reopen")
|
||||
}
|
||||
|
||||
b := make([]byte, 5)
|
||||
|
||||
n, err := f.ReadAt(b, -10)
|
||||
|
||||
const wantsub = "negative offset"
|
||||
if !strings.Contains(err.Error(), wantsub) || n != 0 {
|
||||
t.Errorf("ReadAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadAtEOF(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Log("TODO: implement Pread for Windows")
|
||||
return
|
||||
}
|
||||
f := newFile("TestReadAtEOF", t)
|
||||
defer Remove(f.Name())
|
||||
defer f.Close()
|
||||
|
||||
_, err := f.ReadAt(make([]byte, 10), 0)
|
||||
switch err {
|
||||
case io.EOF:
|
||||
// all good
|
||||
case nil:
|
||||
t.Fatalf("ReadAt succeeded")
|
||||
default:
|
||||
t.Fatalf("ReadAt failed: %s", err)
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче