Add mock show generator and timeline benchmark

This commit is contained in:
Ian Gulliver
2026-02-20 20:20:25 -07:00
parent 2c31d9505c
commit 287ca83724
5 changed files with 128 additions and 5 deletions

View File

@@ -34,6 +34,11 @@ func main() {
os.Exit(1) os.Exit(1)
} }
if err := show.Validate(); err != nil {
fmt.Fprintf(os.Stderr, "Error validating show: %v\n", err)
os.Exit(1)
}
timeline, err := BuildTimeline(show) timeline, err := BuildTimeline(show)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Error building timeline: %v\n", err) fmt.Fprintf(os.Stderr, "Error building timeline: %v\n", err)

70
cmd/qrunproxy/mockshow.go Normal file
View File

@@ -0,0 +1,70 @@
package main
import "fmt"
func GenerateMockShow(numTracks, numCues, numBlocks int) *Show {
show := &Show{}
for i := range numTracks {
show.Tracks = append(show.Tracks, &Track{
ID: fmt.Sprintf("track_%d", i),
Name: fmt.Sprintf("Track %d", i),
})
}
for i := range numCues {
show.Blocks = append(show.Blocks, &Block{
ID: fmt.Sprintf("cue_%d", i),
Type: "cue",
Name: fmt.Sprintf("Cue %d", i),
})
}
blocksByTrack := make([][]*Block, numTracks)
for i := range numBlocks {
trackIdx := i % numTracks
trackID := fmt.Sprintf("track_%d", trackIdx)
block := &Block{
ID: fmt.Sprintf("block_%d_%d", trackIdx, len(blocksByTrack[trackIdx])),
Type: "media",
Track: trackID,
Name: fmt.Sprintf("Block %d-%d", trackIdx, len(blocksByTrack[trackIdx])),
}
show.Blocks = append(show.Blocks, block)
blocksByTrack[trackIdx] = append(blocksByTrack[trackIdx], block)
}
for trackIdx := range numTracks {
blocks := blocksByTrack[trackIdx]
for i := 1; i < len(blocks); i++ {
show.Triggers = append(show.Triggers, &Trigger{
Source: TriggerSource{Block: blocks[i-1].ID, Signal: "END"},
Targets: []TriggerTarget{{Block: blocks[i].ID, Hook: "START"}},
})
}
}
headPerTrack := make([]int, numTracks)
for i := range numCues {
cue := show.Blocks[i]
targets := []TriggerTarget{}
for trackIdx := range numTracks {
if headPerTrack[trackIdx] >= len(blocksByTrack[trackIdx]) {
continue
}
block := blocksByTrack[trackIdx][headPerTrack[trackIdx]]
targets = append(targets, TriggerTarget{Block: block.ID, Hook: "START"})
depth := len(blocksByTrack[trackIdx]) - headPerTrack[trackIdx]
advance := max(depth/(numCues-i), 1)
headPerTrack[trackIdx] += advance
}
if len(targets) > 0 {
show.Triggers = append(show.Triggers, &Trigger{
Source: TriggerSource{Block: cue.ID, Signal: "GO"},
Targets: targets,
})
}
}
return show
}

View File

@@ -63,7 +63,7 @@ func isValidEventForBlock(block *Block, event string) bool {
} }
} }
func (show *Show) validate() error { func (show *Show) Validate() error {
if show == nil { if show == nil {
return fmt.Errorf("show is nil") return fmt.Errorf("show is nil")
} }

View File

@@ -47,10 +47,6 @@ type cellKey struct {
} }
func BuildTimeline(show *Show) (Timeline, error) { func BuildTimeline(show *Show) (Timeline, error) {
if err := show.validate(); err != nil {
return Timeline{}, err
}
tl := Timeline{ tl := Timeline{
show: show, show: show,
Blocks: map[string]*Block{}, Blocks: map[string]*Block{},

View File

@@ -0,0 +1,52 @@
package main
import (
"encoding/json"
"os"
"testing"
)
func TestBuildTimelineFromShowJSON(t *testing.T) {
buf, err := os.ReadFile("static/show.json")
if err != nil {
t.Fatal(err)
}
var show Show
if err := json.Unmarshal(buf, &show); err != nil {
t.Fatal(err)
}
if err := show.Validate(); err != nil {
t.Fatal(err)
}
if _, err := BuildTimeline(&show); err != nil {
t.Fatal(err)
}
}
func TestBuildTimelineFromMockShow(t *testing.T) {
show := GenerateMockShow(7, 100, 1000)
if err := show.Validate(); err != nil {
t.Fatalf("generated show failed validation: %v", err)
}
tl, err := BuildTimeline(show)
if err != nil {
t.Fatalf("BuildTimeline failed: %v", err)
}
if len(tl.Tracks) != 8 {
t.Errorf("expected 8 tracks (7 + cue), got %d", len(tl.Tracks))
}
if len(tl.Blocks) != 1100 {
t.Errorf("expected 1100 blocks (100 cues + 1000), got %d", len(tl.Blocks))
}
}
func BenchmarkBuildTimeline(b *testing.B) {
show := GenerateMockShow(7, 100, 1000)
if err := show.Validate(); err != nil {
b.Fatal(err)
}
b.ResetTimer()
for range b.N {
BuildTimeline(show)
}
}