Merge pull request #7 from Unity-Technologies/graphics/ugk/update-to-latest

Update to latest idb to enable compatibility with more recent devices
This commit is contained in:
Martin Sternevald 2024-10-15 20:27:17 +02:00 коммит произвёл GitHub
Родитель cc4d8add56 91e79096e6
Коммит aeca9660bb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
998 изменённых файлов: 21137 добавлений и 11674 удалений

Просмотреть файл

@ -11,7 +11,7 @@ version: 2
jobs:
deploy-website:
docker:
- image: circleci/node:14.11.0
- image: cimg/node:16.14.0
steps:
- checkout

20
.github/workflows/build_and_deploy.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,20 @@
name: facebook/idb/build_and_deploy
on:
push:
branches:
- main
jobs:
deploy-website:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.1.0
- uses: actions/setup-node@v4
with:
node-version: 16.14.0
- name: Deploying to GitHub Pages
run: |
git config --global user.email "docusaurus-bot@users.noreply.github.com"
git config --global user.name "Website Deployment Script"
echo "machine github.com login docusaurus-bot password ${{ secrets.GITHUB_TOKEN }}" > ~/.netrc
echo "Deploying website..."
cd website && yarn install && GIT_USER=docusaurus-bot USE_SSH=false yarn deploy

Просмотреть файл

@ -0,0 +1,205 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import FBControlCore
import Foundation
import IDBCompanionUtilities
enum FBFutureError: Error {
/// This indicates an error in objc code where result callback was called but no error or result provided. In case of this, debug `FBFuture` implementation.
case continuationFullfilledWithoutValues
/// This indicates an error in `BridgeFuture.values` implementation. In case of this, debug `BridgeFuture.values` implementation.
case taskGroupReceivedNilResultInternalError
}
/// Swift compiler does not allow usage of generic parameters of objc classes in extension
/// so we need to create a bridge for convenience.
public enum BridgeFuture {
/// Use this to receive results from multiple futures. The results are **ordered in the same order as passed futures**, so you can safely access them from
/// array by indexes.
/// - Note: We should *not* use @discardableResult, results should be dropped explicitly by the callee.
public static func values<T: AnyObject>(_ futures: FBFuture<T>...) async throws -> [T] {
let futuresArr: [FBFuture<T>] = futures
return try await values(futuresArr)
}
/// Use this to receive results from multiple futures. The results are **ordered in the same order as passed futures**, so you can safely access them from
/// array by indexes.
/// - Note: We should *not* use @discardableResult, results should be dropped explicitly by the callee.
public static func values<T: AnyObject>(_ futures: [FBFuture<T>]) async throws -> [T] {
return try await withThrowingTaskGroup(of: (Int, T).self, returning: [T].self) { group in
var results = [T?].init(repeating: nil, count: futures.count)
for (index, future) in futures.enumerated() {
group.addTask {
return try await (index, BridgeFuture.value(future))
}
}
for try await (index, value) in group {
results[index] = value
}
return try results.map { value -> T in
guard let shouldDefinitelyExist = value else {
assertionFailure("This should never happen. We should fullfill all values at that moment")
throw FBFutureError.taskGroupReceivedNilResultInternalError
}
return shouldDefinitelyExist
}
}
}
/// Awaitable value that waits for publishing from the wrapped future
/// - Note: We should *not* use @discardableResult, results should be dropped explicitly by the callee.
public static func value<T: AnyObject>(_ future: FBFuture<T>) async throws -> T {
try await withTaskCancellationHandler {
try await withCheckedThrowingContinuation { continuation in
future.onQueue(BridgeQueues.futureSerialFullfillmentQueue, notifyOfCompletion: { resultFuture in
if let error = resultFuture.error {
continuation.resume(throwing: error)
} else if let value = resultFuture.result {
// swiftlint:disable force_cast
continuation.resume(returning: value as! T)
} else {
continuation.resume(throwing: FBFutureError.continuationFullfilledWithoutValues)
}
})
}
} onCancel: {
future.cancel()
}
}
/// Awaitable value that waits for publishing from the wrapped future.
/// This is convenient bridgeable overload for dealing with objc `NSArray`.
/// - Warning: This operation not safe (as most of objc bridge). That means you should be sure that type bridging will succeed.
/// Consider this method as
///
/// ```
/// // ------- command_executor.m
/// - (FBFuture<NSArray<NSNumer> *> *)doTheThing;
///
/// // ------- swiftfile.swift
/// let futureFromObjc: FBFuture<NSArray> = command_executor.doTheThing() // Note: NSNumber is lost
/// let withoutBridge = BridgeFuture.value(futureFromObjc) // withoutBridge: NSArray
/// let withBridge: [NSNumer] = BridgeFuture.value(futureFromObjc) // withBridge: [NSNumber]
///
/// // But this starts to shine more when you have to pass results to methods/return results, e.g.
/// func operation() -> [Int] {
/// return BridgeFuture.value(futureFromObjc)
/// }
///
/// // Or pass value to some oter method
/// func someMethod(accepts: [NSNumber]) { ... }
///
/// self.someMethod(accepts: BridgeFuture.value(futureFromObjc)
/// ```
public static func value<T>(_ future: FBFuture<NSArray>) async throws -> [T] {
let objcValue = try await value(future)
// swiftlint:disable force_cast
return objcValue as! [T]
}
/// Awaitable value that waits for publishing from the wrapped future.
/// This is convenient bridgeable overload for dealing with objc `NSDictionary`.
/// - Warning: This operation not safe (as most of objc bridge). That means you should be sure that type bridging will succeed.
/// Consider this method as
///
/// ```
/// // ------- command_executor.m
/// - (FBFuture<NSDictionary<FBInstalledApplication *, id> *> *)doTheThing;
///
/// // ------- swiftfile.swift
/// let futureFromObjc: FBFuture<NSDictionary> = command_executor.doTheThing() // Note: types is lost
/// let withoutBridge = BridgeFuture.value(futureFromObjc) // withoutBridge: NSDictionary
/// let withBridge: [FBInstalledApplication: Any] = BridgeFuture.value(futureFromObjc) // withBridge: [FBInstalledApplication: Any]
///
/// // But this starts to shine more when you have to pass results to methods/return results, e.g.
/// func operation() -> [FBInstalledApplication: Any] {
/// return BridgeFuture.value(futureFromObjc)
/// }
///
/// // Or pass value to some oter method
/// func someMethod(accepts: [FBInstalledApplication: Any]) { ... }
///
/// self.someMethod(accepts: BridgeFuture.value(futureFromObjc)
/// ```
public static func value<T: Hashable, U>(_ future: FBFuture<NSDictionary>) async throws -> [T: U] {
let objcValue = try await value(future)
// swiftlint:disable force_cast
return objcValue as! [T: U]
}
/// NSNull is Void equivalent in objc reference world. So is is safe to ignore the result.
public static func await(_ future: FBFuture<NSNull>) async throws {
_ = try await Self.value(future)
}
/// This overload exists because of `FBMutableFuture` does not convert its exact generic type automatically but it can be automatically converted to `FBFuture<AnyObject>`
/// without any problems. This decision may be revisited in future.
public static func await(_ future: FBFuture<AnyObject>) async throws {
_ = try await Self.value(future)
}
/// Interop between swift and objc generics are quite bad, so we have to write wrappers like this.
/// By default swift bridge compiler could not convert generic type of `FBMutableFuture`. But this force cast is 100% valid and works in runtime
/// so we just use this little helper.
public static func convertToFuture<T: AnyObject>(_ mutableFuture: FBMutableFuture<T>) -> FBFuture<T> {
let future: FBFuture<AnyObject> = mutableFuture
// swiftlint:disable force_cast
return future as! FBFuture<T>
}
/// Split FBFutureContext to two pieces: result and later cleanup closure
/// - Parameter futureContext: source future context
/// - Returns: Tuple of extracted result and cleanup closure that **should** be called later to perform all required cleanups
public static func value<T: AnyObject>(_ futureContext: FBFutureContext<T>) async throws -> T {
try FBTeardownContext.current.addCleanup {
let cleanupFuture = futureContext.onQueue(BridgeQueues.futureSerialFullfillmentQueue) { (result: Any, teardown: FBMutableFuture<NSNull>) -> NSNull in
teardown.resolve(withResult: NSNull())
return NSNull()
}
try await BridgeFuture.await(cleanupFuture)
}
return try await value(futureContext.future)
}
/// Awaitable value that waits for publishing from the wrapped futureContext.
/// This is convenient bridgeable overload for dealing with objc `NSArray`.
/// - Warning: This operation not safe (as most of objc bridge). That means you should be sure that type bridging will succeed.
/// Consider this method as
///
/// ```
/// // ------- command_executor.m
/// - (FBFuture<NSArray<NSNumer> *> *)doTheThing;
///
/// // ------- swiftfile.swift
/// let futureFromObjc: FBFuture<NSArray> = command_executor.doTheThing() // Note: NSNumber is lost
/// let withoutBridge = BridgeFuture.value(futureFromObjc) // withoutBridge: NSArray
/// let withBridge: [NSNumer] = BridgeFuture.value(futureFromObjc) // withBridge: [NSNumber]
///
/// // But this starts to shine more when you have to pass results to methods/return results, e.g.
/// func operation() -> [Int] {
/// return BridgeFuture.value(futureFromObjc)
/// }
///
/// // Or pass value to some oter method
/// func someMethod(accepts: [NSNumber]) { ... }
///
/// self.someMethod(accepts: BridgeFuture.value(futureFromObjc)
/// ```
public static func values<T: AnyObject, U>(_ futureContext: FBFutureContext<T>) async throws -> [U] {
let objcValue = try await value(futureContext)
// swiftlint:disable force_cast
return objcValue as! [U]
}
}

Просмотреть файл

@ -0,0 +1,27 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import Foundation
/// Queues collection to bring objc and async-swift worlds together
public enum BridgeQueues {
/// Plain serial queue that is primarily used to convert *all* FBFuture calls to swift awaitable values
public static let futureSerialFullfillmentQueue = DispatchQueue(label: "com.facebook.fbfuture.fullfilment")
/// Some of *commandExecutor* operations requires DispatchQueue to send response.
/// The only purpose of everything handled inside this queue is to passthrough call to swift async world via calling swift `Task` api
/// ```
/// commandExecutor.doSomething(onQueue: BridgeQueues.miscEventReaderQueue) { jobResult in
/// Task {
/// try? await responseStream.send(jobResult)
/// }
/// }
/// ```
///
public static let miscEventReaderQueue = DispatchQueue(label: "com.facebook.miscellaneous.reader", qos: .userInitiated, attributes: .concurrent)
}

Просмотреть файл

