diff --git a/Sources/Workspace/InitPackage.swift b/Sources/Workspace/InitPackage.swift index f66d72c5985..0039c0d613a 100644 --- a/Sources/Workspace/InitPackage.swift +++ b/Sources/Workspace/InitPackage.swift @@ -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() @@ -346,7 +341,6 @@ public final class InitPackage { dependencies: [ "\(pkgname)Macros", .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"), - .product(name: "Testing", package: "swift-testing"), ] ), """ @@ -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 += ##""" @@ -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 @@ -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 { @@ -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") @@ -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") @@ -887,7 +946,6 @@ public final class InitPackage { private enum InitError: Swift.Error { case manifestAlreadyExists - case unsupportedTestingLibraryForPackageType(_ testingLibrary: TestingLibrary, _ packageType: InitPackage.PackageType) } extension InitError: CustomStringConvertible { @@ -895,8 +953,6 @@ extension InitError: CustomStringConvertible { 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" } } } diff --git a/Tests/WorkspaceTests/InitTests.swift b/Tests/WorkspaceTests/InitTests.swift index 2e4c83a2271..92ea8cbb6cd 100644 --- a/Tests/WorkspaceTests/InitTests.swift +++ b/Tests/WorkspaceTests/InitTests.swift @@ -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 {