Skip to content

Commit 7411a19

Browse files
authored
Create an xunit results file even if no tests are run. (#7066)
This PR ensures that when `--xunit-output` is passed to `swift test --parallel`, the output XML file is generated even if no tests are run. Failure modes outside of test failures (i.e. errors thrown during the build process or when trying to run the test process) will still prevent the generation of this file. Resolves #7065.
1 parent e8328ac commit 7411a19

File tree

6 files changed

+56
-8
lines changed

6 files changed

+56
-8
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
/*.xcodeproj
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// swift-tools-version:4.2
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "EmptyTestsPkg",
6+
targets: [
7+
.target(
8+
name: "EmptyTestsPkg",
9+
dependencies: []),
10+
.testTarget(
11+
name: "EmptyTestsPkgTests",
12+
dependencies: ["EmptyTestsPkg"]),
13+
]
14+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
struct EmptyTests {
2+
3+
var text = "Hello, World!"
4+
var bool = false
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import XCTest
2+
@testable import EmptyTestsPkg

Sources/Commands/SwiftTestTool.swift

+15-8
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ public struct SwiftTestTool: SwiftCommand {
241241
// If there were no matches, emit a warning and exit.
242242
if tests.isEmpty {
243243
swiftTool.observabilityScope.emit(.noMatchingTests)
244+
try generateXUnitOutputIfRequested(for: [], swiftTool: swiftTool)
244245
return
245246
}
246247

@@ -264,14 +265,7 @@ public struct SwiftTestTool: SwiftCommand {
264265

265266
let testResults = try runner.run(tests)
266267

267-
// Generate xUnit file if requested
268-
if let xUnitOutput = options.xUnitOutput {
269-
let generator = XUnitGenerator(
270-
fileSystem: swiftTool.fileSystem,
271-
results: testResults
272-
)
273-
try generator.generate(at: xUnitOutput)
274-
}
268+
try generateXUnitOutputIfRequested(for: testResults, swiftTool: swiftTool)
275269

276270
// process code Coverage if request
277271
if self.options.enableCodeCoverage, runner.ranSuccessfully {
@@ -325,6 +319,19 @@ public struct SwiftTestTool: SwiftCommand {
325319
}
326320
}
327321

322+
/// Generate xUnit file if requested.
323+
private func generateXUnitOutputIfRequested(for testResults: [ParallelTestRunner.TestResult], swiftTool: SwiftTool) throws {
324+
guard let xUnitOutput = options.xUnitOutput else {
325+
return
326+
}
327+
328+
let generator = XUnitGenerator(
329+
fileSystem: swiftTool.fileSystem,
330+
results: testResults
331+
)
332+
try generator.generate(at: xUnitOutput)
333+
}
334+
328335
// MARK: - swift-testing
329336

330337
private func swiftTestingRun(_ swiftTool: SwiftTool) throws {

Tests/CommandsTests/TestToolTests.swift

+16
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,22 @@ final class TestToolTests: CommandsTestCase {
128128
}
129129
}
130130

131+
func testSwiftTestXMLOutputWhenEmpty() throws {
132+
try fixture(name: "Miscellaneous/EmptyTestsPkg") { fixturePath in
133+
let xUnitOutput = fixturePath.appending("result.xml")
134+
// Run tests in parallel with verbose output.
135+
let stdout = try SwiftPM.Test.execute(["--parallel", "--verbose", "--xunit-output", xUnitOutput.pathString], packagePath: fixturePath).stdout
136+
// in "swift test" test output goes to stdout
137+
XCTAssertNoMatch(stdout, .contains("passed"))
138+
XCTAssertNoMatch(stdout, .contains("failed"))
139+
140+
// Check the xUnit output.
141+
XCTAssertFileExists(xUnitOutput)
142+
let contents: String = try localFileSystem.readFileContents(xUnitOutput)
143+
XCTAssertMatch(contents, .contains("tests=\"0\" failures=\"0\""))
144+
}
145+
}
146+
131147
func testSwiftTestFilter() throws {
132148
try fixture(name: "Miscellaneous/SkipTests") { fixturePath in
133149
let (stdout, _) = try SwiftPM.Test.execute(["--filter", ".*1"], packagePath: fixturePath)

0 commit comments

Comments
 (0)