Skip to content

Commit 9cd5bd8

Browse files
committed
Align Android UI test with Codename One stub activity
1 parent c78aa4e commit 9cd5bd8

File tree

2 files changed

+114
-48
lines changed

2 files changed

+114
-48
lines changed

scripts/build-android-app.sh

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -287,12 +287,95 @@ sed -e "s|@PACKAGE@|$PACKAGE_NAME|g" \
287287
"$UI_TEST_TEMPLATE" > "$UI_TEST_FILE"
288288
ba_log "Created instrumentation UI test at $UI_TEST_FILE"
289289

290+
STUB_SRC_DIR="$APP_MODULE_DIR/src/main/java/${PACKAGE_PATH}"
291+
mkdir -p "$STUB_SRC_DIR"
292+
STUB_SRC_FILE="$STUB_SRC_DIR/${MAIN_NAME}Stub.java"
293+
if [ ! -f "$STUB_SRC_FILE" ]; then
294+
cat >"$STUB_SRC_FILE" <<EOF
295+
package ${PACKAGE_NAME};
296+
297+
import android.os.Bundle;
298+
299+
import com.codename1.impl.android.CodenameOneActivity;
300+
301+
public class ${MAIN_NAME}Stub extends CodenameOneActivity {
302+
@Override
303+
protected void onCreate(Bundle savedInstanceState) {
304+
super.onCreate(savedInstanceState);
305+
}
306+
}
307+
EOF
308+
ba_log "Created Codename One stub activity at $STUB_SRC_FILE"
309+
else
310+
ba_log "Codename One stub activity already present at $STUB_SRC_FILE"
311+
fi
312+
313+
MANIFEST_FILE="$APP_MODULE_DIR/src/main/AndroidManifest.xml"
314+
if [ ! -f "$MANIFEST_FILE" ]; then
315+
ba_log "AndroidManifest.xml not found at $MANIFEST_FILE" >&2
316+
exit 1
317+
fi
318+
319+
ensure_manifest_stub() {
320+
python3 - "$MANIFEST_FILE" "$MAIN_NAME" <<'PY'
321+
import sys
322+
from pathlib import Path
323+
import xml.etree.ElementTree as ET
324+
325+
manifest_path = Path(sys.argv[1])
326+
main_name = sys.argv[2]
327+
ns_android = "http://schemas.android.com/apk/res/android"
328+
329+
ET.register_namespace('android', ns_android)
330+
tree = ET.parse(manifest_path)
331+
root = tree.getroot()
332+
app = root.find('application')
333+
if app is None:
334+
sys.exit("Application element missing in AndroidManifest.xml")
335+
336+
target = f".{main_name}Stub"
337+
android_name = f"{{{ns_android}}}name"
338+
android_exported = f"{{{ns_android}}}exported"
339+
340+
for activity in app.findall('activity'):
341+
name = activity.get(android_name)
342+
if name in (target, target.lstrip('.')):
343+
break
344+
else:
345+
activity = ET.Element('activity')
346+
activity.set(android_name, target)
347+
activity.set(android_exported, 'false')
348+
app.append(activity)
349+
tree.write(manifest_path, encoding='utf-8', xml_declaration=True)
350+
PY
351+
}
352+
353+
if ! ensure_manifest_stub; then
354+
ba_log "Failed to ensure stub activity declaration in $MANIFEST_FILE" >&2
355+
exit 1
356+
fi
357+
358+
if ! grep -Eq "android:name=\"(\.${MAIN_NAME}Stub|${PACKAGE_NAME//./\\.}.${MAIN_NAME}Stub)\"" "$MANIFEST_FILE"; then
359+
ba_log "Manifest does not declare ${MAIN_NAME}Stub activity" >&2
360+
exit 1
361+
fi
362+
363+
if [ ! -f "$STUB_SRC_FILE" ]; then
364+
ba_log "Missing stub activity source at $STUB_SRC_FILE" >&2
365+
exit 1
366+
fi
367+
290368
APP_BUILD_GRADLE="$APP_MODULE_DIR/build.gradle"
291369
if [ ! -f "$APP_BUILD_GRADLE" ]; then
292370
ba_log "Expected Gradle build file not found at $APP_BUILD_GRADLE" >&2
293371
exit 1
294372
fi
295373

374+
if ! grep -q "android[[:space:]]*{" "$APP_BUILD_GRADLE"; then
375+
ba_log "Gradle build file at $APP_BUILD_GRADLE is missing an android { } block" >&2
376+
exit 1
377+
fi
378+
296379
GRADLE_UPDATE_OUTPUT="$("$SCRIPT_DIR/update_android_ui_test_gradle.py" "$APP_BUILD_GRADLE")"
297380
if [ -n "$GRADLE_UPDATE_OUTPUT" ]; then
298381
while IFS= read -r line; do
@@ -843,6 +926,13 @@ if [ -z "$TEST_APK" ] || [ ! -f "$TEST_APK" ]; then
843926
exit 1
844927
fi
845928

929+
MERGED_MANIFEST="$APP_MODULE_DIR/build/intermediates/packaged_manifests/debug/AndroidManifest.xml"
930+
if [ -f "$MERGED_MANIFEST" ]; then
931+
grep -n "${MAIN_NAME}Stub" "$MERGED_MANIFEST" | sed 's/^/[build-android-app] merged-manifest: /' || true
932+
else
933+
ba_log "Merged manifest not found at $MERGED_MANIFEST"
934+
fi
935+
846936
adb_install_retry() {
847937
local serial="$1" apk="$2" tries=5
848938
local attempt
@@ -912,14 +1002,15 @@ LAUNCH_RESOLVE_OUTPUT="$("$ADB_BIN" -s "$EMULATOR_SERIAL" shell cmd package reso
9121002
if [ -n "$LAUNCH_RESOLVE_OUTPUT" ]; then
9131003
printf '%s\n' "$LAUNCH_RESOLVE_OUTPUT" | sed 's/^/[build-android-app] resolve-launch: /'
9141004
fi
915-
for candidate in CodenameOneActivity MainActivity HelloCodenameOneStub; do
916-
CANDIDATE_RESOLVE_OUTPUT="$(
917-
"$ADB_BIN" -s "$EMULATOR_SERIAL" shell cmd package resolve-activity --brief "$PACKAGE_NAME/.$candidate" 2>&1 || true
1005+
STUB_ACTIVITY_FQCN="$PACKAGE_NAME/.${MAIN_NAME}Stub"
1006+
STUB_RESOLVE_OUTPUT="$(
1007+
"$ADB_BIN" -s "$EMULATOR_SERIAL" shell cmd package resolve-activity --brief "$STUB_ACTIVITY_FQCN" 2>&1 || true
9181008
)"
919-
if [ -n "$CANDIDATE_RESOLVE_OUTPUT" ]; then
920-
printf '%s\n' "$CANDIDATE_RESOLVE_OUTPUT" | sed "s/^/[build-android-app] resolve-$candidate: /"
921-
fi
922-
done
1009+
if [ -n "$STUB_RESOLVE_OUTPUT" ]; then
1010+
printf '%s\n' "$STUB_RESOLVE_OUTPUT" | sed 's/^/[build-android-app] resolve-stub: /'
1011+
else
1012+
ba_log "Unable to resolve stub activity $STUB_ACTIVITY_FQCN on device"
1013+
fi
9231014

9241015
"$ADB_BIN" -s "$EMULATOR_SERIAL" shell am force-stop "$PACKAGE_NAME" >/dev/null 2>&1 || true
9251016
"$ADB_BIN" -s "$EMULATOR_SERIAL" shell am force-stop "${PACKAGE_NAME}.test" >/dev/null 2>&1 || true

scripts/templates/HelloCodenameOneUiTest.java.tmpl

Lines changed: 16 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,10 @@ import android.view.TextureView;
1515
import android.view.View;
1616
import android.view.ViewGroup;
1717
import android.view.Window;
18-
import android.content.ComponentName;
19-
import android.content.Context;
20-
import android.content.Intent;
21-
import android.content.pm.PackageManager;
2218

