Skip to content

Commit 719d652

Browse files
committed
perf(test): parallelise bsoncompare corpus-b and daemon Serve() tests
bsoncompare: add t.Parallel() to the four corpus-b tests (TestCompare_NoChange, TestAssertEqual_SelfComparePasses, TestBuildIDMap_CorpusB, TestReadAllUnits_CorpusB). They are read-only readers of the same file, safe for concurrent access. Wall clock: 30 s → 8.7 s (all four overlap; max wins). daemon: switch TestDaemon_ServeAndPing, TestDaemon_ValidateReturnsResults, TestDaemon_NewWithSocket_ServeBindsCustom, TestClient_PingAndValidate, and TestClient_StartIfNeeded_NoOpWhenAlive to use NewWithSocket with a unique socket in t.TempDir(). This eliminates the default-socket conflict that prevented parallel execution. Add t.Parallel() to all daemon tests. Extract waitForAlive() and newTestDaemon() helpers to reduce duplication. Wall clock: 22.7 s → 11.4 s. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 4f1a96c commit 719d652

6 files changed

Lines changed: 58 additions & 69 deletions

File tree

internal/bsoncompare/assert_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ func (m *mockT) Errorf(format string, args ...any) {
1919
}
2020

2121
func TestAssertEqual_SelfComparePasses(t *testing.T) {
22+
t.Parallel()
2223
mt := &mockT{}
2324
bsoncompare.AssertEqual(mt,
2425
"../../testdata/corpus-b/app.mpr",

internal/bsoncompare/diff_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
)
88

99
func TestCompare_NoChange(t *testing.T) {
10+
t.Parallel()
1011
diffs, err := bsoncompare.Compare(
1112
"../../testdata/corpus-b/app.mpr",
1213
"../../testdata/corpus-b/app.mpr",

internal/bsoncompare/idmap_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
)
88

99
func TestBuildIDMap_CorpusB(t *testing.T) {
10+
t.Parallel()
1011
units, err := bsoncompare.ReadAllUnits("../../testdata/corpus-b/app.mpr")
1112
if err != nil {
1213
t.Fatal(err)

internal/bsoncompare/mprreader_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
)
88

99
func TestReadAllUnits_CorpusB(t *testing.T) {
10+
t.Parallel()
1011
units, err := bsoncompare.ReadAllUnits("../../testdata/corpus-b/app.mpr")
1112
if err != nil {
1213
t.Fatalf("ReadAllUnits: %v", err)

internal/expr/daemon/client_test.go

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package daemon_test
44

55
import (
6+
"path/filepath"
67
"testing"
78
"time"
89

@@ -13,12 +14,14 @@ import (
1314
)
1415

1516
func TestNewClient_DefaultsSocketPath(t *testing.T) {
17+
t.Parallel()
1618
c := daemon.NewClient(daemon.ClientOptions{MprPath: "/tmp/foo/App.mpr"})
1719
assert.NotEmpty(t, c.SocketPath())
1820
assert.Equal(t, daemon.SocketPath("/tmp/foo/App.mpr"), c.SocketPath())
1921
}
2022

2123
func TestNewClient_ExplicitSocketPath(t *testing.T) {
24+
t.Parallel()
2225
c := daemon.NewClient(daemon.ClientOptions{
2326
MprPath: "/tmp/foo/App.mpr",
2427
SocketPath: "/tmp/custom_42.sock",
@@ -27,25 +30,18 @@ func TestNewClient_ExplicitSocketPath(t *testing.T) {
2730
}
2831

2932
func TestClient_PingAndValidate(t *testing.T) {
33+
t.Parallel()
3034
corpusAMPR := testutil.FindMPR(t, "CORPUS_A_MPR", "testdata/corpus-a/app.mpr")
3135

32-
// 直接启动 in-process daemon(无需走 StartIfNeeded → exec.Command 子进程路径),
33-
// 这样 client.go 的 Ping/Validate 可以独立验证而不依赖 Task 8 的 CLI。
34-
d, err := daemon.New(corpusAMPR, 5*time.Minute)
36+
// Use a unique socket so this test can run in parallel with other Serve() tests.
37+
sock := filepath.Join(t.TempDir(), "c.sock")
38+
d, err := daemon.NewWithSocket(corpusAMPR, sock, 5*time.Minute)
3539
require.NoError(t, err)
3640
go func() { _ = d.Serve() }()
3741
t.Cleanup(func() { _ = d.Stop() })
42+
waitForAlive(t, sock)
3843

39-
deadline := time.Now().Add(2 * time.Second)
40-
for time.Now().Before(deadline) {
41-
if daemon.IsAlive(d.SocketPath()) {
42-
break
43-
}
44-
time.Sleep(20 * time.Millisecond)
45-
}
46-
require.True(t, daemon.IsAlive(d.SocketPath()))
47-
48-
c := daemon.NewClient(daemon.ClientOptions{MprPath: corpusAMPR})
44+
c := daemon.NewClient(daemon.ClientOptions{MprPath: corpusAMPR, SocketPath: sock})
4945

5046
// Ping
5147
pr, err := c.Ping()
@@ -64,23 +60,17 @@ func TestClient_PingAndValidate(t *testing.T) {
6460
}
6561

6662
func TestClient_StartIfNeeded_NoOpWhenAlive(t *testing.T) {
63+
t.Parallel()
6764
corpusAMPR := testutil.FindMPR(t, "CORPUS_A_MPR", "testdata/corpus-a/app.mpr")
6865

69-
d, err := daemon.New(corpusAMPR, 5*time.Minute)
66+
sock := filepath.Join(t.TempDir(), "s.sock")
67+
d, err := daemon.NewWithSocket(corpusAMPR, sock, 5*time.Minute)
7068
require.NoError(t, err)
7169
go func() { _ = d.Serve() }()
7270
t.Cleanup(func() { _ = d.Stop() })
71+
waitForAlive(t, sock)
7372

74-
deadline := time.Now().Add(2 * time.Second)
75-
for time.Now().Before(deadline) {
76-
if daemon.IsAlive(d.SocketPath()) {
77-
break
78-
}
79-
time.Sleep(20 * time.Millisecond)
80-
}
81-
require.True(t, daemon.IsAlive(d.SocketPath()))
82-
83-
c := daemon.NewClient(daemon.ClientOptions{MprPath: corpusAMPR})
73+
c := daemon.NewClient(daemon.ClientOptions{MprPath: corpusAMPR, SocketPath: sock})
8474
// daemon 已活:StartIfNeeded 必须立即返回 nil,不尝试 exec.Command
8575
require.NoError(t, c.StartIfNeeded())
8676
}

internal/expr/daemon/daemon_test.go

Lines changed: 40 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,41 @@ import (
1616
"github.com/stretchr/testify/require"
1717
)
1818

19-
func TestDaemon_ServeAndPing(t *testing.T) {
20-
corpusAMPR := testutil.FindMPR(t, "CORPUS_A_MPR", "testdata/corpus-a/app.mpr")
21-
22-
d, err := daemon.New(corpusAMPR, 5*time.Minute)
23-
require.NoError(t, err)
24-
25-
go func() { _ = d.Serve() }()
26-
t.Cleanup(func() { _ = d.Stop() })
27-
28-
// 等待 socket 出现 (最多 2s)
29-
sockPath := daemon.SocketPath(corpusAMPR)
30-
deadline := time.Now().Add(2 * time.Second)
19+
// waitForAlive polls the socket until it is alive or a 6-second deadline
20+
// expires. 6 s covers the corpus-a index build (~5 s) plus headroom.
21+
func waitForAlive(t *testing.T, sockPath string) {
22+
t.Helper()
23+
deadline := time.Now().Add(6 * time.Second)
3124
for time.Now().Before(deadline) {
3225
if daemon.IsAlive(sockPath) {
33-
break
26+
return
3427
}
3528
time.Sleep(20 * time.Millisecond)
3629
}
37-
require.True(t, daemon.IsAlive(sockPath), "socket should be alive after Serve")
30+
require.True(t, daemon.IsAlive(sockPath), "daemon not alive within 6 s at %s", sockPath)
31+
}
32+
33+
// newTestDaemon starts a daemon bound to a unique socket in t.TempDir()
34+
// so tests that call Serve() can run in parallel without socket conflicts.
35+
func newTestDaemon(t *testing.T, mprPath string) *daemon.Daemon {
36+
t.Helper()
37+
sock := filepath.Join(t.TempDir(), "d.sock")
38+
d, err := daemon.NewWithSocket(mprPath, sock, 5*time.Minute)
39+
require.NoError(t, err)
40+
go func() { _ = d.Serve() }()
41+
t.Cleanup(func() { _ = d.Stop() })
42+
return d
43+
}
44+
45+
func TestDaemon_ServeAndPing(t *testing.T) {
46+
t.Parallel()
47+
corpusAMPR := testutil.FindMPR(t, "CORPUS_A_MPR", "testdata/corpus-a/app.mpr")
48+
49+
d := newTestDaemon(t, corpusAMPR)
50+
waitForAlive(t, d.SocketPath())
3851

3952
// 用空 MprPath 触发 ping 路径
40-
conn, err := net.Dial("unix", sockPath)
53+
conn, err := net.Dial("unix", d.SocketPath())
4154
require.NoError(t, err)
4255
defer conn.Close()
4356
require.NoError(t, json.NewEncoder(conn).Encode(daemon.ValidateRequest{}))
@@ -51,24 +64,13 @@ func TestDaemon_ServeAndPing(t *testing.T) {
5164
}
5265

5366
func TestDaemon_ValidateReturnsResults(t *testing.T) {
67+
t.Parallel()
5468
corpusAMPR := testutil.FindMPR(t, "CORPUS_A_MPR", "testdata/corpus-a/app.mpr")
5569

56-
d, err := daemon.New(corpusAMPR, 5*time.Minute)
57-
require.NoError(t, err)
58-
go func() { _ = d.Serve() }()
59-
t.Cleanup(func() { _ = d.Stop() })
60-
61-
sockPath := daemon.SocketPath(corpusAMPR)
62-
deadline := time.Now().Add(2 * time.Second)
63-
for time.Now().Before(deadline) {
64-
if daemon.IsAlive(sockPath) {
65-
break
66-
}
67-
time.Sleep(20 * time.Millisecond)
68-
}
69-
require.True(t, daemon.IsAlive(sockPath))
70+
d := newTestDaemon(t, corpusAMPR)
71+
waitForAlive(t, d.SocketPath())
7072

71-
conn, err := net.Dial("unix", sockPath)
73+
conn, err := net.Dial("unix", d.SocketPath())
7274
require.NoError(t, err)
7375
defer conn.Close()
7476
require.NoError(t, json.NewEncoder(conn).Encode(daemon.ValidateRequest{
@@ -78,12 +80,11 @@ func TestDaemon_ValidateReturnsResults(t *testing.T) {
7880
require.NoError(t, json.NewDecoder(conn).Decode(&resp))
7981

8082
assert.Empty(t, resp.Error, "no daemon-side error expected")
81-
// corpus-a 项目期望至少有少量 SEM 命中(不强求精确数字)
82-
// 仅验证 daemon pipeline 跑通;results 可能为 0 也可能 >0。
8383
assert.NotNil(t, resp.Results)
8484
}
8585

8686
func TestDaemon_SocketDirExists(t *testing.T) {
87+
t.Parallel()
8788
corpusAMPR := testutil.FindMPR(t, "CORPUS_A_MPR", "testdata/corpus-a/app.mpr")
8889
d, err := daemon.New(corpusAMPR, 5*time.Minute)
8990
require.NoError(t, err)
@@ -95,6 +96,7 @@ func TestDaemon_SocketDirExists(t *testing.T) {
9596
}
9697

9798
func TestDaemon_NewWithSocket_HonoursOverride(t *testing.T) {
99+
t.Parallel()
98100
corpusAMPR := testutil.FindMPR(t, "CORPUS_A_MPR", "testdata/corpus-a/app.mpr")
99101
custom := filepath.Join(t.TempDir(), "custom.sock")
100102
d, err := daemon.NewWithSocket(corpusAMPR, custom, 5*time.Minute)
@@ -107,6 +109,7 @@ func TestDaemon_NewWithSocket_HonoursOverride(t *testing.T) {
107109
}
108110

109111
func TestDaemon_NewWithSocket_EmptyFallsBackToDefault(t *testing.T) {
112+
t.Parallel()
110113
corpusAMPR := testutil.FindMPR(t, "CORPUS_A_MPR", "testdata/corpus-a/app.mpr")
111114
d, err := daemon.NewWithSocket(corpusAMPR, "", 5*time.Minute)
112115
require.NoError(t, err)
@@ -117,21 +120,13 @@ func TestDaemon_NewWithSocket_EmptyFallsBackToDefault(t *testing.T) {
117120
}
118121

119122
func TestDaemon_NewWithSocket_ServeBindsCustom(t *testing.T) {
123+
t.Parallel()
120124
corpusAMPR := testutil.FindMPR(t, "CORPUS_A_MPR", "testdata/corpus-a/app.mpr")
121-
custom := filepath.Join(t.TempDir(), "bind.sock")
122-
d, err := daemon.NewWithSocket(corpusAMPR, custom, 5*time.Minute)
123-
require.NoError(t, err)
124-
go func() { _ = d.Serve() }()
125-
t.Cleanup(func() { _ = d.Stop() })
126125

127-
deadline := time.Now().Add(2 * time.Second)
128-
for time.Now().Before(deadline) {
129-
if daemon.IsAlive(custom) {
130-
break
131-
}
132-
time.Sleep(20 * time.Millisecond)
133-
}
134-
assert.True(t, daemon.IsAlive(custom),
126+
d := newTestDaemon(t, corpusAMPR)
127+
waitForAlive(t, d.SocketPath())
128+
129+
assert.True(t, daemon.IsAlive(d.SocketPath()),
135130
"Serve() must bind the override socket path")
136131
assert.False(t, daemon.IsAlive(daemon.SocketPath(corpusAMPR)),
137132
"default SocketPath must NOT be bound when override is given")

0 commit comments

Comments
 (0)