Created
March 11, 2021 15:00
-
-
Save eakurnikov/abe8c7a8ce1e96078ce1bf189eac5522 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class TestResultsFilesProvider( | |
private val testResultsDirsProvider: TestResultsDirsProvider, | |
private val addTimestamps: Boolean | |
) { | |
private val logcatRootDir = File("logcat") | |
private val screenshotsRootDir = File("screenshots") | |
private val videoRootDir = File("video") | |
private val viewHierarchy = File("view_hierarchy") | |
fun provideLogcatFile(tag: String): File = | |
testResultsDirsProvider | |
.provideCleared(logcatRootDir) | |
.resolve(getFileName(tag, FileExtension.TXT)) | |
fun provideScreenshotFile(tag: String): File = | |
testResultsDirsProvider | |
.provideCleared(screenshotsRootDir) | |
.resolve(getFileName(tag, FileExtension.PNG)) | |
fun provideVideoFile(tag: String): File = | |
testResultsDirsProvider | |
.provideCleared(videoRootDir) | |
.resolve(getFileName(tag, FileExtension.MP4)) | |
fun provideViewHierarchyFile(tag: String): File = | |
testResultsDirsProvider | |
.provideCleared(viewHierarchy) | |
.resolve(getFileName(tag, FileExtension.XML)) | |
private fun getFileName(tag: String, ext: FileExtension): String = | |
"${if (addTimestamps) "${System.currentTimeMillis()}_" else ""}$tag$ext" | |
} | |
class TestResultsDirsProvider( | |
private val groupByRunNumbers: Boolean | |
) { | |
private val dirProvider = DirProvider() | |
private val testRunsCount = mutableMapOf<TestMethod, Int>() | |
fun provideCleared(path: File, subDir: String? = null): File = | |
dirProvider.provideCleared(provide(path, subDir)) | |
fun provide(path: File, subDir: String? = null): File = | |
getDir(dirProvider.provide(path), subDir) | |
fun onNewTestRun() { | |
val testMethod = Thread.currentThread().stackTrace.findTestMethod() | |
testRunsCount[testMethod] = (testRunsCount[testMethod] ?: 0) + 1 | |
} | |
private fun getDir(rootDir: File, subDir: String? = null): File { | |
val testMethod: TestMethod = Thread.currentThread().stackTrace.findTestMethod() | |
val runNumber: Int = testRunsCount[testMethod] ?: 1 | |
val dirName: String = getDirName(testMethod, runNumber) | |
return if (subDir != null) { | |
rootDir.resolve(subDir).resolve(dirName) | |
} else { | |
rootDir.resolve(dirName) | |
} | |
} | |
private fun getDirName(testMethod: TestMethod, runNumber: Int): String { | |
val clearedClassName: String = | |
testMethod.className.replace("[^A-Za-z0-9._-]".toRegex(), "_") | |
val directDirName = "$clearedClassName${File.separator}${testMethod.methodName}" | |
return "${if (groupByRunNumbers) "run_$runNumber${File.separator}" else ""}$directDirName" | |
} | |
} | |
class DirProvider { | |
private val clearedDirs = HashSet<File>() | |
fun provide(path: File): File { | |
val dir: File = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | |
Environment.getExternalStorageDirectory().resolve(path) | |
} else { | |
TestEnvironment.targetContext.applicationContext.getDir( | |
path.canonicalPath, | |
Context.MODE_WORLD_READABLE | |
) | |
} | |
if (!dir.exists()) { | |
dir.createDirectoryRWX() | |
} | |
return dir | |
} | |
fun provideCleared(dir: File): File { | |
if (!clearedDirs.contains(dir)) { | |
clearDir(path = dir, inclusive = false) | |
clearedDirs.add(dir) | |
} | |
if (!dir.exists()) { | |
dir.createDirectoryRWX() | |
} | |
return dir | |
} | |
private fun clearDir(path: File, inclusive: Boolean) { | |
if (path.isDirectory) { | |
path.listFiles()?.run { | |
forEach { child -> clearDir(path = child, inclusive = true) } | |
} | |
} | |
if (inclusive) { | |
path.delete() | |
} | |
} | |
private fun File.createDirectoryRWX() { | |
mkdirs() | |
setReadable(true, false) | |
setWritable(true, false) | |
setExecutable(true, false) | |
} | |
} | |
private const val TEST_CASE_CLASS_JUNIT_4 = "org.junit.runners.model.FrameworkMethod$1" | |
private const val TEST_CASE_METHOD_JUNIT_4 = "runReflectiveCall" | |
fun Array<StackTraceElement>.findTestMethod(): TestMethod = | |
findTestClassTraceElement().let { TestMethod(it.className, it.methodName) } | |
fun Array<StackTraceElement>.findTestClassTraceElement(): StackTraceElement = | |
this.withIndex().reversed() | |
.find { (_, element) -> element.isJunit4() } | |
?.let { (i, _) -> extractStackElement(i) } | |
?: throw IllegalArgumentException( | |
"Could not find test class! Trace: ${this.map { it.toString() }}" | |
) | |
fun StackTraceElement.isJunit4(): Boolean = | |
TEST_CASE_CLASS_JUNIT_4 == className && TEST_CASE_METHOD_JUNIT_4 == methodName | |
@Suppress("MagicNumber") | |
fun Array<StackTraceElement>.extractStackElement(i: Int): StackTraceElement = | |
this[if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) i - 2 else i - 3] | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment