Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ jobs:
- name: Run unit tests
run: ./gradlew testDebugUnitTest --no-daemon

- name: Validate Compose Preview Screenshots
run: ./gradlew :app:validateDebugScreenshotTest --no-daemon

- name: Upload screenshot test report on failure
if: failure()
uses: actions/upload-artifact@v7
with:
name: screenshot-test-report
path: app/build/reports/screenshotTest/preview/debug/

- name: Upload test results
if: always()
uses: actions/upload-artifact@v7
Expand Down Expand Up @@ -276,3 +286,148 @@ jobs:
with:
name: instrumented-test-results-api${{ matrix.api-level }}${{ matrix.suffix && format('-{0}', matrix.suffix) || '' }}
path: app/build/reports/androidTests/connected/

# Experiment: drive the whole emulator flow with the new Android CLI
# (https://d.android.com/tools/agents/android-cli) instead of
# sdkmanager/avdmanager and the emulator-runner action.
android-cli-experiment:
name: Android CLI experiment (API 37.0 canary emulator)
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main')
runs-on: ubuntu-latest
timeout-minutes: 30
continue-on-error: true

steps:
- uses: actions/checkout@v6
with:
submodules: recursive

- uses: actions/setup-java@v5
with:
java-version: '17'
distribution: temurin
cache: gradle

- uses: gradle/actions/setup-gradle@v6

- name: Install Boost via apt
run: sudo apt-get install -y libboost-dev

- name: Resolve Boost dirs
run: |
BOOST_DIR=$(find /usr/lib -maxdepth 4 -name "Boost-*" -type d 2>/dev/null \
| sort -V | tail -1)
echo "BOOST_CMAKE_DIR=$BOOST_DIR" >> "$GITHUB_ENV"
mkdir -p /tmp/boost-headers
ln -sfn /usr/include/boost /tmp/boost-headers/boost
echo "BOOST_INCLUDE_DIR=/tmp/boost-headers" >> "$GITHUB_ENV"

- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm

- name: Install Android CLI
run: |
curl -fsSL https://dl.google.com/android/cli/latest/linux_x86_64/install.sh | bash
echo "$HOME/.local/bin" >> "$GITHUB_PATH"

- name: Android CLI diagnostics
run: |
android --version
android info
android sdk list | head -40

- name: Install canary emulator and API 37.0 ps16k image with the CLI
run: |
android sdk install --canary emulator platform-tools system-images/android-37.0/google_apis_ps16k/x86_64
android sdk list | grep -iE "^ emulator|android-37" || true

# 'android emulator create' is profile-based with no way to pin an image (it
# picks google_apis_playstore, whose first-boot Play overlays steal window
# focus and break Espresso). An AVD is just ini files, so create the profile
# with the CLI and retarget it at the pinned image — no sdkmanager/avdmanager.
- name: Create AVD with the CLI and retarget it to API 37.0 ps16k
run: |
android emulator create medium_phone
DEVICE="$(android emulator list | head -n1)"
AVD_DIR="$HOME/.android/avd/$DEVICE.avd"
if [ -z "$DEVICE" ] || [ ! -d "$AVD_DIR" ]; then
echo "ERROR: created AVD not found (DEVICE='$DEVICE')"
ls -la "$HOME/.android/avd/" || true
exit 1
fi
echo "CLI created AVD: $DEVICE"
CFG="$AVD_DIR/config.ini"
# Replace-or-append so a key missing from the CLI-written config cannot
# silently keep its profile default.
set_ini() {
if grep -q "^$1=" "$CFG"; then sed -i "s#^$1=.*#$1=$2#" "$CFG"; else echo "$1=$2" >> "$CFG"; fi
}
sed -i 's/^target=.*/target=android-37.0/' "$HOME/.android/avd/$DEVICE.ini"
set_ini image.sysdir.1 'system-images/android-37.0/google_apis_ps16k/x86_64/'
set_ini tag.id google_apis
set_ini tag.display 'Google APIs'
set_ini PlayStore.enabled false
set_ini hw.ramSize 4096
set_ini hw.cpu.ncore 4
set_ini disk.dataPartition.size 8192M
# Optional plural tag keys, if the profile wrote them, must not keep
# claiming the playstore image.
if grep -q '^tag.ids=' "$CFG"; then sed -i 's/^tag.ids=.*/tag.ids=google_apis,page_size_16kb/' "$CFG"; fi
if grep -q '^tag.displaynames=' "$CFG"; then sed -i 's/^tag.displaynames=.*/tag.displaynames=Google APIs,16 KB Page Size/' "$CFG"; fi
echo "===== root ini ====="; cat "$HOME/.android/avd/$DEVICE.ini"
echo "===== config.ini ====="; cat "$CFG"
echo "DEVICE=$DEVICE" >> "$GITHUB_ENV"

