Skip to content
Snippets Groups Projects
Unverified Commit e12aa6df authored by Matthias Urhahn's avatar Matthias Urhahn Committed by GitHub
Browse files

CircleCI config overhaul, instrumentation tests & screenshot automation...

CircleCI config overhaul, instrumentation tests & screenshot automation (EXPOSUREAPP-4480,DEV) (#2160)

* CircleCI overhaul and execution of instrumentation tests.

* Screenshots work, now run for release builds only.

* Tweak emulator to possibly improve performance.

* Compress results.

* Don't upload test xml files if circle CI already shows them.
Zip and upload test html reports instead of xml files.

Co-authored-by: default avatarharambasicluka <64483219+harambasicluka@users.noreply.github.com>
parent 70da3ab2
No related branches found
No related tags found
No related merge requests found
......@@ -2,16 +2,23 @@ version: 2.1
orbs:
android: circleci/android@0.2.1
sonarcloud: sonarsource/sonarcloud@1.0.2
#######################
# Commands section
# For code reuse.
#######################
commands:
install-ndk: android/install-ndk
restore-android-build-cache: android/restore-build-cache
save-android-build-cache: android/save-build-cache
scan-sonar: sonarcloud/scan
restore-gradle-cache:
description: "Restore gradle caches"
steps:
- restore_cache:
key: jars-{{ checksum "build.gradle" }}-{{ checksum "Corona-Warn-App/build.gradle" }}-{{ checksum "Server-Protocol-Buffer/build.gradle" }}
save-gradle-cache:
description: "Save gradle caches"
steps:
......@@ -19,17 +26,7 @@ commands:
paths:
- ~/.gradle
key: jars-{{ checksum "build.gradle" }}-{{ checksum "Corona-Warn-App/build.gradle" }}-{{ checksum "Server-Protocol-Buffer/build.gradle" }}
require-version-bump:
description: "Require version bump for binary assembling"
steps:
- run:
name: "Check if assemble required"
command: |
last_commit=$(git log -1 --pretty=%B)
if [[ $last_commit != *"Version bump"* ]]; then
circleci-agent step halt
echo "Skipping job"
fi
run-gradle-cmd:
description: "Running gradle command with environment options"
parameters:
......@@ -41,10 +38,19 @@ commands:
steps:
- run:
name: << parameters.desc >>
command: ./gradlew << parameters.cmd >>
command: >
./gradlew -PdisablePreDex
<< parameters.cmd >>
no_output_timeout: 30m
environment:
JVM_OPTS: -Xmx2048m
GRADLE_OPTS: -Xmx1536m -XX:+HeapDumpOnOutOfMemoryError -Dorg.gradle.caching=true -Dorg.gradle.configureondemand=true -Dkotlin.compiler.execution.strategy=in-process -Dkotlin.incremental=false
JVM_OPTS: -Xmx4096m
GRADLE_OPTS: >
-Xmx1536m -XX:+HeapDumpOnOutOfMemoryError
-Dorg.gradle.caching=true
-Dorg.gradle.configureondemand=true
-Dkotlin.compiler.execution.strategy=in-process
-Dkotlin.incremental=false
run-gradle-cmd-test-splitting:
description: "Running gradle command with environment options and test splitting"
parameters:
......@@ -59,13 +65,135 @@ commands:
command: circleci tests glob "**/test*/**/*.kt" | circleci tests split | xargs -n 1 echo
- run:
name: << parameters.desc >>
command: ./gradlew << parameters.cmd >> -i -PtestFilter="`circleci tests glob "**/test*/**/*.kt" | circleci tests split`"
command: >
./gradlew -PdisablePreDex
<< parameters.cmd >>
-i -PtestFilter="`circleci tests glob "**/test*/**/*.kt" | circleci tests split`"
environment:
JVM_OPTS: -Xmx2048m
GRADLE_OPTS: -Xmx1536m -XX:+HeapDumpOnOutOfMemoryError -Dorg.gradle.caching=true -Dorg.gradle.configureondemand=true -Dkotlin.compiler.execution.strategy=in-process -Dkotlin.incremental=false
JVM_OPTS: -Xmx4096m
GRADLE_OPTS: >
-Xmx1536m -XX:+HeapDumpOnOutOfMemoryError
-Dorg.gradle.caching=true
-Dorg.gradle.configureondemand=true
-Dkotlin.compiler.execution.strategy=in-process
-Dkotlin.incremental=false
skip-for-external-pull-requests:
description: "Skip for external pull requests due to missing access to secrets."
steps:
- run:
name: Early return if this build is from a forked PR
command: |
if [ -n "$CIRCLE_PR_NUMBER" ]; then
echo "Nothing to do for forked PRs, so marking this step successful"
circleci step halt
fi
setup-android-macos:
description: "Setup Android environment on macOS executor"
steps:
- restore_cache:
key: android-sdk-v1-{{ arch }}-{{ checksum ".circleci/install-android-sdk.sh" }}
- run:
name: Set ANDROID_SDK_ROOT environment variable
command: echo 'export ANDROID_SDK_ROOT=$HOME/android-sdk' >> $BASH_ENV
- run:
name: Install Android SDK
command: |
sh .circleci/install-android-sdk.sh
echo 'export PATH=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$PATH' >> $BASH_ENV
echo 'export PATH=$ANDROID_SDK_ROOT/cmdline-tools/latest:$PATH' >> $BASH_ENV
echo 'export PATH=$ANDROID_SDK_ROOT/platform-tools:$PATH' >> $BASH_ENV
echo 'export PATH=$ANDROID_SDK_ROOT/emulator:$PATH' >> $BASH_ENV
echo 'export PATH=$ANDROID_SDK_ROOT/build-tools/29.0.3:$PATH' >> $BASH_ENV
source $BASH_ENV
sdkmanager --list
- save_cache:
key: android-sdk-v1-{{ arch }}-{{ checksum ".circleci/install-android-sdk.sh" }}
paths:
- /Users/distiller/android-sdk
run-emulator-for-api:
parameters:
apilevel:
type: integer
description: "Setup and start emulator for API<< parameters.apilevel >>"
steps:
- run:
name: Create emulator AVD
command: >
echo "no" | avdmanager --verbose create avd --force
--name "emulator_API<< parameters.apilevel >>"
--package "system-images;android-<< parameters.apilevel >>;google_apis;x86_64"
- run:
name: Configure emulator settings
command: |
cd $HOME/.android/avd/emulator_API<< parameters.apilevel >>.avd
sed -i '' -e 's/hw.lcd.density=[0-9]*/hw.lcd.density=560/g' config.ini
sed -i '' -e 's/hw.lcd.height=[0-9]*/hw.lcd.height=2880/g' config.ini
sed -i '' -e 's/hw.lcd.width=[0-9]*/hw.lcd.width=1440/g' config.ini
sed -i '' -e 's/hw.ramSize=[0-9]*/hw.ramSize=1536/g' config.ini
- run:
name: Check host state
command: |
emulator -accel-check
emulator -version
- run:
name: Start emulator AVD
command: >
emulator @emulator_API<< parameters.apilevel >>
-no-window
-no-audio
-no-boot-anim
-memory 2048
-nojni
background: true
no_output_timeout: 60m
- run:
name: Wait for emulator
command: |
adb wait-for-device shell 'while [[ -z $(getprop dev.bootcomplete) ]]; do sleep 1; done;'
adb devices
sleep 5
- run:
name: Disable animations
command: |
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
kill-all-emulators:
description: "Kill all emulators"
steps:
- run:
name: Kill all emulators
command: |
adb devices | grep emulator | cut -f1 | while read line; do adb -s $line emu kill; done
sleep 2
adb kill-server
sleep 2
compress-path:
parameters:
input:
type: string
output:
type: string
description: "Compress << parameters.input >> to << parameters.output >>"
steps:
- run:
name: Compress files
command: >
zip -r
<< parameters.output >>
<< parameters.input >>
#######################
# Jobs section
# Tasks that get executed
#######################
jobs:
quick_build_device_release_no_tests:
detekt:
executor: android/android
resource_class: large
working_directory: ~/project
......@@ -73,7 +201,21 @@ jobs:
- checkout
- restore-gradle-cache
- restore-android-build-cache
- require-version-bump
- run-gradle-cmd:
desc: Detekt check
cmd: ":Corona-Warn-App:detekt"
- store_artifacts:
path: Corona-Warn-App/build/reports
destination: reports
quick_build_device_release_no_tests:
executor: android/android
resource_class: xlarge
working_directory: ~/project
steps:
- checkout
- restore-gradle-cache
- restore-android-build-cache
- run-gradle-cmd:
desc: Quick Build
cmd: "assembleDeviceRelease"
......@@ -82,15 +224,15 @@ jobs:
- store_artifacts:
path: Corona-Warn-App/build/reports
destination: reports
quick_build_device_for_testers_release_no_tests:
executor: android/android
resource_class: large
resource_class: xlarge
working_directory: ~/project
steps:
- checkout
- restore-gradle-cache
- restore-android-build-cache
- require-version-bump
- run-gradle-cmd:
desc: Quick Build
cmd: ":Corona-Warn-App:assembleDeviceForTestersRelease"
......@@ -99,9 +241,10 @@ jobs:
- store_artifacts:
path: Corona-Warn-App/build/reports
destination: reports
device_release_unit_tests:
unit_tests_device_release:
executor: android/android
resource_class: large
resource_class: xlarge
working_directory: ~/project
parallelism: 3
steps:
......@@ -113,18 +256,22 @@ jobs:
cmd: ":Corona-Warn-App:testDeviceReleaseUnitTest -i"
- save-gradle-cache
- save-android-build-cache
- store_artifacts:
path: Corona-Warn-App/build/reports
destination: reports
- store_test_results:
path: Corona-Warn-App/build/test-results
- persist_to_workspace:
root: /home/circleci
paths:
- ./project
device_for_testers_release_unit_tests:
- compress-path:
input: ./Corona-Warn-App/build/reports
output: /tmp/unit_tests_device_release.zip
- store_artifacts:
path: /tmp/unit_tests_device_release.zip
destination: zips/unit_tests_device_release.zip
unit_tests_device_for_testers_release:
executor: android/android
resource_class: large
resource_class: xlarge
working_directory: ~/project
parallelism: 3
steps:
......@@ -136,14 +283,19 @@ jobs:
cmd: ":Corona-Warn-App:testDeviceForTestersReleaseUnitTest"
- save-gradle-cache
- save-android-build-cache
- store_artifacts:
path: Corona-Warn-App/build/reports
destination: reports
- store_test_results:
path: Corona-Warn-App/build/test-results
- compress-path:
input: ./Corona-Warn-App/build/reports
output: /tmp/unit_tests_device_for_testers_release.zip
- store_artifacts:
path: /tmp/unit_tests_device_for_testers_release.zip
destination: zips/unit_tests_device_for_testers_release.zip
lint_device_release_check:
executor: android/android
resource_class: large
resource_class: xlarge
working_directory: ~/project
steps:
- checkout
......@@ -155,23 +307,10 @@ jobs:
- store_artifacts:
path: Corona-Warn-App/build/reports
destination: reports
ktlint_device_release_check:
executor: android/android
resource_class: medium
working_directory: ~/project
steps:
- checkout
- restore-gradle-cache
- restore-android-build-cache
- run-gradle-cmd:
desc: Ktlint check deviceRelease
cmd: ":Corona-Warn-App:ktlintDeviceReleaseCheck"
- store_artifacts:
path: Corona-Warn-App/build/reports
destination: reports
lint_device_for_testers_release_check:
executor: android/android
resource_class: large
resource_class: xlarge
working_directory: ~/project
steps:
- checkout
......@@ -183,7 +322,8 @@ jobs:
- store_artifacts:
path: Corona-Warn-App/build/reports
destination: reports
ktlint_device_for_testers_release_check:
ktlint_device_release_check:
executor: android/android
resource_class: medium
working_directory: ~/project
......@@ -192,12 +332,13 @@ jobs:
- restore-gradle-cache
- restore-android-build-cache
- run-gradle-cmd:
desc: Ktlint check deviceForTestersRelease
cmd: ":Corona-Warn-App:ktlintDeviceForTestersReleaseCheck"
desc: Ktlint check deviceRelease
cmd: ":Corona-Warn-App:ktlintDeviceReleaseCheck"
- store_artifacts:
path: Corona-Warn-App/build/reports
destination: reports
detekt:
ktlint_device_for_testers_release_check:
executor: android/android
resource_class: medium
working_directory: ~/project
......@@ -206,16 +347,18 @@ jobs:
- restore-gradle-cache
- restore-android-build-cache
- run-gradle-cmd:
desc: Detekt check
cmd: ":Corona-Warn-App:detekt"
desc: Ktlint check deviceForTestersRelease
cmd: ":Corona-Warn-App:ktlintDeviceForTestersReleaseCheck"
- store_artifacts:
path: Corona-Warn-App/build/reports
destination: reports
run_sonar:
executor: android/android
resource_class: large
resource_class: xlarge
working_directory: ~/project
steps:
- skip-for-external-pull-requests
- attach_workspace:
at: /home/circleci
- restore-gradle-cache
......@@ -224,9 +367,10 @@ jobs:
desc: JaCoCo report
cmd: ":Corona-Warn-App:jacocoTestReportDeviceRelease -i"
- scan-sonar
quick_build_device_for_testers_signed:
executor: android/android
resource_class: large
resource_class: xlarge
working_directory: ~/project
steps:
- checkout
......@@ -276,22 +420,101 @@ jobs:
curl --location --request POST $tsystems_upload_url \
--header "Authorization: Bearer $tsystems_upload_bearer" \
--form "file=@${fileName}" \
instrumentation_tests_device:
macos:
xcode: "12.3.0"
resource_class: large
steps:
- checkout
- setup-android-macos
- run-gradle-cmd:
desc: Build Apks for instrumentation test
cmd: >
:Corona-Warn-App:assembleDeviceDebug
:Corona-Warn-App:assembleDeviceDebugAndroidTest
- run-emulator-for-api:
apilevel: 29
- run-gradle-cmd:
desc: Run instrumentation tests on device
cmd: >
:Corona-Warn-App:connectedDeviceDebugAndroidTest
--info --continue
-Pandroid.testInstrumentationRunnerArguments.notAnnotation=testhelpers.Screenshot
-Pandroid.testInstrumentationRunnerArguments.clearPackageData=true
- kill-all-emulators
- store_test_results:
path: Corona-Warn-App/build/outputs/androidTest-results
- compress-path:
input: ./Corona-Warn-App/build/reports
output: /tmp/instrumentation_tests_device.zip
- store_artifacts:
path: /tmp/instrumentation_tests_device.zip
destination: zips/instrumentation_tests_device.zip
device_screenshots:
macos:
xcode: "12.3.0"
resource_class: large
steps:
- checkout
- restore_cache:
key: gem-cache-v1-{{ arch }}-{{ checksum "Gemfile.lock" }}
- run: bundle check || bundle install --path vendor/bundle
- save_cache:
key: gem-cache-v1-{{ arch }}-{{ checksum "Gemfile.lock" }}
paths:
- vendor/bundle
- setup-android-macos
- run-gradle-cmd:
desc: Build APKs for screenshots
cmd: >
:Corona-Warn-App:assembleDebug
:Corona-Warn-App:assembleAndroidTest
- run-emulator-for-api:
apilevel: 29
- run:
name: Run fastlane screengrab
command: |
for i in {1..5}; do bundle exec fastlane screengrab && break || sleep 15; done
no_output_timeout: 30m
- kill-all-emulators
- store_artifacts:
path: fastlane/metadata/android
destination: screenshots
- compress-path:
input: ./fastlane/metadata/android
output: /tmp/device_screenshots.zip
- store_artifacts:
path: /tmp/device_screenshots.zip
destination: zips/device_screenshots.zip
#######################
# Workflow section
# Job execution orders
#######################
workflows:
version: 2
quick_build:
check_buildtype_device:
jobs:
- quick_build_device_release_no_tests
- quick_build_device_for_testers_release_no_tests
- device_release_unit_tests
- device_for_testers_release_unit_tests
- detekt
- lint_device_release_check
- lint_device_for_testers_release_check
- ktlint_device_release_check
- ktlint_device_for_testers_release_check
- detekt
- quick_build_device_release_no_tests
- unit_tests_device_release
- run_sonar:
requires:
- device_release_unit_tests
- unit_tests_device_release
- instrumentation_tests_device
check_buildtype_device_for_testers:
jobs:
- detekt
- lint_device_for_testers_release_check
- ktlint_device_for_testers_release_check
- quick_build_device_for_testers_release_no_tests
- unit_tests_device_for_testers_release
signed_build:
jobs:
- quick_build_device_for_testers_signed:
......@@ -301,3 +524,6 @@ workflows:
- /^v.*/
branches:
ignore: /.*/
- device_screenshots:
requires:
- quick_build_device_for_testers_signed
\ No newline at end of file
if [ -d $ANDROID_SDK_ROOT ]
then
echo "Directory $ANDROID_SDK_ROOT already exists so we're skipping the install. If you'd like to install fresh tools, edit this script to invalidate the CI cache..."
exit 0
fi
mkdir -p $ANDROID_SDK_ROOT
cd $ANDROID_SDK_ROOT
curl https://dl.google.com/android/repository/commandlinetools-mac-6858069_latest.zip -o cmdline-tools.zip
unzip cmdline-tools.zip -d $ANDROID_SDK_ROOT/cmdline-tools
mv $ANDROID_SDK_ROOT/cmdline-tools/cmdline-tools $ANDROID_SDK_ROOT/cmdline-tools/latest
mkdir -p "$ANDROID_SDK_ROOT/licenses"
echo "24333f8a63b6825ea9c5514f83c2829b004d1fee" > "$ANDROID_SDK_ROOT/licenses/android-sdk-license"
echo "84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_SDK_ROOT/licenses/android-sdk-preview-license"
echo "d975f751698a77b662f1254ddbeed3901e976f5a" > "$ANDROID_SDK_ROOT/licenses/intel-android-extra-license"
SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager
$SDKMANAGER "platform-tools"
$SDKMANAGER "platforms;android-29"
$SDKMANAGER "build-tools;29.0.3"
$SDKMANAGER "ndk-bundle"
$SDKMANAGER "system-images;android-29;google_apis;x86_64"
$SDKMANAGER "emulator"
echo "y" | sudo $SDKMANAGER --install "ndk;21.2.6472646" --sdk_root=${ANDROID_SDK_ROOT}
\ No newline at end of file
......@@ -12,4 +12,5 @@
/test_environments.json
*.jks
/keystore.properties
.local/*
fastlane/metadata
......@@ -196,6 +196,12 @@ android {
includeAndroidResources = true
returnDefaultValues = true
}
// Using orchestration toegether with mockk on x86 (32bit) emulator images crashes
// Leaving this in here as reminder
// https://github.com/android/android-test/issues/352
// https://github.com/mockk/mockk/issues/466
// execution 'ANDROIDX_TEST_ORCHESTRATOR'
}
kapt {
......@@ -317,9 +323,11 @@ dependencies {
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
kaptTest "com.google.dagger:dagger-compiler:$dagger_version"
kaptAndroidTest "com.google.dagger:dagger-compiler:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
kaptTest "com.google.dagger:dagger-android-processor:$dagger_version"
kaptAndroidTest "com.google.dagger:dagger-android-processor:$dagger_version"
def assisted_injection_version = "0.6.0"
compileOnly "com.squareup.inject:assisted-inject-annotations-dagger2:$assisted_injection_version"
......
......@@ -14,22 +14,26 @@ object DiaryData {
val LIST_ITEMS = listOf(
ListItem.Data(
R.drawable.ic_contact_diary_person_item,
"Max Mustermann"
"Max Mustermann",
ListItem.Type.PERSON
),
ListItem.Data(
R.drawable.ic_contact_diary_person_item,
"Erika Mustermann"
"Erika Mustermann",
ListItem.Type.PERSON
),
ListItem.Data(
R.drawable.ic_contact_diary_location,
"Fitnessstudio"
"Fitnessstudio",
ListItem.Type.LOCATION
),
ListItem.Data(
R.drawable.ic_contact_diary_location,
"Supermarket"
"Supermarket",
ListItem.Type.LOCATION
)
)
......
......@@ -2,13 +2,26 @@ package de.rki.coronawarnapp.util.security
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository
import de.rki.coronawarnapp.storage.AppDatabase
import de.rki.coronawarnapp.storage.tracing.TracingIntervalEntity
import de.rki.coronawarnapp.storage.tracing.TracingIntervalRepository
import de.rki.coronawarnapp.util.di.AppInjector
import de.rki.coronawarnapp.util.di.ApplicationComponent
import io.kotest.matchers.shouldBe
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.clearAllMocks
import io.mockk.coEvery
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.just
import io.mockk.mockkObject
import kotlinx.coroutines.runBlocking
import net.sqlcipher.database.SQLiteException
import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.not
import org.junit.After
import org.junit.Assert.assertThat
import org.junit.Assert.assertTrue
import org.junit.Before
......@@ -19,6 +32,11 @@ import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class DBPasswordTest {
@MockK lateinit var applicationComponent: ApplicationComponent
@MockK lateinit var encryptedSharedPreferencesFactory: EncryptedPreferencesFactory
@MockK lateinit var errorResetTool: EncryptionErrorResetTool
@MockK lateinit var keyCacheRepository: KeyCacheRepository
private val appContext: Context
get() = ApplicationProvider.getApplicationContext()
......@@ -27,10 +45,29 @@ class DBPasswordTest {
@Before
fun setUp() {
MockKAnnotations.init(this)
mockkObject(AppInjector)
every { AppInjector.component } returns applicationComponent
encryptedSharedPreferencesFactory = EncryptedPreferencesFactory(appContext)
every { applicationComponent.encryptedPreferencesFactory } returns encryptedSharedPreferencesFactory
every { applicationComponent.errorResetTool } returns errorResetTool
every { applicationComponent.keyCacheRepository } returns keyCacheRepository.apply {
coEvery { keyCacheRepository.clear() } just Runs
}
mockkObject(TracingIntervalRepository)
every { TracingIntervalRepository.resetInstance() } just Runs
clearSharedPreferences()
AppDatabase.reset(appContext)
}
@After
fun teardown() {
clearAllMocks()
}
@Test
fun generatesPassphraseInCorrectLength() {
val passphrase = SecurityHelper.getDBPassword()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment