diff --git a/maven/core-unittests/src/test/java/com/codename1/samples/AsyncResourceSample.java b/maven/core-unittests/src/test/java/com/codename1/samples/AsyncResourceSample.java new file mode 100644 index 0000000000..3346f14e48 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/samples/AsyncResourceSample.java @@ -0,0 +1,72 @@ +package com.codename1.samples; + +import com.codename1.components.ToastBar; +import com.codename1.io.Log; +import com.codename1.media.MediaManager; +import com.codename1.ui.Button; +import com.codename1.ui.Dialog; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BoxLayout; + +/** + * Test-local copy of the AsyncResource sample used by {@link AsyncResourceSampleTest}. + */ +public class AsyncResourceSample { + private static final String SUCCESS_URI = "https://sample-videos.com/audio/mp3/crowd-cheering.mp3"; + private static final String ERROR_URI = "https://sample-videos.com/audio/mp3/crowd-cheering-not-found.mp3"; + + private Form current; + + public void start() { + if (current != null) { + current.show(); + return; + } + final Form hi = new Form("Hi World", BoxLayout.y()); + hi.add(new Label("Hi World")); + final Button playAsync = new Button("Play Async"); + playAsync.addActionListener(e -> handleAsyncMedia(playAsync, SUCCESS_URI)); + hi.add(playAsync); + + final Button playAsyncErr = new Button("Play Async (Not Found)"); + playAsyncErr.addActionListener(e -> handleAsyncMedia(playAsyncErr, ERROR_URI)); + hi.add(playAsyncErr); + hi.show(); + } + + private void handleAsyncMedia(final Button source, String uri) { + source.setEnabled(false); + final ToastBar.Status status = ToastBar.getInstance().createStatus(); + status.setMessage("Loading Audio..."); + status.setShowProgressIndicator(true); + status.show(); + source.repaint(); + MediaManager.createMediaAsync(uri, false, null) + .ready(media -> { + status.clear(); + source.setEnabled(true); + source.repaint(); + media.play(); + }) + .except(ex -> { + status.clear(); + source.setEnabled(true); + source.repaint(); + Log.e(ex); + ToastBar.showErrorMessage(ex.getMessage()); + }); + } + + public void stop() { + current = Display.getInstance().getCurrent(); + if (current instanceof Dialog) { + ((Dialog) current).dispose(); + current = Display.getInstance().getCurrent(); + } + } + + public void destroy() { + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/samples/AsyncResourceSampleTest.java b/maven/core-unittests/src/test/java/com/codename1/samples/AsyncResourceSampleTest.java new file mode 100644 index 0000000000..1be71fba95 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/samples/AsyncResourceSampleTest.java @@ -0,0 +1,162 @@ +package com.codename1.samples; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.media.Media; +import com.codename1.ui.Button; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.util.AsyncResource; + +import static org.junit.jupiter.api.Assertions.*; + +class AsyncResourceSampleTest extends UITestBase { + private static final String SUCCESS_URI = "https://sample-videos.com/audio/mp3/crowd-cheering.mp3"; + private static final String ERROR_URI = "https://sample-videos.com/audio/mp3/crowd-cheering-not-found.mp3"; + + @FormTest + void playAsyncCompletesAndPlaysMedia() { + AsyncResource asyncMedia = new AsyncResource(); + FakeMedia media = new FakeMedia(); + implementation.setMediaAsync(SUCCESS_URI, asyncMedia); + + AsyncResourceSample sample = new AsyncResourceSample(); + sample.start(); + + Button playAsync = findButton(Display.getInstance().getCurrent(), "Play Async"); + assertNotNull(playAsync); + + playAsync.released(); + assertFalse(playAsync.isEnabled()); + + implementation.completeMediaAsync(SUCCESS_URI, media); + flushSerialCalls(); + + assertTrue(playAsync.isEnabled()); + assertTrue(media.playInvoked); + } + + @FormTest + void playAsyncHandlesErrors() { + AsyncResource asyncMedia = new AsyncResource(); + implementation.setMediaAsync(ERROR_URI, asyncMedia); + + AsyncResourceSample sample = new AsyncResourceSample(); + sample.start(); + + Button errorButton = findButton(Display.getInstance().getCurrent(), "Play Async (Not Found)"); + assertNotNull(errorButton); + + errorButton.released(); + assertFalse(errorButton.isEnabled()); + + RuntimeException failure = new RuntimeException("Resource missing"); + implementation.failMediaAsync(ERROR_URI, failure); + flushSerialCalls(); + + assertTrue(errorButton.isEnabled()); + } + + private Button findButton(Form form, String text) { + if (form == null) { + return null; + } + return findButtonRecursive(form.getContentPane(), text); + } + + private Button findButtonRecursive(Container container, String text) { + for (Component child : container) { + if (child instanceof Button) { + Button button = (Button) child; + if (text.equals(button.getText())) { + return button; + } + } + if (child instanceof Container) { + Button nested = findButtonRecursive((Container) child, text); + if (nested != null) { + return nested; + } + } + } + return null; + } + + private static class FakeMedia implements Media { + private boolean playInvoked; + + public void play() { + playInvoked = true; + } + + public void pause() { + } + + public void prepare() { + } + + public void cleanup() { + } + + public int getTime() { + return 0; + } + + public void setTime(int time) { + } + + public int getDuration() { + return 0; + } + + public int getVolume() { + return 0; + } + + public void setVolume(int vol) { + } + + public boolean isPlaying() { + return playInvoked; + } + + public Component getVideoComponent() { + return null; + } + + public boolean isVideo() { + return false; + } + + public boolean isFullScreen() { + return false; + } + + public void setFullScreen(boolean fullScreen) { + } + + public boolean isNativePlayerMode() { + return false; + } + + public void setNativePlayerMode(boolean nativePlayerMode) { + } + + public void setVariable(String key, Object value) { + } + + public Object getVariable(String key) { + return null; + } + + public boolean isTimeSupported() { + return true; + } + + public boolean isSeekSupported() { + return true; + } + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java b/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java index 8418c66364..796623b9db 100644 --- a/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java +++ b/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java @@ -125,6 +125,7 @@ public class TestCodenameOneImplementation extends CodenameOneImplementation { private Media backgroundMedia; private Media media; private AsyncResource mediaAsync; + private final Map> mediaAsyncByUri = new ConcurrentHashMap>(); private Purchase inAppPurchase; private int startRemoteControlInvocations; private int stopRemoteControlInvocations; @@ -339,6 +340,13 @@ public boolean supportsNativeImageCache() { return nativeImageCacheSupported; } + private AsyncResource resolveMediaAsync(String uri) { + if (uri != null && mediaAsyncByUri.containsKey(uri)) { + return mediaAsyncByUri.get(uri); + } + return mediaAsync; + } + @Override public AsyncResource createBackgroundMediaAsync(String uri) { return backgroundMediaAsync; @@ -359,7 +367,7 @@ public Media createBackgroundMedia(String uri) throws IOException { @Override public AsyncResource createMediaAsync(String uri, boolean video, Runnable onCompletion) { - return mediaAsync; + return resolveMediaAsync(uri); } @Override @@ -380,6 +388,34 @@ public void setMediaAsync(AsyncResource mediaAsync) { this.mediaAsync = mediaAsync; } + public void setMediaAsync(String uri, AsyncResource asyncResource) { + if (uri == null) { + mediaAsync = asyncResource; + } else if (asyncResource == null) { + mediaAsyncByUri.remove(uri); + } else { + mediaAsyncByUri.put(uri, asyncResource); + } + } + + public void clearMediaAsyncMappings() { + mediaAsyncByUri.clear(); + } + + public void completeMediaAsync(String uri, Media value) { + AsyncResource async = resolveMediaAsync(uri); + if (async != null) { + async.complete(value); + } + } + + public void failMediaAsync(String uri, Throwable error) { + AsyncResource async = resolveMediaAsync(uri); + if (async != null) { + async.error(error); + } + } + @Override public void initializeTextSelection(TextSelection aThis) { initializeTextSelectionCount++;