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 }