Skip to content

Commit cf18fa5

Browse files
Improve unit test coverage for Picker, MediaManager, RemoteControlCallback, AbstractMedia, and AudioBuffer. (#4237)
- Added `AudioBufferCoverageTest` to cover `AudioBuffer$1` and `AudioBuffer$2` (concurrent callback modifications). - Added `AbstractMediaCoverageTest` to cover `AbstractMedia$10` and `AbstractMedia$11` (chained pause requests). - Updated `MediaCoverageTest` to cover `MediaManager$1$2` (timer in `pauseImpl`) and `RemoteControlCallback` methods (`stop`, `fastForward`, `rewind`). - Verified `Picker$3$1` is covered by existing `PickerCoverageTest`. - Identified `Picker$1$8` as unreachable dead code. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent e029dc4 commit cf18fa5

File tree

3 files changed

+294
-26
lines changed

3 files changed

+294
-26
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package com.codename1.media;
2+
3+
import com.codename1.junit.FormTest;
4+
import com.codename1.junit.UITestBase;
5+
import com.codename1.ui.Component;
6+
import com.codename1.ui.Display;
7+
import com.codename1.ui.DisplayTest;
8+
import org.junit.jupiter.api.Assertions;
9+
10+
import java.util.concurrent.atomic.AtomicBoolean;
11+
12+
public class AbstractMediaCoverageTest extends UITestBase {
13+
14+
static class MockAsyncMedia extends AbstractMedia {
15+
private final AtomicBoolean playing = new AtomicBoolean(false);
16+
17+
@Override
18+
protected void playImpl() {
19+
// Simulate async completion
20+
new Thread(() -> {
21+
try {
22+
Thread.sleep(10);
23+
} catch (InterruptedException e) {}
24+
playing.set(true);
25+
// Fire on EDT to be safe with EventDispatcher?
26+
// AbstractMedia doesn't require it but it's good practice.
27+
// But let's keep it on thread to simulate native callbacks.
28+
fireMediaStateChange(State.Playing);
29+
}).start();
30+
}
31+
32+
@Override
33+
protected void pauseImpl() {
34+
// Simulate async completion
35+
new Thread(() -> {
36+
try {
37+
Thread.sleep(10);
38+
} catch (InterruptedException e) {}
39+
playing.set(false);
40+
fireMediaStateChange(State.Paused);
41+
}).start();
42+
}
43+
44+
@Override
45+
public void prepare() {}
46+
@Override
47+
public void cleanup() {}
48+
@Override
49+
public int getTime() { return 0; }
50+
@Override
51+
public void setTime(int time) {}
52+
@Override
53+
public int getDuration() { return 0; }
54+
@Override
55+
public int getVolume() { return 0; }
56+
@Override
57+
public void setVolume(int vol) {}
58+
@Override
59+
public boolean isPlaying() { return playing.get(); }
60+
@Override
61+
public Component getVideoComponent() { return null; }
62+
@Override
63+
public boolean isVideo() { return false; }
64+
@Override
65+
public boolean isFullScreen() { return false; }
66+
@Override
67+
public void setFullScreen(boolean fullScreen) {}
68+
@Override
69+
public boolean isNativePlayerMode() { return false; }
70+
@Override
71+
public void setNativePlayerMode(boolean nativePlayer) {}
72+
@Override
73+
public void setVariable(String key, Object value) {}
74+
@Override
75+
public Object getVariable(String key) { return null; }
76+
}
77+
78+
@FormTest
79+
public void testChainedPauseRequests() throws InterruptedException {
80+
// This test targets AbstractMedia$10 and AbstractMedia$11
81+
// which are created when pauseAsync is called while a pause request is pending.
82+
83+
MockAsyncMedia media = new MockAsyncMedia();
84+
// Start playing first
85+
AsyncMedia.PlayRequest playReq = media.playAsync();
86+
long start = System.currentTimeMillis();
87+
while (!playReq.isDone() && System.currentTimeMillis() - start < 2000) {
88+
DisplayTest.flushEdt();
89+
Thread.sleep(10);
90+
}
91+
Assertions.assertTrue(playReq.isDone(), "Play request should complete");
92+
93+
// Now trigger pause
94+
AsyncMedia.PauseRequest pauseReq1 = media.pauseAsync();
95+
96+
// While pauseReq1 is pending, trigger pauseAsync again
97+
AsyncMedia.PauseRequest pauseReq2 = media.pauseAsync();
98+
99+
Assertions.assertNotSame(pauseReq1, pauseReq2, "Should create new request object");
100+
101+
// Wait for pauseReq1
102+
start = System.currentTimeMillis();
103+
while (!pauseReq1.isDone() && System.currentTimeMillis() - start < 2000) {
104+
DisplayTest.flushEdt();
105+
Thread.sleep(10);
106+
}
107+
Assertions.assertTrue(pauseReq1.isDone(), "First pause request should complete");
108+
109+
// pauseReq2 should also be done (chained)
110+
start = System.currentTimeMillis();
111+
while (!pauseReq2.isDone() && System.currentTimeMillis() - start < 2000) {
112+
DisplayTest.flushEdt();
113+
Thread.sleep(10);
114+
}
115+
116+
Assertions.assertTrue(pauseReq2.isDone(), "Chained pause request should complete");
117+
}
118+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.codename1.media;
2+
3+
import com.codename1.junit.FormTest;
4+
import com.codename1.junit.UITestBase;
5+
import org.junit.jupiter.api.Assertions;
6+
7+
import java.util.concurrent.atomic.AtomicBoolean;
8+
9+
public class AudioBufferCoverageTest extends UITestBase {
10+
11+
@FormTest
12+
public void testAddRemoveCallbackDuringFire() {
13+
AudioBuffer buffer = new AudioBuffer(1024);
14+
15+
final AtomicBoolean callback2Ran = new AtomicBoolean(false);
16+
final AudioBuffer.AudioBufferCallback callback2 = new AudioBuffer.AudioBufferCallback() {
17+
@Override
18+
public void frameReceived(AudioBuffer buffer) {
19+
callback2Ran.set(true);
20+
}
21+
};
22+
23+
final AudioBuffer.AudioBufferCallback callback3 = new AudioBuffer.AudioBufferCallback() {
24+
@Override
25+
public void frameReceived(AudioBuffer buffer) {
26+
// Do nothing
27+
}
28+
};
29+
buffer.addCallback(callback3);
30+
31+
32+
// Add a callback that adds another callback while firing
33+
// Covers AudioBuffer$1 (Runnable in addCallback)
34+
buffer.addCallback(new AudioBuffer.AudioBufferCallback() {
35+
@Override
36+
public void frameReceived(AudioBuffer b) {
37+
// This is called during fireFrameReceived
38+
// So inFireFrame is true.
39+
// Adding callback2 should be deferred.
40+
b.addCallback(callback2);
41+
42+
// Removing callback3 should be deferred.
43+
// Covers AudioBuffer$2 (Runnable in removeCallback)
44+
b.removeCallback(callback3);
45+
}
46+
});
47+
48+
float[] data = new float[1024];
49+
// This triggers fireFrameReceived
50+
buffer.copyFrom(44100, 1, data);
51+
52+
// At this point, fireFrameReceived finished.
53+
// Pending ops should have run.
54+
// callback2 should be added.
55+
// callback3 should be removed.
56+
57+
callback2Ran.set(false);
58+
buffer.copyFrom(44100, 1, data);
59+
60+
Assertions.assertTrue(callback2Ran.get(), "Callback added during fire should be active in next fire");
61+
}
62+
}

maven/core-unittests/src/test/java/com/codename1/media/MediaCoverageTest.java

Lines changed: 114 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.codename1.media;
22

3-
import com.codename1.ui.Display;
43
import com.codename1.ui.DisplayTest;
54
import com.codename1.testing.TestCodenameOneImplementation;
65
import org.junit.jupiter.api.AfterEach;
@@ -23,6 +22,10 @@ public void testRemoteControlCallback() {
2322
final AtomicBoolean pauseCalled = new AtomicBoolean(false);
2423
final AtomicBoolean toggleCalled = new AtomicBoolean(false);
2524
final AtomicBoolean seekCalled = new AtomicBoolean(false);
25+
final AtomicBoolean stopCalled = new AtomicBoolean(false);
26+
final AtomicBoolean ffCalled = new AtomicBoolean(false);
27+
final AtomicBoolean rwCalled = new AtomicBoolean(false);
28+
2629
final long[] seekPos = new long[1];
2730

2831
RemoteControlListener listener = new RemoteControlListener() {
@@ -41,6 +44,21 @@ public void seekTo(long pos) {
4144
seekCalled.set(true);
4245
seekPos[0] = pos;
4346
}
47+
48+
@Override
49+
public void stop() {
50+
stopCalled.set(true);
51+
}
52+
53+
@Override
54+
public void fastForward() {
55+
ffCalled.set(true);
56+
}
57+
58+
@Override
59+
public void rewind() {
60+
rwCalled.set(true);
61+
}
4462
};
4563

4664
MediaManager.setRemoteControlListener(listener);
@@ -57,6 +75,19 @@ public void seekTo(long pos) {
5775
DisplayTest.flushEdt();
5876
Assertions.assertTrue(seekCalled.get(), "RemoteControlCallback.seekTo() should call listener.seekTo()");
5977
Assertions.assertEquals(12345L, seekPos[0], "RemoteControlCallback.seekTo() should pass correct position");
78+
79+
// Test missing methods for coverage
80+
RemoteControlCallback.stop();
81+
DisplayTest.flushEdt();
82+
Assertions.assertTrue(stopCalled.get(), "RemoteControlCallback.stop() should call listener.stop()");
83+
84+
RemoteControlCallback.fastForward();
85+
DisplayTest.flushEdt();
86+
Assertions.assertTrue(ffCalled.get(), "RemoteControlCallback.fastForward() should call listener.fastForward()");
87+
88+
RemoteControlCallback.rewind();
89+
DisplayTest.flushEdt();
90+
Assertions.assertTrue(rwCalled.get(), "RemoteControlCallback.rewind() should call listener.rewind()");
6091
}
6192

6293
@FormTest
@@ -129,37 +160,94 @@ public void setVariable(String key, Object value) {}
129160
playing.set(true);
130161

131162
long start = System.currentTimeMillis();
132-
// Drive animation loop manually to trigger MediaManager's timer
133-
// Note: In unit tests, Display.registerAnimated() might not be automatically serviced.
134-
// We manually animate the current form, which we hope triggers necessary updates,
135-
// or we rely on the fact that MediaManager might be checking via other means or we just test what we can.
136-
// Since MediaManager uses Display.registerAnimated, and Display.animate() isn't easily accessible,
137-
// we might rely on the fact that we are in a FormTest and ensure we pump events.
138-
// Actually, without access to Display.animate(), this test is flaky if MediaManager relies *solely* on it.
139-
// However, let's try driving the form animation and flushing EDT.
140-
141163
while (!stateChangedToPlaying.get() && System.currentTimeMillis() - start < 3000) {
142-
if (Display.getInstance().getCurrent() != null) {
143-
Display.getInstance().getCurrent().animate();
144-
}
145164
DisplayTest.flushEdt();
165+
Thread.sleep(50);
166+
}
167+
}
168+
169+
@FormTest
170+
public void testPauseImplTimer() throws InterruptedException {
171+
// Targets MediaManager$1$2: TimerTask inside pauseImpl of getAsyncMedia
172+
173+
final AtomicBoolean playing = new AtomicBoolean(true);
174+
final AtomicBoolean pauseCalled = new AtomicBoolean(false);
175+
176+
Media mockMedia = new Media() {
177+
@Override
178+
public void prepare() {}
179+
@Override
180+
public void play() {}
181+
@Override
182+
public void pause() {
183+
pauseCalled.set(true);
184+
}
185+
@Override
186+
public void cleanup() {}
187+
@Override
188+
public int getTime() { return 0; }
189+
@Override
190+
public void setTime(int time) {}
191+
@Override
192+
public int getDuration() { return 0; }
193+
@Override
194+
public int getVolume() { return 0; }
195+
@Override
196+
public void setVolume(int vol) {}
197+
@Override
198+
public boolean isPlaying() {
199+
return playing.get();
200+
}
201+
@Override
202+
public Component getVideoComponent() { return null; }
203+
@Override
204+
public boolean isVideo() { return false; }
205+
@Override
206+
public boolean isFullScreen() { return false; }
207+
@Override
208+
public void setFullScreen(boolean fullScreen) {}
209+
@Override
210+
public boolean isNativePlayerMode() { return false; }
211+
@Override
212+
public void setNativePlayerMode(boolean nativePlayer) {}
213+
@Override
214+
public void setVariable(String key, Object value) {}
215+
@Override
216+
public Object getVariable(String key) { return null; }
217+
};
218+
219+
AsyncMedia asyncMedia = MediaManager.getAsyncMedia(mockMedia);
220+
221+
final AtomicBoolean pausedEventFired = new AtomicBoolean(false);
222+
asyncMedia.addMediaStateChangeListener(evt -> {
223+
if (evt.getNewState() == AsyncMedia.State.Paused) {
224+
pausedEventFired.set(true);
225+
}
226+
});
227+
228+
// Ensure we are in Playing state initially (mock isPlaying=true)
229+
Assertions.assertEquals(AsyncMedia.State.Playing, asyncMedia.getState());
230+
231+
// Call pause. This calls pauseImpl.
232+
asyncMedia.pause();
233+
234+
Assertions.assertTrue(pauseCalled.get(), "mockMedia.pause() should be called");
235+
236+
// Media is still "playing" (mock returns true).
237+
// So pauseImpl starts a timer (MediaManager$1$2).
146238

147-
// Hack: Trigger MediaManager animation if possible?
148-
// We can't access it.
149-
// If this fails, we might need to skip this part or verify logic differently.
239+
// Now simulate media stopping playback after delay
240+
Thread.sleep(100);
241+
playing.set(false);
150242

243+
// Wait for timer to fire and check state
244+
long start = System.currentTimeMillis();
245+
while (!pausedEventFired.get() && System.currentTimeMillis() - start < 3000) {
246+
DisplayTest.flushEdt();
151247
Thread.sleep(50);
152248
}
153249

154-
// If this fails, it confirms we can't easily test the timer behavior in this harness.
155-
// But let's check the result.
156-
// Assertions.assertTrue(stateChangedToPlaying.get(), "Timer should detect playing state and fire event");
157-
158-
// Given the constraints and the likely failure due to no global animate loop:
159-
// We will assert if it worked, but if not, we might need to accept we covered the line creation
160-
// (MediaManager$1$1 is likely the listener or timer task).
161-
// The inner class Picker$1$5 etc are the targets.
162-
// For MediaManager$1$1, it is likely the Timer or Runnable.
163-
// We will leave the assertion.
250+
Assertions.assertTrue(pausedEventFired.get(), "Timer should detect paused state and fire event");
164251
}
252+
165253
}

0 commit comments

Comments
 (0)