Skip to content

Commit dc1af9a

Browse files
committed
swift-package-migrate: Add tests for feature resolution errors
This change also extracts feature resolution into a method and removes a superfluous colon from the 'unsupported feature' error message. (cherry picked from commit 8486081)
1 parent a4ff351 commit dc1af9a

File tree

2 files changed

+83
-25
lines changed

2 files changed

+83
-25
lines changed

Sources/Commands/PackageCommands/Migrate.swift

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -64,31 +64,8 @@ extension SwiftPackageCommand {
6464
var options: MigrateOptions
6565

6666
public func run(_ swiftCommandState: SwiftCommandState) async throws {
67-
let toolchain = try swiftCommandState.productsBuildParameters.toolchain
68-
69-
let supportedFeatures = try Dictionary(
70-
uniqueKeysWithValues: toolchain.swiftCompilerSupportedFeatures
71-
.map { ($0.name, $0) }
72-
)
73-
74-
// First, let's validate that all of the features are supported
75-
// by the compiler and are migratable.
76-
77-
var features: [SwiftCompilerFeature] = []
78-
for name in self.options.features {
79-
guard let feature = supportedFeatures[name] else {
80-
let migratableFeatures = supportedFeatures.map(\.value).filter(\.migratable).map(\.name)
81-
throw ValidationError(
82-
"Unsupported feature: \(name). Available features: \(migratableFeatures.joined(separator: ", "))"
83-
)
84-
}
85-
86-
guard feature.migratable else {
87-
throw ValidationError("Feature '\(name)' is not migratable")
88-
}
89-
90-
features.append(feature)
91-
}
67+
// First, validate and resolve the requested feature names.
68+
let features = try self.resolveRequestedFeatures(swiftCommandState)
9269

9370
let targets = self.options.targets
9471

@@ -188,6 +165,44 @@ extension SwiftPackageCommand {
188165
}
189166
}
190167

168+
/// Resolves the requested feature names.
169+
private func resolveRequestedFeatures(
170+
_ swiftCommandState: SwiftCommandState
171+
) throws -> [SwiftCompilerFeature] {
172+
let toolchain = try swiftCommandState.productsBuildParameters.toolchain
173+
174+
// Query the compiler for supported features.
175+
let supportedFeatures = try toolchain.swiftCompilerSupportedFeatures
176+
177+
var resolvedFeatures: [SwiftCompilerFeature] = []
178+
179+
// Resolve the requested feature names, validating that they are
180+
// supported by the compiler and migratable.
181+
for name in self.options.features {
182+
let feature = supportedFeatures.first { $0.name == name }
183+
184+
guard let feature else {
185+
let migratableCommaSeparatedFeatures = supportedFeatures
186+
.filter(\.migratable)
187+
.map(\.name)
188+
.sorted()
189+
.joined(separator: ", ")
190+
191+
throw ValidationError(
192+
"Unsupported feature '\(name)'. Available features: \(migratableCommaSeparatedFeatures)"
193+
)
194+
}
195+
196+
guard feature.migratable else {
197+
throw ValidationError("Feature '\(name)' is not migratable")
198+
}
199+
200+
resolvedFeatures.append(feature)
201+
}
202+
203+
return resolvedFeatures
204+
}
205+
191206
private func createBuildSystem(
192207
_ swiftCommandState: SwiftCommandState,
193208
targets: OrderedSet<String>,

Tests/CommandsTests/PackageCommandTests.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2115,6 +2115,49 @@ class PackageCommandTestCase: CommandsBuildProviderTestCase {
21152115
XCTAssertNoMatch(stdout, .contains("--package-path"))
21162116
}
21172117

2118+
func testMigrateCommandNoFeatures() async throws {
2119+
try await XCTAssertThrowsCommandExecutionError(
2120+
await self.execute(["migrate"])
2121+
) { error in
2122+
XCTAssertMatch(
2123+
error.stderr,
2124+
.contains("error: Missing expected argument '--to-feature <to-feature>'")
2125+
)
2126+
}
2127+
}
2128+
2129+
func testMigrateCommandUnknownFeature() async throws {
2130+
try XCTSkipIf(
2131+
!UserToolchain.default.supportesSupportedFeatures,
2132+
"skipping because test environment compiler doesn't support `-print-supported-features`"
2133+
)
2134+
2135+
try await XCTAssertThrowsCommandExecutionError(
2136+
await self.execute(["migrate", "--to-feature", "X"])
2137+
) { error in
2138+
XCTAssertMatch(
2139+
error.stderr,
2140+
.contains("error: Unsupported feature 'X'. Available features:")
2141+
)
2142+
}
2143+
}
2144+
2145+
func testMigrateCommandNonMigratableFeature() async throws {
2146+
try XCTSkipIf(
2147+
!UserToolchain.default.supportesSupportedFeatures,
2148+
"skipping because test environment compiler doesn't support `-print-supported-features`"
2149+
)
2150+
2151+
try await XCTAssertThrowsCommandExecutionError(
2152+
await self.execute(["migrate", "--to-feature", "StrictConcurrency"])
2153+
) { error in
2154+
XCTAssertMatch(
2155+
error.stderr,
2156+
.contains("error: Feature 'StrictConcurrency' is not migratable")
2157+
)
2158+
}
2159+
}
2160+
21182161
func testMigrateCommand() async throws {
21192162
try XCTSkipIf(
21202163
!UserToolchain.default.supportesSupportedFeatures,

0 commit comments

Comments
 (0)