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 }