@ -0,0 +1,24 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <CompanionLib/FBIDBCommandExecutor.h>
#import <CompanionLib/FBXCTestDescriptor.h>
#import <CompanionLib/FBIDBLogger.h>
#import <CompanionLib/FBCodeCoverageRequest.h>
#import <CompanionLib/FBIDBLogger.h>
#import <CompanionLib/FBDsymInstallLinkToBundle.h>
#import <CompanionLib/FBXCTestRunRequest.h>
#import <CompanionLib/FBDataDownloadInput.h>
#import <CompanionLib/FBIDBLogger.h>
#import <CompanionLib/FBIDBStorageManager.h>
#import <CompanionLib/FBIDBTestOperation.h>
#import <CompanionLib/FBiOSTargetProvider.h>
#import <CompanionLib/FBTestApplicationsPair.h>
#import <CompanionLib/FBXCTestDescriptor.h>
#import <CompanionLib/FBXCTestReporterConfiguration.h>
#import <CompanionLib/FBXCTestRunFileReader.h>
#import <CompanionLib/FBIDBError.h>

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -9,11 +9,12 @@
#import <FBControlCore/FBControlCore.h>
#import "FBXCTestDescriptor.h"
NS_ASSUME_NONNULL_BEGIN
@protocol FBXCTestDescriptor;
@protocol FBControlCoreLogger;
@class FBBundleStorageManager;
@class FBDsymInstallLinkToBundle;
@class FBIDBLogger;
@class FBIDBPortsConfiguration;
@class FBIDBStorageManager;
@ -21,9 +22,15 @@ NS_ASSUME_NONNULL_BEGIN
@class FBInstalledArtifact;
@class FBSimulatorHIDEvent;
@class FBTemporaryDirectory;
@class FBXCTestRunRequest;
@protocol FBXCTestReporter;
extern FBFileContainerKind const FBFileContainerKindXctest;
extern FBFileContainerKind const FBFileContainerKindDylib;
extern FBFileContainerKind const FBFileContainerKindDsym;
extern FBFileContainerKind const FBFileContainerKindFramework;
@interface FBIDBCommandExecutor : NSObject
#pragma mark Initializers
@ -34,11 +41,11 @@ NS_ASSUME_NONNULL_BEGIN
@param target the target to run against.
@param storageManager storage for all bundles
@param temporaryDirectory the temporary directory to use.
@param ports the ports to use.
@param debugserverPort will launch debug server at that port.
@param logger a logger to log to.
@return a new FBIDBCommandExecutor instance
*/
+ (instancetype)commandExecutorForTarget:(id<FBiOSTarget>)target storageManager:(FBIDBStorageManager *)storageManager temporaryDirectory:(FBTemporaryDirectory *)temporaryDirectory ports:(FBIDBPortsConfiguration *)ports logger:(FBIDBLogger *)logger;
+ (instancetype)commandExecutorForTarget:(id<FBiOSTarget>)target storageManager:(FBIDBStorageManager *)storageManager temporaryDirectory:(FBTemporaryDirectory *)temporaryDirectory debugserverPort:(in_port_t)debugserverPort logger:(FBIDBLogger *)logger;
#pragma mark Properties
@ -72,9 +79,10 @@ NS_ASSUME_NONNULL_BEGIN
@param filePath the path to a file on disk with the file.
@param makeDebuggable whether the app should be installed in a debuggable state or not.
@param overrideModificationTime if YES the archive contests' `mtime` will be ignored. Current timestamp will be used as mtime of extracted files/directories.
@return A future that resolves with the App Bundle Id
*/
- (FBFuture<FBInstalledArtifact *> *)install_app_file_path:(NSString *)filePath make_debuggable:(BOOL)makeDebuggable;
- (FBFuture<FBInstalledArtifact *> *)install_app_file_path:(NSString *)filePath make_debuggable:(BOOL)makeDebuggable override_modification_time:(BOOL)overrideModificationTime;
/**
Install an App via a Data stream.
@ -82,25 +90,28 @@ NS_ASSUME_NONNULL_BEGIN
@param input the input to pipe.
@param compression the compression type to use
@param makeDebuggable whether the app should be installed in a debuggable state or not.
@param overrideModificationTime if YES the archive contests' `mtime` will be ignored. Current timestamp will be used as mtime of extracted files/directories.
@return A future that resolves with the App Bundle Id
*/
- (FBFuture<FBInstalledArtifact *> *)install_app_stream:(FBProcessInput *)input compression:(FBCompressionFormat)compression make_debuggable:(BOOL)makeDebuggable;
- (FBFuture<FBInstalledArtifact *> *)install_app_stream:(FBProcessInput *)input compression:(FBCompressionFormat)compression make_debuggable:(BOOL)makeDebuggable override_modification_time:(BOOL)overrideModificationTime;
/**
Installs an xctest bundle by file path.
@param filePath the local file path of the xctest bundle
@param skipSigningBundles pass true to skip signing xctest bundles.
@return a Future that resolves with the xctest identifier.
*/
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_file_path:(NSString *)filePath;
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_file_path:(NSString *)filePath skipSigningBundles:(BOOL)skipSigningBundles;
/**
Installs an xctest bundle by a stream of tar data
@param input a tar stream of the xctest data.
@param skipSigningBundles pass true to skip signing xctest bundles.
@return a Future that resolves with the xctest identifier.
*/
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_stream:(FBProcessInput *)input;
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_stream:(FBProcessInput *)input skipSigningBundles:(BOOL)skipSigningBundles;
/**
Installs a dylib from a file path.
@ -139,19 +150,20 @@ NS_ASSUME_NONNULL_BEGIN
Installs a dSYM from a file path.
@param filePath the input to pipe.
@param bundleID if specified installed dsym will be linked into the app bundle container.
@param linkTo if specified installed dsym will be linked into bundle container.
@return A future that resolves with the dSYM Name
*/
- (FBFuture<FBInstalledArtifact *> *)install_dsym_file_path:(NSString *)filePath linkToApp:(nullable NSString *)bundleID;
- (FBFuture<FBInstalledArtifact *> *)install_dsym_file_path:(NSString *)filePath linkTo:(nullable FBDsymInstallLinkToBundle *)linkTo;
/**
Installs dSYM(s) from a zip stream.
@param input the input to pipe.
@param bundleID if specified installed dsym will be linked into the app bundle container.
@param compression the compression type to use
@param linkTo if specified installed dsym will be linked into bundle container.
@return A future that resolves with the directory containing the dSYM(s)
*/
- (FBFuture<FBInstalledArtifact *> *)install_dsym_stream:(FBProcessInput *)input linkToApp:(nullable NSString *)bundleID;
- (FBFuture<FBInstalledArtifact *> *)install_dsym_stream:(FBProcessInput *)input compression:(FBCompressionFormat)compression linkTo:(nullable FBDsymInstallLinkToBundle *)linkTo;
/**
Takes a Screenshot
@ -168,7 +180,7 @@ NS_ASSUME_NONNULL_BEGIN
@param nestedFormat YES if the legacy format should be used, NO otherwise.
@return A Future that resolves with the accessibility info
*/
- (FBFuture<NSArray<NSDictionary<NSString *, id> *> *> *)accessibility_info_at_point:(nullable NSValue *)point nestedFormat:(BOOL)nestedFormat;
- (FBFuture<id> *)accessibility_info_at_point:(nullable NSValue *)point nestedFormat:(BOOL)nestedFormat;
/**
Adds media files (photos, videos, ...) to the target
@ -211,7 +223,16 @@ NS_ASSUME_NONNULL_BEGIN
@param bundleID app to approve services for
@return a Future that resolves when complete.
*/
- (FBFuture<NSNull *> *)approve:(NSSet<FBSettingsApprovalService> *)services for_application:(NSString *)bundleID;
- (FBFuture<NSNull *> *)approve:(NSSet<FBTargetSettingsService> *)services for_application:(NSString *)bundleID;
/**
Revokes the given services for an app
@param services services to revoke
@param bundleID app to revoke services for
@return a Future that resolves when complete.
*/
- (FBFuture<NSNull *> *)revoke:(NSSet<FBTargetSettingsService> *)services for_application:(NSString *)bundleID;
/**
Approves the deeplink given a schema and app.
@ -223,6 +244,16 @@ This allows to avoid the permission popup the first time we open a deeplink
*/
- (FBFuture<NSNull *> *)approve_deeplink:(NSString *)scheme for_application:(NSString *)bundleID;
/**
Revokes the deeplink given a schema and app.
This enables the permission popup the first time we open a deeplink
@param scheme scheme of the deeplink url (the part before ":")
@param bundleID app to revoke services for
@return a Future that resolves when complete.
*/
- (FBFuture<NSNull *> *)revoke_deeplink:(NSString *)scheme for_application:(NSString *)bundleID;
/**
Open a url on the target
@ -249,9 +280,9 @@ This allows to avoid the permission popup the first time we open a deeplink
/**
List the xctests installed
@return a Future that resolves with a set of tests.
@return a Future that resolves with an array of tests.
*/
- (FBFuture<NSSet<id<FBXCTestDescriptor>> *> *)list_test_bundles;
- (FBFuture<NSArray<id<FBXCTestDescriptor>> *> *)list_test_bundles;
/**
List the tests in an installed bundle
@ -368,10 +399,11 @@ This allows to avoid the permission popup the first time we open a deeplink
@param name preference name
@param value preference value
@param type preference value type
@param domain preference domain - optional
@return a Future that resolves when successful.
*/
- (FBFuture<NSNull *> *)set_preference:(NSString *)name value:(NSString *)value domain:(nullable NSString *)domain;
- (FBFuture<NSNull *> *)set_preference:(NSString *)name value:(NSString *)value type:(nullable NSString *)type domain:(nullable NSString *)domain;
/**
Gets a preference value by its name and domain. If domain not specified assumed to be Apple Global Domain
@ -512,7 +544,7 @@ This allows to avoid the permission popup the first time we open a deeplink
/**
Spawn a dap protocol server from dapPath
@param dapPath relative path to the root container where dap is installed
@param stdIn where the dap process reads
@param stdOut where the dap process writes

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -10,17 +10,24 @@
#import <FBSimulatorControl/FBSimulatorControl.h>
#import <FBDeviceControl/FBDeviceControl.h>
#import "FBXCTestDescriptor.h"
#import "FBXCTestRunRequest.h"
#import "FBXCTestRunRequest.h"
#import "FBIDBStorageManager.h"
#import "FBIDBError.h"
#import "FBIDBLogger.h"
#import "FBIDBPortsConfiguration.h"
#import "FBDsymInstallLinkToBundle.h"
FBFileContainerKind const FBFileContainerKindXctest = @"xctest";
FBFileContainerKind const FBFileContainerKindDylib = @"dylib";
FBFileContainerKind const FBFileContainerKindDsym = @"dsym";
FBFileContainerKind const FBFileContainerKindFramework = @"framework";
@interface FBIDBCommandExecutor ()
@property (nonatomic, strong, readonly) id<FBiOSTarget> target;
@property (nonatomic, strong, readonly) FBIDBLogger *logger;
@property (nonatomic, strong, readonly) FBIDBPortsConfiguration *ports;
@property (nonatomic, readonly) in_port_t debugserverPort;
@end
@ -28,12 +35,12 @@
#pragma mark Initializers
+ (instancetype)commandExecutorForTarget:(id<FBiOSTarget>)target storageManager:(FBIDBStorageManager *)storageManager temporaryDirectory:(FBTemporaryDirectory *)temporaryDirectory ports:(FBIDBPortsConfiguration *)ports logger:(FBIDBLogger *)logger
+ (instancetype)commandExecutorForTarget:(id<FBiOSTarget>)target storageManager:(FBIDBStorageManager *)storageManager temporaryDirectory:(FBTemporaryDirectory *)temporaryDirectory debugserverPort:(in_port_t)debugserverPort logger:(FBIDBLogger *)logger
{
return [[self alloc] initWithTarget:target storageManager:storageManager temporaryDirectory:temporaryDirectory ports:ports logger:[logger withName:@"grpc_handler"]];
return [[self alloc] initWithTarget:target storageManager:storageManager temporaryDirectory:temporaryDirectory debugserverPort:debugserverPort logger:[logger withName:@"grpc_handler"]];
}
- (instancetype)initWithTarget:(id<FBiOSTarget>)target storageManager:(FBIDBStorageManager *)storageManager temporaryDirectory:(FBTemporaryDirectory *)temporaryDirectory ports:(FBIDBPortsConfiguration *)ports logger:(FBIDBLogger *)logger
- (instancetype)initWithTarget:(id<FBiOSTarget>)target storageManager:(FBIDBStorageManager *)storageManager temporaryDirectory:(FBTemporaryDirectory *)temporaryDirectory debugserverPort:(in_port_t)debugserverPort logger:(FBIDBLogger *)logger
{
self = [super init];
if (!self) {
@ -43,7 +50,7 @@
_target = target;
_storageManager = storageManager;
_temporaryDirectory = temporaryDirectory;
_ports = ports;
_debugserverPort = debugserverPort;
_logger = logger;
return self;
@ -69,29 +76,29 @@
}];
}
- (FBFuture<FBInstalledArtifact *> *)install_app_file_path:(NSString *)filePath make_debuggable:(BOOL)makeDebuggable
- (FBFuture<FBInstalledArtifact *> *)install_app_file_path:(NSString *)filePath make_debuggable:(BOOL)makeDebuggable override_modification_time:(BOOL)overrideModificationTime
{
// Use .app directly, or extract an .ipa
if ([FBBundleDescriptor isApplicationAtPath:filePath]) {
return [self installAppBundle:[FBFutureContext futureContextWithFuture:[FBBundleDescriptor extractedApplicationAtPath:filePath]] makeDebuggable:makeDebuggable];
} else {
return [self installExtractedApp:[self.temporaryDirectory withArchiveExtractedFromFile:filePath] makeDebuggable:makeDebuggable];
return [self installExtractedApp:[self.temporaryDirectory withArchiveExtractedFromFile:filePath overrideModificationTime:overrideModificationTime] makeDebuggable:makeDebuggable];
}
}
- (FBFuture<FBInstalledArtifact *> *)install_app_stream:(FBProcessInput *)input compression:(FBCompressionFormat)compression make_debuggable:(BOOL)makeDebuggable
- (FBFuture<FBInstalledArtifact *> *)install_app_stream:(FBProcessInput *)input compression:(FBCompressionFormat)compression make_debuggable:(BOOL)makeDebuggable override_modification_time:(BOOL)overrideModificationTime
{
return [self installExtractedApp:[self.temporaryDirectory withArchiveExtractedFromStream:input compression:compression] makeDebuggable:makeDebuggable];
return [self installExtractedApp:[self.temporaryDirectory withArchiveExtractedFromStream:input compression:compression overrideModificationTime:overrideModificationTime] makeDebuggable:makeDebuggable];
}
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_file_path:(NSString *)filePath
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_file_path:(NSString *)filePath skipSigningBundles:(BOOL)skipSigningBundles
{
return [self installXctestFilePath:[FBFutureContext futureContextWithFuture:[FBFuture futureWithResult:[NSURL fileURLWithPath:filePath]]]];
return [self installXctestFilePath:[FBFutureContext futureContextWithFuture:[FBFuture futureWithResult:[NSURL fileURLWithPath:filePath]]] skipSigningBundles:skipSigningBundles];
}
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_stream:(FBProcessInput *)stream
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_stream:(FBProcessInput *)stream skipSigningBundles:(BOOL)skipSigningBundles
{
return [self installXctest:[self.temporaryDirectory withArchiveExtractedFromStream:stream compression:FBCompressionFormatGZIP]];
return [self installXctest:[self.temporaryDirectory withArchiveExtractedFromStream:stream compression:FBCompressionFormatGZIP] skipSigningBundles:skipSigningBundles];
}
- (FBFuture<FBInstalledArtifact *> *)install_dylib_file_path:(NSString *)filePath
@ -114,14 +121,14 @@
return [self installBundle:[self.temporaryDirectory withArchiveExtractedFromStream:input compression:FBCompressionFormatGZIP] intoStorage:self.storageManager.framework];
}
- (FBFuture<FBInstalledArtifact *> *)install_dsym_file_path:(NSString *)filePath linkToApp:(nullable NSString *)bundleID
- (FBFuture<FBInstalledArtifact *> *)install_dsym_file_path:(NSString *)filePath linkTo:(nullable FBDsymInstallLinkToBundle *)linkTo
{
return [self installAndLinkDsym:[FBFutureContext futureContextWithFuture:[FBFuture futureWithResult:[NSURL fileURLWithPath:filePath]]] intoStorage:self.storageManager.dsym linkToApp:bundleID];
return [self installAndLinkDsym:[FBFutureContext futureContextWithFuture:[FBFuture futureWithResult:[NSURL fileURLWithPath:filePath]]] intoStorage:self.storageManager.dsym linkTo:linkTo];
}
- (FBFuture<FBInstalledArtifact *> *)install_dsym_stream:(FBProcessInput *)input linkToApp:(nullable NSString *)bundleID
- (FBFuture<FBInstalledArtifact *> *)install_dsym_stream:(FBProcessInput *)input compression:(FBCompressionFormat)compression linkTo:(nullable FBDsymInstallLinkToBundle *)linkTo
{
return [self installAndLinkDsym:[self dsymDirnameFromUnzipDir:[self.temporaryDirectory withArchiveExtractedFromStream:input compression:FBCompressionFormatGZIP]] intoStorage:self.storageManager.dsym linkToApp:bundleID];
return [self installAndLinkDsym:[self dsymDirnameFromUnzipDir:[self.temporaryDirectory withArchiveExtractedFromStream:input compression:compression]] intoStorage:self.storageManager.dsym linkTo:linkTo];
}
#pragma mark Public Methods
@ -135,7 +142,7 @@
}];
}
- (FBFuture<NSArray<NSDictionary<NSString *, id> *> *> *)accessibility_info_at_point:(nullable NSValue *)value nestedFormat:(BOOL)nestedFormat
- (FBFuture<id> *)accessibility_info_at_point:(nullable NSValue *)value nestedFormat:(BOOL)nestedFormat
{
return [[self
accessibilityCommands]
@ -175,7 +182,7 @@
}];
}
- (FBFuture<NSNull *> *)approve:(NSSet<FBSettingsApprovalService> *)services for_application:(NSString *)bundleID
- (FBFuture<NSNull *> *)approve:(NSSet<FBTargetSettingsService> *)services for_application:(NSString *)bundleID
{
return [self.settingsCommands
onQueue:self.target.workQueue fmap:^FBFuture *(id<FBSimulatorSettingsCommands> commands) {
@ -183,6 +190,14 @@
}];
}
- (FBFuture<NSNull *> *)revoke:(NSSet<FBTargetSettingsService> *)services for_application:(NSString *)bundleID
{
return [self.settingsCommands
onQueue:self.target.workQueue fmap:^FBFuture *(id<FBSimulatorSettingsCommands> commands) {
return [commands revokeAccess:[NSSet setWithObject:bundleID] toServices:services];
}];
}
- (FBFuture<NSNull *> *)approve_deeplink:(NSString *)scheme for_application:(NSString *)bundleID
{
return [self.settingsCommands
@ -191,6 +206,14 @@
}];
}
- (FBFuture<NSNull *> *)revoke_deeplink:(NSString *)scheme for_application:(NSString *)bundleID
{
return [self.settingsCommands
onQueue:self.target.workQueue fmap:^FBFuture *(id<FBSimulatorSettingsCommands> commands) {
return [commands revokeAccess:[NSSet setWithObject:bundleID] toDeeplink:scheme];
}];
}
- (FBFuture<NSNull *> *)open_url:(NSString *)url
{
return [self.lifecycleCommands
@ -253,11 +276,11 @@
}];
}
- (FBFuture<NSSet<id<FBXCTestDescriptor>> *> *)list_test_bundles
- (FBFuture<NSArray<id<FBXCTestDescriptor>> *> *)list_test_bundles
{
return [FBFuture onQueue:self.target.workQueue resolve:^{
NSError *error;
NSSet<id<FBXCTestDescriptor>> *testDescriptors = [self.storageManager.xctest listTestDescriptorsWithError:&error];
NSArray<id<FBXCTestDescriptor>> *testDescriptors = [self.storageManager.xctest listTestDescriptorsWithError:&error];
if (testDescriptors == nil) {
return [FBFuture futureWithError:error];
}
@ -265,7 +288,8 @@
}];
}
static const NSTimeInterval ListTestBundleTimeout = 60.0;
// Some Mac tests are big that dlopen might take long
static const NSTimeInterval ListTestBundleTimeout = 180.0;
- (FBFuture<NSArray<NSString *> *> *)list_tests_in_bundle:(NSString *)bundleID with_app:(NSString *)appPath
{
@ -302,7 +326,7 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
- (FBFuture<NSNull *> *)kill_application:(NSString *)bundleID
{
return [self.target killApplicationWithBundleID:bundleID];
return [[self.target killApplicationWithBundleID:bundleID] fallback:NSNull.null];
}
- (FBFuture<id<FBLaunchedApplication>> *)launch_app:(FBApplicationLaunchConfiguration *)configuration
@ -354,10 +378,13 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
failFuture];
}
return [[self
return [[[self
debugserver_prepare:bundleID]
onQueue:self.target.workQueue fmap:^(FBBundleDescriptor *application) {
return [commands launchDebugServerForHostApplication:application port:self.ports.debugserverPort];
return [commands launchDebugServerForHostApplication:application port:self.debugserverPort];
}]
onQueue:self.target.workQueue doOnResolved:^(id<FBDebugServer> debugServer) {
self.debugServer = debugServer;
}];
}
@ -482,12 +509,12 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
}];
}
- (FBFuture<NSNull *> *)set_preference:(NSString *)name value:(NSString *)value domain:(nullable NSString *)domain
- (FBFuture<NSNull *> *)set_preference:(NSString *)name value:(NSString *)value type:(nullable NSString *)type domain:(nullable NSString *)domain
{
return [[self
settingsCommands]
onQueue:self.target.workQueue fmap:^(id<FBSimulatorSettingsCommands> commands) {
return [commands setPreference:name value:value domain:domain];
return [commands setPreference:name value:value type:type domain:domain];
}];
}
@ -505,7 +532,7 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
return [[self
settingsCommands]
onQueue:self.target.workQueue fmap:^(id<FBSimulatorSettingsCommands> commands) {
return [commands setPreference:@"AppleLocale" value:identifier domain:nil];
return [commands setPreference:@"AppleLocale" value:identifier type:nil domain:nil];
}];
}
@ -561,7 +588,7 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
onQueue:self.target.workQueue pop:^FBFuture *(id<FBFileContainer> container) {
NSMutableArray<FBFuture<NSNull *> *> *futures = NSMutableArray.array;
for (NSURL *originPath in paths) {
[futures addObject:[container copyFromHost:originPath toContainer:destinationPath]];
[futures addObject:[container copyFromHost:originPath.path toContainer:destinationPath]];
}
return [[FBFuture futureWithFutures:futures] mapReplace:NSNull.null];
}];
@ -661,7 +688,7 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
describeFormat:@"Target doesn't conform to FBDapServerCommand protocol %@", commands]
failFuture];
}
return [commands launchDapServer:dapPath stdIn:stdIn stdOut:stdOut];
}
@ -706,9 +733,24 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
if ([containerType isEqualToString:FBFileContainerKindDiskImages]) {
return [commands fileCommandsForDiskImages];
}
if ([containerType isEqualToString:FBFileContainerKindSymbols]) {
return [commands fileCommandsForSymbols];
}
if ([containerType isEqualToString:FBFileContainerKindAuxillary]) {
return [commands fileCommandsForAuxillary];
}
if ([containerType isEqualToString:FBFileContainerKindXctest]) {
return [FBFutureContext futureContextWithResult:self.storageManager.xctest.asFileContainer];
}
if ([containerType isEqualToString:FBFileContainerKindDylib]) {
return [FBFutureContext futureContextWithResult:self.storageManager.dylib.asFileContainer];
}
if ([containerType isEqualToString:FBFileContainerKindDsym]) {
return [FBFutureContext futureContextWithResult:self.storageManager.dsym.asFileContainer];
}
if ([containerType isEqualToString:FBFileContainerKindFramework]) {
return [FBFutureContext futureContextWithResult:self.storageManager.framework.asFileContainer];
}
if (containerType == nil || containerType.length == 0) {
// The Default for no, or null container for back-compat.
return [self.target isKindOfClass:FBDevice.class] ? [commands fileCommandsForMediaDirectory] : [commands fileCommandsForRootFilesystem];
@ -841,19 +883,19 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
}];
}
- (FBFuture<FBInstalledArtifact *> *)installXctest:(FBFutureContext<NSURL *> *)extractedXctest
- (FBFuture<FBInstalledArtifact *> *)installXctest:(FBFutureContext<NSURL *> *)extractedXctest skipSigningBundles:(BOOL)skipSigningBundles
{
return [extractedXctest
onQueue:self.target.workQueue pop:^(NSURL *extractionDirectory) {
return [self.storageManager.xctest saveBundleOrTestRunFromBaseDirectory:extractionDirectory];
return [self.storageManager.xctest saveBundleOrTestRunFromBaseDirectory:extractionDirectory skipSigningBundles:skipSigningBundles];
}];
}
- (FBFuture<FBInstalledArtifact *> *)installXctestFilePath:(FBFutureContext<NSURL *> *)bundle
- (FBFuture<FBInstalledArtifact *> *)installXctestFilePath:(FBFutureContext<NSURL *> *)bundle skipSigningBundles:(BOOL)skipSigningBundles
{
return [bundle
onQueue:self.target.workQueue pop:^(NSURL *xctestURL) {
return [self.storageManager.xctest saveBundleOrTestRun:xctestURL];
return [self.storageManager.xctest saveBundleOrTestRun:xctestURL skipSigningBundles:skipSigningBundles];
}];
}
@ -880,18 +922,19 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
if (!subDirs) {
return [FBFuture futureWithError:error];
}
// TODO: support cases when more than one dSYM is included in the archive
if ([subDirs count] != 1) {
return [FBFuture futureWithError:[FBControlCoreError errorForDescription:[NSString stringWithFormat:@"Expected only one dSYM directory: found: %tu", [subDirs count]]]];
// if more than one dSYM is found
// then we treat the parent dir as the dSYM directory
return [FBFuture futureWithResult:parentDir];
}
return [FBFuture futureWithResult:subDirs[0]];
}];
}
// Will install the dsym under standard dsym location
// if linkToApp app is passed:
// after installation it will create a symlink in the app bundle
- (FBFuture<FBInstalledArtifact *> *)installAndLinkDsym:(FBFutureContext<NSURL *> *)extractedFileContext intoStorage:(FBFileStorage *)storage linkToApp:(nullable NSString *)bundleID
// if linkTo is passed:
// after installation it will create a symlink in the bundle container
- (FBFuture<FBInstalledArtifact *> *)installAndLinkDsym:(FBFutureContext<NSURL *> *)extractedFileContext intoStorage:(FBFileStorage *)storage linkTo:(nullable FBDsymInstallLinkToBundle *)linkTo
{
return [extractedFileContext
onQueue:self.target.workQueue pop:^(NSURL *extractionDir) {
@ -900,26 +943,38 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
if (!artifact) {
return [FBFuture futureWithError:error];
}
if (!bundleID) {
if (!linkTo) {
return [FBFuture futureWithResult:artifact];
}
return [[self.target installedApplicationWithBundleID:bundleID]
onQueue:self.target.workQueue fmap:^(FBInstalledApplication *linkToApp) {
FBFuture<NSURL *> *future = nil;
if (linkTo.bundle_type == FBDsymBundleTypeApp) {
future = [[self.target installedApplicationWithBundleID:linkTo.bundle_id] onQueue:self.target.workQueue fmap:^(FBInstalledApplication *linkToApp) {
[self.logger logFormat:@"Going to create a symlink for app bundle: %@", linkToApp.bundle.name];
NSURL *appPath = [[NSURL fileURLWithPath:linkToApp.bundle.path] URLByDeletingLastPathComponent];
NSURL *appDsymURL = [appPath URLByAppendingPathComponent:artifact.path.lastPathComponent];
// delete a simlink if already exists
// TODO: check if what we are deleting is a symlink
[NSFileManager.defaultManager removeItemAtURL:appDsymURL error:nil];
[self.logger logFormat:@"Deleted a symlink for dsym if it already exists: %@", appDsymURL];
NSError *createLinkError = nil;
if (![NSFileManager.defaultManager createSymbolicLinkAtURL:appDsymURL withDestinationURL:artifact.path error:&createLinkError]){
return [FBFuture futureWithError:error];
}
[self.logger logFormat:@"Created a symlink for dsym from: %@ to %@", appDsymURL, artifact.path];
return [FBFuture futureWithResult:artifact];
}];
return [FBFuture futureWithResult:[NSURL fileURLWithPath:linkToApp.bundle.path]];
}];
} else {
id<FBXCTestDescriptor> testDescriptor = [self.storageManager.xctest testDescriptorWithID:linkTo.bundle_id error:&error];
[self.logger logFormat:@"Going to create a symlink for test bundle: %@", testDescriptor.name];
future = [FBFuture futureWithResult:testDescriptor.url];
}
return [future onQueue:self.target.workQueue fmap:^(NSURL *bundlePath) {
NSURL *bundleUrl = [bundlePath URLByDeletingLastPathComponent];
NSURL *dsymURL = [bundleUrl URLByAppendingPathComponent:artifact.path.lastPathComponent];
// delete a simlink if already exists
// TODO: check if what we are deleting is a symlink
[NSFileManager.defaultManager removeItemAtURL:dsymURL error:nil];
[self.logger logFormat:@"Deleted a symlink for dsym if it already exists: %@", dsymURL];
NSError *createLinkError = nil;
if (![NSFileManager.defaultManager createSymbolicLinkAtURL:dsymURL withDestinationURL:artifact.path error:&createLinkError]){
return [FBFuture futureWithError:error];
}
[self.logger logFormat:@"Created a symlink for dsym from: %@ to %@", dsymURL, artifact.path];
return [FBFuture futureWithResult:artifact];
}];
}];
}

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -27,7 +27,12 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign, readonly) FBCodeCoverageFormat format;
- (instancetype)initWithCollect:(BOOL)collect format:(FBCodeCoverageFormat)format;
/**
Determines whether should enable continuous coverage collection
*/
@property (nonatomic, assign, readonly) BOOL shouldEnableContinuousCoverageCollection;
- (instancetype)initWithCollect:(BOOL)collect format:(FBCodeCoverageFormat)format enableContinuousCoverageCollection:(BOOL)enableContinuousCoverageCollection;
@end

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -9,16 +9,16 @@
@implementation FBCodeCoverageRequest
- (instancetype)initWithCollect:(BOOL)collect format:(FBCodeCoverageFormat)format
- (instancetype)initWithCollect:(BOOL)collect format:(FBCodeCoverageFormat)format enableContinuousCoverageCollection:(BOOL)enableContinuousCoverageCollection
{
self = [super init];
if (!self) {
return nil;
}
_collect = collect;
_format = format;
_shouldEnableContinuousCoverageCollection = enableContinuousCoverageCollection;
return self;
}

