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  }