mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 05:28:09 +08:00
252 lines
5.7 KiB
Go
252 lines
5.7 KiB
Go
// This makes the open test suite. It tries to open a file (existing
|
|
// or not existing) with all possible file modes and writes a test
|
|
// matrix.
|
|
//
|
|
// The behaviour is as run on Linux, with the small modification that
|
|
// O_TRUNC with O_RDONLY does **not** truncate the file.
|
|
//
|
|
// Run with go generate (defined in vfs.go)
|
|
//
|
|
//go:build none
|
|
|
|
// FIXME include read too?
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/rclone/rclone/lib/file"
|
|
)
|
|
|
|
// Interprets err into a vfs error
|
|
func whichError(err error) string {
|
|
switch err {
|
|
case nil:
|
|
return "nil"
|
|
case io.EOF:
|
|
return "io.EOF"
|
|
case os.ErrInvalid:
|
|
return "EINVAL"
|
|
}
|
|
s := err.Error()
|
|
switch {
|
|
case strings.Contains(s, "no such file or directory"):
|
|
return "ENOENT"
|
|
case strings.Contains(s, "bad file descriptor"):
|
|
return "EBADF"
|
|
case strings.Contains(s, "file exists"):
|
|
return "EEXIST"
|
|
}
|
|
log.Fatalf("Unknown error: %v", err)
|
|
return ""
|
|
}
|
|
|
|
const accessModeMask = (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)
|
|
|
|
// test Opening, reading and writing the file handle with the flags given
|
|
func test(fileName string, flags int, mode string) {
|
|
// first try with file not existing
|
|
_, err := os.Stat(fileName)
|
|
if !os.IsNotExist(err) {
|
|
log.Fatalf("File must not exist")
|
|
}
|
|
f, openNonExistentErr := file.OpenFile(fileName, flags, 0666)
|
|
|
|
var readNonExistentErr error
|
|
var writeNonExistentErr error
|
|
if openNonExistentErr == nil {
|
|
// read some bytes
|
|
buf := []byte{0, 0}
|
|
_, readNonExistentErr = f.Read(buf)
|
|
|
|
// write some bytes
|
|
_, writeNonExistentErr = f.Write([]byte("hello"))
|
|
|
|
// close
|
|
err = f.Close()
|
|
if err != nil {
|
|
log.Fatalf("failed to close: %v", err)
|
|
}
|
|
}
|
|
|
|
// write the file
|
|
f, err = file.Create(fileName)
|
|
if err != nil {
|
|
log.Fatalf("failed to create: %v", err)
|
|
}
|
|
n, err := f.Write([]byte("hello"))
|
|
if n != 5 || err != nil {
|
|
log.Fatalf("failed to write n=%d: %v", n, err)
|
|
}
|
|
// close
|
|
err = f.Close()
|
|
if err != nil {
|
|
log.Fatalf("failed to close: %v", err)
|
|
}
|
|
|
|
// then open file and try with file existing
|
|
|
|
f, openExistingErr := file.OpenFile(fileName, flags, 0666)
|
|
var readExistingErr error
|
|
var writeExistingErr error
|
|
if openExistingErr == nil {
|
|
// read some bytes
|
|
buf := []byte{0, 0}
|
|
_, readExistingErr = f.Read(buf)
|
|
|
|
// write some bytes
|
|
_, writeExistingErr = f.Write([]byte("HEL"))
|
|
|
|
// close
|
|
err = f.Close()
|
|
if err != nil {
|
|
log.Fatalf("failed to close: %v", err)
|
|
}
|
|
}
|
|
|
|
// read the file
|
|
f, err = file.Open(fileName)
|
|
if err != nil {
|
|
log.Fatalf("failed to open: %v", err)
|
|
}
|
|
var buf = make([]byte, 64)
|
|
n, err = f.Read(buf)
|
|
if err != nil && err != io.EOF {
|
|
log.Fatalf("failed to read n=%d: %v", n, err)
|
|
}
|
|
err = f.Close()
|
|
if err != nil {
|
|
log.Fatalf("failed to close: %v", err)
|
|
}
|
|
contents := string(buf[:n])
|
|
|
|
// remove file
|
|
err = os.Remove(fileName)
|
|
if err != nil {
|
|
log.Fatalf("failed to remove: %v", err)
|
|
}
|
|
|
|
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/open.html
|
|
// The result of using O_TRUNC with O_RDONLY is undefined.
|
|
// Linux seems to truncate the file, but we prefer to return EINVAL
|
|
if (flags&accessModeMask) == os.O_RDONLY && flags&os.O_TRUNC != 0 {
|
|
openNonExistentErr = os.ErrInvalid // EINVAL
|
|
readNonExistentErr = nil
|
|
writeNonExistentErr = nil
|
|
openExistingErr = os.ErrInvalid // EINVAL
|
|
readExistingErr = nil
|
|
writeExistingErr = nil
|
|
contents = "hello"
|
|
}
|
|
|
|
// output the struct
|
|
fmt.Printf(`{
|
|
flags: %s,
|
|
what: %q,
|
|
openNonExistentErr: %s,
|
|
readNonExistentErr: %s,
|
|
writeNonExistentErr: %s,
|
|
openExistingErr: %s,
|
|
readExistingErr: %s,
|
|
writeExistingErr: %s,
|
|
contents: %q,
|
|
},`,
|
|
mode,
|
|
mode,
|
|
whichError(openNonExistentErr),
|
|
whichError(readNonExistentErr),
|
|
whichError(writeNonExistentErr),
|
|
whichError(openExistingErr),
|
|
whichError(readExistingErr),
|
|
whichError(writeExistingErr),
|
|
contents)
|
|
}
|
|
|
|
func main() {
|
|
fmt.Printf(`// Code generated by make_open_tests.go - use go generate to rebuild - DO NOT EDIT
|
|
|
|
package vfs
|
|
|
|
import (
|
|
"os"
|
|
"io"
|
|
)
|
|
|
|
// openTest describes a test of OpenFile
|
|
type openTest struct{
|
|
flags int
|
|
what string
|
|
openNonExistentErr error
|
|
readNonExistentErr error
|
|
writeNonExistentErr error
|
|
openExistingErr error
|
|
readExistingErr error
|
|
writeExistingErr error
|
|
contents string
|
|
}
|
|
|
|
// openTests is a suite of tests for OpenFile with all possible
|
|
// combination of flags. This obeys Unix semantics even on Windows.
|
|
var openTests = []openTest{
|
|
`)
|
|
f, err := os.CreateTemp("", "open-test")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fileName := f.Name()
|
|
_ = f.Close()
|
|
err = os.Remove(fileName)
|
|
if err != nil {
|
|
log.Fatalf("failed to remove: %v", err)
|
|
}
|
|
for _, rwMode := range []int{os.O_RDONLY, os.O_WRONLY, os.O_RDWR} {
|
|
flags0 := rwMode
|
|
parts0 := []string{"os.O_RDONLY", "os.O_WRONLY", "os.O_RDWR"}[rwMode : rwMode+1]
|
|
for _, appendMode := range []int{0, os.O_APPEND} {
|
|
flags1 := flags0 | appendMode
|
|
parts1 := parts0
|
|
if appendMode != 0 {
|
|
parts1 = append(parts1, "os.O_APPEND")
|
|
}
|
|
for _, createMode := range []int{0, os.O_CREATE} {
|
|
flags2 := flags1 | createMode
|
|
parts2 := parts1
|
|
if createMode != 0 {
|
|
parts2 = append(parts2, "os.O_CREATE")
|
|
}
|
|
for _, exclMode := range []int{0, os.O_EXCL} {
|
|
flags3 := flags2 | exclMode
|
|
parts3 := parts2
|
|
if exclMode != 0 {
|
|
parts3 = append(parts2, "os.O_EXCL")
|
|
}
|
|
for _, syncMode := range []int{0, os.O_SYNC} {
|
|
flags4 := flags3 | syncMode
|
|
parts4 := parts3
|
|
if syncMode != 0 {
|
|
parts4 = append(parts4, "os.O_SYNC")
|
|
}
|
|
for _, truncMode := range []int{0, os.O_TRUNC} {
|
|
flags5 := flags4 | truncMode
|
|
parts5 := parts4
|
|
if truncMode != 0 {
|
|
parts5 = append(parts5, "os.O_TRUNC")
|
|
}
|
|
textMode := strings.Join(parts5, "|")
|
|
flags := flags5
|
|
|
|
test(fileName, flags, textMode)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fmt.Printf("\n}\n")
|
|
}
|