Files
RTSP-Streamer/internal/config/tiles_test.go
Levi Woodard 8d95c944bd Per-tile stream quality (high/low), fps cap, latency flags
Cameras carry multiple stream URLs (streams: high/medium/low); each
tile picks one via Quality (auto/high/low). Small tiles can pull the
low substream to cut Pi decode load. discover --enable-rtsp takes a
comma list (high,low), enables each channel, records all enabled ones.
TUI: q cycles a tile quality (shown in the tile); discover enables
high+low. Player: --framedrop + low-delay demuxer flags; player.max_fps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 07:18:57 -05:00

81 lines
2.6 KiB
Go

package config
import "testing"
func TestEffectiveTilesFromSlots(t *testing.T) {
l := Layout{Name: "l", Grid: "2x2", Slots: []string{"a", "", "", "b"}}
tiles := l.EffectiveTiles()
if len(tiles) != 2 {
t.Fatalf("got %d tiles, want 2", len(tiles))
}
// "a" at cell 0 -> (0,0); "b" at cell 3 -> (1,1).
if tiles[0].Camera != "a" || tiles[0].Col != 0 || tiles[0].Row != 0 {
t.Errorf("tile0 = %+v", tiles[0])
}
if tiles[1].Camera != "b" || tiles[1].Col != 1 || tiles[1].Row != 1 {
t.Errorf("tile1 = %+v", tiles[1])
}
}
func TestValidateTiles(t *testing.T) {
base := func(tiles []Tile) *Config {
c := &Config{
Cameras: []Camera{{Name: "a"}, {Name: "b"}},
Layouts: []Layout{{Name: "l", Grid: "4x4", Tiles: tiles}},
}
c.Defaults()
return c
}
// Valid: a 3x4 main plus a 1x1 side.
if err := base([]Tile{{Camera: "a", Col: 0, Row: 0, ColSpan: 3, RowSpan: 4}, {Camera: "b", Col: 3, Row: 0}}).Validate(); err != nil {
t.Errorf("valid layout rejected: %v", err)
}
// Overlap.
if err := base([]Tile{{Camera: "a", Col: 0, Row: 0, ColSpan: 2, RowSpan: 2}, {Camera: "b", Col: 1, Row: 1}}).Validate(); err == nil {
t.Error("expected overlap error")
}
// Out of bounds.
if err := base([]Tile{{Camera: "a", Col: 3, Row: 0, ColSpan: 2, RowSpan: 1}}).Validate(); err == nil {
t.Error("expected out-of-bounds error")
}
// Unknown camera.
if err := base([]Tile{{Camera: "ghost", Col: 0, Row: 0}}).Validate(); err == nil {
t.Error("expected unknown-camera error")
}
}
func TestCameraStreamURL(t *testing.T) {
cam := Camera{Name: "c", Streams: map[string]string{"high": "H", "low": "L"}}
if got := cam.StreamURL("low"); got != "L" {
t.Errorf(`StreamURL("low") = %q, want "L"`, got)
}
if got := cam.StreamURL("high"); got != "H" {
t.Errorf(`StreamURL("high") = %q, want "H"`, got)
}
// Requested quality missing → fall down the preference list, then any.
if got := cam.StreamURL("medium"); got != "L" {
t.Errorf(`StreamURL("medium") = %q, want fallback "L"`, got)
}
if got := cam.StreamURL(""); got != "H" {
t.Errorf(`StreamURL("") = %q, want best "H"`, got)
}
// Legacy single-URL camera.
legacy := Camera{Name: "old", RTSP: "R"}
if got := legacy.StreamURL("low"); got != "R" {
t.Errorf("legacy StreamURL = %q, want RTSP fallback R", got)
}
}
func TestValidateTilesCap(t *testing.T) {
var tiles []Tile
for i := 0; i < MaxTiles+1; i++ {
tiles = append(tiles, Tile{Camera: "a", Col: i, Row: 0})
}
c := &Config{Cameras: []Camera{{Name: "a"}}, Layouts: []Layout{{Name: "l", Grid: "20x1", Tiles: tiles}}}
c.Defaults()
if err := c.Validate(); err == nil {
t.Errorf("expected error exceeding %d-tile cap", MaxTiles)
}
}