From 6b6e1b075ec1b2a00871ef421edf8e85e97ae6ee Mon Sep 17 00:00:00 2001 From: "Artem Zinnatullin :slowpoke" Date: Mon, 29 May 2017 18:08:10 +0300 Subject: [PATCH] Generate separate json files per test. (#31) --- composer/build.gradle | 1 - .../com/gojuno/composer/html/HtmlFullSuite.kt | 4 +- .../html/{HtmlTest.kt => HtmlFullTest.kt} | 13 ++- .../com/gojuno/composer/html/HtmlReport.kt | 15 ++- .../com/gojuno/composer/html/HtmlShortTest.kt | 41 +++++++ .../gojuno/composer/html/HtmlFullSuiteSpec.kt | 2 +- .../{HtmlTestSpec.kt => HtmlFullTestSpec.kt} | 10 +- .../gojuno/composer/html/HtmlReportSpec.kt | 110 ++++++++++++++++++ .../gojuno/composer/html/HtmlShortTestSpec.kt | 41 +++++++ .../test/kotlin/com/gojuno/composer/test.kt | 2 + dependencies.gradle | 5 +- 11 files changed, 224 insertions(+), 20 deletions(-) rename composer/src/main/kotlin/com/gojuno/composer/html/{HtmlTest.kt => HtmlFullTest.kt} (82%) create mode 100644 composer/src/main/kotlin/com/gojuno/composer/html/HtmlShortTest.kt rename composer/src/test/kotlin/com/gojuno/composer/html/{HtmlTestSpec.kt => HtmlFullTestSpec.kt} (85%) create mode 100644 composer/src/test/kotlin/com/gojuno/composer/html/HtmlReportSpec.kt create mode 100644 composer/src/test/kotlin/com/gojuno/composer/html/HtmlShortTestSpec.kt diff --git a/composer/build.gradle b/composer/build.gradle index fbe153f..f2a4eb5 100644 --- a/composer/build.gradle +++ b/composer/build.gradle @@ -20,7 +20,6 @@ dependencies { dependencies { testCompile libraries.spek - testCompile libraries.spekSubjectExtension testCompile libraries.spekJunitPlatformEngine testCompile libraries.assertJ } diff --git a/composer/src/main/kotlin/com/gojuno/composer/html/HtmlFullSuite.kt b/composer/src/main/kotlin/com/gojuno/composer/html/HtmlFullSuite.kt index b8ef50f..8a259da 100644 --- a/composer/src/main/kotlin/com/gojuno/composer/html/HtmlFullSuite.kt +++ b/composer/src/main/kotlin/com/gojuno/composer/html/HtmlFullSuite.kt @@ -11,7 +11,7 @@ data class HtmlFullSuite( val id: String, @SerializedName("tests") - val tests: List, + val tests: List, @SerializedName("passed_count") val passedCount: Int, @@ -31,7 +31,7 @@ data class HtmlFullSuite( fun Suite.toHtmlFullSuite(id: String) = HtmlFullSuite( id = id, - tests = tests.map { it.toHtmlTest() }, + tests = tests.map { it.toHtmlFullTest().toHtmlShortTest() }, passedCount = passedCount, ignoredCount = ignoredCount, failedCount = failedCount, diff --git a/composer/src/main/kotlin/com/gojuno/composer/html/HtmlTest.kt b/composer/src/main/kotlin/com/gojuno/composer/html/HtmlFullTest.kt similarity index 82% rename from composer/src/main/kotlin/com/gojuno/composer/html/HtmlTest.kt rename to composer/src/main/kotlin/com/gojuno/composer/html/HtmlFullTest.kt index 361e224..1cef66a 100644 --- a/composer/src/main/kotlin/com/gojuno/composer/html/HtmlTest.kt +++ b/composer/src/main/kotlin/com/gojuno/composer/html/HtmlFullTest.kt @@ -4,7 +4,7 @@ import com.gojuno.composer.AdbDeviceTest import com.google.gson.annotations.SerializedName import java.util.concurrent.TimeUnit.NANOSECONDS -data class HtmlTest( +data class HtmlFullTest( @SerializedName("package_name") val packageName: String, @@ -15,6 +15,9 @@ data class HtmlTest( @SerializedName("name") val name: String, + @SerializedName("id") + val id: String = "$packageName$className$name", + @SerializedName("duration_millis") val durationMillis: Long, @@ -52,15 +55,15 @@ data class HtmlTest( } } -fun AdbDeviceTest.toHtmlTest() = HtmlTest( +fun AdbDeviceTest.toHtmlFullTest() = HtmlFullTest( packageName = className.substringBeforeLast("."), className = className.substringAfterLast("."), name = testName, durationMillis = NANOSECONDS.toMillis(durationNanos), status = when (status) { - AdbDeviceTest.Status.Passed -> HtmlTest.Status.Passed - AdbDeviceTest.Status.Ignored -> HtmlTest.Status.Ignored - is AdbDeviceTest.Status.Failed -> HtmlTest.Status.Failed + AdbDeviceTest.Status.Passed -> HtmlFullTest.Status.Passed + AdbDeviceTest.Status.Ignored -> HtmlFullTest.Status.Ignored + is AdbDeviceTest.Status.Failed -> HtmlFullTest.Status.Failed }, stacktrace = when (status) { is AdbDeviceTest.Status.Failed -> status.stacktrace diff --git a/composer/src/main/kotlin/com/gojuno/composer/html/HtmlReport.kt b/composer/src/main/kotlin/com/gojuno/composer/html/HtmlReport.kt index 8f3613b..8a3a107 100644 --- a/composer/src/main/kotlin/com/gojuno/composer/html/HtmlReport.kt +++ b/composer/src/main/kotlin/com/gojuno/composer/html/HtmlReport.kt @@ -5,6 +5,12 @@ import com.google.gson.Gson import rx.Completable import java.io.File +/** + * Following file tree structure will be created: + * - index.json + * - suites/suiteId.json + * - suites/deviceId/testId.json + */ fun writeHtmlReport(gson: Gson, suites: List, outputDir: File): Completable = Completable.fromCallable { outputDir.mkdirs() @@ -18,8 +24,11 @@ fun writeHtmlReport(gson: Gson, suites: List, outputDir: File): Completab val suitesDir = File(outputDir, "suites").apply { mkdirs() } - suites.mapIndexed { index, suite -> - val suiteJson = gson.toJson(suite.toHtmlFullSuite(id = "$index")) - File(suitesDir, "$index.json").writeText(suiteJson) + suites.mapIndexed { suiteId, suite -> + File(suitesDir, "$suiteId.json").writeText(gson.toJson(suite.toHtmlFullSuite(id = "$suiteId"))) + + suite.tests.map { it.toHtmlFullTest() }.forEach { htmlFullTest -> + File(File(File(suitesDir, "$suiteId"), htmlFullTest.deviceId).apply { mkdirs() }, "${htmlFullTest.id}.json").writeText(gson.toJson(htmlFullTest)) + } } } diff --git a/composer/src/main/kotlin/com/gojuno/composer/html/HtmlShortTest.kt b/composer/src/main/kotlin/com/gojuno/composer/html/HtmlShortTest.kt new file mode 100644 index 0000000..041dcdc --- /dev/null +++ b/composer/src/main/kotlin/com/gojuno/composer/html/HtmlShortTest.kt @@ -0,0 +1,41 @@ +package com.gojuno.composer.html + +import com.google.gson.annotations.SerializedName + +data class HtmlShortTest( + + @SerializedName("id") + val id: String, + + @SerializedName("package_name") + val packageName: String, + + @SerializedName("class_name") + val className: String, + + @SerializedName("name") + val name: String, + + @SerializedName("duration_millis") + val durationMillis: Long, + + @SerializedName("status") + val status: HtmlFullTest.Status, + + @SerializedName("deviceId") + val deviceId: String, + + @SerializedName("properties") + val properties: Map +) + +fun HtmlFullTest.toHtmlShortTest() = HtmlShortTest( + id = id, + packageName = packageName, + className = className, + name = name, + durationMillis = durationMillis, + status = status, + deviceId = deviceId, + properties = properties +) diff --git a/composer/src/test/kotlin/com/gojuno/composer/html/HtmlFullSuiteSpec.kt b/composer/src/test/kotlin/com/gojuno/composer/html/HtmlFullSuiteSpec.kt index d63816f..e9c8b1b 100644 --- a/composer/src/test/kotlin/com/gojuno/composer/html/HtmlFullSuiteSpec.kt +++ b/composer/src/test/kotlin/com/gojuno/composer/html/HtmlFullSuiteSpec.kt @@ -59,7 +59,7 @@ class HtmlFullSuiteSpec : Spek({ failedCount = suite.failedCount, durationMillis = NANOSECONDS.toMillis(suite.durationNanos), devices = suite.devices.map { it.toHtmlDevice() }, - tests = suite.tests.map { it.toHtmlTest() } + tests = suite.tests.map { it.toHtmlFullTest().toHtmlShortTest() } )) } } diff --git a/composer/src/test/kotlin/com/gojuno/composer/html/HtmlTestSpec.kt b/composer/src/test/kotlin/com/gojuno/composer/html/HtmlFullTestSpec.kt similarity index 85% rename from composer/src/test/kotlin/com/gojuno/composer/html/HtmlTestSpec.kt rename to composer/src/test/kotlin/com/gojuno/composer/html/HtmlFullTestSpec.kt index 2ee7de8..3a91936 100644 --- a/composer/src/test/kotlin/com/gojuno/composer/html/HtmlTestSpec.kt +++ b/composer/src/test/kotlin/com/gojuno/composer/html/HtmlFullTestSpec.kt @@ -9,7 +9,7 @@ import org.jetbrains.spek.api.dsl.context import org.jetbrains.spek.api.dsl.it import java.util.concurrent.TimeUnit.NANOSECONDS -class HtmlTestSpec : Spek({ +class HtmlFullTestSpec : Spek({ context("AdbDeviceTest.toHtmlTest") { @@ -24,14 +24,14 @@ class HtmlTestSpec : Spek({ screenshots = listOf(testFile(), testFile()) ) - val htmlTest = adbDeviceTest.toHtmlTest() + val htmlTest = adbDeviceTest.toHtmlFullTest() - it("converts AdbDeviceTest to HtmlTest") { - assertThat(htmlTest).isEqualTo(HtmlTest( + it("converts AdbDeviceTest to HtmlFullTest") { + assertThat(htmlTest).isEqualTo(HtmlFullTest( packageName = "com.gojuno.example", className = "TestClass", name = adbDeviceTest.testName, - status = HtmlTest.Status.Passed, + status = HtmlFullTest.Status.Passed, durationMillis = NANOSECONDS.toMillis(adbDeviceTest.durationNanos), stacktrace = null, logcatPath = adbDeviceTest.logcat.path, diff --git a/composer/src/test/kotlin/com/gojuno/composer/html/HtmlReportSpec.kt b/composer/src/test/kotlin/com/gojuno/composer/html/HtmlReportSpec.kt new file mode 100644 index 0000000..9349651 --- /dev/null +++ b/composer/src/test/kotlin/com/gojuno/composer/html/HtmlReportSpec.kt @@ -0,0 +1,110 @@ +package com.gojuno.composer.html + +import com.gojuno.commander.android.AdbDevice +import com.gojuno.composer.AdbDeviceTest +import com.gojuno.composer.Device +import com.gojuno.composer.Suite +import com.gojuno.composer.perform +import com.google.gson.Gson +import org.assertj.core.api.Assertions.assertThat +import org.jetbrains.spek.api.Spek +import org.jetbrains.spek.api.dsl.context +import org.jetbrains.spek.api.dsl.it +import rx.observers.TestSubscriber +import java.io.File +import java.util.concurrent.TimeUnit.MILLISECONDS +import java.util.concurrent.TimeUnit.SECONDS + +class HtmlReportSpec : Spek({ + + context("writeHtmlReport") { + + val adbDevice1 = AdbDevice( + id = "device1", + online = true + ) + + val suites = listOf( + Suite( + testPackage = "com.gojuno.example1", + devices = listOf(Device(id = "device1", logcat = File("device1.logcat"), instrumentationOutput = File("device1.instrumentation"))), + tests = listOf( + AdbDeviceTest( + adbDevice = adbDevice1, + className = "com.gojuno.example1.TestClass", + testName = "test1", + durationNanos = MILLISECONDS.toNanos(1234), + status = AdbDeviceTest.Status.Passed, + logcat = File("com.gojuno.example1.TestClass", "test1.logcat"), + files = listOf(File("com.gojuno.example1.TestClass.test1", "file1"), File("com.gojuno.example1.TestClass.test1", "file2")), + screenshots = listOf(File("com.gojuno.example1.TestClass.test1", "screenshot1"), File("com.gojuno.example1.TestClass.test1", "screenshot2")) + ), + AdbDeviceTest( + adbDevice = adbDevice1, + className = "com.gojuno.example1.TestClass", + testName = "test2", + durationNanos = MILLISECONDS.toNanos(1234), + status = AdbDeviceTest.Status.Failed(stacktrace = "abc"), + logcat = File("com.gojuno.example1.TestClass", "test2.logcat"), + files = listOf(File("com.gojuno.example1.TestClass.test2", "file1"), File("com.gojuno.example1.TestClass.test2", "file2")), + screenshots = listOf(File("com.gojuno.example1.TestClass.test2", "screenshot1"), File("com.gojuno.example1.TestClass.test2", "screenshot2")) + ) + ), + passedCount = 2, + ignoredCount = 0, + failedCount = 1, + durationNanos = MILLISECONDS.toNanos(1234 * 2), + timestampMillis = 1805 + ) + ) + + val outputDir by memoized { File("${System.nanoTime()}") } + + val subscriber = TestSubscriber() + + fun File.deleteOnExitRecursively() { + when (isDirectory) { + false -> deleteOnExit() + true -> listFiles()?.forEach { inner -> inner.deleteOnExitRecursively()} + } + } + + perform { + writeHtmlReport(Gson(), suites, outputDir).subscribe(subscriber) + subscriber.awaitTerminalEvent(5, SECONDS) + outputDir.deleteOnExitRecursively() + } + + it("completes") { + subscriber.assertCompleted() + } + + it("does not emit error") { + subscriber.assertNoErrors() + } + + it("creates index.json") { + assertThat(File(outputDir, "index.json").readText()).isEqualTo( + """{"suites":[{"id":"0","passed_count":2,"ignored_count":0,"failed_count":1,"duration_millis":2468,"devices":[{"id":"device1","logcat_path":"device1.logcat","instrumentation_output_path":"device1.instrumentation"}]}]}""" + ) + } + + it("creates suite json") { + assertThat(File(File(outputDir, "suites"), "0.json").readText()).isEqualTo( + """{"id":"0","tests":[{"id":"com.gojuno.example1TestClasstest1","package_name":"com.gojuno.example1","class_name":"TestClass","name":"test1","duration_millis":1234,"status":"passed","deviceId":"device1","properties":{}},{"id":"com.gojuno.example1TestClasstest2","package_name":"com.gojuno.example1","class_name":"TestClass","name":"test2","duration_millis":1234,"status":"failed","deviceId":"device1","properties":{}}],"passed_count":2,"ignored_count":0,"failed_count":1,"duration_millis":2468,"devices":[{"id":"device1","logcat_path":"device1.logcat","instrumentation_output_path":"device1.instrumentation"}]}""" + ) + } + + it("creates json for 1st test") { + assertThat(File(File(File(File(outputDir, "suites"), "0"), "device1"), "com.gojuno.example1TestClasstest1.json").readText()).isEqualTo( + """{"package_name":"com.gojuno.example1","class_name":"TestClass","name":"test1","id":"com.gojuno.example1TestClasstest1","duration_millis":1234,"status":"passed","logcat_path":"com.gojuno.example1.TestClass/test1.logcat","deviceId":"device1","properties":{},"file_paths":["com.gojuno.example1.TestClass.test1/file1","com.gojuno.example1.TestClass.test1/file2"],"screenshots_paths":["com.gojuno.example1.TestClass.test1/screenshot1","com.gojuno.example1.TestClass.test1/screenshot2"]}""" + ) + } + + it("creates json for 2nd test") { + assertThat(File(File(File(File(outputDir, "suites"), "0"), "device1"), "com.gojuno.example1TestClasstest2.json").readText()).isEqualTo( + """{"package_name":"com.gojuno.example1","class_name":"TestClass","name":"test2","id":"com.gojuno.example1TestClasstest2","duration_millis":1234,"status":"failed","stacktrace":"abc","logcat_path":"com.gojuno.example1.TestClass/test2.logcat","deviceId":"device1","properties":{},"file_paths":["com.gojuno.example1.TestClass.test2/file1","com.gojuno.example1.TestClass.test2/file2"],"screenshots_paths":["com.gojuno.example1.TestClass.test2/screenshot1","com.gojuno.example1.TestClass.test2/screenshot2"]}""" + ) + } + } +}) diff --git a/composer/src/test/kotlin/com/gojuno/composer/html/HtmlShortTestSpec.kt b/composer/src/test/kotlin/com/gojuno/composer/html/HtmlShortTestSpec.kt new file mode 100644 index 0000000..66e767d --- /dev/null +++ b/composer/src/test/kotlin/com/gojuno/composer/html/HtmlShortTestSpec.kt @@ -0,0 +1,41 @@ +package com.gojuno.composer.html + +import org.assertj.core.api.Assertions.assertThat +import org.jetbrains.spek.api.Spek +import org.jetbrains.spek.api.dsl.context +import org.jetbrains.spek.api.dsl.it + +class HtmlShortTestSpec : Spek({ + + context("HtmlFullTest.toHtmlShortTest") { + + val htmlFullTest = HtmlFullTest( + packageName = "com.gojuno.example", + className = "TestClass", + name = "test1", + status = HtmlFullTest.Status.Passed, + durationMillis = 1234, + stacktrace = null, + logcatPath = "testLogcatPath", + filePaths = listOf("testFilePath1", "testFilePath2"), + screenshotsPaths = listOf("testScreenshotPath1", "testScreenshotPath2"), + deviceId = "test-device-id", + properties = mapOf("key1" to "value1", "key2" to "value2") + ) + + val htmlShortTest = htmlFullTest.toHtmlShortTest() + + it("converts HtmlFullTest to HtmlShortTest") { + assertThat(htmlShortTest).isEqualTo(HtmlShortTest( + id = htmlFullTest.id, + packageName = "com.gojuno.example", + className = "TestClass", + name = "test1", + status = HtmlFullTest.Status.Passed, + durationMillis = 1234, + deviceId = "test-device-id", + properties = mapOf("key1" to "value1", "key2" to "value2") + )) + } + } +}) diff --git a/composer/src/test/kotlin/com/gojuno/composer/test.kt b/composer/src/test/kotlin/com/gojuno/composer/test.kt index 213a19a..7ae15a7 100644 --- a/composer/src/test/kotlin/com/gojuno/composer/test.kt +++ b/composer/src/test/kotlin/com/gojuno/composer/test.kt @@ -8,3 +8,5 @@ inline fun fileFromJarResources(name: String) = File(C::class.java.c fun testFile(): File = createTempFile().apply { deleteOnExit() } fun SpecBody.perform(body: () -> Unit) = beforeEachTest(body) + +fun SpecBody.cleanup(body: () -> Unit) = afterEachTest(body) diff --git a/dependencies.gradle b/dependencies.gradle index 4ff530b..7d67bf1 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -9,8 +9,8 @@ ext.versions = [ gson : '2.8.0', junit : '4.12', - junitPlatform : '1.0.0-M3', - spek : '1.1.0', + junitPlatform : '1.0.0-M4', + spek : '1.1.2', assertJ : '3.5.2', ] @@ -29,7 +29,6 @@ ext.libraries = [ junit : "junit:junit:$versions.junit", spek : "org.jetbrains.spek:spek-api:$versions.spek", - spekSubjectExtension : "org.jetbrains.spek:spek-subject-extension:$versions.spek", spekJunitPlatformEngine: "org.jetbrains.spek:spek-junit-platform-engine:$versions.spek", assertJ : "org.assertj:assertj-core:$versions.assertJ", ]