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