Skip to content

Commit f19bb4b

Browse files
committed
Lock library: add tests
Also two minor fixes (t.Errorf instead of Fatalf in the logger; t.Cleanup instead of defer for svr.Close) in the agentapi tests I stole code from.
1 parent 152d484 commit f19bb4b

File tree

2 files changed

+148
-4
lines changed

2 files changed

+148
-4
lines changed

internal/agentapi/client_server_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ var testSocketCounter uint32
1616

1717
func testSocketPath() string {
1818
id := atomic.AddUint32(&testSocketCounter, 1)
19-
return filepath.Join(os.TempDir(), fmt.Sprintf("test-%d-%d", os.Getpid(), id))
19+
return filepath.Join(os.TempDir(), fmt.Sprintf("internal_agentapi_test-%d-%d", os.Getpid(), id))
2020
}
2121

2222
func testLogger(t *testing.T) logger.Logger {
2323
t.Helper()
2424
logger := logger.NewConsoleLogger(
2525
logger.NewTextPrinter(os.Stderr),
26-
func(c int) { t.Fatalf("exit(%d)", c) },
26+
func(c int) { t.Errorf("exit(%d)", c) },
2727
)
2828
return logger
2929
}
@@ -53,7 +53,7 @@ func TestPing(t *testing.T) {
5353
t.Cleanup(canc)
5454

5555
svr, cli := testServerAndClient(t, ctx)
56-
defer svr.Close()
56+
t.Cleanup(func() { svr.Close() })
5757

5858
if err := cli.Ping(ctx); err != nil {
5959
t.Errorf("cli.Ping(ctx) = %v", err)
@@ -66,7 +66,7 @@ func TestLockOperations(t *testing.T) {
6666
t.Cleanup(canc)
6767

6868
svr, cli := testServerAndClient(t, ctx)
69-
defer svr.Close()
69+
t.Cleanup(func() { svr.Close() })
7070

7171
const key = "llama"
7272

lock/lock_test.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package lock
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"sync"
9+
"sync/atomic"
10+
"testing"
11+
"time"
12+
13+
"github.com/buildkite/agent/v3/internal/agentapi"
14+
"github.com/buildkite/agent/v3/logger"
15+
)
16+
17+
var testSocketCounter uint32
18+
19+
func testSocketPath() string {
20+
id := atomic.AddUint32(&testSocketCounter, 1)
21+
return filepath.Join(os.TempDir(), fmt.Sprintf("lock_test-%d-%d", os.Getpid(), id))
22+
}
23+
24+
func testLogger(t *testing.T) logger.Logger {
25+
t.Helper()
26+
logger := logger.NewConsoleLogger(
27+
logger.NewTextPrinter(os.Stderr),
28+
func(c int) { t.Errorf("exit(%d)", c) },
29+
)
30+
return logger
31+
}
32+
33+
func testServerAndClient(t *testing.T, ctx context.Context) (*agentapi.Server, *Client) {
34+
t.Helper()
35+
sockPath, logger := testSocketPath(), testLogger(t)
36+
svr, err := agentapi.NewServer(sockPath, logger)
37+
if err != nil {
38+
t.Fatalf("NewServer(%q, logger) = error %v", sockPath, err)
39+
}
40+
if err := svr.Start(); err != nil {
41+
t.Fatalf("svr.Start() = %v", err)
42+
}
43+
44+
cli, err := agentapi.NewClient(ctx, sockPath)
45+
if err != nil {
46+
t.Fatalf("NewClient(ctx, %q) = error %v", sockPath, err)
47+
}
48+
49+
// lock.NewClient takes the socket *directory*. Rather than temporarily
50+
// symlink the socket created above, I've manually created a client.
51+
return svr, &Client{client: cli}
52+
}
53+
54+
func TestLockUnlock(t *testing.T) {
55+
t.Parallel()
56+
ctx, canc := context.WithTimeout(context.Background(), 10*time.Second)
57+
t.Cleanup(canc)
58+
59+
svr, cli := testServerAndClient(t, ctx)
60+
t.Cleanup(func() { svr.Close() })
61+
62+
// Lock it
63+
token, err := cli.Lock(ctx, "llama")
64+
if err != nil {
65+
t.Errorf("Client.Lock(ctx, llama) error = %v", err)
66+
}
67+
if token == "" {
68+
t.Errorf("Client.Lock(ctx, llama) = %q, want non-empty token", token)
69+
}
70+
71+
// Try unlocking with the wrong token
72+
if err := cli.Unlock(ctx, "llama", "wrong token"); err == nil {
73+
t.Errorf("Client.Unlock(ctx, llama, wrong token) = %v, want non-nil error", err)
74+
}
75+
76+
// Unlock with the correct token
77+
if err := cli.Unlock(ctx, "llama", token); err != nil {
78+
t.Errorf("Client.Unlock(ctx, llama, %q) = %v, want nil", token, err)
79+
}
80+
81+
// Unlocking it again, even with the right token, should fail
82+
if err := cli.Unlock(ctx, "llama", token); err == nil {
83+
t.Errorf("Client.Unlock(ctx, llama, %q) = %v, want non-nil error", token, err)
84+
}
85+
}
86+
87+
func TestLocker(t *testing.T) {
88+
t.Parallel()
89+
ctx, canc := context.WithTimeout(context.Background(), 10*time.Second)
90+
t.Cleanup(canc)
91+
92+
svr, cli := testServerAndClient(t, ctx)
93+
t.Cleanup(func() { svr.Close() })
94+
95+
// This constitutes a test by virtue of Lock/Unlock panicking on any
96+
// internal error.
97+
l := cli.Locker("llama")
98+
99+
var wg sync.WaitGroup
100+
var locks int
101+
for i := 0; i < 10; i++ {
102+
wg.Add(1)
103+
go func() {
104+
l.Lock()
105+
locks++
106+
l.Unlock()
107+
wg.Done()
108+
}()
109+
}
110+
111+
wg.Wait()
112+
113+
if got, want := locks, 10; got != want {
114+
t.Errorf("locks = %d, want %d", got, want)
115+
}
116+
}
117+
118+
func TestDoOnce(t *testing.T) {
119+
t.Parallel()
120+
ctx, canc := context.WithTimeout(context.Background(), 10*time.Second)
121+
t.Cleanup(canc)
122+
123+
svr, cli := testServerAndClient(t, ctx)
124+
t.Cleanup(func() { svr.Close() })
125+
126+
var wg sync.WaitGroup
127+
var calls atomic.Int32
128+
for i := 0; i < 10; i++ {
129+
wg.Add(1)
130+
go func() {
131+
if err := cli.DoOnce(ctx, "once", func() {
132+
calls.Add(1)
133+
}); err != nil {
134+
t.Errorf("Client.DoOnce(ctx, once, inc) = %v", err)
135+
}
136+
wg.Done()
137+
}()
138+
}
139+
140+
wg.Wait()
141+
if got, want := calls.Load(), int32(1); got != want {
142+
t.Errorf("calls.Load() = %d, want %d", got, want)
143+
}
144+
}

0 commit comments

Comments
 (0)