252 lines · 5.7 KB · Go
RawDownload
// 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)////+build none // FIXME include read too? package main import (	"fmt"	"io"	"io/ioutil"	"log"	"os"	"strings" 	"github.com/rclone/rclone/lib/file") // Interprets err into a vfs errorfunc 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 givenfunc 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 OpenFiletype 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 := ioutil.TempFile("", "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")}