github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/vfs/mem_fs_test.go (about) 1 // Copyright 2012 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package vfs 6 7 import ( 8 "io" 9 "io/ioutil" 10 "os" 11 "sort" 12 "strconv" 13 "strings" 14 "testing" 15 16 "github.com/stretchr/testify/require" 17 ) 18 19 func runTestCases(t *testing.T, testCases []string, fs *MemFS) { 20 var f File 21 for _, tc := range testCases { 22 s := strings.Split(tc, " ")[1:] 23 24 saveF := s[0] == "f" && s[1] == "=" 25 if saveF { 26 s = s[2:] 27 } 28 29 fails := s[len(s)-1] == "fails" 30 if fails { 31 s = s[:len(s)-1] 32 } 33 34 var ( 35 fi os.FileInfo 36 g File 37 err error 38 ) 39 switch s[0] { 40 case "create": 41 g, err = fs.Create(s[1]) 42 case "link": 43 err = fs.Link(s[1], s[2]) 44 case "open": 45 g, err = fs.Open(s[1]) 46 case "openDir": 47 g, err = fs.OpenDir(s[1]) 48 case "mkdirall": 49 err = fs.MkdirAll(s[1], 0755) 50 case "remove": 51 err = fs.Remove(s[1]) 52 case "rename": 53 err = fs.Rename(s[1], s[2]) 54 case "reuseForWrite": 55 g, err = fs.ReuseForWrite(s[1], s[2]) 56 case "resetToSynced": 57 fs.ResetToSyncedState() 58 case "ignoreSyncs": 59 fs.SetIgnoreSyncs(true) 60 case "stopIgnoringSyncs": 61 fs.SetIgnoreSyncs(false) 62 case "f.write": 63 _, err = f.Write([]byte(s[1])) 64 case "f.sync": 65 err = f.Sync() 66 case "f.read": 67 n, _ := strconv.Atoi(s[1]) 68 buf := make([]byte, n) 69 _, err = io.ReadFull(f, buf) 70 if err != nil { 71 break 72 } 73 if got, want := string(buf), s[3]; got != want { 74 t.Fatalf("%q: got %q, want %q", tc, got, want) 75 } 76 case "f.readat": 77 n, _ := strconv.Atoi(s[1]) 78 off, _ := strconv.Atoi(s[2]) 79 buf := make([]byte, n) 80 _, err = f.ReadAt(buf, int64(off)) 81 if err != nil { 82 break 83 } 84 if got, want := string(buf), s[4]; got != want { 85 t.Fatalf("%q: got %q, want %q", tc, got, want) 86 } 87 case "f.close": 88 f, err = nil, f.Close() 89 case "f.stat.name": 90 fi, err = f.Stat() 91 if err != nil { 92 break 93 } 94 if got, want := fi.Name(), s[2]; got != want { 95 t.Fatalf("%q: got %q, want %q", tc, got, want) 96 } 97 default: 98 t.Fatalf("bad test case: %q", tc) 99 } 100 101 if saveF { 102 f, g = g, nil 103 } else if g != nil { 104 g.Close() 105 } 106 107 if fails { 108 if err == nil { 109 t.Fatalf("%q: got nil error, want non-nil", tc) 110 } 111 } else { 112 if err != nil { 113 t.Fatalf("%q: %v", tc, err) 114 } 115 } 116 } 117 118 // Both "" and "/" are allowed to be used to refer to the root of the FS 119 // for the purposes of cloning. 120 checkClonedIsEquivalent(t, fs, "") 121 checkClonedIsEquivalent(t, fs, "/") 122 } 123 124 // Test that the FS can be cloned and that the clone serializes identically. 125 func checkClonedIsEquivalent(t *testing.T, fs *MemFS, path string) { 126 t.Helper() 127 clone := NewMem() 128 cloned, err := Clone(fs, clone, path, path) 129 require.NoError(t, err) 130 require.True(t, cloned) 131 require.Equal(t, fs.String(), clone.String()) 132 } 133 134 func TestBasics(t *testing.T) { 135 fs := NewMem() 136 testCases := []string{ 137 // Create a top-level file. 138 "1a: create /foo", 139 // Create a child of that file. It should fail, since /foo is not a directory. 140 "2a: create /foo/x fails", 141 // Create a third-level file. It should fail, since /bar has not been created. 142 // Similarly, opening that file should fail. 143 "3a: create /bar/baz/y fails", 144 "3b: open /bar/baz/y fails", 145 // Make the /bar/baz directory; create a third-level file. Creation should now succeed. 146 "4a: mkdirall /bar/baz", 147 "4b: f = create /bar/baz/y", 148 "4c: f.stat.name == y", 149 // Write some data; read it back. 150 "5a: f.write abcde", 151 "5b: f.close", 152 "5c: f = open /bar/baz/y", 153 "5d: f.read 5 == abcde", 154 "5e: f.readat 2 1 == bc", 155 "5f: f.close", 156 // Link /bar/baz/y to /bar/baz/z. We should be able to read from both files 157 // and remove them independently. 158 "6a: link /bar/baz/y /bar/baz/z", 159 "6b: f = open /bar/baz/z", 160 "6c: f.read 5 == abcde", 161 "6d: f.close", 162 "6e: remove /bar/baz/z", 163 "6f: f = open /bar/baz/y", 164 "6g: f.read 5 == abcde", 165 "6h: f.close", 166 // Remove the file twice. The first should succeed, the second should fail. 167 "7a: remove /bar/baz/y", 168 "7b: remove /bar/baz/y fails", 169 "7c: open /bar/baz/y fails", 170 // Rename /foo to /goo. Trying to open /foo should succeed before the rename and 171 // fail afterwards, and vice versa for /goo. 172 "8a: open /foo", 173 "8b: open /goo fails", 174 "8c: rename /foo /goo", 175 "8d: open /foo fails", 176 "8e: open /goo", 177 // Create /bar/baz/z and rename /bar/baz to /bar/caz. 178 "9a: create /bar/baz/z", 179 "9b: open /bar/baz/z", 180 "9c: open /bar/caz/z fails", 181 "9d: rename /bar/baz /bar/caz", 182 "9e: open /bar/baz/z fails", 183 "9f: open /bar/caz/z", 184 // ReuseForWrite 185 "10a: reuseForWrite /bar/caz/z /bar/z", 186 "10b: open /bar/caz/z fails", 187 "10c: open /bar/z", 188 // Opening the root directory works. 189 "11a: f = open /", 190 "11b: f.stat.name == /", 191 } 192 runTestCases(t, testCases, fs) 193 } 194 195 func TestList(t *testing.T) { 196 fs := NewMem() 197 198 dirnames := []string{ 199 "/bar", 200 "/foo/2", 201 } 202 for _, dirname := range dirnames { 203 err := fs.MkdirAll(dirname, 0755) 204 if err != nil { 205 t.Fatalf("MkdirAll %q: %v", dirname, err) 206 } 207 } 208 209 filenames := []string{ 210 "/a", 211 "/bar/baz", 212 "/foo/0", 213 "/foo/1", 214 "/foo/2/a", 215 "/foo/2/b", 216 "/foo/3", 217 "/foot", 218 } 219 for _, filename := range filenames { 220 f, err := fs.Create(filename) 221 if err != nil { 222 t.Fatalf("Create %q: %v", filename, err) 223 } 224 if err := f.Close(); err != nil { 225 t.Fatalf("Close %q: %v", filename, err) 226 } 227 } 228 229 { 230 got := fs.String() 231 const want = ` / 232 0 a 233 bar/ 234 0 baz 235 foo/ 236 0 0 237 0 1 238 2/ 239 0 a 240 0 b 241 0 3 242 0 foot 243 ` 244 if got != want { 245 t.Fatalf("String:\n----got----\n%s----want----\n%s", got, want) 246 } 247 } 248 249 testCases := []string{ 250 "/:a bar foo foot", 251 "/bar:baz", 252 "/bar/:baz", 253 "/baz:", 254 "/baz/:", 255 "/foo:0 1 2 3", 256 "/foo/:0 1 2 3", 257 "/foo/1:", 258 "/foo/1/:", 259 "/foo/2:a b", 260 "/foo/2/:a b", 261 "/foot:", 262 "/foot/:", 263 } 264 for _, tc := range testCases { 265 s := strings.Split(tc, ":") 266 list, _ := fs.List(s[0]) 267 sort.Strings(list) 268 got := strings.Join(list, " ") 269 want := s[1] 270 if got != want { 271 t.Errorf("List %q: got %q, want %q", s[0], got, want) 272 } 273 } 274 } 275 276 func TestMemFile(t *testing.T) { 277 want := "foo" 278 f := NewMemFile([]byte(want)) 279 buf, err := ioutil.ReadAll(f) 280 if err != nil { 281 t.Fatalf("%v", err) 282 } 283 if got := string(buf); got != want { 284 t.Fatalf("got %q, want %q", got, want) 285 } 286 } 287 288 func TestStrictFS(t *testing.T) { 289 fs := NewStrictMem() 290 testCases := []string{ 291 // Created file disappears if directory is not synced. 292 "1a: create /foo", 293 "1b: open /foo", 294 "1c: resetToSynced", 295 "1d: open /foo fails", 296 297 // Create directory and a file in it and write and read from it. 298 "2a: mkdirall /bar", 299 "2b: f = create /bar/y", 300 "2c: f.stat.name == y", 301 // Write some data; read it back. 302 "2d: f.write abcde", 303 "2e: f.close", 304 "2f: f = open /bar/y", 305 "2g: f.read 5 == abcde", 306 "2h: f.close", 307 "2i: open /bar", 308 309 // Resetting causes both the directory and file to disappear. 310 "3a: resetToSynced", 311 "3b: openDir /bar fails", 312 "3c: open /bar/y fails", 313 314 // Create the directory and file again. Link the file to another file in the same dir, 315 // and to a file in the root dir. Sync the root dir. After reset, the created dir and the 316 // file in the root dir are the only ones visible. 317 "4a: mkdirall /bar", 318 "4b: create /bar/y", 319 "4c: f = openDir /", 320 "4d: f.sync", 321 "4e: f.close", 322 "4f: link /bar/y /bar/z", 323 "4g: link /bar/y /z", 324 "4h: f = openDir /", 325 "4i: f.sync", 326 "4j: f.close", 327 "4k: resetToSynced", 328 "4l: openDir /bar", 329 "4m: open /bar/y fails", 330 "4n: open /bar/z fails", 331 "4o: open /z", 332 333 // Create the file in the directory again and this time sync /bar directory. The file is 334 // preserved after reset. 335 "5a: create /bar/y", 336 "5b: f = openDir /bar", 337 "5c: f.sync", 338 "5d: f.close", 339 "5e: resetToSynced", 340 "5f: openDir /bar", 341 "5g: open /bar/y", 342 343 // Unsynced data in the file is lost on reset. 344 "5a: f = create /bar/y", 345 "5b: f.write a", 346 "5c: f.sync", 347 "5d: f.write b", 348 "5e: f.close", 349 "5f: f = openDir /bar", 350 "5g: f.sync", 351 "5h: f.close", 352 "5i: resetToSynced", 353 "5j: f = open /bar/y", 354 "5k: f.read 1 = a", 355 "5l: f.read 1 fails", 356 "5m: f.close", 357 358 // reuseForWrite works correctly in strict mode in that unsynced data does not overwrite 359 // previous contents when a reset happens. 360 "6a: f = create /z", 361 "6b: f.write abcdefgh", 362 "6c: f.sync", 363 "6d: f.close", 364 "6e: f = reuseForWrite /z /y", 365 "6f: f.write x", 366 "6g: f.sync", 367 "6h: f.write y", // will be lost 368 "6i: f.close", 369 "6j: f = openDir /", 370 "6k: f.sync", 371 "6l: f.close", 372 "6m: resetToSynced", 373 "6n: f = open /y", 374 "6o: f.read 8 = xbcdefgh", 375 "6p: f.close", 376 377 // Ignore syncs. 378 "7a: f = create /z", 379 "7b: f.write a", 380 "7c: f.sync", 381 "7d: ignoreSyncs", 382 "7e: f.write b", 383 "7f: f.sync", 384 "7g: f.close", 385 "7h: stopIgnoringSyncs", 386 "7e: f = openDir /", 387 "7f: f.sync", 388 "7g: f.close", 389 "7h: resetToSynced", 390 "7i: f = open /z", 391 "7j: f.read 1 = a", 392 "7k: f.read 1 fails", 393 "7l: f.close", 394 } 395 runTestCases(t, testCases, fs) 396 }