From 72417cf84a389fefaf4d02bf4c28b280adf70e31 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 16:59:15 +0300 Subject: [PATCH 01/56] Updated gradle to 9.5.0 --- gradle/wrapper/gradle-wrapper.properties | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c61a118f7..24ac9f37b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,11 @@ +# +# 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 0c7473d61de97183500159d73311987ef1f9eb11 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 17:04:22 +0300 Subject: [PATCH 02/56] Updated CI scripts with --no-daemon, --build-cache, and --max-workers=2 flags --- .semaphore/semaphore.yml | 2 +- script/ci-instrumentation-tests-enterprise.sh | 7 ++++++- script/ci-instrumentation-tests-flaky.sh | 7 ++++++- script/ci-instrumentation-tests-with-mailserver.sh | 7 ++++++- script/ci-instrumentation-tests-without-mailserver.sh | 7 ++++++- script/ci-junit-tests.sh | 7 ++++++- script/ci-lint-checks.sh | 7 ++++++- 7 files changed, 37 insertions(+), 7 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index f80cd11f2..e7b935f2d 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -79,7 +79,7 @@ blocks: # print Java version - java -version # compile project - - ./gradlew --console=plain assembleConsumerUiTests + - ./gradlew --console=plain --no-daemon --build-cache --max-workers=2 assembleConsumerUiTests epilogue: on_pass: commands: diff --git a/script/ci-instrumentation-tests-enterprise.sh b/script/ci-instrumentation-tests-enterprise.sh index b197fcb09..ca8bcc384 100755 --- a/script/ci-instrumentation-tests-enterprise.sh +++ b/script/ci-instrumentation-tests-enterprise.sh @@ -1,4 +1,9 @@ #!/bin/bash -./gradlew --console=plain :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +./gradlew --console=plain --no-daemon --build-cache --max-workers=2 --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.EnterpriseTestsFilter diff --git a/script/ci-instrumentation-tests-flaky.sh b/script/ci-instrumentation-tests-flaky.sh index 706d3f24b..9fd35ced3 100755 --- a/script/ci-instrumentation-tests-flaky.sh +++ b/script/ci-instrumentation-tests-flaky.sh @@ -1,4 +1,9 @@ #!/bin/bash -./gradlew --console=plain :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.ReadyForCIAndFlakyFilter diff --git a/script/ci-instrumentation-tests-with-mailserver.sh b/script/ci-instrumentation-tests-with-mailserver.sh index 41da56141..62f0184e0 100755 --- a/script/ci-instrumentation-tests-with-mailserver.sh +++ b/script/ci-instrumentation-tests-with-mailserver.sh @@ -1,10 +1,15 @@ #!/bin/bash +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + #print test names #adb shell am instrument -r -w \ # -e filter com.flowcrypt.email.junit.filters.DependsOnMailServerFilter \ # -e log true \ # com.flowcrypt.email.debug.test/androidx.test.runner.AndroidJUnitRunner -./gradlew --console=plain :FlowCrypt:connectedConsumerUiTestsAndroidTest \ +./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedConsumerUiTestsAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.DependsOnMailServerFilter diff --git a/script/ci-instrumentation-tests-without-mailserver.sh b/script/ci-instrumentation-tests-without-mailserver.sh index 778887d21..acdc12b48 100755 --- a/script/ci-instrumentation-tests-without-mailserver.sh +++ b/script/ci-instrumentation-tests-without-mailserver.sh @@ -1,5 +1,10 @@ #!/bin/bash +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + if [[ -z "$1" ]]; then echo "numShards is unset or set to the empty string" @@ -28,7 +33,7 @@ if [[ ${varShardIndex} -ge ${varNumShards} ]] # -e log true \ # com.flowcrypt.email.debug.test/androidx.test.runner.AndroidJUnitRunner - ./gradlew --console=plain :FlowCrypt:connectedConsumerUiTestsAndroidTest \ + ./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedConsumerUiTestsAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.DoesNotNeedMailServerFilter \ -Pandroid.testInstrumentationRunnerArguments.numShards="${varNumShards}" \ -Pandroid.testInstrumentationRunnerArguments.shardIndex="${varShardIndex}" diff --git a/script/ci-junit-tests.sh b/script/ci-junit-tests.sh index dfdcb6a2e..eeafac333 100755 --- a/script/ci-junit-tests.sh +++ b/script/ci-junit-tests.sh @@ -1,3 +1,8 @@ #!/bin/bash -./gradlew --console=plain :FlowCrypt:testConsumerUiTestsUnitTest +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:testConsumerUiTestsUnitTest diff --git a/script/ci-lint-checks.sh b/script/ci-lint-checks.sh index cdd01071a..28d447c36 100755 --- a/script/ci-lint-checks.sh +++ b/script/ci-lint-checks.sh @@ -1,3 +1,8 @@ #!/bin/bash -./gradlew --console=plain :FlowCrypt:lintConsumerUiTests +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:lintConsumerUiTests From cd861b6cf879043917dff1de557b2f3c325f72d6 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 17:17:42 +0300 Subject: [PATCH 03/56] Refactored Semaphore CI caching strategy and separated checksums --- .semaphore/semaphore.yml | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index e7b935f2d..e5898d748 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -46,21 +46,22 @@ global_job_config: - sudo rm -rf ~/.rbenv ~/.phpbrew - checkout # init environment variables - - export SUM=$(checksum build.gradle.kts)-$(checksum FlowCrypt/build.gradle.kts)-$(checksum ./script/ci-install-android-sdk.sh) - - export APP_GRADLE_CACHE=gradle-cache-$SUM # per conf files hash - - export ANDROID_SDK_CACHE=android-sdk-$SUM # per conf files hash - - export BUILD_CXX_CACHE=build-cxx-cache-$SEMAPHORE_GIT_BRANCH-$SUM # per branch and conf files hash - - export BUILD_NATIVE_CACHE=build-native-cache-$SEMAPHORE_GIT_BRANCH-$SUM # per branch and conf files hash - - export BUILD_CACHE=build-cache-$SEMAPHORE_GIT_BRANCH-$SUM # per branch and conf files hash + - export GRADLE_SUM=$(checksum build.gradle.kts)-$(checksum FlowCrypt/build.gradle.kts) + - export SDK_SUM=$(checksum ./script/ci-install-android-sdk.sh) + - export APP_GRADLE_CACHE=project-gradle-cache-$GRADLE_SUM # project .gradle cache, per Gradle config hash + - export ANDROID_SDK_CACHE=android-sdk-$SDK_SUM # Android SDK cache, per SDK installer hash + - export BUILD_CXX_CACHE=build-cxx-cache-$SEMAPHORE_GIT_BRANCH-$GRADLE_SUM # per branch and Gradle config hash + - export BUILD_NATIVE_CACHE=build-native-cache-$SEMAPHORE_GIT_BRANCH-$GRADLE_SUM # per branch and Gradle config hash # restore app caches - cache restore $APP_GRADLE_CACHE - cache restore $BUILD_CXX_CACHE - cache restore $BUILD_NATIVE_CACHE - - cache restore $BUILD_CACHE # restore global caches - cache restore $ANDROID_SDK_CACHE - cache restore gradle-wrapper + - cache restore gradle-cache-$GRADLE_SUM - cache restore gradle-cache + - cache restore android-build-cache-$GRADLE_SUM - cache restore android-build-cache # Install Android dependencies if needed - ./script/ci-install-android-sdk.sh @@ -88,18 +89,14 @@ blocks: - cache has_key $APP_GRADLE_CACHE || cache store $APP_GRADLE_CACHE .gradle - cache has_key $BUILD_CXX_CACHE || cache store $BUILD_CXX_CACHE FlowCrypt/.cxx - cache has_key $BUILD_NATIVE_CACHE || cache store $BUILD_NATIVE_CACHE FlowCrypt/.externalNativeBuild - - cache has_key $BUILD_CACHE || cache store $BUILD_CACHE FlowCrypt/build # clean and store global cache - echo "Store the global cache" - find ~/.gradle/caches/ -name "*.lock" -type f -delete # https://medium.com/cirruslabs/mastering-gradle-caching-and-incremental-builds-37eb1af7fcde - cache has_key $ANDROID_SDK_CACHE || cache store $ANDROID_SDK_CACHE $ANDROID_HOME - - cache delete gradle-wrapper - - cache delete gradle-cache - - cache delete android-build-cache - - cache store gradle-wrapper ~/.gradle/wrapper - - cache store gradle-cache ~/.gradle/caches - - cache store android-build-cache ~/.android/build-cache + - cache has_key gradle-wrapper || cache store gradle-wrapper ~/.gradle/wrapper + - cache has_key gradle-cache-$GRADLE_SUM || cache store gradle-cache-$GRADLE_SUM ~/.gradle/caches + - cache has_key android-build-cache-$GRADLE_SUM || cache store android-build-cache-$GRADLE_SUM ~/.android/build-cache - name: 'Testing' task: From bae1500baabadbd2571a0c1163b6cd4d8dd2e4a3 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 17:19:00 +0300 Subject: [PATCH 04/56] wip --- .semaphore/semaphore.yml | 58 ---------------------------------------- 1 file changed, 58 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index e5898d748..48109cb59 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -101,51 +101,6 @@ blocks: - name: 'Testing' task: jobs: - - name: 'Lint(structural quality)' - execution_time_limit: - minutes: 15 - commands: - # run Lint checks - - ./script/ci-lint-checks.sh - - - name: 'JUnit tests' - execution_time_limit: - minutes: 15 - commands: - # run JUnit tests - - ./script/ci-junit-tests.sh - - - name: 'Instrumentation tests(No email server)' - execution_time_limit: - minutes: 60 - matrix: - - env_var: EMULATOR - values: [ "0", "1", "2", "3" ] - commands: - # Setup and run an emulator - - ./script/ci-setup-and-run-emulator.sh - # wait until ready - - ./script/ci-wait-for-emulator.sh - # Run filtered logging for TestRunner - - adb logcat -v color TestRunner:V *:S > ~/logcat_log.txt & - # Run instrumentation tests - - ./script/ci-instrumentation-tests-without-mailserver.sh 4 $EMULATOR - - - name: 'Instrumentation tests(with email server)' - execution_time_limit: - minutes: 60 - commands: - # Run an email server - - cd docker-mailserver && ./run_email_server.sh && cd - - # Setup and run an emulator - - ./script/ci-setup-and-run-emulator.sh - #wait until ready - - ./script/ci-wait-for-emulator.sh - # Run filtered logging for TestRunner - - adb logcat -v color TestRunner:V *:S > ~/logcat_log.txt & - # Run instrumentation tests - - ./script/ci-instrumentation-tests-with-mailserver.sh - - name: 'Instrumentation tests(enterprise)' execution_time_limit: minutes: 60 @@ -159,19 +114,6 @@ blocks: # Run instrumentation tests - ./script/ci-instrumentation-tests-enterprise.sh - - name: 'Instrumentation tests(flaky)' - execution_time_limit: - minutes: 60 - commands: - # Setup and run an emulator - - ./script/ci-setup-and-run-emulator.sh - #wait until ready - - ./script/ci-wait-for-emulator.sh - # Run filtered logging for TestRunner - - adb logcat -v color TestRunner:V *:S > ~/logcat_log.txt & - # Run instrumentation tests - - ./script/ci-instrumentation-tests-flaky.sh - epilogue: always: commands: From 5bcab0e9ebafc186e1a23b3ecbf0aa65f6edfa88 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 17:36:56 +0300 Subject: [PATCH 05/56] Refactored CI scripts for improved robustness and emulator handling - Updated shebangs to `#!/usr/bin/env bash` and added `set -euo pipefail` for better error handling. - Improved emulator setup in `ci-setup-and-run-emulator.sh` by ensuring a fresh AVD state and configuring hardware parameters like RAM and GPU. - Optimized `ci-wait-for-emulator.sh` with more reliable boot completion checks and a retry loop for internet connectivity. - Added existence checks for directories and files in artifact and test result publishing scripts to prevent errors when files are missing. - Cleaned up redundant script headers and refactored argument handling in instrumentation test scripts. --- ...ccess-actions-for-instrumentation-tests.sh | 28 +++++++---- ...-get-and-publish-debug-info-as-artifact.sh | 46 ++++++++++++------ script/ci-instrumentation-tests-enterprise.sh | 6 ++- script/ci-instrumentation-tests-flaky.sh | 4 +- ...i-instrumentation-tests-with-mailserver.sh | 4 +- ...nstrumentation-tests-without-mailserver.sh | 47 +++++++++---------- script/ci-junit-tests.sh | 4 +- script/ci-lint-checks.sh | 4 +- script/ci-publish-test-results.sh | 26 +++++++--- script/ci-setup-and-run-emulator.sh | 42 ++++++++++++++--- script/ci-wait-for-emulator.sh | 39 ++++++++++----- 11 files changed, 173 insertions(+), 77 deletions(-) diff --git a/script/ci-after-success-actions-for-instrumentation-tests.sh b/script/ci-after-success-actions-for-instrumentation-tests.sh index 599b26f38..c1a1552c5 100755 --- a/script/ci-after-success-actions-for-instrumentation-tests.sh +++ b/script/ci-after-success-actions-for-instrumentation-tests.sh @@ -1,18 +1,30 @@ -#!/bin/bash +#!/usr/bin/env bash + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -euo pipefail if [[ "$SEMAPHORE_JOB_NAME" =~ ^Instrumentation.* ]]; then # print debug info about connected device echo "Print connected devices" adb devices - # store logcat log + if [[ -f "$HOME/logcat_log.txt" ]]; then echo "Store logcat log" - artifact push job ~/logcat_log.txt + artifact push job "$HOME/logcat_log.txt" + else + echo "No logcat_log.txt found, skipping" + fi - # store screenshots - echo "Store screenshots" - adb pull "/sdcard/Pictures" - adb shell ls /sdcard/Pictures + echo "Store screenshots" + if adb shell test -d /sdcard/Pictures; then + rm -rf Pictures + adb pull "/sdcard/Pictures" Pictures artifact push job Pictures + else + echo "No /sdcard/Pictures directory found, skipping" + fi fi - diff --git a/script/ci-get-and-publish-debug-info-as-artifact.sh b/script/ci-get-and-publish-debug-info-as-artifact.sh index bf51181f9..1088399f1 100755 --- a/script/ci-get-and-publish-debug-info-as-artifact.sh +++ b/script/ci-get-and-publish-debug-info-as-artifact.sh @@ -1,23 +1,39 @@ -#!/bin/bash +#!/usr/bin/env bash + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -euo pipefail if [[ "$SEMAPHORE_JOB_NAME" =~ ^Lint.* ]]; then - # don't do any things for 'Lint(structural quality)' job - exit 0 + # Do nothing for 'Lint(structural quality)' job. + exit 0 fi -echo "Store tests results for $SEMAPHORE_JOB_NAME" -artifact push job FlowCrypt/build/reports/ +reports_dir="FlowCrypt/build/reports/" +if [[ -d "$reports_dir" ]]; then + echo "Store test reports for $SEMAPHORE_JOB_NAME" + artifact push job "$reports_dir" +else + echo "Reports directory does not exist: $reports_dir" +fi if [[ "$SEMAPHORE_JOB_NAME" =~ ^Instrumentation.* ]]; then - # store full logcat log - echo "Collect logcat logs as logcat.txt.gz for $SEMAPHORE_JOB_NAME" - adb logcat -d | gzip > ~/logcat.txt.gz - artifact push job ~/logcat.txt.gz + # store full logcat log + echo "Collect logcat logs as logcat.txt.gz for $SEMAPHORE_JOB_NAME" + adb logcat -d | gzip > "$HOME/logcat.txt.gz" + artifact push job "$HOME/logcat.txt.gz" - # store the device's screenshot. it may help to debug a failure - echo "Store the device's screenshot for $SEMAPHORE_JOB_NAME" - adb shell screencap -p /sdcard/screencap.png - adb pull "/sdcard/screencap.png" - artifact push job screencap.png + echo "Store the device's screenshot for $SEMAPHORE_JOB_NAME" + if adb shell screencap -p /sdcard/screencap.png; then + if adb pull "/sdcard/screencap.png"; then + artifact push job screencap.png + else + echo "Could not pull screencap.png" + fi + else + echo "Could not create screencap.png" + fi fi - diff --git a/script/ci-instrumentation-tests-enterprise.sh b/script/ci-instrumentation-tests-enterprise.sh index ca8bcc384..69b49d314 100755 --- a/script/ci-instrumentation-tests-enterprise.sh +++ b/script/ci-instrumentation-tests-enterprise.sh @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env bash # # © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com # Contributors: denbond7 # -./gradlew --console=plain --no-daemon --build-cache --max-workers=2 --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ +set -euo pipefail + +./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.EnterpriseTestsFilter diff --git a/script/ci-instrumentation-tests-flaky.sh b/script/ci-instrumentation-tests-flaky.sh index 9fd35ced3..13ec29982 100755 --- a/script/ci-instrumentation-tests-flaky.sh +++ b/script/ci-instrumentation-tests-flaky.sh @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env bash # # © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com # Contributors: denbond7 # +set -euo pipefail + ./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.ReadyForCIAndFlakyFilter diff --git a/script/ci-instrumentation-tests-with-mailserver.sh b/script/ci-instrumentation-tests-with-mailserver.sh index 62f0184e0..b33bfbd0c 100755 --- a/script/ci-instrumentation-tests-with-mailserver.sh +++ b/script/ci-instrumentation-tests-with-mailserver.sh @@ -1,10 +1,12 @@ -#!/bin/bash +#!/usr/bin/env bash # # © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com # Contributors: denbond7 # +set -euo pipefail + #print test names #adb shell am instrument -r -w \ # -e filter com.flowcrypt.email.junit.filters.DependsOnMailServerFilter \ diff --git a/script/ci-instrumentation-tests-without-mailserver.sh b/script/ci-instrumentation-tests-without-mailserver.sh index acdc12b48..a78c53e56 100755 --- a/script/ci-instrumentation-tests-without-mailserver.sh +++ b/script/ci-instrumentation-tests-without-mailserver.sh @@ -1,40 +1,39 @@ -#!/bin/bash +#!/usr/bin/env bash # # © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com # Contributors: denbond7 # -if [[ -z "$1" ]]; - then +set -euo pipefail + +if [[ -z "${1:-}" ]]; then echo "numShards is unset or set to the empty string" exit 1 - else - varNumShards=$1 fi -if [[ -z "$2" ]]; - then +if [[ -z "${2:-}" ]]; then echo "shardIndex is unset or set to the empty string" exit 1 - else - varShardIndex=$2 fi -if [[ ${varShardIndex} -ge ${varNumShards} ]] - then +numShards="$1" +shardIndex="$2" + +if (( shardIndex >= numShards )); then echo "shardIndex should be lower than numShards" - else - #print test names - #adb shell am instrument -w \ - # -e filter com.flowcrypt.email.junit.filters.DoesNotNeedMailServerFilter \ - # -e numShards 3 \ - # -e shardIndex 1 \ - # -e log true \ - # com.flowcrypt.email.debug.test/androidx.test.runner.AndroidJUnitRunner - - ./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedConsumerUiTestsAndroidTest \ - -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.DoesNotNeedMailServerFilter \ - -Pandroid.testInstrumentationRunnerArguments.numShards="${varNumShards}" \ - -Pandroid.testInstrumentationRunnerArguments.shardIndex="${varShardIndex}" + exit 1 fi + +#print test names +#adb shell am instrument -w \ +# -e filter com.flowcrypt.email.junit.filters.DoesNotNeedMailServerFilter \ +# -e numShards 3 \ +# -e shardIndex 1 \ +# -e log true \ +# com.flowcrypt.email.debug.test/androidx.test.runner.AndroidJUnitRunner + +./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedConsumerUiTestsAndroidTest \ + -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.DoesNotNeedMailServerFilter \ + -Pandroid.testInstrumentationRunnerArguments.numShards="${numShards}" \ + -Pandroid.testInstrumentationRunnerArguments.shardIndex="${shardIndex}" diff --git a/script/ci-junit-tests.sh b/script/ci-junit-tests.sh index eeafac333..a07519f3a 100755 --- a/script/ci-junit-tests.sh +++ b/script/ci-junit-tests.sh @@ -1,8 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # # © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com # Contributors: denbond7 # +set -euo pipefail + ./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:testConsumerUiTestsUnitTest diff --git a/script/ci-lint-checks.sh b/script/ci-lint-checks.sh index 28d447c36..88c328aee 100755 --- a/script/ci-lint-checks.sh +++ b/script/ci-lint-checks.sh @@ -1,8 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # # © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com # Contributors: denbond7 # +set -euo pipefail + ./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:lintConsumerUiTests diff --git a/script/ci-publish-test-results.sh b/script/ci-publish-test-results.sh index 4c6622657..d5c21df8c 100755 --- a/script/ci-publish-test-results.sh +++ b/script/ci-publish-test-results.sh @@ -1,12 +1,26 @@ -#!/bin/bash +#!/usr/bin/env bash + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -euo pipefail if [[ "$SEMAPHORE_JOB_NAME" =~ ^Instrumentation.* ]]; then - # publish test results for Instrumentation tests - test-results publish ~/git/flowcrypt-android/FlowCrypt/build/outputs/androidTest-results/connected/ --name "$SEMAPHORE_JOB_NAME" + results_dir="$HOME/git/flowcrypt-android/FlowCrypt/build/outputs/androidTest-results/connected/" + if [[ -d "$results_dir" ]]; then + test-results publish "$results_dir" --name "$SEMAPHORE_JOB_NAME" + else + echo "Instrumentation test results directory does not exist: $results_dir" + fi fi if [[ "$SEMAPHORE_JOB_NAME" =~ ^JUnit.* ]]; then - # publish test results for JUnit tests - test-results publish ~/git/flowcrypt-android/FlowCrypt/build/test-results/ --name "$SEMAPHORE_JOB_NAME" + results_dir="$HOME/git/flowcrypt-android/FlowCrypt/build/test-results/" + if [[ -d "$results_dir" ]]; then + test-results publish "$results_dir" --name "$SEMAPHORE_JOB_NAME" + else + echo "JUnit test results directory does not exist: $results_dir" + fi fi - diff --git a/script/ci-setup-and-run-emulator.sh b/script/ci-setup-and-run-emulator.sh index 6df16d71f..368857243 100755 --- a/script/ci-setup-and-run-emulator.sh +++ b/script/ci-setup-and-run-emulator.sh @@ -1,15 +1,45 @@ -#!/bin/bash +#!/usr/bin/env bash # # © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com # Contributors: denbond7 # +set -euo pipefail + +AVD_NAME="ci-emulator" +SYSTEM_IMAGE="system-images;android-36;google_apis;x86_64" +DEVICE_NAME="pixel_9" +ABI="google_apis/x86_64" + "$ANDROID_HOME/emulator/emulator" -accel-check + +# Keep the emulator fresh for every CI job. Do not reuse AVD state. +rm -rf "$HOME/.android/avd/${AVD_NAME}.avd" "$HOME/.android/avd/${AVD_NAME}.ini" + avdmanager list devices #debug -echo -ne '\n' | avdmanager -v create avd --name ci-emulator --package "system-images;android-36;google_apis;x86_64" --device 'pixel_9' --abi 'google_apis/x86_64' -cat ~/.android/avd/ci-emulator.avd/config.ini -# echo "hw.ramSize=3064" >> ~/.android/avd/ci-emulator.avd/config.ini -# cat ~/.android/avd/ci-emulator.avd/config.ini + +echo -ne '\n' | avdmanager -v create avd \ + --name "$AVD_NAME" \ + --package "$SYSTEM_IMAGE" \ + --device "$DEVICE_NAME" \ + --abi "$ABI" + +# Keep RAM modest for e2-standard-2. This file belongs to the fresh AVD created above. +echo "hw.ramSize=2048" >> "$HOME/.android/avd/${AVD_NAME}.avd/config.ini" +cat "$HOME/.android/avd/${AVD_NAME}.avd/config.ini" + "$ANDROID_HOME/emulator/emulator" -list-avds #debug -"$ANDROID_HOME/emulator/emulator" -avd ci-emulator -no-snapshot -no-window -no-boot-anim -no-audio -gpu auto -read-only -no-metrics & + +"$ANDROID_HOME/emulator/emulator" \ + -avd "$AVD_NAME" \ + -no-window \ + -no-boot-anim \ + -no-audio \ + -no-snapshot \ + -no-snapshot-load \ + -no-snapshot-save \ + -wipe-data \ + -gpu swiftshader_indirect \ + -read-only \ + -no-metrics & diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index 6e622f643..316c70640 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -1,35 +1,50 @@ -#!/bin/bash +#!/usr/bin/env bash + # # © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com # Contributors: denbond7 # +set -euo pipefail set -o xtrace -adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;' -adb shell wm dismiss-keyguard -sleep 1 + +adb wait-for-device +adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' +adb shell wm dismiss-keyguard || true + 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 ################################################################################################### -# to test WKD we need to route all traffic for localhost:443 to localhost:1212 +# To test WKD we need to route all traffic for localhost:443 to localhost:1212 # as we can't use 443 directly for a mock web server. +################################################################################################### adb root -# Need wait for the root environment -sleep 20 +adb wait-for-device +adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' + adb shell "echo 1 > /proc/sys/net/ipv4/ip_forward" adb shell "iptables -t nat -A PREROUTING -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" adb shell "iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" -################################################################################################### # https://developer.android.com/tools/adb#forwardports -# forwards requests on a specific host port to a different port on a device. -# It can be helpful for debugging a mock web server +# Forwards requests on a specific host port to a different port on a device. adb forward tcp:1212 tcp:1212 -#check the emulator has internet connection -adb shell "ping -c 1 www.google.com" +# Check that the emulator has internet connection. +for attempt in {1..5}; do + if adb shell "ping -c 1 www.google.com"; then + break + fi + + if [[ "$attempt" -eq 5 ]]; then + echo "Emulator has no internet connection" + exit 1 + fi + + sleep 2 +done echo "Emulator is ready" set +o xtrace From 3ffc7824bf7d801777d3d12b37b09c07f1dbc45d Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 17:41:16 +0300 Subject: [PATCH 06/56] Removed --max-workers=2 from CI scripts and semaphore configuration --- .semaphore/semaphore.yml | 2 +- script/ci-instrumentation-tests-enterprise.sh | 2 +- script/ci-instrumentation-tests-flaky.sh | 2 +- script/ci-instrumentation-tests-with-mailserver.sh | 2 +- script/ci-instrumentation-tests-without-mailserver.sh | 2 +- script/ci-junit-tests.sh | 2 +- script/ci-lint-checks.sh | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 48109cb59..43e681f6c 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -80,7 +80,7 @@ blocks: # print Java version - java -version # compile project - - ./gradlew --console=plain --no-daemon --build-cache --max-workers=2 assembleConsumerUiTests + - ./gradlew --console=plain --no-daemon --build-cache assembleConsumerUiTests epilogue: on_pass: commands: diff --git a/script/ci-instrumentation-tests-enterprise.sh b/script/ci-instrumentation-tests-enterprise.sh index 69b49d314..ff179352e 100755 --- a/script/ci-instrumentation-tests-enterprise.sh +++ b/script/ci-instrumentation-tests-enterprise.sh @@ -7,5 +7,5 @@ set -euo pipefail -./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ +./gradlew --console=plain --no-daemon --build-cache :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.EnterpriseTestsFilter diff --git a/script/ci-instrumentation-tests-flaky.sh b/script/ci-instrumentation-tests-flaky.sh index 13ec29982..26f122293 100755 --- a/script/ci-instrumentation-tests-flaky.sh +++ b/script/ci-instrumentation-tests-flaky.sh @@ -7,5 +7,5 @@ set -euo pipefail -./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ +./gradlew --console=plain --no-daemon --build-cache :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.ReadyForCIAndFlakyFilter diff --git a/script/ci-instrumentation-tests-with-mailserver.sh b/script/ci-instrumentation-tests-with-mailserver.sh index b33bfbd0c..64f0d2998 100755 --- a/script/ci-instrumentation-tests-with-mailserver.sh +++ b/script/ci-instrumentation-tests-with-mailserver.sh @@ -13,5 +13,5 @@ set -euo pipefail # -e log true \ # com.flowcrypt.email.debug.test/androidx.test.runner.AndroidJUnitRunner -./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedConsumerUiTestsAndroidTest \ +./gradlew --console=plain --no-daemon --build-cache :FlowCrypt:connectedConsumerUiTestsAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.DependsOnMailServerFilter diff --git a/script/ci-instrumentation-tests-without-mailserver.sh b/script/ci-instrumentation-tests-without-mailserver.sh index a78c53e56..b904c77ac 100755 --- a/script/ci-instrumentation-tests-without-mailserver.sh +++ b/script/ci-instrumentation-tests-without-mailserver.sh @@ -33,7 +33,7 @@ fi # -e log true \ # com.flowcrypt.email.debug.test/androidx.test.runner.AndroidJUnitRunner -./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:connectedConsumerUiTestsAndroidTest \ +./gradlew --console=plain --no-daemon --build-cache :FlowCrypt:connectedConsumerUiTestsAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.DoesNotNeedMailServerFilter \ -Pandroid.testInstrumentationRunnerArguments.numShards="${numShards}" \ -Pandroid.testInstrumentationRunnerArguments.shardIndex="${shardIndex}" diff --git a/script/ci-junit-tests.sh b/script/ci-junit-tests.sh index a07519f3a..31b13e0b7 100755 --- a/script/ci-junit-tests.sh +++ b/script/ci-junit-tests.sh @@ -7,4 +7,4 @@ set -euo pipefail -./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:testConsumerUiTestsUnitTest +./gradlew --console=plain --no-daemon --build-cache :FlowCrypt:testConsumerUiTestsUnitTest diff --git a/script/ci-lint-checks.sh b/script/ci-lint-checks.sh index 88c328aee..dab6d43eb 100755 --- a/script/ci-lint-checks.sh +++ b/script/ci-lint-checks.sh @@ -7,4 +7,4 @@ set -euo pipefail -./gradlew --console=plain --no-daemon --build-cache --max-workers=2 :FlowCrypt:lintConsumerUiTests +./gradlew --console=plain --no-daemon --build-cache :FlowCrypt:lintConsumerUiTests From 66a468b35ec96a1cd682a05d1f5abe3ccd40c9cf Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 17:47:57 +0300 Subject: [PATCH 07/56] Removed native and redundant build caches from Semaphore configuration --- .semaphore/semaphore.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 43e681f6c..012d63274 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -50,19 +50,12 @@ global_job_config: - export SDK_SUM=$(checksum ./script/ci-install-android-sdk.sh) - export APP_GRADLE_CACHE=project-gradle-cache-$GRADLE_SUM # project .gradle cache, per Gradle config hash - export ANDROID_SDK_CACHE=android-sdk-$SDK_SUM # Android SDK cache, per SDK installer hash - - export BUILD_CXX_CACHE=build-cxx-cache-$SEMAPHORE_GIT_BRANCH-$GRADLE_SUM # per branch and Gradle config hash - - export BUILD_NATIVE_CACHE=build-native-cache-$SEMAPHORE_GIT_BRANCH-$GRADLE_SUM # per branch and Gradle config hash # restore app caches - cache restore $APP_GRADLE_CACHE - - cache restore $BUILD_CXX_CACHE - - cache restore $BUILD_NATIVE_CACHE # restore global caches - cache restore $ANDROID_SDK_CACHE - cache restore gradle-wrapper - cache restore gradle-cache-$GRADLE_SUM - - cache restore gradle-cache - - cache restore android-build-cache-$GRADLE_SUM - - cache restore android-build-cache # Install Android dependencies if needed - ./script/ci-install-android-sdk.sh # Install ninja @@ -87,8 +80,6 @@ blocks: # store app cache - echo "Store the app cache" - cache has_key $APP_GRADLE_CACHE || cache store $APP_GRADLE_CACHE .gradle - - cache has_key $BUILD_CXX_CACHE || cache store $BUILD_CXX_CACHE FlowCrypt/.cxx - - cache has_key $BUILD_NATIVE_CACHE || cache store $BUILD_NATIVE_CACHE FlowCrypt/.externalNativeBuild # clean and store global cache - echo "Store the global cache" @@ -96,7 +87,6 @@ blocks: - cache has_key $ANDROID_SDK_CACHE || cache store $ANDROID_SDK_CACHE $ANDROID_HOME - cache has_key gradle-wrapper || cache store gradle-wrapper ~/.gradle/wrapper - cache has_key gradle-cache-$GRADLE_SUM || cache store gradle-cache-$GRADLE_SUM ~/.gradle/caches - - cache has_key android-build-cache-$GRADLE_SUM || cache store android-build-cache-$GRADLE_SUM ~/.android/build-cache - name: 'Testing' task: From c3a92ab2a47d42c4a6e86e365130bf6ca00d5a45 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 18:09:44 +0300 Subject: [PATCH 08/56] Removed ninja-build installation from semaphore.yml --- .semaphore/semaphore.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 012d63274..62c0ecb62 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -58,8 +58,6 @@ global_job_config: - cache restore gradle-cache-$GRADLE_SUM # Install Android dependencies if needed - ./script/ci-install-android-sdk.sh - # Install ninja - - sudo apt install -y ninja-build blocks: - name: 'Build' execution_time_limit: From 7b7ae48f0f1ea46b76cf44610e7e845a07e9693c Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 18:32:09 +0300 Subject: [PATCH 09/56] Configured emulator DNS server and added resolution checks in CI scripts --- script/ci-setup-and-run-emulator.sh | 3 ++- script/ci-wait-for-emulator.sh | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/script/ci-setup-and-run-emulator.sh b/script/ci-setup-and-run-emulator.sh index 368857243..59ace1392 100755 --- a/script/ci-setup-and-run-emulator.sh +++ b/script/ci-setup-and-run-emulator.sh @@ -42,4 +42,5 @@ cat "$HOME/.android/avd/${AVD_NAME}.avd/config.ini" -wipe-data \ -gpu swiftshader_indirect \ -read-only \ - -no-metrics & + -no-metrics \ + -dns-server 10.0.2.2 & diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index 316c70640..8c058edd5 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -32,6 +32,10 @@ adb shell "iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT # Forwards requests on a specific host port to a different port on a device. adb forward tcp:1212 tcp:1212 +# Print DNS configuration for easier CI debugging. +adb shell getprop net.dns1 || true +adb shell getprop net.dns2 || true + # Check that the emulator has internet connection. for attempt in {1..5}; do if adb shell "ping -c 1 www.google.com"; then @@ -46,5 +50,21 @@ for attempt in {1..5}; do sleep 2 done +# Check that Android emulator can resolve FlowCrypt test domains. +# Host-side dnsmasq resolving is not enough: the emulator has its own DNS configuration. +for attempt in {1..5}; do + if adb shell "ping -c 1 fes.flowcrypt.test"; then + break + fi + + if [[ "$attempt" -eq 5 ]]; then + echo "Emulator can't resolve fes.flowcrypt.test" + echo "Make sure ci-setup-and-run-emulator.sh starts emulator with: -dns-server 10.0.2.2" + exit 1 + fi + + sleep 2 +done + echo "Emulator is ready" -set +o xtrace +set +o xtrace \ No newline at end of file From b7704e1ee86246d50a3ac16da18db31f9f87f95b Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 18:40:25 +0300 Subject: [PATCH 10/56] Replaced -dns-server flag with /etc/hosts entries in emulator CI scripts --- script/ci-setup-and-run-emulator.sh | 3 +-- script/ci-wait-for-emulator.sh | 8 +++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/script/ci-setup-and-run-emulator.sh b/script/ci-setup-and-run-emulator.sh index 59ace1392..368857243 100755 --- a/script/ci-setup-and-run-emulator.sh +++ b/script/ci-setup-and-run-emulator.sh @@ -42,5 +42,4 @@ cat "$HOME/.android/avd/${AVD_NAME}.avd/config.ini" -wipe-data \ -gpu swiftshader_indirect \ -read-only \ - -no-metrics \ - -dns-server 10.0.2.2 & + -no-metrics & diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index 8c058edd5..66b8f8544 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -21,6 +21,13 @@ adb shell settings put global animator_duration_scale 0 # as we can't use 443 directly for a mock web server. ################################################################################################### adb root + +adb remount + +adb shell 'echo "10.0.2.2 fes.flowcrypt.test" >> /system/etc/hosts' +adb shell 'echo "10.0.2.2 fel.flowcrypt.test" >> /system/etc/hosts' +adb shell 'echo "10.0.2.2 fel.localhost" >> /system/etc/hosts' + adb wait-for-device adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' @@ -59,7 +66,6 @@ for attempt in {1..5}; do if [[ "$attempt" -eq 5 ]]; then echo "Emulator can't resolve fes.flowcrypt.test" - echo "Make sure ci-setup-and-run-emulator.sh starts emulator with: -dns-server 10.0.2.2" exit 1 fi From b1fbe04025e4d28e1896502dcffd3198373fb03d Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 18:46:32 +0300 Subject: [PATCH 11/56] Removed hosts file modifications and adb remount from ci-wait-for-emulator.sh --- script/ci-wait-for-emulator.sh | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index 66b8f8544..0d94995d4 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -9,6 +9,7 @@ set -euo pipefail set -o xtrace adb wait-for-device +# shellcheck disable=SC2016 adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' adb shell wm dismiss-keyguard || true @@ -21,14 +22,8 @@ adb shell settings put global animator_duration_scale 0 # as we can't use 443 directly for a mock web server. ################################################################################################### adb root - -adb remount - -adb shell 'echo "10.0.2.2 fes.flowcrypt.test" >> /system/etc/hosts' -adb shell 'echo "10.0.2.2 fel.flowcrypt.test" >> /system/etc/hosts' -adb shell 'echo "10.0.2.2 fel.localhost" >> /system/etc/hosts' - adb wait-for-device +# shellcheck disable=SC2016 adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' adb shell "echo 1 > /proc/sys/net/ipv4/ip_forward" From 50a6484bf624a952104a40fcf491e75aba69011b Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 18:56:33 +0300 Subject: [PATCH 12/56] Updated ci-wait-for-emulator.sh to restart adb if root fails --- script/ci-wait-for-emulator.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index 0d94995d4..ee2cf5949 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -21,7 +21,21 @@ adb shell settings put global animator_duration_scale 0 # To test WKD we need to route all traffic for localhost:443 to localhost:1212 # as we can't use 443 directly for a mock web server. ################################################################################################### -adb root + +adb root || { + echo "adb root failed, restarting adb and waiting for emulator..." + + adb kill-server || true + adb start-server + + adb wait-for-device + + # shellcheck disable=SC2016 + adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' +} + +sleep 2 + adb wait-for-device # shellcheck disable=SC2016 adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' From affd17ddc550368828dde01f114873dad8eac0a3 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 19:02:47 +0300 Subject: [PATCH 13/56] Updated ci-setup-and-run-emulator.sh to use avdmanager for AVD deletion --- script/ci-setup-and-run-emulator.sh | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/script/ci-setup-and-run-emulator.sh b/script/ci-setup-and-run-emulator.sh index 368857243..ad6d0c819 100755 --- a/script/ci-setup-and-run-emulator.sh +++ b/script/ci-setup-and-run-emulator.sh @@ -14,10 +14,19 @@ ABI="google_apis/x86_64" "$ANDROID_HOME/emulator/emulator" -accel-check -# Keep the emulator fresh for every CI job. Do not reuse AVD state. -rm -rf "$HOME/.android/avd/${AVD_NAME}.avd" "$HOME/.android/avd/${AVD_NAME}.ini" +# Keep the emulator fresh for every CI job. +# Remove existing AVD completely before creating a new one. +if avdmanager list avd | grep -q "Name: ${AVD_NAME}"; then + echo "Removing existing AVD: ${AVD_NAME}" -avdmanager list devices #debug + avdmanager delete avd --name "$AVD_NAME" || true +fi + +rm -rf \ + "$HOME/.android/avd/${AVD_NAME}.avd" \ + "$HOME/.android/avd/${AVD_NAME}.ini" + +#avdmanager list devices # debug echo -ne '\n' | avdmanager -v create avd \ --name "$AVD_NAME" \ @@ -25,11 +34,13 @@ echo -ne '\n' | avdmanager -v create avd \ --device "$DEVICE_NAME" \ --abi "$ABI" -# Keep RAM modest for e2-standard-2. This file belongs to the fresh AVD created above. +# Keep RAM modest for e2-standard-2. +# This file belongs to the fresh AVD created above. echo "hw.ramSize=2048" >> "$HOME/.android/avd/${AVD_NAME}.avd/config.ini" + cat "$HOME/.android/avd/${AVD_NAME}.avd/config.ini" -"$ANDROID_HOME/emulator/emulator" -list-avds #debug +#"$ANDROID_HOME/emulator/emulator" -list-avds # debug "$ANDROID_HOME/emulator/emulator" \ -avd "$AVD_NAME" \ From f60401a4f2fd713af05025664ec995a98a880904 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 19:05:15 +0300 Subject: [PATCH 14/56] Commented out debug logging in ci-setup-and-run-emulator.sh --- script/ci-setup-and-run-emulator.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/ci-setup-and-run-emulator.sh b/script/ci-setup-and-run-emulator.sh index ad6d0c819..710858b7a 100755 --- a/script/ci-setup-and-run-emulator.sh +++ b/script/ci-setup-and-run-emulator.sh @@ -38,7 +38,7 @@ echo -ne '\n' | avdmanager -v create avd \ # This file belongs to the fresh AVD created above. echo "hw.ramSize=2048" >> "$HOME/.android/avd/${AVD_NAME}.avd/config.ini" -cat "$HOME/.android/avd/${AVD_NAME}.avd/config.ini" +#cat "$HOME/.android/avd/${AVD_NAME}.avd/config.ini"# debug #"$ANDROID_HOME/emulator/emulator" -list-avds # debug From 2de6140b445ba6fc87092b08133bac9cf3a3dfc5 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 19:14:02 +0300 Subject: [PATCH 15/56] Added DNS initialization check to ci-wait-for-emulator.sh --- script/ci-wait-for-emulator.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index ee2cf5949..820a48868 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -40,6 +40,26 @@ adb wait-for-device # shellcheck disable=SC2016 adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' +# Give Android connectivity service time to restore DNS after adb root/adbd restart. +for attempt in {1..30}; do + DNS1="$(adb shell getprop net.dns1 | tr -d '\r' || true)" + DNS2="$(adb shell getprop net.dns2 | tr -d '\r' || true)" + + echo "DNS attempt ${attempt}: net.dns1=${DNS1}, net.dns2=${DNS2}" + + if [[ -n "$DNS1" || -n "$DNS2" ]]; then + break + fi + + if [[ "$attempt" -eq 30 ]]; then + echo "Emulator DNS was not initialized after adb root" + adb shell dumpsys connectivity || true + exit 1 + fi + + sleep 2 +done + adb shell "echo 1 > /proc/sys/net/ipv4/ip_forward" adb shell "iptables -t nat -A PREROUTING -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" adb shell "iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" From 3b48b9db1514c7300ab6f15e30f8516e2c9a8302 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 19:18:12 +0300 Subject: [PATCH 16/56] Updated ci-wait-for-emulator.sh to increase ping attempts and remove DNS property checks --- script/ci-wait-for-emulator.sh | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index 820a48868..1b1835fca 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -40,26 +40,6 @@ adb wait-for-device # shellcheck disable=SC2016 adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' -# Give Android connectivity service time to restore DNS after adb root/adbd restart. -for attempt in {1..30}; do - DNS1="$(adb shell getprop net.dns1 | tr -d '\r' || true)" - DNS2="$(adb shell getprop net.dns2 | tr -d '\r' || true)" - - echo "DNS attempt ${attempt}: net.dns1=${DNS1}, net.dns2=${DNS2}" - - if [[ -n "$DNS1" || -n "$DNS2" ]]; then - break - fi - - if [[ "$attempt" -eq 30 ]]; then - echo "Emulator DNS was not initialized after adb root" - adb shell dumpsys connectivity || true - exit 1 - fi - - sleep 2 -done - adb shell "echo 1 > /proc/sys/net/ipv4/ip_forward" adb shell "iptables -t nat -A PREROUTING -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" adb shell "iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" @@ -73,12 +53,12 @@ adb shell getprop net.dns1 || true adb shell getprop net.dns2 || true # Check that the emulator has internet connection. -for attempt in {1..5}; do +for attempt in {1..30}; do if adb shell "ping -c 1 www.google.com"; then break fi - if [[ "$attempt" -eq 5 ]]; then + if [[ "$attempt" -eq 30 ]]; then echo "Emulator has no internet connection" exit 1 fi @@ -88,12 +68,12 @@ done # Check that Android emulator can resolve FlowCrypt test domains. # Host-side dnsmasq resolving is not enough: the emulator has its own DNS configuration. -for attempt in {1..5}; do +for attempt in {1..30}; do if adb shell "ping -c 1 fes.flowcrypt.test"; then break fi - if [[ "$attempt" -eq 5 ]]; then + if [[ "$attempt" -eq 30 ]]; then echo "Emulator can't resolve fes.flowcrypt.test" exit 1 fi From 0352bb8bd1e645b8348b476e680e74f95a9a530c Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 20:01:39 +0300 Subject: [PATCH 17/56] Added network diagnostic logging and improved ping verification in ci-wait-for-emulator.sh --- script/ci-wait-for-emulator.sh | 99 +++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 42 deletions(-) diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index 1b1835fca..ea2c22626 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -8,15 +8,60 @@ set -euo pipefail set -o xtrace -adb wait-for-device -# shellcheck disable=SC2016 -adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' +wait_for_boot_completed() { + adb wait-for-device + + # shellcheck disable=SC2016 + adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' +} + +print_network_debug() { + local label="$1" + + echo "========== Network debug: ${label} ==========" + + adb devices || true + adb shell getprop net.dns1 || true + adb shell getprop net.dns2 || true + adb shell ip route || true + adb shell ip addr || true + adb shell iptables -t nat -S || true + adb forward --list || true + + echo "========== End network debug: ${label} ==========" +} + +check_ping_or_fail() { + local host="$1" + local label="$2" + + for attempt in {1..30}; do + if adb shell "ping -c 1 ${host}"; then + return 0 + fi + + if [[ "$attempt" -eq 30 ]]; then + echo "Failed to ping ${host}: ${label}" + print_network_debug "ping failed: ${host}" + exit 1 + fi + + sleep 2 + done +} + +wait_for_boot_completed + adb shell wm dismiss-keyguard || true 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 +print_network_debug "before adb root" +check_ping_or_fail "www.google.com" "internet before adb root" +check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test before adb root" + ################################################################################################### # To test WKD we need to route all traffic for localhost:443 to localhost:1212 # as we can't use 443 directly for a mock web server. @@ -28,19 +73,18 @@ adb root || { adb kill-server || true adb start-server - adb wait-for-device - - # shellcheck disable=SC2016 - adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' + wait_for_boot_completed } sleep 2 +wait_for_boot_completed -adb wait-for-device -# shellcheck disable=SC2016 -adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' +print_network_debug "after adb root" +check_ping_or_fail "www.google.com" "internet after adb root" +check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test after adb root" adb shell "echo 1 > /proc/sys/net/ipv4/ip_forward" + adb shell "iptables -t nat -A PREROUTING -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" adb shell "iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" @@ -48,38 +92,9 @@ adb shell "iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT # Forwards requests on a specific host port to a different port on a device. adb forward tcp:1212 tcp:1212 -# Print DNS configuration for easier CI debugging. -adb shell getprop net.dns1 || true -adb shell getprop net.dns2 || true - -# Check that the emulator has internet connection. -for attempt in {1..30}; do - if adb shell "ping -c 1 www.google.com"; then - break - fi - - if [[ "$attempt" -eq 30 ]]; then - echo "Emulator has no internet connection" - exit 1 - fi - - sleep 2 -done - -# Check that Android emulator can resolve FlowCrypt test domains. -# Host-side dnsmasq resolving is not enough: the emulator has its own DNS configuration. -for attempt in {1..30}; do - if adb shell "ping -c 1 fes.flowcrypt.test"; then - break - fi - - if [[ "$attempt" -eq 30 ]]; then - echo "Emulator can't resolve fes.flowcrypt.test" - exit 1 - fi - - sleep 2 -done +print_network_debug "after iptables and adb forward" +check_ping_or_fail "www.google.com" "internet after iptables" +check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test after iptables" echo "Emulator is ready" set +o xtrace \ No newline at end of file From 5117b2fa3ae276f5ce5d65e60a85534bbeba98c3 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 21:34:57 +0300 Subject: [PATCH 18/56] Enabled HTTP logging and updated CI configuration to run debug instrumentation tests --- .semaphore/semaphore.yml | 4 ++-- FlowCrypt/build.gradle.kts | 10 ++++------ .../ui/gmailapi/SearchMessagesGmailApiFlowTest.kt | 2 ++ script/ci-instrumentation-tests-flaky.sh | 2 +- script/ci-wait-for-emulator.sh | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 62c0ecb62..b446e55f1 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -89,7 +89,7 @@ blocks: - name: 'Testing' task: jobs: - - name: 'Instrumentation tests(enterprise)' + - name: 'Instrumentation tests(debug)' execution_time_limit: minutes: 60 commands: @@ -100,7 +100,7 @@ blocks: # Run filtered logging for TestRunner - adb logcat -v color TestRunner:V *:S > ~/logcat_log.txt & # Run instrumentation tests - - ./script/ci-instrumentation-tests-enterprise.sh + - ./script/ci-instrumentation-tests-flaky.sh epilogue: always: diff --git a/FlowCrypt/build.gradle.kts b/FlowCrypt/build.gradle.kts index e8179f29a..6f37c4d0f 100644 --- a/FlowCrypt/build.gradle.kts +++ b/FlowCrypt/build.gradle.kts @@ -6,8 +6,6 @@ import com.android.build.api.artifact.SingleArtifact import com.android.build.api.variant.ResValue -import org.gradle.api.GradleException -import java.io.File import com.android.ddmlib.DdmPreferences import java.io.FileInputStream import java.text.SimpleDateFormat @@ -127,10 +125,10 @@ android { "SHARED_TENANT_FES_URL", "\"https://flowcrypt.test/shared-tenant-fes/\"" ) - buildConfigField("boolean", "IS_HTTP_LOG_ENABLED", "false") - buildConfigField("String", "HTTP_LOG_LEVEL", "\"NONE\"") - resValue("string", "gradle_is_http_log_enabled", "false") - resValue("string", "gradle_http_log_level", "NONE") + buildConfigField("boolean", "IS_HTTP_LOG_ENABLED", "true") + buildConfigField("String", "HTTP_LOG_LEVEL", "\"BODY\"") + resValue("string", "gradle_is_http_log_enabled", "true") + resValue("string", "gradle_http_log_level", "BODY") } } diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/SearchMessagesGmailApiFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/SearchMessagesGmailApiFlowTest.kt index cc7ff3377..9b3cd5895 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/SearchMessagesGmailApiFlowTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/SearchMessagesGmailApiFlowTest.kt @@ -20,6 +20,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import com.flowcrypt.email.R import com.flowcrypt.email.TestConstants +import com.flowcrypt.email.junit.annotations.DebugTest import com.flowcrypt.email.junit.annotations.FlowCryptTestSettings import com.flowcrypt.email.matchers.CustomMatchers.Companion.withRecyclerViewItemCount import com.flowcrypt.email.rules.ClearAppSettingsRule @@ -49,6 +50,7 @@ import java.util.concurrent.TimeUnit @MediumTest @RunWith(AndroidJUnit4::class) @FlowCryptTestSettings(useCommonIdling = false) +@DebugTest class SearchMessagesGmailApiFlowTest : BaseGmailApiTest() { override val mockWebServerRule = diff --git a/script/ci-instrumentation-tests-flaky.sh b/script/ci-instrumentation-tests-flaky.sh index 26f122293..328a437db 100755 --- a/script/ci-instrumentation-tests-flaky.sh +++ b/script/ci-instrumentation-tests-flaky.sh @@ -8,4 +8,4 @@ set -euo pipefail ./gradlew --console=plain --no-daemon --build-cache :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ - -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.ReadyForCIAndFlakyFilter + -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.DebugTestsFilter diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index ea2c22626..be807eb02 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -35,7 +35,7 @@ check_ping_or_fail() { local host="$1" local label="$2" - for attempt in {1..30}; do + for attempt in {1..10}; do if adb shell "ping -c 1 ${host}"; then return 0 fi @@ -43,7 +43,7 @@ check_ping_or_fail() { if [[ "$attempt" -eq 30 ]]; then echo "Failed to ping ${host}: ${label}" print_network_debug "ping failed: ${host}" - exit 1 + #exit 1 fi sleep 2 From 6c80d2e0dd1c0edd3501b4f9ada75f22ad1eb09a Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sun, 10 May 2026 22:26:55 +0300 Subject: [PATCH 19/56] Refactored ci-wait-for-emulator.sh to improve network setup and verification --- script/ci-wait-for-emulator.sh | 78 +++++++++++++++++----------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index be807eb02..ae06bea29 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -15,41 +15,35 @@ wait_for_boot_completed() { adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' } -print_network_debug() { - local label="$1" - - echo "========== Network debug: ${label} ==========" - - adb devices || true - adb shell getprop net.dns1 || true - adb shell getprop net.dns2 || true - adb shell ip route || true - adb shell ip addr || true - adb shell iptables -t nat -S || true - adb forward --list || true - - echo "========== End network debug: ${label} ==========" -} - check_ping_or_fail() { local host="$1" local label="$2" - for attempt in {1..10}; do + for attempt in {1..30}; do if adb shell "ping -c 1 ${host}"; then return 0 fi if [[ "$attempt" -eq 30 ]]; then echo "Failed to ping ${host}: ${label}" - print_network_debug "ping failed: ${host}" - #exit 1 + exit 1 fi sleep 2 done } +check_iptables_rule_or_fail() { + local chain="$1" + local expected_rule="$2" + + if ! adb shell "iptables -t nat -S ${chain}" | grep -F -- "${expected_rule}"; then + echo "iptables rule was not applied:" + echo "${expected_rule}" + exit 1 + fi +} + wait_for_boot_completed adb shell wm dismiss-keyguard || true @@ -58,43 +52,51 @@ 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 -print_network_debug "before adb root" -check_ping_or_fail "www.google.com" "internet before adb root" -check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test before adb root" - ################################################################################################### # To test WKD we need to route all traffic for localhost:443 to localhost:1212 # as we can't use 443 directly for a mock web server. ################################################################################################### - -adb root || { +if adb root; then + echo "adb root succeeded" +else echo "adb root failed, restarting adb and waiting for emulator..." adb kill-server || true adb start-server +fi - wait_for_boot_completed -} - -sleep 2 wait_for_boot_completed -print_network_debug "after adb root" -check_ping_or_fail "www.google.com" "internet after adb root" -check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test after adb root" - adb shell "echo 1 > /proc/sys/net/ipv4/ip_forward" -adb shell "iptables -t nat -A PREROUTING -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" -adb shell "iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" +adb shell "iptables -t nat -D OUTPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1212" || true +adb shell "iptables -t nat -D PREROUTING -s 127.0.0.1/32 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1212" || true + +adb shell "iptables -t nat -A PREROUTING -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to-ports 1212" +adb shell "iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to-ports 1212" + +check_iptables_rule_or_fail \ + "PREROUTING" \ + "-A PREROUTING -s 127.0.0.1/32 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1212" + +check_iptables_rule_or_fail \ + "OUTPUT" \ + "-A OUTPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1212" +################################################################################################### # https://developer.android.com/tools/adb#forwardports # Forwards requests on a specific host port to a different port on a device. +# It can be helpful for debugging a mock web server +adb forward --remove tcp:1212 || true adb forward tcp:1212 tcp:1212 +if ! adb forward --list | grep -F -- "tcp:1212 tcp:1212"; then + echo "adb forward was not applied: tcp:1212 tcp:1212" + exit 1 +fi -print_network_debug "after iptables and adb forward" -check_ping_or_fail "www.google.com" "internet after iptables" -check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test after iptables" +################################################################################################### +check_ping_or_fail "www.google.com" "internet connection" +check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test after iptables and adb forward" echo "Emulator is ready" set +o xtrace \ No newline at end of file From a2f4c8165332cadad054398767b67a5c29fde499 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Mon, 11 May 2026 08:32:35 +0300 Subject: [PATCH 20/56] Added CI failure debug script and updated Semaphore configuration --- .semaphore/semaphore.yml | 2 ++ script/ci-after-fail-debug.sh | 33 +++++++++++++++++++++++++++++++++ script/ci-wait-for-emulator.sh | 1 - 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100755 script/ci-after-fail-debug.sh diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index b446e55f1..ec0b65446 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -115,6 +115,8 @@ blocks: commands: # collect and store debug info as artifacts - ./script/ci-get-and-publish-debug-info-as-artifact.sh + # print debug info + - ./script/ci-after-fail-debug.sh after_pipeline: task: diff --git a/script/ci-after-fail-debug.sh b/script/ci-after-fail-debug.sh new file mode 100755 index 000000000..5d6b49676 --- /dev/null +++ b/script/ci-after-fail-debug.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -euo pipefail +set -o xtrace + +check_ping_or_fail() { + local host="$1" + local label="$2" + + for attempt in {1..30}; do + if adb shell "ping -c 1 ${host}"; then + return 0 + fi + + if [[ "$attempt" -eq 30 ]]; then + echo "Failed to ping ${host}: ${label}" + exit 1 + fi + + sleep 2 + done +} + +################################################################################################### +check_ping_or_fail "www.google.com" "internet connection" +check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test forwarding" + +set +o xtrace \ No newline at end of file diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index ae06bea29..69fbdf567 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -96,7 +96,6 @@ fi ################################################################################################### check_ping_or_fail "www.google.com" "internet connection" -check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test after iptables and adb forward" echo "Emulator is ready" set +o xtrace \ No newline at end of file From 0d1b07f872cbc34dd298eaae6fb53d193412adf1 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sat, 16 May 2026 13:00:06 +0300 Subject: [PATCH 21/56] Add Ubuntu 24.04 Docker image for Android emulator SDK environment --- docker/TestEnvironment/Dockerfile | 66 ++++++++++++++++++++++++++++ docker/TestEnvironment/README.md | 49 +++++++++++++++++++++ docker/TestEnvironment/create-avd.sh | 33 ++++++++++++++ script/ci-install-android-sdk.sh | 23 +++++++--- 4 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 docker/TestEnvironment/Dockerfile create mode 100644 docker/TestEnvironment/README.md create mode 100644 docker/TestEnvironment/create-avd.sh diff --git a/docker/TestEnvironment/Dockerfile b/docker/TestEnvironment/Dockerfile new file mode 100644 index 000000000..22414e131 --- /dev/null +++ b/docker/TestEnvironment/Dockerfile @@ -0,0 +1,66 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive +ARG ANDROID_PLATFORM=android-36 +ARG ANDROID_SYSTEM_IMAGE=system-images;android-36;google_apis;x86_64 +ARG ANDROID_BUILD_TOOLS=36.0.0 + +ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 +ENV ANDROID_HOME=/opt/android-sdk +ENV ANDROID_SDK_ROOT=/opt/android-sdk +ENV ANDROID_AVD_HOME=/opt/android-avd +ENV PATH=/opt/android-sdk/emulator:/opt/android-sdk/platform-tools:/opt/android-sdk/cmdline-tools/latest/bin:/usr/lib/jvm/java-21-openjdk-amd64/bin:${PATH} + +RUN apt-get update && apt-get install -y --no-install-recommends \ + bash \ + ca-certificates \ + curl \ + dnsutils \ + git \ + libasound2t64 \ + libc6 \ + libc6-dev \ + libdbus-1-3 \ + libfontconfig1 \ + libgcc-s1 \ + libgl1 \ + libglu1-mesa \ + libnspr4 \ + libnss3 \ + libpulse0 \ + libstdc++6 \ + libu2f-udev \ + libx11-6 \ + libx11-xcb1 \ + libxcb1 \ + libxcursor1 \ + libxdamage1 \ + libxext6 \ + libxi6 \ + libxkbcommon0 \ + libxrandr2 \ + libxrender1 \ + libxtst6 \ + openjdk-21-jdk \ + sudo \ + unzip \ + wget \ + && rm -rf /var/lib/apt/lists/* + +COPY script/ci-install-android-sdk.sh /tmp/ci-install-android-sdk.sh +RUN chmod +x /tmp/ci-install-android-sdk.sh \ + && mkdir -p "${ANDROID_AVD_HOME}" \ + && ANDROID_PLATFORM="${ANDROID_PLATFORM}" \ + ANDROID_SYSTEM_IMAGE="${ANDROID_SYSTEM_IMAGE}" \ + ANDROID_BUILD_TOOLS="${ANDROID_BUILD_TOOLS}" \ + RUN_KVM_CHECK=0 \ + /tmp/ci-install-android-sdk.sh \ + && sdkmanager --list_installed \ + && rm -f /tmp/ci-install-android-sdk.sh + +COPY docker/TestEnvironment/create-avd.sh /usr/local/bin/create-avd.sh +RUN chmod +x /usr/local/bin/create-avd.sh + +WORKDIR /workspace + +CMD ["/bin/bash"] diff --git a/docker/TestEnvironment/README.md b/docker/TestEnvironment/README.md new file mode 100644 index 000000000..e8ab049f7 --- /dev/null +++ b/docker/TestEnvironment/README.md @@ -0,0 +1,49 @@ +# Test Environment Docker Image + +This directory contains a Docker context for a headless Android test environment based on +`ubuntu:24.04`. + +The image installs: + +- OpenJDK 21 +- Android SDK command-line tools +- `platform-tools` +- Android Emulator +- Android platform `android-36` +- Build tools `36.0.0` +- System image `system-images;android-36;google_apis;x86_64` + +## Build + +```bash +docker build -t flowcrypt-android-test-env -f docker/TestEnvironment/Dockerfile . +``` + +## Create an AVD inside the container + +```bash +docker run --rm -it flowcrypt-android-test-env create-avd.sh +``` + +## Run the emulator inside the container + +The container includes the emulator binaries, but actual emulator launch usually needs: + +- `/dev/kvm` passed through to the container +- additional Docker flags such as `--device /dev/kvm` + +Example: + +```bash +docker run --rm -it \ + --device /dev/kvm \ + flowcrypt-android-test-env \ + bash +``` + +Then inside the container: + +```bash +create-avd.sh +emulator -avd ci-emulator -no-window -no-boot-anim -no-audio -gpu swiftshader_indirect +``` diff --git a/docker/TestEnvironment/create-avd.sh b/docker/TestEnvironment/create-avd.sh new file mode 100644 index 000000000..0fcc57554 --- /dev/null +++ b/docker/TestEnvironment/create-avd.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -euo pipefail + +AVD_NAME="${AVD_NAME:-ci-emulator}" +SYSTEM_IMAGE="${SYSTEM_IMAGE:-system-images;android-36;google_apis;x86_64}" +DEVICE_NAME="${DEVICE_NAME:-pixel_9}" +ABI="${ABI:-google_apis/x86_64}" + +mkdir -p "${ANDROID_AVD_HOME:-$HOME/.android/avd}" "$HOME/.android" +touch "$HOME/.android/repositories.cfg" + +if avdmanager list avd | grep -q "Name: ${AVD_NAME}"; then + avdmanager delete avd --name "${AVD_NAME}" || true +fi + +rm -rf \ + "${ANDROID_AVD_HOME:-$HOME/.android/avd}/${AVD_NAME}.avd" \ + "${ANDROID_AVD_HOME:-$HOME/.android/avd}/${AVD_NAME}.ini" + +echo "no" | avdmanager create avd \ + --force \ + --name "${AVD_NAME}" \ + --package "${SYSTEM_IMAGE}" \ + --device "${DEVICE_NAME}" \ + --abi "${ABI}" + +echo "Created AVD ${AVD_NAME}" diff --git a/script/ci-install-android-sdk.sh b/script/ci-install-android-sdk.sh index 084866507..123fec611 100755 --- a/script/ci-install-android-sdk.sh +++ b/script/ci-install-android-sdk.sh @@ -22,11 +22,15 @@ fi # ----------------------------- export ANDROID_HOME="${ANDROID_HOME:-$HOME/Android/Sdk}" export ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT:-$ANDROID_HOME}" +RUN_KVM_CHECK="${RUN_KVM_CHECK:-1}" +ANDROID_PLATFORM="${ANDROID_PLATFORM:-android-36}" +ANDROID_SYSTEM_IMAGE="${ANDROID_SYSTEM_IMAGE:-system-images;android-36;google_apis;x86_64}" +ANDROID_BUILD_TOOLS="${ANDROID_BUILD_TOOLS:-}" # ----------------------------- # Pin cmdline-tools archive here # ----------------------------- -SDK_ARCHIVE="commandlinetools-linux-14742923_latest.zip" +SDK_ARCHIVE="${SDK_ARCHIVE:-commandlinetools-linux-14742923_latest.zip}" # ------------------------------------------------------------ # Check that SDK_ARCHIVE is the latest Android cmdline-tools @@ -87,15 +91,17 @@ check_cmdline_tools_latest_or_fail # KVM deps (as in your script) # ----------------------------- sudo apt-get -qq install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils > /dev/null -sudo kvm-ok +if [[ "$RUN_KVM_CHECK" == "1" ]]; then + sudo kvm-ok +fi # ----------------------------- # Install SDK if ~/Android doesn't exist (as in your script) # ----------------------------- -if [[ -d "$HOME/Android" ]]; then - echo "$HOME/Android already exists, skipping installation" +if [[ -x "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" ]]; then + echo "$ANDROID_HOME already exists, skipping installation" else - echo "$HOME/Android does not exist, installing" + echo "$ANDROID_HOME does not exist, installing" mkdir -p "$ANDROID_HOME" # download, unpack and remove sdk archive @@ -122,10 +128,13 @@ else # Install Android SDK (echo "yes" | "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" --licenses > /dev/null | grep -v = || true) - ( sleep 5; echo "y" ) | ("${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" "platforms;android-36" > /dev/null | grep -v = || true) + ( sleep 5; echo "y" ) | ("${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" "platforms;${ANDROID_PLATFORM}" > /dev/null | grep -v = || true) ("${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" "platform-tools" | grep -v = || true) ("${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" "emulator" | grep -v = || true) - (echo "y" | "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" "system-images;android-36;google_apis;x86_64" > /dev/null | grep -v = || true) + if [[ -n "$ANDROID_BUILD_TOOLS" ]]; then + ("${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" "build-tools;${ANDROID_BUILD_TOOLS}" | grep -v = || true) + fi + (echo "y" | "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" "${ANDROID_SYSTEM_IMAGE}" > /dev/null | grep -v = || true) fi # Uncomment this for debug From c7ae7df86a686f3b2c61aadfdb223f2ec0ddb1d0 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sat, 16 May 2026 13:08:23 +0300 Subject: [PATCH 22/56] Refactor Android emulator setup into shared scripts --- docker/TestEnvironment/Dockerfile | 6 ++-- docker/TestEnvironment/README.md | 11 ++++-- docker/TestEnvironment/create-avd.sh | 33 ------------------ script/ci-setup-and-run-emulator.sh | 52 +++------------------------- script/create-avd.sh | 40 +++++++++++++++++++++ script/run-emulator.sh | 37 ++++++++++++++++++++ 6 files changed, 94 insertions(+), 85 deletions(-) delete mode 100644 docker/TestEnvironment/create-avd.sh create mode 100755 script/create-avd.sh create mode 100755 script/run-emulator.sh diff --git a/docker/TestEnvironment/Dockerfile b/docker/TestEnvironment/Dockerfile index 22414e131..2ea9f705b 100644 --- a/docker/TestEnvironment/Dockerfile +++ b/docker/TestEnvironment/Dockerfile @@ -58,8 +58,10 @@ RUN chmod +x /tmp/ci-install-android-sdk.sh \ && sdkmanager --list_installed \ && rm -f /tmp/ci-install-android-sdk.sh -COPY docker/TestEnvironment/create-avd.sh /usr/local/bin/create-avd.sh -RUN chmod +x /usr/local/bin/create-avd.sh +RUN mkdir -p /opt/flowcrypt/scripts +COPY script/create-avd.sh /opt/flowcrypt/scripts/create-avd.sh +COPY script/run-emulator.sh /opt/flowcrypt/scripts/run-emulator.sh +RUN chmod +x /opt/flowcrypt/scripts/create-avd.sh /opt/flowcrypt/scripts/run-emulator.sh WORKDIR /workspace diff --git a/docker/TestEnvironment/README.md b/docker/TestEnvironment/README.md index e8ab049f7..f8a6afe87 100644 --- a/docker/TestEnvironment/README.md +++ b/docker/TestEnvironment/README.md @@ -22,11 +22,16 @@ docker build -t flowcrypt-android-test-env -f docker/TestEnvironment/Dockerfile ## Create an AVD inside the container ```bash -docker run --rm -it flowcrypt-android-test-env create-avd.sh +docker run --rm -it flowcrypt-android-test-env /opt/flowcrypt/scripts/create-avd.sh ``` ## Run the emulator inside the container +The container includes reusable helpers: + +- `/opt/flowcrypt/scripts/create-avd.sh` +- `/opt/flowcrypt/scripts/run-emulator.sh` + The container includes the emulator binaries, but actual emulator launch usually needs: - `/dev/kvm` passed through to the container @@ -44,6 +49,6 @@ docker run --rm -it \ Then inside the container: ```bash -create-avd.sh -emulator -avd ci-emulator -no-window -no-boot-anim -no-audio -gpu swiftshader_indirect +/opt/flowcrypt/scripts/create-avd.sh +/opt/flowcrypt/scripts/run-emulator.sh ``` diff --git a/docker/TestEnvironment/create-avd.sh b/docker/TestEnvironment/create-avd.sh deleted file mode 100644 index 0fcc57554..000000000 --- a/docker/TestEnvironment/create-avd.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -# -# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com -# Contributors: denbond7 -# - -set -euo pipefail - -AVD_NAME="${AVD_NAME:-ci-emulator}" -SYSTEM_IMAGE="${SYSTEM_IMAGE:-system-images;android-36;google_apis;x86_64}" -DEVICE_NAME="${DEVICE_NAME:-pixel_9}" -ABI="${ABI:-google_apis/x86_64}" - -mkdir -p "${ANDROID_AVD_HOME:-$HOME/.android/avd}" "$HOME/.android" -touch "$HOME/.android/repositories.cfg" - -if avdmanager list avd | grep -q "Name: ${AVD_NAME}"; then - avdmanager delete avd --name "${AVD_NAME}" || true -fi - -rm -rf \ - "${ANDROID_AVD_HOME:-$HOME/.android/avd}/${AVD_NAME}.avd" \ - "${ANDROID_AVD_HOME:-$HOME/.android/avd}/${AVD_NAME}.ini" - -echo "no" | avdmanager create avd \ - --force \ - --name "${AVD_NAME}" \ - --package "${SYSTEM_IMAGE}" \ - --device "${DEVICE_NAME}" \ - --abi "${ABI}" - -echo "Created AVD ${AVD_NAME}" diff --git a/script/ci-setup-and-run-emulator.sh b/script/ci-setup-and-run-emulator.sh index 710858b7a..dbdff025b 100755 --- a/script/ci-setup-and-run-emulator.sh +++ b/script/ci-setup-and-run-emulator.sh @@ -7,50 +7,8 @@ set -euo pipefail -AVD_NAME="ci-emulator" -SYSTEM_IMAGE="system-images;android-36;google_apis;x86_64" -DEVICE_NAME="pixel_9" -ABI="google_apis/x86_64" - -"$ANDROID_HOME/emulator/emulator" -accel-check - -# Keep the emulator fresh for every CI job. -# Remove existing AVD completely before creating a new one. -if avdmanager list avd | grep -q "Name: ${AVD_NAME}"; then - echo "Removing existing AVD: ${AVD_NAME}" - - avdmanager delete avd --name "$AVD_NAME" || true -fi - -rm -rf \ - "$HOME/.android/avd/${AVD_NAME}.avd" \ - "$HOME/.android/avd/${AVD_NAME}.ini" - -#avdmanager list devices # debug - -echo -ne '\n' | avdmanager -v create avd \ - --name "$AVD_NAME" \ - --package "$SYSTEM_IMAGE" \ - --device "$DEVICE_NAME" \ - --abi "$ABI" - -# Keep RAM modest for e2-standard-2. -# This file belongs to the fresh AVD created above. -echo "hw.ramSize=2048" >> "$HOME/.android/avd/${AVD_NAME}.avd/config.ini" - -#cat "$HOME/.android/avd/${AVD_NAME}.avd/config.ini"# debug - -#"$ANDROID_HOME/emulator/emulator" -list-avds # debug - -"$ANDROID_HOME/emulator/emulator" \ - -avd "$AVD_NAME" \ - -no-window \ - -no-boot-anim \ - -no-audio \ - -no-snapshot \ - -no-snapshot-load \ - -no-snapshot-save \ - -wipe-data \ - -gpu swiftshader_indirect \ - -read-only \ - -no-metrics & +AVD_RAM_SIZE=2048 ./script/create-avd.sh +EMULATOR_GPU_MODE=swiftshader_indirect \ +EMULATOR_READ_ONLY=1 \ +EMULATOR_WIPE_DATA=1 \ +./script/run-emulator.sh diff --git a/script/create-avd.sh b/script/create-avd.sh new file mode 100755 index 000000000..a15982cad --- /dev/null +++ b/script/create-avd.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -euo pipefail + +AVD_NAME="${AVD_NAME:-ci-emulator}" +SYSTEM_IMAGE="${SYSTEM_IMAGE:-system-images;android-36;google_apis;x86_64}" +DEVICE_NAME="${DEVICE_NAME:-pixel_9}" +ABI="${ABI:-google_apis/x86_64}" +AVD_DIR="${ANDROID_AVD_HOME:-$HOME/.android/avd}" +AVD_RAM_SIZE="${AVD_RAM_SIZE:-}" + +mkdir -p "$AVD_DIR" "$HOME/.android" +touch "$HOME/.android/repositories.cfg" + +if avdmanager list avd | grep -q "Name: ${AVD_NAME}"; then + echo "Removing existing AVD: ${AVD_NAME}" + avdmanager delete avd --name "$AVD_NAME" || true +fi + +rm -rf \ + "${AVD_DIR}/${AVD_NAME}.avd" \ + "${AVD_DIR}/${AVD_NAME}.ini" + +echo -ne '\n' | avdmanager -v create avd \ + --force \ + --name "$AVD_NAME" \ + --package "$SYSTEM_IMAGE" \ + --device "$DEVICE_NAME" \ + --abi "$ABI" + +if [[ -n "$AVD_RAM_SIZE" ]]; then + echo "hw.ramSize=${AVD_RAM_SIZE}" >> "${AVD_DIR}/${AVD_NAME}.avd/config.ini" +fi + +echo "Created AVD ${AVD_NAME}" diff --git a/script/run-emulator.sh b/script/run-emulator.sh new file mode 100755 index 000000000..7ecb67791 --- /dev/null +++ b/script/run-emulator.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -euo pipefail + +AVD_NAME="${AVD_NAME:-ci-emulator}" +EMULATOR_GPU_MODE="${EMULATOR_GPU_MODE:-swiftshader_indirect}" +EMULATOR_READ_ONLY="${EMULATOR_READ_ONLY:-1}" +EMULATOR_WIPE_DATA="${EMULATOR_WIPE_DATA:-1}" + +"$ANDROID_HOME/emulator/emulator" -accel-check + +emulator_args=( + -avd "$AVD_NAME" + -no-window + -no-boot-anim + -no-audio + -no-snapshot + -no-snapshot-load + -no-snapshot-save + -gpu "$EMULATOR_GPU_MODE" + -no-metrics +) + +if [[ "$EMULATOR_WIPE_DATA" == "1" ]]; then + emulator_args+=(-wipe-data) +fi + +if [[ "$EMULATOR_READ_ONLY" == "1" ]]; then + emulator_args+=(-read-only) +fi + +"$ANDROID_HOME/emulator/emulator" "${emulator_args[@]}" & From 758ed1ef1cd1cff1ee78b7cce7d41f16709000c6 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sat, 16 May 2026 13:12:57 +0300 Subject: [PATCH 23/56] Refactoring --- docker/TestEnvironment/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/TestEnvironment/README.md b/docker/TestEnvironment/README.md index f8a6afe87..99549f43a 100644 --- a/docker/TestEnvironment/README.md +++ b/docker/TestEnvironment/README.md @@ -16,13 +16,13 @@ The image installs: ## Build ```bash -docker build -t flowcrypt-android-test-env -f docker/TestEnvironment/Dockerfile . +docker build -t flowcrypt/android-test-env -f docker/TestEnvironment/Dockerfile . ``` ## Create an AVD inside the container ```bash -docker run --rm -it flowcrypt-android-test-env /opt/flowcrypt/scripts/create-avd.sh +docker run --rm -it flowcrypt/android-test-env /opt/flowcrypt/scripts/create-avd.sh ``` ## Run the emulator inside the container @@ -42,7 +42,7 @@ Example: ```bash docker run --rm -it \ --device /dev/kvm \ - flowcrypt-android-test-env \ + flowcrypt/android-test-env \ bash ``` From 5be136fce69a99d61c9ce1e0587214c755bc8b54 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sat, 16 May 2026 13:22:21 +0300 Subject: [PATCH 24/56] Slipped KVM for Docker image --- docker/TestEnvironment/Dockerfile | 1 + script/ci-install-android-sdk.sh | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docker/TestEnvironment/Dockerfile b/docker/TestEnvironment/Dockerfile index 2ea9f705b..8a0d2c15d 100644 --- a/docker/TestEnvironment/Dockerfile +++ b/docker/TestEnvironment/Dockerfile @@ -53,6 +53,7 @@ RUN chmod +x /tmp/ci-install-android-sdk.sh \ && ANDROID_PLATFORM="${ANDROID_PLATFORM}" \ ANDROID_SYSTEM_IMAGE="${ANDROID_SYSTEM_IMAGE}" \ ANDROID_BUILD_TOOLS="${ANDROID_BUILD_TOOLS}" \ + INSTALL_KVM_DEPS=0 \ RUN_KVM_CHECK=0 \ /tmp/ci-install-android-sdk.sh \ && sdkmanager --list_installed \ diff --git a/script/ci-install-android-sdk.sh b/script/ci-install-android-sdk.sh index 123fec611..605cbaddc 100755 --- a/script/ci-install-android-sdk.sh +++ b/script/ci-install-android-sdk.sh @@ -22,6 +22,7 @@ fi # ----------------------------- export ANDROID_HOME="${ANDROID_HOME:-$HOME/Android/Sdk}" export ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT:-$ANDROID_HOME}" +INSTALL_KVM_DEPS="${INSTALL_KVM_DEPS:-1}" RUN_KVM_CHECK="${RUN_KVM_CHECK:-1}" ANDROID_PLATFORM="${ANDROID_PLATFORM:-android-36}" ANDROID_SYSTEM_IMAGE="${ANDROID_SYSTEM_IMAGE:-system-images;android-36;google_apis;x86_64}" @@ -90,7 +91,9 @@ check_cmdline_tools_latest_or_fail # ----------------------------- # KVM deps (as in your script) # ----------------------------- -sudo apt-get -qq install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils > /dev/null +if [[ "$INSTALL_KVM_DEPS" == "1" ]]; then + sudo apt-get -qq install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils > /dev/null +fi if [[ "$RUN_KVM_CHECK" == "1" ]]; then sudo kvm-ok fi From 23de806d1a05bfbf6a717245d6234d844fe835d5 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sat, 16 May 2026 13:32:14 +0300 Subject: [PATCH 25/56] Modified Dockerfile --- docker/TestEnvironment/Dockerfile | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/docker/TestEnvironment/Dockerfile b/docker/TestEnvironment/Dockerfile index 8a0d2c15d..c9ac82d2d 100644 --- a/docker/TestEnvironment/Dockerfile +++ b/docker/TestEnvironment/Dockerfile @@ -15,34 +15,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ bash \ ca-certificates \ curl \ - dnsutils \ - git \ - libasound2t64 \ - libc6 \ - libc6-dev \ libdbus-1-3 \ libfontconfig1 \ - libgcc-s1 \ libgl1 \ - libglu1-mesa \ libnspr4 \ libnss3 \ - libpulse0 \ - libstdc++6 \ libu2f-udev \ - libx11-6 \ - libx11-xcb1 \ - libxcb1 \ - libxcursor1 \ - libxdamage1 \ - libxext6 \ - libxi6 \ libxkbcommon0 \ - libxrandr2 \ - libxrender1 \ - libxtst6 \ - openjdk-21-jdk \ - sudo \ + openjdk-21-jre-headless \ unzip \ wget \ && rm -rf /var/lib/apt/lists/* From 725eb51b0e613916b208d3bb31449d1323781867 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sat, 16 May 2026 13:41:29 +0300 Subject: [PATCH 26/56] Modified Dockerfile --- docker/TestEnvironment/Dockerfile | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/docker/TestEnvironment/Dockerfile b/docker/TestEnvironment/Dockerfile index c9ac82d2d..a5311736d 100644 --- a/docker/TestEnvironment/Dockerfile +++ b/docker/TestEnvironment/Dockerfile @@ -15,14 +15,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ bash \ ca-certificates \ curl \ - libdbus-1-3 \ - libfontconfig1 \ - libgl1 \ - libnspr4 \ - libnss3 \ - libu2f-udev \ - libxkbcommon0 \ - openjdk-21-jre-headless \ + openjdk-21-jdk \ unzip \ wget \ && rm -rf /var/lib/apt/lists/* From 9ebb93546ba373e7cf8e82c9a70d927dcfd1754b Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sat, 16 May 2026 19:13:50 +0300 Subject: [PATCH 27/56] Modified Dockerfile --- docker/TestEnvironment/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker/TestEnvironment/Dockerfile b/docker/TestEnvironment/Dockerfile index a5311736d..39e30a9fd 100644 --- a/docker/TestEnvironment/Dockerfile +++ b/docker/TestEnvironment/Dockerfile @@ -18,6 +18,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ openjdk-21-jdk \ unzip \ wget \ + iputils-ping \ + git \ + sudo \ && rm -rf /var/lib/apt/lists/* COPY script/ci-install-android-sdk.sh /tmp/ci-install-android-sdk.sh From 342fc6de7bd0e1e9cdbedfc077eba95f356495f4 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sat, 16 May 2026 19:46:22 +0300 Subject: [PATCH 28/56] Added docker/HttpsTestWebServer --- docker/HttpsTestWebServer/Dockerfile | 8 ++++++++ docker/HttpsTestWebServer/nginx.conf | 15 +++++++++++++++ docker/HttpsTestWebServer/rebuild.sh | 16 ++++++++++++++++ docker/HttpsTestWebServer/run.sh | 15 +++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 docker/HttpsTestWebServer/Dockerfile create mode 100644 docker/HttpsTestWebServer/nginx.conf create mode 100755 docker/HttpsTestWebServer/rebuild.sh create mode 100755 docker/HttpsTestWebServer/run.sh diff --git a/docker/HttpsTestWebServer/Dockerfile b/docker/HttpsTestWebServer/Dockerfile new file mode 100644 index 000000000..7a212ce91 --- /dev/null +++ b/docker/HttpsTestWebServer/Dockerfile @@ -0,0 +1,8 @@ +FROM nginx:1.27-alpine + +COPY docker/HttpsTestWebServer/nginx.conf /etc/nginx/conf.d/default.conf + +COPY FlowCrypt/src/androidTest/resources/ssl/server_combined.pem \ + /etc/nginx/certs/server_combined.pem + +EXPOSE 443 \ No newline at end of file diff --git a/docker/HttpsTestWebServer/nginx.conf b/docker/HttpsTestWebServer/nginx.conf new file mode 100644 index 000000000..ac1b53f3a --- /dev/null +++ b/docker/HttpsTestWebServer/nginx.conf @@ -0,0 +1,15 @@ +server { + listen 443 ssl; + + server_name + flowcrypt.test + *.flowcrypt.test; + + ssl_certificate /etc/nginx/certs/server_combined.pem; + ssl_certificate_key /etc/nginx/certs/server_combined.pem; + + location / { + default_type text/plain; + return 200 "hello from docker https mock server\n"; + } +} \ No newline at end of file diff --git a/docker/HttpsTestWebServer/rebuild.sh b/docker/HttpsTestWebServer/rebuild.sh new file mode 100755 index 000000000..514a0bf74 --- /dev/null +++ b/docker/HttpsTestWebServer/rebuild.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(realpath "$SCRIPT_DIR/../..")" + +docker build \ + -t flowcrypt-https-test-server \ + -f "$SCRIPT_DIR/Dockerfile" \ + "$REPO_ROOT" \ No newline at end of file diff --git a/docker/HttpsTestWebServer/run.sh b/docker/HttpsTestWebServer/run.sh new file mode 100755 index 000000000..5aa47648c --- /dev/null +++ b/docker/HttpsTestWebServer/run.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -euo pipefail + +docker rm -f flowcrypt-https-test-server 2>/dev/null || true + +docker run --rm \ + --name flowcrypt-https-test-server \ + -p 1212:443 \ + flowcrypt-https-test-server \ No newline at end of file From 9edc6eedb6f475b28cf9a6cbfe7c366e54530ae0 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Sat, 16 May 2026 20:10:21 +0300 Subject: [PATCH 29/56] Updated docker/TestEnvironment --- docker/TestEnvironment/Dockerfile | 4 ++++ docker/TestEnvironment/README.md | 17 ++++++++++++++++- docker/TestEnvironment/rebuild.sh | 16 ++++++++++++++++ docker/TestEnvironment/run.sh | 18 ++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100755 docker/TestEnvironment/rebuild.sh create mode 100755 docker/TestEnvironment/run.sh diff --git a/docker/TestEnvironment/Dockerfile b/docker/TestEnvironment/Dockerfile index 39e30a9fd..9ce002826 100644 --- a/docker/TestEnvironment/Dockerfile +++ b/docker/TestEnvironment/Dockerfile @@ -42,4 +42,8 @@ RUN chmod +x /opt/flowcrypt/scripts/create-avd.sh /opt/flowcrypt/scripts/run-emu WORKDIR /workspace +#RUN apt-get update && apt-get install -y --no-install-recommends \ +# iputils-ping \ +# && rm -rf /var/lib/apt/lists/* + CMD ["/bin/bash"] diff --git a/docker/TestEnvironment/README.md b/docker/TestEnvironment/README.md index 99549f43a..ed1227327 100644 --- a/docker/TestEnvironment/README.md +++ b/docker/TestEnvironment/README.md @@ -15,6 +15,14 @@ The image installs: ## Build +Use the helper script: + +```bash +./docker/TestEnvironment/rebuild.sh +``` + +Equivalent manual command: + ```bash docker build -t flowcrypt/android-test-env -f docker/TestEnvironment/Dockerfile . ``` @@ -37,11 +45,18 @@ The container includes the emulator binaries, but actual emulator launch usually - `/dev/kvm` passed through to the container - additional Docker flags such as `--device /dev/kvm` -Example: +Use the helper script: + +```bash +./docker/TestEnvironment/run.sh +``` + +Equivalent manual command: ```bash docker run --rm -it \ --device /dev/kvm \ + --name flowcrypt-android-test-env \ flowcrypt/android-test-env \ bash ``` diff --git a/docker/TestEnvironment/rebuild.sh b/docker/TestEnvironment/rebuild.sh new file mode 100755 index 000000000..8cb90277f --- /dev/null +++ b/docker/TestEnvironment/rebuild.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(realpath "$SCRIPT_DIR/../..")" + +docker build \ + -t flowcrypt/android-test-env \ + -f "$SCRIPT_DIR/Dockerfile" \ + "$REPO_ROOT" diff --git a/docker/TestEnvironment/run.sh b/docker/TestEnvironment/run.sh new file mode 100755 index 000000000..888b00b0c --- /dev/null +++ b/docker/TestEnvironment/run.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -euo pipefail + +IMAGE_NAME="flowcrypt/android-test-env" +CONTAINER_NAME="flowcrypt-android-test-env" + +docker rm -f "$CONTAINER_NAME" 2>/dev/null || true + +docker run --rm -it \ + --name "$CONTAINER_NAME" \ + --device /dev/kvm \ + "$IMAGE_NAME" From 9cf7f183c60d4da60930297774a6c23dd8976fec Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Wed, 20 May 2026 18:12:13 +0300 Subject: [PATCH 30/56] ci(android): replace emulator iptables setup with /system/etc/hosts routing for flowcrypt.test domains --- docker/TestEnvironment/Dockerfile | 3 +- script/ci-wait-for-emulator.sh | 68 +++++++++++++++---------------- script/run-emulator.sh | 8 ++-- 3 files changed, 38 insertions(+), 41 deletions(-) diff --git a/docker/TestEnvironment/Dockerfile b/docker/TestEnvironment/Dockerfile index 9ce002826..c238d0862 100644 --- a/docker/TestEnvironment/Dockerfile +++ b/docker/TestEnvironment/Dockerfile @@ -38,7 +38,8 @@ RUN chmod +x /tmp/ci-install-android-sdk.sh \ RUN mkdir -p /opt/flowcrypt/scripts COPY script/create-avd.sh /opt/flowcrypt/scripts/create-avd.sh COPY script/run-emulator.sh /opt/flowcrypt/scripts/run-emulator.sh -RUN chmod +x /opt/flowcrypt/scripts/create-avd.sh /opt/flowcrypt/scripts/run-emulator.sh +COPY script/ci-wait-for-emulator.sh /opt/flowcrypt/scripts/ci-wait-for-emulator.sh +RUN chmod +x /opt/flowcrypt/scripts/create-avd.sh /opt/flowcrypt/scripts/run-emulator.sh /opt/flowcrypt/scripts/ci-wait-for-emulator.sh WORKDIR /workspace diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index 69fbdf567..c1e4ab357 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -8,10 +8,15 @@ set -euo pipefail set -o xtrace +FLOWCRYPT_TEST_DOMAINS=( + "flowcrypt.test" + "api.flowcrypt.test" + "attester.flowcrypt.test" +) + wait_for_boot_completed() { adb wait-for-device - # shellcheck disable=SC2016 adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' } @@ -33,15 +38,23 @@ check_ping_or_fail() { done } -check_iptables_rule_or_fail() { - local chain="$1" - local expected_rule="$2" +write_flowcrypt_hosts() { + local hosts_content + + hosts_content=$( + { + echo "127.0.0.1 localhost" + echo "::1 ip6-localhost" - if ! adb shell "iptables -t nat -S ${chain}" | grep -F -- "${expected_rule}"; then - echo "iptables rule was not applied:" - echo "${expected_rule}" - exit 1 - fi + for domain in "${FLOWCRYPT_TEST_DOMAINS[@]}"; do + echo "10.0.2.2 ${domain}" + done + } + ) + + adb shell "cat > /system/etc/hosts <<'EOF' +${hosts_content} +EOF" } wait_for_boot_completed @@ -52,10 +65,6 @@ 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 -################################################################################################### -# To test WKD we need to route all traffic for localhost:443 to localhost:1212 -# as we can't use 443 directly for a mock web server. -################################################################################################### if adb root; then echo "adb root succeeded" else @@ -67,35 +76,22 @@ fi wait_for_boot_completed -adb shell "echo 1 > /proc/sys/net/ipv4/ip_forward" - -adb shell "iptables -t nat -D OUTPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1212" || true -adb shell "iptables -t nat -D PREROUTING -s 127.0.0.1/32 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1212" || true +adb disable-verity || true +adb reboot -adb shell "iptables -t nat -A PREROUTING -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to-ports 1212" -adb shell "iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to-ports 1212" +wait_for_boot_completed -check_iptables_rule_or_fail \ - "PREROUTING" \ - "-A PREROUTING -s 127.0.0.1/32 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1212" +adb root +adb remount -check_iptables_rule_or_fail \ - "OUTPUT" \ - "-A OUTPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 1212" -################################################################################################### +write_flowcrypt_hosts -# https://developer.android.com/tools/adb#forwardports -# Forwards requests on a specific host port to a different port on a device. -# It can be helpful for debugging a mock web server -adb forward --remove tcp:1212 || true -adb forward tcp:1212 tcp:1212 -if ! adb forward --list | grep -F -- "tcp:1212 tcp:1212"; then - echo "adb forward was not applied: tcp:1212 tcp:1212" - exit 1 -fi +adb shell cat /system/etc/hosts -################################################################################################### +check_ping_or_fail "10.0.2.2" "host machine" +check_ping_or_fail "api.flowcrypt.test" "FlowCrypt test domain routing" check_ping_or_fail "www.google.com" "internet connection" echo "Emulator is ready" + set +o xtrace \ No newline at end of file diff --git a/script/run-emulator.sh b/script/run-emulator.sh index 7ecb67791..df8edfb99 100755 --- a/script/run-emulator.sh +++ b/script/run-emulator.sh @@ -9,7 +9,7 @@ set -euo pipefail AVD_NAME="${AVD_NAME:-ci-emulator}" EMULATOR_GPU_MODE="${EMULATOR_GPU_MODE:-swiftshader_indirect}" -EMULATOR_READ_ONLY="${EMULATOR_READ_ONLY:-1}" +EMULATOR_WRITABLE_SYSTEM="${EMULATOR_WRITABLE_SYSTEM:-1}" EMULATOR_WIPE_DATA="${EMULATOR_WIPE_DATA:-1}" "$ANDROID_HOME/emulator/emulator" -accel-check @@ -30,8 +30,8 @@ if [[ "$EMULATOR_WIPE_DATA" == "1" ]]; then emulator_args+=(-wipe-data) fi -if [[ "$EMULATOR_READ_ONLY" == "1" ]]; then - emulator_args+=(-read-only) +if [[ "$EMULATOR_WRITABLE_SYSTEM" == "1" ]]; then + emulator_args+=(-writable-system) fi -"$ANDROID_HOME/emulator/emulator" "${emulator_args[@]}" & +"$ANDROID_HOME/emulator/emulator" "${emulator_args[@]}" & \ No newline at end of file From dc98698bc704c3469a8de40b2f5356b5c8ba6768 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Wed, 20 May 2026 18:38:40 +0300 Subject: [PATCH 31/56] ci(semaphore): redirect local HTTPS traffic from 443 to mock server on 1212 for emulator UI tests --- .semaphore/semaphore.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index ec0b65446..9d5b5e4ce 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -36,6 +36,11 @@ global_job_config: # redirect the systemd-resolved to use the localhost as the primary nameserver - sudo sed -i '1inameserver 127.0.0.1\' /etc/resolv.conf - sudo systemctl restart dnsmasq + # redirect HTTPS traffic to local mock server on :1212 + - sudo iptables -t nat -D PREROUTING -p tcp --dport 443 -m addrtype --dst-type LOCAL -j REDIRECT --to-ports 1212 2>/dev/null || true + - sudo iptables -t nat -D OUTPUT -p tcp --dport 443 -m addrtype --dst-type LOCAL -j REDIRECT --to-ports 1212 2>/dev/null || true + - sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -m addrtype --dst-type LOCAL -j REDIRECT --to-ports 1212 + - sudo iptables -t nat -A OUTPUT -p tcp --dport 443 -m addrtype --dst-type LOCAL -j REDIRECT --to-ports 1212 # print some debug info - ping fel.localhost -c 1 - ping fel.flowcrypt.test -c 1 From a1482e64caaca5d088094ff746ee6a01dc2872c1 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Wed, 20 May 2026 19:02:17 +0300 Subject: [PATCH 32/56] ci(semaphore): fix emulator routing by redirecting 10.0.2.2:443 to 1212 and add fes.flowcrypt.test host mapping --- .semaphore/semaphore.yml | 4 +--- script/ci-wait-for-emulator.sh | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 9d5b5e4ce..8f54d828c 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -36,10 +36,8 @@ global_job_config: # redirect the systemd-resolved to use the localhost as the primary nameserver - sudo sed -i '1inameserver 127.0.0.1\' /etc/resolv.conf - sudo systemctl restart dnsmasq - # redirect HTTPS traffic to local mock server on :1212 - - sudo iptables -t nat -D PREROUTING -p tcp --dport 443 -m addrtype --dst-type LOCAL -j REDIRECT --to-ports 1212 2>/dev/null || true + # redirect emulator HTTPS traffic (:443 -> :1212) - sudo iptables -t nat -D OUTPUT -p tcp --dport 443 -m addrtype --dst-type LOCAL -j REDIRECT --to-ports 1212 2>/dev/null || true - - sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -m addrtype --dst-type LOCAL -j REDIRECT --to-ports 1212 - sudo iptables -t nat -A OUTPUT -p tcp --dport 443 -m addrtype --dst-type LOCAL -j REDIRECT --to-ports 1212 # print some debug info - ping fel.localhost -c 1 diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index c1e4ab357..82928ec3a 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -12,6 +12,7 @@ FLOWCRYPT_TEST_DOMAINS=( "flowcrypt.test" "api.flowcrypt.test" "attester.flowcrypt.test" + "fes.flowcrypt.test" ) wait_for_boot_completed() { @@ -94,4 +95,4 @@ check_ping_or_fail "www.google.com" "internet connection" echo "Emulator is ready" -set +o xtrace \ No newline at end of file +set +o xtrace From f3239ae0cb578edd1d6dff119d551f376d5aabb1 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 09:15:27 +0300 Subject: [PATCH 33/56] docker(https-test-webserver): run dnsmasq alongside nginx and expose host DNS for *.flowcrypt.test - install dnsmasq in HttpsTestWebServer image - add dnsmasq config mapping *.flowcrypt.test to 127.0.0.1 - add entrypoint to start dnsmasq and nginx in one container - switch run.sh to host networking with NET_ADMIN/NET_BIND_SERVICE - expose DNS ports (53 tcp/udp) and HTTPS (443) --- docker/HttpsTestWebServer/Dockerfile | 11 ++++++++++- docker/HttpsTestWebServer/dnsmasq.conf | 8 ++++++++ docker/HttpsTestWebServer/entrypoint.sh | 11 +++++++++++ docker/HttpsTestWebServer/run.sh | 6 ++++-- 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 docker/HttpsTestWebServer/dnsmasq.conf create mode 100755 docker/HttpsTestWebServer/entrypoint.sh diff --git a/docker/HttpsTestWebServer/Dockerfile b/docker/HttpsTestWebServer/Dockerfile index 7a212ce91..0127195bf 100644 --- a/docker/HttpsTestWebServer/Dockerfile +++ b/docker/HttpsTestWebServer/Dockerfile @@ -1,8 +1,17 @@ FROM nginx:1.27-alpine +RUN apk add --no-cache dnsmasq + COPY docker/HttpsTestWebServer/nginx.conf /etc/nginx/conf.d/default.conf COPY FlowCrypt/src/androidTest/resources/ssl/server_combined.pem \ /etc/nginx/certs/server_combined.pem -EXPOSE 443 \ No newline at end of file +COPY docker/HttpsTestWebServer/dnsmasq.conf /etc/dnsmasq.d/flowcrypt-test.conf +COPY docker/HttpsTestWebServer/entrypoint.sh /entrypoint.sh + +RUN chmod +x /entrypoint.sh + +EXPOSE 53/udp 53/tcp 443 + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/HttpsTestWebServer/dnsmasq.conf b/docker/HttpsTestWebServer/dnsmasq.conf new file mode 100644 index 000000000..c637e314e --- /dev/null +++ b/docker/HttpsTestWebServer/dnsmasq.conf @@ -0,0 +1,8 @@ +interface=docker0 +bind-interfaces +listen-address=172.17.0.1 +port=53 + +address=/flowcrypt.test/127.0.0.1 +log-queries +log-facility=- \ No newline at end of file diff --git a/docker/HttpsTestWebServer/entrypoint.sh b/docker/HttpsTestWebServer/entrypoint.sh new file mode 100755 index 000000000..e3897be24 --- /dev/null +++ b/docker/HttpsTestWebServer/entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -e + +dnsmasq --no-daemon --conf-file=/etc/dnsmasq.d/flowcrypt-test.conf --log-facility=- & +exec nginx -g 'daemon off;' diff --git a/docker/HttpsTestWebServer/run.sh b/docker/HttpsTestWebServer/run.sh index 5aa47648c..d7dc7a14b 100755 --- a/docker/HttpsTestWebServer/run.sh +++ b/docker/HttpsTestWebServer/run.sh @@ -11,5 +11,7 @@ docker rm -f flowcrypt-https-test-server 2>/dev/null || true docker run --rm \ --name flowcrypt-https-test-server \ - -p 1212:443 \ - flowcrypt-https-test-server \ No newline at end of file + --network host \ + --cap-add NET_ADMIN \ + --cap-add NET_BIND_SERVICE \ + flowcrypt-https-test-server From 32e6d71bab74d0f741932279a6640f5a43413f7d Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 09:19:16 +0300 Subject: [PATCH 34/56] Modified ci-after-fail-debug.sh --- script/ci-after-fail-debug.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/ci-after-fail-debug.sh b/script/ci-after-fail-debug.sh index 5d6b49676..8b72bc6c8 100755 --- a/script/ci-after-fail-debug.sh +++ b/script/ci-after-fail-debug.sh @@ -12,12 +12,12 @@ check_ping_or_fail() { local host="$1" local label="$2" - for attempt in {1..30}; do + for attempt in {1..10}; do if adb shell "ping -c 1 ${host}"; then return 0 fi - if [[ "$attempt" -eq 30 ]]; then + if [[ "$attempt" -eq 10 ]]; then echo "Failed to ping ${host}: ${label}" exit 1 fi From 39fa9e2e42a4318a0e5622654ebdff6181626c5c Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 09:24:29 +0300 Subject: [PATCH 35/56] Restored some files --- .semaphore/semaphore.yml | 3 - script/ci-wait-for-emulator.sh | 101 +++++++-------------------------- script/run-emulator.sh | 6 +- 3 files changed, 20 insertions(+), 90 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 8f54d828c..ec0b65446 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -36,9 +36,6 @@ global_job_config: # redirect the systemd-resolved to use the localhost as the primary nameserver - sudo sed -i '1inameserver 127.0.0.1\' /etc/resolv.conf - sudo systemctl restart dnsmasq - # redirect emulator HTTPS traffic (:443 -> :1212) - - sudo iptables -t nat -D OUTPUT -p tcp --dport 443 -m addrtype --dst-type LOCAL -j REDIRECT --to-ports 1212 2>/dev/null || true - - sudo iptables -t nat -A OUTPUT -p tcp --dport 443 -m addrtype --dst-type LOCAL -j REDIRECT --to-ports 1212 # print some debug info - ping fel.localhost -c 1 - ping fel.flowcrypt.test -c 1 diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index 82928ec3a..6e622f643 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -1,98 +1,35 @@ -#!/usr/bin/env bash - +#!/bin/bash # # © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com # Contributors: denbond7 # -set -euo pipefail set -o xtrace - -FLOWCRYPT_TEST_DOMAINS=( - "flowcrypt.test" - "api.flowcrypt.test" - "attester.flowcrypt.test" - "fes.flowcrypt.test" -) - -wait_for_boot_completed() { - adb wait-for-device - - adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' -} - -check_ping_or_fail() { - local host="$1" - local label="$2" - - for attempt in {1..30}; do - if adb shell "ping -c 1 ${host}"; then - return 0 - fi - - if [[ "$attempt" -eq 30 ]]; then - echo "Failed to ping ${host}: ${label}" - exit 1 - fi - - sleep 2 - done -} - -write_flowcrypt_hosts() { - local hosts_content - - hosts_content=$( - { - echo "127.0.0.1 localhost" - echo "::1 ip6-localhost" - - for domain in "${FLOWCRYPT_TEST_DOMAINS[@]}"; do - echo "10.0.2.2 ${domain}" - done - } - ) - - adb shell "cat > /system/etc/hosts <<'EOF' -${hosts_content} -EOF" -} - -wait_for_boot_completed - -adb shell wm dismiss-keyguard || true - +adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;' +adb shell wm dismiss-keyguard +sleep 1 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 -if adb root; then - echo "adb root succeeded" -else - echo "adb root failed, restarting adb and waiting for emulator..." - - adb kill-server || true - adb start-server -fi - -wait_for_boot_completed - -adb disable-verity || true -adb reboot - -wait_for_boot_completed - +################################################################################################### +# to test WKD we need to route all traffic for localhost:443 to localhost:1212 +# as we can't use 443 directly for a mock web server. adb root -adb remount +# Need wait for the root environment +sleep 20 +adb shell "echo 1 > /proc/sys/net/ipv4/ip_forward" +adb shell "iptables -t nat -A PREROUTING -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" +adb shell "iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" +################################################################################################### -write_flowcrypt_hosts +# https://developer.android.com/tools/adb#forwardports +# forwards requests on a specific host port to a different port on a device. +# It can be helpful for debugging a mock web server +adb forward tcp:1212 tcp:1212 -adb shell cat /system/etc/hosts - -check_ping_or_fail "10.0.2.2" "host machine" -check_ping_or_fail "api.flowcrypt.test" "FlowCrypt test domain routing" -check_ping_or_fail "www.google.com" "internet connection" +#check the emulator has internet connection +adb shell "ping -c 1 www.google.com" echo "Emulator is ready" - set +o xtrace diff --git a/script/run-emulator.sh b/script/run-emulator.sh index df8edfb99..e8e090e58 100755 --- a/script/run-emulator.sh +++ b/script/run-emulator.sh @@ -9,7 +9,6 @@ set -euo pipefail AVD_NAME="${AVD_NAME:-ci-emulator}" EMULATOR_GPU_MODE="${EMULATOR_GPU_MODE:-swiftshader_indirect}" -EMULATOR_WRITABLE_SYSTEM="${EMULATOR_WRITABLE_SYSTEM:-1}" EMULATOR_WIPE_DATA="${EMULATOR_WIPE_DATA:-1}" "$ANDROID_HOME/emulator/emulator" -accel-check @@ -23,6 +22,7 @@ emulator_args=( -no-snapshot-load -no-snapshot-save -gpu "$EMULATOR_GPU_MODE" + -read-only -no-metrics ) @@ -30,8 +30,4 @@ if [[ "$EMULATOR_WIPE_DATA" == "1" ]]; then emulator_args+=(-wipe-data) fi -if [[ "$EMULATOR_WRITABLE_SYSTEM" == "1" ]]; then - emulator_args+=(-writable-system) -fi - "$ANDROID_HOME/emulator/emulator" "${emulator_args[@]}" & \ No newline at end of file From f48ee1d14cbfbbf4f6231ac6de50e5559e6a7d48 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 10:05:28 +0300 Subject: [PATCH 36/56] ci(android): force emulator DNS via host and harden 443->1212 redirect for flowcrypt.test tests --- README.md | 12 +++++++++++- script/ci-wait-for-emulator.sh | 5 +++-- script/run-emulator.sh | 3 ++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b1a521b10..0e5f81e1a 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,17 @@ This guide follows Google's recommendations for [testing apps on Android](https: Please follow these steps to setup your virtual or physical device: - [Set up your test environment](https://developer.android.com/training/testing/espresso/setup#set-up-environment). -- Some of the tests use [MockWebServer](https://github.com/square/okhttp/tree/master/mockwebserver). We run a local mock web server via [FlowCryptMockWebServerRule](https://github.com/FlowCrypt/flowcrypt-android/blob/master/FlowCrypt/src/androidTest/java/com/flowcrypt/email/rules/FlowCryptMockWebServerRule.kt). It uses [TestConstants.MOCK_WEB_SERVER_PORT](https://github.com/FlowCrypt/flowcrypt-android/blob/master/FlowCrypt/src/androidTest/java/com/flowcrypt/email/TestConstants.kt#L19) as a port. Unfortunately, the Android system doesn't allow us to use the `HTTPS 433` port by default to run a web server on the `localhost (127.0.0.1)`. That's why we have to run a mock web server on another port (for example, `1212`) and route all traffic from `127.0.0.1:433` to `127.0.0.1:1212`. For that purpose, you can use the [script/ci-wait-for-emulator.sh](https://github.com/FlowCrypt/flowcrypt-android/blob/master/script/ci-wait-for-emulator.sh#L13) script. +- Some of the tests use [MockWebServer](https://github.com/square/okhttp/tree/master/mockwebserver). + We run a local mock web server + via [FlowCryptMockWebServerRule](https://github.com/FlowCrypt/flowcrypt-android/blob/master/FlowCrypt/src/androidTest/java/com/flowcrypt/email/rules/FlowCryptMockWebServerRule.kt). + It + uses [TestConstants.MOCK_WEB_SERVER_PORT](https://github.com/FlowCrypt/flowcrypt-android/blob/master/FlowCrypt/src/androidTest/java/com/flowcrypt/email/TestConstants.kt#L19) + as a port. Unfortunately, the Android system doesn't allow us to use the `HTTPS 443` port by + default to run a web server on the `localhost (127.0.0.1)`. That's why we have to run a mock web + server on another port (for example, `1212`) and route all traffic from `127.0.0.1:443` to + `127.0.0.1:1212`. For that purpose, you can use + the [script/ci-wait-for-emulator.sh](https://github.com/FlowCrypt/flowcrypt-android/blob/master/script/ci-wait-for-emulator.sh#L13) + script. - Additionally, the test environment should handle all requests to `*.flowcrypt.test`. All traffic to `*.flowcrypt.test` should be routed to `localhost (127.0.0.1)`. You can use a DNS server to do this. For example, using these commands: ```bash diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index 6e622f643..b3c11fd79 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -19,8 +19,9 @@ adb root # Need wait for the root environment sleep 20 adb shell "echo 1 > /proc/sys/net/ipv4/ip_forward" -adb shell "iptables -t nat -A PREROUTING -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" -adb shell "iptables -t nat -A OUTPUT -s 127.0.0.1 -p tcp --dport 443 -j REDIRECT --to 1212" +adb shell "iptables -t nat -D OUTPUT -p tcp -d 127.0.0.1 --dport 443 -j REDIRECT --to-ports 1212" || true +adb shell "iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport 443 -j REDIRECT --to-ports 1212" +adb shell "iptables -t nat -S OUTPUT" ################################################################################################### # https://developer.android.com/tools/adb#forwardports diff --git a/script/run-emulator.sh b/script/run-emulator.sh index e8e090e58..899521302 100755 --- a/script/run-emulator.sh +++ b/script/run-emulator.sh @@ -22,6 +22,7 @@ emulator_args=( -no-snapshot-load -no-snapshot-save -gpu "$EMULATOR_GPU_MODE" + -dns-server 10.0.2.2 -read-only -no-metrics ) @@ -30,4 +31,4 @@ if [[ "$EMULATOR_WIPE_DATA" == "1" ]]; then emulator_args+=(-wipe-data) fi -"$ANDROID_HOME/emulator/emulator" "${emulator_args[@]}" & \ No newline at end of file +"$ANDROID_HOME/emulator/emulator" "${emulator_args[@]}" & From 037be6485722e9ba2b6b85c117f6e1916c88be41 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 11:06:19 +0300 Subject: [PATCH 37/56] Improved ci-after-fail-debug.sh --- script/ci-after-fail-debug.sh | 152 ++++++++++++++++++++++++++++++++-- 1 file changed, 147 insertions(+), 5 deletions(-) diff --git a/script/ci-after-fail-debug.sh b/script/ci-after-fail-debug.sh index 8b72bc6c8..754e624c5 100755 --- a/script/ci-after-fail-debug.sh +++ b/script/ci-after-fail-debug.sh @@ -6,7 +6,129 @@ # set -euo pipefail -set -o xtrace + +SUMMARY_FAILURES=0 + +print_section() { + local title="$1" + + echo + echo "======================================================================" + echo "${title}" + date -u '+UTC time: %Y-%m-%d %H:%M:%S' + echo "======================================================================" +} + +run_debug_cmd() { + local description="$1" + shift + + echo + echo "[debug] ${description}" + "$@" || true +} + +adb_root_shell() { + local cmd="$1" + + adb shell "su 0 sh -c '${cmd}' 2>/dev/null || sh -c '${cmd}'" +} + +summary_check() { + local label="$1" + shift + + if "$@" >/dev/null 2>&1; then + echo "[summary] PASS: ${label}" + else + echo "[summary] FAIL: ${label}" + SUMMARY_FAILURES=$((SUMMARY_FAILURES + 1)) + fi +} + +collect_network_debug_info() { + print_section "Host-side adb state" + run_debug_cmd "adb devices" adb devices -l + run_debug_cmd "adb server version" adb version + run_debug_cmd "adb forward list" adb forward --list + run_debug_cmd "adb reverse list" adb reverse --list + + print_section "Device network overview" + run_debug_cmd "boot completed" adb shell getprop sys.boot_completed + run_debug_cmd "net/dns props" adb shell "getprop | grep -E 'net\\.|dns|dhcp|private_dns'" + run_debug_cmd "ip addr" adb shell ip addr + run_debug_cmd "ip route" adb shell ip route + run_debug_cmd "connectivity dumpsys" adb shell dumpsys connectivity + run_debug_cmd "private dns mode" adb shell settings get global private_dns_mode + + print_section "DNS and internet checks" + + run_debug_cmd \ + "ping raw IP 8.8.8.8" \ + adb shell ping -c 1 8.8.8.8 + + run_debug_cmd \ + "ping www.google.com" \ + adb shell ping -c 1 www.google.com + + run_debug_cmd \ + "ping emulator host gateway 10.0.2.2" \ + adb shell ping -c 1 10.0.2.2 + + run_debug_cmd \ + "ping fes.flowcrypt.test" \ + adb shell ping -c 1 fes.flowcrypt.test + + print_section "NAT and sockets" + + run_debug_cmd \ + "iptables nat OUTPUT rules" \ + adb_root_shell "iptables -t nat -S OUTPUT" + + run_debug_cmd \ + "iptables nat OUTPUT counters" \ + adb_root_shell "iptables -t nat -L OUTPUT -n -v" + + run_debug_cmd \ + "listeners netstat" \ + adb shell netstat -lntp +} + +print_summary() { + print_section "Network summary" + + summary_check \ + "internet raw IP (8.8.8.8)" \ + adb shell ping -c 1 8.8.8.8 + + summary_check \ + "internet DNS (www.google.com)" \ + adb shell ping -c 1 www.google.com + + summary_check \ + "emulator host gateway 10.0.2.2" \ + adb shell ping -c 1 10.0.2.2 + + summary_check \ + "flowcrypt.test ping" \ + adb shell ping -c 1 fes.flowcrypt.test + + summary_check \ + "iptables OUTPUT redirect 443->1212 present" \ + adb_root_shell \ + "iptables -t nat -S OUTPUT | grep -F -- '--dport 443 -j REDIRECT --to-ports 1212'" + + summary_check \ + "listener on :1212 present" \ + adb shell \ + "netstat -lnt 2>/dev/null | grep -E '(^|[:.])1212([[:space:]]|$)'" + + if [[ "$SUMMARY_FAILURES" -eq 0 ]]; then + echo "[summary] RESULT: PASS" + else + echo "[summary] RESULT: FAIL (${SUMMARY_FAILURES} checks failed)" + fi +} check_ping_or_fail() { local host="$1" @@ -19,7 +141,7 @@ check_ping_or_fail() { if [[ "$attempt" -eq 10 ]]; then echo "Failed to ping ${host}: ${label}" - exit 1 + return 1 fi sleep 2 @@ -27,7 +149,27 @@ check_ping_or_fail() { } ################################################################################################### -check_ping_or_fail "www.google.com" "internet connection" -check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test forwarding" -set +o xtrace \ No newline at end of file +NETWORK_FAILURE=0 + +if ! check_ping_or_fail "www.google.com" "internet connection"; then + NETWORK_FAILURE=1 +fi + +if ! check_ping_or_fail "10.0.2.2" "emulator host gateway"; then + NETWORK_FAILURE=1 +fi + +if ! check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test DNS/reachability"; then + NETWORK_FAILURE=1 +fi + +if [[ "$NETWORK_FAILURE" -eq 1 ]]; then + print_section "Network failure diagnostics" + collect_network_debug_info + print_summary + exit 1 +fi + +print_section "Network status" +echo "All network checks passed" \ No newline at end of file From ff8d20e0923cf1835d49d48adbecce3b2604334a Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 11:35:10 +0300 Subject: [PATCH 38/56] Added ci-setup-DNS.sh --- .semaphore/semaphore.yml | 15 ++----------- script/ci-setup-DNS.sh | 47 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 13 deletions(-) create mode 100755 script/ci-setup-DNS.sh diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index ec0b65446..4396f3c3b 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -25,20 +25,9 @@ global_job_config: value: /home/semaphore/Android/Sdk prologue: commands: - # fix DNS to ping *.localhost and *.flowcrypt.test - sudo apt-get update - - sudo apt install -y dnsmasq resolvconf - - echo "#added by flowcrypt" | sudo tee -a /etc/dnsmasq.conf - - echo "listen-address=127.0.0.1" | sudo tee -a /etc/dnsmasq.conf - - echo "address=/test/127.0.0.1" | sudo tee -a /etc/dnsmasq.conf - - echo "address=/localhost/127.0.0.1" | sudo tee -a /etc/dnsmasq.conf - - echo "server=8.8.8.8" | sudo tee -a /etc/dnsmasq.conf - # redirect the systemd-resolved to use the localhost as the primary nameserver - - sudo sed -i '1inameserver 127.0.0.1\' /etc/resolv.conf - - sudo systemctl restart dnsmasq - # print some debug info - - ping fel.localhost -c 1 - - ping fel.flowcrypt.test -c 1 + # fix DNS to ping *.localhost and *.flowcrypt.test + - ./script/ci-setup-DNS.sh # use JAVA 21 by default - sem-version java 21 # general settings diff --git a/script/ci-setup-DNS.sh b/script/ci-setup-DNS.sh new file mode 100755 index 000000000..3eb53f591 --- /dev/null +++ b/script/ci-setup-DNS.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# +# © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com +# Contributors: denbond7 +# + +set -euo pipefail + +echo "Installing DNS tools..." +sudo apt install -y dnsmasq dnsutils + +echo "Configuring dnsmasq..." +sudo tee /etc/dnsmasq.d/flowcrypt.conf >/dev/null <<'EOF' +# added by flowcrypt +listen-address=127.0.0.1 +bind-interfaces + +# Do not read /etc/resolv.conf to avoid recursive localhost DNS loops. +no-resolv + +# Upstream DNS for public domains. +server=8.8.8.8 +server=1.1.1.1 + +# Local test domains. +address=/test/127.0.0.1 +address=/localhost/127.0.0.1 +EOF + +echo "Restarting dnsmasq..." +sudo systemctl restart dnsmasq + +echo "DNS debug info:" +sudo ss -lunp | grep ':53' || true + +echo "Checking dnsmasq directly..." +dig @127.0.0.1 fel.localhost +dig @127.0.0.1 fel.flowcrypt.test +dig @127.0.0.1 www.google.com + +echo "Checking host resolver..." +ping fel.localhost -c 1 +ping fel.flowcrypt.test -c 1 +ping www.google.com -c 1 + +echo "DNS setup completed successfully." \ No newline at end of file From 318af190357d922329a00f6fe44c63ab2b42c708 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 11:37:45 +0300 Subject: [PATCH 39/56] Fix ci-setup-DNS.sh usage on CI --- .semaphore/semaphore.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 4396f3c3b..e1f1a648d 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -26,14 +26,14 @@ global_job_config: prologue: commands: - sudo apt-get update - # fix DNS to ping *.localhost and *.flowcrypt.test - - ./script/ci-setup-DNS.sh # use JAVA 21 by default - sem-version java 21 # general settings - export PATH=${ANDROID_HOME}/emulator:${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${PATH} - sudo rm -rf ~/.rbenv ~/.phpbrew - checkout + # fix DNS to ping *.localhost and *.flowcrypt.test + - ./script/ci-setup-DNS.sh # init environment variables - export GRADLE_SUM=$(checksum build.gradle.kts)-$(checksum FlowCrypt/build.gradle.kts) - export SDK_SUM=$(checksum ./script/ci-install-android-sdk.sh) From aeec4a0b81e3f4276493b95343d1ccdcb6ba4e17 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 11:41:34 +0300 Subject: [PATCH 40/56] Fixed ci-setup-DNS.sh --- script/ci-setup-DNS.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/script/ci-setup-DNS.sh b/script/ci-setup-DNS.sh index 3eb53f591..738b16a3b 100755 --- a/script/ci-setup-DNS.sh +++ b/script/ci-setup-DNS.sh @@ -28,11 +28,15 @@ address=/test/127.0.0.1 address=/localhost/127.0.0.1 EOF +echo "Configuring host resolver to use dnsmasq..." +echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf >/dev/null + echo "Restarting dnsmasq..." sudo systemctl restart dnsmasq echo "DNS debug info:" sudo ss -lunp | grep ':53' || true +cat /etc/resolv.conf echo "Checking dnsmasq directly..." dig @127.0.0.1 fel.localhost From 7a6e3d02312ae813e54313eaf5cf48b18d92febb Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 12:04:42 +0300 Subject: [PATCH 41/56] Modified ci-wait-for-emulator.sh --- script/ci-wait-for-emulator.sh | 129 ++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 9 deletions(-) diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index b3c11fd79..a9667110f 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -4,33 +4,144 @@ # Contributors: denbond7 # +set -euo pipefail set -o xtrace -adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;' + +wait_for_boot_completed() { + adb wait-for-device + + # shellcheck disable=SC2016 + adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' +} + +wait_for_network_after_adb_root() { + for attempt in {1..30}; do + if adb shell "ping -c 1 10.0.2.2" && adb shell "ping -c 1 8.8.8.8"; then + return 0 + fi + + echo "Waiting for emulator network after adb root... attempt ${attempt}/30" + sleep 2 + done + + echo "Emulator network did not become ready after adb root" + print_network_debug "network after adb root" + exit 1 +} + +check_ping_or_fail() { + local host="$1" + local label="$2" + + for attempt in {1..10}; do + if adb shell "ping -c 1 ${host}"; then + return 0 + fi + + echo "Waiting for ${label}... attempt ${attempt}/10" + sleep 2 + done + + echo "Failed to ping ${host}: ${label}" + print_network_debug "${label}" + exit 1 +} + +print_network_debug() { + local reason="$1" + + set +e + + echo + echo "======================================================================" + echo "Network failure diagnostics: ${reason}" + echo "UTC time: $(date -u '+%Y-%m-%d %H:%M:%S')" + echo "======================================================================" + + echo + echo "[debug] adb devices" + adb devices -l + + echo + echo "[debug] adb forward list" + adb forward --list + + echo + echo "[debug] net/dns props" + adb shell getprop | grep -iE 'dns|net' || true + + echo + echo "[debug] ip addr" + adb shell ip addr || true + + echo + echo "[debug] ip route" + adb shell ip route || true + + echo + echo "[debug] connectivity DNS" + adb shell dumpsys connectivity | grep -iE 'DnsAddresses|ServerAddress|Active default network' || true + + echo + echo "[debug] ping gateway" + adb shell ping -c 1 10.0.2.2 || true + + echo + echo "[debug] ping raw internet IP" + adb shell ping -c 1 8.8.8.8 || true + + echo + echo "[debug] ping public DNS name" + adb shell ping -c 1 www.google.com || true + + echo + echo "[debug] ping flowcrypt test domain" + adb shell ping -c 1 fes.flowcrypt.test || true + + set -e +} + +wait_for_boot_completed + adb shell wm dismiss-keyguard sleep 1 + 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 ################################################################################################### -# to test WKD we need to route all traffic for localhost:443 to localhost:1212 +# To test WKD we need to route all traffic for localhost:443 to localhost:1212 # as we can't use 443 directly for a mock web server. + adb root -# Need wait for the root environment -sleep 20 + +# adb root restarts adbd, so wait until the device is available again. +wait_for_boot_completed + +# adb root can temporarily reset Android networking. +wait_for_network_after_adb_root + adb shell "echo 1 > /proc/sys/net/ipv4/ip_forward" + adb shell "iptables -t nat -D OUTPUT -p tcp -d 127.0.0.1 --dport 443 -j REDIRECT --to-ports 1212" || true adb shell "iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport 443 -j REDIRECT --to-ports 1212" + adb shell "iptables -t nat -S OUTPUT" + ################################################################################################### # https://developer.android.com/tools/adb#forwardports -# forwards requests on a specific host port to a different port on a device. -# It can be helpful for debugging a mock web server +# Forwards requests on a specific host port to a different port on a device. +# It can be helpful for debugging a mock web server. adb forward tcp:1212 tcp:1212 -#check the emulator has internet connection -adb shell "ping -c 1 www.google.com" +# Check emulator network before running tests. +check_ping_or_fail "10.0.2.2" "emulator host gateway" +check_ping_or_fail "8.8.8.8" "internet raw IP connectivity" +check_ping_or_fail "www.google.com" "internet DNS" +check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test DNS/reachability" echo "Emulator is ready" -set +o xtrace + +set +o xtrace \ No newline at end of file From c562beb80f1cff07f3de01b7dbad6d2f08b8d3f7 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 12:26:45 +0300 Subject: [PATCH 42/56] Modified ci-wait-for-emulator.sh and run-emulator.sh --- script/ci-wait-for-emulator.sh | 17 +++++++++-------- script/run-emulator.sh | 6 +++++- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index a9667110f..068f86410 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -5,7 +5,6 @@ # set -euo pipefail -set -o xtrace wait_for_boot_completed() { adb wait-for-device @@ -114,14 +113,18 @@ adb shell settings put global animator_duration_scale 0 # To test WKD we need to route all traffic for localhost:443 to localhost:1212 # as we can't use 443 directly for a mock web server. -adb root +echo "[debug] DNS before adb root" +adb shell dumpsys connectivity | grep -iE 'DnsAddresses|ServerAddress|Active default network' || true +adb shell ping -c 1 www.google.com || true -# adb root restarts adbd, so wait until the device is available again. +adb root wait_for_boot_completed - -# adb root can temporarily reset Android networking. wait_for_network_after_adb_root +echo "[debug] DNS after adb root" +adb shell dumpsys connectivity | grep -iE 'DnsAddresses|ServerAddress|Active default network' || true +adb shell ping -c 1 www.google.com || true + adb shell "echo 1 > /proc/sys/net/ipv4/ip_forward" adb shell "iptables -t nat -D OUTPUT -p tcp -d 127.0.0.1 --dport 443 -j REDIRECT --to-ports 1212" || true @@ -142,6 +145,4 @@ check_ping_or_fail "8.8.8.8" "internet raw IP connectivity" check_ping_or_fail "www.google.com" "internet DNS" check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test DNS/reachability" -echo "Emulator is ready" - -set +o xtrace \ No newline at end of file +echo "Emulator is ready" \ No newline at end of file diff --git a/script/run-emulator.sh b/script/run-emulator.sh index 899521302..54c1fe142 100755 --- a/script/run-emulator.sh +++ b/script/run-emulator.sh @@ -22,7 +22,11 @@ emulator_args=( -no-snapshot-load -no-snapshot-save -gpu "$EMULATOR_GPU_MODE" - -dns-server 10.0.2.2 + + # This value is used by the emulator process on the host. + # Android guest will still see 10.0.2.3 as DNS, but that proxy will forward to host 127.0.0.1. + -dns-server 127.0.0.1 + -read-only -no-metrics ) From 4a59a4703e01987add0e5c69228c31a52e7c3220 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 12:45:44 +0300 Subject: [PATCH 43/56] wip --- .../email/ui/gmailapi/SearchMessagesGmailApiFlowTest.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/SearchMessagesGmailApiFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/SearchMessagesGmailApiFlowTest.kt index 9b3cd5895..cc7ff3377 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/SearchMessagesGmailApiFlowTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/SearchMessagesGmailApiFlowTest.kt @@ -20,7 +20,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import com.flowcrypt.email.R import com.flowcrypt.email.TestConstants -import com.flowcrypt.email.junit.annotations.DebugTest import com.flowcrypt.email.junit.annotations.FlowCryptTestSettings import com.flowcrypt.email.matchers.CustomMatchers.Companion.withRecyclerViewItemCount import com.flowcrypt.email.rules.ClearAppSettingsRule @@ -50,7 +49,6 @@ import java.util.concurrent.TimeUnit @MediumTest @RunWith(AndroidJUnit4::class) @FlowCryptTestSettings(useCommonIdling = false) -@DebugTest class SearchMessagesGmailApiFlowTest : BaseGmailApiTest() { override val mockWebServerRule = From 4dc10833036df3a4d09424da2864164c5c14e9e8 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 12:48:06 +0300 Subject: [PATCH 44/56] Restored ci-instrumentation-tests-flaky.sh --- script/ci-instrumentation-tests-flaky.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/ci-instrumentation-tests-flaky.sh b/script/ci-instrumentation-tests-flaky.sh index 328a437db..26f122293 100755 --- a/script/ci-instrumentation-tests-flaky.sh +++ b/script/ci-instrumentation-tests-flaky.sh @@ -8,4 +8,4 @@ set -euo pipefail ./gradlew --console=plain --no-daemon --build-cache :FlowCrypt:connectedEnterpriseUiTestsAndroidTest \ - -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.DebugTestsFilter + -Pandroid.testInstrumentationRunnerArguments.filter=com.flowcrypt.email.junit.filters.ReadyForCIAndFlakyFilter From 1becdedc14bb9161d77c9f53d3526a645090706c Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 12:49:15 +0300 Subject: [PATCH 45/56] Restored build.gradle.kts --- FlowCrypt/build.gradle.kts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FlowCrypt/build.gradle.kts b/FlowCrypt/build.gradle.kts index 6f37c4d0f..c0e5ea7a3 100644 --- a/FlowCrypt/build.gradle.kts +++ b/FlowCrypt/build.gradle.kts @@ -125,10 +125,10 @@ android { "SHARED_TENANT_FES_URL", "\"https://flowcrypt.test/shared-tenant-fes/\"" ) - buildConfigField("boolean", "IS_HTTP_LOG_ENABLED", "true") - buildConfigField("String", "HTTP_LOG_LEVEL", "\"BODY\"") - resValue("string", "gradle_is_http_log_enabled", "true") - resValue("string", "gradle_http_log_level", "BODY") + buildConfigField("boolean", "IS_HTTP_LOG_ENABLED", "false") + buildConfigField("String", "HTTP_LOG_LEVEL", "\"NONE\"") + resValue("string", "gradle_is_http_log_enabled", "false") + resValue("string", "gradle_http_log_level", "NONE") } } From d62a1aa2a5ee51f56f1af2b0f584ec85ba40fa87 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 12:59:58 +0300 Subject: [PATCH 46/56] i(dns): align HttpsTestWebServer dnsmasq with CI DNS setup and add startup DNS checks --- docker/HttpsTestWebServer/Dockerfile | 2 +- docker/HttpsTestWebServer/dnsmasq.conf | 17 +++++++++++++---- docker/HttpsTestWebServer/entrypoint.sh | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/docker/HttpsTestWebServer/Dockerfile b/docker/HttpsTestWebServer/Dockerfile index 0127195bf..469578f35 100644 --- a/docker/HttpsTestWebServer/Dockerfile +++ b/docker/HttpsTestWebServer/Dockerfile @@ -1,6 +1,6 @@ FROM nginx:1.27-alpine -RUN apk add --no-cache dnsmasq +RUN apk add --no-cache dnsmasq bind-tools iputils COPY docker/HttpsTestWebServer/nginx.conf /etc/nginx/conf.d/default.conf diff --git a/docker/HttpsTestWebServer/dnsmasq.conf b/docker/HttpsTestWebServer/dnsmasq.conf index c637e314e..ee40202b0 100644 --- a/docker/HttpsTestWebServer/dnsmasq.conf +++ b/docker/HttpsTestWebServer/dnsmasq.conf @@ -1,8 +1,17 @@ -interface=docker0 bind-interfaces -listen-address=172.17.0.1 +listen-address=127.0.0.1 port=53 -address=/flowcrypt.test/127.0.0.1 +# Do not read /etc/resolv.conf to avoid recursive localhost DNS loops. +no-resolv + +# Upstream DNS for public domains. +server=8.8.8.8 +server=1.1.1.1 + +# Local test domains. +address=/test/127.0.0.1 +address=/localhost/127.0.0.1 + log-queries -log-facility=- \ No newline at end of file +log-facility=- diff --git a/docker/HttpsTestWebServer/entrypoint.sh b/docker/HttpsTestWebServer/entrypoint.sh index e3897be24..62e0ca088 100755 --- a/docker/HttpsTestWebServer/entrypoint.sh +++ b/docker/HttpsTestWebServer/entrypoint.sh @@ -8,4 +8,18 @@ set -e dnsmasq --no-daemon --conf-file=/etc/dnsmasq.d/flowcrypt-test.conf --log-facility=- & + +echo "Configuring resolver to use dnsmasq..." +printf "nameserver 127.0.0.1\n" > /etc/resolv.conf + +echo "Checking dnsmasq directly..." +dig @127.0.0.1 fel.localhost +short +dig @127.0.0.1 fel.flowcrypt.test +short +dig @127.0.0.1 www.google.com +short + +echo "Checking resolver..." +ping -c 1 fel.localhost +ping -c 1 fel.flowcrypt.test +ping -c 1 www.google.com + exec nginx -g 'daemon off;' From 6f069bd3b527f38bce383c065eb44f359ac34d2b Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 13:29:36 +0300 Subject: [PATCH 47/56] Restored some configurations in semaphore.yml --- .semaphore/semaphore.yml | 60 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index e1f1a648d..b7bdd0790 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -78,7 +78,65 @@ blocks: - name: 'Testing' task: jobs: - - name: 'Instrumentation tests(debug)' + - name: 'Lint(structural quality)' + execution_time_limit: + minutes: 15 + commands: + # run Lint checks + - ./script/ci-lint-checks.sh + + - name: 'JUnit tests' + execution_time_limit: + minutes: 15 + commands: + # run JUnit tests + - ./script/ci-junit-tests.sh + + - name: 'Instrumentation tests(No email server)' + execution_time_limit: + minutes: 60 + matrix: + - env_var: EMULATOR + values: [ "0", "1", "2", "3" ] + commands: + # Setup and run an emulator + - ./script/ci-setup-and-run-emulator.sh + # wait until ready + - ./script/ci-wait-for-emulator.sh + # Run filtered logging for TestRunner + - adb logcat -v color TestRunner:V *:S > ~/logcat_log.txt & + # Run instrumentation tests + - ./script/ci-instrumentation-tests-without-mailserver.sh 4 $EMULATOR + + - name: 'Instrumentation tests(with email server)' + execution_time_limit: + minutes: 60 + commands: + # Run an email server + - cd docker-mailserver && ./run_email_server.sh && cd - + # Setup and run an emulator + - ./script/ci-setup-and-run-emulator.sh + #wait until ready + - ./script/ci-wait-for-emulator.sh + # Run filtered logging for TestRunner + - adb logcat -v color TestRunner:V *:S > ~/logcat_log.txt & + # Run instrumentation tests + - ./script/ci-instrumentation-tests-with-mailserver.sh + + - name: 'Instrumentation tests(enterprise)' + execution_time_limit: + minutes: 60 + commands: + # Setup and run an emulator + - ./script/ci-setup-and-run-emulator.sh + #wait until ready + - ./script/ci-wait-for-emulator.sh + # Run filtered logging for TestRunner + - adb logcat -v color TestRunner:V *:S > ~/logcat_log.txt & + # Run instrumentation tests + - ./script/ci-instrumentation-tests-enterprise.sh + + - name: 'Instrumentation tests(flaky)' execution_time_limit: minutes: 60 commands: From f642175649f007e7f359c2acdfbf602f7ac6f5eb Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 14:13:27 +0300 Subject: [PATCH 48/56] Updated gradle to 9.5.1 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 24ac9f37b..8dcfaa9ce 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -5,7 +5,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From da8fa2355f383e810f55c96a4baabd7f5777820a Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 15:34:40 +0300 Subject: [PATCH 49/56] Improved ci-wait-for-emulator.sh --- script/ci-wait-for-emulator.sh | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index 068f86410..87abfe65d 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -15,7 +15,9 @@ wait_for_boot_completed() { wait_for_network_after_adb_root() { for attempt in {1..30}; do - if adb shell "ping -c 1 10.0.2.2" && adb shell "ping -c 1 8.8.8.8"; then + if adb shell "ping -c 1 10.0.2.2" >/dev/null 2>&1 \ + && adb shell "ping -c 1 8.8.8.8" >/dev/null 2>&1; then + echo "Emulator network is ready after adb root" return 0 fi @@ -24,7 +26,9 @@ wait_for_network_after_adb_root() { done echo "Emulator network did not become ready after adb root" + print_network_debug "network after adb root" + exit 1 } @@ -32,17 +36,21 @@ check_ping_or_fail() { local host="$1" local label="$2" - for attempt in {1..10}; do - if adb shell "ping -c 1 ${host}"; then + for attempt in {1..15}; do + if adb shell "ping -c 1 ${host}" >/dev/null 2>&1; then + echo "PASS: ${label}" return 0 fi - echo "Waiting for ${label}... attempt ${attempt}/10" + echo "Waiting for ${label}... attempt ${attempt}/15" + sleep 2 done - echo "Failed to ping ${host}: ${label}" + echo "FAIL: ${label}" + print_network_debug "${label}" + exit 1 } @@ -103,6 +111,7 @@ print_network_debug() { wait_for_boot_completed adb shell wm dismiss-keyguard + sleep 1 adb shell settings put global window_animation_scale 0 @@ -112,22 +121,32 @@ adb shell settings put global animator_duration_scale 0 ################################################################################################### # To test WKD we need to route all traffic for localhost:443 to localhost:1212 # as we can't use 443 directly for a mock web server. +################################################################################################### echo "[debug] DNS before adb root" + adb shell dumpsys connectivity | grep -iE 'DnsAddresses|ServerAddress|Active default network' || true adb shell ping -c 1 www.google.com || true +adb shell ping -c 1 fes.flowcrypt.test || true adb root + +# adb root restarts adbd, so wait until the device is available again. wait_for_boot_completed + +# Android networking may take some time to recover after adb root. wait_for_network_after_adb_root echo "[debug] DNS after adb root" + adb shell dumpsys connectivity | grep -iE 'DnsAddresses|ServerAddress|Active default network' || true adb shell ping -c 1 www.google.com || true +adb shell ping -c 1 fes.flowcrypt.test || true adb shell "echo 1 > /proc/sys/net/ipv4/ip_forward" adb shell "iptables -t nat -D OUTPUT -p tcp -d 127.0.0.1 --dport 443 -j REDIRECT --to-ports 1212" || true + adb shell "iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport 443 -j REDIRECT --to-ports 1212" adb shell "iptables -t nat -S OUTPUT" @@ -139,7 +158,10 @@ adb shell "iptables -t nat -S OUTPUT" # It can be helpful for debugging a mock web server. adb forward tcp:1212 tcp:1212 -# Check emulator network before running tests. +################################################################################################### +# Final network validation before running tests. +################################################################################################### + check_ping_or_fail "10.0.2.2" "emulator host gateway" check_ping_or_fail "8.8.8.8" "internet raw IP connectivity" check_ping_or_fail "www.google.com" "internet DNS" From 039354e2c662ce4561e31159b6a7d207426e6c91 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 15:39:35 +0300 Subject: [PATCH 50/56] docker(test-env): route emulator DNS via host dnsmasq and document host-network adb behavior --- docker/TestEnvironment/Dockerfile | 1 + docker/TestEnvironment/README.md | 25 +++++++++++++++++++++++++ docker/TestEnvironment/run.sh | 4 ++++ script/run-emulator.sh | 10 +++++++--- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/docker/TestEnvironment/Dockerfile b/docker/TestEnvironment/Dockerfile index c238d0862..dcddc6727 100644 --- a/docker/TestEnvironment/Dockerfile +++ b/docker/TestEnvironment/Dockerfile @@ -45,6 +45,7 @@ WORKDIR /workspace #RUN apt-get update && apt-get install -y --no-install-recommends \ # iputils-ping \ +# dnsutils \ # && rm -rf /var/lib/apt/lists/* CMD ["/bin/bash"] diff --git a/docker/TestEnvironment/README.md b/docker/TestEnvironment/README.md index ed1227327..2e8f5ba5d 100644 --- a/docker/TestEnvironment/README.md +++ b/docker/TestEnvironment/README.md @@ -45,16 +45,33 @@ The container includes the emulator binaries, but actual emulator launch usually - `/dev/kvm` passed through to the container - additional Docker flags such as `--device /dev/kvm` +If you need emulator DNS resolution for `*.localhost` / `*.flowcrypt.test` from +`docker/HttpsTestWebServer`, run that container first so host `dnsmasq` is available on +`127.0.0.1:53`. `run.sh` starts this container with `--network host --dns 127.0.0.1`. + +Because `--network host` is used, `adb` inside container can see host-side emulator devices +through host `adb` server. To avoid mixing devices, stop host emulator(s) before testing or run +container `adb` on another port (for example `export ADB_SERVER_PORT=5038`). + Use the helper script: ```bash ./docker/TestEnvironment/run.sh ``` +You can override DNS forwarded to emulator: + +```bash +EMULATOR_DNS_SERVER=127.0.0.1 ./docker/TestEnvironment/run.sh +``` + Equivalent manual command: ```bash docker run --rm -it \ + --network host \ + --dns 127.0.0.1 \ + -e EMULATOR_DNS_SERVER=127.0.0.1 \ --device /dev/kvm \ --name flowcrypt-android-test-env \ flowcrypt/android-test-env \ @@ -67,3 +84,11 @@ Then inside the container: /opt/flowcrypt/scripts/create-avd.sh /opt/flowcrypt/scripts/run-emulator.sh ``` + +DNS checks: + +```bash +dig @127.0.0.1 fel.flowcrypt.test +short +adb shell getprop net.dns1 +adb shell ping -c 1 fel.flowcrypt.test +``` diff --git a/docker/TestEnvironment/run.sh b/docker/TestEnvironment/run.sh index 888b00b0c..55c28cdfd 100755 --- a/docker/TestEnvironment/run.sh +++ b/docker/TestEnvironment/run.sh @@ -9,10 +9,14 @@ set -euo pipefail IMAGE_NAME="flowcrypt/android-test-env" CONTAINER_NAME="flowcrypt-android-test-env" +EMULATOR_DNS_SERVER="${EMULATOR_DNS_SERVER:-127.0.0.1}" docker rm -f "$CONTAINER_NAME" 2>/dev/null || true docker run --rm -it \ --name "$CONTAINER_NAME" \ + --network host \ + --dns 127.0.0.1 \ + -e EMULATOR_DNS_SERVER="$EMULATOR_DNS_SERVER" \ --device /dev/kvm \ "$IMAGE_NAME" diff --git a/script/run-emulator.sh b/script/run-emulator.sh index 54c1fe142..f4ecae7a3 100755 --- a/script/run-emulator.sh +++ b/script/run-emulator.sh @@ -10,6 +10,7 @@ set -euo pipefail AVD_NAME="${AVD_NAME:-ci-emulator}" EMULATOR_GPU_MODE="${EMULATOR_GPU_MODE:-swiftshader_indirect}" EMULATOR_WIPE_DATA="${EMULATOR_WIPE_DATA:-1}" +EMULATOR_DNS_SERVER="${EMULATOR_DNS_SERVER:-127.0.0.1}" "$ANDROID_HOME/emulator/emulator" -accel-check @@ -23,9 +24,10 @@ emulator_args=( -no-snapshot-save -gpu "$EMULATOR_GPU_MODE" - # This value is used by the emulator process on the host. - # Android guest will still see 10.0.2.3 as DNS, but that proxy will forward to host 127.0.0.1. - -dns-server 127.0.0.1 + # This value is used by the emulator process on the host/container. + # Android guest will still see 10.0.2.3 as DNS, but that proxy will forward + # to the DNS server configured here. + -dns-server "$EMULATOR_DNS_SERVER" -read-only -no-metrics @@ -36,3 +38,5 @@ if [[ "$EMULATOR_WIPE_DATA" == "1" ]]; then fi "$ANDROID_HOME/emulator/emulator" "${emulator_args[@]}" & + +echo "Started emulator with DNS server: $EMULATOR_DNS_SERVER" From 575da343a63d9cf98f42fe0dcb368a519a9f1c66 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Thu, 21 May 2026 16:07:24 +0300 Subject: [PATCH 51/56] Temporary disabled ComposeScreenPasswordProtectedDisallowedTermsReFetchConfigurationFlowTest.kt --- ...swordProtectedDisallowedTermsReFetchConfigurationFlowTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsReFetchConfigurationFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsReFetchConfigurationFlowTest.kt index 6da001deb..df6d76464 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsReFetchConfigurationFlowTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsReFetchConfigurationFlowTest.kt @@ -40,6 +40,7 @@ import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.RecordedRequest import org.hamcrest.Matchers.allOf +import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain @@ -63,6 +64,7 @@ import java.util.concurrent.TimeUnit subject = "", isNew = true ) +@Ignore("temporary disabled") class ComposeScreenPasswordProtectedDisallowedTermsReFetchConfigurationFlowTest : BaseComposeScreenPasswordProtectedDisallowedTermsTest( ACCOUNT_ENTITY_WITH_EXISTING_OPTIONAL_PARAMETERS From 82ec651b565ca5890a388b7291af2d50c6b6478b Mon Sep 17 00:00:00 2001 From: denbond7 Date: Thu, 21 May 2026 16:42:08 +0300 Subject: [PATCH 52/56] Improved ci-wait-for-emulator.sh --- script/ci-wait-for-emulator.sh | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/script/ci-wait-for-emulator.sh b/script/ci-wait-for-emulator.sh index 87abfe65d..8dd09e6f8 100755 --- a/script/ci-wait-for-emulator.sh +++ b/script/ci-wait-for-emulator.sh @@ -13,6 +13,21 @@ wait_for_boot_completed() { adb shell 'while [[ "$(getprop sys.boot_completed)" != "1" ]]; do sleep 1; done;' } +run_adb_root_or_fail() { + for attempt in {1..10}; do + if adb root; then + adb wait-for-device + return 0 + fi + + echo "adb root failed... attempt ${attempt}/10" + sleep 2 + done + + echo "adb root failed after 10 attempts" + exit 1 +} + wait_for_network_after_adb_root() { for attempt in {1..30}; do if adb shell "ping -c 1 10.0.2.2" >/dev/null 2>&1 \ @@ -129,7 +144,7 @@ adb shell dumpsys connectivity | grep -iE 'DnsAddresses|ServerAddress|Active def adb shell ping -c 1 www.google.com || true adb shell ping -c 1 fes.flowcrypt.test || true -adb root +run_adb_root_or_fail # adb root restarts adbd, so wait until the device is available again. wait_for_boot_completed @@ -167,4 +182,4 @@ check_ping_or_fail "8.8.8.8" "internet raw IP connectivity" check_ping_or_fail "www.google.com" "internet DNS" check_ping_or_fail "fes.flowcrypt.test" "flowcrypt.test DNS/reachability" -echo "Emulator is ready" \ No newline at end of file +echo "Emulator is ready" From e615d4d1d2b53d44674b1c0473118a93474b2d17 Mon Sep 17 00:00:00 2001 From: denbond7 Date: Thu, 21 May 2026 17:55:41 +0300 Subject: [PATCH 53/56] Updated docker/TestEnvironment/Dockerfile --- docker/TestEnvironment/Dockerfile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docker/TestEnvironment/Dockerfile b/docker/TestEnvironment/Dockerfile index dcddc6727..4d1182fcf 100644 --- a/docker/TestEnvironment/Dockerfile +++ b/docker/TestEnvironment/Dockerfile @@ -19,6 +19,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ unzip \ wget \ iputils-ping \ + dnsutils \ git \ sudo \ && rm -rf /var/lib/apt/lists/* @@ -43,9 +44,4 @@ RUN chmod +x /opt/flowcrypt/scripts/create-avd.sh /opt/flowcrypt/scripts/run-emu WORKDIR /workspace -#RUN apt-get update && apt-get install -y --no-install-recommends \ -# iputils-ping \ -# dnsutils \ -# && rm -rf /var/lib/apt/lists/* - CMD ["/bin/bash"] From f2905bb0b02efd20078f425f4b6df31cb9512d68 Mon Sep 17 00:00:00 2001 From: denbond7 Date: Thu, 21 May 2026 18:03:47 +0300 Subject: [PATCH 54/56] docs(readme): align DNS test setup docs with ci-setup-DNS.sh Alternative: docs: replace manual dnsmasq steps with ci-setup-DNS.sh usage --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0e5f81e1a..1fe5496c4 100644 --- a/README.md +++ b/README.md @@ -26,17 +26,19 @@ Please follow these steps to setup your virtual or physical device: `127.0.0.1:1212`. For that purpose, you can use the [script/ci-wait-for-emulator.sh](https://github.com/FlowCrypt/flowcrypt-android/blob/master/script/ci-wait-for-emulator.sh#L13) script. -- Additionally, the test environment should handle all requests to `*.flowcrypt.test`. All traffic to `*.flowcrypt.test` should be routed to `localhost (127.0.0.1)`. You can use a DNS server to do this. For example, using these commands: +- Additionally, the test environment should handle all requests to `*.flowcrypt.test`. All traffic to `*.flowcrypt.test` should be routed to `localhost (127.0.0.1)`. Use the `./script/ci-setup-DNS.sh` helper script: ```bash -1. sudo apt install -y dnsmasq resolvconf -2. echo "#added by flowcrypt" | sudo tee -a /etc/dnsmasq.conf -3. echo "listen-address=127.0.0.1" | sudo tee -a /etc/dnsmasq.conf -4. echo "address=/flowcrypt.test/127.0.0.1" | sudo tee -a /etc/dnsmasq.conf -5. echo "address=/localhost/127.0.0.1" | sudo tee -a /etc/dnsmasq.conf -6. sudo systemctl restart dnsmasq +./script/ci-setup-DNS.sh ``` +The script: +- Installs `dnsmasq` and `dnsutils`. +- Creates `/etc/dnsmasq.d/flowcrypt.conf` with local rules for `*.test` and `*.localhost`. +- Sets `no-resolv` with upstream DNS servers (`8.8.8.8` and `1.1.1.1`). +- Sets `/etc/resolv.conf` to `nameserver 127.0.0.1`. +- Restarts `dnsmasq` and verifies resolution with `dig` and `ping`. + ### ✔️ Test types We have two types of tests: From 63711c0ab8b15ccb8ccace4e9c1b9b02d2159ddb Mon Sep 17 00:00:00 2001 From: denbond7 Date: Thu, 21 May 2026 18:08:27 +0300 Subject: [PATCH 55/56] docs(readme): fixed redundant git difference --- README.md | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 1fe5496c4..e52046fa6 100644 --- a/README.md +++ b/README.md @@ -15,18 +15,8 @@ This guide follows Google's recommendations for [testing apps on Android](https: Please follow these steps to setup your virtual or physical device: - [Set up your test environment](https://developer.android.com/training/testing/espresso/setup#set-up-environment). -- Some of the tests use [MockWebServer](https://github.com/square/okhttp/tree/master/mockwebserver). - We run a local mock web server - via [FlowCryptMockWebServerRule](https://github.com/FlowCrypt/flowcrypt-android/blob/master/FlowCrypt/src/androidTest/java/com/flowcrypt/email/rules/FlowCryptMockWebServerRule.kt). - It - uses [TestConstants.MOCK_WEB_SERVER_PORT](https://github.com/FlowCrypt/flowcrypt-android/blob/master/FlowCrypt/src/androidTest/java/com/flowcrypt/email/TestConstants.kt#L19) - as a port. Unfortunately, the Android system doesn't allow us to use the `HTTPS 443` port by - default to run a web server on the `localhost (127.0.0.1)`. That's why we have to run a mock web - server on another port (for example, `1212`) and route all traffic from `127.0.0.1:443` to - `127.0.0.1:1212`. For that purpose, you can use - the [script/ci-wait-for-emulator.sh](https://github.com/FlowCrypt/flowcrypt-android/blob/master/script/ci-wait-for-emulator.sh#L13) - script. -- Additionally, the test environment should handle all requests to `*.flowcrypt.test`. All traffic to `*.flowcrypt.test` should be routed to `localhost (127.0.0.1)`. Use the `./script/ci-setup-DNS.sh` helper script: +- Some of the tests use [MockWebServer](https://github.com/square/okhttp/tree/master/mockwebserver). We run a local mock web server via [FlowCryptMockWebServerRule](https://github.com/FlowCrypt/flowcrypt-android/blob/master/FlowCrypt/src/androidTest/java/com/flowcrypt/email/rules/FlowCryptMockWebServerRule.kt). It uses [TestConstants.MOCK_WEB_SERVER_PORT](https://github.com/FlowCrypt/flowcrypt-android/blob/master/FlowCrypt/src/androidTest/java/com/flowcrypt/email/TestConstants.kt#L19) as a port. Unfortunately, the Android system doesn't allow us to use the `HTTPS 443` port by default to run a web server on the `localhost (127.0.0.1)`. That's why we have to run a mock web server on another port (for example, `1212`) and route all traffic from `127.0.0.1:443` to `127.0.0.1:1212`. For that purpose, you can use the [script/ci-wait-for-emulator.sh](https://github.com/FlowCrypt/flowcrypt-android/blob/master/script/ci-wait-for-emulator.sh#L13) script. +- Additionally, the test environment should handle all requests to `*.flowcrypt.test`. All traffic to `*.flowcrypt.test` should be routed to `localhost (127.0.0.1)`. You can use a DNS server to do this. For example, using these commands: ```bash ./script/ci-setup-DNS.sh From 51a607b0b773306f35b511c460df1b30518c1970 Mon Sep 17 00:00:00 2001 From: denbond7 Date: Thu, 21 May 2026 18:51:46 +0300 Subject: [PATCH 56/56] switched to use -gpu auto for emulators on CI --- script/ci-setup-and-run-emulator.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/ci-setup-and-run-emulator.sh b/script/ci-setup-and-run-emulator.sh index dbdff025b..04537a81a 100755 --- a/script/ci-setup-and-run-emulator.sh +++ b/script/ci-setup-and-run-emulator.sh @@ -8,7 +8,7 @@ set -euo pipefail AVD_RAM_SIZE=2048 ./script/create-avd.sh -EMULATOR_GPU_MODE=swiftshader_indirect \ +EMULATOR_GPU_MODE=auto \ EMULATOR_READ_ONLY=1 \ EMULATOR_WIPE_DATA=1 \ ./script/run-emulator.sh