Skip to content

Commit cee7d12

Browse files
committed
Switched to androidx testing
1 parent 4151c2a commit cee7d12

File tree

2 files changed

+264
-89
lines changed

2 files changed

+264
-89
lines changed

scripts/build-android-app.sh

Lines changed: 152 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ xmlstarlet sel -N "$NS" -t -c "/mvn:project/mvn:build/mvn:plugins" -n "$ROOT_POM
201201
[ -f "$APP_DIR/build.sh" ] && chmod +x "$APP_DIR/build.sh"
202202

203203
SETTINGS_FILE="$APP_DIR/common/codenameone_settings.properties"
204+
echo "codename1.arg.android.useAndroidX=true" >> "$SETTINGS_FILE"
204205
[ -f "$SETTINGS_FILE" ] || { ba_log "codenameone_settings.properties not found at $SETTINGS_FILE" >&2; exit 1; }
205206

206207
# --- Read settings ---
@@ -266,112 +267,175 @@ if [ -z "$GRADLE_PROJECT_DIR" ]; then
266267
fi
267268

268269
ba_log "Configuring instrumentation test sources in $GRADLE_PROJECT_DIR"
270+
271+
# Ensure AndroidX flags in gradle.properties
272+
# --- BEGIN: robust Gradle patch for AndroidX tests ---
273+
GRADLE_PROPS="$GRADLE_PROJECT_DIR/gradle.properties"
274+
grep -q '^android.useAndroidX=' "$GRADLE_PROPS" 2>/dev/null || echo 'android.useAndroidX=true' >> "$GRADLE_PROPS"
275+
grep -q '^android.enableJetifier=' "$GRADLE_PROPS" 2>/dev/null || echo 'android.enableJetifier=true' >> "$GRADLE_PROPS"
276+
269277
APP_BUILD_GRADLE="$GRADLE_PROJECT_DIR/app/build.gradle"
270-
if [ -f "$APP_BUILD_GRADLE" ]; then
271-
python3 - "$APP_BUILD_GRADLE" <<'PYTHON'
272-
import pathlib
273-
import re
274-
import sys
275-
276-
path = pathlib.Path(sys.argv[1])
277-
text = path.read_text()
278-
modified = False
279-
280-
if "android.test.InstrumentationTestRunner" not in text:
281-
def add_runner(match):
282-
prefix = match.group(0)
283-
return prefix + "\n testInstrumentationRunner \"android.test.InstrumentationTestRunner\""
284-
285-
new_text, count = re.subn(r"(defaultConfig\s*\{)", add_runner, text, count=1, flags=re.MULTILINE)
286-
if count:
287-
text = new_text
288-
modified = True
289-
else:
290-
raise SystemExit("defaultConfig block not found while adding instrumentation runner")
278+
ROOT_BUILD_GRADLE="$GRADLE_PROJECT_DIR/build.gradle"
291279

292-
libraries = [
293-
"useLibrary 'android.test.base'",
294-
"useLibrary 'android.test.mock'",
295-
"useLibrary 'android.test.runner'",
296-
]
280+
# Ensure repos in both root and app
281+
for F in "$ROOT_BUILD_GRADLE" "$APP_BUILD_GRADLE"; do
282+
if [ -f "$F" ]; then
283+
if ! grep -qE '^\s*repositories\s*{' "$F"; then
284+
cat >> "$F" <<'EOS'
297285
298-
missing_libraries = [lib for lib in libraries if lib not in text]
299-
if missing_libraries:
300-
match = re.search(r"^(\s*android\s*\{)", text, flags=re.MULTILINE)
301-
if not match:
302-
raise SystemExit("android block not found while adding instrumentation libraries")
303-
line = match.group(1)
304-
indent = re.match(r"^(\s*)", line).group(1)
305-
insertion = "".join(f"\n{indent} {lib}" for lib in missing_libraries)
306-
text = text[: match.end()] + insertion + text[match.end():]
307-
modified = True
308-
309-
if modified:
310-
if not text.endswith("\n"):
311-
text += "\n"
312-
path.write_text(text)
313-
PYTHON
314-
ba_log "Ensured instrumentation runner and libraries are declared"
315-
else
316-
ba_log "Warning: Gradle build file not found at $APP_BUILD_GRADLE; skipping instrumentation dependency configuration" >&2
317-
fi
286+
repositories {
287+
google()
288+
mavenCentral()
289+
}
290+
EOS
291+
else
292+
grep -q 'google()' "$F" || sed -E -i '0,/repositories[[:space:]]*\{/s//repositories {\n google()\n mavenCentral()/' "$F"
293+
grep -q 'mavenCentral()' "$F" || sed -E -i '0,/repositories[[:space:]]*\{/s//repositories {\n google()\n mavenCentral()/' "$F"
294+
fi
295+
fi
296+
done
297+
298+
# Edit app/build.gradle
299+
python3 - "$APP_BUILD_GRADLE" <<'PY'
300+
import sys, re, pathlib
301+
p = pathlib.Path(sys.argv[1]); txt = p.read_text(); orig = txt; changed = False
302+
303+
def strip_block(name, s):
304+
return re.sub(rf'(?ms)^\s*{name}\s*\{{.*?\}}\s*', '', s)
305+
306+
module_view = strip_block('buildscript', strip_block('pluginManagement', txt))
307+
308+
# 1) android { compileSdkVersion/targetSdkVersion }
309+
def ensure_sdk(body):
310+
# If android { ... } exists, update/insert inside defaultConfig and the android block
311+
if re.search(r'(?m)^\s*android\s*\{', body):
312+
# compileSdkVersion
313+
if re.search(r'(?m)^\s*compileSdkVersion\s+\d+', body) is None:
314+
body = re.sub(r'(?m)(^\s*android\s*\{)', r'\1\n compileSdkVersion 33', body, count=1)
315+
else:
316+
body = re.sub(r'(?m)^\s*compileSdkVersion\s+\d+', ' compileSdkVersion 33', body)
317+
# targetSdkVersion
318+
if re.search(r'(?ms)^\s*defaultConfig\s*\{.*?^\s*\}', body):
319+
dc = re.search(r'(?ms)^\s*defaultConfig\s*\{.*?^\s*\}', body)
320+
block = dc.group(0)
321+
if re.search(r'(?m)^\s*targetSdkVersion\s+\d+', block):
322+
block2 = re.sub(r'(?m)^\s*targetSdkVersion\s+\d+', ' targetSdkVersion 33', block)
323+
else:
324+
block2 = re.sub(r'(\{\s*)', r'\1\n targetSdkVersion 33', block, count=1)
325+
body = body[:dc.start()] + block2 + body[dc.end():]
326+
else:
327+
body = re.sub(r'(?m)(^\s*android\s*\{)', r'\1\n defaultConfig {\n targetSdkVersion 33\n }', body, count=1)
328+
else:
329+
# No android block at all: add minimal
330+
body += '\n\nandroid {\n compileSdkVersion 33\n defaultConfig { targetSdkVersion 33 }\n}\n'
331+
return body
332+
333+
txt2 = ensure_sdk(txt)
334+
if txt2 != txt: txt = txt2; module_view = strip_block('buildscript', strip_block('pluginManagement', txt)); changed = True
335+
336+
# 2) testInstrumentationRunner -> AndroidX
337+
if "androidx.test.runner.AndroidJUnitRunner" not in module_view:
338+
t2, n = re.subn(r'(?m)^\s*testInstrumentationRunner\s*".*?"\s*$', ' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"', txt)
339+
if n == 0:
340+
t2, n = re.subn(r'(?m)(^\s*defaultConfig\s*\{)', r'\1\n testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"', txt, count=1)
341+
if n == 0:
342+
t2, n = re.subn(r'(?ms)(^\s*android\s*\{)', r'\1\n defaultConfig {\n testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"\n }', txt, count=1)
343+
if n: txt = t2; module_view = strip_block('buildscript', strip_block('pluginManagement', txt)); changed = True
344+
345+
# 3) remove legacy useLibrary lines
346+
t2, n = re.subn(r'(?m)^\s*useLibrary\s+\'android\.test\.(base|mock|runner)\'\s*$', '', txt)
347+
if n: txt = t2; module_view = strip_block('buildscript', strip_block('pluginManagement', txt)); changed = True
348+
349+
# 4) deps: choose androidTestImplementation vs androidTestCompile
350+
uses_modern = re.search(r'(?m)^\s*(implementation|api|testImplementation|androidTestImplementation)\b', module_view) is not None
351+
conf = "androidTestImplementation" if uses_modern else "androidTestCompile"
352+
need = [
353+
("androidx.test.ext:junit:1.1.5", conf), # AndroidJUnit4
354+
("androidx.test:runner:1.5.2", conf),
355+
("androidx.test:core:1.5.0", conf),
356+
("androidx.test.services:storage:1.4.2", conf),
357+
]
358+
to_add = [(c, k) for (c, k) in need if c not in module_view]
359+
360+
if to_add:
361+
block = "\n\ndependencies {\n" + "".join([f" {k} \"{c}\"\n" for c, k in to_add]) + "}\n"
362+
txt = txt.rstrip() + block
363+
changed = True
364+
365+
if changed and txt != orig:
366+
if not txt.endswith("\n"): txt += "\n"
367+
p.write_text(txt)
368+
print(f"Patched app/build.gradle (SDK=33; deps via {conf})")
369+
else:
370+
print("No changes needed in app/build.gradle")
371+
PY
372+
# --- END: robust Gradle patch ---
373+
374+
echo "----- app/build.gradle tail -----"
375+
tail -n 80 "$APP_BUILD_GRADLE" | sed 's/^/| /'
376+
echo "---------------------------------"
318377

