github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/filesystem/locking/locker_test.go (about) 1 package locking 2 3 import ( 4 "bytes" 5 "os" 6 "os/exec" 7 "strings" 8 "testing" 9 10 "github.com/mutagen-io/mutagen/pkg/mutagen" 11 ) 12 13 const ( 14 // lockerTestExecutablePackage is the Go package to build for running 15 // concurrent lock tests. 16 lockerTestExecutablePackage = "github.com/mutagen-io/mutagen/pkg/filesystem/locking/lockertest" 17 18 // lockerTestFailMessage is a sentinel message used to indicate lock 19 // acquisition failure in the test executable. We could use an exit code, 20 // but "go run" doesn't forward them and different systems might handle them 21 // differently. 22 lockerTestFailMessage = "lock acquisition failed" 23 ) 24 25 // TestLockerFailOnDirectory tests that a locker creation fails for a directory. 26 func TestLockerFailOnDirectory(t *testing.T) { 27 if _, err := NewLocker(t.TempDir(), 0600); err == nil { 28 t.Fatal("creating a locker on a directory path succeeded") 29 } 30 } 31 32 // TestLockerCycle tests the lifecycle of a Locker. 33 func TestLockerCycle(t *testing.T) { 34 // Create a temporary file and defer its removal. 35 lockfile, err := os.CreateTemp("", "mutagen_filesystem_lock") 36 if err != nil { 37 t.Fatal("unable to create temporary lock file:", err) 38 } else if err = lockfile.Close(); err != nil { 39 t.Error("unable to close temporary lock file:", err) 40 } 41 defer os.Remove(lockfile.Name()) 42 43 // Create a locker. 44 locker, err := NewLocker(lockfile.Name(), 0600) 45 if err != nil { 46 t.Fatal("unable to create locker:", err) 47 } 48 49 // Attempt to acquire the lock. 50 if err := locker.Lock(true); err != nil { 51 t.Fatal("unable to acquire lock:", err) 52 } 53 54 // Verify that the lock state is correct. 55 if !locker.Held() { 56 t.Error("lock incorrectly reported as unlocked") 57 } 58 59 // Attempt to release the lock. 60 if err := locker.Unlock(); err != nil { 61 t.Fatal("unable to release lock:", err) 62 } 63 64 // Attempt to close the locker. 65 if err := locker.Close(); err != nil { 66 t.Fatal("unable to close locker:", err) 67 } 68 } 69 70 // TestLockDuplicateFail tests that an additional attempt to acquire a lock by a 71 // separate process will fail. 72 func TestLockDuplicateFail(t *testing.T) { 73 // Compute the path to the Mutagen source tree. 74 mutagenSourcePath, err := mutagen.SourceTreePath() 75 if err != nil { 76 t.Fatal("unable to compute path to Mutagen source tree:", err) 77 } 78 79 // Create a temporary file and defer its removal. 80 lockfile, err := os.CreateTemp("", "mutagen_filesystem_lock") 81 if err != nil { 82 t.Fatal("unable to create temporary lock file:", err) 83 } else if err = lockfile.Close(); err != nil { 84 t.Error("unable to close temporary lock file:", err) 85 } 86 defer os.Remove(lockfile.Name()) 87 88 // Create a locker for the file, acquire the lock, and defer the release of 89 // the lock and closure of the locker. 90 locker, err := NewLocker(lockfile.Name(), 0600) 91 if err != nil { 92 t.Fatal("unable to create locker:", err) 93 } else if err = locker.Lock(true); err != nil { 94 t.Fatal("unable to acquire lock:", err) 95 } 96 defer func() { 97 locker.Unlock() 98 locker.Close() 99 }() 100 101 // Attempt to run the test executable and ensure that it fails with the 102 // proper error code (indicating failed lock acquisition). 103 testCommand := exec.Command("go", "run", lockerTestExecutablePackage, lockfile.Name()) 104 testCommand.Dir = mutagenSourcePath 105 errorBuffer := &bytes.Buffer{} 106 testCommand.Stderr = errorBuffer 107 if err := testCommand.Run(); err == nil { 108 t.Error("test command succeeded unexpectedly") 109 } else if !strings.Contains(errorBuffer.String(), lockerTestFailMessage) { 110 t.Error("test command error output did not contain failure message", errorBuffer.String()) 111 } 112 }