From 352265c000a9ef85245693648892473148d97823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Perez=20Neto?= Date: Mon, 30 Mar 2026 23:39:48 -0300 Subject: [PATCH 1/2] refactor: extract SPMResultParser and remove duplicated spmOutcome --- .../Execution/IncompatibleMutantExecutor.swift | 13 +------------ .../Execution/Parsing/SPMResultParser.swift | 12 ++++++++++++ .../Execution/TestExecutionStage.swift | 13 +------------ 3 files changed, 14 insertions(+), 24 deletions(-) create mode 100644 Sources/SwiftMutationTesting/Execution/Parsing/SPMResultParser.swift diff --git a/Sources/SwiftMutationTesting/Execution/IncompatibleMutantExecutor.swift b/Sources/SwiftMutationTesting/Execution/IncompatibleMutantExecutor.swift index 002f4fa..51b0080 100644 --- a/Sources/SwiftMutationTesting/Execution/IncompatibleMutantExecutor.swift +++ b/Sources/SwiftMutationTesting/Execution/IncompatibleMutantExecutor.swift @@ -74,7 +74,7 @@ struct IncompatibleMutantExecutor: Sendable { ) case .spm: - outcome = spmOutcome(exitCode: launched.exitCode, output: launched.output) + outcome = SPMResultParser().parse(exitCode: launched.exitCode, output: launched.output) } try? sandbox.cleanup() @@ -168,17 +168,6 @@ struct IncompatibleMutantExecutor: Sendable { ) } - private func spmOutcome(exitCode: Int32, output: String) -> TestRunOutcome { - if exitCode == -1 { return .timedOut } - if exitCode == 0 { return .testsSucceeded } - - switch TestOutputParser().parse(output) { - case .killed(let name): return .testsFailed(failingTest: name) - case .crashed: return .crashed - case .unviable: return .unviable - } - } - private func storeAndReport( mutant: MutantDescriptor, key: MutantCacheKey, diff --git a/Sources/SwiftMutationTesting/Execution/Parsing/SPMResultParser.swift b/Sources/SwiftMutationTesting/Execution/Parsing/SPMResultParser.swift new file mode 100644 index 0000000..04c98cf --- /dev/null +++ b/Sources/SwiftMutationTesting/Execution/Parsing/SPMResultParser.swift @@ -0,0 +1,12 @@ +struct SPMResultParser: Sendable { + func parse(exitCode: Int32, output: String) -> TestRunOutcome { + if exitCode == -1 { return .timedOut } + if exitCode == 0 { return .testsSucceeded } + + switch TestOutputParser().parse(output) { + case .killed(let name): return .testsFailed(failingTest: name) + case .crashed: return .crashed + case .unviable: return .unviable + } + } +} diff --git a/Sources/SwiftMutationTesting/Execution/TestExecutionStage.swift b/Sources/SwiftMutationTesting/Execution/TestExecutionStage.swift index f208d3e..b3412d0 100644 --- a/Sources/SwiftMutationTesting/Execution/TestExecutionStage.swift +++ b/Sources/SwiftMutationTesting/Execution/TestExecutionStage.swift @@ -94,7 +94,7 @@ struct TestExecutionStage: Sendable { } await context.pool.release(slot) - let outcome = spmOutcome(exitCode: launched.exitCode, output: launched.output) + let outcome = SPMResultParser().parse(exitCode: launched.exitCode, output: launched.output) let status = outcome.asExecutionStatus let result = ExecutionResult(descriptor: mutant, status: status, testDuration: launched.duration) await cacheStore.store(status: status, for: key) @@ -130,17 +130,6 @@ struct TestExecutionStage: Sendable { ) } - private func spmOutcome(exitCode: Int32, output: String) -> TestRunOutcome { - if exitCode == -1 { return .timedOut } - if exitCode == 0 { return .testsSucceeded } - - switch TestOutputParser().parse(output) { - case .killed(let name): return .testsFailed(failingTest: name) - case .crashed: return .crashed - case .unviable: return .unviable - } - } - private func launch( plistData: Data, slot: SimulatorSlot, From a5dcce1cb71d4c0a6cc5dbc7313884e2f62a7f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Perez=20Neto?= Date: Mon, 30 Mar 2026 23:39:53 -0300 Subject: [PATCH 2/2] test: add SPMResultParserTests --- .../Parsing/SPMResultParserTests.swift | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Tests/SwiftMutationTestingTests/Unit/Execution/Parsing/SPMResultParserTests.swift diff --git a/Tests/SwiftMutationTestingTests/Unit/Execution/Parsing/SPMResultParserTests.swift b/Tests/SwiftMutationTestingTests/Unit/Execution/Parsing/SPMResultParserTests.swift new file mode 100644 index 0000000..f8349fa --- /dev/null +++ b/Tests/SwiftMutationTestingTests/Unit/Execution/Parsing/SPMResultParserTests.swift @@ -0,0 +1,45 @@ +import Testing + +@testable import SwiftMutationTesting + +@Suite("SPMResultParser") +struct SPMResultParserTests { + private let parser = SPMResultParser() + + @Test("Given exit code -1, when parse called, then returns timedOut") + func exitCodeMinusOneReturnsTimedOut() { + #expect(parser.parse(exitCode: -1, output: "") == .timedOut) + } + + @Test("Given exit code 0, when parse called, then returns testsSucceeded") + func exitCodeZeroReturnsTestsSucceeded() { + #expect(parser.parse(exitCode: 0, output: "") == .testsSucceeded) + } + + @Test("Given exit code 1 and Swift Testing failure in output, when parse called, then returns testsFailed") + func swiftTestingFailureOutputReturnsTestsFailed() { + let output = #"Test "myTest" failed after 0.001 seconds."# + let result = parser.parse(exitCode: 1, output: output) + #expect(result == .testsFailed(failingTest: "myTest")) + } + + @Test("Given exit code 1 and XCTest failure in output, when parse called, then returns testsFailed") + func xcTestFailureOutputReturnsTestsFailed() { + let output = "Test Case '-[MySuite myTest]' failed (0.001 seconds)." + let result = parser.parse(exitCode: 1, output: output) + #expect(result == .testsFailed(failingTest: "MySuite.myTest")) + } + + @Test("Given exit code 1 and crash in output, when parse called, then returns crashed") + func crashOutputReturnsCrashed() { + let output = "Test run started.\nFatal error: something went wrong" + let result = parser.parse(exitCode: 1, output: output) + #expect(result == .crashed) + } + + @Test("Given exit code 1 and no recognisable test output, when parse called, then returns unviable") + func noTestOutputReturnsUnviable() { + let result = parser.parse(exitCode: 1, output: "something unrelated") + #expect(result == .unviable) + } +}