[#525] Refactoring Integration Test Module

* [#525] Refactoring Integration Test Module

- Change module type to test + related Gradle scripts change
- Rename module to - remove the trailing Lib suffix + documentation
- Move module structure from test to main source

* Static checks result fix

* Temporary enable FTL test

* Test updated emulator.wft version

* Update locked emulator.wtf dependency version

* Enable emulator.wft tests for ui-integration-test module on CI

* Fix enable emulator.wft tests for ui-integration-test module on CI

- Update documentation

* Test Fladle configuration for ui-integration-test module

* Fix Fladle for ui-integration-test module

* Rename ui-integration-test module flade configuration

* Disable again FTL action from PRs

* Clear support for FTL from ui-integration-test module

* Refactor the emulator.wtf support across our modules.

* Fix Mermaid graph syntax

* Minor change in comment

* Fix ui-integration-test module run configuration

- Fixed manually
- Aligned with other run configurations
- Stable and beta versions of AS behave differentially, but after this fix it seems to be working as expected in both

* Update emulator.wtf run configuration

- Covered ui-integration-test module too
- Changed configuration name
This commit is contained in:
Honza Rychnovsky 2022-10-14 14:17:15 +02:00 committed by GitHub
parent 3d1d8fd363
commit bffa5870b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 91 additions and 60 deletions

View File

@ -294,7 +294,7 @@ jobs:
ORG_GRADLE_PROJECT_ZCASH_DEBUG_APP_NAME_SUFFIX: ""
ORG_GRADLE_PROJECT_ZCASH_EMULATOR_WTF_API_KEY: ${{ secrets.EMULATOR_WTF_API_KEY }}
run: |
./gradlew testDebugWithEmulatorWtf :app:testZcashmainnetDebugWithEmulatorWtf
./gradlew testDebugWithEmulatorWtf :app:testZcashmainnetDebugWithEmulatorWtf :ui-integration-test:testZcashmainnetDebugWithEmulatorWtf
- name: Collect Artifacts
if: ${{ always() }}
timeout-minutes: 1

View File

@ -1,15 +1,19 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="testDebugWithEmulatorWtf :app:testZcashmainnetDebugWithEmulatorWtf" type="GradleRunConfiguration" factoryName="Gradle">
<configuration default="false" name="testOnEmulatorWtf" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="testDebugWithEmulatorWtf :app:testZcashmainnetDebugWithEmulatorWtf" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list />
<list>
<option value="testDebugWithEmulatorWtf" />
<option value=":app:testZcashmainnetDebugWithEmulatorWtf" />
<option value=":ui-integration-test:testZcashmainnetDebugWithEmulatorWtf" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ui-integration-test:connectedCheck" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
<module name="zcash-android-app.ui-integration-test-lib" />
<module name="zcash-android-app.ui-integration-test" />
<option name="TESTING_TYPE" value="0" />
<option name="METHOD_NAME" value="" />
<option name="CLASS_NAME" value="" />
@ -13,6 +13,8 @@
<option name="RETENTION_COMPRESS_SNAPSHOTS" value="false" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
<option name="FORCE_STOP_RUNNING_APP" value="true" />
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />

View File

@ -315,27 +315,4 @@ fladle {
emulatorwtf {
directoriesToPull.set(listOf("/sdcard/googletest/test_outputfiles"))
val appMinSdkVersion = run {
@Suppress("MagicNumber", "PropertyName", "VariableNaming")
val EMULATOR_WTF_MIN_SDK = 23
val buildMinSdk = project.properties["ANDROID_APP_MIN_SDK_VERSION"].toString().toInt()
buildMinSdk.coerceAtLeast(EMULATOR_WTF_MIN_SDK).toString()
}
val targetSdkVersion = run {
@Suppress("MagicNumber", "PropertyName", "VariableNaming")
val EMULATOR_WTF_MAX_SDK = 31
val buildTargetSdk = project.properties["ANDROID_TARGET_SDK_VERSION"].toString().toInt()
buildTargetSdk.coerceAtMost(EMULATOR_WTF_MAX_SDK).toString()
}
devices.set(
listOf(
mapOf("model" to "Pixel2", "version" to appMinSdkVersion),
mapOf("model" to "Pixel2", "version" to targetSdkVersion)
)
)
}

View File

@ -166,7 +166,7 @@ org.ow2.asm:asm-util:9.1=runtimeClasspath
org.ow2.asm:asm:9.1=compileClasspath,runtimeClasspath
org.slf4j:slf4j-api:1.7.30=runtimeClasspath
org.tensorflow:tensorflow-lite-metadata:0.1.0-rc2=runtimeClasspath
wtf.emulator:gradle-plugin:0.0.10=compileClasspath,runtimeClasspath
wtf.emulator:gradle-plugin:0.0.12=compileClasspath,runtimeClasspath
xerces:xercesImpl:2.12.0=runtimeClasspath
xml-apis:xml-apis:1.4.01=runtimeClasspath
empty=annotationProcessor

View File

@ -52,6 +52,28 @@ pluginManager.withPlugin("com.android.library") {
}
}
pluginManager.withPlugin("com.android.test") {
project.the<com.android.build.gradle.TestExtension>().apply {
configureBaseExtension(isLibrary = true)
defaultConfig {
minSdk = project.property("ANDROID_LIB_MIN_SDK_VERSION").toString().toInt()
targetSdk = project.property("ANDROID_TARGET_SDK_VERSION").toString().toInt()
// The last two are for support of pseudolocales in debug builds.
// If we add other localizations, they should be included in this list.
// By explicitly setting supported locales, we strip out unused localizations from third party
// libraries (e.g. play services)
resourceConfigurations.addAll(listOf("en", "en-rUS", "en-rGB", "en-rAU", "en_XA", "ar_XB"))
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
testCoverage {
jacocoVersion = project.property("JACOCO_VERSION").toString()
}
}
}
@Suppress("LongMethod")
fun com.android.build.gradle.BaseExtension.configureBaseExtension(isLibrary: Boolean) {
compileSdkVersion(project.property("ANDROID_COMPILE_SDK_VERSION").toString().toInt())

View File

@ -7,6 +7,8 @@ val EMULATOR_WTF_MIN_SDK = 23
@Suppress("MagicNumber", "PropertyName", "VariableNaming")
val EMULATOR_WTF_MAX_SDK = 31
internal val className = this::class.simpleName
pluginManager.withPlugin("wtf.emulator.gradle") {
project.the<wtf.emulator.EwExtension>().apply {
val tokenString = project.properties["ZCASH_EMULATOR_WTF_API_KEY"].toString()
@ -14,8 +16,18 @@ pluginManager.withPlugin("wtf.emulator.gradle") {
token.set(tokenString)
}
val libraryMinSdkVersion = run {
val buildMinSdk = project.properties["ANDROID_LIB_MIN_SDK_VERSION"].toString().toInt()
val buildMinSdk = if (pluginManager.hasPlugin("com.android.application")) {
project.properties["ANDROID_APP_MIN_SDK_VERSION"].toString().toInt()
} else if (pluginManager.hasPlugin("com.android.test")) {
project.properties["ANDROID_APP_MIN_SDK_VERSION"].toString().toInt()
} else if (pluginManager.hasPlugin("com.android.library")) {
project.properties["ANDROID_LIB_MIN_SDK_VERSION"].toString().toInt()
} else {
throw IllegalArgumentException("Unsupported plugin type. Make sure that the plugin type you've added is" +
" supported by ${this.javaClass.name}.")
}
val moduleMinSdkVersion = run {
buildMinSdk.coerceAtLeast(EMULATOR_WTF_MIN_SDK).toString()
}
val targetSdkVersion = run {
@ -25,7 +37,7 @@ pluginManager.withPlugin("wtf.emulator.gradle") {
devices.set(
listOf(
mapOf("model" to "Pixel2", "version" to libraryMinSdkVersion),
mapOf("model" to "Pixel2", "version" to moduleMinSdkVersion),
mapOf("model" to "Pixel2", "version" to targetSdkVersion)
)
)

View File

@ -132,7 +132,7 @@ kover {
"spackle-android-lib",
"test-lib",
"ui-design-lib",
"ui-integration-test-lib",
"ui-integration-test",
"ui-lib",
)
}

View File

@ -166,7 +166,7 @@ org.ow2.asm:asm-util:9.1=classpath
org.ow2.asm:asm:9.1=classpath
org.slf4j:slf4j-api:1.7.30=classpath
org.tensorflow:tensorflow-lite-metadata:0.1.0-rc2=classpath
wtf.emulator:gradle-plugin:0.0.10=classpath
wtf.emulator:gradle-plugin:0.0.12=classpath
xerces:xercesImpl:2.12.0=classpath
xml-apis:xml-apis:1.4.01=classpath
xmlpull:xmlpull:1.1.3.1=classpath

View File

@ -32,7 +32,7 @@ The logical components of the app are implemented as a number of Gradle modules.
* ui
* `ui-design` — Contains UI theme elements only. Besides offering modularization, this allows for hiding of some Material Design components behind our own custom components.
* `ui-lib` — User interface that the user interacts with. This contains 99% of the UI code, along with localizations, icons, and other assets.
* `ui-integration-test-lib` — Is dedicated for integration tests only. It has Android Test Orchestrator turned on — it allows us to run each of our tests within its own invocation of Instrumentation, and thus brings us benefits for the testing environment (minimal shared state, crashes are isolated, permissions are reset).
* `ui-integration-test` — Is a pure test module dedicated for integration tests only. It has Android Test Orchestrator turned on — it allows us to run each of our tests within its own invocation of Instrumentation, and thus brings us benefits for the testing environment (minimal shared state, crashes are isolated, permissions are reset).
* preference
* `preference-api-lib` — Multiplatform interfaces for key-value storage of preferences.
* `preference-impl-android-lib` — Android-specific implementation for preference storage.
@ -68,11 +68,11 @@ The following diagram shows a rough depiction of dependencies between the module
subgraph ui
uiDesignLib[[ui-design-lib]];
uiLib[[ui-lib]];
uiIntegrationTestLib[[ui-integration-test-lib]];
uiIntegrationTest[[ui-integration-test]];
end
uiDesignLib[[ui-design-lib]] --> uiLib[[ui-lib]];
uiLib[[ui-lib]] --> uiIntegrationTestLib[[ui-integration-test-lib]];
uiDesignLib[[ui-design-lib]] -- > uiIntegrationTestLib[[ui-integration-test-lib]];
uiLib[[ui-lib]] --> uiIntegrationTest[[ui-integration-test]];
uiDesignLib[[ui-design-lib]] --> uiIntegrationTest[[ui-integration-test]];
subgraph spackle
spackleLib[[spackle-lib]];
spackleAndroidLib[[spackle-android-lib]];

View File

@ -160,7 +160,7 @@ For Continuous Integration, see [CI.md](CI.md). The rest of this section is reg
1. If you are an Electric Coin Co team member: We are still setting up a process for this, because emulator.wtf does not yet support individual API tokens
1. If you are an open source contributor: Visit http://emulator.wtf and request an API key
1. Set the emulator.wtf API key as a global Gradle property `ZCASH_EMULATOR_WTF_API_KEY` under `~/.gradle/gradle.properties`
1. Run the Gradle task `./gradlew testDebugWithEmulatorWtf :app:testZcashmainnetDebugWithEmulatorWtf` (emulator.wtf tasks do build the app, so you don't need to build them beforehand)
1. Run the Gradle task `./gradlew testDebugWithEmulatorWtf :app:testZcashmainnetDebugWithEmulatorWtf :ui-integration-test:testZcashmainnetDebugWithEmulatorWtf` (emulator.wtf tasks do build the app, so you don't need to build them beforehand)
## Testnet funds

View File

@ -96,7 +96,7 @@ ANDROID_NDK_VERSION=23.0.7599858
ANDROID_GRADLE_PLUGIN_VERSION=7.3.0
DETEKT_VERSION=1.21.0
EMULATOR_WTF_GRADLE_PLUGIN_VERSION=0.0.10
EMULATOR_WTF_GRADLE_PLUGIN_VERSION=0.0.12
FLANK_VERSION=21.09.0
FULLADLE_VERSION=0.17.4
GRADLE_VERSIONS_PLUGIN_VERSION=0.42.0

View File

@ -43,6 +43,7 @@ pluginManagement {
id("com.android.application") version (androidGradlePluginVersion) apply (false)
id("com.android.library") version (androidGradlePluginVersion) apply (false)
id("com.android.test") version (androidGradlePluginVersion) apply (false)
id("com.github.ben-manes.versions") version (extra["GRADLE_VERSIONS_PLUGIN_VERSION"].toString()) apply (false)
id("com.github.triplet.play") version (extra["PLAY_PUBLISHER_PLUGIN_VERSION"].toString()) apply (false)
id("com.osacky.fulladle") version (extra["FULLADLE_VERSION"].toString()) apply (false)
@ -270,7 +271,7 @@ include("spackle-lib")
include("spackle-android-lib")
include("test-lib")
include("ui-design-lib")
include("ui-integration-test-lib")
include("ui-integration-test")
include("ui-lib")
val zcashSdkIncludedBuildPath = extra["SDK_INCLUDED_BUILD_PATH"].toString()

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -1,16 +1,20 @@
plugins {
id("com.android.library")
id("com.android.test")
kotlin("android")
id("secant.android-build-conventions")
id("wtf.emulator.gradle")
id("secant.emulator-wtf-conventions")
}
// Force orchestrator to be used for this module, because we need cleared state to generate screenshots
// Force orchestrator to be used for this module, because we need cleared state before each test
val isOrchestratorEnabled = true
android {
namespace = "co.electriccoin.zcash.ui.integration"
// Target needs to be set to com.android.application type module
targetProjectPath = ":${projects.app.name}"
// Run tests in this module
experimentalProperties["android.experimental.self-instrumenting"] = true
defaultConfig {
if (isOrchestratorEnabled) {
@ -20,6 +24,17 @@ android {
testInstrumentationRunner = "co.electriccoin.zcash.test.ZcashUiTestRunner"
}
// Define the same flavors as in app module
flavorDimensions.add("network")
productFlavors {
create("zcashtestnet") {
dimension = "network"
}
create("zcashmainnet") {
dimension = "network"
}
}
if (isOrchestratorEnabled) {
testOptions {
execution = "ANDROIDX_TEST_ORCHESTRATOR"
@ -36,18 +51,18 @@ android {
}
dependencies {
androidTestImplementation(projects.uiLib)
androidTestImplementation(projects.uiDesignLib)
androidTestImplementation(projects.testLib)
androidTestImplementation(projects.spackleAndroidLib)
implementation(projects.uiLib)
implementation(projects.uiDesignLib)
implementation(projects.testLib)
implementation(projects.spackleAndroidLib)
androidTestImplementation(libs.bundles.androidx.test)
androidTestImplementation(libs.bundles.androidx.compose.core)
androidTestImplementation(libs.bundles.play.core)
implementation(libs.bundles.androidx.test)
implementation(libs.bundles.androidx.compose.core)
implementation(libs.bundles.play.core)
androidTestImplementation(libs.androidx.compose.test.junit)
androidTestImplementation(libs.androidx.navigation.compose)
androidTestImplementation(libs.androidx.uiAutomator)
implementation(libs.androidx.compose.test.junit)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.uiAutomator)
if (isOrchestratorEnabled) {
androidTestUtil(libs.androidx.test.orchestrator) {

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="zcash-ui-integration-test">

View File

@ -11,9 +11,11 @@ import androidx.test.uiautomator.UiSelector
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
fun getStringResource(@StringRes resId: Int) = ApplicationProvider.getApplicationContext<Context>().getString(resId)
fun getStringResource(@StringRes resId: Int) =
ApplicationProvider.getApplicationContext<Context>().getString(resId)
fun getStringResourceWithArgs(@StringRes resId: Int, vararg formatArgs: String) = ApplicationProvider.getApplicationContext<Context>().getString(resId, *formatArgs)
fun getStringResourceWithArgs(@StringRes resId: Int, vararg formatArgs: String) =
ApplicationProvider.getApplicationContext<Context>().getString(resId, *formatArgs)
// We're using indexes to find the right button, as it seems to be the best available way to test a click
// action on a permission button. These indexes remain the same for LTR as well as RTL layout direction.