Skip to content

Commit 63645fc

Browse files
committed
Drafting migration to Swift 6.1
1 parent bc07181 commit 63645fc

File tree

4 files changed

+140
-14
lines changed

4 files changed

+140
-14
lines changed

GeneratedTestingTarget/main.swift

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#if canImport(Darwin)
2+
@preconcurrency import Darwin
3+
#elseif canImport(Glibc)
4+
@preconcurrency import Glibc // disable concurrency safety checks for stdout/stderr flushing
5+
#elseif canImport(CRT)
6+
@preconcurrency import CRT
7+
#endif
8+
9+
// Must be after the Glibc import, because the first occurency of a (transitive) import determines whether
10+
// something is a @preconcurrency import.
11+
import XCTest
12+
13+
// Type names not exported by the proprietary version of XCTest .
14+
#if os(macOS) && !canImport(SwiftXCTest)
15+
private typealias XCTestCaseClosure = (XCTestCase) throws -> Void
16+
private typealias XCTestCaseEntry
17+
= (testCaseClass: XCTestCase.Type, allTests: [(String, XCTestCaseClosure)])
18+
#endif
19+
20+
/// The type of `T`'s generated static `allTests` property.
21+
private typealias AllTests<T> = [(String, (T) -> () throws -> Void)]
22+
23+
private extension Array {
24+
25+
/// Transforms `self` into an `XCTestCaseEntry` by erasing `T` from the type and representing
26+
/// it as a string, or returns an empty result if `T` is-not-a `XCTestCase`.
27+
func eraseTestTypes<T>() -> XCTestCaseEntry where Self == AllTests<T> {
28+
guard let t = T.self as? XCTestCase.Type else {
29+
// Skip any methods scraped from non-XCTestCase types.
30+
return (testCaseClass: XCTestCase.self, allTests: [])
31+
}
32+
33+
func xcTestCaseClosure(_ f: @escaping (T) -> () throws -> Void) -> XCTestCaseClosure {
34+
{ (x: XCTestCase) throws -> Void in try f(x as! T)() }
35+
}
36+
37+
let allTests = self.map { name_f in (name_f.0, xcTestCaseClosure(name_f.1)) }
38+
return (testCaseClass: t, allTests: allTests)
39+
}
40+
41+
}
42+
43+
private extension TestCase {
44+
45+
static var allTests: AllTests<TestCase> {
46+
return [
47+
("test1", test1),
48+
("testExtensionMethodsCanBeTests", testExtensionMethodsCanBeTests),
49+
]
50+
}
51+
52+
}
53+
54+
private extension StructsAreNotTestCases.NestedStructClassesCanBeTestCases {
55+
56+
static var allTests: AllTests<StructsAreNotTestCases.NestedStructClassesCanBeTestCases> {
57+
return [
58+
("testNestedClassMethodsCanBeTests", testNestedClassMethodsCanBeTests),
59+
]
60+
}
61+
62+
}
63+
64+
private extension StructsAreNotTestCases {
65+
66+
static var allTests: AllTests<StructsAreNotTestCases> {
67+
return [
68+
("testStructExtensionMethodsAreNotTests", testStructExtensionMethodsAreNotTests),
69+
]
70+
}
71+
72+
}
73+
74+
private extension EnumsAreNotTestCases.NestedEnumClassesCanBeTestCases {
75+
76+
static var allTests: AllTests<EnumsAreNotTestCases.NestedEnumClassesCanBeTestCases> {
77+
return [
78+
("testNestedClassMethodsCanBeTests", testNestedClassMethodsCanBeTests),
79+
]
80+
}
81+
82+
}
83+
84+
private extension NotAllClassesAreTestCases {
85+
86+
static var allTests: AllTests<NotAllClassesAreTestCases> {
87+
return [
88+
("testNonXCTestCaseMethodsAreNotTests", testNonXCTestCaseMethodsAreNotTests),
89+
]
90+
}
91+
92+
}
93+
94+
/// Feeds `allTestCases` to `XCTMain` if the latter is available.
95+
func runAllTestCases() {
96+
/// The full complement of test cases and their individual tests
97+
let allTestCases: [XCTestCaseEntry] = [
98+
TestCase.allTests.eraseTestTypes(),
99+
StructsAreNotTestCases.NestedStructClassesCanBeTestCases.allTests.eraseTestTypes(),
100+
StructsAreNotTestCases.allTests.eraseTestTypes(),
101+
EnumsAreNotTestCases.NestedEnumClassesCanBeTestCases.allTests.eraseTestTypes(),
102+
NotAllClassesAreTestCases.allTests.eraseTestTypes()
103+
]
104+
#if !os(Windows)
105+
// ignore SIGPIPE which is sent when writing to closed file descriptors.
106+
_ = signal(SIGPIPE, SIG_IGN)
107+
#endif
108+
109+
atexit({
110+
fflush(stdout)
111+
fflush(stderr)
112+
})
113+
#if !os(macOS) || canImport(SwiftXCTest)
114+
XCTMain(allTestCases)
115+
#endif
116+
}
117+
runAllTestCases()

