Skip to content

Commit 152c651

Browse files
committed
Improved the XML escaping and address some comments
1 parent 719c979 commit 152c651

File tree

3 files changed

+37
-16
lines changed

3 files changed

+37
-16
lines changed

Sources/Commands/SwiftTestCommand.swift

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,6 @@ public struct SwiftTestCommand: AsyncSwiftCommand {
342342
try generateXUnitOutputIfRequested(
343343
for: testResults,
344344
swiftCommandState: swiftCommandState,
345-
detailedFailureMessage: self.options.shouldShowDetailedFailureMessage
346345
)
347346
results.append(result)
348347
}
@@ -420,7 +419,6 @@ public struct SwiftTestCommand: AsyncSwiftCommand {
420419
private func generateXUnitOutputIfRequested(
421420
for testResults: [ParallelTestRunner.TestResult],
422421
swiftCommandState: SwiftCommandState,
423-
detailedFailureMessage: Bool
424422
) throws {
425423
guard let xUnitOutput = options.xUnitOutput else {
426424
return
@@ -430,7 +428,10 @@ public struct SwiftTestCommand: AsyncSwiftCommand {
430428
fileSystem: swiftCommandState.fileSystem,
431429
results: testResults
432430
)
433-
try generator.generate(at: xUnitOutput, detailedFailureMessage: detailedFailureMessage)
431+
try generator.generate(
432+
at: xUnitOutput,
433+
detailedFailureMessage: self.options.shouldShowDetailedFailureMessage
434+
)
434435
}
435436

436437
// MARK: - Common implementation
@@ -1418,14 +1419,7 @@ final class XUnitGenerator {
14181419
"""
14191420

14201421
if !result.success {
1421-
var failureMessage: String = "failed"
1422-
if detailedFailureMessage {
1423-
failureMessage = result.output
1424-
failureMessage.replace("&", with: "&")
1425-
failureMessage.replace("\"", with:""")
1426-
failureMessage.replace(">", with: ">")
1427-
failureMessage.replace("<", with: "&lt;")
1428-
}
1422+
let failureMessage = detailedFailureMessage ? result.output.map(_escapeForXML).joined() : "failure"
14291423
content += "<failure message=\"\(failureMessage)\"></failure>\n"
14301424
}
14311425

@@ -1443,6 +1437,32 @@ final class XUnitGenerator {
14431437
}
14441438
}
14451439

1440+
/// Escape a single Unicode character for use in an XML-encoded string.
1441+
///
1442+
/// - Parameters:
1443+
/// - character: The character to escape.
1444+
///
1445+
/// - Returns: `character`, or a string containing its escaped form.
1446+
private func _escapeForXML(_ character: Character) -> String {
1447+
switch character {
1448+
case "\"":
1449+
"&quot;"
1450+
case "<":
1451+
"&lt;"
1452+
case ">":
1453+
"&gt;"
1454+
case "&":
1455+
"&amp;"
1456+
case _ where !character.isASCII || character.isNewline:
1457+
character.unicodeScalars.lazy
1458+
.map(\.value)
1459+
.map { "&#\($0);" }
1460+
.joined()
1461+
default:
1462+
String(character)
1463+
}
1464+
}
1465+
14461466
extension SwiftCommandState {
14471467
func buildParametersForTest(
14481468
options: TestCommandOptions

Sources/_InternalTestSupport/SwiftPMProduct.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,15 @@ extension SwiftPM {
7676
/// - args: The arguments to pass.
7777
/// - env: Additional environment variables to pass. The values here are merged with default env.
7878
/// - packagePath: Adds argument `--package-path <path>` if not nil.
79+
/// - throwIfCommandFails: If set, will throw an error if the command does not have a 0 return code.
7980
///
8081
/// - Returns: The output of the process.
8182
@discardableResult
8283
public func execute(
8384
_ args: [String] = [],
8485
packagePath: AbsolutePath? = nil,
8586
env: Environment? = nil,
86-
errorIfCommandUnsuccessful: Bool = true
87+
throwIfCommandFails: Bool = true
8788
) async throws -> (stdout: String, stderr: String) {
8889
let result = try await executeProcess(
8990
args,
@@ -95,7 +96,7 @@ extension SwiftPM {
9596
let stderr = try result.utf8stderrOutput()
9697

9798
let returnValue = (stdout: stdout, stderr: stderr)
98-
if (!errorIfCommandUnsuccessful) { return returnValue }
99+
if (!throwIfCommandFails) { return returnValue }
99100

100101
if result.exitStatus == .terminated(code: 0) {
101102
return returnValue

Tests/CommandsTests/TestCommandTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ final class TestCommandTests: CommandsTestCase {
2121
private func execute(
2222
_ args: [String],
2323
packagePath: AbsolutePath? = nil,
24-
errorIfCommandUnsuccessful: Bool = true
24+
throwIfCommandFails: Bool = true
2525
) async throws -> (stdout: String, stderr: String) {
26-
try await SwiftPM.Test.execute(args, packagePath: packagePath, errorIfCommandUnsuccessful: errorIfCommandUnsuccessful)
26+
try await SwiftPM.Test.execute(args, packagePath: packagePath, throwIfCommandFails: throwIfCommandFails)
2727
}
2828

2929
func testUsage() async throws {
@@ -208,7 +208,7 @@ final class TestCommandTests: CommandsTestCase {
208208
xUnitOutput.pathString
209209
] + extraCommandArgs,
210210
packagePath: fixturePath,
211-
errorIfCommandUnsuccessful: false
211+
throwIfCommandFails: false
212212
)
213213

214214
// THEN we expect \(xUnitUnderTest) to exists

0 commit comments

Comments
 (0)