| | |
| | | |
| | | import ( |
| | | "strings" |
| | | "sync/atomic" |
| | | "testing" |
| | | "time" |
| | | |
| | | "voicesnap/internal/engine" |
| | | ) |
| | | |
| | | func TestLiveCaptionDisplayTextFitsOverlayBuffer(t *testing.T) { |
| | |
| | | } |
| | | } |
| | | |
| | | func TestInitEngineSerializesAndDiscardsStaleEngine(t *testing.T) { |
| | | first := &closeTrackingTestEngine{name: "first"} |
| | | second := &closeTrackingTestEngine{name: "second"} |
| | | firstStarted := make(chan struct{}) |
| | | allowFirst := make(chan struct{}) |
| | | calls := atomic.Int32{} |
| | | |
| | | a := &App{ |
| | | engineFactory: func(initID uint64) (engine.Engine, engineMetadata, bool, error) { |
| | | call := calls.Add(1) |
| | | switch call { |
| | | case 1: |
| | | close(firstStarted) |
| | | <-allowFirst |
| | | return first, engineMetadata{InitID: initID, ResolvedModelID: "first", HardwareInfo: "first"}, true, nil |
| | | case 2: |
| | | return second, engineMetadata{InitID: initID, ResolvedModelID: "second", HardwareInfo: "second"}, true, nil |
| | | default: |
| | | t.Fatalf("unexpected engine factory call %d", call) |
| | | return nil, engineMetadata{}, false, nil |
| | | } |
| | | }, |
| | | } |
| | | |
| | | doneFirst := make(chan struct{}) |
| | | go func() { |
| | | a.initEngine() |
| | | close(doneFirst) |
| | | }() |
| | | |
| | | select { |
| | | case <-firstStarted: |
| | | case <-time.After(time.Second): |
| | | t.Fatal("first engine init did not start") |
| | | } |
| | | |
| | | doneSecond := make(chan struct{}) |
| | | go func() { |
| | | a.initEngine() |
| | | close(doneSecond) |
| | | }() |
| | | |
| | | waitForCondition(t, time.Second, func() bool { |
| | | return a.engineInitSeq.Load() == 2 |
| | | }) |
| | | close(allowFirst) |
| | | |
| | | select { |
| | | case <-doneFirst: |
| | | case <-time.After(time.Second): |
| | | t.Fatal("first engine init did not finish") |
| | | } |
| | | select { |
| | | case <-doneSecond: |
| | | case <-time.After(time.Second): |
| | | t.Fatal("second engine init did not finish") |
| | | } |
| | | |
| | | a.engineMu.Lock() |
| | | gotEngine := a.eng |
| | | gotMeta := a.engineMeta |
| | | a.engineMu.Unlock() |
| | | |
| | | if gotEngine != second { |
| | | t.Fatalf("final engine = %#v, want second", gotEngine) |
| | | } |
| | | if !first.closed.Load() { |
| | | t.Fatal("stale first engine should be closed") |
| | | } |
| | | if second.closed.Load() { |
| | | t.Fatal("current second engine should remain open") |
| | | } |
| | | if gotMeta.ResolvedModelID != "second" || gotMeta.InitID != 2 { |
| | | t.Fatalf("engine metadata = %+v, want second init metadata", gotMeta) |
| | | } |
| | | } |
| | | |
| | | func waitForCondition(t *testing.T, timeout time.Duration, fn func() bool) { |
| | | t.Helper() |
| | | deadline := time.Now().Add(timeout) |
| | | for time.Now().Before(deadline) { |
| | | if fn() { |
| | | return |
| | | } |
| | | time.Sleep(10 * time.Millisecond) |
| | | } |
| | | t.Fatal("condition was not met before timeout") |
| | | } |
| | | |
| | | func TestShouldStartHoldPreCaptureLocked(t *testing.T) { |
| | | a := &App{} |
| | | a.holdPreCaptureEnabled.Store(true) |
| | |
| | | func (plainTestEngine) HardwareInfo() string { return "test" } |
| | | func (plainTestEngine) Close() {} |
| | | |
| | | type closeTrackingTestEngine struct { |
| | | plainTestEngine |
| | | name string |
| | | closed atomic.Bool |
| | | } |
| | | |
| | | func (e *closeTrackingTestEngine) HardwareInfo() string { return e.name } |
| | | func (e *closeTrackingTestEngine) Close() { e.closed.Store(true) } |
| | | |
| | | type tailCaptureTestEngine struct { |
| | | plainTestEngine |
| | | delay time.Duration |