These tests cover parseSinkLine and parseSinks, which parse the output of wpctl status. Both functions will be removed in the next commit as part of migrating away from PipeWire/wpctl to pure SDL_mixer volume control.
146 lines
3.4 KiB
Go
146 lines
3.4 KiB
Go
package mixer
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"rhemawavetech.com/rhemawave/internal/db"
|
|
)
|
|
|
|
// Regression tests for critical audio bugs (v0.5)
|
|
|
|
// Regression: Stop() used to return early when AudioEnabled was false,
|
|
// leaving music playing after the user toggled audio off mid-playback.
|
|
func TestStopCleansUpWhenAudioDisabled(t *testing.T) {
|
|
mu.Lock()
|
|
wasInited := inited
|
|
inited = true
|
|
deviceOpen = false
|
|
currentPath = "/some/track.wav"
|
|
currentLoops = -1
|
|
mu.Unlock()
|
|
|
|
defer func() {
|
|
mu.Lock()
|
|
inited = wasInited
|
|
currentPath = ""
|
|
currentLoops = 0
|
|
mu.Unlock()
|
|
}()
|
|
|
|
cfg := &db.AppConfig{AudioEnabled: false}
|
|
Stop(0, cfg)
|
|
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if currentPath != "" {
|
|
t.Errorf("currentPath = %q, want empty after Stop with AudioEnabled=false", currentPath)
|
|
}
|
|
if currentLoops != 0 {
|
|
t.Errorf("currentLoops = %d, want 0 after Stop with AudioEnabled=false", currentLoops)
|
|
}
|
|
}
|
|
|
|
// Regression: SwitchDevice used to convert oldDevice="" to "system default"
|
|
// and pass that string to openDeviceLocked, which SDL does not recognize.
|
|
func TestSwitchDeviceReturnsErrorWhenUninitialized(t *testing.T) {
|
|
mu.Lock()
|
|
wasInited := inited
|
|
inited = false
|
|
deviceOpen = false
|
|
mu.Unlock()
|
|
|
|
defer func() {
|
|
mu.Lock()
|
|
inited = wasInited
|
|
mu.Unlock()
|
|
}()
|
|
|
|
cfg := &db.AppConfig{AudioEnabled: true, SavedVolume: 0.5}
|
|
err := SwitchDevice("Some Device", cfg)
|
|
if err == nil {
|
|
t.Fatal("SwitchDevice on uninitialized mixer should return error")
|
|
}
|
|
}
|
|
|
|
// Regression: SwitchDevice with nil config must not panic when attempting
|
|
// to restore volume after reopening the device.
|
|
func TestSwitchDeviceNilConfigDoesNotPanic(t *testing.T) {
|
|
mu.Lock()
|
|
wasInited := inited
|
|
inited = false
|
|
deviceOpen = false
|
|
mu.Unlock()
|
|
|
|
defer func() {
|
|
mu.Lock()
|
|
inited = wasInited
|
|
mu.Unlock()
|
|
}()
|
|
|
|
err := SwitchDevice("Some Device", nil)
|
|
if err == nil {
|
|
t.Fatal("SwitchDevice on uninitialized mixer should return error")
|
|
}
|
|
}
|
|
|
|
// Regression: SwitchDevice with empty device name (system default) should
|
|
// not be converted to "system default" before passing to openDeviceLocked.
|
|
func TestSwitchDeviceAcceptsEmptyDeviceName(t *testing.T) {
|
|
mu.Lock()
|
|
wasInited := inited
|
|
inited = false
|
|
deviceOpen = false
|
|
mu.Unlock()
|
|
|
|
defer func() {
|
|
mu.Lock()
|
|
inited = wasInited
|
|
mu.Unlock()
|
|
}()
|
|
|
|
cfg := &db.AppConfig{SavedVolume: 0.75}
|
|
err := SwitchDevice("", cfg)
|
|
// Returns error because mixer is not initialized, but should not panic
|
|
// and should handle the empty name gracefully.
|
|
if err == nil {
|
|
t.Fatal("SwitchDevice on uninitialized mixer should return error")
|
|
}
|
|
}
|
|
|
|
// TestPlayDoesNotDeadlockWhenUninitialized verifies that Play() returns an error
|
|
// (not a deadlock) when the mixer has not been initialized.
|
|
// The old code would deadlock because Play() held mu then called SwitchDevice()
|
|
// which also tried to acquire mu.
|
|
func TestPlayDoesNotDeadlockWhenUninitialized(t *testing.T) {
|
|
// Ensure mixer is in uninitialized state
|
|
mu.Lock()
|
|
wasInited := inited
|
|
inited = false
|
|
deviceOpen = false
|
|
mu.Unlock()
|
|
|
|
defer func() {
|
|
mu.Lock()
|
|
inited = wasInited
|
|
mu.Unlock()
|
|
}()
|
|
|
|
cfg := &db.AppConfig{AudioEnabled: true}
|
|
done := make(chan error, 1)
|
|
go func() {
|
|
done <- Play("nonexistent.wav", false, cfg)
|
|
}()
|
|
|
|
select {
|
|
case err := <-done:
|
|
if err == nil {
|
|
t.Fatal("Play() returned nil error on uninitialized mixer, want error")
|
|
}
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("Play() deadlocked on uninitialized mixer (did not return within 5s)")
|
|
}
|
|
}
|
|
|
|
|