Skip to content

Commit db617fa

Browse files
committed
test(transport/file): goleak-track Conn.Close
`transport/file/main_test.go` runs `goleak.VerifyTestMain`, so a leaked server goroutine eventually fails the package — but at teardown, with no signal pointing to the test that produced the leak. Mirror `transport/ssh`'s `TestConn_Close_Idempotent` shape: open a real Conn, call `Close` twice, and wrap the body in `goleak.VerifyNone(t)` so the failure mode is per-test. The Conn is constructed against a real on-disk fixture so the goroutine reaches its real shutdown path (`cancel → pipe close → <-c.done`); a struct-literal Conn would short-circuit before that path runs. The test is not marked `t.Parallel()`: `goleak.VerifyNone` snapshots the live goroutine set, so concurrent `testing.tRunner` frames from sibling parallel tests register as unexpected stacks and fail the verification. The package-wide `VerifyTestMain` already covers cross-test leak surfaces; this test pins the per-test shutdown contract. Signed-off-by: Hidde Beydals <hidde@hhh.computer>
1 parent 9d61dcc commit db617fa

1 file changed

Lines changed: 43 additions & 0 deletions

File tree

transport/file/conn_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package filet
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
"go.uber.org/goleak"
8+
9+
"github.com/hiddeco/go-ls-remote/transport"
10+
)
11+
12+
// TestConn_Close_NoGoroutineLeaks pins the goroutine-shutdown
13+
// contract for [Conn.Close]: the single server goroutine that
14+
// [Transport.Open] spawns must exit before the second [Conn.Close]
15+
// returns. Wrapping the call sequence in `goleak.VerifyNone` turns
16+
// a leaked server goroutine into a per-test failure; the
17+
// package-wide `goleak.VerifyTestMain` only catches the leak at
18+
// teardown, which obscures the failing call site.
19+
//
20+
// The [transport.Conn] is constructed via the same fixture helpers
21+
// the rest of the package uses so the goroutine reaches its real
22+
// shutdown path (cancel → pipe-close → `<-c.done`), not a
23+
// short-circuit on an uninitialised struct.
24+
//
25+
//nolint:paralleltest // goleak.VerifyNone snapshots live goroutines; parallel siblings register as unexpected stacks.
26+
func TestConn_Close_NoGoroutineLeaks(t *testing.T) {
27+
defer goleak.VerifyNone(t)
28+
29+
gitdir := materializeServeableFixture(t, "empty")
30+
u, err := transport.ParseURL("file://" + gitdir)
31+
require.NoError(t, err)
32+
33+
tr := New()
34+
conn, err := tr.Open(t.Context(), u, transport.OpenOptions{
35+
UserAgent: "close-leak-test/0.0",
36+
})
37+
require.NoError(t, err)
38+
39+
require.NoError(t, conn.Close(),
40+
"first Close returns nil on a clean shutdown")
41+
require.NoError(t, conn.Close(),
42+
"second Close is a no-op returning nil; goleak verifies the goroutine left")
43+
}

0 commit comments

Comments
 (0)