From bb2e1ee1c8e2ae0ae7c4b5d65c8726885a62bcec Mon Sep 17 00:00:00 2001 From: zarazaex69 Date: Sun, 24 May 2026 02:42:13 +0300 Subject: [PATCH] test(e2e): allow multiple transports in local soak test via comma-separated list or 'all' --- internal/e2e/local_soak_test.go | 72 +++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/internal/e2e/local_soak_test.go b/internal/e2e/local_soak_test.go index 2f9fbcb..0dc2f17 100644 --- a/internal/e2e/local_soak_test.go +++ b/internal/e2e/local_soak_test.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "net" + "strings" "sync" "sync/atomic" "testing" @@ -37,6 +38,11 @@ import ( // -olcrtc.local-soak-duration=12h \ // -timeout=13h // +// To pump every built-in transport sequentially in a single run pass +// `-olcrtc.local-soak-transport=all` (or a comma-separated subset like +// `datachannel,vp8channel`). Each transport gets its own subtest and its +// own full -olcrtc.local-soak-duration window. +// // The test is gated by -olcrtc.local-soak so it never runs in regular CI. var ( @@ -53,7 +59,8 @@ var ( localSoakTransport = flag.String( //nolint:gochecknoglobals // package-level state intentional "olcrtc.local-soak-transport", transportData, - "transport to pump through: datachannel|videochannel|seichannel|vp8channel", + "transport(s) to pump through: datachannel|videochannel|seichannel|vp8channel, "+ + "or 'all', or a comma-separated subset (e.g. datachannel,vp8channel)", ) localSoakChunk = flag.Int( //nolint:gochecknoglobals // package-level state intentional "olcrtc.local-soak-chunk", @@ -90,15 +97,34 @@ func TestLocalThroughputSoak(t *testing.T) { t.Fatalf("invalid -olcrtc.local-soak-chunk=%d", *localSoakChunk) } + transports, err := resolveLocalSoakTransports(*localSoakTransport) + if err != nil { + t.Fatalf("invalid -olcrtc.local-soak-transport=%q: %v", *localSoakTransport, err) + } + + for _, transportName := range transports { + t.Run(transportName, func(t *testing.T) { + runLocalSoakOnce(t, transportName) + }) + } +} + +// runLocalSoakOnce builds a fresh tunnel for transportName and pumps it +// for one full -olcrtc.local-soak-duration window. Each subtest gets its +// own carrier, SOCKS port and goroutines via t.Cleanup, so transports +// don't share state and a leak in one of them won't poison the next. +func runLocalSoakOnce(t *testing.T, transportName string) { + t.Helper() + // Connection setup itself can be slow (first WebRTC handshake on // some transports), so don't fold it into the duration budget. const setupBudget = 30 * time.Second t.Logf("[soak] transport=%s duration=%s chunk=%d verify=%t progress=%s", - *localSoakTransport, *localSoakDuration, *localSoakChunk, + transportName, *localSoakDuration, *localSoakChunk, *localSoakVerify, *localSoakProgress) - rt := startLocalSoakTunnel(t, *localSoakTransport) + rt := startLocalSoakTunnel(t, transportName) echoAddr := startEchoServer(t) conn, err := connectViaSOCKSWithin(rt.socksAddr, echoAddr, setupBudget) @@ -120,7 +146,7 @@ func TestLocalThroughputSoak(t *testing.T) { } t.Logf("[soak] DONE transport=%s elapsed=%s sent=%s recv=%s send=%s/s recv=%s/s", - *localSoakTransport, + transportName, stats.elapsed.Round(time.Second), humanBytes(stats.sent), humanBytes(stats.recv), @@ -129,6 +155,44 @@ func TestLocalThroughputSoak(t *testing.T) { ) } +// resolveLocalSoakTransports turns the -olcrtc.local-soak-transport flag +// value into an ordered, deduplicated list of built-in transport names. +// Accepts "all" as a shorthand for builtInTransportNames(), or a +// comma-separated subset (with whitespace tolerated around items). +func resolveLocalSoakTransports(value string) ([]string, error) { + trimmed := strings.TrimSpace(value) + if trimmed == "" { + return nil, errors.New("empty value") + } + if strings.EqualFold(trimmed, "all") { + return builtInTransportNames(), nil + } + + known := make(map[string]struct{}, 4) + for _, name := range builtInTransportNames() { + known[name] = struct{}{} + } + + items := splitTestList(trimmed) + if len(items) == 0 { + return nil, errors.New("no transports listed") + } + + seen := make(map[string]struct{}, len(items)) + out := make([]string, 0, len(items)) + for _, name := range items { + if _, ok := known[name]; !ok { + return nil, fmt.Errorf("unknown transport %q", name) + } + if _, dup := seen[name]; dup { + continue + } + seen[name] = struct{}{} + out = append(out, name) + } + return out, nil +} + // startLocalSoakTunnel mirrors startTunnel but lets the caller pick the // transport (the original is hard-coded to datachannel). func startLocalSoakTunnel(t *testing.T, transportName string) *tunnelRuntime {