github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/vfs/file_lock_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_test 16 17 import ( 18 "bytes" 19 "flag" 20 "io/ioutil" 21 "os" 22 "os/exec" 23 "testing" 24 25 "github.com/stretchr/testify/require" 26 "github.com/zuoyebang/bitalosdb/internal/vfs" 27 ) 28 29 var lockFilename = flag.String("lockfile", "", "File to lock. A non-empty value implies a child process.") 30 31 func spawn(prog, filename string) ([]byte, error) { 32 return exec.Command(prog, "-lockfile", filename, "-test.v", 33 "-test.run=TestLock$").CombinedOutput() 34 } 35 36 // TestLock locks a file, spawns a second process that attempts to grab the 37 // lock to verify it fails. 38 // Then it closes the lock, and spawns a third copy to verify it can be 39 // relocked. 40 func TestLock(t *testing.T) { 41 child := *lockFilename != "" 42 var filename string 43 if child { 44 filename = *lockFilename 45 } else { 46 f, err := ioutil.TempFile("", "golang-bitalosdb-db-testlock-") 47 require.NoError(t, err) 48 49 filename = f.Name() 50 // NB: On Windows, locking will fail if the file is already open by the 51 // current process, so we close the lockfile here. 52 require.NoError(t, f.Close()) 53 defer os.Remove(filename) 54 } 55 56 // Avoid truncating an existing, non-empty file. 57 fi, err := os.Stat(filename) 58 if err == nil && fi.Size() != 0 { 59 t.Fatalf("The file %s is not empty", filename) 60 } 61 62 t.Logf("Locking: %s", filename) 63 lock, err := vfs.Default.Lock(filename) 64 if err != nil { 65 t.Fatalf("Could not lock %s: %v", filename, err) 66 } 67 68 if !child { 69 t.Logf("Spawning child, should fail to grab lock.") 70 out, err := spawn(os.Args[0], filename) 71 if err == nil { 72 t.Fatalf("Attempt to grab open lock should have failed.\n%s", out) 73 } 74 if !bytes.Contains(out, []byte("Could not lock")) { 75 t.Fatalf("Child failed with unexpected output: %s", out) 76 } 77 t.Logf("Child failed to grab lock as expected.") 78 } 79 80 t.Logf("Unlocking %s", filename) 81 if err := lock.Close(); err != nil { 82 t.Fatalf("Could not unlock %s: %v", filename, err) 83 } 84 85 if !child { 86 t.Logf("Spawning child, should successfully grab lock.") 87 if out, err := spawn(os.Args[0], filename); err != nil { 88 t.Fatalf("Attempt to re-open lock should have succeeded: %v\n%s", 89 err, out) 90 } 91 t.Logf("Child grabbed lock.") 92 } 93 } 94 95 func TestLockSameProcess(t *testing.T) { 96 f, err := ioutil.TempFile("", "bitalosdb-testlocksameprocess-") 97 require.NoError(t, err) 98 filename := f.Name() 99 100 // NB: On Windows, locking will fail if the file is already open by the 101 // current process, so we close the lockfile here. 102 require.NoError(t, f.Close()) 103 defer os.Remove(filename) 104 105 lock1, err := vfs.Default.Lock(filename) 106 require.NoError(t, err) 107 108 // Locking the file again from within the same process should fail. 109 // On Unix, Lock should detect the file in the global map of 110 // process-locked files. 111 // On Windows, locking will fail since the file is already open by the 112 // current process. 113 _, err = vfs.Default.Lock(filename) 114 require.Error(t, err) 115 116 require.NoError(t, lock1.Close()) 117 }