# 'android emulator start' passes neither -noaudio nor -no-window, so the
# runner needs libpulse and an X display.
- name: Install emulator host dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq libpulse0 xvfb
Xvfb :99 -screen 0 1280x800x24 &
echo "DISPLAY=:99" >> "$GITHUB_ENV"

- name: Start emulator with the CLI
timeout-minutes: 15
run: |
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-/usr/local/lib/android/sdk}}"
export PATH="$SDK/platform-tools:$PATH"
echo "Starting AVD: $DEVICE"
# start returns 0 even if the emulator process dies; verify via adb.
android emulator start --cold "$DEVICE"
for _ in $(seq 1 30); do adb get-state >/dev/null 2>&1 && break; sleep 2; done
adb get-state >/dev/null 2>&1 || { echo "===== emulator.log ====="; cat "$HOME/.android/$DEVICE/emulator.log" 2>/dev/null || echo "(no emulator.log)"; exit 1; }
adb devices
adb shell getprop ro.build.version.sdk
adb shell getprop sys.boot_completed
# The CLI has no disable-animations equivalent (emulator-runner sets it);
# Espresso needs animations off.
adb shell settings put global window_animation_scale 0
adb shell settings put global transition_animation_scale 0
adb shell settings put global animator_duration_scale 0
adb logcat -v time > "${{ github.workspace }}/emulator_logcat.txt" &

- name: Dump emulator.log on failure
if: failure()
run: cat "$HOME/.android/$DEVICE/emulator.log" 2>/dev/null || echo "(no emulator.log)"

- name: Run instrumented tests
run: ./gradlew connectedDebugAndroidTest --no-daemon

- name: Stop emulator
if: always()
run: android emulator stop "$DEVICE" || true

- name: Dump logcat on failure
if: failure()
run: tail -n 500 "${{ github.workspace }}/emulator_logcat.txt" 2>/dev/null || echo "no logcat captured"

- name: Upload test report on failure
if: failure()
uses: actions/upload-artifact@v7
with:
name: instrumented-test-report-android-cli-experiment
path: app/build/reports/androidTests/connected/
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# SimpleTorrent

[![Android CI](https://github.com/jpcottin/SimpleTorrent/actions/workflows/android.yml/badge.svg?branch=master)](https://github.com/jpcottin/SimpleTorrent/actions/workflows/android.yml)

<details>
<summary><b>CI details</b> — emulator matrix, API 35 → 37.1, plus an Android CLI leg</summary>

Instrumented legs run only on pushes to master (PRs run unit tests, lint, and both APK builds).

| Legs | Image | Emulator channel | GPU | Gating |
|---|---|---|---|---|
| API 35 | `google_apis` x86_64 | stable | swiftshader | ✅ blocking |
| API 37.0 | `google_apis_ps16k` (16 KB page size) | stable | lavapipe | non-blocking |
| API 37.0 | `google_apis_ps16k` | canary (`--channel=3`) | lavapipe, auto | non-blocking |
| API 37.1 | `google_apis_ps16k` | canary | lavapipe, auto | non-blocking |
| Android CLI experiment | `google_apis_ps16k` 37.0 | canary | emulator default | non-blocking |

The Android CLI leg drives the whole flow with the [`android` CLI](https://d.android.com/tools/agents/android-cli) (`android sdk install --canary`, `android emulator create/start/stop`) instead of `sdkmanager`/`avdmanager` and the emulator-runner action.

All emulator-runner legs use full diagnostics (`-verbose -show-kernel -debug-metrics -metrics-collection`) and a `cmdline-tools;latest` update so `avdmanager` writes a valid `target=android-37.x` (the runner's preinstalled version writes `android-0`, which the emulator clamps to API 3, disabling the Vulkan/GLDirectMem auto-enable the ps16k images need).

</details>

A minimal, fully native BitTorrent client for Android, built as a learning project to explore the
Android NDK and the libtorrent-rasterbar C++ library.

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading