@@ -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
203203SETTINGS_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,155 @@ if [ -z "$GRADLE_PROJECT_DIR" ]; then
266267fi
267268
268269ba_log " Configuring instrumentation test sources in $GRADLE_PROJECT_DIR "
270+
271+ # Make sure the generated project opts into AndroidX/Jetifier
272+ GRADLE_PROPS=" $GRADLE_PROJECT_DIR /gradle.properties"
273+ mkdir -p " $( dirname " $GRADLE_PROPS " ) "
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+
269277APP_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
278+ ROOT_BUILD_GRADLE=" $GRADLE_PROJECT_DIR /build.gradle"
279+
280+ ensure_repos_block () {
281+ local file=" $1 "
282+ if [ -f " $file " ]; then
283+ if ! grep -q ' google()' " $file " ; then
284+ # add google() to the first repositories { } block, or create one at bottom
285+ if grep -q ' repositories[[:space:]]*{' " $file " ; then
286+ sed -E -i ' 0,/repositories[[:space:]]*\{/s//repositories {\n google()\n mavenCentral()/' " $file "
287+ else
288+ printf ' \nrepositories {\n google()\n mavenCentral()\n}\n' >> " $file "
289+ fi
290+ fi
291+ if ! grep -q ' mavenCentral()' " $file " ; then
292+ sed -E -i ' 0,/repositories[[:space:]]*\{/s//repositories {\n google()\n mavenCentral()/' " $file "
293+ fi
294+ fi
295+ }
275296
276- path = pathlib.Path(sys.argv[1 ])
277- text = path.read_text()
297+ # Ensure repos in both root and app build.gradle
298+ ensure_repos_block " $ROOT_BUILD_GRADLE "
299+ ensure_repos_block " $APP_BUILD_GRADLE "
300+
301+ if [ -f " $APP_BUILD_GRADLE " ]; then
302+ python3 - " $APP_BUILD_GRADLE " << 'PY '
303+ import re, sys, pathlib
304+ p = pathlib.Path(sys.argv[1])
305+ t = p.read_text()
306+ orig = t
278307modified = False
279308
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\" "
309+ # Runner -> AndroidX
310+ if "androidx.test.runner.AndroidJUnitRunner" not in t:
311+ t2, n = re.subn(r'testInstrumentationRunner\s*".*?"',
312+ 'testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"', t)
313+ if n == 0:
314+ t2, n = re.subn(r'(defaultConfig\s*\{)',
315+ r'\1\n testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"', t, count=1)
316+ t = t2
317+ modified = True
284318
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" )
291-
292- libraries = [
293- " useLibrary 'android.test.base'" ,
294- " useLibrary 'android.test.mock'" ,
295- " useLibrary 'android.test.runner'" ,
296- ]
297-
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():]
319+ # Remove legacy useLibrary lines
320+ t2, n = re.subn(r'^\s*useLibrary\s+\'android\.test\.(base|mock|runner)\'\s*\n?', '', t, flags=re.M)
321+ if n:
322+ t = t2
307323 modified = True
308324
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"
325+ # Decide config name: if file uses modern 'implementation/api', use androidTestImplementation; else androidTestCompile
326+ uses_modern = re.search(r'(?m)^\s*(implementation|api|testImplementation|androidTestImplementation)\b', t) is not None
327+ conf = "androidTestImplementation" if uses_modern else "androidTestCompile"
328+
329+ # Ensure dependencies block exists
330+ if not re.search(r'(?ms)^\s*dependencies\s*\{.*?\}', t):
331+ t += "\n\ndependencies {\n}\n"
332+
333+ # Inject deps if missing
334+ def add_dep(body, depcoord):
335+ if depcoord in body:
336+ return body
337+ return re.sub(r'(?ms)(^\s*dependencies\s*\{)', r'\1\n ' + conf + f' "{depcoord}"', body, count=1)
338+
339+ for dep in [
340+ "androidx.test:runner:1.5.2",
341+ "androidx.test:core:1.5.0",
342+ "androidx.test.services:storage:1.4.2",
343+ ]:
344+ t2 = add_dep(t, dep)
345+ if t2 != t:
346+ t = t2
347+ modified = True
348+
349+ if modified and t != orig:
350+ if not t.endswith("\n"): t += "\n"
351+ p.write_text(t)
352+ print(f"Patched build.gradle (deps via {conf})")
353+ else:
354+ print("No build.gradle changes needed")
355+ PY
356+ ba_log " Ensured AndroidX test runner/deps and repositories are declared (legacy-safe)"
315357else
316358 ba_log " Warning: Gradle build file not found at $APP_BUILD_GRADLE ; skipping instrumentation dependency configuration" >&2
317359fi
318360
319361TEST_SRC_DIR=" $GRADLE_PROJECT_DIR /app/src/androidTest/java/${PACKAGE_PATH} "
320362mkdir -p " $TEST_SRC_DIR "
321363TEST_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;
364+ cat > " $TEST_CLASS " << 'EOF '
365+ package @PACKAGE@;
327366
328367import android.app.Instrumentation;
329368import android.os.ParcelFileDescriptor;
330- import android.util.Base64;
331369
370+ import androidx.test.ext.junit.runners.AndroidJUnit4;
371+ import androidx.test.platform.app.InstrumentationRegistry;
372+ import androidx.test.services.storage.TestStorage;
373+ import androidx.test.core.app.ApplicationProvider;
374+
375+ import org.junit.Test;
376+ import org.junit.runner.RunWith;
377+
378+ import java.io.File;
332379import java.io.FileInputStream;
333- import java.io.IOException ;
380+ import java.io.FileOutputStream ;
334381import java.io.InputStream;
382+ import java.io.ByteArrayOutputStream;
335383
336- public class HelloCodenameOneInstrumentedTest extends InstrumentationTestCase {
384+ import static org.junit.Assert.assertEquals;
337385
338- public void testUseAppContext() {
339- Context appContext = getInstrumentation().getTargetContext();
340- assertEquals("$PACKAGE_NAME ", appContext.getPackageName());
341- }
386+ @RunWith(AndroidJUnit4.class)
387+ public class HelloCodenameOneInstrumentedTest {
342388
343- private static final int CHUNK = 200_000;
389+ @Test
390+ public void testUseAppContext_andSaveScreenshot() throws Exception {
391+ // Keep your assertion
392+ String expectedPkg = "@PACKAGE@";
393+ String actualPkg = ApplicationProvider.getApplicationContext().getPackageName();
394+ assertEquals(expectedPkg, actualPkg);
344395
345- public static void printPngToStdout(Instrumentation inst) {
346- try {
396+ // Capture a PNG from the emulator
397+ Instrumentation inst = InstrumentationRegistry.getInstrumentation();
347398 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));
399+ byte[] png = readAll(new FileInputStream(pfd.getFileDescriptor()));
400+
401+ // Write to AndroidX TestStorage (runner copies back to host)
402+ File out = new File(TestStorage.getInstance().getOutputDir(), "emulator-screenshot.png");
403+ try (FileOutputStream fos = new FileOutputStream(out)) {
404+ fos.write(png);
357405 }
358- System.out.println("<<CN1_SCREENSHOT_END>>");
359- System.out.flush();
360- } catch (IOException err) {
361- err.printStackTrace();
362- throw new RuntimeException(err);
363- }
406+ System.out.println("WROTE_TO_TEST_STORAGE:" + out.getAbsolutePath());
364407 }
365408
366- private static byte[] readAll(InputStream in) throws IOException {
409+ private static byte[] readAll(InputStream in) throws Exception {
367410 byte[] buf = new byte[64 * 1024];
368411 int n;
369- java.io. ByteArrayOutputStream out = new java.io. ByteArrayOutputStream();
412+ ByteArrayOutputStream out = new ByteArrayOutputStream();
370413 while ((n = in.read(buf)) != -1) out.write(buf, 0, n);
371414 return out.toByteArray();
372415 }
373416}
374417EOF
418+ sed -i " s|@PACKAGE@|$PACKAGE_NAME |g" " $TEST_CLASS "
375419ba_log " Created instrumentation test at $TEST_CLASS "
376420
377421DEFAULT_ANDROID_TEST=" $GRADLE_PROJECT_DIR /app/src/androidTest/java/com/example/myapplication2/ExampleInstrumentedTest.java"
0 commit comments