diff --git a/Documentation/Porting.md b/Documentation/Porting.md index ce179d53..0cc6bf70 100644 --- a/Documentation/Porting.md +++ b/Documentation/Porting.md @@ -145,8 +145,10 @@ to load that information: + let resourceName: Str255 = switch kind { + case .testContent: + "__swift5_tests" ++#if !SWT_NO_LEGACY_TEST_DISCOVERY + case .typeMetadata: + "__swift5_types" ++#endif + } + + let oldRefNum = CurResFile() @@ -219,15 +221,19 @@ diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals +#elif defined(macintosh) +extern "C" const char testContentSectionBegin __asm__("..."); +extern "C" const char testContentSectionEnd __asm__("..."); ++#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) +extern "C" const char typeMetadataSectionBegin __asm__("..."); +extern "C" const char typeMetadataSectionEnd __asm__("..."); ++#endif #else #warning Platform-specific implementation missing: Runtime test discovery unavailable (static) static const char testContentSectionBegin = 0; static const char& testContentSectionEnd = testContentSectionBegin; + #if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) static const char typeMetadataSectionBegin = 0; static const char& typeMetadataSectionEnd = testContentSectionBegin; #endif + #endif ``` These symbols must have unique addresses corresponding to the first byte of the diff --git a/Package.swift b/Package.swift index 4a22b98e..57c6ea42 100644 --- a/Package.swift +++ b/Package.swift @@ -156,6 +156,8 @@ extension Array where Element == PackageDescription.SwiftSetting { .enableExperimentalFeature("AccessLevelOnImport"), .enableUpcomingFeature("InternalImportsByDefault"), + .enableExperimentalFeature("SymbolLinkageMarkers"), + .define("SWT_TARGET_OS_APPLE", .when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS])), .define("SWT_NO_EXIT_TESTS", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])), diff --git a/Sources/Testing/Discovery+Platform.swift b/Sources/Testing/Discovery+Platform.swift index 7fcb94fe..208f5181 100644 --- a/Sources/Testing/Discovery+Platform.swift +++ b/Sources/Testing/Discovery+Platform.swift @@ -24,8 +24,10 @@ struct SectionBounds: Sendable { /// The test content metadata section. case testContent +#if !SWT_NO_LEGACY_TEST_DISCOVERY /// The type metadata section. case typeMetadata +#endif } /// All section bounds of the given kind found in the current process. @@ -72,36 +74,22 @@ private let _startCollectingSectionBounds: Void = { return } - // If this image contains the Swift section we need, acquire the lock and + // If this image contains the Swift section(s) we need, acquire the lock and // store the section's bounds. - let testContentSectionBounds: SectionBounds? = { + func findSectionBounds(forSectionNamed segmentName: String, _ sectionName: String, ofKind kind: SectionBounds.Kind) { var size = CUnsignedLong(0) - if let start = getsectiondata(mh, "__DATA_CONST", "__swift5_tests", &size), size > 0 { + if let start = getsectiondata(mh, segmentName, sectionName, &size), size > 0 { let buffer = UnsafeRawBufferPointer(start: start, count: Int(clamping: size)) - return SectionBounds(imageAddress: mh, buffer: buffer) - } - return nil - }() - - let typeMetadataSectionBounds: SectionBounds? = { - var size = CUnsignedLong(0) - if let start = getsectiondata(mh, "__TEXT", "__swift5_types", &size), size > 0 { - let buffer = UnsafeRawBufferPointer(start: start, count: Int(clamping: size)) - return SectionBounds(imageAddress: mh, buffer: buffer) - } - return nil - }() - - if testContentSectionBounds != nil || typeMetadataSectionBounds != nil { - _sectionBounds.withLock { sectionBounds in - if let testContentSectionBounds { - sectionBounds[.testContent]!.append(testContentSectionBounds) - } - if let typeMetadataSectionBounds { - sectionBounds[.typeMetadata]!.append(typeMetadataSectionBounds) + let sb = SectionBounds(imageAddress: mh, buffer: buffer) + _sectionBounds.withLock { sectionBounds in + sectionBounds[kind]!.append(sb) } } } + findSectionBounds(forSectionNamed: "__DATA_CONST", "__swift5_tests", ofKind: .testContent) +#if !SWT_NO_LEGACY_TEST_DISCOVERY + findSectionBounds(forSectionNamed: "__TEXT", "__swift5_types", ofKind: .typeMetadata) +#endif } #if _runtime(_ObjC) @@ -160,8 +148,10 @@ private func _sectionBounds(_ kind: SectionBounds.Kind) -> [SectionBounds] { let range = switch context.pointee.kind { case .testContent: sections.swift5_tests +#if !SWT_NO_LEGACY_TEST_DISCOVERY case .typeMetadata: sections.swift5_type_metadata +#endif } let start = UnsafeRawPointer(bitPattern: range.start) let size = Int(clamping: range.length) @@ -250,8 +240,10 @@ private func _sectionBounds(_ kind: SectionBounds.Kind) -> [SectionBounds] { let sectionName = switch kind { case .testContent: ".sw5test" +#if !SWT_NO_LEGACY_TEST_DISCOVERY case .typeMetadata: ".sw5tymd" +#endif } return HMODULE.all.compactMap { _findSection(named: sectionName, in: $0) } } @@ -283,8 +275,10 @@ private func _sectionBounds(_ kind: SectionBounds.Kind) -> CollectionOfOne some Sequence .withMemoryRebound(to: Any.Type.self) { Array($0) } } } +#endif diff --git a/Sources/Testing/Test+Discovery.swift b/Sources/Testing/Test+Discovery.swift index 751a7de8..b6e84a85 100644 --- a/Sources/Testing/Test+Discovery.swift +++ b/Sources/Testing/Test+Discovery.swift @@ -55,6 +55,7 @@ extension Test: TestContent { } } +#if !SWT_NO_LEGACY_TEST_DISCOVERY if discoveryMode != .newOnly && generators.isEmpty { generators += types(withNamesContaining: testContainerTypeNameMagic).lazy .compactMap { $0 as? any __TestContainer.Type } @@ -62,6 +63,7 @@ extension Test: TestContent { { @Sendable in await type.__tests } } } +#endif // *Now* we call all the generators and return their results. // Reduce into a set rather than an array to deduplicate tests that were diff --git a/Sources/TestingMacros/CMakeLists.txt b/Sources/TestingMacros/CMakeLists.txt index acf09f33..4a42d966 100644 --- a/Sources/TestingMacros/CMakeLists.txt +++ b/Sources/TestingMacros/CMakeLists.txt @@ -87,6 +87,7 @@ target_sources(TestingMacros PRIVATE Support/Additions/DeclGroupSyntaxAdditions.swift Support/Additions/EditorPlaceholderExprSyntaxAdditions.swift Support/Additions/FunctionDeclSyntaxAdditions.swift + Support/Additions/IntegerLiteralExprSyntaxAdditions.swift Support/Additions/MacroExpansionContextAdditions.swift Support/Additions/TokenSyntaxAdditions.swift Support/Additions/TriviaPieceAdditions.swift @@ -103,6 +104,7 @@ target_sources(TestingMacros PRIVATE Support/DiagnosticMessage+Diagnosing.swift Support/SourceCodeCapturing.swift Support/SourceLocationGeneration.swift + Support/TestContentGeneration.swift TagMacro.swift TestDeclarationMacro.swift TestingMacrosMain.swift) diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index 3d2013c6..daacae48 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -431,11 +431,58 @@ extension ExitTestConditionMacro { // Create a local type that can be discovered at runtime and which contains // the exit test body. - let enumName = context.makeUniqueName("__🟠$exit_test_body__") + let accessorName = context.makeUniqueName("") + let outValueArgumentName = context.makeUniqueName("outValue") + let hintArgumentName = context.makeUniqueName("hint") + let sourceLocationLocalName = context.makeUniqueName("sourceLocation") + decls.append( + """ + #if hasFeature(SymbolLinkageMarkers) + func \(accessorName)(_ \(outValueArgumentName): UnsafeMutableRawPointer, _ \(hintArgumentName): UnsafeRawPointer?) -> Bool { + let \(sourceLocationLocalName) = \(createSourceLocationExpr(of: macro, context: context)) + if let \(hintArgumentName) = \(hintArgumentName)?.load(as: Testing.SourceLocation.self), + \(hintArgumentName) != \(sourceLocationLocalName) { + return false + } + \(outValueArgumentName).initializeMemory( + as: Testing.__ExitTest.self, + to: .init( + __expectedExitCondition: \(arguments[expectedExitConditionIndex].expression.trimmed), + sourceLocation: \(sourceLocationLocalName), + body: \(bodyThunkName) + ) + ) + return true + } + #endif + """ + ) + + let enumName = context.makeUniqueName("") + let sectionContent = makeTestContentRecordDecl( + named: .identifier("__sectionContent"), + in: TypeSyntax(IdentifierTypeSyntax(name: enumName)), + ofKind: .exitTest, + accessingWith: accessorName + ) + decls.append( + """ + #if hasFeature(SymbolLinkageMarkers) + @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") + enum \(enumName) { + \(sectionContent) + } + #endif + """ + ) + +#if !SWT_NO_LEGACY_TEST_DISCOVERY + // Emit a legacy type declaration if SymbolLinkageMarkers is off. + let legacyEnumName = context.makeUniqueName("__🟠$exit_test_body__") decls.append( """ @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") - enum \(enumName): Testing.__ExitTestContainer, Sendable { + enum \(legacyEnumName): Testing.__ExitTestContainer, Sendable { static var __sourceLocation: Testing.SourceLocation { \(createSourceLocationExpr(of: macro, context: context)) } @@ -448,6 +495,7 @@ extension ExitTestConditionMacro { } """ ) +#endif arguments[trailingClosureIndex].expression = ExprSyntax( ClosureExprSyntax { diff --git a/Sources/TestingMacros/SuiteDeclarationMacro.swift b/Sources/TestingMacros/SuiteDeclarationMacro.swift index c9fb6bb0..9aa3bfaf 100644 --- a/Sources/TestingMacros/SuiteDeclarationMacro.swift +++ b/Sources/TestingMacros/SuiteDeclarationMacro.swift @@ -127,6 +127,39 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { // Parse the @Suite attribute. let attributeInfo = AttributeInfo(byParsing: suiteAttribute, on: declaration, in: context) + let accessorName = context.makeUniqueName("") + result.append( + """ + #if hasFeature(SymbolLinkageMarkers) + @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") + private static let \(accessorName): @convention(c) (UnsafeMutableRawPointer, UnsafeRawPointer?) -> Bool = { + $0.initializeMemory(as: (@Sendable () async -> Testing.Test).self) { @Sendable () async in + .__type( + \(declaration.type.trimmed).self, + \(raw: attributeInfo.functionArgumentList(in: context)) + ) + } + _ = $1 // Ignored. + return true + } + #endif + """ + ) + + let sectionContentName = context.makeUniqueName("") + result.append( + makeTestContentRecordDecl( + named: sectionContentName, + in: declaration.type, + ofKind: .testDeclaration, + accessingWith: accessorName, + context: 1 << 0 /* suite decl */ + ) + ) + +#if !SWT_NO_LEGACY_TEST_DISCOVERY + // Emit a legacy type declaration if SymbolLinkageMarkers is off. + // // The emitted type must be public or the compiler can optimize it away // (since it is not actually used anywhere that the compiler can see.) // @@ -142,17 +175,24 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { """ @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") enum \(enumName): Testing.__TestContainer { - static var __tests: [Testing.Test] { - get async {[ + private static var __test: Testing.Test { + get async { .__type( \(declaration.type.trimmed).self, \(raw: attributeInfo.functionArgumentList(in: context)) ) - ]} + } + } + + static var __tests: [Testing.Test] { + get async { + [await __test] + } } } """ ) +#endif return result } diff --git a/Sources/TestingMacros/Support/Additions/IntegerLiteralExprSyntaxAdditions.swift b/Sources/TestingMacros/Support/Additions/IntegerLiteralExprSyntaxAdditions.swift new file mode 100644 index 00000000..e2310b44 --- /dev/null +++ b/Sources/TestingMacros/Support/Additions/IntegerLiteralExprSyntaxAdditions.swift @@ -0,0 +1,18 @@ +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for Swift project authors +// + +import SwiftSyntax + +extension IntegerLiteralExprSyntax { + init(_ value: some BinaryInteger, radix: IntegerLiteralExprSyntax.Radix = .decimal) { + let stringValue = "\(radix.literalPrefix)\(String(value, radix: radix.size))" + self.init(literal: .integerLiteral(stringValue)) + } +} diff --git a/Sources/TestingMacros/Support/Additions/TokenSyntaxAdditions.swift b/Sources/TestingMacros/Support/Additions/TokenSyntaxAdditions.swift index 12e6abb2..39f21b73 100644 --- a/Sources/TestingMacros/Support/Additions/TokenSyntaxAdditions.swift +++ b/Sources/TestingMacros/Support/Additions/TokenSyntaxAdditions.swift @@ -47,3 +47,14 @@ extension TokenSyntax { return nil } } + + /// The `static` keyword, if `typeName` is not `nil`. + /// + /// - Parameters: + /// - typeName: The name of the type containing the macro being expanded. + /// + /// - Returns: A token representing the `static` keyword, or one representing + /// nothing if `typeName` is `nil`. + func staticKeyword(for typeName: TypeSyntax?) -> TokenSyntax { + (typeName != nil) ? .keyword(.static) : .unknown("") + } diff --git a/Sources/TestingMacros/Support/TestContentGeneration.swift b/Sources/TestingMacros/Support/TestContentGeneration.swift new file mode 100644 index 00000000..1d845cdb --- /dev/null +++ b/Sources/TestingMacros/Support/TestContentGeneration.swift @@ -0,0 +1,91 @@ +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for Swift project authors +// + +import SwiftSyntax +import SwiftSyntaxMacros + +/// An enumeration representing the different kinds of test content known to the +/// testing library. +/// +/// When adding cases to this enumeration, be sure to also update the +/// corresponding enumeration in TestContent.md. +enum TestContentKind: UInt32 { + /// A test or suite declaration. + case testDeclaration = 0x74657374 + + /// An exit test. + case exitTest = 0x65786974 + + /// This kind value as a comment (`/* 'abcd' */`) if it looks like it might be + /// a [FourCC](https://en.wikipedia.org/wiki/FourCC) value, or `nil` if not. + var commentRepresentation: Trivia? { + return withUnsafeBytes(of: rawValue.bigEndian) { bytes in + if bytes.allSatisfy(Unicode.ASCII.isASCII) { + let characters = String(decoding: bytes, as: Unicode.ASCII.self) + let allAlphanumeric = characters.allSatisfy { $0.isLetter || $0.isWholeNumber } + if allAlphanumeric { + return .blockComment("/* '\(characters)' */") + } + } + return nil + } + } +} + +/// Make a test content record that can be discovered at runtime by the testing +/// library. +/// +/// - Parameters: +/// - name: The name of the record declaration to use in Swift source. The +/// value of this argument should be unique in the context in which the +/// declaration will be emitted. +/// - typeName: The name of the type enclosing the resulting declaration, or +/// `nil` if it will not be emitted into a type's scope. +/// - kind: The kind of test content record being emitted. +/// - accessorName: The Swift name of an `@convention(c)` function to emit +/// into the resulting record. +/// - context: A value to emit as the `context` field of the test content +/// record. +/// +/// - Returns: A variable declaration that, when emitted into Swift source, will +/// cause the linker to emit data in a location that is discoverable at +/// runtime. +func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? = nil, ofKind kind: TestContentKind, accessingWith accessorName: TokenSyntax, context: UInt32 = 0) -> DeclSyntax { + let kindExpr = IntegerLiteralExprSyntax(kind.rawValue, radix: .hex) + let kindComment = kind.commentRepresentation.map { .space + $0 } ?? Trivia() + let contextExpr = if context == 0 { + IntegerLiteralExprSyntax(0) + } else { + IntegerLiteralExprSyntax(context, radix: .binary) + } + + return """ + #if hasFeature(SymbolLinkageMarkers) + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS) + @_section("__DATA_CONST,__swift5_tests") + #elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI) + @_section("swift5_tests") + #elseif os(Windows) + @_section(".sw5test$B") + #else + @__testing(warning: "Platform-specific implementation missing: test content section name unavailable") + #endif + @_used + @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") + private \(staticKeyword(for: typeName)) let \(name): Testing.__TestContentRecord = ( + \(kindExpr),\(kindComment) + 0, + \(accessorName), + \(contextExpr), + 0 + ) + #endif + """ +} diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift index 1a3f2c44..8b02a9fd 100644 --- a/Sources/TestingMacros/TestDeclarationMacro.swift +++ b/Sources/TestingMacros/TestDeclarationMacro.swift @@ -172,17 +172,6 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { return FunctionParameterClauseSyntax(parameters: parameterList) } - /// The `static` keyword, if `typeName` is not `nil`. - /// - /// - Parameters: - /// - typeName: The name of the type containing the macro being expanded. - /// - /// - Returns: A token representing the `static` keyword, or one representing - /// nothing if `typeName` is `nil`. - private static func _staticKeyword(for typeName: TypeSyntax?) -> TokenSyntax { - (typeName != nil) ? .keyword(.static) : .unknown("") - } - /// Create a thunk function with a normalized signature that calls a /// developer-supplied test function. /// @@ -340,7 +329,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { let thunkName = context.makeUniqueName(thunking: functionDecl) let thunkDecl: DeclSyntax = """ @available(*, deprecated, message: "This function is an implementation detail of the testing library. Do not use it directly.") - @Sendable private \(_staticKeyword(for: typeName)) func \(thunkName)\(thunkParamsExpr) async throws -> Void { + @Sendable private \(staticKeyword(for: typeName)) func \(thunkName)\(thunkParamsExpr) async throws -> Void { \(thunkBody) } """ @@ -405,16 +394,14 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { // Create the expression that returns the Test instance for the function. var testsBody: CodeBlockItemListSyntax = """ - return [ - .__function( - named: \(literal: functionDecl.completeName.trimmedDescription), - in: \(typeNameExpr), - xcTestCompatibleSelector: \(selectorExpr ?? "nil"), - \(raw: attributeInfo.functionArgumentList(in: context)), - parameters: \(raw: functionDecl.testFunctionParameterList), - testFunction: \(thunkDecl.name) - ) - ] + return .__function( + named: \(literal: functionDecl.completeName.trimmedDescription), + in: \(typeNameExpr), + xcTestCompatibleSelector: \(selectorExpr ?? "nil"), + \(raw: attributeInfo.functionArgumentList(in: context)), + parameters: \(raw: functionDecl.testFunctionParameterList), + testFunction: \(thunkDecl.name) + ) """ // If this function has arguments, then it can only be referenced (let alone @@ -430,16 +417,14 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { result.append( """ @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") - private \(_staticKeyword(for: typeName)) nonisolated func \(unavailableTestName)() async -> [Testing.Test] { - [ - .__function( - named: \(literal: functionDecl.completeName.trimmedDescription), - in: \(typeNameExpr), - xcTestCompatibleSelector: \(selectorExpr ?? "nil"), - \(raw: attributeInfo.functionArgumentList(in: context)), - testFunction: {} - ) - ] + private \(staticKeyword(for: typeName)) nonisolated func \(unavailableTestName)() async -> Testing.Test { + .__function( + named: \(literal: functionDecl.completeName.trimmedDescription), + in: \(typeNameExpr), + xcTestCompatibleSelector: \(selectorExpr ?? "nil"), + \(raw: attributeInfo.functionArgumentList(in: context)), + testFunction: {} + ) } """ ) @@ -454,6 +439,41 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { ) } + let accessorName = context.makeUniqueName(thunking: functionDecl) + result.append( + """ + #if hasFeature(SymbolLinkageMarkers) + @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.") + private \(staticKeyword(for: typeName)) let \(accessorName): @convention(c) (UnsafeMutableRawPointer, UnsafeRawPointer?) -> Bool = { + $0.initializeMemory(as: (@Sendable () async -> Testing.Test).self) { @Sendable () async in + \(raw: testsBody) + } + _ = $1 // Ignored. + return true + } + #endif + """ + ) + + var flags = UInt32(0) + if attributeInfo.hasFunctionArguments { + flags |= 1 << 1 /* is parameterized */ + } + + let sectionContentName = context.makeUniqueName(thunking: functionDecl) + result.append( + makeTestContentRecordDecl( + named: sectionContentName, + in: typeName, + ofKind: .testDeclaration, + accessingWith: accessorName, + context: flags + ) + ) + +#if !SWT_NO_LEGACY_TEST_DISCOVERY + // Emit a legacy type declaration if SymbolLinkageMarkers is off. + // // The emitted type must be public or the compiler can optimize it away // (since it is not actually used anywhere that the compiler can see.) // @@ -469,14 +489,21 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { """ @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") enum \(enumName): Testing.__TestContainer { - static var __tests: [Testing.Test] { + private static var __test: Testing.Test { get async { \(raw: testsBody) } } + + static var __tests: [Testing.Test] { + get async { + [await __test] + } + } } """ ) +#endif return result } diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals/Discovery.cpp index f66eaf87..bd308483 100644 --- a/Sources/_TestingInternals/Discovery.cpp +++ b/Sources/_TestingInternals/Discovery.cpp @@ -10,11 +10,13 @@ #include "Discovery.h" +#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) #include #include #include #include #include +#endif #if defined(SWT_NO_DYNAMIC_LINKING) #pragma mark - Statically-linked section bounds @@ -22,30 +24,39 @@ #if defined(__APPLE__) extern "C" const char testContentSectionBegin __asm("section$start$__DATA_CONST$__swift5_tests"); extern "C" const char testContentSectionEnd __asm("section$end$__DATA_CONST$__swift5_tests"); +#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) extern "C" const char typeMetadataSectionBegin __asm__("section$start$__TEXT$__swift5_types"); extern "C" const char typeMetadataSectionEnd __asm__("section$end$__TEXT$__swift5_types"); +#endif #elif defined(__wasi__) extern "C" const char testContentSectionBegin __asm__("__start_swift5_tests"); extern "C" const char testContentSectionEnd __asm__("__stop_swift5_tests"); +#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) extern "C" const char typeMetadataSectionBegin __asm__("__start_swift5_type_metadata"); extern "C" const char typeMetadataSectionEnd __asm__("__stop_swift5_type_metadata"); +#endif #else #warning Platform-specific implementation missing: Runtime test discovery unavailable (static) static const char testContentSectionBegin = 0; static const char& testContentSectionEnd = testContentSectionBegin; +#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) static const char typeMetadataSectionBegin = 0; static const char& typeMetadataSectionEnd = typeMetadataSectionBegin; #endif +#endif const void *_Nonnull const SWTTestContentSectionBounds[2] = { &testContentSectionBegin, &testContentSectionEnd }; +#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) const void *_Nonnull const SWTTypeMetadataSectionBounds[2] = { &typeMetadataSectionBegin, &typeMetadataSectionEnd }; #endif +#endif +#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) #pragma mark - Swift ABI #if defined(__PTRAUTH_INTRINSICS__) @@ -232,3 +243,4 @@ void **swt_copyTypesWithNamesContaining(const void *sectionBegin, size_t section *outCount = resultCount; return result; } +#endif diff --git a/Sources/_TestingInternals/include/Discovery.h b/Sources/_TestingInternals/include/Discovery.h index b0cfe070..e78719f2 100644 --- a/Sources/_TestingInternals/include/Discovery.h +++ b/Sources/_TestingInternals/include/Discovery.h @@ -82,6 +82,7 @@ SWT_EXTERN const void *_Nonnull const SWTTestContentSectionBounds[2]; /// when Swift files import the `_TestingInternals` C++ module. SWT_EXTERN const void *_Nonnull const SWTTypeMetadataSectionBounds[2]; +#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) #pragma mark - Legacy test discovery /// Copy all types known to Swift found in the given type metadata section with @@ -102,6 +103,7 @@ SWT_EXTERN void *_Nonnull *_Nullable swt_copyTypesWithNamesContaining( const char *nameSubstring, size_t *outCount ) SWT_SWIFT_NAME(swt_copyTypes(in:_:withNamesContaining:count:)); +#endif SWT_ASSUME_NONNULL_END diff --git a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift index a77acfea..1d7dbbdc 100644 --- a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift +++ b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift @@ -402,7 +402,9 @@ struct TestDeclarationMacroTests { func differentFunctionTypes(input: String, expectedTypeName: String?, otherCode: String?) throws { let (output, _) = try parse(input) - #expect(output.contains("__TestContainer")) + withKnownIssue("https://github.com/swiftlang/swift-syntax/issues/2923") { + #expect(output.contains("@_section")) + } if let expectedTypeName { #expect(output.contains(expectedTypeName)) } diff --git a/Tests/TestingTests/MiscellaneousTests.swift b/Tests/TestingTests/MiscellaneousTests.swift index 49df6cc6..60e781cf 100644 --- a/Tests/TestingTests/MiscellaneousTests.swift +++ b/Tests/TestingTests/MiscellaneousTests.swift @@ -654,4 +654,21 @@ struct MiscellaneousTests { } } #endif + +#if !SWT_NO_LEGACY_TEST_DISCOVERY + @Test("Legacy test discovery finds the same number of tests") func discoveredTestCount() async { + let oldFlag = Environment.variable(named: "SWT_USE_LEGACY_TEST_DISCOVERY") + defer { + Environment.setVariable(oldFlag, named: "SWT_USE_LEGACY_TEST_DISCOVERY") + } + + Environment.setVariable("1", named: "SWT_USE_LEGACY_TEST_DISCOVERY") + let testsWithOldCode = await Array(Test.all).count + + Environment.setVariable("0", named: "SWT_USE_LEGACY_TEST_DISCOVERY") + let testsWithNewCode = await Array(Test.all).count + + #expect(testsWithOldCode == testsWithNewCode) + } +#endif } diff --git a/cmake/modules/shared/CompilerSettings.cmake b/cmake/modules/shared/CompilerSettings.cmake index 49e2579f..13fa101e 100644 --- a/cmake/modules/shared/CompilerSettings.cmake +++ b/cmake/modules/shared/CompilerSettings.cmake @@ -16,6 +16,8 @@ add_compile_options( add_compile_options( "SHELL:$<$:-Xfrontend -enable-upcoming-feature -Xfrontend ExistentialAny>" "SHELL:$<$:-Xfrontend -enable-upcoming-feature -Xfrontend InternalImportsByDefault>") +add_compile_options( + "SHELL:$<$:-Xfrontend -enable-experimental-feature -Xfrontend SymbolLinkageMarkers>") # Platform-specific definitions. if(APPLE)