Files
olcrtc/internal/e2e/real_soak_test.go

155 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package e2e
import (
"context"
"errors"
"flag"
"testing"
"time"
enginebuiltin "github.com/openlibrecommunity/olcrtc/internal/engine/builtin"
)
// Real-carrier throughput soak: same pump as TestLocalThroughputSoak but
// over a real WebRTC provider (Jitsi, Telemost, WBStream).
//
// Quick start:
//
// go test -count=1 -v ./internal/e2e \
// -run '^TestRealThroughputSoak$' \
// -olcrtc.real-e2e \
// -olcrtc.real-soak \
// -olcrtc.real-soak-carrier=jitsi \
// -olcrtc.real-soak-transport=seichannel \
// -olcrtc.real-soak-duration=30m \
// -timeout=60m
var (
realSoakEnabled = flag.Bool( //nolint:gochecknoglobals // package-level state intentional
"olcrtc.real-soak",
false,
"run TestRealThroughputSoak (long-running real-carrier throughput pump)",
)
realSoakDuration = flag.Duration( //nolint:gochecknoglobals // package-level state intentional
"olcrtc.real-soak-duration",
5*time.Minute,
"how long to pump traffic per carrier×transport case",
)
realSoakCarrier = flag.String( //nolint:gochecknoglobals // package-level state intentional
"olcrtc.real-soak-carrier",
"jitsi",
"carrier(s) to use: comma-separated list (e.g. jitsi,telemost,wbstream)",
)
realSoakTransport = flag.String( //nolint:gochecknoglobals // package-level state intentional
"olcrtc.real-soak-transport",
"seichannel",
"transport(s): datachannel|videochannel|seichannel|vp8channel or comma-separated",
)
realSoakChunk = flag.Int( //nolint:gochecknoglobals // package-level state intentional
"olcrtc.real-soak-chunk",
4096,
"write/read chunk size in bytes",
)
realSoakProgress = flag.Duration( //nolint:gochecknoglobals // package-level state intentional
"olcrtc.real-soak-progress",
30*time.Second,
"how often to log throughput progress lines",
)
realSoakVerify = flag.Bool( //nolint:gochecknoglobals // package-level state intentional
"olcrtc.real-soak-verify",
true,
"verify echoed bytes match the sent pattern",
)
)
func TestRealThroughputSoak(t *testing.T) {
if !*realE2E {
t.Skip("real e2e disabled; pass -olcrtc.real-e2e to enable")
}
if !*realSoakEnabled {
t.Skip("real soak disabled; pass -olcrtc.real-soak to enable")
}
if *realSoakDuration <= 0 {
t.Skip("real soak duration is zero")
}
carriers := splitTestList(*realSoakCarrier)
if len(carriers) == 0 {
t.Fatal("no carriers specified in -olcrtc.real-soak-carrier")
}
transports, err := resolveLocalSoakTransports(*realSoakTransport)
if err != nil {
t.Fatalf("invalid -olcrtc.real-soak-transport=%q: %v", *realSoakTransport, err)
}
echoAddr := startEchoServer(t)
for _, carrierName := range carriers {
t.Run(carrierName, func(t *testing.T) {
roomCtx, cancelRoom := context.WithTimeout(context.Background(), *realSoakDuration+2*time.Minute)
defer cancelRoom()
roomURL := requireRealRoom(roomCtx, t, carrierName)
for _, transportName := range transports {
t.Run(transportName, func(t *testing.T) {
runRealSoakOnce(t, carrierName, transportName, roomURL, echoAddr)
})
}
})
}
}
func runRealSoakOnce(t *testing.T, carrierName, transportName, roomURL, echoAddr string) {
t.Helper()
const setupBudget = 90 * time.Second
t.Logf("[soak] carrier=%s transport=%s duration=%s chunk=%d verify=%t progress=%s",
carrierName, transportName, *realSoakDuration, *realSoakChunk,
*realSoakVerify, *realSoakProgress)
expectation := realE2ECaseExpectation(carrierName, transportName)
ctx, cancel := context.WithTimeout(context.Background(), *realSoakDuration+setupBudget)
defer cancel()
rt, err := startRealTunnel(ctx, t, carrierName, transportName, roomURL, testClientDeviceID, testClientDeviceID)
if err != nil {
if errors.Is(err, enginebuiltin.ErrAuthFailed) {
t.Skipf("auth failed (skip): %v", err)
}
if expectation == realE2EExpectUnstable || expectation == realE2EExpectFail {
t.Skipf("start tunnel failed (expected %s): %v", realE2EExpectationLabel(expectation), err)
}
t.Fatalf("start tunnel: %v", err)
}
_ = rt
conn, err := connectViaSOCKSWithin(rt.socksAddr, echoAddr, setupBudget)
if err != nil {
t.Fatalf("connect via SOCKS: %v", err)
}
defer func() { _ = conn.Close() }()
pumpCtx, cancelPump := context.WithTimeout(context.Background(), *realSoakDuration)
defer cancelPump()
stats := runLocalSoakPump(pumpCtx, t, conn, *realSoakChunk, *realSoakVerify, *realSoakProgress)
if stats.sent == 0 || stats.recv == 0 {
t.Fatalf("no traffic moved: sent=%d recv=%d", stats.sent, stats.recv)
}
if stats.err != nil && !isExpectedShutdownErr(stats.err) {
t.Fatalf("pump error: %v", stats.err)
}
t.Logf("[soak] DONE carrier=%s transport=%s elapsed=%s sent=%s recv=%s send=%s/s recv=%s/s",
carrierName, transportName,
stats.elapsed.Round(time.Second),
humanBytes(stats.sent),
humanBytes(stats.recv),
humanBytes(int64(float64(stats.sent)/stats.elapsed.Seconds())),
humanBytes(int64(float64(stats.recv)/stats.elapsed.Seconds())),
)
}