github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/vfs/make_open_tests.go (about)

     1  // This makes the open test suite. It tries to open a file (existing
     2  // or not existing) with all possible file modes and writes a test
     3  // matrix.
     4  //
     5  // The behaviour is as run on Linux, with the small modification that
     6  // O_TRUNC with O_RDONLY does **not** truncate the file.
     7  //
     8  // Run with go generate (defined in vfs.go)
     9  //
    10  //+build none
    11  
    12  // FIXME include read too?
    13  
    14  package main
    15  
    16  import (
    17  	"fmt"
    18  	"io"
    19  	"io/ioutil"
    20  	"log"
    21  	"os"
    22  	"strings"
    23  
    24  	"github.com/rclone/rclone/lib/file"
    25  )
    26  
    27  // Interprets err into a vfs error
    28  func whichError(err error) string {
    29  	switch err {
    30  	case nil:
    31  		return "nil"
    32  	case io.EOF:
    33  		return "io.EOF"
    34  	case os.ErrInvalid:
    35  		return "EINVAL"
    36  	}
    37  	s := err.Error()
    38  	switch {
    39  	case strings.Contains(s, "no such file or directory"):
    40  		return "ENOENT"
    41  	case strings.Contains(s, "bad file descriptor"):
    42  		return "EBADF"
    43  	case strings.Contains(s, "file exists"):
    44  		return "EEXIST"
    45  	}
    46  	log.Fatalf("Unknown error: %v", err)
    47  	return ""
    48  }
    49  
    50  const accessModeMask = (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)
    51  
    52  // test Opening, reading and writing the file handle with the flags given
    53  func test(fileName string, flags int, mode string) {
    54  	// first try with file not existing
    55  	_, err := os.Stat(fileName)
    56  	if !os.IsNotExist(err) {
    57  		log.Fatalf("File must not exist")
    58  	}
    59  	f, openNonExistentErr := file.OpenFile(fileName, flags, 0666)
    60  
    61  	var readNonExistentErr error
    62  	var writeNonExistentErr error
    63  	if openNonExistentErr == nil {
    64  		// read some bytes
    65  		buf := []byte{0, 0}
    66  		_, readNonExistentErr = f.Read(buf)
    67  
    68  		// write some bytes
    69  		_, writeNonExistentErr = f.Write([]byte("hello"))
    70  
    71  		// close
    72  		err = f.Close()
    73  		if err != nil {
    74  			log.Fatalf("failed to close: %v", err)
    75  		}
    76  	}
    77  
    78  	// write the file
    79  	f, err = file.Create(fileName)
    80  	if err != nil {
    81  		log.Fatalf("failed to create: %v", err)
    82  	}
    83  	n, err := f.Write([]byte("hello"))
    84  	if n != 5 || err != nil {
    85  		log.Fatalf("failed to write n=%d: %v", n, err)
    86  	}
    87  	// close
    88  	err = f.Close()
    89  	if err != nil {
    90  		log.Fatalf("failed to close: %v", err)
    91  	}
    92  
    93  	// then open file and try with file existing
    94  
    95  	f, openExistingErr := file.OpenFile(fileName, flags, 0666)
    96  	var readExistingErr error
    97  	var writeExistingErr error
    98  	if openExistingErr == nil {
    99  		// read some bytes
   100  		buf := []byte{0, 0}
   101  		_, readExistingErr = f.Read(buf)
   102  
   103  		// write some bytes
   104  		_, writeExistingErr = f.Write([]byte("HEL"))
   105  
   106  		// close
   107  		err = f.Close()
   108  		if err != nil {
   109  			log.Fatalf("failed to close: %v", err)
   110  		}
   111  	}
   112  
   113  	// read the file
   114  	f, err = file.Open(fileName)
   115  	if err != nil {
   116  		log.Fatalf("failed to open: %v", err)
   117  	}
   118  	var buf = make([]byte, 64)
   119  	n, err = f.Read(buf)
   120  	if err != nil && err != io.EOF {
   121  		log.Fatalf("failed to read n=%d: %v", n, err)
   122  	}
   123  	err = f.Close()
   124  	if err != nil {
   125  		log.Fatalf("failed to close: %v", err)
   126  	}
   127  	contents := string(buf[:n])
   128  
   129  	// remove file
   130  	err = os.Remove(fileName)
   131  	if err != nil {
   132  		log.Fatalf("failed to remove: %v", err)
   133  	}
   134  
   135  	// http://pubs.opengroup.org/onlinepubs/7908799/xsh/open.html
   136  	// The result of using O_TRUNC with O_RDONLY is undefined.
   137  	// Linux seems to truncate the file, but we prefer to return EINVAL
   138  	if (flags&accessModeMask) == os.O_RDONLY && flags&os.O_TRUNC != 0 {
   139  		openNonExistentErr = os.ErrInvalid // EINVAL
   140  		readNonExistentErr = nil
   141  		writeNonExistentErr = nil
   142  		openExistingErr = os.ErrInvalid // EINVAL
   143  		readExistingErr = nil
   144  		writeExistingErr = nil
   145  		contents = "hello"
   146  	}
   147  
   148  	// output the struct
   149  	fmt.Printf(`{
   150  	flags: %s,
   151  	what: %q,
   152  	openNonExistentErr: %s,
   153  	readNonExistentErr: %s,
   154  	writeNonExistentErr: %s,
   155  	openExistingErr: %s,
   156  	readExistingErr: %s,
   157  	writeExistingErr: %s,
   158  	contents: %q,
   159  },`,
   160  		mode,
   161  		mode,
   162  		whichError(openNonExistentErr),
   163  		whichError(readNonExistentErr),
   164  		whichError(writeNonExistentErr),
   165  		whichError(openExistingErr),
   166  		whichError(readExistingErr),
   167  		whichError(writeExistingErr),
   168  		contents)
   169  }
   170  
   171  func main() {
   172  	fmt.Printf(`// Code generated by make_open_tests.go - use go generate to rebuild - DO NOT EDIT
   173  
   174  package vfs
   175  
   176  import (
   177  	"os"
   178  	"io"
   179  )
   180  
   181  // openTest describes a test of OpenFile
   182  type openTest struct{
   183  	flags int
   184  	what string
   185  	openNonExistentErr error
   186  	readNonExistentErr error
   187  	writeNonExistentErr error
   188  	openExistingErr error
   189  	readExistingErr error
   190  	writeExistingErr error
   191  	contents string
   192  }
   193  
   194  // openTests is a suite of tests for OpenFile with all possible
   195  // combination of flags.  This obeys Unix semantics even on Windows.
   196  var openTests = []openTest{
   197  `)
   198  	f, err := ioutil.TempFile("", "open-test")
   199  	if err != nil {
   200  		log.Fatal(err)
   201  	}
   202  	fileName := f.Name()
   203  	_ = f.Close()
   204  	err = os.Remove(fileName)
   205  	if err != nil {
   206  		log.Fatalf("failed to remove: %v", err)
   207  	}
   208  	for _, rwMode := range []int{os.O_RDONLY, os.O_WRONLY, os.O_RDWR} {
   209  		flags0 := rwMode
   210  		parts0 := []string{"os.O_RDONLY", "os.O_WRONLY", "os.O_RDWR"}[rwMode : rwMode+1]
   211  		for _, appendMode := range []int{0, os.O_APPEND} {
   212  			flags1 := flags0 | appendMode
   213  			parts1 := parts0
   214  			if appendMode != 0 {
   215  				parts1 = append(parts1, "os.O_APPEND")
   216  			}
   217  			for _, createMode := range []int{0, os.O_CREATE} {
   218  				flags2 := flags1 | createMode
   219  				parts2 := parts1
   220  				if createMode != 0 {
   221  					parts2 = append(parts2, "os.O_CREATE")
   222  				}
   223  				for _, exclMode := range []int{0, os.O_EXCL} {
   224  					flags3 := flags2 | exclMode
   225  					parts3 := parts2
   226  					if exclMode != 0 {
   227  						parts3 = append(parts2, "os.O_EXCL")
   228  					}
   229  					for _, syncMode := range []int{0, os.O_SYNC} {
   230  						flags4 := flags3 | syncMode
   231  						parts4 := parts3
   232  						if syncMode != 0 {
   233  							parts4 = append(parts4, "os.O_SYNC")
   234  						}
   235  						for _, truncMode := range []int{0, os.O_TRUNC} {
   236  							flags5 := flags4 | truncMode
   237  							parts5 := parts4
   238  							if truncMode != 0 {
   239  								parts5 = append(parts5, "os.O_TRUNC")
   240  							}
   241  							textMode := strings.Join(parts5, "|")
   242  							flags := flags5
   243  
   244  							test(fileName, flags, textMode)
   245  						}
   246  					}
   247  				}
   248  			}
   249  		}
   250  	}
   251  	fmt.Printf("\n}\n")
   252  }