Package.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version: 5.9
1+
// swift-tools-version: 6.1
22

33
import PackageDescription
44

@@ -48,6 +48,13 @@ let package = Package(
4848
dependencies: ["DummyTestee", "XCTestImporter"],
4949
path: "Tests",
5050
exclude: ["XCTestImporter.swift", "CMakeLists.txt", "Dummy.swift"],
51-
sources: ["Tests.swift"])
52-
]
51+
sources: ["Tests.swift"]),
52+
// Use this for testing the generated file.
53+
// Generate the main.swift by running this: `swift run -Xswiftc -DBUILDING_WITH_SWIFT_PACKAGE_MANAGER GenerateSwiftXCTestMain -o GeneratedTestingTarget/main.swift /home/ambrus/GenerateSwiftXCTestMain/Tests/Tests.swift`
54+
// Then copy Tests.swift into this directory and try to build the target. `swift run GeneratedTestingTarget`
55+
.executableTarget(
56+
name: "GeneratedTestingTarget",
57+
dependencies: ["Tests"],
58+
path: "GeneratedTestingTarget")
59+
],
5360
)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ the scope of test method bodies could produce unpredictable results.
2525

2626
There are two methods:
2727

28-
1. **Swift Package Manager**: `swift test`
28+
1. **Swift Package Manager**: `swift test -Xswiftc -DBUILDING_WITH_SWIFT_PACKAGE_MANAGER`
2929
2. **CMake**: `
3030

3131
```sh

Sources/GenerateSwiftXCTestMain.swift

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ struct GenerateSwiftXCTestMain: ParsableCommand {
4545
///
4646
/// - See also: `discoveredTests()` for more information.
4747
func discoveredTests(in f: URL) throws -> TestCatalog {
48-
let tree = try parseAndEmitDiagnostics(source: String(contentsOf: f))
48+
let tree = try parseAndEmitDiagnostics(source: String(contentsOf: f, encoding: .utf8))
4949
let scraper = TestScraper()
5050
scraper.walk(tree)
5151
return scraper.result
@@ -73,15 +73,18 @@ struct GenerateSwiftXCTestMain: ParsableCommand {
7373

7474
let output =
7575
"""
76-
import XCTest
7776
#if canImport(Darwin)
78-
import Darwin
77+
@preconcurrency import Darwin
7978
#elseif canImport(Glibc)
80-
import Glibc
79+
@preconcurrency import Glibc // disable concurrency safety checks for stdout/stderr flushing
8180
#elseif canImport(CRT)
82-
import CRT
81+
@preconcurrency import CRT
8382
#endif
8483
84+
// Must be after the Glibc import, because the first occurency of a (transitive) import determines whether
85+
// something is a @preconcurrency import.
86+
import XCTest
87+
8588
// Type names not exported by the proprietary version of XCTest .
8689
#if os(macOS) && !canImport(SwiftXCTest)
8790
private typealias XCTestCaseClosure = (XCTestCase) throws -> Void
@@ -114,13 +117,12 @@ struct GenerateSwiftXCTestMain: ParsableCommand {
114117
115118
\(tests.map(allTestsExtension).joined(separator: "\n\n"))
116119
117-
/// The full complement of test cases and their individual tests
118-
private let allTestCases: [XCTestCaseEntry] = [
119-
\(tests.keys.map {"\($0).allTests.eraseTestTypes()"}.joined(separator: ",\n "))
120-
]
121-
122120
/// Feeds `allTestCases` to `XCTMain` if the latter is available.
123121
func runAllTestCases() {
122+
/// The full complement of test cases and their individual tests
123+
let allTestCases: [XCTestCaseEntry] = [
124+
\(tests.keys.map {"\($0).allTests.eraseTestTypes()"}.joined(separator: ",\n "))
125+
]
124126
#if !os(Windows)
125127
// ignore SIGPIPE which is sent when writing to closed file descriptors.
126128
_ = signal(SIGPIPE, SIG_IGN)

0 commit comments

Comments
 (0)