|
1 | 1 | package com.codename1.media; |
2 | 2 |
|
3 | | -import com.codename1.junit.FormTest; |
4 | 3 | import com.codename1.junit.UITestBase; |
5 | | -import com.codename1.ui.Component; |
6 | | -import com.codename1.ui.Display; |
7 | | -import com.codename1.ui.DisplayTest; |
| 4 | +import com.codename1.junit.FormTest; |
8 | 5 | import org.junit.jupiter.api.Assertions; |
9 | | - |
10 | | -import java.util.concurrent.atomic.AtomicBoolean; |
| 6 | +import com.codename1.ui.DisplayTest; |
| 7 | +import com.codename1.ui.Component; |
| 8 | +import com.codename1.media.AsyncMedia.MediaException; |
| 9 | +import com.codename1.media.AsyncMedia.MediaErrorType; |
11 | 10 |
|
12 | 11 | public class AbstractMediaCoverageTest extends UITestBase { |
13 | 12 |
|
14 | | - static class MockAsyncMedia extends AbstractMedia { |
15 | | - private final AtomicBoolean playing = new AtomicBoolean(false); |
| 13 | + static class TestMedia extends AbstractMedia { |
| 14 | + boolean playImplCalled = false; |
| 15 | + boolean pauseImplCalled = false; |
| 16 | + boolean playing = false; |
| 17 | + int time = 0; |
| 18 | + int duration = 10000; |
| 19 | + int volume = 100; |
16 | 20 |
|
17 | 21 | @Override |
18 | 22 | 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(); |
| 23 | + playImplCalled = true; |
30 | 24 | } |
31 | 25 |
|
32 | 26 | @Override |
33 | 27 | 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(); |
| 28 | + pauseImplCalled = true; |
42 | 29 | } |
43 | 30 |
|
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; } |
| 31 | + public void triggerStateChange(State newState) { |
| 32 | + if (newState == State.Playing) playing = true; |
| 33 | + if (newState == State.Paused) playing = false; |
| 34 | + fireMediaStateChange(newState); |
| 35 | + } |
| 36 | + |
| 37 | + public void triggerError(MediaException ex) { |
| 38 | + fireMediaError(ex); |
| 39 | + } |
| 40 | + |
| 41 | + @Override public void setTime(int time) { this.time = time; } |
| 42 | + @Override public int getTime() { return time; } |
| 43 | + @Override public int getDuration() { return duration; } |
| 44 | + @Override public void setVolume(int vol) { this.volume = vol; } |
| 45 | + @Override public int getVolume() { return volume; } |
| 46 | + @Override public boolean isPlaying() { return playing; } |
| 47 | + @Override public void cleanup() {} |
| 48 | + @Override public Component getVideoComponent() { return null; } |
| 49 | + @Override public boolean isVideo() { return false; } |
| 50 | + @Override public boolean isFullScreen() { return false; } |
| 51 | + @Override public void setFullScreen(boolean fullScreen) {} |
| 52 | + @Override public boolean isNativePlayerMode() { return false; } |
| 53 | + @Override public void setNativePlayerMode(boolean nativePlayer) {} |
| 54 | + @Override public void setVariable(String key, Object value) {} |
| 55 | + @Override public Object getVariable(String key) { return null; } |
| 56 | + @Override public void prepare() {} |
76 | 57 | } |
77 | 58 |
|
78 | 59 | @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"); |
| 60 | + public void testAsyncInterleaving() { |
| 61 | + TestMedia media = new TestMedia(); |
| 62 | + media.playing = false; // Initially paused |
92 | 63 |
|
93 | | - // Now trigger pause |
94 | | - AsyncMedia.PauseRequest pauseReq1 = media.pauseAsync(); |
| 64 | + // 1. playAsync() called. Sets pendingPlayRequest. |
| 65 | + media.playAsync(); |
| 66 | + Assertions.assertTrue(media.playImplCalled, "Play should be called"); |
| 67 | + media.playImplCalled = false; |
95 | 68 |
|
96 | | - // While pauseReq1 is pending, trigger pauseAsync again |
97 | | - AsyncMedia.PauseRequest pauseReq2 = media.pauseAsync(); |
| 69 | + // 2. pauseAsync() called while play is pending (state not yet Playing). |
| 70 | + // This should attach listeners to the pending play request. |
| 71 | + AsyncMedia.PauseRequest pauseReq = media.pauseAsync(); |
98 | 72 |
|
99 | | - Assertions.assertNotSame(pauseReq1, pauseReq2, "Should create new request object"); |
| 73 | + // 3. Fail the play request. |
| 74 | + // This should trigger the except callback ($9) attached by pauseAsync. |
| 75 | + // The callback calls pauseAsync(out). |
| 76 | + media.triggerError(new MediaException(MediaErrorType.Unknown, "Simulated error")); |
100 | 77 |
|
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"); |
| 78 | + DisplayTest.flushEdt(); |
108 | 79 |
|
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 | | - } |
| 80 | + // When play fails, pendingPlayRequest becomes null. |
| 81 | + // The callback calls pauseAsync(out). |
| 82 | + // Since pendingPlayRequest is null, it proceeds to check pendingPauseRequest. |
| 83 | + // It proceeds to check state. State is Paused (playing=false). |
| 84 | + // So it completes immediately without calling pauseImpl. |
| 85 | + |
| 86 | + Assertions.assertTrue(pauseReq.isDone(), "Pause request should complete"); |
| 87 | + } |
| 88 | + |
| 89 | + @FormTest |
| 90 | + public void testPlayAfterPause() { |
| 91 | + TestMedia media = new TestMedia(); |
| 92 | + media.playing = true; // Initially playing so pauseAsync works |
| 93 | + media.pauseImplCalled = false; |
| 94 | + media.playImplCalled = false; |
| 95 | + |
| 96 | + // 1. pauseAsync |
| 97 | + media.pauseAsync(); |
| 98 | + Assertions.assertTrue(media.pauseImplCalled, "Pause should be called"); |
| 99 | + media.pauseImplCalled = false; |
| 100 | + |
| 101 | + // 2. playAsync while pause is pending (state not yet Paused) |
| 102 | + media.playAsync(); |
| 103 | + |
| 104 | + // 3. Succeed pause |
| 105 | + media.triggerStateChange(AsyncMedia.State.Paused); |
| 106 | + DisplayTest.flushEdt(); |
| 107 | + |
| 108 | + // Pause completed. Listener calls playAsync(). |
| 109 | + // playAsync sees state Paused. Calls playImpl. |
| 110 | + |
| 111 | + Assertions.assertTrue(media.playImplCalled, "Play should be called after pause success"); |
| 112 | + } |
| 113 | + |
| 114 | + @FormTest |
| 115 | + public void testPlayAfterPlay() { |
| 116 | + TestMedia media = new TestMedia(); |
| 117 | + media.playing = false; |
| 118 | + media.playAsync(); |
| 119 | + // pendingPlayRequest is set. |
| 120 | + |
| 121 | + AsyncMedia.PlayRequest req2 = media.playAsync(); |
| 122 | + // This attaches ready/except to pendingPlayRequest ($4, $5). |
| 123 | + |
| 124 | + media.triggerStateChange(AsyncMedia.State.Playing); |
| 125 | + DisplayTest.flushEdt(); |
115 | 126 |
|
116 | | - Assertions.assertTrue(pauseReq2.isDone(), "Chained pause request should complete"); |
| 127 | + // Both requests should complete. |
| 128 | + Assertions.assertTrue(req2.isDone(), "Second play request should be done"); |
117 | 129 | } |
118 | 130 | } |
0 commit comments