]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgo/go/cmd/go/internal/lockedfile/lockedfile_test.go
libgo: update to Go1.14beta1
[thirdparty/gcc.git] / libgo / go / cmd / go / internal / lockedfile / lockedfile_test.go
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 does not support inter-process file locking.
6 // +build !js
7
8 package lockedfile_test
9
10 import (
11 "io/ioutil"
12 "os"
13 "path/filepath"
14 "testing"
15 "time"
16
17 "cmd/go/internal/lockedfile"
18 )
19
20 func mustTempDir(t *testing.T) (dir string, remove func()) {
21 t.Helper()
22
23 dir, err := ioutil.TempDir("", filepath.Base(t.Name()))
24 if err != nil {
25 t.Fatal(err)
26 }
27 return dir, func() { os.RemoveAll(dir) }
28 }
29
30 const (
31 quiescent = 10 * time.Millisecond
32 probablyStillBlocked = 10 * time.Second
33 )
34
35 func mustBlock(t *testing.T, desc string, f func()) (wait func(*testing.T)) {
36 t.Helper()
37
38 done := make(chan struct{})
39 go func() {
40 f()
41 close(done)
42 }()
43
44 select {
45 case <-done:
46 t.Fatalf("%s unexpectedly did not block", desc)
47 return nil
48
49 case <-time.After(quiescent):
50 return func(t *testing.T) {
51 t.Helper()
52 select {
53 case <-time.After(probablyStillBlocked):
54 t.Fatalf("%s is unexpectedly still blocked after %v", desc, probablyStillBlocked)
55 case <-done:
56 }
57 }
58 }
59 }
60
61 func TestMutexExcludes(t *testing.T) {
62 t.Parallel()
63
64 dir, remove := mustTempDir(t)
65 defer remove()
66
67 path := filepath.Join(dir, "lock")
68
69 mu := lockedfile.MutexAt(path)
70 t.Logf("mu := MutexAt(_)")
71
72 unlock, err := mu.Lock()
73 if err != nil {
74 t.Fatalf("mu.Lock: %v", err)
75 }
76 t.Logf("unlock, _ := mu.Lock()")
77
78 mu2 := lockedfile.MutexAt(mu.Path)
79 t.Logf("mu2 := MutexAt(mu.Path)")
80
81 wait := mustBlock(t, "mu2.Lock()", func() {
82 unlock2, err := mu2.Lock()
83 if err != nil {
84 t.Errorf("mu2.Lock: %v", err)
85 return
86 }
87 t.Logf("unlock2, _ := mu2.Lock()")
88 t.Logf("unlock2()")
89 unlock2()
90 })
91
92 t.Logf("unlock()")
93 unlock()
94 wait(t)
95 }
96
97 func TestReadWaitsForLock(t *testing.T) {
98 t.Parallel()
99
100 dir, remove := mustTempDir(t)
101 defer remove()
102
103 path := filepath.Join(dir, "timestamp.txt")
104
105 f, err := lockedfile.Create(path)
106 if err != nil {
107 t.Fatalf("Create: %v", err)
108 }
109 defer f.Close()
110
111 const (
112 part1 = "part 1\n"
113 part2 = "part 2\n"
114 )
115 _, err = f.WriteString(part1)
116 if err != nil {
117 t.Fatalf("WriteString: %v", err)
118 }
119 t.Logf("WriteString(%q) = <nil>", part1)
120
121 wait := mustBlock(t, "Read", func() {
122 b, err := lockedfile.Read(path)
123 if err != nil {
124 t.Errorf("Read: %v", err)
125 return
126 }
127
128 const want = part1 + part2
129 got := string(b)
130 if got == want {
131 t.Logf("Read(_) = %q", got)
132 } else {
133 t.Errorf("Read(_) = %q, _; want %q", got, want)
134 }
135 })
136
137 _, err = f.WriteString(part2)
138 if err != nil {
139 t.Errorf("WriteString: %v", err)
140 } else {
141 t.Logf("WriteString(%q) = <nil>", part2)
142 }
143 f.Close()
144
145 wait(t)
146 }
147
148 func TestCanLockExistingFile(t *testing.T) {
149 t.Parallel()
150
151 dir, remove := mustTempDir(t)
152 defer remove()
153 path := filepath.Join(dir, "existing.txt")
154
155 if err := ioutil.WriteFile(path, []byte("ok"), 0777); err != nil {
156 t.Fatalf("ioutil.WriteFile: %v", err)
157 }
158
159 f, err := lockedfile.Edit(path)
160 if err != nil {
161 t.Fatalf("first Edit: %v", err)
162 }
163
164 wait := mustBlock(t, "Edit", func() {
165 other, err := lockedfile.Edit(path)
166 if err != nil {
167 t.Errorf("second Edit: %v", err)
168 }
169 other.Close()
170 })
171
172 f.Close()
173 wait(t)
174 }