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  }