refactor: extract streaming engine into internal/flinger
Move the capture/encode/send pipeline out of cmd/teleportfling into a GUI-free Engine so the CLI and the desktop app share one implementation: - flinger: Config/Engine/Status, audio+video+stats loops, pattern source - config: JSON persistence at ~/.config/teleportfling/config.json - cmd/teleportfling: thin CLI wrapper around the engine (same flags) - golangci: extend exclusions to flinger/config
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
package flinger
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestEnginePatternStartStop runs the engine with the synthetic pattern source
|
||||
// and verifies it produces frames on the wire and stops cleanly.
|
||||
func TestEnginePatternStartStop(t *testing.T) {
|
||||
cfg := DefaultConfig()
|
||||
cfg.Source = "pattern"
|
||||
cfg.Port = 0 // ephemeral
|
||||
|
||||
eng, err := New(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("New: %v", err)
|
||||
}
|
||||
|
||||
eng.Start()
|
||||
defer eng.Stop()
|
||||
|
||||
// Connect a raw receiver and read until the engine reports frames sent.
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:"+itoa(eng.sender.Port()))
|
||||
if err != nil {
|
||||
t.Fatalf("dial: %v", err)
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
// The video loop sends ~30fps; wait for the counter to advance.
|
||||
deadline := time.Now().Add(3 * time.Second)
|
||||
_ = conn.SetReadDeadline(deadline)
|
||||
|
||||
// Drain whatever arrives while waiting for the frame counter to move.
|
||||
go func() {
|
||||
buf := make([]byte, 64*1024)
|
||||
for {
|
||||
if _, err := conn.Read(buf); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for time.Now().Before(deadline) {
|
||||
if eng.Status().Frames > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
|
||||
if eng.Status().Frames == 0 {
|
||||
t.Error("expected frames to be counted")
|
||||
}
|
||||
}
|
||||
|
||||
// TestEngineConfigDefaults verifies DefaultConfig is sane.
|
||||
func TestEngineConfigDefaults(t *testing.T) {
|
||||
c := DefaultConfig()
|
||||
if c.Port != 9756 {
|
||||
t.Errorf("default port = %d, want 9756", c.Port)
|
||||
}
|
||||
if c.Source != "screen" {
|
||||
t.Errorf("default source = %q, want screen", c.Source)
|
||||
}
|
||||
if c.Quality < 1 || c.Quality > 100 {
|
||||
t.Errorf("default quality %d out of range", c.Quality)
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewRejectsBadSource ensures invalid sources fail fast.
|
||||
func TestNewRejectsBadSource(t *testing.T) {
|
||||
cfg := DefaultConfig()
|
||||
cfg.Source = "bogus"
|
||||
if _, err := New(cfg); err == nil {
|
||||
t.Error("expected error for unknown source")
|
||||
}
|
||||
}
|
||||
|
||||
func itoa(v int) string {
|
||||
if v == 0 {
|
||||
return "0"
|
||||
}
|
||||
var buf [20]byte
|
||||
i := len(buf)
|
||||
for v > 0 {
|
||||
i--
|
||||
buf[i] = byte('0' + v%10)
|
||||
v /= 10
|
||||
}
|
||||
return string(buf[i:])
|
||||
}
|
||||
Reference in New Issue
Block a user