diff --git a/src/main/kotlin/file/SetupApp.kt b/src/main/kotlin/file/SetupApp.kt index fa2d636..87fa2d5 100644 --- a/src/main/kotlin/file/SetupApp.kt +++ b/src/main/kotlin/file/SetupApp.kt @@ -33,6 +33,11 @@ object SetupApp { private data class WurstProcessResult(val exitCode: Int, val output: List) + internal data class WurstTestSummary(val passed: Int, val total: Int) { + val condensed: String + get() = "Tests: $passed/$total passed" + } + internal const val AGENTS_TEMPLATE_VERSION = "2026-09-06" private const val AGENTS_TEMPLATE_MARKER_PREFIX = "" @@ -519,6 +524,27 @@ object SetupApp { }.coerceAtLeast(1) } + internal fun wurstTestSummary(output: List): WurstTestSummary? { + val patterns = listOf( + Regex("""^Tests:\s*(\d+)/(\d+)\s+passed\s*$""", RegexOption.IGNORE_CASE), + Regex("""^Tests succeeded:\s*(\d+)/(\d+)\s*$""", RegexOption.IGNORE_CASE) + ) + return output.asReversed().asSequence() + .map { it.trim() } + .mapNotNull { line -> patterns.firstNotNullOfOrNull { it.matchEntire(line) } } + .mapNotNull { match -> + val passed = match.groupValues[1].toIntOrNull() ?: return@mapNotNull null + val total = match.groupValues[2].toIntOrNull() ?: return@mapNotNull null + WurstTestSummary(passed, total) + } + .firstOrNull() + } + + internal fun isSuccessfulTestRun(exitCode: Int, output: List): Boolean { + val summary = wurstTestSummary(output) ?: return false + return exitCode == 0 && summary.total > 0 && summary.passed == summary.total + } + private fun isQuietCompilerDiagnosticLine(line: String): Boolean { return line.startsWith("Error ", ignoreCase = true) || line.startsWith("FAILED ", ignoreCase = true) || @@ -1079,13 +1105,25 @@ object SetupApp { } val result = startWurstProcess(args) - when (result.exitCode) { - 0 -> { pass("✅ All tests succeeded."); ExitHandler.exit(0) } - else -> { - printCompilerFailure("test", result) - ExitHandler.exit(1) + val summary = wurstTestSummary(result.output) + if (setup.quiet && summary != null) { + println(summary.condensed) + } + + if (isSuccessfulTestRun(result.exitCode, result.output)) { + if (!setup.quiet) { + pass("✅ All tests succeeded.") } + ExitHandler.exit(0) + } + + when { + result.exitCode != 0 -> printCompilerFailure("test", result) + summary == null -> fail("❌ Wurst test failed: the compiler did not report any test results.") + summary.total == 0 -> fail("❌ Wurst test failed: no tests were found.") + else -> printCompilerFailure("test", result) } + ExitHandler.exit(1) } private fun typecheckProject(configData: WurstProjectConfigData) { diff --git a/src/test/kotlin/GenerateTests.kt b/src/test/kotlin/GenerateTests.kt index 51f54cd..d11a977 100644 --- a/src/test/kotlin/GenerateTests.kt +++ b/src/test/kotlin/GenerateTests.kt @@ -344,6 +344,28 @@ class GenerateTests { Assert.assertEquals(SetupApp.quietCompilerErrorCount(output), 1) } + @Test(priority = 10) + fun testWurstTestSummarySupportsCompactAndLegacyOutput() { + Assert.assertEquals( + SetupApp.wurstTestSummary(listOf("Tests: 7/8 passed")), + SetupApp.WurstTestSummary(7, 8) + ) + Assert.assertEquals( + SetupApp.wurstTestSummary(listOf("Tests succeeded: 3/3")), + SetupApp.WurstTestSummary(3, 3) + ) + Assert.assertEquals(SetupApp.wurstTestSummary(listOf("Finished running tests")), null) + } + + @Test(priority = 10) + fun testSuccessfulWurstTestRunRequiresExecutedPassingTests() { + Assert.assertTrue(SetupApp.isSuccessfulTestRun(0, listOf("Tests: 2/2 passed"))) + Assert.assertFalse(SetupApp.isSuccessfulTestRun(1, listOf("Tests: 2/2 passed"))) + Assert.assertFalse(SetupApp.isSuccessfulTestRun(0, listOf("Tests: 1/2 passed"))) + Assert.assertFalse(SetupApp.isSuccessfulTestRun(0, listOf("Tests: 0/0 passed"))) + Assert.assertFalse(SetupApp.isSuccessfulTestRun(0, listOf("Finished running tests"))) + } + @Test(priority = 10) fun testQuietCompilerDiagnosticsNormalizeVerboseFallbackErrors() { val output = listOf(