2319
import androidx.annotation.Nullable;
2420
import androidx.test.core.app.ActivityScenario;
21+
import androidx.test.ext.junit.rules.ActivityScenarioRule;
2522
import androidx.test.ext.junit.runners.AndroidJUnit4;
2623
import androidx.test.platform.app.InstrumentationRegistry;
2724

@@ -30,6 +27,7 @@ import com.codename1.ui.Form;
3027
import com.codename1.ui.Image;
3128
import com.codename1.ui.util.ImageIO;
3229

30+
import org.junit.Rule;
3331
import org.junit.Test;
3432
import org.junit.runner.RunWith;
3533

@@ -48,48 +46,25 @@ public class @MAIN_NAME@UiTest {
4846
private static final long RENDER_TIMEOUT_MS = 15_000L;
4947
private static final long EDT_TIMEOUT_MS = 10_000L;
5048

49+
@Rule
50+
public final ActivityScenarioRule<@MAIN_NAME@Stub> scenarioRule =
51+
new ActivityScenarioRule<>(@[email protected]);
52+
5153
@Test
5254
public void mainFormScreenshotContainsRenderedContent() throws Exception {
53-
try (ActivityScenario<? extends Activity> scenario = launchAppActivity()) {
54-
Form form = waitForMainForm();
55-
assertNotNull("Codename One main form should be available", form);
55+
ActivityScenario<@MAIN_NAME@Stub> scenario = scenarioRule.getScenario();
56+
assertNotNull("ActivityScenario should be initialized", scenario);
5657

57-
Bitmap androidScreenshot = waitForAndroidScreenshot(scenario);
58-
assertTrue("Android screenshot should show rendered content", hasRenderableContent(androidScreenshot));
59-
saveBitmap(androidScreenshot, "@[email protected]");
58+
Form form = waitForMainForm();
59+
assertNotNull("Codename One main form should be available", form);
6060

61-
Image codenameOneScreenshot = waitForCodenameOneScreenshot();
62-
assertTrue("Codename One screenshot should show rendered content", hasRenderableContent(codenameOneScreenshot));
63-
saveCodenameOneScreenshot(codenameOneScreenshot, "@[email protected]");
64-
}
65-
}
61+
Bitmap androidScreenshot = waitForAndroidScreenshot(scenario);
62+
assertTrue("Android screenshot should show rendered content", hasRenderableContent(androidScreenshot));
63+
saveBitmap(androidScreenshot, "@[email protected]");
6664

67-
private ActivityScenario<? extends Activity> launchAppActivity() {
68-
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
69-
PackageManager packageManager = context.getPackageManager();
70-
String packageName = context.getPackageName();
71-
72-
Intent launchIntent = packageManager.getLaunchIntentForPackage(packageName);
73-
if (launchIntent != null) {
74-
return ActivityScenario.launch(launchIntent);
75-
}
76-
77-
String[] fallbacks = new String[] {
78-
packageName + ".CodenameOneActivity",
79-
packageName + ".MainActivity",
80-
packageName + ".HelloCodenameOneStub"
81-
};
82-
for (String candidate : fallbacks) {
83-
ComponentName component = new ComponentName(packageName, candidate);
84-
try {
85-
packageManager.getActivityInfo(component, 0);
86-
Intent intent = new Intent().setComponent(component);
87-
return ActivityScenario.launch(intent);
88-
} catch (PackageManager.NameNotFoundException ignored) {
89-
// Try the next candidate.
90-
}
91-
}
92-
throw new AssertionError("No launchable activity found for package " + packageName);
65+
Image codenameOneScreenshot = waitForCodenameOneScreenshot();
66+
assertTrue("Codename One screenshot should show rendered content", hasRenderableContent(codenameOneScreenshot));
67+
saveCodenameOneScreenshot(codenameOneScreenshot, "@[email protected]");
9368
}
9469

9570
private Form waitForMainForm() throws Exception {

0 commit comments

Comments
 (0)