From 69c841850c3788dba1994a62d035edc18d3403e3 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:10:13 +0200 Subject: [PATCH 1/2] Add tests for request listeners and UI fallbacks --- .../RequestBuilderActionListenerTest.java | 221 ++++++++++++++++++ .../com/codename1/payment/PurchaseTest.java | 31 +++ .../com/codename1/ui/BrowserWindowTest.java | 33 ++- .../java/com/codename1/ui/DisplayTest.java | 40 ++++ 4 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 maven/core-unittests/src/test/java/com/codename1/io/rest/RequestBuilderActionListenerTest.java diff --git a/maven/core-unittests/src/test/java/com/codename1/io/rest/RequestBuilderActionListenerTest.java b/maven/core-unittests/src/test/java/com/codename1/io/rest/RequestBuilderActionListenerTest.java new file mode 100644 index 0000000000..2afe8e79fa --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/io/rest/RequestBuilderActionListenerTest.java @@ -0,0 +1,221 @@ +package com.codename1.io.rest; + +import com.codename1.junit.UITestBase; +import com.codename1.properties.IntProperty; +import com.codename1.properties.Property; +import com.codename1.properties.PropertyBusinessObject; +import com.codename1.properties.PropertyIndex; +import com.codename1.testing.TestCodenameOneImplementation.TestConnection; +import com.codename1.util.Callback; +import com.codename1.util.OnComplete; +import com.codename1.util.SuccessCallback; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class RequestBuilderActionListenerTest extends UITestBase { + private static final String BASE_URL = "https://example.com"; + + @BeforeEach + void clearConnections() { + implementation.clearConnections(); + implementation.clearQueuedRequests(); + } + + @Test + void fetchAsJsonMapInvokesCallback() { + prepareJsonConnection(BASE_URL + "/json", "{\"status\":\"ok\",\"count\":2}"); + + RequestBuilder builder = new RequestBuilder("GET", BASE_URL + "/json"); + final AtomicReference> holder = new AtomicReference>(); + CountDownLatch latch = new CountDownLatch(1); + + builder.fetchAsJsonMap(new OnComplete>() { + public void completed(Response value) { + holder.set(value); + latch.countDown(); + } + }); + + waitFor(latch, 2000); + + Response response = holder.get(); + assertNotNull(response); + assertEquals(200, response.getResponseCode()); + assertEquals("ok", response.getResponseData().get("status")); + } + + @Test + void getAsJsonMapInvokesCallback() { + prepareJsonConnection(BASE_URL + "/legacy-json", "{\"status\":\"legacy\"}"); + + RequestBuilder builder = new RequestBuilder("GET", BASE_URL + "/legacy-json"); + final AtomicReference> holder = new AtomicReference>(); + CountDownLatch latch = new CountDownLatch(1); + + builder.getAsJsonMap(new SuccessCallback>() { + public void onSucess(Response value) { + holder.set(value); + latch.countDown(); + } + }); + + waitFor(latch, 2000); + + Response response = holder.get(); + assertNotNull(response); + assertEquals("legacy", response.getResponseData().get("status")); + } + + @Test + void fetchAsPropertiesParsesBusinessObject() { + prepareJsonConnection(BASE_URL + "/item", "{\"name\":\"Widget\",\"count\":4}"); + + RequestBuilder builder = new RequestBuilder("GET", BASE_URL + "/item"); + final AtomicReference> holder = new AtomicReference>(); + CountDownLatch latch = new CountDownLatch(1); + + builder.fetchAsProperties(new OnComplete>() { + public void completed(Response value) { + holder.set(value); + latch.countDown(); + } + }, SampleItem.class); + + waitFor(latch, 2000); + + Response response = holder.get(); + assertNotNull(response); + SampleItem item = (SampleItem) response.getResponseData(); + assertEquals("Widget", item.name.get()); + assertEquals(4, item.count.get()); + } + + @Test + void fetchAsPropertyListParsesBusinessObjectList() { + prepareJsonConnection(BASE_URL + "/items", "{\"items\":[{\"name\":\"A\",\"count\":1},{\"name\":\"B\",\"count\":2}]}"); + + RequestBuilder builder = new RequestBuilder("GET", BASE_URL + "/items"); + final AtomicReference>> holder = new AtomicReference>>(); + CountDownLatch latch = new CountDownLatch(1); + + builder.fetchAsPropertyList(new OnComplete>>() { + public void completed(Response> value) { + holder.set(value); + latch.countDown(); + } + }, SampleItem.class, "items"); + + waitFor(latch, 2000); + + Response> response = holder.get(); + assertNotNull(response); + List items = response.getResponseData(); + assertEquals(2, items.size()); + SampleItem first = (SampleItem) items.get(0); + SampleItem second = (SampleItem) items.get(1); + assertEquals("A", first.name.get()); + assertEquals(2, second.count.get()); + } + + @Test + void getAsBytesAsyncSupportsCallbackAndOnComplete() { + byte[] payload = "bytes".getBytes(StandardCharsets.UTF_8); + prepareBytesConnection(BASE_URL + "/bytes-legacy", payload); + prepareBytesConnection(BASE_URL + "/bytes", payload); + + final AtomicReference> callbackHolder = new AtomicReference>(); + final AtomicReference> onCompleteHolder = new AtomicReference>(); + CountDownLatch latch = new CountDownLatch(2); + + RequestBuilder legacyBuilder = new RequestBuilder("GET", BASE_URL + "/bytes-legacy"); + legacyBuilder.getAsBytesAsync(new Callback>() { + public void onSucess(Response value) { + callbackHolder.set(value); + latch.countDown(); + } + + public void onError(Object sender, Throwable err, int errorCode, String errorMessage) { + } + }); + + RequestBuilder builder = new RequestBuilder("GET", BASE_URL + "/bytes"); + builder.fetchAsBytes(new OnComplete>() { + public void completed(Response value) { + onCompleteHolder.set(value); + latch.countDown(); + } + }); + + waitFor(latch, 2000); + + assertTrue(Arrays.equals(payload, callbackHolder.get().getResponseData())); + assertTrue(Arrays.equals(payload, onCompleteHolder.get().getResponseData())); + } + + @Test + void getAsStringAsyncSupportsCallbackAndOnComplete() { + prepareBytesConnection(BASE_URL + "/string-legacy", "hello".getBytes(StandardCharsets.UTF_8)); + prepareBytesConnection(BASE_URL + "/string", "world".getBytes(StandardCharsets.UTF_8)); + + final AtomicReference> callbackHolder = new AtomicReference>(); + final AtomicReference> onCompleteHolder = new AtomicReference>(); + CountDownLatch latch = new CountDownLatch(2); + + RequestBuilder legacyBuilder = new RequestBuilder("GET", BASE_URL + "/string-legacy"); + legacyBuilder.getAsStringAsync(new Callback>() { + public void onSucess(Response value) { + callbackHolder.set(value); + latch.countDown(); + } + + public void onError(Object sender, Throwable err, int errorCode, String errorMessage) { + } + }); + + RequestBuilder builder = new RequestBuilder("GET", BASE_URL + "/string"); + builder.fetchAsString(new OnComplete>() { + public void completed(Response value) { + onCompleteHolder.set(value); + latch.countDown(); + } + }); + + waitFor(latch, 2000); + + assertEquals("hello", callbackHolder.get().getResponseData()); + assertEquals("world", onCompleteHolder.get().getResponseData()); + } + + private void prepareJsonConnection(String url, String json) { + prepareBytesConnection(url, json.getBytes(StandardCharsets.UTF_8)); + } + + private void prepareBytesConnection(String url, byte[] payload) { + TestConnection connection = implementation.createConnection(url); + connection.setInputData(payload); + connection.setContentLength(payload.length); + connection.setResponseCode(200); + connection.setResponseMessage("OK"); + } + + static class SampleItem implements PropertyBusinessObject { + final Property name = new Property("name"); + final IntProperty count = new IntProperty("count"); + private final PropertyIndex index = new PropertyIndex(this, "SampleItem", name, count); + + public PropertyIndex getPropertyIndex() { + return index; + } + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/payment/PurchaseTest.java b/maven/core-unittests/src/test/java/com/codename1/payment/PurchaseTest.java index 927f3b0491..d297568096 100644 --- a/maven/core-unittests/src/test/java/com/codename1/payment/PurchaseTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/payment/PurchaseTest.java @@ -9,6 +9,7 @@ import com.codename1.util.SuccessCallback; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; @@ -242,6 +243,36 @@ void testSynchronizeReceiptsSyncReturnsFalseWhenSubmitFails() { assertEquals("silver", pending.get(0).getSku()); } + @Test + void testSynchronizeReceiptsSyncWaitsForAsyncFetch() { + final Receipt asyncReceipt = createReceipt("async", new Date(1000L), new Date(5000L)); + ReceiptStore store = new ReceiptStore() { + public void fetchReceipts(final SuccessCallback callback) { + new Thread(new Runnable() { + public void run() { + try { + Thread.sleep(50L); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + callback.onSucess(new Receipt[]{asyncReceipt}); + } + }, "ReceiptFetch").start(); + } + + public void submitReceipt(Receipt receipt, SuccessCallback callback) { + callback.onSucess(Boolean.TRUE); + } + }; + purchase.setReceiptStore(store); + + boolean success = purchase.synchronizeReceiptsSync(0); + assertTrue(success); + List receipts = purchase.getReceipts(); + assertEquals(1, receipts.size()); + assertEquals("async", receipts.get(0).getSku()); + } + private static class TestReceiptStore implements ReceiptStore { private List receipts = new ArrayList(); private final List submitted = new ArrayList(); diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/BrowserWindowTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/BrowserWindowTest.java index 2cc90d90dd..21dd7cc40a 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/BrowserWindowTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/BrowserWindowTest.java @@ -5,11 +5,8 @@ import com.codename1.ui.events.ActionEvent; import com.codename1.ui.events.ActionListener; import com.codename1.ui.geom.Dimension; -import com.codename1.ui.plaf.Style; import org.junit.jupiter.api.Test; -import java.lang.reflect.Field; -import java.util.Hashtable; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.*; @@ -66,4 +63,34 @@ public void testEvalRequest() { request.setJS("alert('Hello');"); assertEquals("alert('Hello');", request.getJS()); } + + @FormTest + void fallbackWindowUsesBrowserForm() { + implementation.setNativeBrowserWindow(null); + + Form backForm = Display.getInstance().getCurrent(); + assertNotNull(backForm); + + BrowserWindow window = new BrowserWindow("https://example.com/start"); + AtomicInteger closeCount = new AtomicInteger(); + window.addCloseListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + closeCount.incrementAndGet(); + } + }); + + window.show(); + Form browserForm = Display.getInstance().getCurrent(); + assertNotSame(backForm, browserForm); + + window.setTitle("Docs"); + assertEquals("Docs", browserForm.getTitle()); + + window.close(); + assertEquals(1, closeCount.get()); + assertSame(backForm, Display.getInstance().getCurrent()); + + window.close(); + assertEquals(1, closeCount.get()); + } } diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/DisplayTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/DisplayTest.java index d6f4d766a5..e94eb43b92 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/DisplayTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/DisplayTest.java @@ -1,10 +1,14 @@ package com.codename1.ui; import com.codename1.junit.UITestBase; +import com.codename1.system.CrashReport; import com.codename1.ui.plaf.Style; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + import static org.junit.jupiter.api.Assertions.*; public class DisplayTest extends UITestBase { @@ -97,4 +101,40 @@ public void run() { display.setEnableAsyncStackTraces(oldEnable); } } + + @Test + void testEdtExceptionCapturedByCrashReporter() { + Display display = Display.getInstance(); + boolean oldEnable = display.isEnableAsyncStackTraces(); + CrashReport oldReporter = display.getCrashReporter(); + final AtomicReference captured = new AtomicReference(); + CountDownLatch latch = new CountDownLatch(1); + + try { + display.setEnableAsyncStackTraces(true); + display.setCrashReporter(new CrashReport() { + public void exception(Throwable t) { + captured.set(t); + latch.countDown(); + } + }); + + display.callSerially(new Runnable() { + public void run() { + throw new RuntimeException("boom"); + } + }); + + waitFor(latch, 2000); + + Throwable error = captured.get(); + assertNotNull(error); + assertEquals("com.codename1.ui.Display$EdtException", error.getClass().getName()); + assertNotNull(error.getCause()); + assertEquals("boom", error.getCause().getMessage()); + } finally { + display.setCrashReporter(oldReporter); + display.setEnableAsyncStackTraces(oldEnable); + } + } } From 9600d07cd517335ac2b883d1abc9ae2f04f43aff Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:27:20 +0200 Subject: [PATCH 2/2] Remove unstable EDT exception test --- .../java/com/codename1/ui/DisplayTest.java | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/DisplayTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/DisplayTest.java index e94eb43b92..c006d423c3 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/DisplayTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/DisplayTest.java @@ -1,14 +1,10 @@ package com.codename1.ui; import com.codename1.junit.UITestBase; -import com.codename1.system.CrashReport; import com.codename1.ui.plaf.Style; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - import static org.junit.jupiter.api.Assertions.*; public class DisplayTest extends UITestBase { @@ -102,39 +98,4 @@ public void run() { } } - @Test - void testEdtExceptionCapturedByCrashReporter() { - Display display = Display.getInstance(); - boolean oldEnable = display.isEnableAsyncStackTraces(); - CrashReport oldReporter = display.getCrashReporter(); - final AtomicReference captured = new AtomicReference(); - CountDownLatch latch = new CountDownLatch(1); - - try { - display.setEnableAsyncStackTraces(true); - display.setCrashReporter(new CrashReport() { - public void exception(Throwable t) { - captured.set(t); - latch.countDown(); - } - }); - - display.callSerially(new Runnable() { - public void run() { - throw new RuntimeException("boom"); - } - }); - - waitFor(latch, 2000); - - Throwable error = captured.get(); - assertNotNull(error); - assertEquals("com.codename1.ui.Display$EdtException", error.getClass().getName()); - assertNotNull(error.getCause()); - assertEquals("boom", error.getCause().getMessage()); - } finally { - display.setCrashReporter(oldReporter); - display.setEnableAsyncStackTraces(oldEnable); - } - } }