Files
TeleportFling/internal/protocol/protocol_test.go
T
petere 10fa72b228 feat: implement M1 teleport protocol sender
Stream a synthetic test pattern and silent PCM audio over the OBS
Teleport protocol:
- protocol: wire format (Header/ImageHeader/WaveHeader), BT.709 full
  range colour matrix, JPEG encode via turbojpeg cgo, WAVE packet builder
- output: TCP sender with per-connection buffered channels and drop-on-overflow
- discovery: multicast announce via peerdiscovery
- cmd: teleportfling CLI with flags, test-pattern frame generator

Verified end-to-end: OBS discovers and renders the stream with correct
colours and motion.
2026-09-18 18:36:25 +01:00

116 lines
3.6 KiB
Go

package protocol
import (
"bytes"
"testing"
)
// TestWriteReadVideoPacket verifies the full video packet round-trip through
// WritePacket/ReadPacket, including the little-endian float32s in the image
// header and the exact byte layout (Header + ImageHeader + payload).
func TestWriteReadVideoPacket(t *testing.T) {
img := DefaultBT709Full()
img.ColorMatrix[0] = 0.12345
img.ColorRangeMax[2] = 0.9999
h := Header{Type: VideoType, Timestamp: 1_700_000_000, Size: 5}
wire, err := WritePacket(h, &img, nil, []byte("hello"))
if err != nil {
t.Fatalf("WritePacket: %v", err)
}
gotH, gotImg, gotWave, payload, err := ReadPacket(bytes.NewReader(wire))
if err != nil {
t.Fatalf("ReadPacket: %v", err)
}
if gotH != h {
t.Errorf("header mismatch: got %+v want %+v", gotH, h)
}
if gotImg == nil || gotWave != nil {
t.Fatalf("expected image header and no wave header")
}
if *gotImg != img {
t.Errorf("image header mismatch:\n got %+v\nwant %+v", *gotImg, img)
}
if string(payload) != "hello" {
t.Errorf("payload mismatch: got %q", payload)
}
}
// TestVideoPacketWireSize locks the on-wire size of a video packet to the
// reference layout: 16-byte Header + 80-byte ImageHeader + payload.
func TestVideoPacketWireSize(t *testing.T) {
const payload = 10
ih := DefaultBT709Full()
h := Header{Type: VideoType, Size: payload}
wire, err := WritePacket(h, &ih, nil, make([]byte, payload))
if err != nil {
t.Fatalf("WritePacket: %v", err)
}
if len(wire) != headerSize+imageHeaderSize+payload {
t.Errorf("wire size = %d, want %d", len(wire), headerSize+imageHeaderSize+payload)
}
}
// TestWriteReadAudioPacket round-trips a WAVE packet and checks that the
// WaveHeader fields arrive intact.
func TestWriteReadAudioPacket(t *testing.T) {
pcm := make([]byte, 480*2*4) // 480 frames, 2 ch, 4 bytes float
h := Header{Type: AudioType, Timestamp: 42, Size: int32(len(pcm))}
w := WaveHeader{Format: AudioFormatF32, SampleRate: 48000, Speakers: 2, Frames: 480}
wire, err := WritePacket(h, nil, &w, pcm)
if err != nil {
t.Fatalf("WritePacket: %v", err)
}
if len(wire) != headerSize+waveHeaderSize+len(pcm) {
t.Errorf("wire size = %d, want %d", len(wire), headerSize+waveHeaderSize+len(pcm))
}
_, gotImg, gotWave, payload, err := ReadPacket(bytes.NewReader(wire))
if err != nil {
t.Fatalf("ReadPacket: %v", err)
}
if gotWave == nil || gotImg != nil {
t.Fatalf("expected wave header and no image header")
}
if *gotWave != w {
t.Errorf("wave header mismatch: got %+v want %+v", *gotWave, w)
}
if !bytes.Equal(payload, pcm) {
t.Errorf("pcm payload mismatch")
}
}
// TestBuildWavePacket checks the convenience builder enforces PCM length
// vs. the declared format/speakers/frames.
func TestBuildWavePacket(t *testing.T) {
good := make([]byte, 480*2*4)
if _, err := BuildWavePacket(1, AudioFormatF32, 48000, 2, 480, good); err != nil {
t.Errorf("valid packet rejected: %v", err)
}
if _, err := BuildWavePacket(1, AudioFormatF32, 48000, 2, 480, good[:len(good)-1]); err == nil {
t.Error("truncated pcm accepted")
}
u8 := make([]byte, 480) // 1 ch, 480 frames, 1 byte
if _, err := BuildWavePacket(1, AudioFormatU8, 48000, 1, 480, u8); err != nil {
t.Errorf("u8 packet rejected: %v", err)
}
}
// TestUnsupportedPacketType ensures ReadPacket rejects unknown markers by
// feeding it a raw 16-byte header with a non-"JPEG"/"WAVE" type.
func TestUnsupportedPacketType(t *testing.T) {
var wire [headerSize]byte
wire[0] = 'Z'
wire[1] = 'Z'
wire[2] = 'Z'
wire[3] = 'Z'
if _, _, _, _, err := ReadPacket(bytes.NewReader(wire[:])); err == nil {
t.Error("unknown packet type accepted")
}
}