Skip to content

Commit 2e93797

Browse files
committed
Test: ensure swift-build does not emit fatal error on console
This partially ensures github issue 6605 will not re-occur. Add an automated tests that ensures swift-build will not emit `error: fatalError` message on the console output upon compilation failure as the swift-driver/swift-frontend emits that error. A similar test should be added, that invokes `swift build`
1 parent 5efd210 commit 2e93797

File tree

4 files changed

+198
-1
lines changed

4 files changed

+198
-1
lines changed

Package.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,12 @@ if ProcessInfo.processInfo.environment["SWIFTCI_DISABLE_SDK_DEPENDENT_TESTS"] ==
873873
"Basics",
874874
]
875875
),
876-
876+
.testTarget(
877+
name: "_InternalTestSupportTests",
878+
dependencies: [
879+
"_InternalTestSupport"
880+
]
881+
),
877882
.testTarget(
878883
name: "CommandsTests",
879884
dependencies: [

Sources/_InternalTestSupport/misc.swift

+49
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import Foundation
1314
import Basics
1415
import struct Foundation.URL
1516
#if os(macOS)
@@ -465,3 +466,51 @@ extension PackageIdentity: @retroactive ExpressibleByStringInterpolation {}
465466
extension AbsolutePath: @retroactive ExpressibleByStringLiteral {}
466467
extension AbsolutePath: @retroactive ExpressibleByStringInterpolation {}
467468
#endif
469+
470+
public func getNumberOfMatches(of match: String, in value: String) -> Int {
471+
return value.components(separatedBy: match).count - 1
472+
}
473+
474+
475+
private func log_message(
476+
_ message: String,
477+
level: String,
478+
file: StaticString = #file,
479+
line: UInt = #line
480+
) {
481+
let message = [
482+
"Emitted by Test",
483+
"\(file):\(line)",
484+
"\(level)",
485+
message + "\n",
486+
].joined(separator: " | ")
487+
488+
fputs(message, stderr)
489+
490+
}
491+
492+
public func log_info(
493+
_ message: String,
494+
file: StaticString = #file,
495+
line: UInt = #line
496+
) {
497+
log_message(
498+
message,
499+
level: "INFO",
500+
file: file,
501+
line: line
502+
)
503+
}
504+
505+
public func log_debug(
506+
_ message: String,
507+
file: StaticString = #file,
508+
line: UInt = #line
509+
) {
510+
log_message(
511+
message,
512+
level: "DEBUG",
513+
file: file,
514+
line: line
515+
)
516+
}

Tests/CommandsTests/BuildCommandTests.swift

+33
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import Foundation
1314
import Basics
1415
@testable import Commands
1516
@testable import CoreCommands
@@ -678,4 +679,36 @@ final class BuildCommandTests: CommandsTestCase {
678679
XCTAssertGreaterThan(codeCovFiles.count, 0)
679680
}
680681
}
682+
683+
// Test for GitHub Issue #6605
684+
func testFatalErrorDisplayedOnlyOnceWhenSingleXCTestHasFatalErrorInBuildCompilaation() async throws {
685+
log_info("GIVEN we have a Swift Package that has a fatalError building the tests")
686+
try await fixture(name: "Miscellaneous/Errors/FatalErrorInSingleXCTest/TypeLibrary") {fixturePath in
687+
log_info("WHEN swift-build --build-tests is executed")
688+
await XCTAssertAsyncThrowsError(try await self.execute(["--build-tests"], packagePath: fixturePath)) { error in
689+
log_info("THEN I expect a failure")
690+
guard case SwiftPMError.executionFailure(_, let stdoutUT, let stderrUT) = error else {
691+
XCTFail()
692+
return
693+
}
694+
695+
let matchString = "error: fatalError"
696+
let stdoutMatches = getNumberOfMatches(of: matchString, in: stdoutUT)
697+
let stderrMatches = getNumberOfMatches(of: matchString, in: stderrUT)
698+
let actualNumMatches = stdoutMatches + stderrMatches
699+
log_debug("stdout: \(stdoutUT.debugDescription)")
700+
log_debug("stderr: \(stderrUT.debugDescription)")
701+
log_debug("number stdout matches: \(stdoutMatches)")
702+
log_debug("number stderr matches: \(stderrMatches)")
703+
704+
log_info("AND a fatal error message is not printed to the console")
705+
XCTAssertEqual(
706+
actualNumMatches,
707+
0,
708+
"We expected 1 math but found \(actualNumMatches) occurrances"
709+
)
710+
}
711+
}
712+
}
713+
681714
}
+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import _InternalTestSupport
2+
3+
import XCTest
4+
5+
class TestGetNumberOfMatches: XCTestCase {
6+
7+
func testEmptyStringMatchesOnEmptyStringZeroTimes() {
8+
let matchOn = ""
9+
let value = ""
10+
let expectedNumMatches = 0
11+
12+
let actual = getNumberOfMatches(of: matchOn, in: value)
13+
14+
XCTAssertEqual(actual, expectedNumMatches, "Actual is not as expected")
15+
}
16+
17+
func testEmptyStringMatchesOnNonEmptySingleLineStringZeroTimes() {
18+
let matchOn = ""
19+
let value = "This is a non-empty string"
20+
let expectedNumMatches = 0
21+
22+
let actual = getNumberOfMatches(of: matchOn, in: value)
23+
24+
XCTAssertEqual(actual, expectedNumMatches, "Actual is not as expected")
25+
}
26+
27+
func testEmptyStringMatchesOnNonEmptyMultilineStringWithNeLineCharacterZeroTimes() {
28+
let matchOn = ""
29+
let value = "This is a non-empty string\nThis is the second line"
30+
let expectedNumMatches = 0
31+
32+
let actual = getNumberOfMatches(of: matchOn, in: value)
33+
34+
XCTAssertEqual(actual, expectedNumMatches, "Actual is not as expected")
35+
}
36+
37+
func testEmptyStringMatchesOnNonEmptyMultilineStringUsingTripleDoubleQuotesZeroTimes() {
38+
let matchOn = ""
39+
let value = """
40+
This is a non-empty string
41+
This is the second line
42+
This is the third line
43+
"""
44+
let expectedNumMatches = 0
45+
46+
let actual = getNumberOfMatches(of: matchOn, in: value)
47+
48+
XCTAssertEqual(actual, expectedNumMatches, "Actual is not as expected")
49+
}
50+
51+
func testfatalErrorMatchesOnMultiLineWithTwoOccurencesReturnsTwo() {
52+
let matchOn = "error: fatalError"
53+
let value = """
54+
> swift test 25/10/24 10:44:14
55+
Building for debugging...
56+
/Users/maxd/Documents/personal/repro-swiftpm-6605/Tests/repro-swiftpm-6605Tests/repro_swiftpm_6605Tests.swift:7:19: error: division by zero
57+
let y = 1 / x
58+
^
59+
error: fatalError
60+
61+
error: fatalError
62+
"""
63+
let expectedNumMatches = 2
64+
65+
let actual = getNumberOfMatches(of: matchOn, in: value)
66+
67+
XCTAssertEqual(actual, expectedNumMatches, "Actual is not as expected")
68+
}
69+
70+
func testfatalErrorWithLeadingNewLineMatchesOnMultiLineWithTwoOccurencesReturnsTwo() {
71+
let matchOn = "\nerror: fatalError"
72+
let value = """
73+
> swift test 25/10/24 10:44:14
74+
Building for debugging...
75+
/Users/maxd/Documents/personal/repro-swiftpm-6605/Tests/repro-swiftpm-6605Tests/repro_swiftpm_6605Tests.swift:7:19: error: division by zero
76+
let y = 1 / x
77+
^
78+
error: fatalError
79+
80+
error: fatalError
81+
"""
82+
let expectedNumMatches = 2
83+
84+
let actual = getNumberOfMatches(of: matchOn, in: value)
85+
86+
XCTAssertEqual(actual, expectedNumMatches, "Actual is not as expected")
87+
}
88+
89+
func testfatalErrorWithLeadingAndTrailingNewLineMatchesOnMultiLineWithOneOccurencesReturnsOne() {
90+
let matchOn = "\nerror: fatalError\n"
91+
let value = """
92+
> swift test 25/10/24 10:44:14
93+
Building for debugging...
94+
/Users/maxd/Documents/personal/repro-swiftpm-6605/Tests/repro-swiftpm-6605Tests/repro_swiftpm_6605Tests.swift:7:19: error: division by zero
95+
let y = 1 / x
96+
^
97+
error: fatalError
98+
99+
error: fatalError
100+
"""
101+
let expectedNumMatches = 1
102+
103+
let actual = getNumberOfMatches(of: matchOn, in: value)
104+
105+
XCTAssertEqual(actual, expectedNumMatches, "Actual is not as expected")
106+
}
107+
108+
109+
110+
}

0 commit comments

Comments
 (0)