319378
TEST_SRC_DIR="$GRADLE_PROJECT_DIR/app/src/androidTest/java/${PACKAGE_PATH}"
320379
mkdir -p "$TEST_SRC_DIR"
321380
TEST_CLASS="$TEST_SRC_DIR/HelloCodenameOneInstrumentedTest.java"
322-
cat >"$TEST_CLASS" <<EOF
323-
package $PACKAGE_NAME;
324-
325-
import android.content.Context;
326-
import android.test.InstrumentationTestCase;
381+
cat >"$TEST_CLASS" <<'EOF'
382+
package @PACKAGE@;
327383
328384
import android.app.Instrumentation;
329-
import android.os.ParcelFileDescriptor;
385+
import android.graphics.Bitmap;
386+
import android.view.View;
387+
import android.graphics.Canvas;
388+
import android.os.Build;
330389
import android.util.Base64;
331390
332-
import java.io.FileInputStream;
333-
import java.io.IOException;
334-
import java.io.InputStream;
391+
import androidx.test.core.app.ApplicationProvider;
392+
import androidx.test.ext.junit.runners.AndroidJUnit4;
393+
import androidx.test.platform.app.InstrumentationRegistry;
335394
336-
public class HelloCodenameOneInstrumentedTest extends InstrumentationTestCase {
395+
import org.junit.Test;
396+
import org.junit.runner.RunWith;
337397
338-
public void testUseAppContext() {
339-
Context appContext = getInstrumentation().getTargetContext();
340-
assertEquals("$PACKAGE_NAME", appContext.getPackageName());
341-
}
398+
import java.io.ByteArrayOutputStream;
342399
343-
private static final int CHUNK = 200_000;
344-
345-
public static void printPngToStdout(Instrumentation inst) {
346-
try {
347-
ParcelFileDescriptor pfd = inst.getUiAutomation().executeShellCommand("screencap -p");
348-
byte[] png;
349-
try (InputStream in = new FileInputStream(pfd.getFileDescriptor())) {
350-
png = readAll(in);
351-
}
352-
String b64 = Base64.encodeToString(png, Base64.NO_WRAP);
353-
System.out.println("<<CN1_SCREENSHOT_BEGIN>>");
354-
for (int i = 0; i < b64.length(); i += CHUNK) {
355-
int end = Math.min(i + CHUNK, b64.length());
356-
System.out.println(b64.substring(i, end));
357-
}
358-
System.out.println("<<CN1_SCREENSHOT_END>>");
359-
System.out.flush();
360-
} catch (IOException err) {
361-
err.printStackTrace();
362-
throw new RuntimeException(err);
363-
}
364-
}
400+
import static org.junit.Assert.assertEquals;
401+
402+
@RunWith(AndroidJUnit4.class)
403+
public class HelloCodenameOneInstrumentedTest {
404+
405+
@Test
406+
public void testUseAppContext_andEmitScreenshot() throws Exception {
407+
Context ctx = ApplicationProvider.getApplicationContext();
408+
assertEquals("@PACKAGE@", ctx.getPackageName());
409+
410+
Instrumentation inst = InstrumentationRegistry.getInstrumentation();
411+
412+
Thread.sleep(2000);
413+
414+
// Run on UI thread in the AUT process
415+
inst.runOnMainSync(() -> {
416+
try {
417+
android.app.Activity activity =
418+
(android.app.Activity) ctx;
419+
420+
View root = activity.getWindow().getDecorView().getRootView();
421+
Bitmap bmp = Bitmap.createBitmap(
422+
root.getWidth(), root.getHeight(),
423+
Bitmap.Config.ARGB_8888);
424+
Canvas canvas = new Canvas(bmp);
425+
root.draw(canvas);
365426
366-
private static byte[] readAll(InputStream in) throws IOException {
367-
byte[] buf = new byte[64 * 1024];
368-
int n;
369-
java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
370-
while ((n = in.read(buf)) != -1) out.write(buf, 0, n);
371-
return out.toByteArray();
427+
ByteArrayOutputStream out = new ByteArrayOutputStream();
428+
bmp.compress(Bitmap.CompressFormat.PNG, 100, out);
429+
String b64 = Base64.encodeToString(out.toByteArray(), Base64.NO_WRAP);
430+
System.out.println("<<CN1_SCREENSHOT_BEGIN>>" + b64 + "<<CN1_SCREENSHOT_END>>");
431+
} catch (Throwable t) {
432+
System.out.println("<<CN1_SCREENSHOT_BEGIN>><<ERROR " + t + ">><<CN1_SCREENSHOT_END>>");
433+
}
434+
});
372435
}
373436
}
374437
EOF
438+
sed -i "s|@PACKAGE@|$PACKAGE_NAME|g" "$TEST_CLASS"
375439
ba_log "Created instrumentation test at $TEST_CLASS"
376440

377441
DEFAULT_ANDROID_TEST="$GRADLE_PROJECT_DIR/app/src/androidTest/java/com/example/myapplication2/ExampleInstrumentedTest.java"

0 commit comments

Comments
 (0)