diff --git a/Sources/SWBTaskConstruction/TaskProducers/TaskProducer.swift b/Sources/SWBTaskConstruction/TaskProducers/TaskProducer.swift index d5397dac..cd3c36d0 100644 --- a/Sources/SWBTaskConstruction/TaskProducers/TaskProducer.swift +++ b/Sources/SWBTaskConstruction/TaskProducers/TaskProducer.swift @@ -867,7 +867,7 @@ public class TaskProducerContext: StaleFileRemovalContext, BuildFileResolution } /// Report a note from task construction. - func note(_ message: String, location: Diagnostic.Location = .unknown, component: Component = .default) { + public func note(_ message: String, location: Diagnostic.Location = .unknown, component: Component = .default) { if let configuredTarget { delegate.note(.overrideTarget(configuredTarget), message, location: location, component: component) } else { @@ -876,7 +876,7 @@ public class TaskProducerContext: StaleFileRemovalContext, BuildFileResolution } /// Report a warning from task construction. - func warning(_ message: String, location: Diagnostic.Location = .unknown, component: Component = .default) { + public func warning(_ message: String, location: Diagnostic.Location = .unknown, component: Component = .default) { if let configuredTarget { delegate.warning(.overrideTarget(configuredTarget), message, location: location, component: component) } else { @@ -894,7 +894,7 @@ public class TaskProducerContext: StaleFileRemovalContext, BuildFileResolution } /// Report a remark from task construction. - func remark(_ message: String, location: Diagnostic.Location = .unknown, component: Component = .default) { + public func remark(_ message: String, location: Diagnostic.Location = .unknown, component: Component = .default) { if let configuredTarget { delegate.remark(.overrideTarget(configuredTarget), message, location: location, component: component) } else { diff --git a/Sources/SWBUniversalPlatform/TestEntryPointTaskProducer.swift b/Sources/SWBUniversalPlatform/TestEntryPointTaskProducer.swift index 23ec56f0..4730192c 100644 --- a/Sources/SWBUniversalPlatform/TestEntryPointTaskProducer.swift +++ b/Sources/SWBUniversalPlatform/TestEntryPointTaskProducer.swift @@ -36,12 +36,17 @@ class TestEntryPointTaskProducer: PhasedTaskProducer, TaskProducer { guard settings.productType?.conformsTo(identifier: "com.apple.product-type.bundle.unit-test") == true else { continue } + guard settings.globalScope.evaluate(BuiltinMacros.SWIFT_ENABLE_TESTABILITY) || settings.globalScope.evaluate(BuiltinMacros.OTHER_SWIFT_FLAGS).contains("-enable-testing") else { + context.warning("Skipping XCTest discovery for '\(directDependency.target.name)' because it was not built for testing") + continue + } guard settings.globalScope.evaluate(BuiltinMacros.SWIFT_INDEX_STORE_ENABLE) else { - context.error("Cannot perform test discovery for '\(directDependency.target.name)' because index while building is disabled") + context.warning("Skipping XCTest discovery for '\(directDependency.target.name)' because indexing was disabled") continue } let path = settings.globalScope.evaluate(BuiltinMacros.SWIFT_INDEX_STORE_PATH) guard !path.isEmpty else { + context.warning("Skipping XCTest discovery for '\(directDependency.target.name)' because the index store path could not be determined") continue } indexStoreDirectories.append(path) diff --git a/Tests/SWBBuildSystemTests/BuildOperationTests.swift b/Tests/SWBBuildSystemTests/BuildOperationTests.swift index 820558d1..80628efa 100644 --- a/Tests/SWBBuildSystemTests/BuildOperationTests.swift +++ b/Tests/SWBBuildSystemTests/BuildOperationTests.swift @@ -576,6 +576,133 @@ fileprivate struct BuildOperationTests: CoreBasedTests { } } + @Test(.requireSDKs(.host), .skipHostOS(.macOS), .skipHostOS(.windows, "cannot find testing library")) + func unitTestWithGeneratedEntryPoint_testabilityDisabled() async throws { + try await withTemporaryDirectory(removeTreeOnDeinit: false) { (tmpDir: Path) in + let testProject = try await TestProject( + "TestProject", + sourceRoot: tmpDir, + groupTree: TestGroup( + "SomeFiles", + children: [ + TestFile("library.swift"), + TestFile("test.swift"), + ]), + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "ARCHS": "$(ARCHS_STANDARD)", + "CODE_SIGNING_ALLOWED": "NO", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "$(HOST_PLATFORM)", + "SUPPORTED_PLATFORMS": "$(HOST_PLATFORM)", + "SWIFT_VERSION": swiftVersion, + "INDEX_DATA_STORE_DIR": "\(tmpDir.join("index").str)", + "LINKER_DRIVER": "swiftc", + "ENABLE_TESTABILITY": "NO", + "SWIFT_ENABLE_TESTABILITY": "NO", + ]) + ], + targets: [ + TestStandardTarget( + "UnitTestRunner", + type: .swiftpmTestRunner, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "LD_RUNPATH_SEARCH_PATHS": "$(RPATH_ORIGIN)", + ]), + ], + buildPhases: [ + TestSourcesBuildPhase(), + TestFrameworksBuildPhase([ + "MyTests.so" + ]) + ], + dependencies: ["MyTests"] + ), + TestStandardTarget( + "MyTests", + type: .unitTest, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "LD_RUNPATH_SEARCH_PATHS": "$(RPATH_ORIGIN)", + "LD_DYLIB_INSTALL_NAME": "MyTests.so" + ]) + ], + buildPhases: [ + TestSourcesBuildPhase(["test.swift"]), + TestFrameworksBuildPhase([ + TestBuildFile(.target("library")), + ]) + ], dependencies: [ + "library" + ], + productReferenceName: "MyTests.so" + ), + TestStandardTarget( + "library", + type: .dynamicLibrary, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "LD_RUNPATH_SEARCH_PATHS": "$(RPATH_ORIGIN)", + "LD_DYLIB_INSTALL_NAME": "liblibrary.so", + + // FIXME: Find a way to make these default + "EXECUTABLE_PREFIX": "lib", + "EXECUTABLE_PREFIX[sdk=windows*]": "", + ]) + ], + buildPhases: [ + TestSourcesBuildPhase(["library.swift"]), + ], + ) + ]) + let core = try await getCore() + let tester = try await BuildOperationTester(core, testProject, simulated: false) + try localFS.createDirectory(tmpDir.join("index")) + let projectDir = tester.workspace.projects[0].sourceRoot + + try await tester.fs.writeFileContents(projectDir.join("library.swift")) { stream in + stream <<< "public func foo() -> Int { 42 }\n" + } + + try await tester.fs.writeFileContents(projectDir.join("test.swift")) { stream in + stream <<< """ + import Testing + import XCTest + import library + @Suite struct MySuite { + @Test func myTest() { + #expect(foo() == 42) + } + } + + final class MYXCTests: XCTestCase { + func testFoo() { + XCTAssertTrue(true) + } + } + """ + } + + let destination: RunDestinationInfo = .host + try await tester.checkBuild(runDestination: destination, persistent: true) { results in + results.checkWarning(.prefix("Skipping XCTest discovery for 'MyTests' because it was not built for testing")) + results.checkNoErrors() + + let environment = destination.hostRuntimeEnvironment(core) + + do { + let executionResult = try await Process.getOutput(url: URL(fileURLWithPath: projectDir.join("build").join("Debug\(destination.builtProductsDirSuffix)").join(core.hostOperatingSystem.imageFormat.executableName(basename: "UnitTestRunner")).str), arguments: [], environment: environment) + #expect(String(decoding: executionResult.stdout, as: UTF8.self).contains("Executed 0 tests")) + } + do { + let executionResult = try await Process.getOutput(url: URL(fileURLWithPath: projectDir.join("build").join("Debug\(destination.builtProductsDirSuffix)").join(core.hostOperatingSystem.imageFormat.executableName(basename: "UnitTestRunner")).str), arguments: ["--testing-library", "swift-testing"], environment: environment) + #expect(String(decoding: executionResult.stderr, as: UTF8.self).contains("Test run with 1 test ")) + } + } + } + } + /// Check that environment variables are propagated from the user environment correctly. @Test(.requireSDKs(.host), .skipHostOS(.windows), .requireSystemPackages(apt: "yacc", yum: "byacc")) func userEnvironment() async throws {