Skip to content

Commit

Permalink
automatic android unit tests (#89)
Browse files Browse the repository at this point in the history
* fix build type

* first go for android unittests

* no need to build app for the unit tests

* debug output

* wrong debug command

* run android unittests with gui (so the users sees something + for the ci pipeline

* another shot at android unit testing

* try enable pages automatically

* pages are now enabled automatically

* fix arch

* auto generate a key in case it is not set

* increase scheduler time scaling for android tests

* fix deploy key

* further increase timing scale for android

* Revert "try enable pages automatically"

This reverts commit 4c588ef.

* Revert "pages are now enabled automatically"

This reverts commit 3ef79c2.

* make cache test more robust (tile order doesn't matter)

* account for super slow timers on android emulator

* test start the app on android. this should test shader compilation.

* fix warning

* try apt-get update before installing

* make unit tests more robust timing wise

* disable fuzzy rate tester for emulator android test + try to make scheduler test more robust.

* once more stability of scheduler tests

* fix more android emulator unit tests

* run opengl tests first to test the emulator

* timing more robust on the emulator

* so so

* update dependency

* change read_colour_attachment_pixel to be type safe, remove obsolete code, fix unit tests for webassembly and android emulator

* fix ubo comparison

* improve unit test stability: inserting might not create equal elements. taking time piont might take longer than a millisecond (on emulator)

* improve unit test stability: inserting might not create equal elements. taking time piont might take longer than a millisecond (on emulator)  #2

* increase emulator timeout + move gl tests further down again.
  • Loading branch information
adam-ce authored Jan 21, 2024
1 parent e88232c commit 1d88304
Show file tree
Hide file tree
Showing 20 changed files with 381 additions and 275 deletions.
181 changes: 181 additions & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
name: "Android"
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]


# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "android"
cancel-in-progress: true

env:
ANDROID_ARCH: x86_64
ANDROID_TARGET: google_apis_playstore
API_LEVEL: 33
ANDROID_BUILD_TOOLS_VERSION: 34.0.0
ANDROID_SDK_PACKAGES: system-images;android-33;google_apis_playstore;x86_64 platforms;android-33 build-tools;34.0.0 platform-tools emulator
EMULATOR_TIMEOUT: 900
EMULATOR_NAME: nexus

jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3

- name: Install dependencies
run: |
brew install tree
- name: Setup JDK
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'

- name: Install Qt native version (required for cross compilation)
uses: jurplel/install-qt-action@v3
with:
aqtversion: '==3.1.*'
version: '6.6.1'
host: mac
target: 'desktop'
arch: clang_64
dir: '${{github.workspace}}/qt'
install-deps: 'true'

- name: Set QT_HOST_PATH
run: echo "QT_HOST_PATH=${Qt6_DIR}" >> "$GITHUB_ENV"

- name: Install Qt Android version
uses: jurplel/install-qt-action@v3
with:
aqtversion: '==3.1.*'
version: '6.6.1'
host: mac
target: 'android'
arch: android_x86_64
dir: '${{github.workspace}}/qt'
install-deps: 'true'
modules: 'qtcharts qtpositioning'

- name: Make qt binaries executable
run: |
chmod u+x ${Qt6_DIR}/bin/*
- name: Set environment variables
shell: bash
run: |
BUILD_DIR="build"
echo "BUILD_DIR=$BUILD_DIR" >> $GITHUB_ENV
- name: Configure
env:
CMAKE_PREFIX_PATH: ${{env.Qt6_DIR}}/lib/cmake
run: >
${Qt6_DIR}/bin/qt-cmake
-B $BUILD_DIR
-DCMAKE_BUILD_TYPE=Release
-DALP_ENABLE_APP_SHUTDOWN_AFTER_60S=ON
-DALP_ENABLE_ASSERTS=ON
-S ${{ github.workspace }}
- name: Build
run: |
cmake --build $BUILD_DIR
- name: Debug output
if: always()
run: |
tree $BUILD_DIR
- name: Signing Android packages
run: |
keytool -genkey -v -keystore alp.keystore -alias alpinemaps -keyalg RSA -sigalg SHA1withRSA -keysize 2048 -validity 10000 -keypass asdfasdf -storepass asdfasdf -dname "CN=Franz, OU=IT, O=Furz, L=Rattenberg, ST=Tirol, C=AT"
ANDROID_DEPLOY="$QT_HOST_PATH/bin/androiddeployqt --input"
DEPLOY_PARAMS="--android-platform android-33 --gradle --release --sign alp.keystore alpinemaps --storepass asdfasdf"
$ANDROID_DEPLOY $BUILD_DIR/app/android-alpineapp-deployment-settings.json --output $BUILD_DIR/app/android-build/ $DEPLOY_PARAMS
$ANDROID_DEPLOY $BUILD_DIR/unittests/nucleus/android-unittests_nucleus-deployment-settings.json --output $BUILD_DIR/unittests/nucleus/android-build $DEPLOY_PARAMS
$ANDROID_DEPLOY $BUILD_DIR/unittests/gl_engine/android-unittests_gl_engine-deployment-settings.json --output $BUILD_DIR/unittests/gl_engine/android-build $DEPLOY_PARAMS
$ANDROID_DEPLOY $BUILD_DIR/alp_external/radix/unittests/android-unittests_radix-deployment-settings.json --output $BUILD_DIR/alp_external/radix/unittests/android-build $DEPLOY_PARAMS
- name: Add avdmanager and sdkmanager to system PATH
run: |
echo "$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/emulator:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools/${{ env.ANDROID_BUILD_TOOLS_VERSION }}" >> $GITHUB_PATH
- name: Install Sdk
run: |
yes Y | sdkmanager --licenses
sdkmanager --install ${ANDROID_SDK_PACKAGES}
- name: Build emulator
run: |
echo "no" | avdmanager --verbose create avd --force -n $EMULATOR_NAME --abi "${ANDROID_TARGET}/${ANDROID_ARCH}" -k "system-images;android-${API_LEVEL};${ANDROID_TARGET};${ANDROID_ARCH}"
- name: Launch emulator
run: |
wget https://raw.githubusercontent.com/amrsa1/android-emulator-workflow/main/start_emu_headless.sh
chmod +x ./start_emu_headless.sh
EMULATOR_TIMEOUT=$EMULATOR_TIMEOUT EMULATOR_NAME=$EMULATOR_NAME ./start_emu_headless.sh
- name: Debug output
if: always()
run: |
tree $BUILD_DIR
- name: Run unittests_radix on emulator
run: |
adb devices
adb install $BUILD_DIR/alp_external/radix/unittests/android-build/build/outputs/apk/release/android-build-release-signed.apk
adb shell am start -W -n org.qtproject.example.unittests_radix/org.qtproject.qt.android.bindings.QtActivity
while [ "$(adb shell dumpsys window windows | grep -i "unittests_radix" | wc -l | tr -d '[:space:]')" -ne 0 ]; do sleep 2; done && echo "Execution finished."
adb logcat -d -s libunittests_radix_x86_64.so
adb logcat -d -s libunittests_radix_x86_64.so | grep -q "Catch::Session finished with exit code 0" || exit 1
- name: Run unittests_nucleus on emulator
run: |
adb devices
adb install $BUILD_DIR/unittests/nucleus/android-build/build/outputs/apk/release/android-build-release-signed.apk
adb shell am start -W -n org.qtproject.example.unittests_nucleus/org.qtproject.qt.android.bindings.QtActivity
while [ "$(adb shell dumpsys window windows | grep -i "unittests_nucleus" | wc -l | tr -d '[:space:]')" -ne 0 ]; do sleep 2; done && echo "Execution finished."
adb logcat -d -s libunittests_nucleus_x86_64.so
# usually the tests are saying exit with 0 and then they die. that happens even if we return 1 from main.
# so instead we print something after finishing the tests and check it here.
adb logcat -d -s libunittests_nucleus_x86_64.so | grep -q "Catch::Session finished with exit code 0" || exit 1
- name: Run unittests_gl_engine on emulator
run: |
adb devices
adb install $BUILD_DIR/unittests/gl_engine/android-build/build/outputs/apk/release/android-build-release-signed.apk
adb shell am start -W -n org.qtproject.example.unittests_gl_engine/org.qtproject.qt.android.bindings.QtActivity
while [ "$(adb shell dumpsys window windows | grep -i "unittests_gl_engine" | wc -l | tr -d '[:space:]')" -ne 0 ]; do sleep 2; done && echo "Execution finished."
adb logcat -d -s libunittests_gl_engine_x86_64.so
adb logcat -d -s libunittests_gl_engine_x86_64.so | grep -q "Catch::Session finished with exit code 0" || exit 1
- name: Run alpine app on emulator
run: |
adb devices
adb install $BUILD_DIR/app/android-build/build/outputs/apk/release/android-build-release-signed.apk
adb shell am start -W -n org.alpinemaps.app/org.qtproject.qt.android.bindings.QtActivity
while [ "$(adb shell dumpsys window windows | grep -i "org\.alpinemaps\.app" | wc -l | tr -d '[:space:]')" -ne 0 ]; do sleep 2; done && echo "Execution finished."
adb logcat -d -s AlpineApp
adb logcat -d -s AlpineApp | grep -q "AlpineApp shuts down after 60s." || exit 1
23 changes: 16 additions & 7 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ jobs:

steps:
- name: Install dependencies
run: sudo apt-get install -y build-essential ninja-build
run: |
sudo apt-get update
sudo apt-get install -y build-essential ninja-build
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -118,7 +120,7 @@ jobs:
- name: Build
run: cmake --build $BUILD_DIR ${{ matrix.additional_build_flags }}

- name: Signing Android package
- name: Signing Android package with common key
env:
secret_test: ${{ secrets.KEYSTOREPASSWORD }}
if: matrix.qttarget == 'android' && env.secret_test != ''
Expand All @@ -127,23 +129,30 @@ jobs:
base64 -d release.keystore.base64 > release.keystore
$QT_HOST_PATH/bin/androiddeployqt --input $DEPLOYMENT_SETTINGS --output $ANDROID_BUILD_DIR --android-platform android-33 --gradle --release --sign release.keystore alpinemaps --storepass ${{ secrets.KEYSTOREPASSWORD }}
- name: Readme why Android package was not signed
- name: Signing Android packages with generated key
env:
secret_test: ${{ secrets.KEYSTOREPASSWORD }}
if: matrix.qttarget == 'android' && env.secret_test == ''
run: |
README_PATH=$APK_DIR/read_me_in_case_you_wonder_why_the_apk_is_not_signed.txt
echo "Well, the apk wasn't signed because there is no key to sign it with in your repository (the one this was created in)." >> $README_PATH
keytool -genkey -v -keystore release.keystore -alias alpinemaps -keyalg RSA -sigalg SHA1withRSA -keysize 2048 -validity 10000 -keypass asdfasdf -storepass asdfasdf -dname "CN=Franz, OU=IT, O=Furz, L=Rattenberg, ST=Tirol, C=AT"
$QT_HOST_PATH/bin/androiddeployqt --input $DEPLOYMENT_SETTINGS --output $ANDROID_BUILD_DIR --android-platform android-33 --gradle --release --sign release.keystore alpinemaps --storepass asdfasdf
README_PATH=$APK_DIR/read_me.txt
echo "The apk was signed with a generated key which changes every time the apk is generated. This means, that android might refuse to install it if another apk with the same app was installed previously. You'll have to deinstall it. Doing so will delete all settings and cache." >> $README_PATH
echo "" >> $README_PATH
echo "These are the steps to generate such a key:" >> $README_PATH
echo "In order to prevent that, you have to generate your own key or use our public key:" >> $README_PATH
echo "" >> $README_PATH
echo "To generate your own key:" >> $README_PATH
echo "- https://stackoverflow.com/questions/3997748/how-can-i-create-a-keystore. Use 'alpinemaps' as the alias!" >> $README_PATH
echo "- If you have the android dev setup ready in Qt Creator, you can also create the keystore via Projects (on the left side toolboar) -> Android Qt ... -> Build -> Build Steps -> Build Android APK -> Application Signature -> Create. Use 'alpinemaps' as the alias!" >> $README_PATH
echo "- Then you have to encode the keystore in base64, e.g., on linux via 'base64 keystorefile > keystorefile.base64'" >> $README_PATH
echo "- Finally, create the following secrets in github -> your repo -> Settings -> Secrets and variables -> Actions -> Repository secrets" >> $README_PATH
echo " SIGNINGKEYBASE64 = the base64 encoded key" >> $README_PATH
echo " KEYSTOREPASSWORD = the password used to create the keystore" >> $README_PATH
echo "" >> $README_PATH
echo "Oh, and I hope this saved your day ;)" >> $README_PATH
echo "To use our public key, go to https://github.com/AlpineMapsOrg/renderer/blob/main/creating_apk_keys.md" >> $README_PATH
echo "" >> $README_PATH
echo "Oh, and I hope this saved your day :)" >> $README_PATH
- name: Copy android packages
if: matrix.qttarget == 'android'
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
- name: Install Linux Dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential ninja-build lld clang-15 libgl1-mesa-dev libxcb-cursor-dev xorg-dev libxrandr-dev libxcursor-dev libudev-dev libopenal-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev libdrm-dev libgbm-dev xvfb libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0
- name: Install Qt
Expand All @@ -65,7 +66,7 @@ jobs:
CMAKE_PREFIX_PATH: ${{env.Qt6_Dir}}/lib/cmake
run: >
cmake -G Ninja
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
-DCMAKE_BUILD_TYPE=${{matrix.BUILD_TYPE}}
-DALP_ENABLE_ASSERTS=ON
-DALP_ENABLE_ADDRESS_SANITIZER=ON
-DALP_ENABLE_APP_SHUTDOWN_AFTER_60S=ON
Expand Down
5 changes: 4 additions & 1 deletion app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,10 @@ int main(int argc, char **argv)
root_window->showMaximized();
#endif
#ifdef ALP_APP_SHUTDOWN_AFTER_60S
QTimer::singleShot(60000, &app, &QCoreApplication::quit);
QTimer::singleShot(60000, &app, []() {
qDebug() << "AlpineApp shuts down after 60s.";
QGuiApplication::quit();
});
#endif

RenderThreadNotifier::instance()
Expand Down
3 changes: 3 additions & 0 deletions cmake/alp_add_unittest.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ find_package(Qt6 REQUIRED COMPONENTS Test)
if (NOT TARGET Catch2)
alp_add_git_repository(catch2 URL https://github.com/catchorg/Catch2.git COMMITISH v3.5.1)
endif()
alp_add_git_repository(qml_catch2_console URL https://github.com/AlpineMapsOrg/qml_catch2_console.git COMMITISH v24.01.20 DO_NOT_ADD_SUBPROJECT)

if (EMSCRIPTEN AND ALP_ENABLE_THREADING)
target_compile_options(Catch2 PRIVATE -pthread)
Expand All @@ -41,6 +42,8 @@ function(alp_add_unittest name)
list(APPEND ALP_INSTALL_FILES "$<TARGET_FILE_DIR:${name}>/${name}.worker.js")
endif()
install(FILES ${ALP_INSTALL_FILES} DESTINATION ${ALP_WWW_INSTALL_DIR})
elseif(ANDROID)
add_qml_catch2_console_unittests(${name} ${ARGN})
else()
qt_add_executable(${name} ${ARGN} ${CMAKE_SOURCE_DIR}/unittests/main.cpp)
target_link_libraries(${name} PUBLIC Catch2::Catch2)
Expand Down
Loading

0 comments on commit 1d88304

Please sign in to comment.