otaSkins, final String currentSkin,
+ final boolean desktopSkinActive) throws MalformedURLException {
+ legacyMenu.removeAll();
+
+ for (String entry : otaSkins) {
+ JRadioButtonMenuItem item = buildSkinRadioItem(frm, entry, currentSkin, desktopSkinActive);
+ skinGroup.add(item);
+ legacyMenu.add(item);
+ }
+ if (!otaSkins.isEmpty()) {
+ legacyMenu.addSeparator();
+ }
+
JMenuItem more = new JMenuItem("More...");
- skinMenu.add(more);
+ legacyMenu.add(more);
more.addActionListener(new ActionListener() {
@Override
@@ -5550,7 +5657,12 @@ public void run() {
downloadMessage.setVisible(false);
d.setVisible(false);
try {
- populateLegacySkinsMenu(frm, skinMenu);
+ // Rebuild the whole Skins menu - newly
+ // downloaded entries land in the Legacy
+ // submenu but the shared ButtonGroup
+ // spans both, so a partial rebuild
+ // would leak the previous group.
+ createSkinsMenu(frm, topLevelMenu);
} catch (MalformedURLException ex) {
Logger.getLogger(JavaSEPort.class.getName()).log(Level.SEVERE, null, ex);
}
@@ -5577,15 +5689,15 @@ public void run() {
}
});
- skinMenu.addSeparator();
+ legacyMenu.addSeparator();
JMenuItem reset = new JMenuItem("Reset Skins");
- skinMenu.add(reset);
+ legacyMenu.add(reset);
reset.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
if(JOptionPane.showConfirmDialog(frm,
- "Are you sure you want to reset skins to default?",
- "Clean Storage",
+ "Are you sure you want to reset skins to default?",
+ "Clean Storage",
JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION){
Preferences pref = Preferences.userNodeForPackage(JavaSEPort.class);
pref.put("skins", DEFAULT_SKINS);
diff --git a/scripts/javase/lib/SimulatorModeTestApp.java b/scripts/javase/lib/SimulatorModeTestApp.java
index 9a362540f4..5a691bc7b3 100644
--- a/scripts/javase/lib/SimulatorModeTestApp.java
+++ b/scripts/javase/lib/SimulatorModeTestApp.java
@@ -1,16 +1,27 @@
package com.codenameone.examples.javase.tests;
+import com.codename1.impl.javase.JavaSEPort;
import com.codename1.ui.Button;
import com.codename1.ui.CN;
+import com.codename1.ui.CheckBox;
import com.codename1.ui.Dialog;
+import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.Label;
+import com.codename1.ui.TextField;
+import com.codename1.ui.Toolbar;
import com.codename1.io.ConnectionRequest;
import com.codename1.io.NetworkManager;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.Resources;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
/**
* Small simulator app used by JavaSE integration tests.
@@ -19,11 +30,26 @@ public class SimulatorModeTestApp {
private Form current;
public void init(Object context) {
+ Toolbar.setGlobalToolbar(true);
+ boolean appThemeInstalled = false;
try {
Resources theme = Resources.openLayered("/theme");
UIManager.getInstance().setThemeProps(theme.getTheme(theme.getThemeResourceNames()[0]));
+ appThemeInstalled = true;
} catch (Exception ignored) {
- // Fallback to default theme if test resource isn't available.
+ // No app theme bundled - fall through to native-theme-only
+ // path below so the screenshot still reflects the user's
+ // Native Theme menu pick.
+ }
+ if (!appThemeInstalled && Display.getInstance().hasNativeTheme()) {
+ // JavaSEPort.loadSkinFile has already cached the
+ // simulatorNativeTheme pick as nativeThemeRes, but nothing
+ // has pushed it into UIManager. Installing it directly
+ // lights up the iOS Modern / Android Material chrome in
+ // the captured screenshot; without this the form renders
+ // with the bare DefaultLookAndFeel and the screenshots
+ // for every theme look identical.
+ Display.getInstance().installNativeTheme();
}
}
@@ -34,14 +60,50 @@ public void start() {
}
String mode = System.getProperty("cn1.test.window.mode", "unknown");
Form form = new Form("JavaSE Simulator Test", new BorderLayout());
+ // The Toolbar carries most of the visual identity of a native
+ // theme (title bar color, font, status-bar treatment), so we
+ // need the global Toolbar in place for the screenshot to look
+ // different across themes.
+ Toolbar tb = form.getToolbar();
+ tb.setTitle("JavaSE Simulator Test");
+ tb.addMaterialCommandToSideMenu("Settings", com.codename1.ui.FontImage.MATERIAL_SETTINGS,
+ evt -> { /* placeholder */ });
+
form.add(BorderLayout.NORTH, new Label("Window mode: " + mode));
com.codename1.ui.Container body = new com.codename1.ui.Container(BoxLayout.y());
body.add(new Label("Robot validation baseline"));
- body.add(new Button("Primary Action"));
+ Button primary = new Button("Primary Action");
+ primary.setUIID("RaisedButton");
+ body.add(primary);
Button dialogButton = new Button("Open Dialog");
dialogButton.addActionListener(evt -> Dialog.show("Mode", "Current mode: " + mode, "OK", null));
body.add(dialogButton);
+ // Add a few more theme-bearing components so the screenshot
+ // captures more than just two flat buttons. CheckBox + TextField
+ // pick up theme-specific colors and metrics that diverge
+ // visibly between iOS Modern, Android Material and the legacy
+ // pair.
+ body.add(new CheckBox("Enable feature"));
+ TextField tf = new TextField("", "Text field hint", 20, TextField.ANY);
+ body.add(tf);
+
+ // Native-theme verification: when the harness sets
+ // cn1.test.expectedNativeTheme, query JavaSEPort for the
+ // resource it actually loaded and emit a result line to both
+ // stdout and an optional sentinel file. The outer verifier reads
+ // either channel to assert the menu's simulatorNativeTheme
+ // preference was honored on simulator startup. We surface the
+ // result in the UI too so the screenshot of a failing case is
+ // self-explanatory.
+ String expectedNativeTheme = System.getProperty("cn1.test.expectedNativeTheme");
+ if (expectedNativeTheme != null && !expectedNativeTheme.isEmpty()) {
+ String diagnostic = reportNativeThemeResult(expectedNativeTheme);
+ Label diagLabel = new Label(diagnostic);
+ diagLabel.setUIID("Label");
+ body.add(diagLabel);
+ }
+
form.add(BorderLayout.CENTER, body);
current = form;
@@ -76,6 +138,78 @@ public void start() {
});
}
+ /**
+ * Reports whether the active native theme matches {@code expected}.
+ *
+ * The current Simulator path stores the user's "Native Theme" menu
+ * choice in the {@code simulatorNativeTheme} preference, then reloads
+ * the simulator. On reload, {@code JavaSEPort.loadSkinFile} reads the
+ * preference, loads {@code /<name>.res} from the classpath, and
+ * caches the resolved key. We check three things:
+ *
+ *
+ * - {@code JavaSEPort.getCurrentSimulatorNativeTheme()} matches
+ * the expected key - this is what the simulator's loadSkinFile
+ * captured from the preference / build hints. A mismatch here
+ * means the preference wasn't honored or auto-resolution
+ * picked something else.
+ * - {@code JavaSEPort.getNativeTheme()} returns non-null - the
+ * cached {@code Resources} is what {@code installNativeTheme}
+ * actually layers under the app theme. Null here means the
+ * .res lookup failed even though the override was set, which
+ * is what you'd hit if the modern themes weren't bundled.
+ * - The expected {@code .res} is still present in the classpath
+ * so the test is exercising a real load path.
+ *
+ *
+ * The string returned is what we render on the form for the
+ * screenshot.
+ */
+ private String reportNativeThemeResult(String expected) {
+ String resolvedKey = null;
+ boolean nativeResLoaded = false;
+ boolean expectedResPresent = false;
+ try {
+ resolvedKey = JavaSEPort.getCurrentSimulatorNativeTheme();
+ Resources nativeRes = JavaSEPort.getNativeTheme();
+ nativeResLoaded = nativeRes != null;
+ InputStream is = JavaSEPort.class.getResourceAsStream("/" + expected + ".res");
+ if (is != null) {
+ expectedResPresent = true;
+ try { is.close(); } catch (Exception ignored) { }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ boolean pass = expected.equals(resolvedKey) && nativeResLoaded && expectedResPresent;
+ String result = pass ? "PASS" : "FAIL";
+ String line = "[native-theme-test] result=" + result
+ + " expected=" + expected
+ + " resolvedKey=" + resolvedKey
+ + " nativeResLoaded=" + nativeResLoaded
+ + " expectedResPresent=" + expectedResPresent;
+ System.out.println(line);
+ // Optional sentinel file so harnesses that can't tail stdout in
+ // realtime can still read the result. The path is configured by
+ // the verifier; absence is fine.
+ String sentinel = System.getProperty("cn1.test.nativeThemeResultFile");
+ if (sentinel != null && !sentinel.isEmpty()) {
+ try {
+ Path p = Paths.get(sentinel);
+ Path parent = p.getParent();
+ if (parent != null) {
+ Files.createDirectories(parent);
+ }
+ Files.write(p, (line + System.lineSeparator()).getBytes(StandardCharsets.UTF_8),
+ StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ return "Native theme " + result + ": expected=" + expected
+ + " loaded=" + (resolvedKey != null ? resolvedKey : "(none)");
+ }
+
public void stop() {
current = CN.getCurrentForm();
}
diff --git a/scripts/javase/lib/SimulatorWindowModeVerifier.java b/scripts/javase/lib/SimulatorWindowModeVerifier.java
index f4b1cbd4de..5b3612dcba 100644
--- a/scripts/javase/lib/SimulatorWindowModeVerifier.java
+++ b/scripts/javase/lib/SimulatorWindowModeVerifier.java
@@ -33,6 +33,14 @@ public static void main(String[] args) {
Path projectDir = prepareCodenameOneSettings();
Path prefsRoot = configureSimulatorPreferences(parsed, projectDir);
+ // Native-theme scenarios write the result line to this
+ // sentinel so the verifier can read it after capturing the
+ // screenshot. Path lives in the temp project so different
+ // scenario runs don't trample each other.
+ Path nativeThemeSentinel = parsed.nativeTheme != null
+ ? projectDir.resolve("native-theme-result.txt")
+ : null;
+
List cmd = new ArrayList();
String javaExec = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
cmd.add(javaExec);
@@ -55,6 +63,10 @@ public static void main(String[] args) {
cmd.add("-Dcn1.simulator.autoTestRecorder=true");
cmd.add("-Dcn1.simulator.autoTestRecorderRecord=true");
}
+ if (parsed.nativeTheme != null) {
+ cmd.add("-Dcn1.test.expectedNativeTheme=" + parsed.nativeTheme);
+ cmd.add("-Dcn1.test.nativeThemeResultFile=" + nativeThemeSentinel.toAbsolutePath());
+ }
if (parsed.skinPath != null && parsed.skinPath.length() > 0) {
cmd.add("-Dskin=" + parsed.skinPath);
cmd.add("-Ddskin=" + parsed.skinPath);
@@ -67,7 +79,17 @@ public static void main(String[] args) {
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.directory(projectDir.toFile());
pb.redirectErrorStream(true);
- pb.inheritIO();
+ if (parsed.nativeTheme != null) {
+ // Capture output to a log so we can also confirm the
+ // result line on stdout if the sentinel file isn't
+ // written for some reason. Without the redirect the
+ // inherited stdout would be swallowed by the JVM and
+ // unavailable to the assertion below.
+ Path logPath = projectDir.resolve("simulator-output.log");
+ pb.redirectOutput(logPath.toFile());
+ } else {
+ pb.inheritIO();
+ }
child = pb.start();
waitForSimulatorWarmup(Duration.ofSeconds("network-monitor".equals(parsed.scenario) ? 12 : 8));
@@ -80,7 +102,13 @@ public static void main(String[] args) {
if (!ImageIO.write(image, "png", screenshotPath.toFile())) {
throw new AssertionError("No PNG writer available; screenshot was not written");
}
- System.out.println("[javase-verifier] screenshot=" + screenshotPath + " mode=" + parsed.mode + " scenario=" + parsed.scenario);
+ System.out.println("[javase-verifier] screenshot=" + screenshotPath
+ + " mode=" + parsed.mode + " scenario=" + parsed.scenario
+ + (parsed.nativeTheme != null ? " nativeTheme=" + parsed.nativeTheme : ""));
+
+ if (parsed.nativeTheme != null) {
+ assertNativeThemeApplied(parsed, nativeThemeSentinel, projectDir);
+ }
exitCode = 0;
} catch (Throwable t) {
t.printStackTrace(System.err);
@@ -129,6 +157,40 @@ private static void validateScreenshotContent(BufferedImage image) {
}
}
+ /**
+ * Reads the result line written by {@code SimulatorModeTestApp}
+ * during simulator startup and verifies it reports a PASS. The
+ * sentinel file is preferred since it lands the line atomically;
+ * we fall back to the captured stdout log if the sentinel is
+ * missing (e.g. the app's init threw before the report ran).
+ */
+ private static void assertNativeThemeApplied(Args args, Path sentinel, Path projectDir) throws Exception {
+ String line = null;
+ if (sentinel != null && Files.exists(sentinel)) {
+ line = new String(Files.readAllBytes(sentinel), StandardCharsets.UTF_8).trim();
+ }
+ if (line == null || line.isEmpty()) {
+ Path log = projectDir.resolve("simulator-output.log");
+ if (Files.exists(log)) {
+ for (String l : Files.readAllLines(log, StandardCharsets.UTF_8)) {
+ if (l.startsWith("[native-theme-test]")) {
+ line = l.trim();
+ break;
+ }
+ }
+ }
+ }
+ if (line == null || line.isEmpty()) {
+ throw new AssertionError("Native theme test produced no result line for "
+ + args.nativeTheme + " (sentinel=" + sentinel + ")");
+ }
+ System.out.println("[javase-verifier] native-theme assertion: " + line);
+ if (!line.contains("result=PASS")) {
+ throw new AssertionError("Native theme " + args.nativeTheme
+ + " was not loaded by the simulator: " + line);
+ }
+ }
+
private static Path prepareCodenameOneSettings() throws Exception {
Path tempProject = Files.createTempDirectory("cn1-javase-sim-project");
Path settings = tempProject.resolve("codenameone_settings.properties");
@@ -147,6 +209,14 @@ private static Path configureSimulatorPreferences(Args args, Path projectDir) th
System.setProperty("java.util.prefs.userRoot", prefsRoot.toAbsolutePath().toString());
Preferences prefs = Preferences.userNodeForPackage(com.codename1.impl.javase.JavaSEPort.class);
prefs.putBoolean("Portrait", !"landscape".equals(args.scenario));
+ if (args.nativeTheme != null) {
+ // Mirrors exactly what the "Native Theme" menu writes when
+ // the user picks an explicit theme - this is the lever the
+ // simulator menu acts on, so testing the lever directly
+ // covers the menu's reload path without driving the menu
+ // via AWT events.
+ prefs.put("simulatorNativeTheme", args.nativeTheme);
+ }
prefs.flush();
return prefsRoot;
}
@@ -157,13 +227,16 @@ private static final class Args {
final String simClasspath;
final String skinPath;
final String scenario;
+ final String nativeTheme;
- private Args(String mode, String screenshotPath, String simClasspath, String skinPath, String scenario) {
+ private Args(String mode, String screenshotPath, String simClasspath, String skinPath, String scenario,
+ String nativeTheme) {
this.mode = mode;
this.screenshotPath = screenshotPath;
this.simClasspath = simClasspath;
this.skinPath = skinPath;
this.scenario = scenario;
+ this.nativeTheme = nativeTheme;
}
static Args parse(String[] args) {
@@ -172,6 +245,7 @@ static Args parse(String[] args) {
String simClasspath = null;
String skinPath = null;
String scenario = "default";
+ String nativeTheme = null;
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if ("--mode".equals(arg) && i + 1 < args.length) {
@@ -184,6 +258,8 @@ static Args parse(String[] args) {
skinPath = args[++i];
} else if ("--scenario".equals(arg) && i + 1 < args.length) {
scenario = args[++i];
+ } else if ("--native-theme".equals(arg) && i + 1 < args.length) {
+ nativeTheme = args[++i];
}
}
if (!"single".equals(mode) && !"multi".equals(mode)) {
@@ -195,7 +271,7 @@ static Args parse(String[] args) {
if (simClasspath == null || simClasspath.trim().isEmpty()) {
throw new IllegalArgumentException("--sim-classpath is required");
}
- return new Args(mode, screenshot, simClasspath, skinPath, scenario);
+ return new Args(mode, screenshot, simClasspath, skinPath, scenario, nativeTheme);
}
}
}
diff --git a/scripts/javase/screenshots/README.md b/scripts/javase/screenshots/README.md
index fb2210d2df..df3b4239ad 100644
--- a/scripts/javase/screenshots/README.md
+++ b/scripts/javase/screenshots/README.md
@@ -9,6 +9,20 @@ This directory stores baseline PNG files for JavaSE simulator integration screen
- `javase-single-component-inspector.png`
- `javase-single-network-monitor.png`
- `javase-single-test-recorder.png`
+- `javase-single-native-theme-ios-modern.png`
+- `javase-single-native-theme-ios7.png`
+- `javase-single-native-theme-android-material.png`
+- `javase-single-native-theme-android-holo.png`
The CI workflow compares generated simulator screenshots with these files.
If screenshots differ (or are missing), CI uploads artifacts and posts a PR comment with visual previews.
+
+The `javase-single-native-theme-*` baselines exercise the Simulator's
+"Native Theme" menu - each one runs with the corresponding
+`simulatorNativeTheme` preference set, the same preference the menu
+writes when the user picks a theme. The captured chrome should
+visibly differ between them: iOS Modern shows rounded blue buttons
+and the modern iOS title bar, iOS 7 shows the flat 2014-era style,
+Android Material shows raised purple buttons + Material chrome, and
+Android Holo Light shows the all-caps Holo button labels in light
+blue.
diff --git a/scripts/javase/screenshots/javase-multi-landscape.png b/scripts/javase/screenshots/javase-multi-landscape.png
index f9a9ae6b26..0bd93c04da 100644
Binary files a/scripts/javase/screenshots/javase-multi-landscape.png and b/scripts/javase/screenshots/javase-multi-landscape.png differ
diff --git a/scripts/javase/screenshots/javase-multi-window.png b/scripts/javase/screenshots/javase-multi-window.png
index b8c0597cd1..554f1c5ab5 100644
Binary files a/scripts/javase/screenshots/javase-multi-window.png and b/scripts/javase/screenshots/javase-multi-window.png differ
diff --git a/scripts/javase/screenshots/javase-single-component-inspector.png b/scripts/javase/screenshots/javase-single-component-inspector.png
index 1058f86764..d3fdd7f66e 100644
Binary files a/scripts/javase/screenshots/javase-single-component-inspector.png and b/scripts/javase/screenshots/javase-single-component-inspector.png differ
diff --git a/scripts/javase/screenshots/javase-single-landscape.png b/scripts/javase/screenshots/javase-single-landscape.png
index 4ac64099a0..62861b956a 100644
Binary files a/scripts/javase/screenshots/javase-single-landscape.png and b/scripts/javase/screenshots/javase-single-landscape.png differ
diff --git a/scripts/javase/screenshots/javase-single-native-theme-android-holo.png b/scripts/javase/screenshots/javase-single-native-theme-android-holo.png
new file mode 100644
index 0000000000..0379da5cc3
Binary files /dev/null and b/scripts/javase/screenshots/javase-single-native-theme-android-holo.png differ
diff --git a/scripts/javase/screenshots/javase-single-native-theme-android-material.png b/scripts/javase/screenshots/javase-single-native-theme-android-material.png
new file mode 100644
index 0000000000..c9d6e99b7e
Binary files /dev/null and b/scripts/javase/screenshots/javase-single-native-theme-android-material.png differ
diff --git a/scripts/javase/screenshots/javase-single-native-theme-ios-modern.png b/scripts/javase/screenshots/javase-single-native-theme-ios-modern.png
new file mode 100644
index 0000000000..f990150efe
Binary files /dev/null and b/scripts/javase/screenshots/javase-single-native-theme-ios-modern.png differ
diff --git a/scripts/javase/screenshots/javase-single-native-theme-ios7.png b/scripts/javase/screenshots/javase-single-native-theme-ios7.png
new file mode 100644
index 0000000000..85572c6424
Binary files /dev/null and b/scripts/javase/screenshots/javase-single-native-theme-ios7.png differ
diff --git a/scripts/javase/screenshots/javase-single-network-monitor.png b/scripts/javase/screenshots/javase-single-network-monitor.png
index 2bbfb4dd74..fc0d0f8180 100644
Binary files a/scripts/javase/screenshots/javase-single-network-monitor.png and b/scripts/javase/screenshots/javase-single-network-monitor.png differ
diff --git a/scripts/javase/screenshots/javase-single-test-recorder.png b/scripts/javase/screenshots/javase-single-test-recorder.png
index 1b5e5a1321..17e44fd9ad 100644
Binary files a/scripts/javase/screenshots/javase-single-test-recorder.png and b/scripts/javase/screenshots/javase-single-test-recorder.png differ
diff --git a/scripts/javase/screenshots/javase-single-window.png b/scripts/javase/screenshots/javase-single-window.png
index 365bff3168..25253c8ae8 100644
Binary files a/scripts/javase/screenshots/javase-single-window.png and b/scripts/javase/screenshots/javase-single-window.png differ
diff --git a/scripts/run-javase-simulator-integration-tests.sh b/scripts/run-javase-simulator-integration-tests.sh
index cb6e7220c7..f7376642c4 100755
--- a/scripts/run-javase-simulator-integration-tests.sh
+++ b/scripts/run-javase-simulator-integration-tests.sh
@@ -126,6 +126,15 @@ js_log "Ensuring CLDC11 port is built (required bootclasspath for CodenameOne co
js_log "Using Java 8 for ant build: $JAVA8_HOME_DETECTED"
JAVA_HOME="$JAVA8_HOME_DETECTED" PATH="$JAVA8_HOME_DETECTED/bin:$PATH" ant -noinput -buildfile Ports/CLDC11/build.xml jar
+# Compile the CSS-driven native themes (iOSModernTheme.res,
+# AndroidMaterialTheme.res). The JavaSE ant build copies them out of
+# Themes/, with failonerror=false, so a missing pair would silently
+# produce a JavaSE.jar without the modern themes - which is precisely
+# the failure mode the native-theme verification below catches. Run
+# the compiler ourselves so a fresh checkout doesn't surprise CI.
+js_log "Building CSS-driven native themes"
+"$REPO_ROOT/scripts/build-native-themes.sh"
+
js_log "Ensuring JavaSE port is built"
js_log "Using Java 8 for ant build: $JAVA8_HOME_DETECTED"
JAVA_HOME="$JAVA8_HOME_DETECTED" PATH="$JAVA8_HOME_DETECTED/bin:$PATH" ant -noinput -buildfile Ports/JavaSE/build.xml jar
@@ -177,6 +186,52 @@ for mode in "${MODES[@]}"; do
done
done
+# Native-theme override regression coverage. The Simulator's "Native
+# Theme" menu writes simulatorNativeTheme and triggers a simulator
+# reload; the bundled themes (iOSModernTheme, AndroidMaterialTheme,
+# the iOS7 / Android Holo legacy themes) must all resolve through
+# JavaSEPort.loadSkinFile's override branch. We exercise each one
+# end-to-end: set the preference the menu would set, restart the
+# simulator the same way the menu does, and assert the running app
+# sees a Resources object whose first theme name matches what we'd
+# get from opening the bundled .res directly. This catches both
+# "menu wrote the wrong preference" and "preference wasn't honored
+# on reload" regressions.
+NATIVE_THEME_SCENARIOS=(
+ "iOSModernTheme"
+ "iOS7Theme"
+ "AndroidMaterialTheme"
+ "android_holo_light"
+)
+for theme_key in "${NATIVE_THEME_SCENARIOS[@]}"; do
+ case "$theme_key" in
+ iOSModernTheme) scenario_label=ios-modern ;;
+ iOS7Theme) scenario_label=ios7 ;;
+ AndroidMaterialTheme) scenario_label=android-material ;;
+ android_holo_light) scenario_label=android-holo ;;
+ *) scenario_label="$theme_key" ;;
+ esac
+ test_name="javase-single-native-theme-${scenario_label}"
+ png="$RAW_DIR/${test_name}.png"
+ js_log "Running simulator native-theme verification for ${theme_key}"
+ FULL_SIM_CLASSPATH="$CN1_CLASSPATH:$CLASS_DIR"
+ xvfb-run -a -s "-screen 0 2200x1400x24" "$JAVA_BIN" -Djava.awt.headless=false \
+ -cp "$FULL_SIM_CLASSPATH" \
+ com.codenameone.examples.javase.tests.SimulatorWindowModeVerifier \
+ --mode single \
+ --scenario "native-theme-${scenario_label}" \
+ --sim-classpath "$FULL_SIM_CLASSPATH" \
+ --skin "$SIM_SKIN_PATH" \
+ --screenshot "$png" \
+ --native-theme "$theme_key"
+
+ if [ ! -s "$png" ]; then
+ js_log "Expected screenshot was not produced for native-theme ${theme_key}" >&2
+ exit 11
+ fi
+ ACTUAL_ENTRIES+=("${test_name}=$png")
+done
+
COMPARE_JSON="$ARTIFACTS_DIR/screenshot-compare.json"
SUMMARY_FILE="$ARTIFACTS_DIR/screenshot-summary.txt"
COMMENT_FILE="$ARTIFACTS_DIR/screenshot-comment.md"