diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index ac89f26f..b5ac4145 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -387,7 +387,7 @@ jobs: with: name: Release binaries - name: Robo test - timeout-minutes: 15 + timeout-minutes: 20 env: # Path depends on `release_build` job, plus path of `Download a single artifact` step BINARIES_ZIP_PATH: binaries.zip diff --git a/.idea/runConfigurations/testDebugWithEmulatorWtf.xml b/.idea/runConfigurations/testDebugWithEmulatorWtf__app_testZcashmainnetDebugWithEmulatorWtf.xml similarity index 68% rename from .idea/runConfigurations/testDebugWithEmulatorWtf.xml rename to .idea/runConfigurations/testDebugWithEmulatorWtf__app_testZcashmainnetDebugWithEmulatorWtf.xml index 9603a184..91ad73de 100644 --- a/.idea/runConfigurations/testDebugWithEmulatorWtf.xml +++ b/.idea/runConfigurations/testDebugWithEmulatorWtf__app_testZcashmainnetDebugWithEmulatorWtf.xml @@ -1,17 +1,15 @@ - + diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 29ab04ef..b5274392 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -101,6 +101,7 @@ android { listOf( "**/*.kotlin_metadata", ".readme", + "build-data.properties", "META-INF/*.kotlin_module", "META-INF/android.arch**", "META-INF/androidx**", @@ -110,7 +111,6 @@ android { "META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor", "META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar", "META-INF/services/org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages\$Extension", - "build-data.properties", "firebase-**.properties", "kotlin/**", "play-services-**.properties", @@ -206,27 +206,6 @@ if (googlePlayServiceKeyFilePath.isNotEmpty()) { } } -val reportsDirectory = "${buildDir}/reports/androidTests/connected" - -// This is coordinated with `EccScreenCaptureProcessor` -val onDeviceScreenshotsDirectory = "/sdcard/Pictures/zcash_screenshots" - -val clearScreenshotsTask = tasks.create("clearScreenshots") { - executable = project.android.adbExecutable.absolutePath - args = listOf("shell", "rm", "-r", onDeviceScreenshotsDirectory) -} - -val fetchScreenshotsTask = tasks.create("fetchScreenshots") { - executable = project.android.adbExecutable.absolutePath - args = listOf("pull", onDeviceScreenshotsDirectory, reportsDirectory) - finalizedBy(clearScreenshotsTask) -} - -tasks.whenTaskAdded { - if (name == "connectedZcashmainnetDebugAndroidTest") { - finalizedBy(fetchScreenshotsTask) - } -} fladle { // Firebase Test Lab has min and max values that might differ from our project's @@ -265,7 +244,7 @@ fladle { } ) - testTimeout.set("5m") + testTimeout.set("3m") devices.addAll( mapOf("model" to "Pixel2", "version" to minSdkVersion), @@ -278,13 +257,19 @@ fladle { } emulatorwtf { - // This path needs to be coordinated with the implementation in the app module's tests - directoriesToPull.set(listOf("/sdcard/Pictures/zcash_screenshots")) + 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() + } devices.set( listOf( - // TODO [#285]: Our screenshot tests don't work on older devices - // mapOf("model" to "Pixel2", "version" to minSdkVersion), + mapOf("model" to "Pixel2", "version" to appMinSdkVersion), // TODO [#430]: App won't run on API 31 Intel emulators @Suppress("MagicNumber") mapOf("model" to "Pixel2", "version" to 30) diff --git a/app/src/androidTest/AndroidManifest.xml b/app/src/androidTest/AndroidManifest.xml index f96e8cee..0b472d7c 100644 --- a/app/src/androidTest/AndroidManifest.xml +++ b/app/src/androidTest/AndroidManifest.xml @@ -1,10 +1,4 @@ - - - - - + diff --git a/app/src/androidTest/java/co/electriccoin/zcash/app/ScreenshotTest.kt b/app/src/androidTest/java/co/electriccoin/zcash/app/ScreenshotTest.kt index 25bf7116..c52c56a8 100644 --- a/app/src/androidTest/java/co/electriccoin/zcash/app/ScreenshotTest.kt +++ b/app/src/androidTest/java/co/electriccoin/zcash/app/ScreenshotTest.kt @@ -1,8 +1,5 @@ package co.electriccoin.zcash.app -import android.Manifest -import android.content.pm.PackageManager -import android.os.Build import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.hasContentDescription import androidx.compose.ui.test.hasTestTag @@ -18,16 +15,15 @@ import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollTo import androidx.compose.ui.test.performTextInput import androidx.test.core.app.ApplicationProvider +import androidx.test.core.graphics.writeToTestStorage +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.espresso.screenshot.captureToBitmap import androidx.test.ext.junit.rules.ActivityScenarioRule -import androidx.test.filters.SdkSuppress import androidx.test.filters.SmallTest -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.rule.GrantPermissionRule -import androidx.test.runner.screenshot.Screenshot import cash.z.ecc.sdk.ext.ui.model.MonetarySeparators import cash.z.ecc.sdk.fixture.SeedPhraseFixture import cash.z.ecc.sdk.fixture.WalletAddressFixture -import co.electriccoin.zcash.app.test.EccScreenCaptureProcessor import co.electriccoin.zcash.app.test.getStringResource import co.electriccoin.zcash.spackle.FirebaseTestLabUtil import co.electriccoin.zcash.test.UiTestPrerequisites @@ -39,44 +35,21 @@ import co.electriccoin.zcash.ui.screen.restore.RestoreTag import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -import org.junit.BeforeClass import org.junit.Rule import org.junit.Test -import org.junit.rules.RuleChain -// TODO [#285]: Screenshot tests fail on older devices due to issue granting external storage permission -@SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q) class ScreenshotTest : UiTestPrerequisites() { companion object { - @BeforeClass - @JvmStatic - fun setupPPlus() { - if (Build.VERSION_CODES.P <= Build.VERSION.SDK_INT) { - val instrumentation = InstrumentationRegistry.getInstrumentation() - if (PackageManager.PERMISSION_DENIED == instrumentation.context.checkCallingOrSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - instrumentation.uiAutomation.grantRuntimePermission(instrumentation.context.packageName, Manifest.permission.WRITE_EXTERNAL_STORAGE) - } - } - } - fun takeScreenshot(screenshotName: String) { - val screenshot = Screenshot.capture().apply { - name = screenshotName - } - screenshot.process(setOf(EccScreenCaptureProcessor.new())) + onView(isRoot()) + .captureToBitmap() + .writeToTestStorage(screenshotName) } } - private val composeTestRule = createAndroidComposeRule(MainActivity::class.java) - @get:Rule - val ruleChain = if (Build.VERSION_CODES.P <= Build.VERSION.SDK_INT) { - composeTestRule - } else { - val runtimePermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE) - RuleChain.outerRule(runtimePermissionRule).around(composeTestRule) - } + val composeTestRule = createAndroidComposeRule(MainActivity::class.java) private fun navigateTo(route: String) = runBlocking { withContext(Dispatchers.Main) { diff --git a/app/src/androidTest/java/co/electriccoin/zcash/app/test/EccScreenCaptureProcessor.kt b/app/src/androidTest/java/co/electriccoin/zcash/app/test/EccScreenCaptureProcessor.kt deleted file mode 100644 index 0f5fd922..00000000 --- a/app/src/androidTest/java/co/electriccoin/zcash/app/test/EccScreenCaptureProcessor.kt +++ /dev/null @@ -1,55 +0,0 @@ -package co.electriccoin.zcash.app.test - -import android.os.Build -import android.os.Environment -import androidx.test.runner.screenshot.ScreenCapture -import androidx.test.runner.screenshot.ScreenCaptureProcessor -import java.io.BufferedOutputStream -import java.io.File -import java.io.FileOutputStream -import java.io.IOException -import java.util.UUID - -class EccScreenCaptureProcessor private constructor(private val screenshotDir: File) : ScreenCaptureProcessor { - - @Throws(IOException::class) - override fun process(capture: ScreenCapture): String { - screenshotDir.checkDirectoryIsWriteable() - - val filename = newFilename( - name = capture.name ?: "", - suffix = capture.format.toString().lowercase() - ) - - BufferedOutputStream(FileOutputStream(File(screenshotDir, filename))).use { - capture.bitmap.compress(capture.format, DEFAULT_QUALITY, it) - it.flush() - } - - return filename - } - - companion object { - const val DEFAULT_QUALITY = 100 - - fun new(): EccScreenCaptureProcessor { - - // Screenshots need to be stored in a public directory so they won't get cleared by Test Orchestrator - // This path must be coordinated with the build.gradle.kts script which copies these off the device - @Suppress("DEPRECATION") - val screenshotsDirectory = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "zcash_screenshots").also { - it.mkdirs() - } - - return EccScreenCaptureProcessor(screenshotsDirectory) - } - - private fun newFilename(name: String, suffix: String) = "screenshot-$name-${Build.VERSION.SDK_INT}-${Build.DEVICE}-${UUID.randomUUID()}.$suffix" - } -} - -private fun File.checkDirectoryIsWriteable() { - if (!isDirectory && !canWrite()) { - throw IOException("The directory $this does not exist or is not writable.") - } -} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 68809d92..21c5beab 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,9 +1,9 @@ + package="co.electriccoin.zcash.app"> @@ -11,7 +11,7 @@ Using an alias ensures we can refactor the actual Activity without breaking clients. --> diff --git a/app/src/main/java/co/electriccoin/zcash/app/ZcashApplication.kt b/app/src/main/java/co/electriccoin/zcash/app/ZcashApplication.kt index 1b679bc4..5cbd981c 100644 --- a/app/src/main/java/co/electriccoin/zcash/app/ZcashApplication.kt +++ b/app/src/main/java/co/electriccoin/zcash/app/ZcashApplication.kt @@ -1,7 +1,6 @@ package co.electriccoin.zcash.app import android.app.Application -import co.electriccoin.zcash.BuildConfig import co.electriccoin.zcash.crash.android.CrashReporter import co.electriccoin.zcash.spackle.StrictModeCompat import co.electriccoin.zcash.spackle.Twig diff --git a/build-convention/src/main/kotlin/zcash.emulator-wtf-conventions.gradle.kts b/build-convention/src/main/kotlin/zcash.emulator-wtf-conventions.gradle.kts index 6f324dfc..5f56e68c 100644 --- a/build-convention/src/main/kotlin/zcash.emulator-wtf-conventions.gradle.kts +++ b/build-convention/src/main/kotlin/zcash.emulator-wtf-conventions.gradle.kts @@ -2,7 +2,7 @@ // These are determined by `ew-cli --models` @Suppress("MagicNumber", "PropertyName", "VariableNaming") -val EMULATOR_WTF_MIN_SDK = 24 +val EMULATOR_WTF_MIN_SDK = 23 @Suppress("MagicNumber", "PropertyName", "VariableNaming") val EMULATOR_WTF_MAX_SDK = 31 diff --git a/build.gradle.kts b/build.gradle.kts index ed91815f..c7cc5a82 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -97,12 +97,11 @@ fladle { filesToDownload.set(listOf( "*/matrix_*/*test_results_merged\\.xml", - "*/matrix_*/*/artifacts/sdcard/Pictures/zcash_screenshots/*\\.png" + "*/matrix_*/*/artifacts/sdcard/googletest/test_outputfiles/*\\.png" )) directoriesToPull.set(listOf( - // This path needs to be coordinated with the implementation in the app module's tests - "/sdcard/Pictures/zcash_screenshots" + "/sdcard/googletest/test_outputfiles" )) } diff --git a/gradle.properties b/gradle.properties index 62dbc75b..70ce4b47 100644 --- a/gradle.properties +++ b/gradle.properties @@ -64,7 +64,7 @@ IS_SDK_INCLUDED_BUILD=false # A lower version on the libraries helps to ensure some degree of backwards compatiblity, for the project # as a whole. But a higher version on the app ensures that we aren't directly supporting users # with old devices. -ANDROID_LIB_MIN_SDK_VERSION=23 +ANDROID_LIB_MIN_SDK_VERSION=24 ANDROID_APP_MIN_SDK_VERSION=27 ANDROID_TARGET_SDK_VERSION=31 ANDROID_COMPILE_SDK_VERSION=31 @@ -91,14 +91,14 @@ ANDROIDX_COMPOSE_MATERIAL3_VERSION=1.0.0-alpha09 ANDROIDX_COMPOSE_VERSION=1.1.1 ANDROIDX_CONSTRAINTLAYOUT_VERSION=1.0.0 ANDROIDX_CORE_VERSION=1.7.0 -ANDROIDX_ESPRESSO_VERSION=3.4.0 +ANDROIDX_ESPRESSO_VERSION=3.5.0-alpha07 ANDROIDX_LIFECYCLE_VERSION=2.4.1 ANDROIDX_NAVIGATION_COMPOSE_VERSION=2.4.2 ANDROIDX_SECURITY_CRYPTO_VERSION=1.1.0-alpha03 ANDROIDX_SPLASH_SCREEN_VERSION=1.0.0-rc01 -ANDROIDX_TEST_JUNIT_VERSION=1.1.3 -ANDROIDX_TEST_ORCHESTRATOR_VERSION=1.4.1 -ANDROIDX_TEST_CORE_VERSION=1.4.1-alpha06 +ANDROIDX_TEST_JUNIT_VERSION=1.1.4-alpha07 +ANDROIDX_TEST_ORCHESTRATOR_VERSION=1.4.2-alpha03 +ANDROIDX_TEST_CORE_VERSION=1.4.1-alpha07 ANDROIDX_TEST_RUNNER_VERSION=1.5.0-alpha03 ANDROIDX_UI_AUTOMATOR_VERSION=2.2.0-alpha1 ANDROIDX_WORK_MANAGER_VERSION=2.7.1 diff --git a/settings.gradle.kts b/settings.gradle.kts index 9d16a18b..fa82b447 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -199,8 +199,8 @@ dependencyResolutionManagement { //alias("androidx-espresso-contrib", "androidx.test.espresso:espresso-contrib:$androidxEspressoVersion") library("androidx-espresso-core", "androidx.test.espresso:espresso-core:$androidxEspressoVersion") library("androidx-espresso-intents", "androidx.test.espresso:espresso-intents:$androidxEspressoVersion") - library("androidx-test-core", "androidx.test:core:$androidxTestCoreVersion") - library("androidx-test-junit", "androidx.test.ext:junit:$androidxTestJunitVersion") + library("androidx-test-core", "androidx.test:core-ktx:$androidxTestCoreVersion") + library("androidx-test-junit", "androidx.test.ext:junit-ktx:$androidxTestJunitVersion") library("androidx-test-orchestrator", "androidx.test:orchestrator:$androidxTestOrchestratorVersion") library("androidx-test-runner", "androidx.test:runner:$androidxTestRunnerVersion") library("androidx-uiAutomator", "androidx.test.uiautomator:uiautomator-v18:$androidxUiAutomatorVersion")