github.com/searKing/golang/go@v1.2.117/sync/filelock/lockedfile_test.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // js and wasip1 do not support inter-process file locking.
     6  //
     7  //go:build !js && !wasip1
     8  
     9  package filelock_test
    10  
    11  import (
    12  	"os"
    13  	"path/filepath"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/searKing/golang/go/sync/filelock"
    18  )
    19  
    20  func mustTempDir(t *testing.T) (dir string, remove func()) {
    21  	t.Helper()
    22  
    23  	dir, err := os.MkdirTemp("", filepath.Base(t.Name()))
    24  	if err != nil {
    25  		t.Fatal(err)
    26  	}
    27  	return dir, func() { os.RemoveAll(dir) }
    28  }
    29  
    30  func mustBlockFunc(t *testing.T, desc string, f func()) (wait func(*testing.T)) {
    31  	t.Helper()
    32  
    33  	done := make(chan struct{})
    34  	go func() {
    35  		f()
    36  		close(done)
    37  	}()
    38  
    39  	timer := time.NewTimer(quiescent)
    40  	defer timer.Stop()
    41  	select {
    42  	case <-done:
    43  		t.Fatalf("%s unexpectedly did not block", desc)
    44  	case <-timer.C:
    45  	}
    46  
    47  	return func(t *testing.T) {
    48  		logTimer := time.NewTimer(quiescent)
    49  		defer logTimer.Stop()
    50  
    51  		select {
    52  		case <-logTimer.C:
    53  			// We expect the operation to have unblocked by now,
    54  			// but maybe it's just slow. Write to the test log
    55  			// in case the test times out, but don't fail it.
    56  			t.Helper()
    57  			t.Logf("%s is unexpectedly still blocked after %v", desc, quiescent)
    58  
    59  			// Wait for the operation to actually complete, no matter how long it
    60  			// takes. If the test has deadlocked, this will cause the test to time out
    61  			// and dump goroutines.
    62  			<-done
    63  
    64  		case <-done:
    65  		}
    66  	}
    67  }
    68  
    69  func TestMutexExcludes(t *testing.T) {
    70  	t.Parallel()
    71  
    72  	dir, remove := mustTempDir(t)
    73  	defer remove()
    74  
    75  	path := filepath.Join(dir, "lock")
    76  
    77  	mu := filelock.MutexAt(path)
    78  	t.Logf("mu := MutexAt(_)")
    79  
    80  	unlock, err := mu.Lock()
    81  	if err != nil {
    82  		t.Fatalf("mu.Lock: %v", err)
    83  	}
    84  	t.Logf("unlock, _  := mu.Lock()")
    85  
    86  	mu2 := filelock.MutexAt(mu.Path)
    87  	t.Logf("mu2 := MutexAt(mu.Path)")
    88  
    89  	wait := mustBlockFunc(t, "mu2.Lock()", func() {
    90  		unlock2, err := mu2.Lock()
    91  		if err != nil {
    92  			t.Errorf("mu2.Lock: %v", err)
    93  			return
    94  		}
    95  		t.Logf("unlock2, _ := mu2.Lock()")
    96  		t.Logf("unlock2()")
    97  		unlock2()
    98  	})
    99  
   100  	t.Logf("unlock()")
   101  	unlock()
   102  	wait(t)
   103  }
   104  
   105  func TestReadWaitsForLock(t *testing.T) {
   106  	t.Parallel()
   107  
   108  	dir, remove := mustTempDir(t)
   109  	defer remove()
   110  
   111  	path := filepath.Join(dir, "timestamp.txt")
   112  
   113  	f, err := filelock.Create(path)
   114  	if err != nil {
   115  		t.Fatalf("Create: %v", err)
   116  	}
   117  	defer f.Close()
   118  
   119  	const (
   120  		part1 = "part 1\n"
   121  		part2 = "part 2\n"
   122  	)
   123  	_, err = f.File.WriteString(part1)
   124  	if err != nil {
   125  		t.Fatalf("WriteString: %v", err)
   126  	}
   127  	t.Logf("WriteString(%q) = <nil>", part1)
   128  
   129  	wait := mustBlockFunc(t, "Read", func() {
   130  		b, err := filelock.Read(path)
   131  		if err != nil {
   132  			t.Errorf("Read: %v", err)
   133  			return
   134  		}
   135  
   136  		const want = part1 + part2
   137  		got := string(b)
   138  		if got == want {
   139  			t.Logf("Read(_) = %q", got)
   140  		} else {
   141  			t.Errorf("Read(_) = %q, _; want %q", got, want)
   142  		}
   143  	})
   144  
   145  	_, err = f.File.WriteString(part2)
   146  	if err != nil {
   147  		t.Errorf("WriteString: %v", err)
   148  	} else {
   149  		t.Logf("WriteString(%q) = <nil>", part2)
   150  	}
   151  	f.Close()
   152  
   153  	wait(t)
   154  }
   155  
   156  func TestCanLockExistingFile(t *testing.T) {
   157  	t.Parallel()
   158  
   159  	dir, remove := mustTempDir(t)
   160  	defer remove()
   161  	path := filepath.Join(dir, "existing.txt")
   162  
   163  	if err := os.WriteFile(path, []byte("ok"), 0777); err != nil {
   164  		t.Fatalf("os.WriteFile: %v", err)
   165  	}
   166  
   167  	f, err := filelock.Edit(path)
   168  	if err != nil {
   169  		t.Fatalf("first Edit: %v", err)
   170  	}
   171  
   172  	wait := mustBlockFunc(t, "Edit", func() {
   173  		other, err := filelock.Edit(path)
   174  		if err != nil {
   175  			t.Errorf("second Edit: %v", err)
   176  		}
   177  		other.Close()
   178  	})
   179  
   180  	f.Close()
   181  	wait(t)
   182  }