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