Ariver
2026-07-12 24f551a39eae849d891da26cc91f90d354e3a2db
C1.source/privatevoice.src/app_live_caption_test.go
@@ -1,9 +1,19 @@
package main
import (
   "context"
   "errors"
   "strings"
   "sync/atomic"
   "testing"
   "time"
   "voicesnap/internal/config"
   "voicesnap/internal/engine"
   "voicesnap/internal/history"
   "voicesnap/internal/overlay"
   "voicesnap/internal/textoutput"
   "voicesnap/internal/userdict"
)
func TestLiveCaptionDisplayTextFitsOverlayBuffer(t *testing.T) {
@@ -131,6 +141,95 @@
   }
}
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)
@@ -163,6 +262,760 @@
   }
}
func TestHoldPreCaptureAutoConfirmsWithoutAdditionalKeyDown(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   a.hk.(*fakeHotkeyListener).down.Store(true)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isRecording && !a.isHoldRecordingPending && a.hotkeyActive
   })
   if got := a.recorder.(*fakeAppRecorder).starts.Load(); got != 1 {
      t.Fatalf("recorder starts = %d, want 1", got)
   }
   if got := a.indicator.(*fakeOverlay).lastStatus.Load(); got != string(overlay.StatusRecording) {
      t.Fatalf("indicator status = %q, want %q", got, overlay.StatusRecording)
   }
}
func TestHoldPreCaptureTimerConfirmsWhileRecorderStartBlocked(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   a.hk.(*fakeHotkeyListener).down.Store(true)
   recorder := a.recorder.(*fakeAppRecorder)
   blockStart := make(chan struct{})
   recorder.startBlock = blockStart
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      return recorder.starts.Load() == 1
   })
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isRecording && !a.isHoldRecordingPending && a.hotkeyActive
   })
   if got := a.indicator.(*fakeOverlay).lastStatus.Load(); got != string(overlay.StatusRecording) {
      t.Fatalf("indicator status = %q, want %q while recorder start is blocked", got, overlay.StatusRecording)
   }
   close(blockStart)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return !a.isHoldRecorderStarting
   })
}
func TestHoldPreCaptureReleaseAfterConfirmRunsRecognitionWhileRecorderStartBlocked(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   listener := a.hk.(*fakeHotkeyListener)
   listener.down.Store(true)
   recorder := a.recorder.(*fakeAppRecorder)
   blockStart := make(chan struct{})
   recorder.startBlock = blockStart
   recorder.samples = []float32{0.2, 0.1, -0.1, -0.2}
   recorder.hasVoice.Store(true)
   eng := &recognizingHoldEngine{text: "PrivateVoice AX POC"}
   a.replaceEngine(eng)
   paster := &fakeAppPaster{}
   a.outputRouter = textoutput.NewRouter(paster)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isRecording && !a.isHoldRecordingPending && a.isHoldRecorderStarting
   })
   listener.down.Store(false)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(false, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      return paster.pasted.Load() == "PrivateVoice AX POC"
   })
   if got := recorder.stopAndGetSamples.Load(); got != 1 {
      t.Fatalf("StopAndGetSamples while recorder start is blocked = %d, want 1", got)
   }
   if got := eng.recognizes.Load(); got != 1 {
      t.Fatalf("recognizes = %d, want 1", got)
   }
   a.mu.Lock()
   if a.isHoldStopPending {
      t.Fatal("normal release while recorder is starting should not mark cancel-style pending stop")
   }
   a.mu.Unlock()
   close(blockStart)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return !a.isRecording && !a.isHoldRecorderStarting && !a.isHoldStopPending
   })
   if got := recorder.stops.Load(); got != 0 {
      t.Fatalf("app-level fake recorder stops = %d, want 0; native cleanup is covered by audio recorder tests", got)
   }
}
func TestHoldPreCaptureReleaseAfterConfirmRunsRecognitionBeforeRecorderStartEntered(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   listener := a.hk.(*fakeHotkeyListener)
   listener.down.Store(true)
   recorder := a.recorder.(*fakeAppRecorder)
   preStartBlock := make(chan struct{})
   recorder.preStartBlock = preStartBlock
   recorder.samples = []float32{0.2, 0.1, -0.1, -0.2}
   recorder.hasVoice.Store(true)
   eng := &recognizingHoldEngine{text: "PrivateVoice AX POC"}
   a.replaceEngine(eng)
   paster := &fakeAppPaster{}
   a.outputRouter = textoutput.NewRouter(paster)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isRecording && !a.isHoldRecordingPending && a.isHoldRecorderStarting
   })
   if got := recorder.starts.Load(); got != 0 {
      t.Fatalf("recorder Start entered before release = %d, want 0", got)
   }
   listener.down.Store(false)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(false, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      return paster.pasted.Load() == "PrivateVoice AX POC"
   })
   if got := recorder.stopAndGetSamples.Load(); got != 1 {
      t.Fatalf("StopAndGetSamples before recorder Start entered = %d, want 1", got)
   }
   if got := eng.recognizes.Load(); got != 1 {
      t.Fatalf("recognizes = %d, want 1", got)
   }
   close(preStartBlock)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return !a.isRecording && !a.isHoldRecorderStarting && !a.isHoldStopPending
   })
   if got := recorder.stops.Load(); got != 0 {
      t.Fatalf("app-level fake recorder stops = %d, want 0 for normal release pipeline", got)
   }
   if got := a.indicator.(*fakeOverlay).lastStatus.Load(); got != string(overlay.StatusDone) {
      t.Fatalf("indicator status = %q, want %q", got, overlay.StatusDone)
   }
}
func TestHoldPreCaptureReleaseWaitsForStartupSamplesBeforeRecognition(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   listener := a.hk.(*fakeHotkeyListener)
   listener.down.Store(true)
   recorder := a.recorder.(*fakeAppRecorder)
   blockStart := make(chan struct{})
   readSamplesReady := make(chan struct{})
   recorder.startBlock = blockStart
   recorder.readSamplesBlock = readSamplesReady
   recorder.samples = []float32{0.2, 0.1, -0.1, -0.2}
   recorder.hasVoice.Store(true)
   eng := &recognizingHoldEngine{text: "PrivateVoice AX POC"}
   a.replaceEngine(eng)
   paster := &fakeAppPaster{}
   a.outputRouter = textoutput.NewRouter(paster)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isRecording && !a.isHoldRecordingPending && a.isHoldRecorderStarting
   })
   listener.down.Store(false)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(false, true)
   a.mu.Unlock()
   time.Sleep(holdStartupSamplePollInterval * 2)
   if got := recorder.stopAndGetSamples.Load(); got != 0 {
      t.Fatalf("StopAndGetSamples before startup samples = %d, want 0", got)
   }
   if got := eng.recognizes.Load(); got != 0 {
      t.Fatalf("recognizes before startup samples = %d, want 0", got)
   }
   close(readSamplesReady)
   waitForCondition(t, time.Second, func() bool {
      return paster.pasted.Load() == "PrivateVoice AX POC"
   })
   if got := recorder.stopAndGetSamples.Load(); got != 1 {
      t.Fatalf("StopAndGetSamples after startup samples = %d, want 1", got)
   }
   if got := eng.recognizes.Load(); got != 1 {
      t.Fatalf("recognizes = %d, want 1", got)
   }
   close(blockStart)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return !a.isRecording && !a.isHoldRecorderStarting && !a.isHoldStopPending
   })
}
func TestHoldPreCaptureReleaseWithoutStartupSamplesReportsAudioNotReady(t *testing.T) {
   oldTimeout := holdStartupSampleWaitTimeout
   oldPoll := holdStartupSamplePollInterval
   holdStartupSampleWaitTimeout = 40 * time.Millisecond
   holdStartupSamplePollInterval = 5 * time.Millisecond
   defer func() {
      holdStartupSampleWaitTimeout = oldTimeout
      holdStartupSamplePollInterval = oldPoll
   }()
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   listener := a.hk.(*fakeHotkeyListener)
   listener.down.Store(true)
   recorder := a.recorder.(*fakeAppRecorder)
   blockStart := make(chan struct{})
   recorder.startBlock = blockStart
   eng := &recognizingHoldEngine{text: "PrivateVoice AX POC"}
   a.replaceEngine(eng)
   paster := &fakeAppPaster{}
   a.outputRouter = textoutput.NewRouter(paster)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isRecording && !a.isHoldRecordingPending && a.isHoldRecorderStarting
   })
   listener.down.Store(false)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(false, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      return recorder.stops.Load() == 1 &&
         a.indicator.(*fakeOverlay).lastStatus.Load() == string(overlay.StatusError)
   })
   if got := recorder.stopAndGetSamples.Load(); got != 0 {
      t.Fatalf("StopAndGetSamples after audio-not-ready timeout = %d, want 0", got)
   }
   if got := eng.recognizes.Load(); got != 0 {
      t.Fatalf("recognizes after audio-not-ready timeout = %d, want 0", got)
   }
   if got := paster.pasted.Load(); got != nil {
      t.Fatalf("pasted text after audio-not-ready timeout = %v, want nil", got)
   }
   if got := a.indicator.(*fakeOverlay).lastStatus.Load(); got != string(overlay.StatusError) {
      t.Fatalf("indicator status = %q, want %q", got, overlay.StatusError)
   }
   close(blockStart)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return !a.isRecording && !a.isHoldRecorderStarting && !a.isHoldStopPending
   })
}
func TestHoldPreCaptureReleaseWhileRecorderPermissionPendingDoesNotRecognize(t *testing.T) {
   oldTimeout := holdStartupSampleWaitTimeout
   oldPoll := holdStartupSamplePollInterval
   holdStartupSampleWaitTimeout = 40 * time.Millisecond
   holdStartupSamplePollInterval = 5 * time.Millisecond
   defer func() {
      holdStartupSampleWaitTimeout = oldTimeout
      holdStartupSamplePollInterval = oldPoll
   }()
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   listener := a.hk.(*fakeHotkeyListener)
   listener.down.Store(true)
   recorder := a.recorder.(*fakeAppRecorder)
   preStartBlock := make(chan struct{})
   recorder.preStartBlock = preStartBlock
   eng := &recognizingHoldEngine{text: "PrivateVoice AX POC"}
   a.replaceEngine(eng)
   paster := &fakeAppPaster{}
   a.outputRouter = textoutput.NewRouter(paster)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isRecording && !a.isHoldRecordingPending && a.isHoldRecorderStarting
   })
   if got := recorder.starts.Load(); got != 0 {
      t.Fatalf("recorder Start entered before permission pending release = %d, want 0", got)
   }
   listener.down.Store(false)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(false, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      return recorder.stops.Load() == 1 &&
         a.indicator.(*fakeOverlay).lastStatus.Load() == string(overlay.StatusError)
   })
   if got := recorder.stopAndGetSamples.Load(); got != 0 {
      t.Fatalf("StopAndGetSamples after permission-pending audio-not-ready = %d, want 0", got)
   }
   if got := eng.recognizes.Load(); got != 0 {
      t.Fatalf("recognizes after permission-pending audio-not-ready = %d, want 0", got)
   }
   if got := paster.pasted.Load(); got != nil {
      t.Fatalf("pasted text after permission-pending audio-not-ready = %v, want nil", got)
   }
   close(preStartBlock)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return !a.isRecording && !a.isHoldRecorderStarting && !a.isHoldStopPending
   })
   if got := recorder.starts.Load(); got != 1 {
      t.Fatalf("recorder Start after permission resumes = %d, want 1", got)
   }
   if got := recorder.stops.Load(); got != 1 {
      t.Fatalf("recorder stops after late permission success = %d, want 1", got)
   }
}
func TestHoldPreCaptureEscapeAfterConfirmDefersStopUntilRecorderStart(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   listener := a.hk.(*fakeHotkeyListener)
   listener.down.Store(true)
   recorder := a.recorder.(*fakeAppRecorder)
   blockStart := make(chan struct{})
   recorder.startBlock = blockStart
   captionCancelled := false
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isRecording && !a.isHoldRecordingPending && a.isHoldRecorderStarting
   })
   a.mu.Lock()
   a.liveCaptionCancel = func() { captionCancelled = true }
   a.mu.Unlock()
   listener.escapeDown.Store(true)
   done := make(chan struct{})
   go func() {
      a.pollHotkey()
      close(done)
   }()
   select {
   case <-done:
   case <-time.After(100 * time.Millisecond):
      t.Fatal("Escape cancel should not wait for blocked recorder start")
   }
   if got := recorder.stops.Load(); got != 1 {
      t.Fatalf("recorder stops before start returned = %d, want 1", got)
   }
   if got := recorder.stopAndGetSamples.Load(); got != 0 {
      t.Fatalf("StopAndGetSamples before recorder start returned = %d, want 0", got)
   }
   a.mu.Lock()
   if !a.isHoldStopPending {
      t.Fatal("Escape while recorder is starting should mark pending stop")
   }
   if a.liveCaptionCancel != nil {
      t.Fatal("Escape while recorder is starting should clear live caption")
   }
   a.mu.Unlock()
   if !captionCancelled {
      t.Fatal("Escape while recorder is starting should cancel live caption")
   }
   close(blockStart)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return !a.isRecording && !a.isHoldRecorderStarting && !a.isHoldStopPending
   })
   if got := recorder.stopAndGetSamples.Load(); got != 0 {
      t.Fatalf("StopAndGetSamples after deferred Escape stop = %d, want 0", got)
   }
   if got := recorder.stops.Load(); got != 1 {
      t.Fatalf("recorder stops = %d, want 1", got)
   }
   if got := a.indicator.(*fakeOverlay).lastStatus.Load(); got != string(overlay.StatusCancelled) {
      t.Fatalf("indicator status = %q, want %q", got, overlay.StatusCancelled)
   }
}
func TestPollHotkeyIgnoresUnsetHotkey(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   listener := a.hk.(*fakeHotkeyListener)
   listener.down.Store(true)
   a.cfg.HotkeyVK = config.UnsetHotkeyVK
   a.pollHotkey()
   if got := listener.keyChecks.Load(); got != 0 {
      t.Fatalf("IsKeyDown calls for unset hotkey = %d, want 0", got)
   }
   if a.hotkeyActive {
      t.Fatal("unset hotkey must not become active")
   }
   if a.isRecording || a.isFreetalking {
      t.Fatal("unset hotkey must not start recording")
   }
}
func TestHoldPreCaptureEscapeWhileRecorderPermissionPendingStopsLateStart(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   listener := a.hk.(*fakeHotkeyListener)
   listener.down.Store(true)
   recorder := a.recorder.(*fakeAppRecorder)
   preStartBlock := make(chan struct{})
   recorder.preStartBlock = preStartBlock
   eng := &recognizingHoldEngine{text: "PrivateVoice AX POC"}
   a.replaceEngine(eng)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isRecording && !a.isHoldRecordingPending && a.isHoldRecorderStarting
   })
   if got := recorder.starts.Load(); got != 0 {
      t.Fatalf("recorder Start entered before Escape during permission pending = %d, want 0", got)
   }
   listener.escapeDown.Store(true)
   done := make(chan struct{})
   go func() {
      a.pollHotkey()
      close(done)
   }()
   select {
   case <-done:
   case <-time.After(100 * time.Millisecond):
      t.Fatal("Escape during permission pending should not wait for recorder Start")
   }
   if got := recorder.stops.Load(); got != 1 {
      t.Fatalf("recorder Stop during permission-pending Escape = %d, want 1", got)
   }
   if got := recorder.stopAndGetSamples.Load(); got != 0 {
      t.Fatalf("StopAndGetSamples during permission-pending Escape = %d, want 0", got)
   }
   if got := eng.recognizes.Load(); got != 0 {
      t.Fatalf("recognizes during permission-pending Escape = %d, want 0", got)
   }
   close(preStartBlock)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return !a.isRecording && !a.isHoldRecorderStarting && !a.isHoldStopPending
   })
   if got := recorder.stops.Load(); got != 1 {
      t.Fatalf("recorder stops after late permission success = %d, want 1", got)
   }
   if got := a.indicator.(*fakeOverlay).lastStatus.Load(); got != string(overlay.StatusCancelled) {
      t.Fatalf("indicator status = %q, want %q", got, overlay.StatusCancelled)
   }
}
func TestHoldPreCaptureCombinationWhileRecorderPermissionPendingStopsLateStart(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   listener := a.hk.(*fakeHotkeyListener)
   listener.down.Store(true)
   recorder := a.recorder.(*fakeAppRecorder)
   preStartBlock := make(chan struct{})
   recorder.preStartBlock = preStartBlock
   eng := &recognizingHoldEngine{text: "PrivateVoice AX POC"}
   a.replaceEngine(eng)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   listener.otherSince.Store(true)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isCombination && !a.isRecording && !a.isHoldRecordingPending && a.isHoldStopPending
   })
   if got := recorder.stops.Load(); got != 1 {
      t.Fatalf("recorder Stop during permission-pending combination cancel = %d, want 1", got)
   }
   if got := recorder.stopAndGetSamples.Load(); got != 0 {
      t.Fatalf("StopAndGetSamples during permission-pending combination cancel = %d, want 0", got)
   }
   if got := eng.recognizes.Load(); got != 0 {
      t.Fatalf("recognizes during permission-pending combination cancel = %d, want 0", got)
   }
   close(preStartBlock)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return !a.isRecording && !a.isHoldRecorderStarting && !a.isHoldStopPending
   })
   if got := recorder.stops.Load(); got != 1 {
      t.Fatalf("recorder stops after late combination permission success = %d, want 1", got)
   }
}
func TestHoldPreCaptureReleaseAfterRecorderStartRunsRecognitionAndOutput(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   listener := a.hk.(*fakeHotkeyListener)
   listener.down.Store(true)
   recorder := a.recorder.(*fakeAppRecorder)
   recorder.samples = []float32{0.2, 0.1, -0.1, -0.2}
   recorder.hasVoice.Store(true)
   eng := &recognizingHoldEngine{text: "PrivateVoice AX POC"}
   a.replaceEngine(eng)
   paster := &fakeAppPaster{}
   a.outputRouter = textoutput.NewRouter(paster)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isRecording && !a.isHoldRecordingPending && !a.isHoldRecorderStarting
   })
   listener.down.Store(false)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(false, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      return paster.pasted.Load() == "PrivateVoice AX POC"
   })
   if got := recorder.stopAndGetSamples.Load(); got != 1 {
      t.Fatalf("StopAndGetSamples = %d, want 1", got)
   }
   if got := eng.recognizes.Load(); got != 1 {
      t.Fatalf("recognizes = %d, want 1", got)
   }
   if got := recorder.stops.Load(); got != 0 {
      t.Fatalf("recorder Stop = %d, want 0 for normal recognition path", got)
   }
}
func TestHoldPreCaptureReleasePipelineSuppressesLateRecorderStartError(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   listener := a.hk.(*fakeHotkeyListener)
   listener.down.Store(true)
   recorder := a.recorder.(*fakeAppRecorder)
   blockStart := make(chan struct{})
   recorder.startBlock = blockStart
   recorder.startErr = errors.New("start failed")
   recorder.samples = []float32{0.2, 0.1, -0.1, -0.2}
   recorder.hasVoice.Store(true)
   eng := &recognizingHoldEngine{text: "PrivateVoice AX POC"}
   a.replaceEngine(eng)
   paster := &fakeAppPaster{}
   a.outputRouter = textoutput.NewRouter(paster)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isRecording && !a.isHoldRecordingPending && a.isHoldRecorderStarting
   })
   listener.down.Store(false)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(false, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      return paster.pasted.Load() == "PrivateVoice AX POC"
   })
   close(blockStart)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return !a.isHoldRecorderStarting
   })
   if got := eng.recognizes.Load(); got != 1 {
      t.Fatalf("recognizes = %d, want 1", got)
   }
   if got := a.indicator.(*fakeOverlay).lastStatus.Load(); got != string(overlay.StatusDone) {
      t.Fatalf("late recorder start error overwrote indicator status = %q, want %q", got, overlay.StatusDone)
   }
}
func TestHoldPreCaptureRecorderStartLateErrorCleansConfirmedState(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   a.hk.(*fakeHotkeyListener).down.Store(true)
   recorder := a.recorder.(*fakeAppRecorder)
   blockStart := make(chan struct{})
   recorder.startBlock = blockStart
   recorder.startErr = errors.New("start failed")
   captionCancelled := false
   a.liveCaptionCancel = func() { captionCancelled = true }
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isRecording && !a.isHoldRecordingPending && a.isHoldRecorderStarting && a.liveCaptionCancel != nil
   })
   close(blockStart)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return !a.isRecording && !a.isHoldRecorderStarting && a.liveCaptionCancel == nil
   })
   if !captionCancelled {
      t.Fatal("late recorder start error should cancel live caption")
   }
   if got := a.indicator.(*fakeOverlay).lastStatus.Load(); got != string(overlay.StatusError) {
      t.Fatalf("indicator status = %q, want %q", got, overlay.StatusError)
   }
}
func TestHoldPreCaptureReleaseBeforeActivationCancelsTimer(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   a.hk.(*fakeHotkeyListener).down.Store(true)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.hk.(*fakeHotkeyListener).down.Store(false)
   a.pollHoldHotkeyLocked(false, true)
   a.mu.Unlock()
   time.Sleep(holdActivationDelay + 50*time.Millisecond)
   a.mu.Lock()
   defer a.mu.Unlock()
   if a.isRecording || a.isHoldRecordingPending {
      t.Fatalf("recording=%t pending=%t, want both false after early release", a.isRecording, a.isHoldRecordingPending)
   }
   if got := a.recorder.(*fakeAppRecorder).stops.Load(); got != 1 {
      t.Fatalf("recorder stops = %d, want 1", got)
   }
}
func TestHoldPreCaptureTimerCancelsWhenKeyReleasedBeforePoll(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   a.hk.(*fakeHotkeyListener).down.Store(true)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   a.hk.(*fakeHotkeyListener).down.Store(false)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return !a.isRecording && !a.isHoldRecordingPending
   })
   a.mu.Lock()
   defer a.mu.Unlock()
   if a.hotkeyActive != true {
      t.Fatal("hotkeyActive should remain true until release poll handles the key-up")
   }
   if got := a.recorder.(*fakeAppRecorder).stops.Load(); got != 1 {
      t.Fatalf("recorder stops = %d, want 1", got)
   }
   if got := a.indicator.(*fakeOverlay).lastStatus.Load(); got == string(overlay.StatusRecording) {
      t.Fatalf("indicator status = %q, should not confirm recording after physical release", got)
   }
}
func TestHoldPreCaptureCombinationBeforeActivationCancels(t *testing.T) {
   a, cleanup := newHoldPreCaptureTestApp()
   defer cleanup()
   a.hk.(*fakeHotkeyListener).down.Store(true)
   a.mu.Lock()
   a.pollHoldHotkeyLocked(true, true)
   a.mu.Unlock()
   a.hk.(*fakeHotkeyListener).otherSince.Store(true)
   waitForCondition(t, time.Second, func() bool {
      a.mu.Lock()
      defer a.mu.Unlock()
      return a.isCombination && !a.isRecording && !a.isHoldRecordingPending
   })
   if got := a.recorder.(*fakeAppRecorder).stops.Load(); got != 1 {
      t.Fatalf("recorder stops = %d, want 1", got)
   }
}
func TestStartRecordingIgnoredWhileStoppingRecording(t *testing.T) {
   a := &App{isStoppingRecording: true}
@@ -189,6 +1042,15 @@
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
@@ -206,3 +1068,134 @@
func (e holdPreCaptureTestEngine) HoldPreCaptureEnabled() bool {
   return e.enabled
}
func newHoldPreCaptureTestApp() (*App, context.CancelFunc) {
   ctx, cancel := context.WithCancel(context.Background())
   a := &App{
      ctx:       ctx,
      cancel:    cancel,
      cfg:       &config.Config{HotkeyVK: 0x5C, HotkeyMode: config.HotkeyModeHold},
      recorder:  &fakeAppRecorder{},
      hk:        &fakeHotkeyListener{},
      indicator: &fakeOverlay{},
      history:   history.New(),
      userdict:  userdict.New(),
   }
   a.replaceEngine(holdPreCaptureTestEngine{enabled: true})
   a.holdPreCaptureEnabled.Store(true)
   return a, cancel
}
type fakeAppRecorder struct {
   starts            atomic.Int32
   stops             atomic.Int32
   stopAndGetSamples atomic.Int32
   hasVoice          atomic.Bool
   samples           []float32
   preStartBlock     <-chan struct{}
   startBlock        <-chan struct{}
   readSamplesBlock  <-chan struct{}
   startErr          error
}
func (r *fakeAppRecorder) OnVolume(func(float64))      {}
func (r *fakeAppRecorder) OnDeviceChange(func(string)) {}
func (r *fakeAppRecorder) Start() error {
   if r.preStartBlock != nil {
      <-r.preStartBlock
   }
   r.starts.Add(1)
   if r.startBlock != nil {
      <-r.startBlock
   }
   return r.startErr
}
func (r *fakeAppRecorder) Stop() { r.stops.Add(1) }
func (r *fakeAppRecorder) StopAndGetSamples() []float32 {
   r.stopAndGetSamples.Add(1)
   return append([]float32(nil), r.samples...)
}
func (r *fakeAppRecorder) HasVoiceActivity() bool { return r.hasVoice.Load() }
func (r *fakeAppRecorder) ReadSamplesSince(offset int) ([]float32, int) {
   if r.readSamplesBlock != nil {
      select {
      case <-r.readSamplesBlock:
      default:
         return nil, 0
      }
   }
   total := len(r.samples)
   if offset < 0 {
      offset = 0
   }
   if offset > total {
      offset = total
   }
   return append([]float32(nil), r.samples[offset:]...), total
}
func (r *fakeAppRecorder) Close() {}
type recognizingHoldEngine struct {
   text       string
   recognizes atomic.Int32
}
func (e *recognizingHoldEngine) Recognize([]float32) (string, error) {
   e.recognizes.Add(1)
   return e.text, nil
}
func (e *recognizingHoldEngine) HardwareInfo() string        { return "test" }
func (e *recognizingHoldEngine) Close()                      {}
func (e *recognizingHoldEngine) HoldPreCaptureEnabled() bool { return true }
type fakeAppPaster struct {
   pasted atomic.Value
   typed  atomic.Value
}
func (p *fakeAppPaster) Paste(text string, keepClipboard bool) error {
   p.pasted.Store(text)
   return nil
}
func (p *fakeAppPaster) TypeText(text string) error {
   p.typed.Store(text)
   return nil
}
type fakeHotkeyListener struct {
   down       atomic.Bool
   escapeDown atomic.Bool
   otherSince atomic.Bool
   keyChecks  atomic.Int32
}
func (h *fakeHotkeyListener) IsKeyDown(key int) bool {
   h.keyChecks.Add(1)
   if key == 0x1B {
      return h.escapeDown.Load()
   }
   return h.down.Load()
}
func (h *fakeHotkeyListener) IsAnyOtherKeyPressed(int) bool { return false }
func (h *fakeHotkeyListener) IsAnyOtherKeyPressedSince(int, time.Time) bool {
   return h.otherSince.Load()
}
func (h *fakeHotkeyListener) Close() {}
type fakeOverlay struct {
   lastStatus atomic.Value
}
func (o *fakeOverlay) Show() {}
func (o *fakeOverlay) Hide() {}
func (o *fakeOverlay) SetStatus(status overlay.Status, text string) {
   o.lastStatus.Store(string(status))
}
func (o *fakeOverlay) SetVolume(float64)        {}
func (o *fakeOverlay) SetPosition(int, int)     {}
func (o *fakeOverlay) GetPosition() (int, int)  { return 0, 0 }
func (o *fakeOverlay) Size() (int, int)         { return 170, 48 }
func (o *fakeOverlay) OnDragged(func(int, int)) {}
func (o *fakeOverlay) Close()                   {}
func (o *fakeOverlay) AutoPosition()            {}