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 }