github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/filenames_test.go (about) 1 // Copyright 2020 The LevelDB-Go and Pebble and Bitalostored 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 bitalostable 6 7 import ( 8 "testing" 9 10 "github.com/cockroachdb/errors" 11 "github.com/stretchr/testify/require" 12 "github.com/zuoyebang/bitalostable/internal/base" 13 "github.com/zuoyebang/bitalostable/vfs" 14 ) 15 16 // TestSetCurrentFileCrash tests a crash that occurs during 17 // a MANIFEST roll, leaving the temporary CURRENT file on 18 // the filesystem. These temporary files should be cleaned 19 // up on Open. 20 func TestSetCurrentFileCrash(t *testing.T) { 21 mem := vfs.NewMem() 22 23 // Initialize a fresh database to write the initial MANIFEST. 24 { 25 d, err := Open("", &Options{FS: mem}) 26 require.NoError(t, err) 27 require.NoError(t, d.Close()) 28 } 29 30 // Open the database again, this time with a FS that 31 // errors on Rename and a tiny max manifest file size 32 // to force manifest rolls. 33 { 34 wantErr := errors.New("rename error") 35 _, err := Open("", &Options{ 36 FS: renameErrorFS{FS: mem, err: wantErr}, 37 Logger: noFatalLogger{t: t}, 38 MaxManifestFileSize: 1, 39 L0CompactionThreshold: 10, 40 }) 41 // Open should fail during a manifest roll, 42 // leaving a temp dir on the filesystem. 43 if !errors.Is(err, wantErr) { 44 t.Fatal(err) 45 } 46 } 47 48 // A temp file should be left on the filesystem 49 // from the failed Rename of the CURRENT file. 50 if temps := allTempFiles(t, mem); len(temps) == 0 { 51 t.Fatal("no temp files on the filesystem") 52 } 53 54 // Open the database a third time with a normal 55 // filesystem again. It should clean up any temp 56 // files on Open. 57 { 58 d, err := Open("", &Options{ 59 FS: mem, 60 MaxManifestFileSize: 1, 61 L0CompactionThreshold: 10, 62 }) 63 require.NoError(t, err) 64 require.NoError(t, d.Close()) 65 if temps := allTempFiles(t, mem); len(temps) > 0 { 66 t.Fatalf("temporary files still on disk: %#v\n", temps) 67 } 68 } 69 } 70 71 func allTempFiles(t *testing.T, fs vfs.FS) []string { 72 var files []string 73 ls, err := fs.List("") 74 require.NoError(t, err) 75 for _, f := range ls { 76 ft, _, ok := base.ParseFilename(fs, f) 77 if ok && ft == fileTypeTemp { 78 files = append(files, f) 79 } 80 } 81 return files 82 } 83 84 type renameErrorFS struct { 85 vfs.FS 86 err error 87 } 88 89 func (fs renameErrorFS) Rename(oldname string, newname string) error { 90 return fs.err 91 } 92 93 // noFatalLogger implements Logger, logging to the contained 94 // *testing.T. Notably it does not panic on calls to Fatalf 95 // to enable unit tests of fatal logic. 96 type noFatalLogger struct { 97 t *testing.T 98 } 99 100 func (l noFatalLogger) Info(args ...interface{}) { 101 l.t.Log(args...) 102 } 103 104 func (l noFatalLogger) Warn(args ...interface{}) { 105 l.t.Error(args...) 106 } 107 108 func (l noFatalLogger) Error(args ...interface{}) { 109 l.t.Error(args...) 110 } 111 112 func (l noFatalLogger) Infof(format string, args ...interface{}) { 113 l.t.Logf(format, args...) 114 } 115 116 func (l noFatalLogger) Warnf(format string, args ...interface{}) { 117 l.t.Errorf(format, args...) 118 } 119 120 func (l noFatalLogger) Errorf(format string, args ...interface{}) { 121 l.t.Errorf(format, args...) 122 } 123 124 func (l noFatalLogger) Fatalf(format string, args ...interface{}) { 125 l.t.Logf(format, args...) 126 } 127 128 func (l noFatalLogger) Cost(arg ...interface{}) func() { 129 return func() {} 130 }