Skip to content

Update macro template to support Swift Testing tests #8890 #8897

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 78 additions & 22 deletions Sources/Workspace/InitPackage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,6 @@ public final class InitPackage {
installedSwiftPMConfiguration: InstalledSwiftPMConfiguration,
fileSystem: FileSystem
) throws {
if options.packageType == .macro && options.supportedTestingLibraries.contains(.swiftTesting) {
// FIXME: https://github.com/swiftlang/swift-syntax/issues/2400
throw InitError.unsupportedTestingLibraryForPackageType(.swiftTesting, .macro)
}

self.options = options
self.pkgname = name
self.moduleName = name.spm_mangledToC99ExtendedIdentifier()
Expand Down Expand Up @@ -346,7 +341,6 @@ public final class InitPackage {
dependencies: [
"\(pkgname)Macros",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
.product(name: "Testing", package: "swift-testing"),
]
),
"""
Expand Down Expand Up @@ -726,15 +720,22 @@ public final class InitPackage {
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros
import SwiftSyntaxMacrosTestSupport
import SwiftSyntaxMacroExpansion

"""##

if options.supportedTestingLibraries.contains(.swiftTesting) {
content += "import Testing\n"
}
if options.supportedTestingLibraries.contains(.xctest) {
content += "import XCTest\n"
content += ##"""
import Testing
import SwiftSyntaxMacrosGenericTestSupport

"""##
} else if options.supportedTestingLibraries.contains(.xctest) {
content += ##"""
import XCTest
import SwiftSyntaxMacrosTestSupport

"""##
}

content += ##"""
Expand All @@ -743,8 +744,8 @@ public final class InitPackage {
#if canImport(\##(moduleName)Macros)
import \##(moduleName)Macros

let testMacros: [String: Macro.Type] = [
"stringify": StringifyMacro.self,
let testMacros: [String: MacroSpec] = [
"stringify": MacroSpec(type: StringifyMacro.self),
]
#endif

Expand All @@ -755,10 +756,68 @@ public final class InitPackage {
// for it *and* Testing if it is enabled.

if options.supportedTestingLibraries.contains(.swiftTesting) {
// FIXME: https://github.com/swiftlang/swift-syntax/issues/2400
}

if options.supportedTestingLibraries.contains(.xctest) {
content += ##"""
struct \##(moduleName)Tests {

@Test
func macro() {
#if canImport(\##(moduleName)Macros)
assertMacroExpansion(
"""
#stringify(a + b)
""",
expandedSource: """
(a + b, "a + b")
""",
macroSpecs: testMacros
) {
Issue.record(
"\($0.message)",
sourceLocation:
SourceLocation(
fileID: $0.location.fileID,
filePath: $0.location.filePath,
line: $0.location.line,
column: $0.location.column
)
)
}
#else
Issue.record("macros are only supported when running tests for the host platform")
#endif
}

@Test
func macroWithStringLiteral() {
#if canImport(\##(moduleName)Macros)
assertMacroExpansion(
#"""
#stringify("Hello, \(name)")
"""#,
expandedSource: #"""
("Hello, \(name)", #""Hello, \(name)""#)
"""#,
macroSpecs: testMacros
) {
Issue.record(
"\($0.message)",
sourceLocation:
SourceLocation(
fileID: $0.location.fileID,
filePath: $0.location.filePath,
line: $0.location.line,
column: $0.location.column
)
)
}
#else
Issue.record("macros are only supported when running tests for the host platform")
#endif
}
}

"""##
} else if options.supportedTestingLibraries.contains(.xctest) {
content += ##"""
final class \##(moduleName)Tests: XCTestCase {
func testMacro() throws {
Expand All @@ -770,7 +829,7 @@ public final class InitPackage {
expandedSource: """
(a + b, "a + b")
""",
macros: testMacros
macroSpecs: testMacros
)
#else
throw XCTSkip("macros are only supported when running tests for the host platform")
Expand All @@ -786,7 +845,7 @@ public final class InitPackage {
expandedSource: #"""
("Hello, \(name)", #""Hello, \(name)""#)
"""#,
macros: testMacros
macroSpecs: testMacros
)
#else
throw XCTSkip("macros are only supported when running tests for the host platform")
Expand Down Expand Up @@ -887,16 +946,13 @@ public final class InitPackage {

private enum InitError: Swift.Error {
case manifestAlreadyExists
case unsupportedTestingLibraryForPackageType(_ testingLibrary: TestingLibrary, _ packageType: InitPackage.PackageType)
}

extension InitError: CustomStringConvertible {
var description: String {
switch self {
case .manifestAlreadyExists:
return "a manifest file already exists in this directory"
case let .unsupportedTestingLibraryForPackageType(library, packageType):
return "\(library) cannot be used when initializing a \(packageType) package"
}
}
}
Expand Down
88 changes: 88 additions & 0 deletions Tests/WorkspaceTests/InitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,94 @@ final class InitTests: XCTestCase {
}
}

func testInitPackageMacroWithXCTest() throws {
try testWithTemporaryDirectory { tmpPath in
let fs = localFileSystem
let path = tmpPath.appending("Foo")
let name = path.basename
try fs.createDirectory(path)

// Create the package
try InitPackage(
name: name,
packageType: .macro,
supportedTestingLibraries: [.xctest],
destinationPath: path,
fileSystem: localFileSystem
).writePackageStructure()

// Verify basic file system content that we expect in the package
let manifest = path.appending("Package.swift")
XCTAssertFileExists(manifest)
let manifestContents: String = try localFileSystem.readFileContents(manifest)


XCTAssertMatch(manifestContents, .and(.contains(".executable("), .contains("targets: [\"FooClient\"]")))
XCTAssertMatch(manifestContents, .and(.contains(".macro("), .contains("name: \"FooMacros\"")))
XCTAssertMatch(manifestContents, .contains(".executableTarget(name: \"FooClient\", dependencies: [\"Foo\"]),"))

let sourceLibrary = path.appending("Sources", "Foo", "Foo.swift")
XCTAssertFileExists(sourceLibrary)
let sourceClient = path.appending("Sources", "FooClient", "main.swift")
XCTAssertFileExists(sourceClient)
let sourceMacros = path.appending("Sources", "FooMacros", "FooMacro.swift")
XCTAssertFileExists(sourceMacros)

let sourceTests = path.appending("Tests", "FooTests", "FooTests.swift")
XCTAssertFileExists(sourceTests)
let sourceTestsContents: String = try localFileSystem.readFileContents(sourceTests)
XCTAssertMatch(sourceTestsContents, .contains("import XCTest"))
XCTAssertMatch(sourceTestsContents, .contains("import SwiftSyntaxMacrosTestSupport"))
XCTAssertNoMatch(sourceTestsContents, .contains("import Testing"))
XCTAssertNoMatch(sourceTestsContents, .contains("import SwiftSyntaxMacrosGenericTestSupport"))
XCTAssertMatch(sourceTestsContents, .contains("final class FooTests: XCTestCase {"))
}
}

func testInitPackageMacroWithSwiftTesting() throws {
try testWithTemporaryDirectory { tmpPath in
let fs = localFileSystem
let path = tmpPath.appending("Foo")
let name = path.basename
try fs.createDirectory(path)

// Create the package
try InitPackage(
name: name,
packageType: .macro,
supportedTestingLibraries: [.swiftTesting],
destinationPath: path,
fileSystem: localFileSystem
).writePackageStructure()

// Verify basic file system content that we expect in the package
let manifest = path.appending("Package.swift")
XCTAssertFileExists(manifest)
let manifestContents: String = try localFileSystem.readFileContents(manifest)


XCTAssertMatch(manifestContents, .and(.contains(".executable("), .contains("targets: [\"FooClient\"]")))
XCTAssertMatch(manifestContents, .and(.contains(".macro("), .contains("name: \"FooMacros\"")))
XCTAssertMatch(manifestContents, .contains(".executableTarget(name: \"FooClient\", dependencies: [\"Foo\"]),"))

let sourceLibrary = path.appending("Sources", "Foo", "Foo.swift")
XCTAssertFileExists(sourceLibrary)
let sourceClient = path.appending("Sources", "FooClient", "main.swift")
XCTAssertFileExists(sourceClient)
let sourceMacros = path.appending("Sources", "FooMacros", "FooMacro.swift")
XCTAssertFileExists(sourceMacros)

let sourceTests = path.appending("Tests", "FooTests", "FooTests.swift")
XCTAssertFileExists(sourceTests)
let sourceTestsContents: String = try localFileSystem.readFileContents(sourceTests)
XCTAssertMatch(sourceTestsContents, .contains("import Testing"))
XCTAssertMatch(sourceTestsContents, .contains("import SwiftSyntaxMacrosGenericTestSupport"))
XCTAssertNoMatch(sourceTestsContents, .contains("import XCTest"))
XCTAssertNoMatch(sourceTestsContents, .contains("import SwiftSyntaxMacrosTestSupport"))
XCTAssertMatch(sourceTestsContents, .contains("struct FooTests {"))
}
}

// MARK: Special case testing

func testInitPackageNonc99Directory() async throws {
Expand Down