Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"time"

"github.com/quic-go/quic-go/integrationtests/tools/israce"
"go.uber.org/goleak"
"golang.org/x/net/proxy"

"github.com/anywherelan/awl/api"
Expand Down Expand Up @@ -279,9 +278,6 @@ func TestUpdateUseAsExitNodeConfig(t *testing.T) {

ts.makeFriends(peer2, peer1)

current := goleak.IgnoreCurrent()
goleak.VerifyNone(t, current)

info, err := peer1.api.PeerInfo()
ts.NoError(err)
ts.Equal("", info.SOCKS5.UsingPeerID)
Expand Down Expand Up @@ -847,9 +843,6 @@ func TestTunnelPackets(t *testing.T) {

ts.makeFriends(peer2, peer1)

current := goleak.IgnoreCurrent()
goleak.VerifyNone(t, current)

const packetSize = 2500
const packetsCount = 2600 // approx 1.1 p2p streams

Expand Down
45 changes: 37 additions & 8 deletions test_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"fmt"
"net"
"os"
"runtime"
"sync/atomic"
"testing"
"time"
Expand All @@ -27,6 +26,7 @@ import (
"github.com/marcopolo/simnet"
"github.com/multiformats/go-multiaddr"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
"go.uber.org/zap/zapcore"
"golang.zx2c4.com/wireguard/tun"

Expand All @@ -39,7 +39,6 @@ import (

const TestTUNBatchSize = 100

// TODO: add support for goleak in TestSuite
type TestSuite struct {
*require.Assertions

Expand All @@ -50,10 +49,33 @@ type TestSuite struct {
}

func NewTestSuite(t testing.TB) *TestSuite {
// TODO: fix
if os.Getenv("CI") != "" && runtime.GOOS == "linux" {
t.Skip("doesn't work on linux in CI, flaky ensurePeersAvailableInDHT can't find peers")
}
// Snapshot goroutine state before anything starts. Because t.Cleanup is LIFO,
// registering here means this check runs last — after all peers and bootstrap
// nodes have been closed — so it reliably detects goroutine leaks.
//
// libp2p.NATPortMap() spawns a short-lived UPnP SSDP discovery goroutine
// (koron/go-ssdp.Search) with a 5-second timeout. It exits on its own and
// is not a real leak, so we filter it out.
ignoreCurrent := goleak.IgnoreCurrent()
t.Cleanup(func() {
goleak.VerifyNone(t, ignoreCurrent,
// UPnP/NAT discovery (Linux): spawned by libp2p.NATPortMap(), self-terminates
// after its timeout (~5s). koron/go-ssdp#6 (closed invalid) rejected adding
// context.Context to Search(), so there is no way to cancel it earlier.
goleak.IgnoreAnyFunction("github.com/koron/go-ssdp.Search"),
// NAT-PMP discovery (macOS/Windows): also spawned by libp2p.NATPortMap(),
// blocks on a UDP read waiting for a router response until its ~10s timeout.
// jackpal/go-nat-pmp has no context/cancellation support and no upstream
// issue tracking this.
goleak.IgnoreAnyFunction("github.com/jackpal/go-nat-pmp.(*Client).GetExternalAddress"),
// go-flow-metrics sweeper: package-level singleton started lazily by
// libp2p bandwidth tracking; intentionally lives for the process lifetime.
// No upstream issue exists for making it stoppable (the closest is
// libp2p/go-flow-metrics#23 which asks for a customizable sweeper but
// does not address stopping it).
goleak.IgnoreAnyFunction("github.com/libp2p/go-flow-metrics.(*sweeper).runActive"),
)
})

ts := &TestSuite{t: t, Assertions: require.New(t)}
ts.initBootstrapNode()
Expand Down Expand Up @@ -371,7 +393,11 @@ func (t *testTun) Read(bufs [][]byte, sizes []int, offset int) (n int, err error
return 0, os.ErrClosed
}
if len(t.t.outboundBuf) == 0 {
t.t.outboundBuf = <-t.t.Outbound
var ok bool
t.t.outboundBuf, ok = <-t.t.Outbound
if !ok {
return 0, os.ErrClosed
}
}

for i := range min(len(bufs), len(t.t.outboundBuf)) {
Expand Down Expand Up @@ -414,7 +440,10 @@ func (t *testTun) MTU() (int, error) { return vpn.InterfaceMTU, nil }
func (t *testTun) Name() (string, error) { return "testTun", nil }
func (t *testTun) Events() <-chan tun.Event { return t.t.events }
func (t *testTun) Close() error {
t.t.isClosed.Store(true)
if t.t.isClosed.Swap(true) {
return nil
}
close(t.t.Outbound)
close(t.t.events)
return nil
}
Expand Down