From b0174ee6c6d12b78ab1776307c51131e06ec4c85 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Sat, 24 Oct 2026 02:10:46 +0100 Subject: [PATCH 1/4] Run the Android Robolectric tests in CI The tests/android module added in #147 only ran by hand against a local core checkout. This workflow runs it on PRs that touch the Android renderers, the module, composer.json or the workflow, using the nativephp/mobile copy Composer installs as the source of core's KeyboardFocusPolicy.kt and NativeUINode.kt, and core's own Gradle wrapper. It fails to compile against 4.6.0, which lacks the flush hook from mobile-air#565, and goes green once composer.json requires ^4.7. A workflow_dispatch input lets it run against dev-main until then. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/android-tests.yml | 94 +++++++++++++++++++++++++++++ tests/android/README.md | 15 +++++ 2 files changed, 109 insertions(+) create mode 100644 .github/workflows/android-tests.yml diff --git a/.github/workflows/android-tests.yml b/.github/workflows/android-tests.yml new file mode 100644 index 0000000..2aec19d --- /dev/null +++ b/.github/workflows/android-tests.yml @@ -0,0 +1,94 @@ +name: Android tests + +# A separate workflow rather than a job in tests.yml, because `paths` filters +# a whole workflow and the Pest/Swift jobs should keep running on every PR. +# Keep this list in sync with the stageSources inputs in +# tests/android/build.gradle.kts. +on: + pull_request: + paths: + - 'resources/android/**' + - 'tests/android/**' + - 'composer.json' + - '.github/workflows/android-tests.yml' + push: + branches: [main] + paths: + - 'resources/android/**' + - 'tests/android/**' + - 'composer.json' + - '.github/workflows/android-tests.yml' + workflow_dispatch: + inputs: + mobile: + description: 'nativephp/mobile version to test against, e.g. dev-main. Empty uses composer.json.' + required: false + default: '' + +permissions: + contents: read + +jobs: + robolectric: + name: Android renderers (Robolectric) + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + coverage: none + + # The module compiles this repo's renderers against core's + # KeyboardFocusPolicy.kt and NativeUINode.kt, taken from the + # nativephp/mobile dist Composer installs into vendor/ (mobile-air has + # no export-ignore rules, so resources/androidstudio ships in the zip). + # + # Until composer.json requires a nativephp/mobile that includes + # mobile-air#565 (4.7), this job fails at compileDebugKotlin: the + # editors pass a flush closure to KeyboardFocusPolicy.fieldFocused, + # which 4.6.0 doesn't accept. It turns green once ^4.7 is required. + - name: Install nativephp/mobile + env: + MOBILE: ${{ inputs.mobile }} + run: | + if [ -n "$MOBILE" ]; then + composer require "nativephp/mobile:$MOBILE" --no-update --no-interaction + fi + composer update --no-dev --prefer-dist --no-interaction --no-progress + composer show nativephp/mobile | grep -E '^(versions|source) ' + + - name: Setup JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + cache: gradle + cache-dependency-path: | + tests/android/*.gradle.kts + vendor/nativephp/mobile/resources/androidstudio/gradle/wrapper/gradle-wrapper.properties + + - name: Check Android SDK platform + run: | + if [ ! -d "$ANDROID_HOME/platforms/android-36" ]; then + "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --install "platforms;android-36" + fi + + # Core's own Gradle wrapper, so the Gradle version moves with core's + # Android project (the module pins the same AGP). + - name: Run Robolectric tests + run: | + vendor/nativephp/mobile/resources/androidstudio/gradlew \ + -p tests/android -PmobileRoot="$PWD/vendor/nativephp/mobile" \ + testDebugUnitTest --console=plain + + - name: Upload test report + if: failure() + uses: actions/upload-artifact@v4 + with: + name: android-test-report + path: tests/android/build/reports/tests/testDebugUnitTest/ + if-no-files-found: ignore diff --git a/tests/android/README.md b/tests/android/README.md index 006148b..a6eb77c 100644 --- a/tests/android/README.md +++ b/tests/android/README.md @@ -14,6 +14,21 @@ Requires JDK 21, Android SDK 36 and a Mobile checkout with the Android -p tests/android -PmobileRoot=/absolute/path/to/mobile testDebugUnitTest ``` +The Composer-installed copy works as the Mobile checkout, which is what CI +uses (`.github/workflows/android-tests.yml`): + +```bash +composer update +vendor/nativephp/mobile/resources/androidstudio/gradlew \ + -p tests/android -PmobileRoot="$PWD/vendor/nativephp/mobile" testDebugUnitTest +``` + +Mobile 4.6.0 doesn't have the flush hook, so against it the module fails to +compile. Until `composer.json` requires 4.7, test against Mobile's main branch +with `composer require nativephp/mobile:dev-main --no-update && composer update` +(don't commit that), or run the workflow by hand with `mobile` set to +`dev-main`. + The twelve cases exercise both editors with live, blur and debounce models, with and without keep-focus-on-submit. They edit while focused, replace the change callback during composition, then invoke the separate Button's semantic From cc992161f713a7182cd72ca9789afec3ea69af8c Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Sat, 24 Oct 2026 02:13:50 +0100 Subject: [PATCH 2/4] TEMP: run the Android job against mobile-air main (reverted next) Co-Authored-By: Claude Opus 5.5 --- .github/workflows/android-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/android-tests.yml b/.github/workflows/android-tests.yml index 2aec19d..c8f7cac 100644 --- a/.github/workflows/android-tests.yml +++ b/.github/workflows/android-tests.yml @@ -53,7 +53,7 @@ jobs: # which 4.6.0 doesn't accept. It turns green once ^4.7 is required. - name: Install nativephp/mobile env: - MOBILE: ${{ inputs.mobile }} + MOBILE: ${{ inputs.mobile || 'dev-main' }} run: | if [ -n "$MOBILE" ]; then composer require "nativephp/mobile:$MOBILE" --no-update --no-interaction From 51c86bf268e90de005ffa6779a2aa6d2e3535550 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Sat, 24 Oct 2026 02:14:34 +0100 Subject: [PATCH 3/4] Print the Android test counts in the job summary Co-Authored-By: Claude Opus 5.5 --- .github/workflows/android-tests.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/android-tests.yml b/.github/workflows/android-tests.yml index c8f7cac..e4b250b 100644 --- a/.github/workflows/android-tests.yml +++ b/.github/workflows/android-tests.yml @@ -85,6 +85,19 @@ jobs: -p tests/android -PmobileRoot="$PWD/vendor/nativephp/mobile" \ testDebugUnitTest --console=plain + # Gradle prints nothing for passing tests, so count them here. + - name: Summarise test results + if: always() + run: | + shopt -s nullglob + results=(tests/android/build/test-results/testDebugUnitTest/*.xml) + if [ ${#results[@]} -eq 0 ]; then + echo "No test results: the module didn't compile or the tests didn't run." | tee -a "$GITHUB_STEP_SUMMARY" + exit 0 + fi + sed -n 's/.* Date: Sat, 24 Oct 2026 02:17:12 +0100 Subject: [PATCH 4/4] Revert the temporary dev-main run This reverts commit cc99216. The job tests what composer.json resolves again. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/android-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/android-tests.yml b/.github/workflows/android-tests.yml index e4b250b..505a3d9 100644 --- a/.github/workflows/android-tests.yml +++ b/.github/workflows/android-tests.yml @@ -53,7 +53,7 @@ jobs: # which 4.6.0 doesn't accept. It turns green once ^4.7 is required. - name: Install nativephp/mobile env: - MOBILE: ${{ inputs.mobile || 'dev-main' }} + MOBILE: ${{ inputs.mobile }} run: | if [ -n "$MOBILE" ]; then composer require "nativephp/mobile:$MOBILE" --no-update --no-interaction