mirror of
https://github.com/openlibrecommunity/olcrtc.git
synced 2026-05-26 07:08:11 +00:00
Break CLI backwards compatibility as planned for refactor/universal-carrier: - Drop -carrier flag; add -auth (auth provider name), -engine (engine name for -auth none), -url and -token (SFU endpoint + access token for direct/none auth mode). - session.Config.Carrier → Auth + Engine + URL + Token. - session.Gen() is now generic: auth.Get(cfg.Auth).(auth.RoomCreator) replaces the hard-coded switch on carrier names. - Register a "none" carrier in builtin (registerDirect) that bypasses auth and connects directly to any engine with caller-supplied URL+Token. - auth/telemost.Provider.Issue now accepts a raw room-ID hash in addition to a full https://telemost.yandex.ru/j/<id> URL. - Plumb Engine/URL/Token from session.Config through server.Run, client.Run/RunWithReady, bringUpLink, link.Config, transport.Config, and carrier.Config so the "none" carrier has access to them end-to-end. - Update all tests and mobile.go call sites. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
430 lines
10 KiB
Go
430 lines
10 KiB
Go
package mobile
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"log"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/openlibrecommunity/olcrtc/internal/logger"
|
|
"github.com/openlibrecommunity/olcrtc/internal/protect"
|
|
)
|
|
|
|
type testProtector struct {
|
|
called int
|
|
}
|
|
|
|
func (p *testProtector) Protect(fd int) bool {
|
|
p.called = fd
|
|
return true
|
|
}
|
|
|
|
type testLogWriter struct {
|
|
got string
|
|
}
|
|
|
|
func (w *testLogWriter) WriteLog(msg string) {
|
|
w.got += msg
|
|
}
|
|
|
|
func resetMobileGlobals(t *testing.T) {
|
|
t.Helper()
|
|
mu.Lock()
|
|
if cancel != nil {
|
|
cancel()
|
|
}
|
|
cancel = nil
|
|
done = nil
|
|
ready = nil
|
|
errRun = nil
|
|
runClientWithReady = clientRunWithReady
|
|
defaults = mobileConfig{}
|
|
defaultsSet = sync.Once{}
|
|
mu.Unlock()
|
|
protect.Protector = nil
|
|
logger.SetVerbose(false)
|
|
}
|
|
|
|
var clientRunWithReady = runClientWithReady //nolint:gochecknoglobals // package-level state intentional
|
|
|
|
var (
|
|
errMobileCheckFailed = errors.New("check failed")
|
|
errMobileRunFailed = errors.New("run failed")
|
|
)
|
|
|
|
func TestProtectorAndLogging(t *testing.T) {
|
|
resetMobileGlobals(t)
|
|
p := &testProtector{}
|
|
SetProtector(p)
|
|
if protect.Protector == nil || !protect.Protector(123) || p.called != 123 {
|
|
t.Fatal("SetProtector() did not install adapter")
|
|
}
|
|
SetProtector(nil)
|
|
if protect.Protector != nil {
|
|
t.Fatal("SetProtector(nil) did not clear protector")
|
|
}
|
|
|
|
w := &testLogWriter{}
|
|
SetLogWriter(w)
|
|
log.Print("hello")
|
|
if !strings.Contains(w.got, "hello") {
|
|
t.Fatalf("log writer got %q, want hello", w.got)
|
|
}
|
|
}
|
|
|
|
func TestDefaultsAndSetters(t *testing.T) {
|
|
resetMobileGlobals(t)
|
|
|
|
SetTransport("dc")
|
|
SetLink("direct")
|
|
SetDNS("9.9.9.9:53")
|
|
SetVP8Options(-1, 999)
|
|
|
|
mu.Lock()
|
|
got := defaults
|
|
mu.Unlock()
|
|
if got.transport != dataTransport || got.link != defaultLink || got.dnsServer != "9.9.9.9:53" ||
|
|
got.vp8FPS != 1 || got.vp8BatchSize != 64 {
|
|
t.Fatalf("defaults = %+v", got)
|
|
}
|
|
|
|
SetDebug(true)
|
|
if !logger.IsVerbose() {
|
|
t.Fatal("SetDebug(true) did not enable verbose")
|
|
}
|
|
SetDebug(false)
|
|
if logger.IsVerbose() {
|
|
t.Fatal("SetDebug(false) did not disable verbose")
|
|
}
|
|
}
|
|
|
|
//nolint:cyclop // table-driven test naturally has many branches
|
|
func TestNormalizeBuildRoomAndClamp(t *testing.T) {
|
|
tests := map[string]string{
|
|
"datachannel": dataTransport,
|
|
"data": dataTransport,
|
|
"dc": dataTransport,
|
|
"vp8channel": defaultTransport,
|
|
"vp8": defaultTransport,
|
|
"bad": defaultTransport,
|
|
}
|
|
for in, want := range tests {
|
|
if got := normalizeTransport(in); got != want {
|
|
t.Fatalf("normalizeTransport(%q) = %q, want %q", in, got, want)
|
|
}
|
|
}
|
|
|
|
if normalizeCarrier(carrierWBStream) != carrierWBStream || normalizeCarrier("jazz") != "jazz" {
|
|
t.Fatal("normalizeCarrier() returned unexpected value")
|
|
}
|
|
|
|
if got := buildRoomURL("telemost", "abc"); got != "https://telemost.yandex.ru/j/abc" {
|
|
t.Fatalf("telemost room URL = %q", got)
|
|
}
|
|
if got := buildRoomURL("jazz", ""); got != "any" {
|
|
t.Fatalf("jazz empty room URL = %q", got)
|
|
}
|
|
if got := buildRoomURL(carrierWBStream, "room"); got != "room" {
|
|
t.Fatalf("wbstream room URL = %q", got)
|
|
}
|
|
|
|
if clampAtLeastOne(0, 10) != 1 || clampAtLeastOne(11, 10) != 10 || clampAtLeastOne(5, 10) != 5 {
|
|
t.Fatal("clampAtLeastOne() returned unexpected value")
|
|
}
|
|
}
|
|
|
|
func TestStartValidation(t *testing.T) {
|
|
resetMobileGlobals(t)
|
|
|
|
if err := startWithConfig("", dataTransport, "room", "client", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errCarrierRequired) { //nolint:lll // long test description
|
|
t.Fatalf("startWithConfig(missing carrier) = %v", err)
|
|
}
|
|
if err := startWithConfig("telemost", dataTransport, "", "client", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errRoomIDRequired) { //nolint:lll // long test description
|
|
t.Fatalf("startWithConfig(missing room) = %v", err)
|
|
}
|
|
if err := startWithConfig("jazz", dataTransport, "", "", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errClientIDRequired) { //nolint:lll // long test description
|
|
t.Fatalf("startWithConfig(missing client) = %v", err)
|
|
}
|
|
if err := startWithConfig("jazz", dataTransport, "", "client", "", 1080, "", "", mobileConfig{}); !errors.Is(err, errKeyHexRequired) { //nolint:lll // long test description
|
|
t.Fatalf("startWithConfig(missing key) = %v", err)
|
|
}
|
|
|
|
mu.Lock()
|
|
cancel = func() {}
|
|
mu.Unlock()
|
|
if err := startWithConfig("jazz", dataTransport, "", "client", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errAlreadyRunning) { //nolint:lll // long test description
|
|
t.Fatalf("startWithConfig(running) = %v", err)
|
|
}
|
|
resetMobileGlobals(t)
|
|
}
|
|
|
|
//nolint:cyclop // table-driven test naturally has many branches
|
|
func TestStartWithInjectedRunnerLifecycle(t *testing.T) {
|
|
resetMobileGlobals(t)
|
|
t.Cleanup(func() {
|
|
resetMobileGlobals(t)
|
|
})
|
|
|
|
runClientWithReady = func(
|
|
ctx context.Context,
|
|
linkName, transportName, carrierName, roomURL, _, clientID string,
|
|
localAddr string,
|
|
dnsServer, _, _ string,
|
|
onReady func(),
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ string,
|
|
_ string,
|
|
_ int,
|
|
_ string,
|
|
_ string,
|
|
_ int,
|
|
_ int,
|
|
vp8FPS int,
|
|
vp8BatchSize int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_, _, _ string,
|
|
) error {
|
|
if linkName != defaultLink || transportName != dataTransport || carrierName != carrierJazz ||
|
|
roomURL != "any" || clientID != "client" || localAddr != "127.0.0.1:1080" ||
|
|
dnsServer != defaultDNSServer || vp8FPS != 60 || vp8BatchSize != 8 {
|
|
t.Fatalf("RunWithReady args mismatch: link=%q transport=%q carrier=%q room=%q client=%q local=%q dns=%q vp8=%d/%d",
|
|
linkName, transportName, carrierName, roomURL, clientID, localAddr, dnsServer, vp8FPS, vp8BatchSize)
|
|
}
|
|
onReady()
|
|
<-ctx.Done()
|
|
return ctx.Err()
|
|
}
|
|
|
|
if err := StartWithTransport(carrierJazz, "dc", "", "client", "key", 1080, "", ""); err != nil {
|
|
t.Fatalf("StartWithTransport() error = %v", err)
|
|
}
|
|
if !IsRunning() {
|
|
t.Fatal("IsRunning() = false, want true")
|
|
}
|
|
if err := WaitReady(100); err != nil {
|
|
t.Fatalf("WaitReady() error = %v", err)
|
|
}
|
|
Stop()
|
|
if IsRunning() {
|
|
t.Fatal("IsRunning() = true after Stop")
|
|
}
|
|
}
|
|
|
|
//nolint:cyclop // table-driven test naturally has many branches
|
|
func TestStartUsesDefaultsAndCheckWithInjectedRunner(t *testing.T) {
|
|
resetMobileGlobals(t)
|
|
t.Cleanup(func() {
|
|
resetMobileGlobals(t)
|
|
})
|
|
|
|
runClientWithReady = func(
|
|
ctx context.Context,
|
|
_, transportName, _, roomURL, _, _ string,
|
|
localAddr string,
|
|
_, socksUser, socksPass string,
|
|
onReady func(),
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ string,
|
|
_ string,
|
|
_ int,
|
|
_ string,
|
|
_ string,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_, _, _ string,
|
|
) error {
|
|
if transportName != defaultTransport || roomURL != "https://telemost.yandex.ru/j/room" ||
|
|
localAddr != "127.0.0.1:1081" || socksUser != "u" || socksPass != "p" {
|
|
t.Fatalf("Start args mismatch: transport=%q room=%q local=%q user/pass=%q/%q",
|
|
transportName, roomURL, localAddr, socksUser, socksPass)
|
|
}
|
|
onReady()
|
|
<-ctx.Done()
|
|
return ctx.Err()
|
|
}
|
|
|
|
if err := Start("telemost", "room", "client", "key", 1081, "u", "p"); err != nil {
|
|
t.Fatalf("Start() error = %v", err)
|
|
}
|
|
if err := WaitReady(100); err != nil {
|
|
t.Fatalf("WaitReady() error = %v", err)
|
|
}
|
|
Stop()
|
|
|
|
runClientWithReady = func(
|
|
ctx context.Context,
|
|
_, transportName, _, _, _, _ string,
|
|
_ string,
|
|
_, _, _ string,
|
|
onReady func(),
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ string,
|
|
_ string,
|
|
_ int,
|
|
_ string,
|
|
_ string,
|
|
_ int,
|
|
_ int,
|
|
vp8FPS int,
|
|
vp8BatchSize int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_, _, _ string,
|
|
) error {
|
|
if transportName != dataTransport || vp8FPS != 1 || vp8BatchSize != 64 {
|
|
t.Fatalf("Check args mismatch: transport=%q vp8=%d/%d", transportName, vp8FPS, vp8BatchSize)
|
|
}
|
|
onReady()
|
|
<-ctx.Done()
|
|
return nil
|
|
}
|
|
elapsed, err := Check("jazz", "dc", "", "client", "key", 1082, 100, -1, 999)
|
|
if err != nil {
|
|
t.Fatalf("Check() error = %v", err)
|
|
}
|
|
if elapsed < 0 {
|
|
t.Fatalf("Check() elapsed = %d", elapsed)
|
|
}
|
|
}
|
|
|
|
func TestCheckTimeoutAndRunError(t *testing.T) {
|
|
resetMobileGlobals(t)
|
|
t.Cleanup(func() {
|
|
resetMobileGlobals(t)
|
|
})
|
|
|
|
runClientWithReady = func(
|
|
ctx context.Context,
|
|
_, _, _, _, _, _ string,
|
|
_ string,
|
|
_, _, _ string,
|
|
_ func(),
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ string,
|
|
_ string,
|
|
_ int,
|
|
_ string,
|
|
_ string,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_ int,
|
|
_, _, _ string,
|
|
) error {
|
|
<-ctx.Done()
|
|
return nil
|
|
}
|
|
if _, err := Check("telemost", defaultTransport, "room", "client", "key", 1083, 1, 30, 1); !errors.Is(err, errStartTimedOut) { //nolint:lll // long test description
|
|
t.Fatalf("Check(timeout) error = %v, want %v", err, errStartTimedOut)
|
|
}
|
|
|
|
want := errMobileCheckFailed
|
|
runClientWithReady = func(
|
|
context.Context,
|
|
string, string, string, string, string, string,
|
|
string,
|
|
string, string, string,
|
|
func(),
|
|
int, int, int,
|
|
string,
|
|
string,
|
|
int,
|
|
string,
|
|
string,
|
|
int,
|
|
int,
|
|
int,
|
|
int,
|
|
int,
|
|
int,
|
|
int,
|
|
int,
|
|
string, string, string,
|
|
) error {
|
|
return want
|
|
}
|
|
if _, err := Check("telemost", defaultTransport, "room", "client", "key", 1084, 100, 30, 1); !errors.Is(err, want) {
|
|
t.Fatalf("Check(run error) = %v, want %v", err, want)
|
|
}
|
|
}
|
|
|
|
func TestWaitReadyStatesAndStop(t *testing.T) {
|
|
resetMobileGlobals(t)
|
|
|
|
if err := WaitReady(1); !errors.Is(err, errNotRunning) {
|
|
t.Fatalf("WaitReady(not running) = %v", err)
|
|
}
|
|
|
|
mu.Lock()
|
|
errRun = errMobileRunFailed
|
|
mu.Unlock()
|
|
if err := WaitReady(1); err == nil || err.Error() != "run failed" {
|
|
t.Fatalf("WaitReady(run err) = %v", err)
|
|
}
|
|
|
|
mu.Lock()
|
|
errRun = nil
|
|
ready = make(chan struct{})
|
|
done = make(chan struct{})
|
|
cancel = func() {}
|
|
mu.Unlock()
|
|
if err := WaitReady(1); !errors.Is(err, errStartTimedOut) {
|
|
t.Fatalf("WaitReady(timeout) = %v", err)
|
|
}
|
|
|
|
mu.Lock()
|
|
close(ready)
|
|
mu.Unlock()
|
|
if err := WaitReady(1); err != nil {
|
|
t.Fatalf("WaitReady(ready) error = %v", err)
|
|
}
|
|
|
|
mu.Lock()
|
|
cancel = func() {}
|
|
done = make(chan struct{})
|
|
doneCh := done
|
|
mu.Unlock()
|
|
go func() {
|
|
time.Sleep(time.Millisecond)
|
|
close(doneCh)
|
|
}()
|
|
Stop()
|
|
mu.Lock()
|
|
cancel = nil
|
|
mu.Unlock()
|
|
}
|
|
|
|
func TestLogBridge(t *testing.T) {
|
|
w := &testLogWriter{}
|
|
n, err := (&logBridge{w: w}).Write([]byte("abc"))
|
|
if err != nil || n != 3 || w.got != "abc" {
|
|
t.Fatalf("logBridge.Write() = (%d, %v), got %q", n, err, w.got)
|
|
}
|
|
}
|