diff --git a/CompanionLib/SwiftServer/MethodHandlers/XCTestRunMethodHandler.swift b/CompanionLib/SwiftServer/MethodHandlers/XCTestRunMethodHandler.swift index b1e3959c2..338d380cf 100644 --- a/CompanionLib/SwiftServer/MethodHandlers/XCTestRunMethodHandler.swift +++ b/CompanionLib/SwiftServer/MethodHandlers/XCTestRunMethodHandler.swift @@ -47,6 +47,22 @@ struct XCTestRunMethodHandler { } func transform(value request: Idb_XctestRunRequest) -> XCTestRunRequest? { + guard var transformed = transformMode(value: request) else { + return nil + } + if request.hasKillAllRunningApplications { + transformed.killAllRunningApplications = request.killAllRunningApplications + } + if request.hasDisableXctestDebugLogging { + transformed.disableXCTestDebugLogging = request.disableXctestDebugLogging + } + if request.hasActivateSuspended { + transformed.activateSuspended = request.activateSuspended + } + return transformed + } + + private func transformMode(value request: Idb_XctestRunRequest) -> XCTestRunRequest? { let testsToRun = request.testsToRun.isEmpty ? nil : Set(request.testsToRun) switch request.mode.mode { case .logic: diff --git a/CompanionLib/XCTest/XCTestDescriptor.swift b/CompanionLib/XCTest/XCTestDescriptor.swift index 9aa1f3576..fc211ef4c 100644 --- a/CompanionLib/XCTest/XCTestDescriptor.swift +++ b/CompanionLib/XCTest/XCTestDescriptor.swift @@ -87,6 +87,9 @@ final class XCTestBootstrapDescriptor: XCTestDescriptor, CustomStringConvertible if request.isLogicTest { return } + guard request.killAllRunningApplications else { + return + } try await XCTestBootstrapDescriptor.killAllRunningApplications(target) } @@ -133,7 +136,8 @@ final class XCTestBootstrapDescriptor: XCTestDescriptor, CustomStringConvertible arguments: request.arguments, logger: logger, processLogDirectory: logDirectoryPath, - waitForDebugger: request.waitForDebugger + waitForDebugger: request.waitForDebugger, + activateSuspended: request.activateSuspended ) let testLaunchConfig = TestLaunchConfiguration( testBundle: testBundle, @@ -151,7 +155,8 @@ final class XCTestBootstrapDescriptor: XCTestDescriptor, CustomStringConvertible coverageDirectoryPath: coverageConfig?.coverageDirectory, enableContinuousCoverageCollection: coverageConfig?.shouldEnableContinuousCoverageCollection ?? false, logDirectoryPath: logDirectoryPath, - reportResultBundle: request.collectResultBundle + reportResultBundle: request.collectResultBundle, + disableXCTestDebugLogging: request.disableXCTestDebugLogging ) return IDBAppHostedTestConfiguration(testLaunchConfiguration: testLaunchConfig, coverageConfiguration: coverageConfig) } @@ -239,12 +244,12 @@ final class XCodebuildTestRunDescriptor: XCTestDescriptor, CustomStringConvertib // MARK: - Private Helper -private func buildAppLaunchConfig(bundleID: String, environment: [String: String], arguments: [String], logger: ControlCoreLogger, processLogDirectory: String?, waitForDebugger: Bool) async throws -> ApplicationLaunchConfiguration { +private func buildAppLaunchConfig(bundleID: String, environment: [String: String], arguments: [String], logger: ControlCoreLogger, processLogDirectory: String?, waitForDebugger: Bool, activateSuspended: Bool? = nil) async throws -> ApplicationLaunchConfiguration { let stdOutConsumer = FBLoggingDataConsumer(logger: logger) let stdErrConsumer = FBLoggingDataConsumer(logger: logger) guard let processLogDirectory else { - return applicationLaunchConfiguration(bundleID: bundleID, environment: environment, arguments: arguments, waitForDebugger: waitForDebugger, stdOut: stdOutConsumer, stdErr: stdErrConsumer) + return applicationLaunchConfiguration(bundleID: bundleID, environment: environment, arguments: arguments, waitForDebugger: waitForDebugger, stdOut: stdOutConsumer, stdErr: stdErrConsumer, activateSuspended: activateSuspended) } // Both mirrors are created before either is awaited, so the two file writers are opened concurrently. @@ -254,7 +259,7 @@ private func buildAppLaunchConfig(bundleID: String, environment: [String: String let stdOut = try await mirroredConsumer(stdOutFuture) let stdErr = try await mirroredConsumer(stdErrFuture) - return applicationLaunchConfiguration(bundleID: bundleID, environment: environment, arguments: arguments, waitForDebugger: waitForDebugger, stdOut: stdOut, stdErr: stdErr) + return applicationLaunchConfiguration(bundleID: bundleID, environment: environment, arguments: arguments, waitForDebugger: waitForDebugger, stdOut: stdOut, stdErr: stdErr, activateSuspended: activateSuspended) } private func mirroredConsumer(_ future: FBFuture) async throws -> DataConsumer { @@ -265,7 +270,7 @@ private func mirroredConsumer(_ future: FBFuture) async throws -> Dat return consumer } -private func applicationLaunchConfiguration(bundleID: String, environment: [String: String], arguments: [String], waitForDebugger: Bool, stdOut: DataConsumer, stdErr: DataConsumer) -> ApplicationLaunchConfiguration { +private func applicationLaunchConfiguration(bundleID: String, environment: [String: String], arguments: [String], waitForDebugger: Bool, stdOut: DataConsumer, stdErr: DataConsumer, activateSuspended: Bool? = nil) -> ApplicationLaunchConfiguration { let io = FBProcessIO( stdIn: nil, stdOut: FBProcessOutput(for: stdOut), @@ -278,6 +283,7 @@ private func applicationLaunchConfiguration(bundleID: String, environment: [Stri environment: environment, waitForDebugger: waitForDebugger, io: io, - launchMode: .relaunchIfRunning + launchMode: .relaunchIfRunning, + activateSuspended: activateSuspended ) } diff --git a/CompanionLib/XCTest/XCTestRunRequest.swift b/CompanionLib/XCTest/XCTestRunRequest.swift index df973a40d..0fb2db81d 100644 --- a/CompanionLib/XCTest/XCTestRunRequest.swift +++ b/CompanionLib/XCTest/XCTestRunRequest.swift @@ -69,6 +69,13 @@ public struct XCTestRunRequest { public let collectLogs: Bool public let waitForDebugger: Bool public let collectResultBundle: Bool + /// Whether to kill every running application before the test host launches. Defaults to the + /// historical behaviour; a driver that owns the device's app state turns it off. + public var killAllRunningApplications: Bool = true + /// Whether to drop the test runner's `_XCT_logDebugMessage` traffic instead of logging it. + public var disableXCTestDebugLogging: Bool = false + /// When set, passed through to the test host's `activate_suspended` launch option. + public var activateSuspended: Bool? public var testBundleID: String? { guard case let .identifier(identifier) = bundle else { return nil } diff --git a/FBControlCore/Configuration/ApplicationLaunchConfiguration.swift b/FBControlCore/Configuration/ApplicationLaunchConfiguration.swift index 245f0a9d8..90e7437cf 100644 --- a/FBControlCore/Configuration/ApplicationLaunchConfiguration.swift +++ b/FBControlCore/Configuration/ApplicationLaunchConfiguration.swift @@ -20,12 +20,15 @@ public final class ApplicationLaunchConfiguration: ProcessLaunchConfiguration { @objc public let bundleName: String? @objc public let waitForDebugger: Bool public let launchMode: ApplicationLaunchMode + /// When set, passed as the `activate_suspended` key to `launchApplicationAsyncWithID`. + public let activateSuspended: Bool? - public init(bundleID: String, bundleName: String?, arguments: [String], environment: [String: String], waitForDebugger: Bool, io: FBProcessIO, launchMode: ApplicationLaunchMode) { + public init(bundleID: String, bundleName: String?, arguments: [String], environment: [String: String], waitForDebugger: Bool, io: FBProcessIO, launchMode: ApplicationLaunchMode, activateSuspended: Bool? = nil) { self.bundleID = bundleID self.bundleName = bundleName self.waitForDebugger = waitForDebugger self.launchMode = launchMode + self.activateSuspended = activateSuspended super.init(arguments: arguments, environment: environment, io: io) } diff --git a/FBControlCore/Configuration/TestLaunchConfiguration.swift b/FBControlCore/Configuration/TestLaunchConfiguration.swift index 6dacd9772..5eb252967 100644 --- a/FBControlCore/Configuration/TestLaunchConfiguration.swift +++ b/FBControlCore/Configuration/TestLaunchConfiguration.swift @@ -25,8 +25,9 @@ public struct TestLaunchConfiguration { public let shouldEnableContinuousCoverageCollection: Bool public let logDirectoryPath: String? public let reportResultBundle: Bool + public let disableXCTestDebugLogging: Bool - public init(testBundle: BundleDescriptor, applicationLaunchConfiguration: ApplicationLaunchConfiguration, testHostBundle: BundleDescriptor?, timeout: TimeInterval, initializeUITesting: Bool, useXcodebuild: Bool, testsToRun: Set?, testsToSkip: Set?, targetApplicationBundle: BundleDescriptor?, xcTestRunProperties: [String: Any]?, resultBundlePath: String?, reportActivities: Bool, coverageDirectoryPath: String?, enableContinuousCoverageCollection: Bool, logDirectoryPath: String?, reportResultBundle: Bool) { + public init(testBundle: BundleDescriptor, applicationLaunchConfiguration: ApplicationLaunchConfiguration, testHostBundle: BundleDescriptor?, timeout: TimeInterval, initializeUITesting: Bool, useXcodebuild: Bool, testsToRun: Set?, testsToSkip: Set?, targetApplicationBundle: BundleDescriptor?, xcTestRunProperties: [String: Any]?, resultBundlePath: String?, reportActivities: Bool, coverageDirectoryPath: String?, enableContinuousCoverageCollection: Bool, logDirectoryPath: String?, reportResultBundle: Bool, disableXCTestDebugLogging: Bool = false) { self.testBundle = testBundle self.applicationLaunchConfiguration = applicationLaunchConfiguration self.testHostBundle = testHostBundle @@ -43,6 +44,7 @@ public struct TestLaunchConfiguration { self.shouldEnableContinuousCoverageCollection = enableContinuousCoverageCollection self.logDirectoryPath = logDirectoryPath self.reportResultBundle = reportResultBundle + self.disableXCTestDebugLogging = disableXCTestDebugLogging } } diff --git a/FBSimulatorControl/Commands/SimulatorApplicationCommands.swift b/FBSimulatorControl/Commands/SimulatorApplicationCommands.swift index 7bb230bec..624af87f4 100644 --- a/FBSimulatorControl/Commands/SimulatorApplicationCommands.swift +++ b/FBSimulatorControl/Commands/SimulatorApplicationCommands.swift @@ -432,6 +432,9 @@ public struct SimulatorApplicationCommands: ApplicationCommands { if let stdErrPath { options["stderr"] = stdErrPath } + if let activateSuspended = configuration.activateSuspended { + options["activate_suspended"] = activateSuspended + } return options } diff --git a/XCTestBootstrap/Strategies/ManagedTestRunStrategy.swift b/XCTestBootstrap/Strategies/ManagedTestRunStrategy.swift index 91e2a3f2c..10f41550e 100644 --- a/XCTestBootstrap/Strategies/ManagedTestRunStrategy.swift +++ b/XCTestBootstrap/Strategies/ManagedTestRunStrategy.swift @@ -42,7 +42,8 @@ public final class ManagedTestRunStrategy { timeout: configuration.timeout, testHostLaunchConfiguration: testHostLaunchConfiguration, testedApplicationAdditionalEnvironment: runnerConfiguration.testedApplicationAdditionalEnvironment, - testConfiguration: runnerConfiguration.testConfiguration + testConfiguration: runnerConfiguration.testConfiguration, + disableXCTestDebugLogging: configuration.disableXCTestDebugLogging ) try await TestManagerAPIMediator.connectAndRunUntilCompletion( diff --git a/XCTestBootstrap/TestManager/FBTestManagerAPIMediatorIDEInterface.m b/XCTestBootstrap/TestManager/FBTestManagerAPIMediatorIDEInterface.m index 4b4b3fefb..b59d174e2 100644 --- a/XCTestBootstrap/TestManager/FBTestManagerAPIMediatorIDEInterface.m +++ b/XCTestBootstrap/TestManager/FBTestManagerAPIMediatorIDEInterface.m @@ -197,6 +197,9 @@ - (id)_XCT_testCaseDidFailForTestClass:(NSString *)testClass method:(NSString *) - (id)_XCT_logDebugMessage:(NSString *)debugMessage { + if (self.context.disableXCTestDebugLogging) { + return nil; + } [self.logger log:[debugMessage stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]]; return nil; } diff --git a/XCTestBootstrap/TestManager/TestManagerContext.swift b/XCTestBootstrap/TestManager/TestManagerContext.swift index c5fd0acdf..2ddbf4827 100644 --- a/XCTestBootstrap/TestManager/TestManagerContext.swift +++ b/XCTestBootstrap/TestManager/TestManagerContext.swift @@ -15,19 +15,22 @@ import Foundation @objc public let testHostLaunchConfiguration: ApplicationLaunchConfiguration @objc public let testedApplicationAdditionalEnvironment: [String: String] @objc public let testConfiguration: FBTestConfiguration + @objc public let disableXCTestDebugLogging: Bool @objc public init( sessionIdentifier: UUID, timeout: TimeInterval, testHostLaunchConfiguration: ApplicationLaunchConfiguration, testedApplicationAdditionalEnvironment: [String: String], - testConfiguration: FBTestConfiguration + testConfiguration: FBTestConfiguration, + disableXCTestDebugLogging: Bool ) { self.sessionIdentifier = sessionIdentifier self.timeout = timeout self.testHostLaunchConfiguration = testHostLaunchConfiguration self.testedApplicationAdditionalEnvironment = testedApplicationAdditionalEnvironment self.testConfiguration = testConfiguration + self.disableXCTestDebugLogging = disableXCTestDebugLogging super.init() } diff --git a/proto/idb.proto b/proto/idb.proto index 90cb4f989..55c015f8f 100644 --- a/proto/idb.proto +++ b/proto/idb.proto @@ -1159,6 +1159,9 @@ message XctestRunRequest { bool wait_for_debugger = 12; CodeCoverage code_coverage = 13; bool collect_result_bundle = 14; + optional bool activate_suspended = 15; + optional bool kill_all_running_applications = 16; + optional bool disable_xctest_debug_logging = 17; } message XctestRunResponse {