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