mirror of
https://github.com/openlibrecommunity/olcrtc.git
synced 2026-06-05 03:49:44 +00:00
155 lines
4.9 KiB
Go
155 lines
4.9 KiB
Go
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())),
|
||
)
|
||
}
|