Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 43 additions & 5 deletions src/main/kotlin/file/SetupApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ object SetupApp {

private data class WurstProcessResult(val exitCode: Int, val output: List<String>)

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 = "<!-- WURST_AGENTS_TEMPLATE_VERSION:"
private const val AGENTS_TEMPLATE_MARKER = "<!-- WURST_AGENTS_TEMPLATE_VERSION: $AGENTS_TEMPLATE_VERSION -->"
Expand Down Expand Up @@ -519,6 +524,27 @@ object SetupApp {
}.coerceAtLeast(1)
}

internal fun wurstTestSummary(output: List<String>): 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<String>): 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) ||
Expand Down Expand Up @@ -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) {
Expand Down
22 changes: 22 additions & 0 deletions src/test/kotlin/GenerateTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading