diff --git a/burst-gradle-plugin/src/test/kotlin/app/cash/burst/gradle/BurstGradlePluginTest.kt b/burst-gradle-plugin/src/test/kotlin/app/cash/burst/gradle/BurstGradlePluginTest.kt index af45119..3506724 100644 --- a/burst-gradle-plugin/src/test/kotlin/app/cash/burst/gradle/BurstGradlePluginTest.kt +++ b/burst-gradle-plugin/src/test/kotlin/app/cash/burst/gradle/BurstGradlePluginTest.kt @@ -18,6 +18,8 @@ package app.cash.burst.gradle import assertk.assertThat import assertk.assertions.contains +import assertk.assertions.containsExactlyInAnyOrder +import assertk.assertions.isFalse import assertk.assertions.isTrue import java.io.File import org.gradle.testkit.runner.GradleRunner @@ -29,13 +31,34 @@ class BurstGradlePluginTest { fun happyPath() { val projectDir = File("src/test/projects/basic") - val taskName = ":lib:compileProductionExecutableKotlinJsZipline" + val taskName = ":lib:jvmTest" val result = createRunner(projectDir, "clean", taskName).build() assertThat(SUCCESS_OUTCOMES) .contains(result.task(taskName)!!.outcome) - val ziplineOut = projectDir.resolve("lib/build/zipline/Production") - assertThat(ziplineOut.resolve("basic-lib.zipline").exists()).isTrue() + val testResults = projectDir.resolve("lib/build/test-results") + val jvmTestXmlFile = testResults.resolve("jvmTest/TEST-CoffeeTest.xml") + + val testSuite = readTestSuite(jvmTestXmlFile) + + assertThat(testSuite.testCases.map { it.name }).containsExactlyInAnyOrder( + "test[jvm]", + "test_Decaf_Oat[jvm]", + "test_Regular_Milk[jvm]", + "test_Regular_None[jvm]", + "test_Decaf_Milk[jvm]", + "test_Decaf_None[jvm]", + "test_Double_Milk[jvm]", + "test_Double_None[jvm]", + "test_Regular_Oat[jvm]", + "test_Double_Oat[jvm]", + ) + + val originalTest = testSuite.testCases.single { it.name == "test[jvm]" } + assertThat(originalTest.skipped).isTrue() + + val sampleVariant = testSuite.testCases.single { it.name == "test_Decaf_Oat[jvm]" } + assertThat(sampleVariant.skipped).isFalse() } private fun createRunner( diff --git a/burst-gradle-plugin/src/test/kotlin/app/cash/burst/gradle/junitXml.kt b/burst-gradle-plugin/src/test/kotlin/app/cash/burst/gradle/junitXml.kt new file mode 100644 index 0000000..79d60c9 --- /dev/null +++ b/burst-gradle-plugin/src/test/kotlin/app/cash/burst/gradle/junitXml.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 Cash App + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package app.cash.burst.gradle + +import java.io.File +import javax.xml.parsers.DocumentBuilderFactory +import org.w3c.dom.Element + +fun readTestSuite(xmlFile: File): TestSuite { + val builder = DocumentBuilderFactory.newInstance().newDocumentBuilder() + val document = builder.parse(xmlFile) + return document.documentElement.toTestSuite() +} + +internal fun Element.toTestSuite(): TestSuite { + val testCases = mutableListOf() + for (i in 0 until childNodes.length) { + val item = childNodes.item(i) + if (item !is Element || item.tagName != "testcase") continue + testCases += item.toTestCase() + } + + return TestSuite( + name = getAttribute("name"), + testCases = testCases, + ) +} + +internal fun Element.toTestCase(): TestCase { + var skipped = false + for (i in 0 until childNodes.length) { + val item = childNodes.item(i) + if (item is Element && item.tagName == "skipped") { + skipped = true + } + } + + return TestCase( + name = getAttribute("name"), + skipped = skipped, + ) +} + +class TestSuite( + val name: String, + val testCases: List, +) + +class TestCase( + val name: String, + val skipped: Boolean, +) diff --git a/burst-gradle-plugin/src/test/projects/basic/lib/build.gradle.kts b/burst-gradle-plugin/src/test/projects/basic/lib/build.gradle.kts index dbb013d..294ab7d 100644 --- a/burst-gradle-plugin/src/test/projects/basic/lib/build.gradle.kts +++ b/burst-gradle-plugin/src/test/projects/basic/lib/build.gradle.kts @@ -10,6 +10,7 @@ kotlin { commonTest { dependencies { implementation("app.cash.burst:burst:${project.property("burstVersion")}") + implementation(kotlin("test")) } } } diff --git a/burst-gradle-plugin/src/test/projects/basic/lib/src/commonMain/kotlin/app/cash/zipline/tests/GreetService.kt b/burst-gradle-plugin/src/test/projects/basic/lib/src/commonMain/kotlin/app/cash/zipline/tests/GreetService.kt deleted file mode 100644 index eed639d..0000000 --- a/burst-gradle-plugin/src/test/projects/basic/lib/src/commonMain/kotlin/app/cash/zipline/tests/GreetService.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2022 Block, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package app.cash.zipline.tests - -import app.cash.zipline.ZiplineService - -interface GreetService : ZiplineService { - fun greet(name: String): String -} diff --git a/burst-gradle-plugin/src/test/projects/basic/lib/src/commonTest/kotlin/CoffeeTest.kt b/burst-gradle-plugin/src/test/projects/basic/lib/src/commonTest/kotlin/CoffeeTest.kt new file mode 100644 index 0000000..d305ba5 --- /dev/null +++ b/burst-gradle-plugin/src/test/projects/basic/lib/src/commonTest/kotlin/CoffeeTest.kt @@ -0,0 +1,16 @@ +import app.cash.burst.Burst +import kotlin.test.Test + +@Burst +class CoffeeTest { + val log = mutableListOf() + + @Test + fun test(espresso: Espresso, dairy: Dairy) { + log += "running $espresso $dairy" + } +} + +enum class Espresso { Decaf, Regular, Double } + +enum class Dairy { None, Milk, Oat } diff --git a/burst-gradle-plugin/src/test/projects/basic/lib/src/jsMain/kotlin/app/cash/zipline/tests/launchGreetServiceJs.kt b/burst-gradle-plugin/src/test/projects/basic/lib/src/jsMain/kotlin/app/cash/zipline/tests/launchGreetServiceJs.kt deleted file mode 100644 index 3302978..0000000 --- a/burst-gradle-plugin/src/test/projects/basic/lib/src/jsMain/kotlin/app/cash/zipline/tests/launchGreetServiceJs.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2022 Block, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package app.cash.zipline.tests - -import app.cash.zipline.Zipline - -@JsExport -fun launchGreetService() { - val zipline = Zipline.get() - - val greetService = object : GreetService { - override fun greet(name: String) = "Hello, $name" - } - - zipline.bind("greetService", greetService) -} diff --git a/burst-gradle-plugin/src/test/projects/basic/lib/src/jvmMain/kotlin/app/cash/zipline/tests/launchGreetServiceJvm.kt b/burst-gradle-plugin/src/test/projects/basic/lib/src/jvmMain/kotlin/app/cash/zipline/tests/launchGreetServiceJvm.kt deleted file mode 100644 index dc8b5b5..0000000 --- a/burst-gradle-plugin/src/test/projects/basic/lib/src/jvmMain/kotlin/app/cash/zipline/tests/launchGreetServiceJvm.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2022 Block, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package app.cash.zipline.tests - -import app.cash.zipline.Zipline -import app.cash.zipline.loader.DefaultFreshnessCheckerNotFresh -import app.cash.zipline.loader.LoadResult -import app.cash.zipline.loader.ManifestVerifier.Companion.NO_SIGNATURE_CHECKS -import app.cash.zipline.loader.ZiplineHttpClient -import app.cash.zipline.loader.ZiplineLoader -import java.util.concurrent.Executors -import kotlin.system.exitProcess -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.asCoroutineDispatcher -import kotlinx.coroutines.runBlocking -import okio.ByteString -import okio.FileSystem -import okio.Path.Companion.toPath - -suspend fun launchZipline(dispatcher: CoroutineDispatcher): Zipline { - // A fake HTTP client that returns files as the Webpack dev server would return them. - val localDirectoryHttpClient = object : ZiplineHttpClient() { - val base = "build/zipline/Production".toPath() - override suspend fun download( - url: String, - requestHeaders: List>, - ): ByteString { - val file = url.substringAfterLast("/") - return FileSystem.SYSTEM.read(base / file) { readByteString() } - } - } - val loader = ZiplineLoader( - dispatcher = dispatcher, - manifestVerifier = NO_SIGNATURE_CHECKS, - httpClient = localDirectoryHttpClient, - ) - - val result = loader.loadOnce("test", DefaultFreshnessCheckerNotFresh, "https://localhost/manifest.zipline.json") - return when (result) { - is LoadResult.Success -> result.zipline - is LoadResult.Failure -> throw result.exception - } -} - -fun main() { - val executorService = Executors.newFixedThreadPool(1) { - Thread(it, "Zipline") - } - val dispatcher = executorService.asCoroutineDispatcher() - runBlocking(dispatcher) { - val zipline = launchZipline(dispatcher) - val greetService = zipline.take("greetService") - val greeting = greetService.greet("Jesse") - println("end-to-end call result: '$greeting'") - } - exitProcess(0) -} diff --git a/burst-kotlin-plugin-tests/src/test/kotlin/app/cash/burst/kotlin/BurstKotlinPluginTest.kt b/burst-kotlin-plugin-tests/src/test/kotlin/app/cash/burst/kotlin/BurstKotlinPluginTest.kt index 5f2ce68..2b86836 100644 --- a/burst-kotlin-plugin-tests/src/test/kotlin/app/cash/burst/kotlin/BurstKotlinPluginTest.kt +++ b/burst-kotlin-plugin-tests/src/test/kotlin/app/cash/burst/kotlin/BurstKotlinPluginTest.kt @@ -39,7 +39,6 @@ class BurstKotlinPluginTest { package app.cash.burst.testing import app.cash.burst.Burst - import kotlin.test.Ignore import kotlin.test.Test @Burst @@ -50,19 +49,6 @@ class BurstKotlinPluginTest { fun test(espresso: Espresso, dairy: Dairy) { log += "running ${'$'}espresso ${'$'}dairy" } - - // Generate this - @Test - @Ignore - fun x_test() { - x_test_Decaf_None() - } - - // Generate this - @Test - fun x_test_Decaf_None() { - test(Espresso.Decaf, Dairy.None) - } } enum class Espresso { Decaf, Regular, Double } diff --git a/burst-kotlin-plugin/src/main/kotlin/app/cash/burst/kotlin/BurstRewriter.kt b/burst-kotlin-plugin/src/main/kotlin/app/cash/burst/kotlin/BurstRewriter.kt index 2f29a9b..4cf0541 100644 --- a/burst-kotlin-plugin/src/main/kotlin/app/cash/burst/kotlin/BurstRewriter.kt +++ b/burst-kotlin-plugin/src/main/kotlin/app/cash/burst/kotlin/BurstRewriter.kt @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.classFqName import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.starProjectedType import org.jetbrains.kotlin.ir.util.classId @@ -87,9 +88,10 @@ internal class BurstRewriter( createVariant(originalDispatchReceiver, variantArguments) } - // Side-effect: add `@Ignore` - // TODO: if its' absent! - original.annotations += burstApis.ignoreClassSymbol.asAnnotation() + // Side-effect: drop `@Test` from the original's annotations. + original.annotations = original.annotations.filter { + it.type.classFqName != burstApis.testClassSymbol.starProjectedType.classFqName + } val result = mutableListOf() result += createFunctionThatCallsAllVariants(originalDispatchReceiver, variants)