Просмотреть файл

@ -0,0 +1,38 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import <FBControlCore/FBControlCore.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, FBDsymBundleType) {
FBDsymBundleTypeXCTest,
FBDsymBundleTypeApp,
};
/**
Describes bundle needs to be linked with Dsym
*/
@interface FBDsymInstallLinkToBundle : NSObject
/**
ID of the bundle the dsym needs to link
*/
@property (nonatomic, copy, readonly) NSString *bundle_id;
/**
Type of bundle
*/
@property (nonatomic, assign, readonly) FBDsymBundleType bundle_type;
- (instancetype)initWith:(NSString *)bundle_id bundle_type:(FBDsymBundleType)bundle_type;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -0,0 +1,25 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "FBDsymInstallLinkToBundle.h"
@implementation FBDsymInstallLinkToBundle
- (instancetype)initWith:(NSString *)bundle_id bundle_type:(FBDsymBundleType)bundle_type
{
self = [super init];
if (!self) {
return nil;
}
_bundle_id = bundle_id;
_bundle_type = bundle_type;
return self;
}
@end

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -37,13 +37,13 @@ NS_ASSUME_NONNULL_BEGIN
@param waitForDebugger a boolean describing whether the tests should stop after Run and wait for a debugger to be attached.
@return an FBXCTestRunRequest instance.
*/
+ (instancetype)logicTestWithTestBundleID:(NSString *)testBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger;
+ (instancetype)logicTestWithTestBundleID:(NSString *)testBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(nullable NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger collectResultBundle:(BOOL)collectResultBundle;
/**
The Initializer for App Tests.
@param testBundleID the bundle id of the test to run.
@param appBundleID the bundle id of the application to inject the test bundle into.
@param testHostAppBundleID the bundle id of the application to inject the test bundle into.
@param environment environment for the application test process.
@param arguments arguments for the application test process.
@param testsToRun the tests to run.
@ -53,13 +53,12 @@ The Initializer for App Tests.
@param coverageRequest information about llvm code coverage collection
@return an FBXCTestRunRequest instance.
*/
+ (instancetype)applicationTestWithTestBundleID:(NSString *)testBundleID appBundleID:(NSString *)appBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger;
+ (instancetype)applicationTestWithTestBundleID:(NSString *)testBundleID testHostAppBundleID:(NSString *)testHostAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(nullable NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger collectResultBundle:(BOOL)collectResultBundle;
/**
The Initializer for UI Tests.
@param testBundleID the bundle id of the test to run.
@param appBundleID the bundle id of the application to automatie.
@param testHostAppBundleID the bundle id of the application hosting the test bundle.
@param environment environment for the logic test process.
@param arguments arguments for the logic test process.
@ -70,7 +69,7 @@ The Initializer for UI Tests.
@param coverageRequest information about llvm code coverage collection
@return an FBXCTestRunRequest instance.
*/
+ (instancetype)uiTestWithTestBundleID:(NSString *)testBundleID appBundleID:(NSString *)appBundleID testHostAppBundleID:(NSString *)testHostAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs;
+ (instancetype)uiTestWithTestBundleID:(NSString *)testBundleID testHostAppBundleID:(NSString *)testHostAppBundleID testTargetAppBundleID:(NSString *)testTargetAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(nullable NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs collectResultBundle:(BOOL)collectResultBundle;
#pragma mark Properties
@ -89,16 +88,16 @@ The Initializer for UI Tests.
*/
@property (nonatomic, copy, readonly) NSString *testBundleID;
/**
The Bundle ID of the Application to test in, if relevant.
*/
@property (nonatomic, copy, nullable, readonly) NSString *appBundleID;
/**
The Bundle ID of the Test Host, if relevant.
*/
@property (nonatomic, copy, nullable, readonly) NSString *testHostAppBundleID;
/**
The Bundle ID of the Test Target (a.k.a. App Under Test), if relevant.
*/
@property (nonatomic, copy, nullable, readonly) NSString *testTargetAppBundleID;
/**
The environment variables for the application, if relevant
*/
@ -149,6 +148,11 @@ The Initializer for UI Tests.
*/
@property (nonatomic, assign, readonly) BOOL waitForDebugger;
/**
If set tests' result bundle will be collected
*/
@property (nonatomic, assign, readonly) BOOL collectResultBundle;
/**
Starts the test operation.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -49,7 +49,10 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
NSURL *dir = [temporaryDirectory ephemeralTemporaryDirectory];
NSString *coverageDirName =[NSString stringWithFormat:@"coverage_%@", NSUUID.UUID.UUIDString];
NSString *coverageDirPath = [dir.path stringByAppendingPathComponent:coverageDirName];
coverageConfig = [[FBCodeCoverageConfiguration alloc] initWithDirectory:coverageDirPath format:self.coverageRequest.format];
if (![NSFileManager.defaultManager createDirectoryAtPath:coverageDirPath withIntermediateDirectories:YES attributes:nil error:&error]) {
return [FBFuture futureWithError:error];
}
coverageConfig = [[FBCodeCoverageConfiguration alloc] initWithDirectory:coverageDirPath format:self.coverageRequest.format enableContinuousCoverageCollection:self.coverageRequest.shouldEnableContinuousCoverageCollection];
}
NSString *testFilter = nil;
@ -78,7 +81,8 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
mirroring:FBLogicTestMirrorFileLogs
coverageConfiguration:coverageConfig
binaryPath:testDescriptor.testBundle.binary.path
logDirectoryPath:logDirectoryPath];
logDirectoryPath:logDirectoryPath
architectures:testDescriptor.architectures];
return [self startTestExecution:configuration target:target reporter:reporter logger:logger];
}
@ -96,7 +100,8 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
coverageConfiguration:configuration.coverageConfiguration
logDirectoryPath:configuration.logDirectoryPath
binariesPaths:@[configuration.binaryPath]
reportAttachments:self.reportAttachments];
reportAttachments:self.reportAttachments
reportResultBundle:self.collectResultBundle];
FBIDBTestOperation *operation = [[FBIDBTestOperation alloc]
initWithConfiguration:configuration
reporterConfiguration:reporterConfiguration
@ -135,11 +140,11 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
}]
onQueue:target.workQueue fmap:^ FBFuture<FBIDBTestOperation *> * (FBIDBAppHostedTestConfiguration *appHostedTestConfig) {
[logger logFormat:@"Obtained app-hosted test configuration %@", appHostedTestConfig];
return [FBXCTestRunRequest_AppTest startTestExecution:appHostedTestConfig reportAttachments:self.reportAttachments target:target reporter:reporter logger:logger];
return [FBXCTestRunRequest_AppTest startTestExecution:appHostedTestConfig reportAttachments:self.reportAttachments target:target reporter:reporter logger:logger reportResultBundle:self.collectResultBundle];
}];
}
+ (FBFuture<FBIDBTestOperation *> *)startTestExecution:(FBIDBAppHostedTestConfiguration *)configuration reportAttachments:(BOOL)reportAttachments target:(id<FBiOSTarget>)target reporter:(id<FBXCTestReporter>)reporter logger:(id<FBControlCoreLogger>)logger
+ (FBFuture<FBIDBTestOperation *> *)startTestExecution:(FBIDBAppHostedTestConfiguration *)configuration reportAttachments:(BOOL)reportAttachments target:(id<FBiOSTarget>)target reporter:(id<FBXCTestReporter>)reporter logger:(id<FBControlCoreLogger>)logger reportResultBundle:(BOOL)reportResultBundle
{
FBTestLaunchConfiguration *testLaunchConfiguration = configuration.testLaunchConfiguration;
FBCodeCoverageConfiguration *coverageConfiguration = configuration.coverageConfiguration;
@ -164,7 +169,8 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
coverageConfiguration:coverageConfiguration
logDirectoryPath:testLaunchConfiguration.logDirectoryPath
binariesPaths:binariesPaths
reportAttachments:reportAttachments];
reportAttachments:reportAttachments
reportResultBundle:reportResultBundle];
return [FBFuture futureWithResult:[[FBIDBTestOperation alloc]
initWithConfiguration:testLaunchConfiguration
reporterConfiguration:reporterConfiguration
@ -203,33 +209,33 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
@implementation FBXCTestRunRequest
@synthesize testBundleID = _testBundleID;
@synthesize appBundleID = _appBundleID;
@synthesize testHostAppBundleID = _testHostAppBundleID;
@synthesize environment = _environment;
@synthesize arguments = _arguments;
@synthesize testsToRun = _testsToRun;
@synthesize testsToSkip = _testsToSkip;
@synthesize testTimeout = _testTimeout;
@synthesize collectResultBundle = _collectResultBundle;
#pragma mark Initializers
+ (instancetype)logicTestWithTestBundleID:(NSString *)testBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger
+ (instancetype)logicTestWithTestBundleID:(NSString *)testBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger collectResultBundle:(BOOL)collectResultBundle
{
return [[FBXCTestRunRequest_LogicTest alloc] initWithTestBundleID:testBundleID appBundleID:nil testHostAppBundleID:nil environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverageRequest collectLogs:collectLogs waitForDebugger:waitForDebugger];
return [[FBXCTestRunRequest_LogicTest alloc] initWithTestBundleID:testBundleID testHostAppBundleID:nil testTargetAppBundleID:nil environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverageRequest collectLogs:collectLogs waitForDebugger:waitForDebugger collectResultBundle:collectResultBundle];
}
+ (instancetype)applicationTestWithTestBundleID:(NSString *)testBundleID appBundleID:(NSString *)appBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger
+ (instancetype)applicationTestWithTestBundleID:(NSString *)testBundleID testHostAppBundleID:(NSString *)testHostAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger collectResultBundle:(BOOL)collectResultBundle
{
return [[FBXCTestRunRequest_AppTest alloc] initWithTestBundleID:testBundleID appBundleID:appBundleID testHostAppBundleID:nil environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverageRequest collectLogs:collectLogs waitForDebugger:waitForDebugger];
return [[FBXCTestRunRequest_AppTest alloc] initWithTestBundleID:testBundleID testHostAppBundleID:testHostAppBundleID testTargetAppBundleID:nil environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverageRequest collectLogs:collectLogs waitForDebugger:waitForDebugger collectResultBundle:collectResultBundle];
}
+ (instancetype)uiTestWithTestBundleID:(NSString *)testBundleID appBundleID:(NSString *)appBundleID testHostAppBundleID:(NSString *)testHostAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs
+ (instancetype)uiTestWithTestBundleID:(NSString *)testBundleID testHostAppBundleID:(NSString *)testHostAppBundleID testTargetAppBundleID:(NSString *)testTargetAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs collectResultBundle:(BOOL)collectResultBundle
{
return [[FBXCTestRunRequest_UITest alloc] initWithTestBundleID:testBundleID appBundleID:appBundleID testHostAppBundleID:testHostAppBundleID environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverageRequest collectLogs:collectLogs waitForDebugger:NO];
return [[FBXCTestRunRequest_UITest alloc] initWithTestBundleID:testBundleID testHostAppBundleID:testHostAppBundleID testTargetAppBundleID:testTargetAppBundleID environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverageRequest collectLogs:collectLogs waitForDebugger:NO collectResultBundle:collectResultBundle];
}
- (instancetype)initWithTestBundleID:(NSString *)testBundleID appBundleID:(NSString *)appBundleID testHostAppBundleID:(NSString *)testHostAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger
- (instancetype)initWithTestBundleID:(NSString *)testBundleID testHostAppBundleID:(NSString *)testHostAppBundleID testTargetAppBundleID:(NSString *)testTargetAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger collectResultBundle:(BOOL)collectResultBundle
{
self = [super init];
if (!self) {
@ -237,8 +243,8 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
}
_testBundleID = testBundleID;
_appBundleID = appBundleID;
_testHostAppBundleID = testHostAppBundleID;
_testTargetAppBundleID = testTargetAppBundleID;
_environment = environment;
_arguments = arguments;
_testsToRun = testsToRun;
@ -249,6 +255,7 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
_coverageRequest = coverageRequest;
_collectLogs = collectLogs;
_waitForDebugger = waitForDebugger;
_collectResultBundle = collectResultBundle;
return self;
}
@ -299,7 +306,3 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
}
@end

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -90,7 +90,7 @@ static void RemoveGlobalLogger(id<FBControlCoreLogger> logger)
+ (instancetype)loggerWithUserDefaults:(NSUserDefaults *)userDefaults
{
BOOL debugLogging = [[userDefaults stringForKey:@"-log-level"].lowercaseString isEqualToString:@"info"] ? NO : YES;
id<FBControlCoreLogger> systemLogger = [FBControlCoreLogger systemLoggerWritingToStderr:YES withDebugLogging:debugLogging];
id<FBControlCoreLogger> systemLogger = [FBControlCoreLoggerFactory systemLoggerWritingToStderr:YES withDebugLogging:debugLogging];
NSMutableArray<id<FBControlCoreLogger>> *loggers = [NSMutableArray arrayWithObject:systemLogger];
NSError *error;
@ -108,7 +108,7 @@ static void RemoveGlobalLogger(id<FBControlCoreLogger> logger)
exit(1);
}
[loggers addObject:[FBControlCoreLogger loggerToFileDescriptor:fileDescriptor closeOnEndOfFile:YES]];
[loggers addObject:[FBControlCoreLoggerFactory loggerToFileDescriptor:fileDescriptor closeOnEndOfFile:YES]];
}
FBIDBLogger *logger = [[[FBIDBLogger alloc] initWithLoggers:loggers] withDateFormatEnabled:YES];
FBControlCoreGlobalConfiguration.defaultLogger = logger;
@ -145,7 +145,7 @@ static void RemoveGlobalLogger(id<FBControlCoreLogger> logger)
{
dispatch_queue_t queue = FBIDBLogger.loggerQueue;
return [FBFuture onQueue:queue resolveValue:^(NSError **_) {
id<FBControlCoreLogger> logger = [FBControlCoreLogger loggerToConsumer:consumer];
id<FBControlCoreLogger> logger = [FBControlCoreLoggerFactory loggerToConsumer:consumer];
id<FBLogOperation> operation = [[FBIDBLogger_Operation alloc] initWithConsumer:consumer logger:logger queue:queue];
AddGlobalLogger(logger);
return operation;

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -7,7 +7,6 @@
#import <Foundation/Foundation.h>
#import <FBControlCore/FBControlCore.h>
#import "FBXCTestDescriptor.h"
NS_ASSUME_NONNULL_BEGIN
@ -17,6 +16,9 @@ extern NSString *const IdbDylibsFolder;
extern NSString *const IdbDsymsFolder;
extern NSString *const IdbFrameworksFolder;
@protocol FBControlCoreLogger;
@protocol FBXCTestDescriptor;
/**
A wrapper around an installed artifact
*/
@ -80,6 +82,13 @@ extern NSString *const IdbFrameworksFolder;
*/
@property (nonatomic, copy, readonly) NSDictionary<NSString *, NSString *> *replacementMapping;
/**
Exposes the receiver as an FBFileContainer instance.
@return a FBFileContainer instance.
*/
- (id<FBFileContainer>)asFileContainer;
/**
Cleans all storage
@ -177,24 +186,25 @@ extern NSString *const IdbFrameworksFolder;
@param baseDirectory the directory containing the test bundle.
@return the bundle id of the installed test, or nil if failed
*/
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRunFromBaseDirectory:(NSURL *)baseDirectory;
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRunFromBaseDirectory:(NSURL *)baseDirectory skipSigningBundles:(BOOL)skipSigningBundles;
/**
Stores a test bundle, based on the file path of the actual test bundle.
This is useful when the test bundle is from an existing and local file path, instead of passed in an archive.
@param filePath the file path of the bundle.
@param skipSigningBundles pass true to skip signing xctest bundles.
@return the bundle id of the installed test, or nil if failed
*/
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRun:(NSURL *)filePath;
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRun:(NSURL *)filePath skipSigningBundles:(BOOL)skipSigningBundles;
/**
Get descriptors for all installed test bundles and xctestrun files.
@param error Set if getting this bundle failed
@return Set of FBXCTestDescriptors of all installed test bundles and xctestrun files
@return List of FBXCTestDescriptors of all installed test bundles and xctestrun files
*/
- (nullable NSSet<id<FBXCTestDescriptor>> *)listTestDescriptorsWithError:(NSError **)error;
- (nullable NSArray<id<FBXCTestDescriptor>> *)listTestDescriptorsWithError:(NSError **)error;
/**
Get test descriptor by bundle id.
@ -260,7 +270,7 @@ extern NSString *const IdbFrameworksFolder;
/**
Interpolate path replacements
@return a dictionary with the replacements defined
*/
- (NSDictionary<NSString *, NSString *> *)replacementMapping;

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -54,6 +54,23 @@ NSString *const IdbFrameworksFolder = @"idb-frameworks";
return self;
}
#pragma mark Methods
- (BOOL)clean:(NSError **)error
{
for (NSURL *url in [NSFileManager.defaultManager contentsOfDirectoryAtURL:self.basePath includingPropertiesForKeys:nil options:0 error:nil]) {
if (![NSFileManager.defaultManager removeItemAtPath:url.path error:error]) {
return NO;
}
}
return YES;
}
- (id<FBFileContainer>)asFileContainer
{
return [FBFileContainer fileContainerForBasePath:self.basePath.path];
}
#pragma mark Properties
- (NSDictionary<NSString *, NSString *> *)replacementMapping
@ -65,16 +82,6 @@ NSString *const IdbFrameworksFolder = @"idb-frameworks";
return replacementMapping;
}
-(BOOL)clean:(NSError **)error
{
for (NSURL *url in [NSFileManager.defaultManager contentsOfDirectoryAtURL:self.basePath includingPropertiesForKeys:nil options:0 error:nil]) {
if (![NSFileManager.defaultManager removeItemAtPath:url.path error:error]) {
return NO;
}
}
return YES;
}
@end
@implementation FBFileStorage
@ -129,23 +136,29 @@ NSString *const IdbFrameworksFolder = @"idb-frameworks";
- (BOOL)checkArchitecture:(FBBundleDescriptor *)bundle error:(NSError **)error
{
NSSet<NSString *> *bundleArchs = bundle.binary.architectures;
NSString *targetArch = self.target.architecture;
const BOOL containsExactArch = [bundleArchs containsObject:targetArch];
NSSet<FBArchitecture> *binaryArchitectures = bundle.binary.architectures;
NSArray<FBArchitecture> *targetArchs = self.target.architectures;
NSSet<FBArchitecture> *supportedArchitectures = [FBiOSTargetConfiguration baseArchsToCompatibleArch:targetArchs];
const BOOL containsExactArch = [binaryArchitectures intersectsSet:supportedArchitectures];
// arm64 binaries are acceptable on arm64e devices, but arm64e is not yet available
const BOOL arm64eEquivalent = [targetArch isEqualToString:@"arm64e"] && [bundleArchs containsObject:@"arm64"];
const BOOL arm64eEquivalent = [targetArchs containsObject:@"arm64e"] && [binaryArchitectures containsObject:@"arm64"];
if (!(containsExactArch || arm64eEquivalent)) {
return [[FBIDBError
describeFormat:@"Targets architecture %@ not in the bundles supported architectures: %@", targetArch, bundleArchs.allObjects]
failBool:error];
describeFormat:@"The supported architectures of the target %@ do not intersect with any architectures in the bundle: %@", [FBCollectionInformation oneLineDescriptionFromArray:supportedArchitectures.allObjects], [FBCollectionInformation oneLineDescriptionFromArray:binaryArchitectures.allObjects]]
failBool:error];
}
return YES;
}
- (FBFuture<FBInstalledArtifact *> *)saveBundle:(FBBundleDescriptor *)bundle
{
return [self saveBundle:bundle skipSigningBundles:NO];
}
- (FBFuture<FBInstalledArtifact *> *)saveBundle:(FBBundleDescriptor *)bundle skipSigningBundles:(BOOL)skipSigningBundles
{
// Check that the bundle matches the architecture of the target.
NSError *error = nil;
@ -162,14 +175,13 @@ NSString *const IdbFrameworksFolder = @"idb-frameworks";
// Copy over bundle
NSURL *sourceBundlePath = [NSURL fileURLWithPath:bundle.path];
NSURL *destinationBundlePath = [storageDirectory URLByAppendingPathComponent:sourceBundlePath.lastPathComponent];
[self.logger logFormat:@"Persisting %@ to %@", bundle.identifier, destinationBundlePath];
if (![NSFileManager.defaultManager copyItemAtURL:sourceBundlePath toURL:destinationBundlePath error:&error]) {
[self.logger logFormat:@"Symlink %@ to %@", bundle.identifier, destinationBundlePath];
if (![NSFileManager.defaultManager createSymbolicLinkAtURL:destinationBundlePath withDestinationURL:sourceBundlePath error:&error]) {
return [FBFuture futureWithError:error];
}
[self.logger logFormat:@"Persisted %@", bundle.identifier];
FBInstalledArtifact *artifact = [[FBInstalledArtifact alloc] initWithName:bundle.identifier uuid:bundle.binary.uuid path:destinationBundlePath];
if (!self.relocateLibraries || ![self.target requiresBundlesToBeSigned]) {
if (!self.relocateLibraries || ![self.target requiresBundlesToBeSigned] || skipSigningBundles) {
return [FBFuture futureWithResult:artifact];
}
bundle = [FBBundleDescriptor bundleFromPath:destinationBundlePath.path error:&error];
@ -250,7 +262,7 @@ static NSString *const XctestRunExtension = @"xctestrun";
#pragma mark Public
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRunFromBaseDirectory:(NSURL *)baseDirectory
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRunFromBaseDirectory:(NSURL *)baseDirectory skipSigningBundles:(BOOL)skipSigningBundles
{
// Find .xctest or .xctestrun in directory.
NSError *error = nil;
@ -279,7 +291,7 @@ static NSString *const XctestRunExtension = @"xctestrun";
}
if (xctestBundleURL) {
return [self saveTestBundle:xctestBundleURL];
return [self saveTestBundle:xctestBundleURL skipSigningBundles:skipSigningBundles];
}
if (xctestrunURL) {
return [self saveTestRun:xctestrunURL];
@ -289,11 +301,11 @@ static NSString *const XctestRunExtension = @"xctestrun";
failFuture];
}
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRun:(NSURL *)filePath
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRun:(NSURL *)filePath skipSigningBundles:(BOOL)skipSigningBundles
{
// save .xctest or .xctestrun
if ([filePath.pathExtension isEqualToString:XctestExtension]) {
return [self saveTestBundle:filePath];
return [self saveTestBundle:filePath skipSigningBundles:skipSigningBundles];
}
if ([filePath.pathExtension isEqualToString:XctestRunExtension]) {
return [self saveTestRun:filePath];
@ -303,9 +315,9 @@ static NSString *const XctestRunExtension = @"xctestrun";
failFuture];
}
- (NSSet<id<FBXCTestDescriptor>> *)listTestDescriptorsWithError:(NSError **)error
- (NSArray<id<FBXCTestDescriptor>> *)listTestDescriptorsWithError:(NSError **)error
{
NSMutableSet<id<FBXCTestDescriptor>> *testDescriptors = [[NSMutableSet alloc] init];
NSMutableArray<id<FBXCTestDescriptor>> *testDescriptors = [[NSMutableArray alloc] init];
// Get xctest bundles
NSSet<NSURL *> *testURLS = [self listTestBundlesWithError:error];
@ -346,12 +358,12 @@ static NSString *const XctestRunExtension = @"xctestrun";
[testDescriptors addObjectsFromArray:descriptors];
}
return testDescriptors;
return [NSArray arrayWithArray:testDescriptors];
}
- (id<FBXCTestDescriptor>)testDescriptorWithID:(NSString *)bundleId error:(NSError **)error
{
NSSet<id<FBXCTestDescriptor>> *testDescriptors = [self listTestDescriptorsWithError:error];
NSArray<id<FBXCTestDescriptor>> *testDescriptors = [self listTestDescriptorsWithError:error];
for (id<FBXCTestDescriptor> testDescriptor in testDescriptors) {
if ([[testDescriptor testBundleID] isEqualToString: bundleId]) {
return testDescriptor;
@ -403,7 +415,7 @@ static NSString *const XctestRunExtension = @"xctestrun";
- (id<FBXCTestDescriptor>)testDescriptorWithURL:(NSURL *)url error:(NSError **)error
{
NSSet<id<FBXCTestDescriptor>> *testDescriptors = [self listTestDescriptorsWithError:error];
NSArray<id<FBXCTestDescriptor>> *testDescriptors = [self listTestDescriptorsWithError:error];
for (id<FBXCTestDescriptor> testDescriptor in testDescriptors) {
if ([[[testDescriptor url] absoluteString] isEqualToString:[url absoluteString]]) {
return testDescriptor;
@ -497,7 +509,7 @@ static NSString *const XctestRunExtension = @"xctestrun";
return [[FBXCodebuildTestRunDescriptor alloc] initWithURL:xctestrunURL name:testTarget testBundle:testBundle testHostBundle:testHostBundle];
}
- (FBFuture<FBInstalledArtifact *> *)saveTestBundle:(NSURL *)testBundleURL
- (FBFuture<FBInstalledArtifact *> *)saveTestBundle:(NSURL *)testBundleURL skipSigningBundles:(BOOL)skipSigningBundles
{
// Test Bundles don't always have a bundle id, so fallback to another name if it's not there.
NSError *error = nil;
@ -505,7 +517,7 @@ static NSString *const XctestRunExtension = @"xctestrun";
if (!bundle) {
return [FBFuture futureWithError:error];
}
return [self saveBundle:bundle];
return [self saveBundle:bundle skipSigningBundles:skipSigningBundles];
}
- (FBFuture<FBInstalledArtifact *> *)saveTestRun:(NSURL *)XCTestRunURL

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -26,9 +26,8 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
if (processLogDirectory) {
FBXCTestLogger *mirrorLogger = [FBXCTestLogger defaultLoggerInDirectory:processLogDirectory];
NSUUID *udid = NSUUID.UUID;
stdOutFuture = [mirrorLogger logConsumptionToFile:stdOutConsumer outputKind:@"out" udid:udid logger:logger];
stdErrFuture = [mirrorLogger logConsumptionToFile:stdErrConsumer outputKind:@"err" udid:udid logger:logger];
stdOutFuture = [mirrorLogger logConsumptionOf:stdOutConsumer toFileNamed:@"test_process_stdout.out" logger:logger];
stdErrFuture = [mirrorLogger logConsumptionOf:stdErrConsumer toFileNamed:@"test_process_stderr.err" logger:logger];
}
return [[FBFuture
@ -141,7 +140,7 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
return [FBFuture futureWithResult:[[FBTestApplicationsPair alloc] initWithApplicationUnderTest:nil testHostApp:nil]];
}
if (request.isUITest) {
if (!request.appBundleID) {
if (!request.testTargetAppBundleID) {
return [[FBIDBError
describe:@"Request for UI Test, but no app_bundle_id provided"]
failFuture];
@ -149,14 +148,15 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
NSString *testHostBundleID = request.testHostAppBundleID ?: @"com.apple.Preferences";
return [[FBFuture
futureWithFutures:@[
[target installedApplicationWithBundleID:request.appBundleID],
[target installedApplicationWithBundleID:request.testTargetAppBundleID],
[target installedApplicationWithBundleID:testHostBundleID],
]]
onQueue:target.asyncQueue map:^(NSArray<FBInstalledApplication *> *applications) {
return [[FBTestApplicationsPair alloc] initWithApplicationUnderTest:applications[0] testHostApp:applications[1]];
}];
}
NSString *bundleID = request.testHostAppBundleID ?: request.appBundleID;
// it's an App Test then
NSString *bundleID = request.testHostAppBundleID;
if (!bundleID) {
return [[FBIDBError
describe:@"Request for Application Test, but no app_bundle_id or test_host_app_bundle_id provided"]
@ -171,19 +171,13 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
- (FBFuture<FBIDBAppHostedTestConfiguration *> *)testConfigWithRunRequest:(FBXCTestRunRequest *)request testApps:(FBTestApplicationsPair *)testApps logDirectoryPath:(NSString *)logDirectoryPath logger:(id<FBControlCoreLogger>)logger queue:(dispatch_queue_t)queue
{
BOOL uiTesting = NO;
FBFuture<FBApplicationLaunchConfiguration *> *appLaunchConfigFuture = nil;
if (request.isUITest) {
appLaunchConfigFuture = BuildAppLaunchConfig(testApps.testHostApp.bundle.identifier, request.environment, request.arguments, logger, logDirectoryPath, request.waitForDebugger, queue);
uiTesting = YES;
} else {
appLaunchConfigFuture = BuildAppLaunchConfig(request.appBundleID, request.environment, request.arguments, logger, logDirectoryPath, request.waitForDebugger, queue);
}
appLaunchConfigFuture = BuildAppLaunchConfig(testApps.testHostApp.bundle.identifier, request.environment, request.arguments, logger, logDirectoryPath, request.waitForDebugger, queue);
FBCodeCoverageConfiguration *coverageConfig = nil;
if (request.coverageRequest.collect) {
NSString *coverageDirName =[NSString stringWithFormat:@"coverage_%@", NSUUID.UUID.UUIDString];
NSString *coverageDirPath = [self.targetAuxillaryDirectory stringByAppendingPathComponent:coverageDirName];
coverageConfig = [[FBCodeCoverageConfiguration alloc] initWithDirectory:coverageDirPath format:request.coverageRequest.format];
coverageConfig = [[FBCodeCoverageConfiguration alloc] initWithDirectory:coverageDirPath format:request.coverageRequest.format enableContinuousCoverageCollection:request.coverageRequest.shouldEnableContinuousCoverageCollection];
}
return [appLaunchConfigFuture onQueue:queue map:^ FBIDBAppHostedTestConfiguration * (FBApplicationLaunchConfiguration *applicationLaunchConfiguration) {
@ -192,7 +186,7 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
applicationLaunchConfiguration:applicationLaunchConfiguration
testHostBundle:testApps.testHostApp.bundle
timeout:(request.testTimeout ? request.testTimeout.doubleValue : 0)
initializeUITesting:uiTesting
initializeUITesting:request.isUITest
useXcodebuild:NO
testsToRun:request.testsToRun
testsToSkip:request.testsToSkip
@ -201,7 +195,9 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
resultBundlePath:nil
reportActivities:request.reportActivities
coverageDirectoryPath:coverageConfig.coverageDirectory
logDirectoryPath:logDirectoryPath];
enableContinuousCoverageCollection:coverageConfig.shouldEnableContinuousCoverageCollection
logDirectoryPath:logDirectoryPath
reportResultBundle:request.collectResultBundle];
return [[FBIDBAppHostedTestConfiguration alloc] initWithTestLaunchConfiguration:testLaunchConfig coverageConfiguration:coverageConfig];
}];
}
@ -278,26 +274,35 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
if (!properties) {
return [FBFuture futureWithError:error];
}
return [BuildAppLaunchConfig(request.appBundleID, request.environment, request.arguments, logger, nil, request.waitForDebugger, queue)
onQueue:queue map:^ FBIDBAppHostedTestConfiguration * (FBApplicationLaunchConfiguration *launchConfig) {
FBTestLaunchConfiguration *testLaunchConfiguration = [[FBTestLaunchConfiguration alloc]
initWithTestBundle:self.testBundle
applicationLaunchConfiguration:launchConfig
testHostBundle:self.testHostBundle
timeout:0
initializeUITesting:request.isUITest
useXcodebuild:YES
testsToRun:request.testsToRun
testsToSkip:request.testsToSkip
targetApplicationBundle:nil
xcTestRunProperties:properties
resultBundlePath:resultBundlePath
reportActivities:request.reportActivities
coverageDirectoryPath:nil
logDirectoryPath:logDirectoryPath];
return [[FBIDBAppHostedTestConfiguration alloc] initWithTestLaunchConfiguration:testLaunchConfiguration coverageConfiguration:nil];
}];
FBApplicationLaunchConfiguration *launchConfig = [[FBApplicationLaunchConfiguration alloc]
initWithBundleID:@"not.used.bundleId"
bundleName:nil
arguments:request.arguments ?: @[]
environment:request.environment ?: @{}
waitForDebugger:request.waitForDebugger
io:[[FBProcessIO alloc] initWithStdIn:nil stdOut:nil stdErr:nil]
launchMode:FBApplicationLaunchModeFailIfRunning];
FBTestLaunchConfiguration *testLaunchConfiguration = [[FBTestLaunchConfiguration alloc]
initWithTestBundle:self.testBundle
applicationLaunchConfiguration:launchConfig
testHostBundle:self.testHostBundle
timeout:0
initializeUITesting:request.isUITest
useXcodebuild:YES
testsToRun:request.testsToRun
testsToSkip:request.testsToSkip
targetApplicationBundle:nil
xcTestRunProperties:properties
resultBundlePath:resultBundlePath
reportActivities:request.reportActivities
coverageDirectoryPath:nil
enableContinuousCoverageCollection:NO
logDirectoryPath:logDirectoryPath
reportResultBundle:request.collectResultBundle];
return [FBFuture futureWithResult:[[FBIDBAppHostedTestConfiguration alloc] initWithTestLaunchConfiguration:testLaunchConfiguration coverageConfiguration:nil]];
}

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -41,7 +41,12 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign, readonly) BOOL reportAttachments;
- (instancetype)initWithResultBundlePath:(nullable NSString *)resultBundlePath coverageConfiguration:(nullable FBCodeCoverageConfiguration *)coverageConfiguration logDirectoryPath:(nullable NSString *)logDirectoryPath binariesPaths:(nullable NSArray<NSString *> *)binariesPaths reportAttachments:(BOOL)reportAttachments;
/**
Whether to report return result bundle or not.
*/
@property (nonatomic, assign, readonly) BOOL reportResultBundle;
- (instancetype)initWithResultBundlePath:(nullable NSString *)resultBundlePath coverageConfiguration:(nullable FBCodeCoverageConfiguration *)coverageConfiguration logDirectoryPath:(nullable NSString *)logDirectoryPath binariesPaths:(nullable NSArray<NSString *> *)binariesPaths reportAttachments:(BOOL)reportAttachments reportResultBundle:(BOOL)reportResultBundle;
@end

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -12,7 +12,7 @@
@implementation FBXCTestReporterConfiguration
- (instancetype)initWithResultBundlePath:(nullable NSString *)resultBundlePath coverageConfiguration:(nullable FBCodeCoverageConfiguration *)coverageConfiguration logDirectoryPath:(nullable NSString *)logDirectoryPath binariesPaths:(nullable NSArray<NSString *> *)binariesPaths reportAttachments:(BOOL)reportAttachments
- (instancetype)initWithResultBundlePath:(nullable NSString *)resultBundlePath coverageConfiguration:(nullable FBCodeCoverageConfiguration *)coverageConfiguration logDirectoryPath:(nullable NSString *)logDirectoryPath binariesPaths:(nullable NSArray<NSString *> *)binariesPaths reportAttachments:(BOOL)reportAttachments reportResultBundle:(BOOL)reportResultBundle
{
self = [super init];
if (!self) {
@ -24,13 +24,14 @@
_logDirectoryPath = logDirectoryPath;
_binariesPaths = binariesPaths;
_reportAttachments = reportAttachments;
_reportResultBundle = reportResultBundle;
return self;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"Result Bundle %@ | Coverage %@ | Log Path %@ | Binaries Paths %@ | Report Attachments %d", self.resultBundlePath, self.coverageConfiguration, self.logDirectoryPath, [FBCollectionInformation oneLineDescriptionFromArray:self.binariesPaths], self.reportAttachments];
return [NSString stringWithFormat:@"Result Bundle %@ | Coverage %@ | Log Path %@ | Binaries Paths %@ | Report Attachments %d | Report Restul Bundle %d", self.resultBundlePath, self.coverageConfiguration, self.logDirectoryPath, [FBCollectionInformation oneLineDescriptionFromArray:self.binariesPaths], self.reportAttachments, self.reportResultBundle];
}
@end

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -28,16 +28,17 @@
describeFormat:@"IDB app storage folder does not exist at: %@", idbAppStoragePath]
fail:error];
}
// dictionaryWithContentsOfURL:error: is only available in NSDictionary not in NSMutableDictionary
NSMutableDictionary<NSString *, id> *xctestrunContents = [[NSDictionary dictionaryWithContentsOfURL:xctestrunURL error:error] mutableCopy];
NSDictionary<NSString *, id> *xctestrunContents = [NSDictionary dictionaryWithContentsOfURL:xctestrunURL error:error];
NSMutableDictionary<NSString *, id> *mutableContents = [NSMutableDictionary dictionaryWithCapacity:xctestrunContents.count];
if (!xctestrunContents) {
return nil;
}
for (NSString *testTarget in xctestrunContents) {
if ([testTarget isEqualToString:@"__xctestrun_metadata__"] || [testTarget isEqualToString:@"CodeCoverageBuildableInfos"]) {
for (NSString *contentKey in xctestrunContents) {
if ([contentKey isEqualToString:@"__xctestrun_metadata__"] || [contentKey isEqualToString:@"CodeCoverageBuildableInfos"]) {
[mutableContents setObject:xctestrunContents[contentKey] forKey:contentKey];
continue;
}
NSMutableDictionary<NSString *, id> *testTargetProperties = [[xctestrunContents objectForKey:testTarget] mutableCopy];
NSMutableDictionary<NSString *, id> *testTargetProperties = [[xctestrunContents objectForKey:contentKey] mutableCopy];
// Expand __TESTROOT__ and __IDB_APPSTORAGE__ in TestHostPath
NSString *testHostPath = [testTargetProperties objectForKey:@"TestHostPath"];
if (testHostPath != nil) {
@ -56,11 +57,22 @@
NSString *targetAppPath = [testTargetProperties objectForKey:@"UITargetAppPath"];
if (targetAppPath != nil) {
targetAppPath = [targetAppPath stringByReplacingOccurrencesOfString:@"__IDB_APPSTORAGE__" withString:idbAppStoragePath];
targetAppPath = [targetAppPath stringByReplacingOccurrencesOfString:@"__TESTROOT__" withString:testRoot];
[testTargetProperties setObject:targetAppPath forKey:@"UITargetAppPath"];
}
[xctestrunContents setObject:testTargetProperties forKey:testTarget];
NSArray<NSString *> *dependencies = [testTargetProperties objectForKey:@"DependentProductPaths"];
if (dependencies && dependencies.count) {
NSMutableArray<NSString *> *expandedDeps = [NSMutableArray arrayWithCapacity:dependencies.count];
for (NSString *dep in dependencies) {
NSString *absPath = [dep stringByReplacingOccurrencesOfString:@"__IDB_APPSTORAGE__" withString:idbAppStoragePath];
absPath = [absPath stringByReplacingOccurrencesOfString:@"__TESTROOT__" withString:testRoot];
[expandedDeps addObject:absPath];
}
[testTargetProperties setObject:[NSArray arrayWithArray:expandedDeps] forKey:@"DependentProductPaths"];
}
[mutableContents setObject:testTargetProperties forKey:contentKey];
}
return xctestrunContents;
return [NSDictionary dictionaryWithDictionary:mutableContents];
}
@end

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -10,3 +10,5 @@ DYLIB_INSTALL_NAME_BASE = @rpath
FRAMEWORK_VERSION = A
VERSIONING_SYSTEM = apple-generic
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path @loader_path/Frameworks
// Only expose PrivateHeaders to Framework targets. Framework targets never expose PrivateHeader imports themselves.
HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)/PrivateHeaders

Просмотреть файл

@ -1,5 +1,5 @@
// Warnings & Errors
CLANG_CXX_LIBRARY = "libc++"
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES
CLANG_WARN_BOOL_CONVERSION = YES
CLANG_WARN_COMMA = YES
@ -8,6 +8,7 @@ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR
CLANG_WARN_DOCUMENTATION_COMMENTS = YES
CLANG_WARN_DUPLICATE_METHOD_MATCH = YES
CLANG_WARN_DUPLICATE_METHOD_MATCH = YES
CLANG_WARN_EMPTY_BODY = YES
CLANG_WARN_ENUM_CONVERSION = YES
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES
@ -17,17 +18,12 @@ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES
CLANG_WARN_STRICT_PROTOTYPES = YES
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES
CLANG_WARN_SUSPICIOUS_MOVE = YES
CLANG_WARN_UNREACHABLE_CODE = YES
CLANG_WARN_DUPLICATE_METHOD_MATCH = YES
COMBINE_HIDPI_IMAGES = YES
DEBUG_INFORMATION_FORMAT = dwarf
ENABLE_STRICT_OBJC_MSGSEND = YES
ENABLE_TESTABILITY = YES
GCC_NO_COMMON_BLOCKS = YES
GCC_OPTIMIZATION_LEVEL = 0
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES
@ -53,24 +49,12 @@ GCC_WARN_UNUSED_FUNCTION = YES
GCC_WARN_UNUSED_LABEL = YES
GCC_WARN_UNUSED_VALUE = YES
GCC_WARN_UNUSED_VARIABLE = YES
ONLY_ACTIVE_ARCH = YES
// Inferred Directories. Important for the linker to be able to find and weak-link private symbols.
APPLE_FRAMEWORKS_DIR = $(DEVELOPER_DIR)/../Frameworks
APPLE_OTHER_FRAMEWORKS_DIR = $(DEVELOPER_DIR)/../OtherFrameworks
APPLE_PLUGINS_DIR = $(DEVELOPER_DIR)/../PlugIns
APPLE_SHARED_FRAMEWORKS_DIR = $(DEVELOPER_DIR)/../SharedFrameworks
// Search Paths for Frameworks & Headers
ALWAYS_SEARCH_USER_PATHS = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) $(APPLE_FRAMEWORKS_DIR) $(APPLE_PLUGINS_DIR) $(APPLE_OTHER_FRAMEWORKS_DIR) $(APPLE_SHARED_FRAMEWORKS_DIR) $(DEVELOPER_LIBRARY_DIR)/PrivateFrameworks $(SDKROOT)/System/Library/PrivateFrameworks /Library/Developer/PrivateFrameworks
HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)/PrivateHeaders
// SDK & OS Versions
MACOSX_DEPLOYMENT_TARGET = 10.14
SDKROOT = macosx
// Language Standards & Features
// Compile-Time Settings
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"
CLANG_CXX_LIBRARY = "libc++"
CLANG_ENABLE_MODULES = YES
@ -83,10 +67,8 @@ GCC_SYMBOLS_PRIVATE_EXTERN = NO
// Preprocessor
ENABLE_NS_ASSERTIONS = YES
ENABLE_STRICT_OBJC_MSGSEND = YES
ENABLE_TESTABILITY = YES
// Deployment
COMBINE_HIDPI_IMAGES = YES
COPY_PHASE_STRIP = NO
INSTALL_PATH = @rpath
SKIP_INSTALL = YES

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -23,17 +23,6 @@ typedef NS_ENUM(NSUInteger, FBApplicationInstallType) {
FBApplicationInstallTypeUserDevelopment = 5, /** The Application has been installed by the user and signed with a development certificate */
};
/**
String Representations of the Installed Type.
*/
typedef NSString *FBApplicationInstallTypeString NS_STRING_ENUM;
extern FBApplicationInstallTypeString const FBApplicationInstallTypeStringUnknown;
extern FBApplicationInstallTypeString const FBApplicationInstallTypeStringSystem;
extern FBApplicationInstallTypeString const FBApplicationInstallTypeStringMac;
extern FBApplicationInstallTypeString const FBApplicationInstallTypeStringUser;
extern FBApplicationInstallTypeString const FBApplicationInstallTypeStringUserEnterprise;
extern FBApplicationInstallTypeString const FBApplicationInstallTypeStringUserDevelopment;
/**
Keys from UserInfo about Applications
*/
@ -61,6 +50,16 @@ extern FBApplicationInstallInfoKey const FBApplicationInstallInfoKeySignerIdenti
*/
+ (instancetype)installedApplicationWithBundle:(FBBundleDescriptor *)bundle installType:(FBApplicationInstallType)installType dataContainer:(nullable NSString *)dataContainer;
/**
The Designated Initializer.
@param bundle the Application Bundle. This represents the bundle as-installed on the target, rather than pre-install.
@param installTypeString the string representation of the install type.
@param dataContainer the Data Container Path, may be nil.
@return a new Installed Application Instance.
*/
+ (instancetype)installedApplicationWithBundle:(FBBundleDescriptor *)bundle installTypeString:(nullable NSString *)installTypeString signerIdentity:(nullable NSString *)signerIdentity dataContainer:(nullable NSString *)dataContainer;
#pragma mark Properties
/**
@ -73,30 +72,16 @@ extern FBApplicationInstallInfoKey const FBApplicationInstallInfoKeySignerIdenti
*/
@property (nonatomic, assign, readonly) FBApplicationInstallType installType;
/**
The "Install Type" enum of the Application, represented as a string.
*/
@property (nonatomic, copy, readonly) NSString *installTypeString;
/**
The data container path of the Application.
*/
@property (nonatomic, copy, nullable, readonly) NSString *dataContainer;
#pragma mark Install Type
/**
Returns a String Represnting the Application Install Type.
@param installType the install type enum.
@return a string of the install type.
*/
+ (FBApplicationInstallTypeString)stringFromApplicationInstallType:(FBApplicationInstallType)installType;
/**
Returns the FBApplicationInstallType from the string representation.
@param installTypeString install type as a string
@param signerIdentity the signer identity.
@return an FBApplicationInstallType
*/
+ (FBApplicationInstallType)installTypeFromString:(nullable FBApplicationInstallTypeString)installTypeString signerIdentity:(nullable NSString *)signerIdentity;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -9,12 +9,12 @@
#import "FBBundleDescriptor.h"
FBApplicationInstallTypeString const FBApplicationInstallTypeStringUnknown = @"unknown";
FBApplicationInstallTypeString const FBApplicationInstallTypeStringSystem = @"system";
FBApplicationInstallTypeString const FBApplicationInstallTypeStringMac = @"mac";
FBApplicationInstallTypeString const FBApplicationInstallTypeStringUser = @"user";
FBApplicationInstallTypeString const FBApplicationInstallTypeStringUserEnterprise = @"user_enterprise";
FBApplicationInstallTypeString const FBApplicationInstallTypeStringUserDevelopment = @"user_development";
static NSString *const FBApplicationInstallTypeStringUnknown = @"unknown";
static NSString *const FBApplicationInstallTypeStringSystem = @"system";
static NSString *const FBApplicationInstallTypeStringMac = @"mac";
static NSString *const FBApplicationInstallTypeStringUser = @"user";
static NSString *const FBApplicationInstallTypeStringUserEnterprise = @"user_enterprise";
static NSString *const FBApplicationInstallTypeStringUserDevelopment = @"user_development";
FBApplicationInstallInfoKey const FBApplicationInstallInfoKeyApplicationType = @"ApplicationType";
FBApplicationInstallInfoKey const FBApplicationInstallInfoKeyBundleIdentifier = @"CFBundleIdentifier";
@ -31,6 +31,11 @@ FBApplicationInstallInfoKey const FBApplicationInstallInfoKeySignerIdentity = @"
return [[self alloc] initWithBundle:bundle installType:installType dataContainer:dataContainer];
}
+ (instancetype)installedApplicationWithBundle:(FBBundleDescriptor *)bundle installTypeString:(NSString *)installTypeString signerIdentity:(NSString *)signerIdentity dataContainer:(NSString *)dataContainer
{
return [self installedApplicationWithBundle:bundle installType:[self installTypeFromString:installTypeString signerIdentity:signerIdentity] dataContainer:dataContainer];
}
- (instancetype)initWithBundle:(FBBundleDescriptor *)bundle installType:(FBApplicationInstallType)installType dataContainer:(NSString *)dataContainer
{
self = [super init];
@ -67,7 +72,7 @@ FBApplicationInstallInfoKey const FBApplicationInstallInfoKeySignerIdentity = @"
return [NSString stringWithFormat:
@"Bundle %@ | Install Type %@ | Container %@",
self.bundle.description,
[FBInstalledApplication stringFromApplicationInstallType:self.installType],
self.installTypeString,
self.dataContainer
];
}
@ -79,9 +84,16 @@ FBApplicationInstallInfoKey const FBApplicationInstallInfoKeySignerIdentity = @"
return self;
}
#pragma mark Properties
- (NSString *)installTypeString
{
return [FBInstalledApplication stringFromApplicationInstallType:self.installType];
}
#pragma mark Install Type
+ (FBApplicationInstallTypeString)stringFromApplicationInstallType:(FBApplicationInstallType)installType
+ (NSString *)stringFromApplicationInstallType:(FBApplicationInstallType)installType
{
switch (installType) {
case FBApplicationInstallTypeUser:
@ -99,7 +111,7 @@ FBApplicationInstallInfoKey const FBApplicationInstallInfoKeySignerIdentity = @"
}
}
+ (FBApplicationInstallType)installTypeFromString:(nullable FBApplicationInstallTypeString)installTypeString signerIdentity:(nullable NSString *)signerIdentity
+ (FBApplicationInstallType)installTypeFromString:(nullable NSString *)installTypeString signerIdentity:(nullable NSString *)signerIdentity
{
if (!installTypeString) {
return FBApplicationInstallTypeUnknown;

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -91,7 +91,7 @@ static NSString *const KeyIsAwaiting = @"FBCONTROLCORE_IS_AWAITING";
static NSTimeInterval const ForeverTimeout = DBL_MAX;
static dispatch_queue_t blockQueue()
static dispatch_queue_t blockQueue(void)
{
return dispatch_queue_create("com.facebook.fbfuture.block", DISPATCH_QUEUE_SERIAL);
}

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -135,7 +135,7 @@ extern dispatch_time_t FBCreateDispatchTimeFromDuration(NSTimeInterval inDuratio
@param futures the futures to compose.
@return a new future with the resolved results of all the composed futures.
*/
+ (FBFuture<NSArray<T> *> *)futureWithFutures:(NSArray<FBFuture<T> *> *)futures;
+ (FBFuture<NSArray<T> *> *)futureWithFutures:(NSArray<FBFuture<T> *> *)futures NS_SWIFT_UNAVAILABLE("Use BridgeFuture.values instead");
/**
Constructrs a Future from an Array of Futures.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
@param nestedFormat if YES then data is returned in the nested format, NO for flat format
@return the accessibility elements for the main screen, wrapped in a Future.
*/
- (FBFuture<NSArray<NSDictionary<NSString *, id> *> *> *)accessibilityElementsWithNestedFormat:(BOOL)nestedFormat;
- (FBFuture<id> *)accessibilityElementsWithNestedFormat:(BOOL)nestedFormat;
/**
Obtain the acessibility element for the main screen at the given point.
@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
@param nestedFormat if YES then data is returned in the nested format, NO for flat format
@return the accessibility element at the provided point, wrapped in a Future.
*/
- (FBFuture<NSDictionary<NSString *, id> *> *)accessibilityElementAtPoint:(CGPoint)point nestedFormat:(BOOL)nestedFormat;
- (FBFuture<id> *)accessibilityElementAtPoint:(CGPoint)point nestedFormat:(BOOL)nestedFormat;
@end

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -98,6 +98,13 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (FBFutureContext<id<FBFileContainer>> *)fileCommandsForDiskImages;
/**
Returns a file container for manipulating device symbols.
@return a Future context that resolves with an implementation of the file container.
*/
- (FBFutureContext<id<FBFileContainer>> *)fileCommandsForSymbols;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -26,6 +26,7 @@ extern FBFileContainerKind const FBFileContainerKindMedia;
extern FBFileContainerKind const FBFileContainerKindProvisioningProfiles;
extern FBFileContainerKind const FBFileContainerKindRoot;
extern FBFileContainerKind const FBFileContainerKindSpringboardIcons;
extern FBFileContainerKind const FBFileContainerKindSymbols;
extern FBFileContainerKind const FBFileContainerKindWallpaper;
@protocol FBDataConsumer;
@ -45,25 +46,25 @@ extern FBFileContainerKind const FBFileContainerKindWallpaper;
@param destinationPath the destination path to copy to, relative to the root of the container.
@return A future that resolves when successful.
*/
- (FBFuture<NSNull *> *)copyFromHost:(NSURL *)sourcePath toContainer:(NSString *)destinationPath;
- (FBFuture<NSNull *> *)copyFromHost:(NSString *)sourcePath toContainer:(NSString *)destinationPath;
/**
Copy a path from inside the container, to the host.
@param containerPath the source path, relative to the root of the container. May be Files and/or Directories.
@param sourcePath the source path, relative to the root of the container. May be Files and/or Directories.
@param destinationPath the destination path on the host.
@return A future that resolves with the destination path when successful.
*/
- (FBFuture<NSString *> *)copyFromContainer:(NSString *)containerPath toHost:(NSString *)destinationPath;
- (FBFuture<NSString *> *)copyFromContainer:(NSString *)sourcePath toHost:(NSString *)destinationPath;
/**
Tails a path from inside the container, to a consumer.
Tails the contents of a file path inside the container, to a data consumer.
@param containerPath the source path to tail, relative to the root of the container. Must be a file
@param path the source path to tail, relative to the root of the container. Must be a file
@param consumer the consumer to write to.
@return a Future that resolves with a Future when the tailing has completed. The wrapped future can be cancelled to end the tailing operation.
*/
- (FBFuture<FBFuture<NSNull *> *> *)tail:(NSString *)containerPath toConsumer:(id<FBDataConsumer>)consumer;
- (FBFuture<FBFuture<NSNull *> *> *)tail:(NSString *)path toConsumer:(id<FBDataConsumer>)consumer;
/**
Create a directory inside the container.
@ -100,6 +101,95 @@ extern FBFileContainerKind const FBFileContainerKindWallpaper;
@end
/**
An abstraction over a file. The file can be local to the host, or remote.
*/
@protocol FBContainedFile <NSObject>
/**
Removes a file path.
If the path is a directory, the entire directory is removed and all contents, recursively.
@param error an error out for any error that occurs.
@return YES on success, NO otherwise.
*/
- (BOOL)removeItemWithError:(NSError **)error;
/**
List the contents of a path.
@param error an error out for any error that occurs.
@return an array of strings representing the directory contents.
*/
- (nullable NSArray<NSString *> *)contentsOfDirectoryWithError:(NSError **)error;
/**
Obtains the contents of the contained file.
@param error an error out for any error that occurs.
@return Data for the file, or an nil on error.
*/
- (nullable NSData *)contentsOfFileWithError:(NSError **)error;
/**
Creates a directory at the given path.
@param error an error out for any error that occurs.
@return YES on success, NO otherwise.
*/
- (BOOL)createDirectoryWithError:(NSError **)error;
/**
Checks whether the path exists, optionally providing information about whether the path is a regular file or directory.
@param isDirectoryOut an outparam for indicating if the path represents a directory.
@return YES if exists, NO otherwise.
*/
- (BOOL)fileExistsIsDirectory:(BOOL *)isDirectoryOut;
/**
Moves the receiver to the provided destination file.
@param destination the destination to move to.
@param error an error out for any error that occurs.
*/
- (BOOL)moveTo:(id<FBContainedFile>)destination error:(NSError **)error;
/**
Replaces the contents of the wrapped file with the provided path on the host filesystem.
@param path the path to pull contents from.
@param error an error out for any error that occurs.
@return YES if successful, NO otherwise.
*/
- (BOOL)populateWithContentsOfHostPath:(NSString *)path error:(NSError **)error;
/**
Replaces provided path on the host filesystem with the contents of the wrapped file.
@param path the path to push contents to.
@param error an error out for any error that occurs.
@return YES if successful, NO otherwise.
*/
- (BOOL)populateHostPathWithContents:(NSString *)path error:(NSError **)error;
/**
Constructs a new contained file by appending a path component.
@param component the component to add.
@param error an error out if the path is invalid.
@return the new contained file.
*/
- (id<FBContainedFile>)fileByAppendingPathComponent:(NSString *)component error:(NSError **)error;
/**
The host path corresponding to this file, if any.
If the file is remote, this will be nil
*/
@property (nonatomic, copy, nullable, readonly) NSString *pathOnHostFileSystem;
@end
/**
Implementations of File Commands.
*/

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -22,8 +22,439 @@ FBFileContainerKind const FBFileContainerKindMedia = @"media";
FBFileContainerKind const FBFileContainerKindProvisioningProfiles = @"provisioning_profiles";
FBFileContainerKind const FBFileContainerKindRoot = @"root";
FBFileContainerKind const FBFileContainerKindSpringboardIcons = @"springboard_icons";
FBFileContainerKind const FBFileContainerKindSymbols = @"symbols";
FBFileContainerKind const FBFileContainerKindWallpaper = @"wallpaper";
@interface FBContainedFile_Host : NSObject <FBContainedFile>
@property (nonatomic, strong, readonly) NSFileManager *fileManager;
@property (nonatomic, copy, readonly) NSString *path;
@end
@implementation FBContainedFile_Host
#pragma mark Initializers
- (instancetype)initWithFileManager:(NSFileManager *)fileManager path:(NSString *)path
{
self = [super init];
if (!self) {
return nil;
}
_fileManager = fileManager;
_path = path;
return self;
}
#pragma mark FBContainedFile
- (BOOL)removeItemWithError:(NSError **)error
{
return [self.fileManager removeItemAtPath:self.path error:error];
}
- (NSArray<NSString *> *)contentsOfDirectoryWithError:(NSError **)error
{
return [self.fileManager contentsOfDirectoryAtPath:self.path error:error];
}
- (NSData *)contentsOfFileWithError:(NSError **)error
{
return [NSData dataWithContentsOfFile:self.path options:0 error:error];
}
- (BOOL)moveTo:(id<FBContainedFile>)destination error:(NSError **)error
{
if (![destination isKindOfClass:FBContainedFile_Host.class]) {
return [[FBControlCoreError
describeFormat:@"Cannot move to %@, it is not on the host filesystem", destination]
failBool:error];
}
FBContainedFile_Host *hostDestination = (FBContainedFile_Host *) destination;
return [self.fileManager moveItemAtPath:self.path toPath:hostDestination.path error:error];
}
- (BOOL)createDirectoryWithError:(NSError **)error
{
return [self.fileManager createDirectoryAtPath:self.path withIntermediateDirectories:YES attributes:nil error:error];
}
- (BOOL)fileExistsIsDirectory:(BOOL *)isDirectoryOut
{
return [self.fileManager fileExistsAtPath:self.path isDirectory:isDirectoryOut];
}
- (BOOL)populateWithContentsOfHostPath:(NSString *)path error:(NSError **)error
{
return [self.fileManager copyItemAtPath:path toPath:self.path error:error];
}
- (BOOL)populateHostPathWithContents:(NSString *)path error:(NSError **)error
{
return [self.fileManager copyItemAtPath:self.path toPath:path error:error];
}
- (id<FBContainedFile>)fileByAppendingPathComponent:(NSString *)component error:(NSError **)error
{
return [[FBContainedFile_Host alloc] initWithFileManager:self.fileManager path:[self.path stringByAppendingPathComponent:component]];
}
- (NSString *)pathOnHostFileSystem
{
return self.path;
}
#pragma mark NSObject
- (NSString *)description
{
return [NSString stringWithFormat:@"Host File %@", self.path];
}
@end
@interface FBContainedFile_Mapped_Host : NSObject <FBContainedFile>
@property (nonatomic, copy, readonly) NSDictionary<NSString *, NSString *> *mappingPaths;
@property (nonatomic, strong, readonly) NSFileManager *fileManager;
@end
@implementation FBContainedFile_Mapped_Host
- (instancetype)initWithMappingPaths:(NSDictionary<NSString *, NSString *> *)mappingPaths fileManager:(NSFileManager *)fileManager;
{
self = [super init];
if (!self) {
return nil;
}
_mappingPaths = mappingPaths;
_fileManager = fileManager;
return self;
}
#pragma mark FBContainedFile
- (BOOL)removeItemWithError:(NSError **)error
{
return [[FBControlCoreError
describeFormat:@"%@ does not operate on root virtual containers", NSStringFromSelector(_cmd)]
failBool:error];
}
- (NSArray<NSString *> *)contentsOfDirectoryWithError:(NSError **)error
{
return self.mappingPaths.allKeys;
}
- (BOOL)createDirectoryWithError:(NSError **)error
{
return [[FBControlCoreError
describeFormat:@"%@ does not operate on root virtual containers", NSStringFromSelector(_cmd)]
failBool:error];
}
- (NSData *)contentsOfFileWithError:(NSError **)error
{
return [[FBControlCoreError
describeFormat:@"%@ does not operate on root virtual containers", NSStringFromSelector(_cmd)]
fail:error];
}
- (BOOL)fileExistsIsDirectory:(BOOL *)isDirectoryOut
{
return NO;
}
- (BOOL)moveTo:(id<FBContainedFile>)destination error:(NSError **)error
{
return [[FBControlCoreError
describe:@"Moving files does not work on root virtual containers"]
failBool:error];
}
- (BOOL)populateWithContentsOfHostPath:(NSString *)path error:(NSError **)error
{
return [[FBControlCoreError
describeFormat:@"%@ does not operate on root virtual containers", NSStringFromSelector(_cmd)]
failBool:error];
}
- (BOOL)populateHostPathWithContents:(NSString *)path error:(NSError **)error
{
return [[FBControlCoreError
describeFormat:@"%@ does not operate on root virtual containers", NSStringFromSelector(_cmd)]
failBool:error];
}
- (id<FBContainedFile>)fileByAppendingPathComponent:(NSString *)component error:(NSError **)error
{
// If the provided path represents the root (the mapping itself), then there's nothing to map to.
NSArray<NSString *> *pathComponents = component.pathComponents;
if ([FBContainedFile_Mapped_Host isRootPathOfContainer:pathComponents]) {
return self;
}
NSString *firstComponent = pathComponents.firstObject;
NSString *nextPath = [FBContainedFile_Mapped_Host popFirstPathComponent:pathComponents];
NSString *mappedPath = self.mappingPaths[firstComponent];
if (!mappedPath) {
return [[FBControlCoreError
describeFormat:@"'%@' is not a valid root path out of %@", firstComponent, [FBCollectionInformation oneLineDescriptionFromArray:self.mappingPaths.allKeys]]
fail:error];
}
id<FBContainedFile> mapped = [[FBContainedFile_Host alloc] initWithFileManager:self.fileManager path:mappedPath];
return [mapped fileByAppendingPathComponent:nextPath error:error];
}
- (NSString *)pathOnHostFileSystem
{
return nil;
}
#pragma mark NSObject
- (NSString *)description
{
return [NSString stringWithFormat:@"Root mapping: %@", [FBCollectionInformation oneLineDescriptionFromArray:self.mappingPaths.allKeys]];
}
#pragma mark Private
+ (BOOL)isRootPathOfContainer:(NSArray<NSString *> *)pathComponents
{
// If no path components this must be the root
if (pathComponents.count == 0) {
return YES;
}
// The root is also signified by a query for the root of the container.
NSString *firstPath = pathComponents.firstObject;
if (pathComponents.count == 1 && ([firstPath isEqualToString:@"."] || [firstPath isEqualToString:@"/"])) {
return YES;
}
// Otherwise we can't be the root path
return NO;
}
+ (NSString *)popFirstPathComponent:(NSArray<NSString *> *)pathComponents
{
// Re-assemble the mapped path, discarding the re-mapped first path component.
BOOL isFirstPathComponent = YES;
NSString *next = @"";
for (NSString *pathComponent in pathComponents) {
if (isFirstPathComponent) {
isFirstPathComponent = NO;
continue;
}
next = [next stringByAppendingPathComponent:pathComponent];
}
return next;
}
@end
@interface FBContainedFile_ContainedRoot : NSObject <FBFileContainer>
@property (nonatomic, strong, readonly) dispatch_queue_t queue;
@property (nonatomic, strong, readonly) id<FBContainedFile> rootFile;
@end
@implementation FBContainedFile_ContainedRoot
- (instancetype)initWithRootFile:(id<FBContainedFile>)rootFile queue:(dispatch_queue_t)queue
{
self = [super init];
if (!self) {
return nil;
}
_rootFile = rootFile;
_queue = queue;
return self;
}
#pragma mark FBFileCommands
- (FBFuture<NSNull *> *)copyFromHost:(NSString *)sourcePath toContainer:(NSString *)destinationPath
{
return [[self
mapToContainedFile:destinationPath]
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (id<FBContainedFile> destination) {
// Attempt to delete first to overwrite
NSError *error;
destination = [destination fileByAppendingPathComponent:sourcePath.lastPathComponent error:&error];
if (!destination) {
return [FBFuture futureWithError:error];
}
[destination removeItemWithError:nil];
if (![destination populateWithContentsOfHostPath:sourcePath error:&error]) {
return [[[FBControlCoreError
describeFormat:@"Could not copy from %@ to %@: %@", sourcePath, destinationPath, error]
causedBy:error]
failFuture];
}
return FBFuture.empty;
}];
}
- (FBFuture<NSString *> *)copyFromContainer:(NSString *)sourcePath toHost:(NSString *)destinationPath
{
return [[self
mapToContainedFile:sourcePath]
onQueue:self.queue fmap:^ FBFuture<NSString *> * (id<FBContainedFile> source) {
BOOL sourceIsDirectory = NO;
if (![source fileExistsIsDirectory:&sourceIsDirectory]) {
return [[FBControlCoreError
describeFormat:@"Source path does not exist: %@", source]
failFuture];
}
NSString *dstPath = destinationPath;
if (!sourceIsDirectory) {
NSError *createDirectoryError;
if (![NSFileManager.defaultManager createDirectoryAtPath:dstPath withIntermediateDirectories:YES attributes:@{} error:&createDirectoryError]) {
return [[[FBControlCoreError
describeFormat:@"Could not create temporary directory: %@", createDirectoryError]
causedBy:createDirectoryError]
failFuture];
}
dstPath = [dstPath stringByAppendingPathComponent:[sourcePath lastPathComponent]];
}
// if it already exists at the destination path we should remove it before copying again
BOOL destinationIsDirectory = NO;
if ([NSFileManager.defaultManager fileExistsAtPath:dstPath isDirectory:&destinationIsDirectory]) {
NSError *removeError;
if (![NSFileManager.defaultManager removeItemAtPath:dstPath error:&removeError]) {
return [[[FBControlCoreError
describeFormat:@"Could not remove %@", dstPath]
causedBy:removeError]
failFuture];
}
}
NSError *copyError;
if (![source populateHostPathWithContents:dstPath error:&copyError]) {
return [[[FBControlCoreError
describeFormat:@"Could not copy from %@ to %@: %@", source, dstPath, copyError]
causedBy:copyError]
failFuture];
}
return [FBFuture futureWithResult:destinationPath];
}];
}
- (FBFuture<FBFuture<NSNull *> *> *)tail:(NSString *)path toConsumer:(id<FBDataConsumer>)consumer
{
return [[[self
mapToContainedFile:path]
onQueue:self.queue fmap:^ FBFuture<FBProcess<NSNull *, id<FBDataConsumer>, NSData *> *> * (id<FBContainedFile> fileToTail) {
NSString *pathOnHostFileSystem = fileToTail.pathOnHostFileSystem;
if (!pathOnHostFileSystem) {
return [[FBControlCoreError
describeFormat:@"Cannot tail %@, it is not on the local filesystem", fileToTail]
failFuture];
}
return [[[[FBProcessBuilder
withLaunchPath:@"/usr/bin/tail"]
withArguments:@[@"-c+1", @"-f", pathOnHostFileSystem]]
withStdOutConsumer:consumer]
start];
}]
onQueue:self.queue map:^(FBProcess *process) {
return [process.statLoc
onQueue:self.queue respondToCancellation:^{
return [process sendSignal:SIGTERM backingOffToKillWithTimeout:1 logger:nil];
}];
}];
}
- (FBFuture<NSNull *> *)createDirectory:(NSString *)directoryPath
{
return [[self
mapToContainedFile:directoryPath]
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (id<FBContainedFile> directory) {
NSError *error;
if (![directory createDirectoryWithError:&error]) {
return [[[FBControlCoreError
describeFormat:@"Could not create directory %@: %@", directory, error]
causedBy:error]
failFuture];
}
return FBFuture.empty;
}];
}
- (FBFuture<NSNull *> *)moveFrom:(NSString *)sourcePath to:(NSString *)destinationPath
{
return [[FBFuture
futureWithFutures:@[
[self mapToContainedFile:sourcePath],
[self mapToContainedFile:destinationPath],
]]
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (NSArray<id<FBContainedFile>> *providedFiles) {
// If the source and destination are on the same filesystem, they can be moved directly.
id<FBContainedFile> source = providedFiles[0];
id<FBContainedFile> destination = providedFiles[1];
NSError *error = nil;
if (![source moveTo:destination error:&error]) {
return [[[FBControlCoreError
describeFormat:@"Could not move item at %@ to %@: %@", source, destination, error]
causedBy:error]
failFuture];
}
return FBFuture.empty;
}];
}
- (FBFuture<NSNull *> *)remove:(NSString *)path
{
return [[self
mapToContainedFile:path]
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (id<FBContainedFile> file) {
NSError *error;
if (![file removeItemWithError:&error]) {
return [[[FBControlCoreError
describeFormat:@"Could not remove item at path %@: %@", file, error]
causedBy:error]
failFuture];
}
return FBFuture.empty;
}];
}
- (FBFuture<NSArray<NSString *> *> *)contentsOfDirectory:(NSString *)path
{
return [[self
mapToContainedFile:path]
onQueue:self.queue fmap:^(id<FBContainedFile> directory) {
NSError *error;
NSArray<NSString *> *contents = [directory contentsOfDirectoryWithError:&error];
if (!contents) {
return [FBFuture futureWithError:error];
}
return [FBFuture futureWithResult:contents];
}];
}
#pragma mark Private
- (FBFuture<id<FBContainedFile>> *)mapToContainedFile:(NSString *)path
{
NSError *error = nil;
id<FBContainedFile> file = [self.rootFile fileByAppendingPathComponent:path error:&error];
if (!file) {
return [FBFuture futureWithError:error];
}
return [FBFuture futureWithResult:file];
}
@end
@interface FBFileContainer_ProvisioningProfile : NSObject <FBFileContainer>
@property (nonatomic, strong, readonly) id<FBProvisioningProfileCommands> commands;
@ -48,12 +479,12 @@ FBFileContainerKind const FBFileContainerKindWallpaper = @"wallpaper";
#pragma mark FBFileContainer Implementation
- (FBFuture<NSNull *> *)copyFromHost:(NSURL *)path toContainer:(NSString *)destinationPath
- (FBFuture<NSNull *> *)copyFromHost:(NSString *)path toContainer:(NSString *)destinationPath
{
return [FBFuture
onQueue:self.queue resolve:^ FBFuture<NSNull *> * {
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:path options:0 error:&error];
NSData *data = [NSData dataWithContentsOfFile:path options:0 error:&error];
if (!data) {
return [FBFuture futureWithError:error];
}
@ -109,411 +540,6 @@ FBFileContainerKind const FBFileContainerKindWallpaper = @"wallpaper";
@end
@interface FBFileContainerBase : NSObject <FBFileContainer>
@property (nonatomic, strong, readonly) dispatch_queue_t queue;
@property (nonatomic, strong, readonly) NSFileManager *fileManager;
@end
@implementation FBFileContainerBase
- (instancetype)initWithQueue:(dispatch_queue_t)queue
{
self = [super init];
if (!self) {
return nil;
}
_queue = queue;
_fileManager = NSFileManager.defaultManager;
return self;
}
#pragma mark FBFileCommands
- (FBFuture<NSNull *> *)copyFromHost:(NSURL *)sourcePath toContainer:(NSString *)destinationPath
{
return [[self
mappedPath:destinationPath]
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (NSString *mappedPath) {
NSError *error;
NSString *destPath = [mappedPath stringByAppendingPathComponent:sourcePath.lastPathComponent];
// Attempt to delete first to overwrite
[self removeItemAtPath:destPath error:nil];
if (![self copyItemAtPath:sourcePath.path toPath:destPath error:&error]) {
return [[[FBControlCoreError
describeFormat:@"Could not copy from %@ to %@: %@", sourcePath, destPath, error]
causedBy:error]
failFuture];
}
return FBFuture.empty;
}];
}
- (FBFuture<NSString *> *)copyFromContainer:(NSString *)containerPath toHost:(NSString *)destinationPath
{
return [[self
mappedPath:containerPath]
onQueue:self.queue fmap:^ FBFuture<NSString *> * (NSString *source) {
BOOL srcIsDirecory = NO;
if (![self.fileManager fileExistsAtPath:source isDirectory:&srcIsDirecory]) {
return [[FBControlCoreError
describeFormat:@"Source path does not exist: %@", source]
failFuture];
}
NSString *dstPath = destinationPath;
if (!srcIsDirecory) {
NSError *createDirectoryError;
if (![self createDirectoryAtPath:dstPath withIntermediateDirectories:YES attributes:nil error:&createDirectoryError]) {
return [[[FBControlCoreError
describeFormat:@"Could not create temporary directory: %@", createDirectoryError]
causedBy:createDirectoryError]
failFuture];
}
dstPath = [dstPath stringByAppendingPathComponent:[source lastPathComponent]];
}
// if it already exists at the destination path we should remove it before copying again
if ([self.fileManager fileExistsAtPath:dstPath]) {
NSError *removeError;
if (![self removeItemAtPath:dstPath error:&removeError]) {
return [[[FBControlCoreError
describeFormat:@"Could not remove %@", dstPath]
causedBy:removeError]
failFuture];
}
}
NSError *copyError;
if (![self copyItemAtPath:source toPath:dstPath error:&copyError]) {
return [[[FBControlCoreError
describeFormat:@"Could not copy from %@ to %@: %@", source, dstPath, copyError]
causedBy:copyError]
failFuture];
}
return [FBFuture futureWithResult:destinationPath];
}];
}
- (FBFuture<FBFuture<NSNull *> *> *)tail:(NSString *)containerPath toConsumer:(id<FBDataConsumer>)consumer
{
return [[[self
mappedPath:containerPath]
onQueue:self.queue fmap:^(NSString *fullSourcePath) {
return [[[[FBProcessBuilder
withLaunchPath:@"/usr/bin/tail"]
withArguments:@[@"-c+1", @"-f", fullSourcePath]]
withStdOutConsumer:consumer]
start];
}]
onQueue:self.queue map:^(FBProcess *task) {
return [task.statLoc
onQueue:self.queue respondToCancellation:^{
return [task sendSignal:SIGTERM backingOffToKillWithTimeout:1 logger:nil];
}];
}];
}
- (FBFuture<NSNull *> *)createDirectory:(NSString *)directoryPath
{
return [[self
mappedPath:directoryPath]
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (NSString *fullPath) {
NSError *error;
if (![self createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:nil error:&error]) {
return [[[FBControlCoreError
describeFormat:@"Could not create directory %@ at container %@: %@", directoryPath, fullPath, error]
causedBy:error]
failFuture];
}
return FBFuture.empty;
}];
}
- (FBFuture<NSNull *> *)moveFrom:(NSString *)sourcePath to:(NSString *)destinationPath
{
return [[FBFuture
futureWithFutures:@[
[self mappedPath:sourcePath],
[self mappedPath:destinationPath],
]]
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (NSArray<NSString *> *mappedPaths) {
NSString *fullSourcePath = mappedPaths[0];
NSString *fullDestinationPath = mappedPaths[1];
NSError *error = nil;
if (![self moveItemAtPath:fullSourcePath toPath:fullDestinationPath error:&error]) {
return [[[FBControlCoreError
describeFormat:@"Could not move item at %@ to %@: %@", fullSourcePath, fullDestinationPath, error]
causedBy:error]
failFuture];
}
return FBFuture.empty;
}];
}
- (FBFuture<NSNull *> *)remove:(NSString *)path
{
return [[self
mappedPath:path]
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (NSString *fullPath) {
NSError *error;
if (![self removeItemAtPath:fullPath error:&error]) {
return [[[FBControlCoreError
describeFormat:@"Could not remove item at path %@: %@", fullPath, error]
causedBy:error]
failFuture];
}
return FBFuture.empty;
}];
}
- (FBFuture<NSArray<NSString *> *> *)contentsOfDirectory:(NSString *)path
{
return [[self
mappedPath:path]
onQueue:self.queue fmap:^(NSString *fullPath) {
NSError *error;
NSArray<NSString *> *contents = [self contentsOfDirectoryAtPath:fullPath error:&error];
if (!contents) {
return [FBFuture futureWithError:error];
}
return [FBFuture futureWithResult:contents];
}];
}
#pragma mark Private
- (FBFuture<NSString *> *)mappedPath:(NSString *)path
{
return [[FBControlCoreError
describeFormat:@"-[%@ %@] must be implemented by subclasses", NSStringFromClass(self.class), NSStringFromSelector(_cmd)]
failFuture];
}
- (NSArray<NSString *> *)contentsOfDirectoryAtPath:(NSString *)fullPath error:(NSError **)error
{
return [self.fileManager contentsOfDirectoryAtPath:fullPath error:error];
}
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
{
return [self.fileManager removeItemAtPath:path error:error];
}
- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
{
return [self.fileManager moveItemAtPath:srcPath toPath:dstPath error:error];
}
- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
{
return [self.fileManager copyItemAtPath:srcPath toPath:dstPath error:error];
}
- (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary<NSFileAttributeKey, id> *)attributes error:(NSError **)error
{
return [self.fileManager createDirectoryAtPath:path withIntermediateDirectories:createIntermediates attributes:attributes error:error];
}
@end
@interface FBBasePathFileContainer : FBFileContainerBase
@property (nonatomic, copy, readonly) NSString *containerPath;
@end
@implementation FBBasePathFileContainer
- (instancetype)initWithContainerPath:(NSString *)containerPath queue:(dispatch_queue_t)queue
{
self = [super initWithQueue:queue];
if (!self) {
return nil;
}
_containerPath = containerPath;
return self;
}
- (FBFuture<NSString *> *)mappedPath:(NSString *)path
{
return [FBFuture futureWithResult:[self.containerPath stringByAppendingPathComponent:path]];
}
@end
@interface FBMappedFileContainer : FBFileContainerBase
@property (nonatomic, copy, readonly) NSDictionary<NSString *, NSString *> *pathMapping;
@property (nonatomic, copy, readonly) NSSet<NSString *> *mappedPaths;
@end
@implementation FBMappedFileContainer
- (instancetype)initWithPathMapping:(NSDictionary<NSString *, NSString *> *)pathMapping queue:(dispatch_queue_t)queue
{
self = [super initWithQueue:queue];
if (!self) {
return nil;
}
_pathMapping = pathMapping;
_mappedPaths = [NSSet setWithArray:pathMapping.allValues];
return self;
}
- (FBFuture<NSString *> *)mappedPath:(NSString *)path
{
NSArray<NSString *> *pathComponents = path.pathComponents;
// If we're the root, there's nothing to map to.
if ([self isRootPathOfContainer:pathComponents]) {
return [FBFuture futureWithResult:path];
}
// Otherwise, take the first path component, which must be name of the container, so it must have a mapping.
NSString *firstPath = pathComponents.firstObject;
NSString *mappedPath = self.pathMapping[firstPath];
if (!mappedPath) {
return [[FBControlCoreError
describeFormat:@"%@ is not a valid container id in %@", firstPath, [FBCollectionInformation oneLineDescriptionFromArray:self.pathMapping.allKeys]]
failFuture];
}
// Re-assemble the mapped path, discarding the re-mapped first path component.
BOOL isFirstPathComponent = YES;
for (NSString *pathComponent in pathComponents) {
if (isFirstPathComponent) {
isFirstPathComponent = NO;
continue;
}
mappedPath = [mappedPath stringByAppendingPathComponent:pathComponent];
}
return [FBFuture futureWithResult:mappedPath];
}
#pragma mark Private
- (NSArray<NSString *> *)contentsOfDirectoryAtPath:(NSString *)fullPath error:(NSError **)error
{
NSArray<NSString *> *pathComponents = fullPath.pathComponents;
// Request for the root container, list all mapped names.
if ([self isRootPathOfContainer:pathComponents]) {
return self.pathMapping.allKeys;
}
return [super contentsOfDirectoryAtPath:fullPath error:error];
}
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
{
NSArray<NSString *> *pathComponents = path.pathComponents;
if ([self isRootPathOfContainer:pathComponents]) {
return [[FBControlCoreError
describeFormat:@"Cannot remove mapped container root at path %@", path]
failBool:error];
}
if ([self isGroupContainerRoot:pathComponents]) {
return [[FBControlCoreError
describeFormat:@"Cannot remove mapped container at path %@", path]
failBool:error];
}
return [super removeItemAtPath:path error:error];
}
- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
{
NSArray<NSString *> *srcPathComponents = srcPath.pathComponents;
NSArray<NSString *> *dstPathComponents = dstPath.pathComponents;
if ([self isRootPathOfContainer:srcPathComponents]) {
return [[FBControlCoreError
describeFormat:@"Cannot move mapped container root at path %@", srcPath]
failBool:error];
}
if ([self isGroupContainerRoot:srcPathComponents]) {
return [[FBControlCoreError
describeFormat:@"Cannot move mapped container at path %@", srcPath]
failBool:error];
}
if ([self isRootPathOfContainer:dstPathComponents]) {
return [[FBControlCoreError
describeFormat:@"Cannot move to mapped container root at path %@", dstPath]
failBool:error];
}
if ([self isGroupContainerRoot:dstPathComponents]) {
return [[FBControlCoreError
describeFormat:@"Cannot move to mapped container at path %@", dstPath]
failBool:error];
}
return [super moveItemAtPath:srcPath toPath:dstPath error:error];
}
- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
{
NSArray<NSString *> *srcPathComponents = srcPath.pathComponents;
NSArray<NSString *> *dstPathComponents = dstPath.pathComponents;
if ([self isRootPathOfContainer:srcPathComponents]) {
return [[FBControlCoreError
describeFormat:@"Cannot copy mapped container root at path %@", srcPath]
failBool:error];
}
if ([self isRootPathOfContainer:dstPathComponents]) {
return [[FBControlCoreError
describeFormat:@"Cannot copy to mapped container root at path %@", dstPath]
failBool:error];
}
if ([self isGroupContainerRoot:dstPathComponents]) {
return [[FBControlCoreError
describeFormat:@"Cannot copy to mapped container at path %@", dstPath]
failBool:error];
}
return [super copyItemAtPath:srcPath toPath:dstPath error:error];
}
- (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary<NSFileAttributeKey, id> *)attributes error:(NSError **)error
{
NSArray<NSString *> *pathComponents = path.pathComponents;
if ([self isRootPathOfContainer:pathComponents]) {
return [[FBControlCoreError
describeFormat:@"Cannot create mapped container root at path %@", path]
failBool:error];
}
if ([self isGroupContainerRoot:pathComponents]) {
return [[FBControlCoreError
describeFormat:@"Cannot create mapped container at path %@", path]
failBool:error];
}
return [super createDirectoryAtPath:path withIntermediateDirectories:createIntermediates attributes:attributes error:error];
}
- (BOOL)isRootPathOfContainer:(NSArray<NSString *> *)pathComponents
{
// If no path components this must be the root
if (pathComponents.count == 0) {
return YES;
}
// The root is also signified by a query for the root of the container.
NSString *firstPath = pathComponents.firstObject;
if (pathComponents.count == 1 && ([firstPath isEqualToString:@"."] || [firstPath isEqualToString:@"/"])) {
return YES;
}
// Otherwise we can't be the root path.
return NO;
}
- (BOOL)isGroupContainerRoot:(NSArray<NSString *> *)pathComponents
{
// Re-assemble the path, confirming whether it matches with one of the mapped paths
NSString *reassembled = [NSURL fileURLWithPathComponents:pathComponents].path;
if ([self.mappedPaths containsObject:reassembled]) {
return YES;
}
// If the canonical path does not match the known paths this can't be the group container root.
return NO;
}
@end
@implementation FBFileContainer
+ (id<FBFileContainer>)fileContainerForProvisioningProfileCommands:(id<FBProvisioningProfileCommands>)commands queue:(dispatch_queue_t)queue
@ -523,14 +549,22 @@ FBFileContainerKind const FBFileContainerKindWallpaper = @"wallpaper";
+ (id<FBFileContainer>)fileContainerForBasePath:(NSString *)basePath
{
dispatch_queue_t queue = dispatch_queue_create("com.facebook.fbcontrolcore.file_container", DISPATCH_QUEUE_SERIAL);
return [[FBBasePathFileContainer alloc] initWithContainerPath:basePath queue:queue];
id<FBContainedFile> rootFile = [[FBContainedFile_Host alloc] initWithFileManager:NSFileManager.defaultManager path:basePath];
return [self fileContainerForRootFile:rootFile];
}
+ (id<FBFileContainer>)fileContainerForPathMapping:(NSDictionary<NSString *, NSString *> *)pathMapping
{
id<FBContainedFile> rootFile = [[FBContainedFile_Mapped_Host alloc] initWithMappingPaths:pathMapping fileManager:NSFileManager.defaultManager];
return [self fileContainerForRootFile:rootFile];
}
#pragma mark Private
+ (id<FBFileContainer>)fileContainerForRootFile:(id<FBContainedFile>)root
{
dispatch_queue_t queue = dispatch_queue_create("com.facebook.fbcontrolcore.file_container", DISPATCH_QUEUE_SERIAL);
return [[FBMappedFileContainer alloc] initWithPathMapping:pathMapping queue:queue];
return [[FBContainedFile_ContainedRoot alloc] initWithRootFile:root queue:queue];
}
@end

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -0,0 +1,41 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import <FBControlCore/FBFuture.h>
#import <FBControlCore/FBiOSTargetConstants.h>
#import <FBControlCore/FBiOSTargetCommandForwarder.h>
NS_ASSUME_NONNULL_BEGIN
/**
Defines an interface for power related commands.
*/
@protocol FBLifecycleCommands <NSObject, FBiOSTargetCommand>
#pragma mark States
/**
Asynchronously waits on the provided state.
@param state the state to wait on
@return A future that resolves when it has transitioned to the given state.
*/
- (FBFuture<NSNull *> *)resolveState:(FBiOSTargetState)state;
/**
Asynchronously waits to leave the provided state.
@param state the state to wait to leave
@return A future that resolves when it has transitioned away from the given state.
*/
- (FBFuture<NSNull *> *)resolveLeavesState:(FBiOSTargetState)state;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -60,14 +60,14 @@
[statLocFuture resolveWithResult:@(statLoc)];
if (WIFSIGNALED(statLoc)) {
int signalCode = WTERMSIG(statLoc);
NSString *message = [NSString stringWithFormat:@"Process %d (%@) died with signal %d", processIdentifier, configuration.processName, signalCode];
NSString *message = [NSString stringWithFormat:@"Process %d (%@) exited with signal %d", processIdentifier, configuration.processName, signalCode];
[logger log:message];
NSError *error = [[FBControlCoreError describe:message] build];
[exitCodeFuture resolveWithError:error];
[signalFuture resolveWithResult:@(signalCode)];
} else {
int exitCode = WEXITSTATUS(statLoc);
NSString *message = [NSString stringWithFormat:@"Process %d (%@) died with exit code %d", processIdentifier, configuration.processName, exitCode];
NSString *message = [NSString stringWithFormat:@"Process %d (%@) exited with code %d", processIdentifier, configuration.processName, exitCode];
[logger log:message];
NSError *error = [[FBControlCoreError describe:message] build];
[signalFuture resolveWithError:error];

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -10,16 +10,16 @@
NS_ASSUME_NONNULL_BEGIN
/**
An Enumeration Representing Approval of Services.
An Enumeration Representing Services.
*/
typedef NSString *FBSettingsApprovalService NS_STRING_ENUM;
typedef NSString *FBTargetSettingsService NS_STRING_ENUM;
extern FBSettingsApprovalService const FBSettingsApprovalServiceContacts;
extern FBSettingsApprovalService const FBSettingsApprovalServicePhotos;
extern FBSettingsApprovalService const FBSettingsApprovalServiceCamera;
extern FBSettingsApprovalService const FBSettingsApprovalServiceLocation;
extern FBSettingsApprovalService const FBSettingsApprovalServiceMicrophone;
extern FBSettingsApprovalService const FBSettingsApprovalServiceUrl;
extern FBSettingsApprovalService const FBSettingsApprovalServiceNotification;
extern FBTargetSettingsService const FBTargetSettingsServiceContacts;
extern FBTargetSettingsService const FBTargetSettingsServicePhotos;
extern FBTargetSettingsService const FBTargetSettingsServiceCamera;
extern FBTargetSettingsService const FBTargetSettingsServiceLocation;
extern FBTargetSettingsService const FBTargetSettingsServiceMicrophone;
extern FBTargetSettingsService const FBTargetSettingsServiceUrl;
extern FBTargetSettingsService const FBTargetSettingsServiceNotification;
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -7,10 +7,10 @@
#import "FBSettingsCommands.h"
FBSettingsApprovalService const FBSettingsApprovalServiceContacts = @"contacts";
FBSettingsApprovalService const FBSettingsApprovalServicePhotos = @"photos";
FBSettingsApprovalService const FBSettingsApprovalServiceCamera = @"camera";
FBSettingsApprovalService const FBSettingsApprovalServiceLocation = @"location";
FBSettingsApprovalService const FBSettingsApprovalServiceMicrophone = @"microphone";
FBSettingsApprovalService const FBSettingsApprovalServiceUrl = @"url";
FBSettingsApprovalService const FBSettingsApprovalServiceNotification = @"notification";
FBTargetSettingsService const FBTargetSettingsServiceContacts = @"contacts";
FBTargetSettingsService const FBTargetSettingsServicePhotos = @"photos";
FBTargetSettingsService const FBTargetSettingsServiceCamera = @"camera";
FBTargetSettingsService const FBTargetSettingsServiceLocation = @"location";
FBTargetSettingsService const FBTargetSettingsServiceMicrophone = @"microphone";
FBTargetSettingsService const FBTargetSettingsServiceUrl = @"url";
FBTargetSettingsService const FBTargetSettingsServiceNotification = @"notification";

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -0,0 +1,54 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import <FBControlCore/FBArchitecture.h>
#import <FBControlCore/FBFuture.h>
NS_ASSUME_NONNULL_BEGIN
@class FBProcessSpawnConfiguration;
@interface FBArchitectureProcessAdapter : NSObject
/// Force binaries to be launched in desired architectures.
///
/// Convenience method for `-[FBArchitectureProcessAdapter adaptProcessConfiguration:toAnyArchitectureIn:hostArchitectures:queue:temporaryDirectory:]`
-(FBFuture<FBProcessSpawnConfiguration *> *)adaptProcessConfiguration:(FBProcessSpawnConfiguration *)processConfiguration toAnyArchitectureIn:(NSSet<FBArchitecture> *)requestedArchitectures queue:(dispatch_queue_t)queue temporaryDirectory:(NSURL *)temporaryDirectory;
/// Force binaries to be launched in desired architectures.
///
/// Up to Xcode 14.2, subprocesses were spawned in the same architecture as parent process by default.
/// But from Xcode 14.3, subprocesses are spawned in arm64 when running on a arm64, regardless of parent process architecture.
/// To force subprocess being spawned in other architecture, there is `arch` utility that does not work in simulator context.
///
/// As a workaround, to bring predictability into which architecture spawned process will be spawned,
/// we lipo-thin the executable to an architecture supported by the host machine.
///
/// The selection of the final architecture is done by comparing consiliating the architectures idb companion needs
/// the process to run with (often dictactated by the architecture of the binary code we want to inject into
/// the spawned process) and the architectures supported by the processor of the host machine.
///
/// As an example, on an arm64 machine, when idb companion needs to inject an x86_64 lib into a process that could
/// run in either x86_64 or arm64, the target process needs to be thinned down to `x86_64` to ensure it runs in the
/// same of the lib that needs to be injected.
///
/// - Parameters:
/// - processConfiguration: Initial process configuration
/// - toAnyArchitectureIn: Set of architectures the process needs to be spawned with. `arm64` will take precedence over `x86_64`
/// - queue: Target Queue
/// - hostArchitectures: Set of architectures supported by the host machine
/// - temporaryDirectory: Target directory where we put lipoed binary
-(FBFuture<FBProcessSpawnConfiguration *> *)adaptProcessConfiguration:(FBProcessSpawnConfiguration *)processConfiguration toAnyArchitectureIn:(NSSet<FBArchitecture> *)architectures hostArchitectures:(NSSet<FBArchitecture> *)hostArchitectures queue:(dispatch_queue_t)queue temporaryDirectory:(NSURL *)temporaryDirectory;
/// Returns supported architectures based on companion launch architecture and launch under rosetta determination.
+(NSSet<FBArchitecture> *)hostMachineSupportedArchitectures;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -0,0 +1,227 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "FBArchitectureProcessAdapter.h"
#import "FBProcessSpawnConfiguration.h"
#import "FBCollectionInformation.h"
#import "FBArchitecture.h"
#import "FBProcessBuilder.h"
#import "FBFuture.h"
#import "FBControlCoreError.h"
#import <mach-o/arch.h>
#include <sys/sysctl.h>
// https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment#Determine-Whether-Your-App-Is-Running-as-a-Translated-Binary
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
static int processIsTranslated(void)
{
int ret = 0;
size_t size = sizeof(ret);
// patternlint-disable-next-line prefer-metasystemcontrol-byname
if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1)
{
if (errno == ENOENT)
return 0;
return -1;
}
return ret;
}
#pragma clang diagnostic pop
@implementation FBArchitectureProcessAdapter
-(FBFuture<FBProcessSpawnConfiguration *> *)adaptProcessConfiguration:(FBProcessSpawnConfiguration *)processConfiguration toAnyArchitectureIn:(NSSet<FBArchitecture> *)requestedArchitectures queue:(dispatch_queue_t)queue temporaryDirectory:(NSURL *)temporaryDirectory {
return [self adaptProcessConfiguration:processConfiguration toAnyArchitectureIn:requestedArchitectures hostArchitectures:[FBArchitectureProcessAdapter hostMachineSupportedArchitectures] queue:queue temporaryDirectory:temporaryDirectory];
}
- (nullable FBArchitecture)selectArchitectureFrom:(NSSet<FBArchitecture> *)requestedArchitectures supportedArchitectures:(NSSet<FBArchitecture> *)supportedArchitectures
{
if([requestedArchitectures containsObject:FBArchitectureArm64] && [supportedArchitectures containsObject:FBArchitectureArm64]) {
return FBArchitectureArm64;
}
if([requestedArchitectures containsObject:FBArchitectureX86_64] && [supportedArchitectures containsObject:FBArchitectureX86_64]) {
return FBArchitectureX86_64;
}
return nil;
}
-(FBFuture<FBProcessSpawnConfiguration *> *)adaptProcessConfiguration:(FBProcessSpawnConfiguration *)processConfiguration toAnyArchitectureIn:(NSSet<FBArchitecture> *)requestedArchitectures hostArchitectures:(NSSet<FBArchitecture> *)hostArchitectures queue:(dispatch_queue_t)queue temporaryDirectory:(NSURL *)temporaryDirectory {
FBArchitecture architecture = [self selectArchitectureFrom:requestedArchitectures supportedArchitectures:hostArchitectures];
if (!architecture) {
return [[FBControlCoreError
describeFormat:@"Could not select an architecture from %@ compatible with %@", [FBCollectionInformation oneLineDescriptionFromArray:requestedArchitectures.allObjects], [FBCollectionInformation oneLineDescriptionFromArray:hostArchitectures.allObjects]
] failFuture];
}
return [[[self verifyArchitectureAvailable:processConfiguration.launchPath architecture:architecture queue:queue]
onQueue:queue fmap:^FBFuture *(NSNull * _) {
NSString *fileName = [[[[processConfiguration.launchPath lastPathComponent] stringByAppendingString:[[NSUUID new] UUIDString]] stringByAppendingString:@"."] stringByAppendingString:architecture];
NSURL *filePath = [temporaryDirectory URLByAppendingPathComponent:fileName isDirectory:NO];
return [[self extractArchitecture:architecture processConfiguration:processConfiguration queue:queue outputPath:filePath] mapReplace:[filePath path]];
}]
onQueue:queue fmap:^FBFuture *(NSString *extractedBinary) {
return [[self getFixedupDyldFrameworkPathFromOriginalBinary:processConfiguration.launchPath queue:queue]
onQueue:queue map:^FBProcessSpawnConfiguration *(NSString *dyldFrameworkPath) {
NSMutableDictionary<NSString *, NSString *> *updatedEnvironment = [processConfiguration.environment mutableCopy];
// DYLD_FRAMEWORK_PATH adds additional search paths for required "*.framewosk"s in binary
// DYLD_LIBRARY_PATH adds additional search paths for required "*.dyld"s in binary
[updatedEnvironment setValue:dyldFrameworkPath forKey:@"DYLD_FRAMEWORK_PATH"];
[updatedEnvironment setValue:dyldFrameworkPath forKey:@"DYLD_LIBRARY_PATH"];
return [[FBProcessSpawnConfiguration alloc] initWithLaunchPath:extractedBinary arguments:processConfiguration.arguments environment:updatedEnvironment io:processConfiguration.io mode:processConfiguration.mode];
}];
}];
}
/// Verifies that we can extract desired architecture from binary
-(FBFuture<NSNull *> *)verifyArchitectureAvailable:(NSString *)binary architecture:(FBArchitecture)architecture queue:(dispatch_queue_t)queue {
return [[[[[[[FBProcessBuilder
withLaunchPath:@"/usr/bin/lipo" arguments:@[binary, @"-verify_arch", architecture]]
withStdOutToDevNull]
withStdErrToDevNull]
runUntilCompletionWithAcceptableExitCodes:[NSSet setWithObject:@0]]
rephraseFailure:@"Desired architecture %@ not found in %@ binary", architecture, binary]
mapReplace:[NSNull null]]
timeout:10 waitingFor:@"lipo -verify_arch"];
}
-(FBFuture<NSString *> *)extractArchitecture:(FBArchitecture)architecture processConfiguration:(FBProcessSpawnConfiguration *)processConfiguration queue:(dispatch_queue_t)queue outputPath:(NSURL *)outputPath {
return [[[[[[[FBProcessBuilder
withLaunchPath:@"/usr/bin/lipo" arguments:@[processConfiguration.launchPath, @"-extract", architecture, @"-output", [outputPath path]]]
withStdOutToDevNull]
withStdErrLineReader:^(NSString * _Nonnull line) {
NSLog(@"LINE %@\n", line);
}]
runUntilCompletionWithAcceptableExitCodes:[NSSet setWithObject:@0]]
rephraseFailure:@"Failed to thin %@ architecture out from %@ binary", architecture, processConfiguration.launchPath]
mapReplace:[NSNull null]]
timeout:10 waitingFor:@"lipo -extract"];
}
/// After we lipoed out arch from binary, new binary placed into temporary folder.
/// That makes all dynamic library imports become incorrect. To fix that up we
/// have to specify `DYLD_FRAMEWORK_PATH` correctly.
-(FBFuture<NSString *> *)getFixedupDyldFrameworkPathFromOriginalBinary:(NSString *)binary queue:(dispatch_queue_t)queue {
NSString *binaryFolder = [[binary stringByResolvingSymlinksInPath] stringByDeletingLastPathComponent];
return [[[self getOtoolInfoFromBinary:binary queue:queue]
onQueue:queue map:^NSSet<NSString *> *(NSString *result) {
return [self extractRpathsFromOtoolOutput:result];
}]
onQueue:queue map:^NSString *(NSSet<NSString *> *result) {
NSMutableArray<NSString *> *rpaths = [NSMutableArray new];
for (NSString *binaryRpath in result) {
if ([binaryRpath hasPrefix:@"@executable_path"]) {
[rpaths addObject:[binaryRpath stringByReplacingOccurrencesOfString:@"@executable_path" withString:binaryFolder]];
}
}
return [rpaths componentsJoinedByString:@":"];
}];
}
-(FBFuture<NSString *> *)getOtoolInfoFromBinary:(NSString *)binary queue:(dispatch_queue_t)queue {
return [[[[[[[FBProcessBuilder
withLaunchPath:@"/usr/bin/otool" arguments:@[@"-l", binary]]
withStdOutInMemoryAsString]
withStdErrToDevNull]
runUntilCompletionWithAcceptableExitCodes:[NSSet setWithObject:@0]]
rephraseFailure:@"Failed query otool -l from %@", binary]
onQueue:queue fmap:^FBFuture<NSString *>*(FBProcess<NSNull *, NSString *, NSString *> *task) {
if (task.stdOut) {
return [FBFuture futureWithResult: task.stdOut];
}
return [[FBControlCoreError describeFormat:@"Failed to call otool -l over %@", binary] failFuture];
}]
timeout:10 waitingFor:@"otool -l"];
}
/// Extracts rpath from full otool output
/// Each `LC_RPATH` entry like
/// ```
/// Load command 19
/// cmd LC_RPATH
/// cmdsize 48
/// path @executable_path/../../Frameworks/ (offset 12)
/// ```
/// transforms to
/// ```
/// @executable_path/../../Frameworks/
/// ```
-(NSSet<NSString *> *)extractRpathsFromOtoolOutput:(NSString *)otoolOutput {
NSArray<NSString *> *lines = [otoolOutput componentsSeparatedByString: @"\n"];
NSMutableSet<NSString *> *result = [NSMutableSet new];
// Rpath entry looks like:
// ```
// Load command 19
// cmd LC_RPATH
// cmdsize 48
// path @executable_path/../../Frameworks/ (offset 12)
// ```
// So if we found occurence of `cmd LC_RPATH` rpath value will be two lines below.
NSUInteger lcRpathValueOffset = 2;
[lines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger index, BOOL *_) {
if ([self isLcPathDefinitionLine:line] && index + lcRpathValueOffset < lines.count) {
NSString *rpathLine = lines[index + lcRpathValueOffset];
NSString *rpath = [self extractRpathValueFromLine:rpathLine];
if (rpath) {
[result addObject:rpath];
}
}
}];
return result;
}
/// Checking for `LC_RPATH` in load commands
/// - Parameter line: Single line entry for otool output
-(bool)isLcPathDefinitionLine:(NSString *)line {
bool hasCMD = false;
bool hasLcRpath = false;
for (NSString *component in [line componentsSeparatedByString: @" "]) {
if ([component isEqualToString:@"cmd"]) {
hasCMD = true;
} else if ([component isEqualToString:@"LC_RPATH"]) {
hasLcRpath = true;
}
}
return (hasCMD && hasLcRpath);
}
// Note: spaces in path names are not available. Currently we use adapter for binaries
// inside Xcode that has relative paths to original binary.
// And there is no spaces in paths over there.
-(nullable NSString *)extractRpathValueFromLine:(NSString *)line {
for (NSString *component in [line componentsSeparatedByString: @" "]) {
if ([component hasPrefix:@"@executable_path"]) {
return component;
}
}
return nil;
}
+(NSSet<FBArchitecture> *)hostMachineSupportedArchitectures {
#if TARGET_CPU_X86_64
int isTranslated = processIsTranslated();
if (isTranslated == 1) {
// Companion running as x86_64 with translation (Rosetta) -> Processor supports Arm64 and x86_64
return [NSSet setWithArray:@[FBArchitectureArm64, FBArchitectureX86_64]];
} else {
// Companion running as x86_64 and translation is disabled or unknown
// Assuming processor only supports x86_64 even if translation state is unknown
return [NSSet setWithArray:@[FBArchitectureX86_64]];
}
#else
return [NSSet setWithArray:@[FBArchitectureArm64, FBArchitectureX86_64]];
#endif
}
@end

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
The Designated Initializer
*/
- (instancetype)initWithTestBundle:(FBBundleDescriptor *)testBundle applicationLaunchConfiguration:(FBApplicationLaunchConfiguration *)applicationLaunchConfiguration testHostBundle:(nullable FBBundleDescriptor *)testHostBundle timeout:(NSTimeInterval)timeout initializeUITesting:(BOOL)initializeUITesting useXcodebuild:(BOOL)useXcodebuild testsToRun:(nullable NSSet<NSString *> *)testsToRun testsToSkip:(nullable NSSet<NSString *> *)testsToSkip targetApplicationBundle:(nullable FBBundleDescriptor *)targetApplicationBundle xcTestRunProperties:(nullable NSDictionary *)xcTestRunProperties resultBundlePath:(nullable NSString *)resultBundlePath reportActivities:(BOOL)reportActivities coverageDirectoryPath:(nullable NSString *)coverageDirectoryPath logDirectoryPath:(nullable NSString *)logDirectoryPath;
- (instancetype)initWithTestBundle:(FBBundleDescriptor *)testBundle applicationLaunchConfiguration:(FBApplicationLaunchConfiguration *)applicationLaunchConfiguration testHostBundle:(nullable FBBundleDescriptor *)testHostBundle timeout:(NSTimeInterval)timeout initializeUITesting:(BOOL)initializeUITesting useXcodebuild:(BOOL)useXcodebuild testsToRun:(nullable NSSet<NSString *> *)testsToRun testsToSkip:(nullable NSSet<NSString *> *)testsToSkip targetApplicationBundle:(nullable FBBundleDescriptor *)targetApplicationBundle xcTestRunProperties:(nullable NSDictionary *)xcTestRunProperties resultBundlePath:(nullable NSString *)resultBundlePath reportActivities:(BOOL)reportActivities coverageDirectoryPath:(nullable NSString *)coverageDirectoryPath enableContinuousCoverageCollection:(BOOL)enableContinuousCoverageCollection logDirectoryPath:(nullable NSString *)logDirectoryPath reportResultBundle:(BOOL)reportResultBundle;
/**
XCTest bundle used for testing
@ -88,11 +88,21 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, copy, readonly, nullable) NSString *coverageDirectoryPath;
/**
Determines whether should enable continuous coverage collection
*/
@property (nonatomic, assign, readonly) BOOL shouldEnableContinuousCoverageCollection;
/**
The Directory to use for storing logs generated during the execution of the test run.
*/
@property (nonatomic, nullable, copy, readonly) NSString *logDirectoryPath;
/*
Path to the result bundle.
*/
@property (nonatomic, assign, readonly) BOOL reportResultBundle;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -17,7 +17,7 @@
@implementation FBTestLaunchConfiguration
- (instancetype)initWithTestBundle:(FBBundleDescriptor *)testBundle applicationLaunchConfiguration:(FBApplicationLaunchConfiguration *)applicationLaunchConfiguration testHostBundle:(nullable FBBundleDescriptor *)testHostBundle timeout:(NSTimeInterval)timeout initializeUITesting:(BOOL)initializeUITesting useXcodebuild:(BOOL)useXcodebuild testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip targetApplicationBundle:(nullable FBBundleDescriptor *)targetApplicationBundle xcTestRunProperties:(NSDictionary *)xcTestRunProperties resultBundlePath:(NSString *)resultBundlePath reportActivities:(BOOL)reportActivities coverageDirectoryPath:(NSString *)coverageDirectoryPath logDirectoryPath:(nullable NSString *)logDirectoryPath
- (instancetype)initWithTestBundle:(FBBundleDescriptor *)testBundle applicationLaunchConfiguration:(FBApplicationLaunchConfiguration *)applicationLaunchConfiguration testHostBundle:(nullable FBBundleDescriptor *)testHostBundle timeout:(NSTimeInterval)timeout initializeUITesting:(BOOL)initializeUITesting useXcodebuild:(BOOL)useXcodebuild testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip targetApplicationBundle:(nullable FBBundleDescriptor *)targetApplicationBundle xcTestRunProperties:(NSDictionary *)xcTestRunProperties resultBundlePath:(NSString *)resultBundlePath reportActivities:(BOOL)reportActivities coverageDirectoryPath:(NSString *)coverageDirectoryPath enableContinuousCoverageCollection:(BOOL)enableContinuousCoverageCollection logDirectoryPath:(nullable NSString *)logDirectoryPath reportResultBundle:(BOOL)reportResultBundle
{
self = [super init];
if (!self) {
@ -37,7 +37,9 @@
_resultBundlePath = resultBundlePath;
_reportActivities = reportActivities;
_coverageDirectoryPath = coverageDirectoryPath;
_shouldEnableContinuousCoverageCollection = enableContinuousCoverageCollection;
_logDirectoryPath = logDirectoryPath;
_reportResultBundle = reportResultBundle;
return self;
}
@ -70,18 +72,20 @@
(self.xcTestRunProperties == configuration.xcTestRunProperties || [self.xcTestRunProperties isEqual:configuration.xcTestRunProperties]) &&
(self.resultBundlePath == configuration.resultBundlePath || [self.resultBundlePath isEqual:configuration.resultBundlePath]) &&
(self.coverageDirectoryPath == configuration.coverageDirectoryPath || [self.coverageDirectoryPath isEqualToString:configuration.coverageDirectoryPath]) &&
(self.logDirectoryPath == configuration.logDirectoryPath || [self.logDirectoryPath isEqualToString:configuration.logDirectoryPath]);
(self.shouldEnableContinuousCoverageCollection == configuration.shouldEnableContinuousCoverageCollection) &&
(self.logDirectoryPath == configuration.logDirectoryPath || [self.logDirectoryPath isEqualToString:configuration.logDirectoryPath]) &&
self.reportResultBundle == configuration.reportResultBundle;
}
- (NSUInteger)hash
{
return self.testBundle.hash ^ self.applicationLaunchConfiguration.hash ^ self.testHostBundle.hash ^ (unsigned long) self.timeout ^ (unsigned long) self.shouldInitializeUITesting ^ (unsigned long) self.shouldUseXcodebuild ^ self.testsToRun.hash ^ self.testsToSkip.hash ^ self.targetApplicationBundle.hash ^ self.xcTestRunProperties.hash ^ self.resultBundlePath.hash ^ self.coverageDirectoryPath.hash ^ self.logDirectoryPath.hash;
return self.testBundle.hash ^ self.applicationLaunchConfiguration.hash ^ self.testHostBundle.hash ^ (unsigned long) self.timeout ^ (unsigned long) self.shouldInitializeUITesting ^ (unsigned long) self.shouldUseXcodebuild ^ self.testsToRun.hash ^ self.testsToSkip.hash ^ self.targetApplicationBundle.hash ^ self.xcTestRunProperties.hash ^ self.resultBundlePath.hash ^ self.coverageDirectoryPath.hash ^ (unsigned long) self.shouldEnableContinuousCoverageCollection ^ self.logDirectoryPath.hash;
}
- (NSString *)description
{
return [NSString stringWithFormat:
@"FBTestLaunchConfiguration TestBundle %@ | AppConfig %@ | HostBundle %@ | UITesting %d | UseXcodebuild %d | TestsToRun %@ | TestsToSkip %@ | Target application bundle %@ xcTestRunProperties %@ | ResultBundlePath %@ | CoverageDirPath %@ | LogDirectoryPath %@" ,
@"FBTestLaunchConfiguration TestBundle %@ | AppConfig %@ | HostBundle %@ | UITesting %d | UseXcodebuild %d | TestsToRun %@ | TestsToSkip %@ | Target application bundle %@ xcTestRunProperties %@ | ResultBundlePath %@ | CoverageDirPath %@ | EnableContinuousCoverageCollection %d | LogDirectoryPath %@ | ReportResultBundle %d" ,
self.testBundle,
self.applicationLaunchConfiguration,
self.testHostBundle,
@ -93,7 +97,9 @@
self.xcTestRunProperties,
self.resultBundlePath,
self.coverageDirectoryPath,
self.logDirectoryPath
self.shouldEnableContinuousCoverageCollection,
self.logDirectoryPath,
self.reportResultBundle
];
}

Просмотреть файл

@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -31,7 +31,7 @@ extern FBVideoStreamEncoding const FBVideoStreamEncodingMinicap;
@param compressionQuality the compression quality to use.
@param scaleFactor the scale factor, between 0-1. nil for no scaling.
*/
- (instancetype)initWithEncoding:(FBVideoStreamEncoding)encoding framesPerSecond:(nullable NSNumber *)framesPerSecond compressionQuality:(nullable NSNumber *)compressionQuality scaleFactor:(nullable NSNumber *)scaleFactor;
- (instancetype)initWithEncoding:(FBVideoStreamEncoding)encoding framesPerSecond:(nullable NSNumber *)framesPerSecond compressionQuality:(nullable NSNumber *)compressionQuality scaleFactor:(nullable NSNumber *)scaleFactor avgBitrate:(nullable NSNumber *)avgBitrate keyFrameRate:(nullable NSNumber *)keyFrameRate;
/**
The encoding of the stream.
@ -54,6 +54,16 @@ extern FBVideoStreamEncoding const FBVideoStreamEncodingMinicap;
*/
@property (nonatomic, copy, nullable, readonly) NSNumber *scaleFactor;
/**
Average bitrate.
*/
@property (nonatomic, copy, nullable, readonly) NSNumber *avgBitrate;
/**
Send a key frame every N seconds.
*/
@property (nonatomic, copy, nullable, readonly) NSNumber *keyFrameRate;
@end
NS_ASSUME_NONNULL_END

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше