os: pull in os.Rename and some of its tests from upstream

Skip part of the test on Windows because of https://github.com/tinygo-org/tinygo/issues/2480

TODO: fix 2480 and unskip test
TODO: pull in rest of upstream tests, fix problems they find

Requested in https://github.com/tinygo-org/tinygo/issues/2109
Этот коммит содержится в:
Dan Kegel 2022-01-03 10:52:41 -08:00 коммит произвёл Ron Evans
родитель 72e15af1fa
коммит 29f7ebc63e
5 изменённых файлов: 127 добавлений и 1 удалений

Просмотреть файл

@ -40,6 +40,14 @@ func Chdir(dir string) error {
return nil
}
// Rename renames (moves) oldpath to newpath.
// If newpath already exists and is not a directory, Rename replaces it.
// OS-specific restrictions may apply when oldpath and newpath are in different directories.
// If there is an error, it will be of type *LinkError.
func Rename(oldpath, newpath string) error {
return rename(oldpath, newpath)
}
// unixFilesystem is an empty handle for a Unix/Linux filesystem. All operations
// are relative to the current working directory.
type unixFilesystem struct {

Просмотреть файл

@ -18,6 +18,15 @@ func fixLongPath(path string) string {
return path
}
func rename(oldname, newname string) error {
// TODO: import rest of upstream tests, handle fancy cases
err := syscall.Rename(oldname, newname)
if err != nil {
return &LinkError{"rename", oldname, newname, err}
}
return nil
}
func Pipe() (r *File, w *File, err error) {
var p [2]int
err = handleSyscallError(syscall.Pipe2(p[:], syscall.O_CLOEXEC))

Просмотреть файл

@ -8,12 +8,21 @@
package os
import (
"internal/syscall/windows"
"syscall"
"unicode/utf16"
)
type syscallFd = syscall.Handle
func rename(oldname, newname string) error {
e := windows.Rename(fixLongPath(oldname), fixLongPath(newname))
if e != nil {
return &LinkError{"rename", oldname, newname, e}
}
return nil
}
func Pipe() (r *File, w *File, err error) {
var p [2]syscall.Handle
e := handleSyscallError(syscall.Pipe(p[:]))

Просмотреть файл

@ -3,6 +3,7 @@
package os_test
import (
"io/ioutil"
. "os"
"path/filepath"
"runtime"
@ -35,7 +36,7 @@ func TestMkdir(t *testing.T) {
func TestStatBadDir(t *testing.T) {
if runtime.GOOS == "windows" {
t.Log("TODO: TestStatBadDir fails on Windows, skipping")
t.Log("TODO: TestStatBadDir: IsNotExist fails on Windows, skipping")
return
}
dir := TempDir()
@ -94,3 +95,88 @@ func TestRemove(t *testing.T) {
t.Fatalf("Remove: %v", err)
}
}
func TestRename(t *testing.T) {
// TODO: use t.TempDir()
from, to := TempDir()+"/"+"TestRename-from", TempDir()+"/"+"TestRename-to"
file, err := Create(from)
defer Remove(from) // TODO: switch to t.Tempdir, remove this line
if err != nil {
t.Fatalf("open %q failed: %v", from, err)
}
defer Remove(to) // TODO: switch to t.Tempdir, remove this line
if err = file.Close(); err != nil {
t.Errorf("close %q failed: %v", from, err)
}
err = Rename(from, to)
if err != nil {
t.Fatalf("rename %q, %q failed: %v", to, from, err)
}
_, err = Stat(to)
if err != nil {
t.Errorf("stat %q failed: %v", to, err)
}
}
func TestRenameOverwriteDest(t *testing.T) {
from, to := TempDir()+"/"+"TestRenameOverwrite-from", TempDir()+"/"+"TestRenameOverwrite-to"
toData := []byte("to")
fromData := []byte("from")
err := ioutil.WriteFile(to, toData, 0777)
defer Remove(to) // TODO: switch to t.Tempdir, remove this line
if err != nil {
t.Fatalf("write file %q failed: %v", to, err)
}
err = ioutil.WriteFile(from, fromData, 0777)
defer Remove(from) // TODO: switch to t.Tempdir, remove this line
if err != nil {
t.Fatalf("write file %q failed: %v", from, err)
}
err = Rename(from, to)
if err != nil {
t.Fatalf("rename %q, %q failed: %v", to, from, err)
}
_, err = Stat(from)
if err == nil {
t.Errorf("from file %q still exists", from)
}
if runtime.GOOS == "windows" {
t.Log("TODO: TestRenameOverwriteDest: IsNotExist fails on Windows, skipping")
} else if err != nil && !IsNotExist(err) {
t.Fatalf("stat from: %v", err)
}
toFi, err := Stat(to)
if err != nil {
t.Fatalf("stat %q failed: %v", to, err)
}
if toFi.Size() != int64(len(fromData)) {
t.Errorf(`"to" size = %d; want %d (old "from" size)`, toFi.Size(), len(fromData))
}
}
func TestRenameFailed(t *testing.T) {
from, to := TempDir()+"/"+"RenameFailed-from", TempDir()+"/"+"RenameFailed-to"
err := Rename(from, to)
switch err := err.(type) {
case *LinkError:
if err.Op != "rename" {
t.Errorf("rename %q, %q: err.Op: want %q, got %q", from, to, "rename", err.Op)
}
if err.Old != from {
t.Errorf("rename %q, %q: err.Old: want %q, got %q", from, to, from, err.Old)
}
if err.New != to {
t.Errorf("rename %q, %q: err.New: want %q, got %q", from, to, to, err.New)
}
case nil:
t.Errorf("rename %q, %q: expected error, got nil", from, to)
default:
t.Errorf("rename %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err)
}
}

Просмотреть файл

@ -95,6 +95,16 @@ func Rmdir(path string) (err error) {
return
}
func Rename(from, to string) (err error) {
fromdata := cstring(from)
todata := cstring(to)
fail := int(libc_rename(&fromdata[0], &todata[0]))
if fail < 0 {
err = getErrno()
}
return
}
func Unlink(path string) (err error) {
data := cstring(path)
fail := int(libc_unlink(&data[0]))
@ -270,6 +280,10 @@ func libc_mkdir(pathname *byte, mode uint32) int32
//export rmdir
func libc_rmdir(pathname *byte) int32
// int rename(const char *from, *to);
//export rename
func libc_rename(from, too *byte) int32
// int unlink(const char *pathname);
//export unlink
func libc_unlink(pathname *byte) int32