From d23c1b9a9da0d448bc2c0fd54d81f59f6b4310b6 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Wed, 13 Jul 2022 19:13:41 +0300 Subject: [PATCH 1/8] Issue#100 Fix screenshot rule for API 21 On API 21 the images could not have been saved before. Added logic to choose external or internal folder. Tested on: API 21, 24, 30, 31, 32 --- .../testutil/screenshot/ScreenshotRule.kt | 10 +++-- .../screenshot/basicScreenCaptureProcessor.kt | 39 ++++++++++++++----- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/ScreenshotRule.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/ScreenshotRule.kt index 36a7756..20165bd 100644 --- a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/ScreenshotRule.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/ScreenshotRule.kt @@ -1,6 +1,7 @@ package org.fnives.test.showcase.android.testutil.screenshot import android.graphics.Bitmap +import android.util.Log import androidx.test.runner.screenshot.ScreenCapture import androidx.test.runner.screenshot.ScreenCaptureProcessor import androidx.test.runner.screenshot.Screenshot @@ -38,28 +39,29 @@ class ScreenshotRule( } } - fun takeScreenshot(prefix: String = this.prefix, baseName: String) { + fun takeScreenshot(prefix: String = this.prefix, baseName: String, capture: ScreenCapture = Screenshot.capture()) { val fileName = if (timestampSuffix) { "$prefix-$baseName-${System.currentTimeMillis()}" } else { "$prefix-$baseName" } - takeScreenshot(filename = fileName) + takeScreenshot(filename = fileName, capture = capture) } @Suppress("PrintStackTrace") - private fun takeScreenshot(filename: String) { - val capture: ScreenCapture = Screenshot.capture() + private fun takeScreenshot(filename: String, capture: ScreenCapture) { capture.name = filename capture.format = Bitmap.CompressFormat.JPEG try { capture.process(setOf(processor)) } catch (e: IOException) { + Log.d(TAG, "Couldn't save image: $e") e.printStackTrace() } } companion object { + const val TAG = "Screenshot Rule" val Description.testScreenshotName get() = "${testClass.simpleName}-$methodName" val Description.beforeTestScreenshotName get() = "$testScreenshotName-BEFORE" val Description.successTestScreenshotName get() = "$testScreenshotName-SUCCESS" diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/basicScreenCaptureProcessor.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/basicScreenCaptureProcessor.kt index be6375d..eed57f0 100644 --- a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/basicScreenCaptureProcessor.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/basicScreenCaptureProcessor.kt @@ -1,19 +1,40 @@ package org.fnives.test.showcase.android.testutil.screenshot -import android.os.Build import android.os.Environment +import android.util.Log import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.runner.screenshot.ScreenCaptureProcessor import androidx.test.runner.screenshot.basicScreenCaptureProcessor import java.io.File -fun basicScreenCaptureProcessor(subDir: String = "test-screenshots") = - basicScreenCaptureProcessor(File(getTestPicturesDir(), subDir)) +fun basicScreenCaptureProcessor(subDir: String = "test-screenshots"): ScreenCaptureProcessor { + val directory = File(getTestPicturesDir(), subDir) + Log.d(ScreenshotRule.TAG, "directory to save screenshots = $directory") + return basicScreenCaptureProcessor(File(getTestPicturesDir(), subDir)) +} +/** + * BasicScreenCaptureProcessor seems to work differently on API versions, + * based on where we have access to save and pull the images from. + * + * see example issue: https://github.com/android/android-test/issues/818 + */ @Suppress("DEPRECATION") -fun getTestPicturesDir() = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - InstrumentationRegistry.getInstrumentation().targetContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES) - } else { - val packageName = InstrumentationRegistry.getInstrumentation().targetContext.packageName - File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), packageName) +fun getTestPicturesDir(): File? { + val packageName = InstrumentationRegistry.getInstrumentation().targetContext.packageName + val environmentFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + val externalFolder = File(environmentFolder, packageName) + if (externalFolder.canWrite()) { + Log.d(ScreenshotRule.TAG, "external folder") + return externalFolder } + + val internalFolder = InstrumentationRegistry.getInstrumentation().targetContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES) + if (internalFolder?.canWrite() == true) { + Log.d(ScreenshotRule.TAG, "internal folder") + return internalFolder + } + Log.d(ScreenshotRule.TAG, "cant find directory the screenshots could be saved into") + + return null +} From 35bf967eabdcce594216c82672d33978b1682c08 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Wed, 13 Jul 2022 19:50:36 +0300 Subject: [PATCH 2/8] Issue#100 Update loging --- gradlescripts/pull-screenshots.gradle | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/gradlescripts/pull-screenshots.gradle b/gradlescripts/pull-screenshots.gradle index f04dacc..29e7860 100644 --- a/gradlescripts/pull-screenshots.gradle +++ b/gradlescripts/pull-screenshots.gradle @@ -81,8 +81,17 @@ task removeLocalScreenshots(type: Delete) { delete files("$savePath") } +task showLogcat(type: Exec) { + group = 'Test-Screenshots' + description = 'Show Logcat' + + //adb logcat -d + commandLine "$adb", 'logcat', '-d' +} + afterEvaluate { - connectedDebugAndroidTest.finalizedBy pullScreenshots + connectedDebugAndroidTest.finalizedBy showLogcat + showLogcat.finalizedBy pullScreenshots pullScreenshots.finalizedBy removeScreenshotsFromDevice clean.dependsOn(removeLocalScreenshots) } \ No newline at end of file From cc2b745d2e7515b6f49ddd0d4d10d5150575fa35 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Wed, 13 Jul 2022 19:50:48 +0300 Subject: [PATCH 3/8] Issue#100 Add itentional error --- .../org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt b/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt index f2666d7..afba776 100644 --- a/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt +++ b/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt @@ -57,7 +57,7 @@ class AuthComposeInstrumentedTest : KoinTest { composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() } navigationRobot.assertAuthScreen() robot.setPassword("alma") - .setUsername("banan") + .setUsername("banaxn") .assertUsername("banan") .assertPassword("alma") From 69f5f15c3a8667fe5ac68e5d939d29ce1710da1a Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Wed, 13 Jul 2022 20:18:45 +0300 Subject: [PATCH 4/8] Issue#100 Update to API based logic --- gradlescripts/deploy.aar.gradle | 1 - gradlescripts/pull-screenshots.gradle | 2 +- .../screenshot/basicScreenCaptureProcessor.kt | 28 ++++++++----------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/gradlescripts/deploy.aar.gradle b/gradlescripts/deploy.aar.gradle index fcc77df..fb32765 100644 --- a/gradlescripts/deploy.aar.gradle +++ b/gradlescripts/deploy.aar.gradle @@ -23,7 +23,6 @@ afterEvaluate { from components.release groupId "$testUtilGroupId" - println("$testUtilArtifactId") version "$testUtilVersion" artifactId "$testUtilArtifactId" artifact sourcesJar diff --git a/gradlescripts/pull-screenshots.gradle b/gradlescripts/pull-screenshots.gradle index 29e7860..ae0b2f5 100644 --- a/gradlescripts/pull-screenshots.gradle +++ b/gradlescripts/pull-screenshots.gradle @@ -20,7 +20,7 @@ def findAdbFromLocal = { def sdkDir = properties.getProperty('sdk.dir') return "$sdkDir/platform-tools/adb" } else { - System.err.println("WARNING: SDK dir not found by local properties, returning static: $System.env.HOME/Library/Android/sdk/platform-tools/adb") + println("WARNING: SDK dir not found by local properties, returning static: $System.env.HOME/Library/Android/sdk/platform-tools/adb") return "$System.env.HOME/Library/Android/sdk/platform-tools/adb" } } diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/basicScreenCaptureProcessor.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/basicScreenCaptureProcessor.kt index eed57f0..c829768 100644 --- a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/basicScreenCaptureProcessor.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/basicScreenCaptureProcessor.kt @@ -1,5 +1,6 @@ package org.fnives.test.showcase.android.testutil.screenshot +import android.os.Build import android.os.Environment import android.util.Log import androidx.test.platform.app.InstrumentationRegistry @@ -20,21 +21,16 @@ fun basicScreenCaptureProcessor(subDir: String = "test-screenshots"): ScreenCapt * see example issue: https://github.com/android/android-test/issues/818 */ @Suppress("DEPRECATION") -fun getTestPicturesDir(): File? { - val packageName = InstrumentationRegistry.getInstrumentation().targetContext.packageName - val environmentFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) - val externalFolder = File(environmentFolder, packageName) - if (externalFolder.canWrite()) { - Log.d(ScreenshotRule.TAG, "external folder") - return externalFolder - } - - val internalFolder = InstrumentationRegistry.getInstrumentation().targetContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES) - if (internalFolder?.canWrite() == true) { +fun getTestPicturesDir(): File? = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { Log.d(ScreenshotRule.TAG, "internal folder") - return internalFolder - } - Log.d(ScreenshotRule.TAG, "cant find directory the screenshots could be saved into") - return null -} + InstrumentationRegistry.getInstrumentation().targetContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES) + } else { + val packageName = InstrumentationRegistry.getInstrumentation().targetContext.packageName + val environmentFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + val externalFolder = File(environmentFolder, packageName) + Log.d(ScreenshotRule.TAG, "external folder") + + externalFolder + } From bae8c0fc964c51ad4ba10dcfe2326a5a567b5bc4 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Wed, 13 Jul 2022 21:33:45 +0300 Subject: [PATCH 5/8] Issue#104 Create Test verifying screenshot pulling works --- .github/workflows/pull-request-jobs.yml | 114 +++++++++--------- .github/workflows/screenshot-tests.yml | 55 +++++++++ .../test/showcase/rule/ScreenshotTest.kt | 36 ++++++ app/verifyfiles.sh | 13 ++ gradlescripts/pull-screenshots.gradle | 54 ++++++--- pullscreenshottest.sh | 3 + .../testutil/screenshot/ScreenshotRule.kt | 1 + .../screenshot/basicScreenCaptureProcessor.kt | 16 ++- 8 files changed, 212 insertions(+), 80 deletions(-) create mode 100644 .github/workflows/screenshot-tests.yml create mode 100644 app/src/androidTest/java/org/fnives/test/showcase/rule/ScreenshotTest.kt create mode 100755 app/verifyfiles.sh create mode 100755 pullscreenshottest.sh diff --git a/.github/workflows/pull-request-jobs.yml b/.github/workflows/pull-request-jobs.yml index c226468..0993b4a 100644 --- a/.github/workflows/pull-request-jobs.yml +++ b/.github/workflows/pull-request-jobs.yml @@ -74,60 +74,60 @@ jobs: path: ./**/build/reports/tests/**/index.html retention-days: 1 - run-tests-on-emulator: - runs-on: macos-latest - strategy: - matrix: - api-level: [ 21, 30 ] - fail-fast: false - steps: - - name: checkout - uses: actions/checkout@v3 - - name: Setup Java - uses: actions/setup-java@v2 - with: - distribution: 'adopt' - java-version: '11' - - name: Gradle cache - uses: gradle/gradle-build-action@v2 - - name: AVD cache - uses: actions/cache@v3 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-${{ matrix.api-level }} - - name: create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@v2 - with: - arch: 'x86_64' - api-level: ${{ matrix.api-level }} - force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: false - script: echo "Generated AVD snapshot for caching." - - name: Run Android Tests - uses: reactivecircus/android-emulator-runner@v2 - with: - arch: 'x86_64' - api-level: ${{ matrix.api-level }} - force-avd-creation: false - emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: true - script: ./gradlew connectedDebugAndroidTest - - name: Upload Test Results - uses: actions/upload-artifact@v2 - if: always() - with: - name: Emulator-Test-Results-${{ matrix.api-level }} - path: ./**/build/reports/androidTests/**/*.html - retention-days: 1 - - name: Upload Test Screenshots - uses: actions/upload-artifact@v2 - if: always() - with: - name: Emulator-Test-Results-${{ matrix.api-level }} - path: ./**/build/testscreenshots/* - retention-days: 1 \ No newline at end of file +# run-tests-on-emulator: +# runs-on: macos-latest +# strategy: +# matrix: +# api-level: [ 21, 30 ] +# fail-fast: false +# steps: +# - name: checkout +# uses: actions/checkout@v3 +# - name: Setup Java +# uses: actions/setup-java@v2 +# with: +# distribution: 'adopt' +# java-version: '11' +# - name: Gradle cache +# uses: gradle/gradle-build-action@v2 +# - name: AVD cache +# uses: actions/cache@v3 +# id: avd-cache +# with: +# path: | +# ~/.android/avd/* +# ~/.android/adb* +# key: avd-${{ matrix.api-level }} +# - name: create AVD and generate snapshot for caching +# if: steps.avd-cache.outputs.cache-hit != 'true' +# uses: reactivecircus/android-emulator-runner@v2 +# with: +# arch: 'x86_64' +# api-level: ${{ matrix.api-level }} +# force-avd-creation: false +# emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none +# disable-animations: false +# script: echo "Generated AVD snapshot for caching." +# - name: Run Android Tests +# uses: reactivecircus/android-emulator-runner@v2 +# with: +# arch: 'x86_64' +# api-level: ${{ matrix.api-level }} +# force-avd-creation: false +# emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none +# disable-animations: true +# script: ./gradlew connectedDebugAndroidTest +# - name: Upload Test Results +# uses: actions/upload-artifact@v2 +# if: always() +# with: +# name: Emulator-Test-Results-${{ matrix.api-level }} +# path: ./**/build/reports/androidTests/**/*.html +# retention-days: 1 +# - name: Upload Test Screenshots +# uses: actions/upload-artifact@v2 +# if: always() +# with: +# name: Emulator-Test-Results-${{ matrix.api-level }} +# path: ./**/build/testscreenshots/* +# retention-days: 1 \ No newline at end of file diff --git a/.github/workflows/screenshot-tests.yml b/.github/workflows/screenshot-tests.yml new file mode 100644 index 0000000..6acc288 --- /dev/null +++ b/.github/workflows/screenshot-tests.yml @@ -0,0 +1,55 @@ +name: Verify Screenshots can be created and pulled + +on: + pull_request: + branches: + - develop + +env: + GITHUB_USERNAME: "fknives" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + run-screenshot-test-on-emulator: + runs-on: macos-latest + strategy: + matrix: + api-level: [ 21, 23, 24, 26, 28, 29, 30, 31 ] + fail-fast: false + steps: + - name: checkout + uses: actions/checkout@v3 + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '11' + - name: Gradle cache + uses: gradle/gradle-build-action@v2 + - name: AVD cache + uses: actions/cache@v3 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-${{ matrix.api-level }} + - name: create AVD and generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + arch: 'x86_64' + api-level: ${{ matrix.api-level }} + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: false + script: echo "Generated AVD snapshot for caching." + - name: Run Android Tests + uses: reactivecircus/android-emulator-runner@v2 + with: + arch: 'x86_64' + api-level: ${{ matrix.api-level }} + force-avd-creation: false + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: true + script: ./pullscreenshottest.sh \ No newline at end of file diff --git a/app/src/androidTest/java/org/fnives/test/showcase/rule/ScreenshotTest.kt b/app/src/androidTest/java/org/fnives/test/showcase/rule/ScreenshotTest.kt new file mode 100644 index 0000000..b8975d9 --- /dev/null +++ b/app/src/androidTest/java/org/fnives/test/showcase/rule/ScreenshotTest.kt @@ -0,0 +1,36 @@ +package org.fnives.test.showcase.rule + +import androidx.lifecycle.Lifecycle +import androidx.test.core.app.ActivityScenario +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.fnives.test.showcase.android.testutil.activity.SafeCloseActivityRule +import org.fnives.test.showcase.android.testutil.screenshot.ScreenshotRule +import org.fnives.test.showcase.android.testutil.synchronization.MainDispatcherTestRule +import org.fnives.test.showcase.ui.splash.SplashActivity +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.runner.RunWith +import org.koin.test.KoinTest + +@Suppress("TestFunctionName") +@RunWith(AndroidJUnit4::class) +class ScreenshotTest : KoinTest { + + private lateinit var activityScenario: ActivityScenario + + private val mainDispatcherTestRule = MainDispatcherTestRule() + + @Rule + @JvmField + val ruleOrder: RuleChain = RuleChain.outerRule(mainDispatcherTestRule) + .around(SafeCloseActivityRule { activityScenario }) + .around(ScreenshotRule(prefix = "screenshot-rule", takeOnSuccess = true)) + + /** GIVEN loggedInState WHEN opened after some time THEN MainActivity is started */ + @Test + fun screenshot() { + activityScenario = ActivityScenario.launch(SplashActivity::class.java) + activityScenario.moveToState(Lifecycle.State.RESUMED) + } +} diff --git a/app/verifyfiles.sh b/app/verifyfiles.sh new file mode 100755 index 0000000..f8685fd --- /dev/null +++ b/app/verifyfiles.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env sh + +# if given folder is empty throws error +if [ -d "$1" ]; then + if [ $(ls -A $1 | wc -l) ]; + then + echo "" + else + exit 1 + fi +else + exit 1 +fi diff --git a/gradlescripts/pull-screenshots.gradle b/gradlescripts/pull-screenshots.gradle index ae0b2f5..157cde0 100644 --- a/gradlescripts/pull-screenshots.gradle +++ b/gradlescripts/pull-screenshots.gradle @@ -29,47 +29,64 @@ def packageName = propertyOrNull("screenshotsPackageName") ?: "$android.defaultC def screenshotDirectory = propertyOrNull("screenshotsDirectory") ?: "test-screenshots" def savePath = propertyOrNull("screenshotsSavePath") ?: "build/testscreenshots/" def adb = propertyOrNull("adbPath") ?: findAdbFromLocal() -def internalFullPath = "/sdcard/Android/data/$packageName/files/Pictures/$screenshotDirectory/" -def deprecatedFullPath = "/sdcard/Pictures/$packageName/$screenshotDirectory/" +def contextInternalFullPath = "/data/data/$packageName/files/$screenshotDirectory/" +def contextExternalFullPath = "/sdcard/Android/data/$packageName/files/Pictures/$screenshotDirectory/" +def environmentExternalFullPath = "/sdcard/Pictures/$packageName/$screenshotDirectory/" -task pullScreenshotsInternal(type: Exec) { +task pullScreenshotsContextInternal(type: Exec) { group = 'Test-Screenshots' - description = 'Pull screenshots From internal Storage' + description = 'Pull screenshots From context.external Storage' ignoreExitValue(true) - commandLine "$adb", 'pull', "$internalFullPath", "$savePath/" + commandLine "$adb", 'pull', "$contextInternalFullPath", "$savePath/" } -task pullScreenshotsDeprecated(type: Exec) { +task pullScreenshotsContextExternal(type: Exec) { group = 'Test-Screenshots' - description = 'Pull screenshots From deprecated External Storage' + description = 'Pull screenshots From context.external Storage' ignoreExitValue(true) - commandLine "$adb", 'pull', "$deprecatedFullPath", "$savePath/" + commandLine "$adb", 'pull', "$contextExternalFullPath", "$savePath/" } -task pullScreenshots(dependsOn: [pullScreenshotsInternal, pullScreenshotsDeprecated]) { +task pullScreenshotsEnvironmentExternal(type: Exec) { + group = 'Test-Screenshots' + description = 'Pull screenshots From environment.external Storage' + + ignoreExitValue(true) + commandLine "$adb", 'pull', "$environmentExternalFullPath", "$savePath/" +} + +task pullScreenshots(dependsOn: [pullScreenshotsContextInternal, pullScreenshotsContextExternal, pullScreenshotsEnvironmentExternal]) { group = 'Test-Screenshots' description = 'Pull screenshots From Device' } -task removeScreenshotsFromDeviceInternal(type: Exec) { +task removeScreenshotsFromDeviceContextInternal(type: Exec) { group = 'Test-Screenshots' - description = 'Remove screenshots From internal Storage' + description = 'Remove screenshots From context.internal Storage' ignoreExitValue(true) - commandLine "$adb", 'shell', 'rm', '-r', "$internalFullPath" + commandLine "$adb", 'shell', 'rm', '-r', "$contextInternalFullPath" } -task removeScreenshotsFromDeviceDeprecated(type: Exec) { +task removeScreenshotsFromDeviceContextExternal(type: Exec) { group = 'Test-Screenshots' - description = 'Remove screenshots From deprecated External Storage' + description = 'Remove screenshots From context.external Storage' ignoreExitValue(true) - commandLine "$adb", 'shell', 'rm', '-r', "$deprecatedFullPath" + commandLine "$adb", 'shell', 'rm', '-r', "$contextExternalFullPath" } -task removeScreenshotsFromDevice(dependsOn: [removeScreenshotsFromDeviceInternal, removeScreenshotsFromDeviceDeprecated]) { +task removeScreenshotsFromDeviceEnvironmentExternal(type: Exec) { + group = 'Test-Screenshots' + description = 'Remove screenshots From environment.external Storage' + + ignoreExitValue(true) + commandLine "$adb", 'shell', 'rm', '-r', "$environmentExternalFullPath" +} + +task removeScreenshotsFromDevice(dependsOn: [removeScreenshotsFromDeviceContextInternal, removeScreenshotsFromDeviceContextExternal, removeScreenshotsFromDeviceEnvironmentExternal]) { group = 'Test-Screenshots' description = 'Remove screenshots From Device' } @@ -89,9 +106,12 @@ task showLogcat(type: Exec) { commandLine "$adb", 'logcat', '-d' } +task hasScreenshots(type: Exec) { + commandLine "sh", "./verifyfiles.sh", "$savePath" +} + afterEvaluate { connectedDebugAndroidTest.finalizedBy showLogcat showLogcat.finalizedBy pullScreenshots - pullScreenshots.finalizedBy removeScreenshotsFromDevice clean.dependsOn(removeLocalScreenshots) } \ No newline at end of file diff --git a/pullscreenshottest.sh b/pullscreenshottest.sh new file mode 100755 index 0000000..3208173 --- /dev/null +++ b/pullscreenshottest.sh @@ -0,0 +1,3 @@ +./gradlew clean +./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=org.fnives.test.showcase.rule.ScreenshotTest +./gradlew app:hasScreenshots \ No newline at end of file diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/ScreenshotRule.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/ScreenshotRule.kt index 20165bd..b27ab27 100644 --- a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/ScreenshotRule.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/ScreenshotRule.kt @@ -54,6 +54,7 @@ class ScreenshotRule( capture.format = Bitmap.CompressFormat.JPEG try { capture.process(setOf(processor)) + Log.d(TAG, "Saved image: $filename") } catch (e: IOException) { Log.d(TAG, "Couldn't save image: $e") e.printStackTrace() diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/basicScreenCaptureProcessor.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/basicScreenCaptureProcessor.kt index c829768..9d2685f 100644 --- a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/basicScreenCaptureProcessor.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/screenshot/basicScreenCaptureProcessor.kt @@ -10,7 +10,7 @@ import java.io.File fun basicScreenCaptureProcessor(subDir: String = "test-screenshots"): ScreenCaptureProcessor { val directory = File(getTestPicturesDir(), subDir) - Log.d(ScreenshotRule.TAG, "directory to save screenshots = $directory") + Log.d(ScreenshotRule.TAG, "directory to save screenshots = ${directory.absolutePath}") return basicScreenCaptureProcessor(File(getTestPicturesDir(), subDir)) } @@ -22,15 +22,19 @@ fun basicScreenCaptureProcessor(subDir: String = "test-screenshots"): ScreenCapt */ @Suppress("DEPRECATION") fun getTestPicturesDir(): File? = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { - Log.d(ScreenshotRule.TAG, "internal folder") + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + Log.d(ScreenshotRule.TAG, "context.internal folder") - InstrumentationRegistry.getInstrumentation().targetContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES) - } else { + InstrumentationRegistry.getInstrumentation().targetContext.filesDir + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { val packageName = InstrumentationRegistry.getInstrumentation().targetContext.packageName val environmentFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) val externalFolder = File(environmentFolder, packageName) - Log.d(ScreenshotRule.TAG, "external folder") + Log.d(ScreenshotRule.TAG, "environment.external folder") externalFolder + } else { + Log.d(ScreenshotRule.TAG, "context.external folder") + + InstrumentationRegistry.getInstrumentation().targetContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES) } From b7e3cc493aba3d69b444c88c85a750578efc6999 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Thu, 14 Jul 2022 00:42:29 +0300 Subject: [PATCH 6/8] Issue#104 Change to Manual trigger and revert back temporary changes --- .github/workflows/pull-request-jobs.yml | 114 +++++++++--------- .github/workflows/screenshot-tests.yml | 5 +- .../ui/AuthComposeInstrumentedTest.kt | 2 +- 3 files changed, 60 insertions(+), 61 deletions(-) diff --git a/.github/workflows/pull-request-jobs.yml b/.github/workflows/pull-request-jobs.yml index 0993b4a..c226468 100644 --- a/.github/workflows/pull-request-jobs.yml +++ b/.github/workflows/pull-request-jobs.yml @@ -74,60 +74,60 @@ jobs: path: ./**/build/reports/tests/**/index.html retention-days: 1 -# run-tests-on-emulator: -# runs-on: macos-latest -# strategy: -# matrix: -# api-level: [ 21, 30 ] -# fail-fast: false -# steps: -# - name: checkout -# uses: actions/checkout@v3 -# - name: Setup Java -# uses: actions/setup-java@v2 -# with: -# distribution: 'adopt' -# java-version: '11' -# - name: Gradle cache -# uses: gradle/gradle-build-action@v2 -# - name: AVD cache -# uses: actions/cache@v3 -# id: avd-cache -# with: -# path: | -# ~/.android/avd/* -# ~/.android/adb* -# key: avd-${{ matrix.api-level }} -# - name: create AVD and generate snapshot for caching -# if: steps.avd-cache.outputs.cache-hit != 'true' -# uses: reactivecircus/android-emulator-runner@v2 -# with: -# arch: 'x86_64' -# api-level: ${{ matrix.api-level }} -# force-avd-creation: false -# emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -# disable-animations: false -# script: echo "Generated AVD snapshot for caching." -# - name: Run Android Tests -# uses: reactivecircus/android-emulator-runner@v2 -# with: -# arch: 'x86_64' -# api-level: ${{ matrix.api-level }} -# force-avd-creation: false -# emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -# disable-animations: true -# script: ./gradlew connectedDebugAndroidTest -# - name: Upload Test Results -# uses: actions/upload-artifact@v2 -# if: always() -# with: -# name: Emulator-Test-Results-${{ matrix.api-level }} -# path: ./**/build/reports/androidTests/**/*.html -# retention-days: 1 -# - name: Upload Test Screenshots -# uses: actions/upload-artifact@v2 -# if: always() -# with: -# name: Emulator-Test-Results-${{ matrix.api-level }} -# path: ./**/build/testscreenshots/* -# retention-days: 1 \ No newline at end of file + run-tests-on-emulator: + runs-on: macos-latest + strategy: + matrix: + api-level: [ 21, 30 ] + fail-fast: false + steps: + - name: checkout + uses: actions/checkout@v3 + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '11' + - name: Gradle cache + uses: gradle/gradle-build-action@v2 + - name: AVD cache + uses: actions/cache@v3 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-${{ matrix.api-level }} + - name: create AVD and generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + arch: 'x86_64' + api-level: ${{ matrix.api-level }} + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: false + script: echo "Generated AVD snapshot for caching." + - name: Run Android Tests + uses: reactivecircus/android-emulator-runner@v2 + with: + arch: 'x86_64' + api-level: ${{ matrix.api-level }} + force-avd-creation: false + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: true + script: ./gradlew connectedDebugAndroidTest + - name: Upload Test Results + uses: actions/upload-artifact@v2 + if: always() + with: + name: Emulator-Test-Results-${{ matrix.api-level }} + path: ./**/build/reports/androidTests/**/*.html + retention-days: 1 + - name: Upload Test Screenshots + uses: actions/upload-artifact@v2 + if: always() + with: + name: Emulator-Test-Results-${{ matrix.api-level }} + path: ./**/build/testscreenshots/* + retention-days: 1 \ No newline at end of file diff --git a/.github/workflows/screenshot-tests.yml b/.github/workflows/screenshot-tests.yml index 6acc288..776ca31 100644 --- a/.github/workflows/screenshot-tests.yml +++ b/.github/workflows/screenshot-tests.yml @@ -1,9 +1,8 @@ name: Verify Screenshots can be created and pulled on: - pull_request: - branches: - - develop + workflow_dispatch: + env: GITHUB_USERNAME: "fknives" diff --git a/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt b/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt index afba776..f2666d7 100644 --- a/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt +++ b/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt @@ -57,7 +57,7 @@ class AuthComposeInstrumentedTest : KoinTest { composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() } navigationRobot.assertAuthScreen() robot.setPassword("alma") - .setUsername("banaxn") + .setUsername("banan") .assertUsername("banan") .assertPassword("alma") From 3d787b37aa1af83c0390d32d874814b73dbd72cf Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Thu, 14 Jul 2022 01:17:09 +0300 Subject: [PATCH 7/8] Issue#104 Setup ReloadKointIfNecessary for ScreenshotTest --- .../java/org/fnives/test/showcase/rule/ScreenshotTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/androidTest/java/org/fnives/test/showcase/rule/ScreenshotTest.kt b/app/src/androidTest/java/org/fnives/test/showcase/rule/ScreenshotTest.kt index b8975d9..b124995 100644 --- a/app/src/androidTest/java/org/fnives/test/showcase/rule/ScreenshotTest.kt +++ b/app/src/androidTest/java/org/fnives/test/showcase/rule/ScreenshotTest.kt @@ -6,6 +6,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import org.fnives.test.showcase.android.testutil.activity.SafeCloseActivityRule import org.fnives.test.showcase.android.testutil.screenshot.ScreenshotRule import org.fnives.test.showcase.android.testutil.synchronization.MainDispatcherTestRule +import org.fnives.test.showcase.testutils.ReloadKoinModulesIfNecessaryTestRule import org.fnives.test.showcase.ui.splash.SplashActivity import org.junit.Rule import org.junit.Test @@ -24,6 +25,7 @@ class ScreenshotTest : KoinTest { @Rule @JvmField val ruleOrder: RuleChain = RuleChain.outerRule(mainDispatcherTestRule) + .around(ReloadKoinModulesIfNecessaryTestRule()) .around(SafeCloseActivityRule { activityScenario }) .around(ScreenshotRule(prefix = "screenshot-rule", takeOnSuccess = true)) From 0ca1d7d9c891a60d9e8e90047de9754bb4e1b64b Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Thu, 14 Jul 2022 10:24:33 +0300 Subject: [PATCH 8/8] Issue#100 Save Logcat logs into file and upload Artifact --- .github/workflows/pull-request-jobs.yml | 7 +++++++ gradlescripts/pull-screenshots.gradle | 12 +++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pull-request-jobs.yml b/.github/workflows/pull-request-jobs.yml index c226468..f6915ca 100644 --- a/.github/workflows/pull-request-jobs.yml +++ b/.github/workflows/pull-request-jobs.yml @@ -130,4 +130,11 @@ jobs: with: name: Emulator-Test-Results-${{ matrix.api-level }} path: ./**/build/testscreenshots/* + retention-days: 1 + - name: Upload Logcat Logs + uses: actions/upload-artifact@v2 + if: always() + with: + name: Emulator-Logcat-Logs-${{ matrix.api-level }} + path: ./**/build/logcat.txt retention-days: 1 \ No newline at end of file diff --git a/gradlescripts/pull-screenshots.gradle b/gradlescripts/pull-screenshots.gradle index 157cde0..ae7f4fe 100644 --- a/gradlescripts/pull-screenshots.gradle +++ b/gradlescripts/pull-screenshots.gradle @@ -98,12 +98,14 @@ task removeLocalScreenshots(type: Delete) { delete files("$savePath") } -task showLogcat(type: Exec) { +task saveLogcatLogs(type: Exec) { group = 'Test-Screenshots' description = 'Show Logcat' - //adb logcat -d - commandLine "$adb", 'logcat', '-d' + doFirst { + standardOutput = new FileOutputStream("${buildDir}/logcat.txt") + } + commandLine "$adb", 'logcat', '-d' } task hasScreenshots(type: Exec) { @@ -111,7 +113,7 @@ task hasScreenshots(type: Exec) { } afterEvaluate { - connectedDebugAndroidTest.finalizedBy showLogcat - showLogcat.finalizedBy pullScreenshots + connectedDebugAndroidTest.finalizedBy saveLogcatLogs + saveLogcatLogs.finalizedBy pullScreenshots clean.dependsOn(removeLocalScreenshots) } \ No newline at end of file