Skip to content

Commit 1a32f06

Browse files
committed
Capture Android and Codename One screenshots in UI test
1 parent 83c2508 commit 1a32f06

File tree

2 files changed

+115
-22
lines changed

2 files changed

+115
-22
lines changed

scripts/build-android-app.sh

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -364,18 +364,49 @@ else
364364
fi
365365
export JAVA_HOME="$ORIGINAL_JAVA_HOME"
366366

367-
SCREENSHOT_FILE=$(find "$SCREENSHOT_OUTPUT_DIR" -maxdepth 1 -name '*.png' | head -n 1 || true)
367+
readarray -t SCREENSHOT_FILES < <(find "$SCREENSHOT_OUTPUT_DIR" -maxdepth 1 -type f -name '*.png' -print | sort)
368368
SCREENSHOT_STATUS=0
369-
if [ -z "$SCREENSHOT_FILE" ]; then
369+
if [ "${#SCREENSHOT_FILES[@]}" -eq 0 ]; then
370370
ba_log "UI test completed but no screenshot was produced in $SCREENSHOT_OUTPUT_DIR" >&2
371371
SCREENSHOT_STATUS=1
372372
else
373-
FINAL_SCREENSHOT="$FINAL_ARTIFACT_DIR/ui-test-screenshot.png"
374-
cp "$SCREENSHOT_FILE" "$FINAL_SCREENSHOT"
373+
DEFAULT_SCREENSHOT=""
374+
ANDROID_SCREENSHOT=""
375+
CODENAMEONE_SCREENSHOT=""
376+
for src in "${SCREENSHOT_FILES[@]}"; do
377+
base=$(basename "$src")
378+
dest="$FINAL_ARTIFACT_DIR/$base"
379+
cp "$src" "$dest"
380+
label="UI test"
381+
case "$base" in
382+
*-android-*.png)
383+
ANDROID_SCREENSHOT="$dest"
384+
label="Android"
385+
;;
386+
*-codenameone-*.png)
387+
CODENAMEONE_SCREENSHOT="$dest"
388+
label="Codename One"
389+
;;
390+
esac
391+
if [ -z "$DEFAULT_SCREENSHOT" ]; then
392+
DEFAULT_SCREENSHOT="$dest"
393+
fi
394+
ba_log "$label screenshot copied to $dest"
395+
done
396+
if [ -n "$ANDROID_SCREENSHOT" ]; then
397+
DEFAULT_SCREENSHOT="$ANDROID_SCREENSHOT"
398+
fi
375399
if [ -n "${GITHUB_ENV:-}" ]; then
376-
printf 'CN1_UI_TEST_SCREENSHOT=%s\n' "$FINAL_SCREENSHOT" >> "$GITHUB_ENV"
400+
if [ -n "$DEFAULT_SCREENSHOT" ]; then
401+
printf 'CN1_UI_TEST_SCREENSHOT=%s\n' "$DEFAULT_SCREENSHOT" >> "$GITHUB_ENV"
402+
fi
403+
if [ -n "$ANDROID_SCREENSHOT" ]; then
404+
printf 'CN1_UI_TEST_ANDROID_SCREENSHOT=%s\n' "$ANDROID_SCREENSHOT" >> "$GITHUB_ENV"
405+
fi
406+
if [ -n "$CODENAMEONE_SCREENSHOT" ]; then
407+
printf 'CN1_UI_TEST_CODENAMEONE_SCREENSHOT=%s\n' "$CODENAMEONE_SCREENSHOT" >> "$GITHUB_ENV"
408+
fi
377409
fi
378-
ba_log "UI test screenshot available at $FINAL_SCREENSHOT"
379410
fi
380411
unset CN1_TEST_SCREENSHOT_DIR
381412

scripts/templates/HelloCodenameOneUiTest.java.tmpl

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@ import android.view.View;
1111

1212
import com.codename1.ui.Display;
1313
import com.codename1.ui.Form;
14+
import com.codename1.ui.Image;
15+
import com.codename1.ui.util.ImageIO;
1416

1517
import java.io.File;
1618
import java.io.FileOutputStream;
1719
import java.io.IOException;
20+
import java.util.concurrent.Callable;
21+
import java.util.concurrent.FutureTask;
1822

1923
import org.junit.After;
2024
import org.junit.Before;
@@ -34,6 +38,7 @@ public class @MAIN_NAME@UiTest {
3438

3539
private static final long STARTUP_TIMEOUT_MS = 30_000L;
3640
private static final long LAYOUT_TIMEOUT_MS = 5_000L;
41+
private static final long EDT_TIMEOUT_MS = 10_000L;
3742

3843
private ActivityController<@MAIN_NAME@Stub> controller;
3944
private @MAIN_NAME@Stub activity;
@@ -61,16 +66,32 @@ public class @MAIN_NAME@UiTest {
6166

6267
ensureViewHasLayout(decorView);
6368

64-
Bitmap screenshot = captureBitmap(decorView);
65-
assertTrue("Screenshot width should be positive", screenshot.getWidth() > 0);
66-
assertTrue("Screenshot height should be positive", screenshot.getHeight() > 0);
69+
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
70+
Bitmap androidScreenshot = captureBitmap(decorView);
71+
assertTrue("Android screenshot width should be positive", androidScreenshot.getWidth() > 0);
72+
assertTrue("Android screenshot height should be positive", androidScreenshot.getHeight() > 0);
6773
assertTrue(
68-
"Screenshot should contain rendered content beyond the background",
69-
hasRenderableContent(screenshot));
70-
71-
File screenshotFile = saveScreenshot(screenshot);
72-
assertTrue("Screenshot file should exist", screenshotFile.isFile());
73-
assertTrue("Screenshot file should not be empty", screenshotFile.length() > 0L);
74+
"Android screenshot should contain rendered content beyond the background",
75+
hasRenderableContent(androidScreenshot));
76+
77+
File androidScreenshotFile = saveBitmap(androidScreenshot, "@[email protected]");
78+
assertTrue("Android screenshot file should exist", androidScreenshotFile.isFile());
79+
assertTrue("Android screenshot file should not be empty", androidScreenshotFile.length() > 0L);
80+
81+
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
82+
Image codenameOneScreenshot = captureCodenameOneScreenshot();
83+
assertNotNull("Codename One screenshot should be available", codenameOneScreenshot);
84+
assertTrue("Codename One screenshot width should be positive", codenameOneScreenshot.getWidth() > 0);
85+
assertTrue("Codename One screenshot height should be positive", codenameOneScreenshot.getHeight() > 0);
86+
assertTrue(
87+
"Codename One screenshot should contain rendered content beyond the background",
88+
hasRenderableContent(codenameOneScreenshot));
89+
90+
File codenameOneScreenshotFile = saveCodenameOneScreenshot(
91+
codenameOneScreenshot,
92+
93+
assertTrue("Codename One screenshot file should exist", codenameOneScreenshotFile.isFile());
94+
assertTrue("Codename One screenshot file should not be empty", codenameOneScreenshotFile.length() > 0L);
7495
}
7596

7697
private static Bitmap captureBitmap(View view) {
@@ -101,12 +122,20 @@ public class @MAIN_NAME@UiTest {
101122
private static boolean hasRenderableContent(Bitmap screenshot) {
102123
int width = screenshot.getWidth();
103124
int height = screenshot.getHeight();
104-
if (width <= 0 || height <= 0) {
105-
return false;
106-
}
107125
int[] pixels = new int[width * height];
108126
screenshot.getPixels(pixels, 0, width, 0, 0, width, height);
109-
if (pixels.length == 0) {
127+
return hasRenderableContent(pixels, width, height);
128+
}
129+
130+
private static boolean hasRenderableContent(Image screenshot) throws Exception {
131+
int width = screenshot.getWidth();
132+
int height = screenshot.getHeight();
133+
int[] pixels = callOnEdt(screenshot::getRGB);
134+
return hasRenderableContent(pixels, width, height);
135+
}
136+
137+
private static boolean hasRenderableContent(int[] pixels, int width, int height) {
138+
if (width <= 0 || height <= 0 || pixels == null || pixels.length == 0) {
110139
return false;
111140
}
112141
int background = pixels[0];
@@ -126,17 +155,32 @@ public class @MAIN_NAME@UiTest {
126155
return false;
127156
}
128157

129-
private static File saveScreenshot(Bitmap screenshot) throws IOException {
158+
private static File saveBitmap(Bitmap screenshot, String fileName) throws IOException {
130159
File outputDir = resolveArtifactDirectory();
131-
File screenshotFile = new File(outputDir, "@[email protected]");
160+
File screenshotFile = new File(outputDir, fileName);
132161
try (FileOutputStream out = new FileOutputStream(screenshotFile)) {
133162
if (!screenshot.compress(Bitmap.CompressFormat.PNG, 100, out)) {
134-
throw new IOException("Failed to encode screenshot as PNG");
163+
throw new IOException("Failed to encode Android screenshot as PNG");
135164
}
136165
}
137166
return screenshotFile;
138167
}
139168

169+
private static File saveCodenameOneScreenshot(Image screenshot, String fileName) throws Exception {
170+
File outputDir = resolveArtifactDirectory();
171+
File screenshotFile = new File(outputDir, fileName);
172+
ImageIO io = callOnEdt(() -> Display.getInstance().getImageIO());
173+
assertNotNull("Codename One ImageIO should be available", io);
174+
try (FileOutputStream out = new FileOutputStream(screenshotFile)) {
175+
FileOutputStream stream = out;
176+
callOnEdt(() -> {
177+
io.save(screenshot, stream, ImageIO.FORMAT_PNG, 1.0f);
178+
return null;
179+
});
180+
}
181+
return screenshotFile;
182+
}
183+
140184
private static File resolveArtifactDirectory() throws IOException {
141185
String directory = System.getenv("CN1_TEST_SCREENSHOT_DIR");
142186
File outputDir = (directory != null && !directory.isEmpty())
@@ -162,4 +206,22 @@ public class @MAIN_NAME@UiTest {
162206
}
163207
throw new AssertionError("Timed out waiting for Codename One main form to be displayed");
164208
}
209+
210+
private static Image captureCodenameOneScreenshot() throws Exception {
211+
return callOnEdt(() -> Display.getInstance().captureScreen());
212+
}
213+
214+
private static <T> T callOnEdt(Callable<T> callable) throws Exception {
215+
FutureTask<T> task = new FutureTask<>(callable);
216+
Display.getInstance().callSerially(task);
217+
long deadline = SystemClock.uptimeMillis() + EDT_TIMEOUT_MS;
218+
while (!task.isDone() && SystemClock.uptimeMillis() < deadline) {
219+
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
220+
SystemClock.sleep(4L);
221+
}
222+
if (!task.isDone()) {
223+
throw new AssertionError("Timed out waiting for Codename One EDT task to finish");
224+
}
225+
return task.get();
226+
}
165227
}

0 commit comments

Comments
 (0)