mirror of
https://github.com/openlibrecommunity/olcrtc.git
synced 2026-05-30 00:49:44 +00:00
418 lines
10 KiB
Go
418 lines
10 KiB
Go
//nolint:all // Test file keeps scenario setup inline.
|
|
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
|
|
|
|
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")
|
|
}
|
|
}
|
|
|
|
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) {
|
|
t.Fatalf("startWithConfig(missing carrier) = %v", err)
|
|
}
|
|
if err := startWithConfig("telemost", dataTransport, "", "client", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errRoomIDRequired) {
|
|
t.Fatalf("startWithConfig(missing room) = %v", err)
|
|
}
|
|
if err := startWithConfig("jazz", dataTransport, "", "", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errClientIDRequired) {
|
|
t.Fatalf("startWithConfig(missing client) = %v", err)
|
|
}
|
|
if err := startWithConfig("jazz", dataTransport, "", "client", "", 1080, "", "", mobileConfig{}); !errors.Is(err, errKeyHexRequired) {
|
|
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) {
|
|
t.Fatalf("startWithConfig(running) = %v", err)
|
|
}
|
|
resetMobileGlobals(t)
|
|
}
|
|
|
|
func TestStartWithInjectedRunnerLifecycle(t *testing.T) {
|
|
resetMobileGlobals(t)
|
|
t.Cleanup(func() {
|
|
resetMobileGlobals(t)
|
|
})
|
|
|
|
runClientWithReady = func(
|
|
ctx context.Context,
|
|
linkName, transportName, carrierName, roomURL, keyHex, clientID string,
|
|
localAddr string,
|
|
dnsServer, socksUser, socksPass string,
|
|
onReady func(),
|
|
videoWidth int,
|
|
videoHeight int,
|
|
videoFPS int,
|
|
videoBitrate string,
|
|
videoHW string,
|
|
videoQRSize int,
|
|
videoQRRecovery string,
|
|
videoCodec string,
|
|
videoTileModule int,
|
|
videoTileRS int,
|
|
vp8FPS int,
|
|
vp8BatchSize int,
|
|
seiFPS int,
|
|
seiBatchSize int,
|
|
seiFragmentSize int,
|
|
seiAckTimeoutMS int,
|
|
) 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")
|
|
}
|
|
}
|
|
|
|
func TestStartUsesDefaultsAndCheckWithInjectedRunner(t *testing.T) {
|
|
resetMobileGlobals(t)
|
|
t.Cleanup(func() {
|
|
resetMobileGlobals(t)
|
|
})
|
|
|
|
runClientWithReady = func(
|
|
ctx context.Context,
|
|
linkName, transportName, carrierName, roomURL, keyHex, clientID string,
|
|
localAddr string,
|
|
dnsServer, socksUser, socksPass string,
|
|
onReady func(),
|
|
videoWidth int,
|
|
videoHeight int,
|
|
videoFPS int,
|
|
videoBitrate string,
|
|
videoHW string,
|
|
videoQRSize int,
|
|
videoQRRecovery string,
|
|
videoCodec string,
|
|
videoTileModule int,
|
|
videoTileRS int,
|
|
vp8FPS int,
|
|
vp8BatchSize int,
|
|
seiFPS int,
|
|
seiBatchSize int,
|
|
seiFragmentSize int,
|
|
seiAckTimeoutMS int,
|
|
) 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,
|
|
linkName, transportName, carrierName, roomURL, keyHex, clientID string,
|
|
localAddr string,
|
|
dnsServer, socksUser, socksPass string,
|
|
onReady func(),
|
|
videoWidth int,
|
|
videoHeight int,
|
|
videoFPS int,
|
|
videoBitrate string,
|
|
videoHW string,
|
|
videoQRSize int,
|
|
videoQRRecovery string,
|
|
videoCodec string,
|
|
videoTileModule int,
|
|
videoTileRS int,
|
|
vp8FPS int,
|
|
vp8BatchSize int,
|
|
seiFPS int,
|
|
seiBatchSize int,
|
|
seiFragmentSize int,
|
|
seiAckTimeoutMS int,
|
|
) 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,
|
|
linkName, transportName, carrierName, roomURL, keyHex, clientID string,
|
|
localAddr string,
|
|
dnsServer, socksUser, socksPass string,
|
|
onReady func(),
|
|
videoWidth int,
|
|
videoHeight int,
|
|
videoFPS int,
|
|
videoBitrate string,
|
|
videoHW string,
|
|
videoQRSize int,
|
|
videoQRRecovery string,
|
|
videoCodec string,
|
|
videoTileModule int,
|
|
videoTileRS int,
|
|
vp8FPS int,
|
|
vp8BatchSize int,
|
|
seiFPS int,
|
|
seiBatchSize int,
|
|
seiFragmentSize int,
|
|
seiAckTimeoutMS int,
|
|
) error {
|
|
<-ctx.Done()
|
|
return nil
|
|
}
|
|
if _, err := Check("telemost", defaultTransport, "room", "client", "key", 1083, 1, 30, 1); !errors.Is(err, errStartTimedOut) {
|
|
t.Fatalf("Check(timeout) error = %v, want %v", err, errStartTimedOut)
|
|
}
|
|
|
|
want := errors.New("check failed")
|
|
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,
|
|
) 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 = errors.New("run failed")
|
|
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)
|
|
}
|
|
}
|