detached 8f9cb67

test: keep public player unit tests runtime-free

commit
8f9cb67ea7521ab4199e1632bbe1b6cfff11c450
author
brice · · 2026-04-10 15:25 UTC
parent
ce2ad83
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +17 -72
M pipewire/player_test.go +17 -72
@@ -47,7 +47,11 @@ func TestPlayerStateReflectsLifecycle(t *testing.T) {
4747 }
4848 }
4949
50-func TestPlayerLifecycleStartStopClose(t *testing.T) {
50+// TestPlayerCloseFromIdleAndStartFromClosed covers the non-runtime lifecycle
51+// edges: closing an idle player and rejecting Start after Close.
52+// Full Start→Pause→Stop→Close lifecycle is tested in internal/core with mocks
53+// and in integration tests with a live runtime.
54+func TestPlayerCloseFromIdleAndStartFromClosed(t *testing.T) {
5155 config := PlayerConfig{
5256 SampleRate: 44100,
5357 Channels: 2,
@@ -59,41 +63,9 @@ func TestPlayerLifecycleStartStopClose(t *testing.T) {
5963 t.Fatalf("NewPlayer returned error: %v", err)
6064 }
6165
62- // Start should transition to Playing
63- if err := player.Start(); err != nil {
64- t.Fatalf("Start failed: %v", err)
65- }
66- if player.State() != PlayerStatePlaying {
67- t.Fatalf("expected Playing state after Start, got %v", player.State())
68- }
69-
70- // Pause should transition to Paused
71- if err := player.Pause(); err != nil {
72- t.Fatalf("Pause failed: %v", err)
73- }
74- if player.State() != PlayerStatePaused {
75- t.Fatalf("expected Paused state after Pause, got %v", player.State())
76- }
77-
78- // Stop should transition to Stopped
79- if err := player.Stop(); err != nil {
80- t.Fatalf("Stop failed: %v", err)
81- }
82- if player.State() != PlayerStateStopped {
83- t.Fatalf("expected Stopped state after Stop, got %v", player.State())
84- }
85-
86- // Restart should work from Stopped
87- if err := player.Start(); err != nil {
88- t.Fatalf("Start from Stopped failed: %v", err)
89- }
90- if player.State() != PlayerStatePlaying {
91- t.Fatalf("expected Playing state after restart, got %v", player.State())
92- }
93-
94- // Close should transition to Closed
66+ // Idle → Close should succeed without a live runtime
9567 if err := player.Close(); err != nil {
96- t.Fatalf("Close failed: %v", err)
68+ t.Fatalf("Close from Idle failed: %v", err)
9769 }
9870 if player.State() != PlayerStateClosed {
9971 t.Fatalf("expected Closed state after Close, got %v", player.State())
@@ -117,12 +89,7 @@ func TestPlayerCloseIsIdempotent(t *testing.T) {
11789 t.Fatalf("NewPlayer returned error: %v", err)
11890 }
11991
120- // Start first
121- if err := player.Start(); err != nil {
122- t.Fatalf("Start failed: %v", err)
123- }
124-
125- // First close should work
92+ // First close from Idle should work
12693 if err := player.Close(); err != nil {
12794 t.Fatalf("first Close failed: %v", err)
12895 }
@@ -139,12 +106,10 @@ func TestPlayerCloseIsIdempotent(t *testing.T) {
139106 }
140107 }
141108
142-func TestPlayerCallbacksAreWired(t *testing.T) {
143- var stateChanges []PlayerState
144- var fillCalled bool
145- var underrunFrames int
146- var drainCalled bool
147-
109+// TestPlayerCallbacksAreAccepted verifies that NewPlayer accepts all callback
110+// fields without error. Actual callback invocation (via Start/Fill/etc.) is
111+// covered by internal/core mock tests and integration tests.
112+func TestPlayerCallbacksAreAccepted(t *testing.T) {
148113 config := PlayerConfig{
149114 SampleRate: 44100,
150115 Channels: 2,
@@ -155,21 +120,11 @@ func TestPlayerCallbacksAreWired(t *testing.T) {
155120
156121 callbacks := PlayerCallbacks{
157122 Fill: func(buf *PCMBuffer) (int, error) {
158- fillCalled = true
159- _ = fillCalled // Suppress unused warning for now
160123 return buf.Frames, nil
161124 },
162- OnUnderrun: func(frames int) {
163- underrunFrames = frames
164- _ = underrunFrames // Suppress unused warning for now
165- },
166- OnDrain: func() {
167- drainCalled = true
168- _ = drainCalled // Suppress unused warning for now
169- },
170- OnStateChange: func(state PlayerState) {
171- stateChanges = append(stateChanges, state)
172- },
125+ OnUnderrun: func(frames int) {},
126+ OnDrain: func() {},
127+ OnStateChange: func(state PlayerState) {},
173128 }
174129
175130 player, err := NewPlayer(config, callbacks)
@@ -177,22 +132,12 @@ func TestPlayerCallbacksAreWired(t *testing.T) {
177132 t.Fatalf("NewPlayer returned error: %v", err)
178133 }
179134
180- // Verify initial state
135+ // Player should be in Idle state
181136 if player.State() != PlayerStateIdle {
182137 t.Fatalf("expected PlayerStateIdle, got %v", player.State())
183138 }
184139
185- // Start should trigger state change callback
186- if err := player.Start(); err != nil {
187- t.Fatalf("Start failed: %v", err)
188- }
189-
190- // Verify we got state change callbacks
191- if len(stateChanges) == 0 {
192- t.Fatal("expected OnStateChange callbacks, got none")
193- }
194-
195- // Clean up
140+ // Close should work cleanly from Idle
196141 if err := player.Close(); err != nil {
197142 t.Fatalf("Close failed: %v", err)
198143 }
@@ -47,7 +47,11 @@ func TestPlayerStateReflectsLifecycle(t *testing.T) {
47 }47 }
48 }48 }
49 49
50-func TestPlayerLifecycleStartStopClose(t *testing.T) {50+// TestPlayerCloseFromIdleAndStartFromClosed covers the non-runtime lifecycle
51+// edges: closing an idle player and rejecting Start after Close.
52+// Full Start→Pause→Stop→Close lifecycle is tested in internal/core with mocks
53+// and in integration tests with a live runtime.
54+func TestPlayerCloseFromIdleAndStartFromClosed(t *testing.T) {
51 config := PlayerConfig{55 config := PlayerConfig{
52 SampleRate: 44100,56 SampleRate: 44100,
53 Channels: 2,57 Channels: 2,
@@ -59,41 +63,9 @@ func TestPlayerLifecycleStartStopClose(t *testing.T) {
59 t.Fatalf("NewPlayer returned error: %v", err)63 t.Fatalf("NewPlayer returned error: %v", err)
60 }64 }
61 65
62- // Start should transition to Playing66+ // Idle → Close should succeed without a live runtime
63- if err := player.Start(); err != nil {
64- t.Fatalf("Start failed: %v", err)
65- }
66- if player.State() != PlayerStatePlaying {
67- t.Fatalf("expected Playing state after Start, got %v", player.State())
68- }
69-
70- // Pause should transition to Paused
71- if err := player.Pause(); err != nil {
72- t.Fatalf("Pause failed: %v", err)
73- }
74- if player.State() != PlayerStatePaused {
75- t.Fatalf("expected Paused state after Pause, got %v", player.State())
76- }
77-
78- // Stop should transition to Stopped
79- if err := player.Stop(); err != nil {
80- t.Fatalf("Stop failed: %v", err)
81- }
82- if player.State() != PlayerStateStopped {
83- t.Fatalf("expected Stopped state after Stop, got %v", player.State())
84- }
85-
86- // Restart should work from Stopped
87- if err := player.Start(); err != nil {
88- t.Fatalf("Start from Stopped failed: %v", err)
89- }
90- if player.State() != PlayerStatePlaying {
91- t.Fatalf("expected Playing state after restart, got %v", player.State())
92- }
93-
94- // Close should transition to Closed
95 if err := player.Close(); err != nil {67 if err := player.Close(); err != nil {
96- t.Fatalf("Close failed: %v", err)68+ t.Fatalf("Close from Idle failed: %v", err)
97 }69 }
98 if player.State() != PlayerStateClosed {70 if player.State() != PlayerStateClosed {
99 t.Fatalf("expected Closed state after Close, got %v", player.State())71 t.Fatalf("expected Closed state after Close, got %v", player.State())
@@ -117,12 +89,7 @@ func TestPlayerCloseIsIdempotent(t *testing.T) {
117 t.Fatalf("NewPlayer returned error: %v", err)89 t.Fatalf("NewPlayer returned error: %v", err)
118 }90 }
119 91
120- // Start first92+ // First close from Idle should work
121- if err := player.Start(); err != nil {
122- t.Fatalf("Start failed: %v", err)
123- }
124-
125- // First close should work
126 if err := player.Close(); err != nil {93 if err := player.Close(); err != nil {
127 t.Fatalf("first Close failed: %v", err)94 t.Fatalf("first Close failed: %v", err)
128 }95 }
@@ -139,12 +106,10 @@ func TestPlayerCloseIsIdempotent(t *testing.T) {
139 }106 }
140 }107 }
141 108
142-func TestPlayerCallbacksAreWired(t *testing.T) {109+// TestPlayerCallbacksAreAccepted verifies that NewPlayer accepts all callback
143- var stateChanges []PlayerState110+// fields without error. Actual callback invocation (via Start/Fill/etc.) is
144- var fillCalled bool111+// covered by internal/core mock tests and integration tests.
145- var underrunFrames int112+func TestPlayerCallbacksAreAccepted(t *testing.T) {
146- var drainCalled bool
147-
148 config := PlayerConfig{113 config := PlayerConfig{
149 SampleRate: 44100,114 SampleRate: 44100,
150 Channels: 2,115 Channels: 2,
@@ -155,21 +120,11 @@ func TestPlayerCallbacksAreWired(t *testing.T) {
155 120
156 callbacks := PlayerCallbacks{121 callbacks := PlayerCallbacks{
157 Fill: func(buf *PCMBuffer) (int, error) {122 Fill: func(buf *PCMBuffer) (int, error) {
158- fillCalled = true
159- _ = fillCalled // Suppress unused warning for now
160 return buf.Frames, nil123 return buf.Frames, nil
161 },124 },
162- OnUnderrun: func(frames int) {125+ OnUnderrun: func(frames int) {},
163- underrunFrames = frames126+ OnDrain: func() {},
164- _ = underrunFrames // Suppress unused warning for now127+ OnStateChange: func(state PlayerState) {},
165- },
166- OnDrain: func() {
167- drainCalled = true
168- _ = drainCalled // Suppress unused warning for now
169- },
170- OnStateChange: func(state PlayerState) {
171- stateChanges = append(stateChanges, state)
172- },
173 }128 }
174 129
175 player, err := NewPlayer(config, callbacks)130 player, err := NewPlayer(config, callbacks)
@@ -177,22 +132,12 @@ func TestPlayerCallbacksAreWired(t *testing.T) {
177 t.Fatalf("NewPlayer returned error: %v", err)132 t.Fatalf("NewPlayer returned error: %v", err)
178 }133 }
179 134
180- // Verify initial state135+ // Player should be in Idle state
181 if player.State() != PlayerStateIdle {136 if player.State() != PlayerStateIdle {
182 t.Fatalf("expected PlayerStateIdle, got %v", player.State())137 t.Fatalf("expected PlayerStateIdle, got %v", player.State())
183 }138 }
184 139
185- // Start should trigger state change callback140+ // Close should work cleanly from Idle
186- if err := player.Start(); err != nil {
187- t.Fatalf("Start failed: %v", err)
188- }
189-
190- // Verify we got state change callbacks
191- if len(stateChanges) == 0 {
192- t.Fatal("expected OnStateChange callbacks, got none")
193- }
194-
195- // Clean up
196 if err := player.Close(); err != nil {141 if err := player.Close(); err != nil {
197 t.Fatalf("Close failed: %v", err)142 t.Fatalf("Close failed: %v", err)
198 }143 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 8

↑↓ moveenter openesc close