diff --git a/gradle.properties b/gradle.properties index 8f80c609..9b94afd7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,7 @@ kotlin.code.style=official android.useAndroidX=true GROUP=com.atiurin POM_ARTIFACT_ID=ultron -VERSION_NAME=2.3.3 +VERSION_NAME=2.3.4 POM_NAME=ultron diff --git a/sample-app/src/androidTest/java/com/atiurin/sampleapp/tests/BaseTest.kt b/sample-app/src/androidTest/java/com/atiurin/sampleapp/tests/BaseTest.kt index cd1a7084..eb0825eb 100644 --- a/sample-app/src/androidTest/java/com/atiurin/sampleapp/tests/BaseTest.kt +++ b/sample-app/src/androidTest/java/com/atiurin/sampleapp/tests/BaseTest.kt @@ -1,5 +1,6 @@ package com.atiurin.sampleapp.tests +import android.os.Environment import androidx.test.platform.app.InstrumentationRegistry import com.atiurin.ultron.testlifecycle.rulesequence.RuleSequence import com.atiurin.sampleapp.data.repositories.CURRENT_USER @@ -37,6 +38,7 @@ abstract class BaseTest { UltronConfig.applyRecommended() UltronAllureConfig.applyRecommended() UltronComposeConfig.applyRecommended() + UltronAllureConfig.setAllureResultsDirectory() UltronComposeConfig.addListener(ScreenshotAttachListener()) UltronComposeConfig.addListener(WindowHierarchyAttachListener()) UltronComposeConfig.addListener(DetailedOperationAllureListener()) diff --git a/ultron-allure/src/main/java/com/atiurin/ultron/allure/attachment/AllureDirectoryUtil.kt b/ultron-allure/src/main/java/com/atiurin/ultron/allure/attachment/AllureDirectoryUtil.kt new file mode 100644 index 00000000..170c7c88 --- /dev/null +++ b/ultron-allure/src/main/java/com/atiurin/ultron/allure/attachment/AllureDirectoryUtil.kt @@ -0,0 +1,18 @@ +package com.atiurin.ultron.allure.attachment + +import androidx.test.platform.app.InstrumentationRegistry +import io.qameta.allure.kotlin.util.PropertiesUtils +import java.io.File + +object AllureDirectoryUtil { + + fun getResultsDirectoryName(): String = PropertiesUtils.resultsDirectoryPath + + /** + * From Allure source code + * see [https://github.com/allure-framework/allure-kotlin/blob/master/allure-kotlin-android/src/main/kotlin/io/qameta/allure/android/AllureAndroidLifecycle.kt] + */ + fun getOriginalResultsDirectory(): File { + return File(InstrumentationRegistry.getInstrumentation().targetContext.filesDir, getResultsDirectoryName()) + } +} \ No newline at end of file diff --git a/ultron-allure/src/main/java/com/atiurin/ultron/allure/config/AllureConfigParams.kt b/ultron-allure/src/main/java/com/atiurin/ultron/allure/config/AllureConfigParams.kt index 0f0fbc10..93ce38fe 100644 --- a/ultron-allure/src/main/java/com/atiurin/ultron/allure/config/AllureConfigParams.kt +++ b/ultron-allure/src/main/java/com/atiurin/ultron/allure/config/AllureConfigParams.kt @@ -1,5 +1,10 @@ package com.atiurin.ultron.allure.config +import androidx.test.platform.app.InstrumentationRegistry +import com.atiurin.ultron.allure.attachment.AllureDirectoryUtil +import com.atiurin.ultron.allure.attachment.AttachUtil +import java.io.File + data class AllureConfigParams( var addScreenshotPolicy: MutableSet = mutableSetOf( AllureAttachStrategy.TEST_FAILURE, diff --git a/ultron-allure/src/main/java/com/atiurin/ultron/allure/config/UltronAllureConfig.kt b/ultron-allure/src/main/java/com/atiurin/ultron/allure/config/UltronAllureConfig.kt index b11baf1c..5a441b89 100644 --- a/ultron-allure/src/main/java/com/atiurin/ultron/allure/config/UltronAllureConfig.kt +++ b/ultron-allure/src/main/java/com/atiurin/ultron/allure/config/UltronAllureConfig.kt @@ -1,6 +1,9 @@ package com.atiurin.ultron.allure.config +import android.os.Environment import androidx.test.platform.app.InstrumentationRegistry +import com.atiurin.ultron.allure.attachment.AllureDirectoryUtil +import com.atiurin.ultron.allure.attachment.AttachUtil import com.atiurin.ultron.allure.condition.AllureConditionExecutorWrapper import com.atiurin.ultron.allure.condition.AllureConditionsExecutor import com.atiurin.ultron.allure.getRunInformer @@ -9,12 +12,16 @@ import com.atiurin.ultron.allure.listeners.ScreenshotAttachListener import com.atiurin.ultron.allure.listeners.WindowHierarchyAttachListener import com.atiurin.ultron.allure.runner.LogcatAttachRunListener import com.atiurin.ultron.allure.runner.ScreenshotAttachRunListener +import com.atiurin.ultron.allure.runner.UltronAllureResultsTransferListener import com.atiurin.ultron.allure.runner.UltronLogAttachRunListener +import com.atiurin.ultron.allure.runner.UltronLogCleanerRunListener import com.atiurin.ultron.allure.runner.WindowHierarchyAttachRunListener import com.atiurin.ultron.core.config.UltronConfig +import com.atiurin.ultron.extensions.simpleClassName import com.atiurin.ultron.listeners.AbstractListener import com.atiurin.ultron.log.UltronLog import com.atiurin.ultron.runner.UltronRunListener +import java.io.File object UltronAllureConfig { var params: AllureConfigParams = AllureConfigParams() @@ -27,6 +34,41 @@ object UltronAllureConfig { UltronConfig.Conditions.conditionExecutorWrapper = AllureConditionExecutorWrapper() } + /** + * Sets the directory where Allure results will be stored. + * Original results artifacts are saved in [AllureDirectoryUtil.getOriginalResultsDirectory] + * + * The results will be copied from [AllureDirectoryUtil.getOriginalResultsDirectory] + * to [targetDirectory] once test run is finished. + * + * @param targetDirectory The directory where the Allure results will be stored. + * + * @see AllureDirectoryUtil.getOriginalResultsDirectory + */ + fun setAllureResultsDirectory(targetDirectory: File) { + removeRunListener(UltronLogCleanerRunListener::class.java) + addRunListener( + UltronAllureResultsTransferListener( + sourceDir = AllureDirectoryUtil.getOriginalResultsDirectory(), + targetDir = targetDirectory + ) + ) + addRunListener(UltronLogCleanerRunListener()) + } + + /** + * Sets the directory where Allure results will be stored with default public directory. + * + * @param publicDirectory The public directory where the Allure results will be stored. + * Default is [Environment.DIRECTORY_DOWNLOADS]. + * + * So, the final path by default will be like '/sdcard/Download/allure-result' + */ + fun setAllureResultsDirectory(publicDirectory: String = Environment.DIRECTORY_DOWNLOADS) { + val targetDir = Environment.getExternalStoragePublicDirectory(publicDirectory).resolve(AllureDirectoryUtil.getResultsDirectoryName()) + setAllureResultsDirectory(targetDir) + } + private fun modify() { if (params.detailedAllureReport) { UltronConfig.addGlobalListener(DetailedOperationAllureListener()) @@ -43,10 +85,10 @@ object UltronAllureConfig { setAllureConditionsExecutorWrapper() setAllureConditionExecutor() } - if (!params.attachUltronLog){ + if (!params.attachUltronLog) { removeRunListener(UltronLogAttachRunListener::class.java) } - if (!params.attachLogcat){ + if (!params.attachLogcat) { removeRunListener(LogcatAttachRunListener::class.java) } UltronLog.info("UltronAllureConfig applied with params $params") @@ -63,10 +105,12 @@ object UltronAllureConfig { } fun addRunListener(listener: UltronRunListener) { + UltronLog.info("Add ${listener::class.simpleName} run listener") InstrumentationRegistry.getInstrumentation().getRunInformer().addListener(listener) } - fun removeRunListener(listenerClass: Class) { + fun removeRunListener(listenerClass: Class) { + UltronLog.info("Remove ${listenerClass.simpleClassName()} run listener") InstrumentationRegistry.getInstrumentation().getRunInformer().removeListener(listenerClass) } } diff --git a/ultron-allure/src/main/java/com/atiurin/ultron/allure/runner/UltronAllureResultsTransferListener.kt b/ultron-allure/src/main/java/com/atiurin/ultron/allure/runner/UltronAllureResultsTransferListener.kt new file mode 100644 index 00000000..fc9839e1 --- /dev/null +++ b/ultron-allure/src/main/java/com/atiurin/ultron/allure/runner/UltronAllureResultsTransferListener.kt @@ -0,0 +1,35 @@ +package com.atiurin.ultron.allure.runner + +import com.atiurin.ultron.extensions.createDirectoryIfNotExists +import com.atiurin.ultron.log.UltronLog +import com.atiurin.ultron.runner.UltronRunListener +import org.junit.runner.Result +import java.io.File +import kotlin.system.measureTimeMillis + +/** + * Transfer files from original Allure results directory [sourceDir] + * to custom directory [targetDir] provided by user [UltronAllureConfig.setAllureResultsDirectory] + */ +class UltronAllureResultsTransferListener(private val sourceDir: File, private val targetDir: File) : UltronRunListener() { + override fun testRunFinished(result: Result) { + UltronLog.info("Copy Allure results from '${sourceDir.absolutePath}' to '${targetDir.absolutePath}'") + targetDir.createDirectoryIfNotExists() + var isSuccessfullyCopied = true + val time = measureTimeMillis { + sourceDir.copyRecursively(targetDir, true, onError = { file, ioException -> + UltronLog.error(""" + |Unable to copy Allure results file '${file.absolutePath}' to '${targetDir.absolutePath}'. + |Got exception : '${ioException.message}'. + |Source directory '${sourceDir.absolutePath}' won't be deleted. + """.trimMargin() + ) + isSuccessfullyCopied = false + OnErrorAction.SKIP + }) + if (isSuccessfullyCopied) sourceDir.deleteRecursively() + } + UltronLog.info("Allure results files transfer time = $time ms") + } +} + diff --git a/ultron/src/main/java/com/atiurin/ultron/extensions/FileExt.kt b/ultron/src/main/java/com/atiurin/ultron/extensions/FileExt.kt index e544e4d9..1fe444cd 100644 --- a/ultron/src/main/java/com/atiurin/ultron/extensions/FileExt.kt +++ b/ultron/src/main/java/com/atiurin/ultron/extensions/FileExt.kt @@ -1,5 +1,6 @@ package com.atiurin.ultron.extensions +import com.atiurin.ultron.log.UltronLog import java.io.File import java.io.PrintWriter @@ -8,4 +9,11 @@ fun File.clearContent() { print("") close() } +} + +fun File.createDirectoryIfNotExists() { + if (!exists()) { + val result = mkdirs() + if (!result) UltronLog.error("Unable to create directory '${this.absolutePath}'") + } } \ No newline at end of file