From be38956a297ef5321be1122fe287f257b8693a1b Mon Sep 17 00:00:00 2001 From: Lucas Statler Date: Thu, 30 Mar 2023 12:16:25 -0700 Subject: [PATCH 01/11] Add logic test support --- BPSampleApp/BPLogicTests-Info.plist | 22 ++ .../BPSampleApp.xcodeproj/project.pbxproj | 210 +++++++++++++++++ .../xcschemes/BPLogicTests.xcscheme | 52 +++++ .../xcschemes/BPSampleApp.xcscheme | 10 + BPSampleApp/BPSampleAppTests-Info.plist | 22 ++ .../LogicTests/BPLogicTests/BPLogicTests.m | 81 +++++++ .../BPPassingLogicTests/BPBulkLogicTests.m | 219 ++++++++++++++++++ .../BPPassingLogicTests/BPPassingLogicTests.m | 34 +++ .../BPPassingLogicTests/SwiftLogicTests.swift | 26 +++ BPSampleApp/LogicTests/Info.plist | 22 ++ .../xcshareddata/WorkspaceSettings.xcsettings | 2 + .../xcshareddata/xcschemes/bluepill.xcscheme | 2 +- bluepill/src/BPApp.h | 2 +- bluepill/src/BPApp.m | 45 ++-- bluepill/src/BPHTMLReportWriter.m | 2 +- bluepill/src/BPPacker.h | 3 +- bluepill/src/BPPacker.m | 3 +- bluepill/src/BPReportCollector.m | 2 +- bluepill/src/BPRunner.h | 3 +- bluepill/src/BPRunner.m | 13 +- bluepill/src/BPSwimlane.h | 3 +- bluepill/src/BPSwimlane.m | 2 +- bluepill/src/main.m | 5 +- bluepill/tests/BPAppTests.m | 6 +- bluepill/tests/BPCLITests.m | 4 +- bluepill/tests/BPIntegrationTests.m | 36 ++- bluepill/tests/BPPackerTests.m | 6 +- bluepill/tests/BPRunnerTests.m | 5 +- bp/bp.xcodeproj/project.pbxproj | 74 ++++-- bp/src/BPConfiguration.h | 1 + bp/src/BPConfiguration.m | 9 +- bp/src/BPSimulator.h | 10 + bp/src/BPSimulator.m | 174 ++++++++++++-- bp/src/BPStats.h | 2 + bp/src/BPUtils.h | 54 ++++- bp/src/BPUtils.m | 78 ++++++- bp/src/Bluepill.m | 123 +++++++++- .../PrivateHeaders/CoreSimulator/SimDevice.h | 32 ++- bp/src/SimulatorHelper.h | 14 ++ bp/src/SimulatorHelper.m | 57 ++++- bp/src/SimulatorMonitor.m | 20 +- bp/tests/BPIntTestCase.m | 40 +--- bp/tests/BPReportTests.m | 6 +- bp/tests/BPTestHelper.h | 7 + bp/tests/BPTestHelper.m | 11 + ...{BluepillTests.m => BluepillHostedTests.m} | 12 +- .../BluepillUnhostedBatchingTests.m | 125 ++++++++++ .../Unhosted Tests/BluepillUnhostedTests.m | 176 ++++++++++++++ bp/tests/Utils/BPTestUtils.h | 29 +++ bp/tests/Utils/BPTestUtils.m | 98 ++++++++ bptestrunner/bluepill_batch_test.bzl | 7 +- 51 files changed, 1830 insertions(+), 171 deletions(-) create mode 100644 BPSampleApp/BPLogicTests-Info.plist create mode 100644 BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPLogicTests.xcscheme create mode 100644 BPSampleApp/BPSampleAppTests-Info.plist create mode 100644 BPSampleApp/LogicTests/BPLogicTests/BPLogicTests.m create mode 100644 BPSampleApp/LogicTests/BPPassingLogicTests/BPBulkLogicTests.m create mode 100644 BPSampleApp/LogicTests/BPPassingLogicTests/BPPassingLogicTests.m create mode 100644 BPSampleApp/LogicTests/BPPassingLogicTests/SwiftLogicTests.swift create mode 100644 BPSampleApp/LogicTests/Info.plist rename bp/tests/{BluepillTests.m => BluepillHostedTests.m} (98%) create mode 100644 bp/tests/Unhosted Tests/BluepillUnhostedBatchingTests.m create mode 100644 bp/tests/Unhosted Tests/BluepillUnhostedTests.m create mode 100644 bp/tests/Utils/BPTestUtils.h create mode 100644 bp/tests/Utils/BPTestUtils.m diff --git a/BPSampleApp/BPLogicTests-Info.plist b/BPSampleApp/BPLogicTests-Info.plist new file mode 100644 index 00000000..6c6c23c4 --- /dev/null +++ b/BPSampleApp/BPLogicTests-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj b/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj index be65cc0a..1e6659d8 100644 --- a/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj +++ b/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj @@ -24,6 +24,10 @@ BAFCCA601E36DC2000E33C31 /* BPSampleAppUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = BAFCCA5F1E36DC2000E33C31 /* BPSampleAppUITests.m */; }; E492360122EF61F300395D98 /* BPSampleAppMoarTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E492360022EF61F300395D98 /* BPSampleAppMoarTests.m */; }; E4F8A34326F3B1AD00FE1267 /* BPSampleAppNewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E4F8A33B26F3B12F00FE1267 /* BPSampleAppNewTests.m */; }; + FBBBD90029A06B7B002B9115 /* BPLogicTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBBD8F129A06B47002B9115 /* BPLogicTests.m */; }; + FBD772B52A33E15D0098CEFD /* BPPassingLogicTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD772B12A33E0620098CEFD /* BPPassingLogicTests.m */; }; + FBD772C52A3483310098CEFD /* SwiftLogicTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBD772C32A34832D0098CEFD /* SwiftLogicTests.swift */; }; + FBD772C72A378F440098CEFD /* BPBulkLogicTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD772C62A378F440098CEFD /* BPBulkLogicTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -115,6 +119,13 @@ E492360022EF61F300395D98 /* BPSampleAppMoarTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPSampleAppMoarTests.m; sourceTree = ""; }; E4F8A33B26F3B12F00FE1267 /* BPSampleAppNewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPSampleAppNewTests.m; sourceTree = ""; }; E4F8A34A26F3B1AD00FE1267 /* BPSampleAppNewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BPSampleAppNewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + FBBBD8F129A06B47002B9115 /* BPLogicTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPLogicTests.m; sourceTree = ""; }; + FBBBD8FE29A06B54002B9115 /* BPLogicTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BPLogicTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + FBD772B12A33E0620098CEFD /* BPPassingLogicTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPPassingLogicTests.m; sourceTree = ""; }; + FBD772BC2A33E15D0098CEFD /* BPPassingLogicTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BPPassingLogicTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + FBD772BD2A33E15D0098CEFD /* BPLogicTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "BPLogicTests-Info.plist"; path = "/Users/lthrockm/ios/bluepill/bluepill/BPSampleApp/BPLogicTests-Info.plist"; sourceTree = ""; }; + FBD772C32A34832D0098CEFD /* SwiftLogicTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLogicTests.swift; sourceTree = ""; }; + FBD772C62A378F440098CEFD /* BPBulkLogicTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPBulkLogicTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -174,6 +185,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + FBBBD8F929A06B54002B9115 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FBD772B72A33E15D0098CEFD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -210,6 +235,7 @@ isa = PBXGroup; children = ( BAB24F301DB5D45E00867756 /* BPSampleApp */, + FBBBD8F029A06B47002B9115 /* LogicTests */, BAB24F4A1DB5D45E00867756 /* BPSampleAppTests */, BAB24F5B1DB5D83C00867756 /* BPAppNegativeTests */, BAA4DA381DC3C02B00A58BCC /* BPSampleAppCrashingTests */, @@ -217,6 +243,7 @@ BA9C2DD31DD7F182007CB967 /* BPSampleAppFatalErrorTests */, BAFCCA5E1E36DC2000E33C31 /* BPSampleAppUITests */, BAB24F2F1DB5D45E00867756 /* Products */, + FBD772BD2A33E15D0098CEFD /* BPLogicTests-Info.plist */, ); sourceTree = ""; }; @@ -231,6 +258,8 @@ BA9C2DD21DD7F182007CB967 /* BPSampleAppFatalErrorTests.xctest */, BAFCCA5D1E36DC2000E33C31 /* BPSampleAppUITests.xctest */, E4F8A34A26F3B1AD00FE1267 /* BPSampleAppNewTests.xctest */, + FBBBD8FE29A06B54002B9115 /* BPLogicTests.xctest */, + FBD772BC2A33E15D0098CEFD /* BPPassingLogicTests.xctest */, ); name = Products; sourceTree = ""; @@ -291,6 +320,33 @@ path = BPSampleAppUITests; sourceTree = ""; }; + FBBBD8F029A06B47002B9115 /* LogicTests */ = { + isa = PBXGroup; + children = ( + FBD772C82A378F6F0098CEFD /* BPLogicTests */, + FBD772C92A378F7E0098CEFD /* BPPassingLogicTests */, + ); + path = LogicTests; + sourceTree = ""; + }; + FBD772C82A378F6F0098CEFD /* BPLogicTests */ = { + isa = PBXGroup; + children = ( + FBBBD8F129A06B47002B9115 /* BPLogicTests.m */, + ); + path = BPLogicTests; + sourceTree = ""; + }; + FBD772C92A378F7E0098CEFD /* BPPassingLogicTests */ = { + isa = PBXGroup; + children = ( + FBD772B12A33E0620098CEFD /* BPPassingLogicTests.m */, + FBD772C62A378F440098CEFD /* BPBulkLogicTests.m */, + FBD772C32A34832D0098CEFD /* SwiftLogicTests.swift */, + ); + path = BPPassingLogicTests; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -437,6 +493,40 @@ productReference = E4F8A34A26F3B1AD00FE1267 /* BPSampleAppNewTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + FBBBD8F229A06B54002B9115 /* BPLogicTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = FBBBD8FB29A06B54002B9115 /* Build configuration list for PBXNativeTarget "BPLogicTests" */; + buildPhases = ( + FBBBD8F529A06B54002B9115 /* Sources */, + FBBBD8F929A06B54002B9115 /* Frameworks */, + FBBBD8FA29A06B54002B9115 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BPLogicTests; + productName = BPSampleAppTests; + productReference = FBBBD8FE29A06B54002B9115 /* BPLogicTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + FBD772B32A33E15D0098CEFD /* BPPassingLogicTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = FBD772B92A33E15D0098CEFD /* Build configuration list for PBXNativeTarget "BPPassingLogicTests" */; + buildPhases = ( + FBD772B42A33E15D0098CEFD /* Sources */, + FBD772B72A33E15D0098CEFD /* Frameworks */, + FBD772B82A33E15D0098CEFD /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BPPassingLogicTests; + productName = BPSampleAppTests; + productReference = FBD772BC2A33E15D0098CEFD /* BPPassingLogicTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -491,6 +581,12 @@ E4F8A33D26F3B1AD00FE1267 = { DevelopmentTeam = 57Y47U492U; }; + FBBBD8F229A06B54002B9115 = { + DevelopmentTeam = 57Y47U492U; + }; + FBD772B32A33E15D0098CEFD = { + DevelopmentTeam = 57Y47U492U; + }; }; }; buildConfigurationList = BAB24F291DB5D45E00867756 /* Build configuration list for PBXProject "BPSampleApp" */; @@ -514,6 +610,8 @@ BA9C2DD11DD7F182007CB967 /* BPSampleAppFatalErrorTests */, BAFCCA5C1E36DC2000E33C31 /* BPSampleAppUITests */, E4F8A33D26F3B1AD00FE1267 /* BPSampleAppNewTests */, + FBBBD8F229A06B54002B9115 /* BPLogicTests */, + FBD772B32A33E15D0098CEFD /* BPPassingLogicTests */, ); }; /* End PBXProject section */ @@ -579,6 +677,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + FBBBD8FA29A06B54002B9115 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FBD772B82A33E15D0098CEFD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -651,6 +763,24 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + FBBBD8F529A06B54002B9115 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FBBBD90029A06B7B002B9115 /* BPLogicTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FBD772B42A33E15D0098CEFD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FBD772C52A3483310098CEFD /* SwiftLogicTests.swift in Sources */, + FBD772B52A33E15D0098CEFD /* BPPassingLogicTests.m in Sources */, + FBD772C72A378F440098CEFD /* BPBulkLogicTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1057,6 +1187,68 @@ }; name = Release; }; + FBBBD8FC29A06B54002B9115 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = 57Y47U492U; + INFOPLIST_FILE = "BPSampleAppTests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = LI.BPSampleAppTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "BPSampleAppTests/BPSampleAppTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; + }; + name = Debug; + }; + FBBBD8FD29A06B54002B9115 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = 57Y47U492U; + INFOPLIST_FILE = "BPSampleAppTests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = LI.BPSampleAppTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "BPSampleAppTests/BPSampleAppTests-Bridging-Header.h"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; + }; + name = Release; + }; + FBD772BA2A33E15D0098CEFD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = 57Y47U492U; + INFOPLIST_FILE = "BPLogicTests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = LI.BPSampleAppTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "BPSampleAppTests/BPSampleAppTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; + }; + name = Debug; + }; + FBD772BB2A33E15D0098CEFD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = 57Y47U492U; + INFOPLIST_FILE = "BPLogicTests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = LI.BPSampleAppTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "BPSampleAppTests/BPSampleAppTests-Bridging-Header.h"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -1141,6 +1333,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + FBBBD8FB29A06B54002B9115 /* Build configuration list for PBXNativeTarget "BPLogicTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FBBBD8FC29A06B54002B9115 /* Debug */, + FBBBD8FD29A06B54002B9115 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FBD772B92A33E15D0098CEFD /* Build configuration list for PBXNativeTarget "BPPassingLogicTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FBD772BA2A33E15D0098CEFD /* Debug */, + FBD772BB2A33E15D0098CEFD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = BAB24F261DB5D45E00867756 /* Project object */; diff --git a/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPLogicTests.xcscheme b/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPLogicTests.xcscheme new file mode 100644 index 00000000..03201efc --- /dev/null +++ b/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPLogicTests.xcscheme @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPSampleApp.xcscheme b/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPSampleApp.xcscheme index 6028fa6b..9fd67d6b 100644 --- a/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPSampleApp.xcscheme +++ b/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPSampleApp.xcscheme @@ -217,6 +217,16 @@ ReferencedContainer = "container:BPSampleApp.xcodeproj"> + + + + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/BPSampleApp/LogicTests/BPLogicTests/BPLogicTests.m b/BPSampleApp/LogicTests/BPLogicTests/BPLogicTests.m new file mode 100644 index 00000000..3618c788 --- /dev/null +++ b/BPSampleApp/LogicTests/BPLogicTests/BPLogicTests.m @@ -0,0 +1,81 @@ +// Copyright 2016 LinkedIn Corporation +// Licensed under the BSD 2-Clause License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at https://opensource.org/licenses/BSD-2-Clause +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +#import + +@interface BPLogicTests : XCTestCase + +@end + +@implementation BPLogicTests + +- (void)testPassingLogicTest1 { + XCTAssert(YES); +} + +- (void)testPassingLogicTest2 { + XCTAssert(YES); +} + +- (void)testFailingLogicTest { + XCTAssert(NO); +} + +/* + This failure should be recognized as a test failure + in the xctest logs, and testing should be able to continue. + */ +- (void)testCrashTestCaseLogicTest { + NSLog(@"BPLogicTests - FORCING TEST EXECUTION CRASH."); + NSObject *unused = @[][666]; +} + +/* + This failure will cause the whole execution to fail, and + requires separate special handling. + */ +- (void)testCrashExecutionLogicTest { + NSLog(@"BPLogicTests - FORCING SIMULATOR CRASH."); + char *p = NULL; + strcpy(p, "I know this will crash my app"); +} + +- (void)testStuckLogicTest { + NSLog(@"BPLogicTests - FORCING TEST TIMEOUT"); + while(1) { + sleep(10); + } +} + +- (void)testSlowLogicTest { + NSLog(@"BPLogicTests - FORCING TEST TIMEOUT"); + while(1) { + NSLog(@"Look I'm trying, but to no avail!"); + sleep(1); + } +} + +// The below should not timeout when run in succession + +- (void)testOneSecondTest1 { + sleep(1); + XCTAssert(YES); +} + +- (void)testOneSecondTest2 { + sleep(1); + XCTAssert(YES); +} + +- (void)testOneSecondTest3 { + sleep(1); + XCTAssert(YES); +} + +@end diff --git a/BPSampleApp/LogicTests/BPPassingLogicTests/BPBulkLogicTests.m b/BPSampleApp/LogicTests/BPPassingLogicTests/BPBulkLogicTests.m new file mode 100644 index 00000000..dab12a07 --- /dev/null +++ b/BPSampleApp/LogicTests/BPPassingLogicTests/BPBulkLogicTests.m @@ -0,0 +1,219 @@ +// Copyright 2016 LinkedIn Corporation +// Licensed under the BSD 2-Clause License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at https://opensource.org/licenses/BSD-2-Clause +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +#import + +@interface BPBulkLogicTests : XCTestCase + +@end + +@implementation BPBulkLogicTests + +- (void)testCase000 { XCTAssert(YES);} +- (void)testCase001 { XCTAssert(YES);} +- (void)testCase002 { XCTAssert(YES);} +- (void)testCase003 { XCTAssert(YES);} +- (void)testCase004 { XCTAssert(YES);} +- (void)testCase005 { XCTAssert(YES);} +- (void)testCase006 { XCTAssert(YES);} +- (void)testCase007 { XCTAssert(YES);} +- (void)testCase008 { XCTAssert(YES);} +- (void)testCase009 { XCTAssert(YES);} +- (void)testCase010 { XCTAssert(YES);} +- (void)testCase011 { XCTAssert(YES);} +- (void)testCase012 { XCTAssert(YES);} +- (void)testCase013 { XCTAssert(YES);} +- (void)testCase014 { XCTAssert(YES);} +- (void)testCase015 { XCTAssert(YES);} +- (void)testCase016 { XCTAssert(YES);} +- (void)testCase017 { XCTAssert(YES);} +- (void)testCase018 { XCTAssert(YES);} +- (void)testCase019 { XCTAssert(YES);} +- (void)testCase020 { XCTAssert(YES);} +- (void)testCase021 { XCTAssert(YES);} +- (void)testCase022 { XCTAssert(YES);} +- (void)testCase023 { XCTAssert(YES);} +- (void)testCase024 { XCTAssert(YES);} +- (void)testCase025 { XCTAssert(YES);} +- (void)testCase026 { XCTAssert(YES);} +- (void)testCase027 { XCTAssert(YES);} +- (void)testCase028 { XCTAssert(YES);} +- (void)testCase029 { XCTAssert(YES);} +- (void)testCase030 { XCTAssert(YES);} +- (void)testCase031 { XCTAssert(YES);} +- (void)testCase032 { XCTAssert(YES);} +- (void)testCase033 { XCTAssert(YES);} +- (void)testCase034 { XCTAssert(YES);} +- (void)testCase035 { XCTAssert(YES);} +- (void)testCase036 { XCTAssert(YES);} +- (void)testCase037 { XCTAssert(YES);} +- (void)testCase038 { XCTAssert(YES);} +- (void)testCase039 { XCTAssert(YES);} +- (void)testCase040 { XCTAssert(YES);} +- (void)testCase041 { XCTAssert(YES);} +- (void)testCase042 { XCTAssert(YES);} +- (void)testCase043 { XCTAssert(YES);} +- (void)testCase044 { XCTAssert(YES);} +- (void)testCase045 { XCTAssert(YES);} +- (void)testCase046 { XCTAssert(YES);} +- (void)testCase047 { XCTAssert(YES);} +- (void)testCase048 { XCTAssert(YES);} +- (void)testCase049 { XCTAssert(YES);} +- (void)testCase050 { XCTAssert(YES);} +- (void)testCase051 { XCTAssert(YES);} +- (void)testCase052 { XCTAssert(YES);} +- (void)testCase053 { XCTAssert(YES);} +- (void)testCase054 { XCTAssert(YES);} +- (void)testCase055 { XCTAssert(YES);} +- (void)testCase056 { XCTAssert(YES);} +- (void)testCase057 { XCTAssert(YES);} +- (void)testCase058 { XCTAssert(YES);} +- (void)testCase059 { XCTAssert(YES);} +- (void)testCase060 { XCTAssert(YES);} +- (void)testCase061 { XCTAssert(YES);} +- (void)testCase062 { XCTAssert(YES);} +- (void)testCase063 { XCTAssert(YES);} +- (void)testCase064 { XCTAssert(YES);} +- (void)testCase065 { XCTAssert(YES);} +- (void)testCase066 { XCTAssert(YES);} +- (void)testCase067 { XCTAssert(YES);} +- (void)testCase068 { XCTAssert(YES);} +- (void)testCase069 { XCTAssert(YES);} +- (void)testCase070 { XCTAssert(YES);} +- (void)testCase071 { XCTAssert(YES);} +- (void)testCase072 { XCTAssert(YES);} +- (void)testCase073 { XCTAssert(YES);} +- (void)testCase074 { XCTAssert(YES);} +- (void)testCase075 { XCTAssert(YES);} +- (void)testCase076 { XCTAssert(YES);} +- (void)testCase077 { XCTAssert(YES);} +- (void)testCase078 { XCTAssert(YES);} +- (void)testCase079 { XCTAssert(YES);} +- (void)testCase080 { XCTAssert(YES);} +- (void)testCase081 { XCTAssert(YES);} +- (void)testCase082 { XCTAssert(YES);} +- (void)testCase083 { XCTAssert(YES);} +- (void)testCase084 { XCTAssert(YES);} +- (void)testCase085 { XCTAssert(YES);} +- (void)testCase086 { XCTAssert(YES);} +- (void)testCase087 { XCTAssert(YES);} +- (void)testCase088 { XCTAssert(YES);} +- (void)testCase089 { XCTAssert(YES);} +- (void)testCase090 { XCTAssert(YES);} +- (void)testCase091 { XCTAssert(YES);} +- (void)testCase092 { XCTAssert(YES);} +- (void)testCase093 { XCTAssert(YES);} +- (void)testCase094 { XCTAssert(YES);} +- (void)testCase095 { XCTAssert(YES);} +- (void)testCase096 { XCTAssert(YES);} +- (void)testCase097 { XCTAssert(YES);} +- (void)testCase098 { XCTAssert(YES);} +- (void)testCase099 { XCTAssert(YES);} +- (void)testCase100 { XCTAssert(YES);} +- (void)testCase101 { XCTAssert(YES);} +- (void)testCase102 { XCTAssert(YES);} +- (void)testCase103 { XCTAssert(YES);} +- (void)testCase104 { XCTAssert(YES);} +- (void)testCase105 { XCTAssert(YES);} +- (void)testCase106 { XCTAssert(YES);} +- (void)testCase107 { XCTAssert(YES);} +- (void)testCase108 { XCTAssert(YES);} +- (void)testCase109 { XCTAssert(YES);} +- (void)testCase110 { XCTAssert(YES);} +- (void)testCase111 { XCTAssert(YES);} +- (void)testCase112 { XCTAssert(YES);} +- (void)testCase113 { XCTAssert(YES);} +- (void)testCase114 { XCTAssert(YES);} +- (void)testCase115 { XCTAssert(YES);} +- (void)testCase116 { XCTAssert(YES);} +- (void)testCase117 { XCTAssert(YES);} +- (void)testCase118 { XCTAssert(YES);} +- (void)testCase119 { XCTAssert(YES);} +- (void)testCase120 { XCTAssert(YES);} +- (void)testCase121 { XCTAssert(YES);} +- (void)testCase122 { XCTAssert(YES);} +- (void)testCase123 { XCTAssert(YES);} +- (void)testCase124 { XCTAssert(YES);} +- (void)testCase125 { XCTAssert(YES);} +- (void)testCase126 { XCTAssert(YES);} +- (void)testCase127 { XCTAssert(YES);} +- (void)testCase128 { XCTAssert(YES);} +- (void)testCase129 { XCTAssert(YES);} +- (void)testCase130 { XCTAssert(YES);} +- (void)testCase131 { XCTAssert(YES);} +- (void)testCase132 { XCTAssert(YES);} +- (void)testCase133 { XCTAssert(YES);} +- (void)testCase134 { XCTAssert(YES);} +- (void)testCase135 { XCTAssert(YES);} +- (void)testCase136 { XCTAssert(YES);} +- (void)testCase137 { XCTAssert(YES);} +- (void)testCase138 { XCTAssert(YES);} +- (void)testCase139 { XCTAssert(YES);} +- (void)testCase140 { XCTAssert(YES);} +- (void)testCase141 { XCTAssert(YES);} +- (void)testCase142 { XCTAssert(YES);} +- (void)testCase143 { XCTAssert(YES);} +- (void)testCase144 { XCTAssert(YES);} +- (void)testCase145 { XCTAssert(YES);} +- (void)testCase146 { XCTAssert(YES);} +- (void)testCase147 { XCTAssert(YES);} +- (void)testCase148 { XCTAssert(YES);} +- (void)testCase149 { XCTAssert(YES);} +- (void)testCase150 { XCTAssert(YES);} +- (void)testCase151 { XCTAssert(YES);} +- (void)testCase152 { XCTAssert(YES);} +- (void)testCase153 { XCTAssert(YES);} +- (void)testCase154 { XCTAssert(YES);} +- (void)testCase155 { XCTAssert(YES);} +- (void)testCase156 { XCTAssert(YES);} +- (void)testCase157 { XCTAssert(YES);} +- (void)testCase158 { XCTAssert(YES);} +- (void)testCase159 { XCTAssert(YES);} +- (void)testCase160 { XCTAssert(YES);} +- (void)testCase161 { XCTAssert(YES);} +- (void)testCase162 { XCTAssert(YES);} +- (void)testCase163 { XCTAssert(YES);} +- (void)testCase164 { XCTAssert(YES);} +- (void)testCase165 { XCTAssert(YES);} +- (void)testCase166 { XCTAssert(YES);} +- (void)testCase167 { XCTAssert(YES);} +- (void)testCase168 { XCTAssert(YES);} +- (void)testCase169 { XCTAssert(YES);} +- (void)testCase170 { XCTAssert(YES);} +- (void)testCase171 { XCTAssert(YES);} +- (void)testCase172 { XCTAssert(YES);} +- (void)testCase173 { XCTAssert(YES);} +- (void)testCase174 { XCTAssert(YES);} +- (void)testCase175 { XCTAssert(YES);} +- (void)testCase176 { XCTAssert(YES);} +- (void)testCase177 { XCTAssert(YES);} +- (void)testCase178 { XCTAssert(YES);} +- (void)testCase179 { XCTAssert(YES);} +- (void)testCase180 { XCTAssert(YES);} +- (void)testCase181 { XCTAssert(YES);} +- (void)testCase182 { XCTAssert(YES);} +- (void)testCase183 { XCTAssert(YES);} +- (void)testCase184 { XCTAssert(YES);} +- (void)testCase185 { XCTAssert(YES);} +- (void)testCase186 { XCTAssert(YES);} +- (void)testCase187 { XCTAssert(YES);} +- (void)testCase188 { XCTAssert(YES);} +- (void)testCase189 { XCTAssert(YES);} +- (void)testCase190 { XCTAssert(YES);} +- (void)testCase191 { XCTAssert(YES);} +- (void)testCase192 { XCTAssert(YES);} +- (void)testCase193 { XCTAssert(YES);} +- (void)testCase194 { XCTAssert(YES);} +- (void)testCase195 { XCTAssert(YES);} +- (void)testCase196 { XCTAssert(YES);} +- (void)testCase197 { XCTAssert(YES);} +- (void)testCase198 { XCTAssert(YES);} +- (void)testCase199 { XCTAssert(YES);} + +@end diff --git a/BPSampleApp/LogicTests/BPPassingLogicTests/BPPassingLogicTests.m b/BPSampleApp/LogicTests/BPPassingLogicTests/BPPassingLogicTests.m new file mode 100644 index 00000000..ba270a0a --- /dev/null +++ b/BPSampleApp/LogicTests/BPPassingLogicTests/BPPassingLogicTests.m @@ -0,0 +1,34 @@ +// Copyright 2016 LinkedIn Corporation +// Licensed under the BSD 2-Clause License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at https://opensource.org/licenses/BSD-2-Clause +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +#import + +@interface BPPassingLogicTests : XCTestCase + +@end + +@implementation BPPassingLogicTests + +- (void)testPassingLogicTest1 { + XCTAssert(YES); +} + +- (void)testPassingLogicTest2 { + XCTAssert(YES); +} + +- (void)testPassingLogicTest3 { + XCTAssert(YES); +} + +- (void)testPassingLogicTest4 { + XCTAssert(YES); +} + +@end diff --git a/BPSampleApp/LogicTests/BPPassingLogicTests/SwiftLogicTests.swift b/BPSampleApp/LogicTests/BPPassingLogicTests/SwiftLogicTests.swift new file mode 100644 index 00000000..09779d66 --- /dev/null +++ b/BPSampleApp/LogicTests/BPPassingLogicTests/SwiftLogicTests.swift @@ -0,0 +1,26 @@ +// Copyright 2016 LinkedIn Corporation +// Licensed under the BSD 2-Clause License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at https://opensource.org/licenses/BSD-2-Clause +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +import XCTest + +final class SwiftLogicTests: XCTestCase { + + func testPassingLogicTest1() { + XCTAssert(true) + } + + func testPassingLogicTest2() { + XCTAssert(true) + } + + func testPassingLogicTest3() { + XCTAssert(true) + } + +} diff --git a/BPSampleApp/LogicTests/Info.plist b/BPSampleApp/LogicTests/Info.plist new file mode 100644 index 00000000..6c6c23c4 --- /dev/null +++ b/BPSampleApp/LogicTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Bluepill.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Bluepill.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings index 54782e32..a6f6fb21 100644 --- a/Bluepill.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ b/Bluepill.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -4,5 +4,7 @@ IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + PreviewsEnabled + diff --git a/bluepill/bluepill.xcodeproj/xcshareddata/xcschemes/bluepill.xcscheme b/bluepill/bluepill.xcodeproj/xcshareddata/xcschemes/bluepill.xcscheme index a8b56da3..58639eb0 100644 --- a/bluepill/bluepill.xcodeproj/xcshareddata/xcschemes/bluepill.xcscheme +++ b/bluepill/bluepill.xcodeproj/xcshareddata/xcschemes/bluepill.xcscheme @@ -1,7 +1,7 @@ + version = "1.8"> diff --git a/bluepill/src/BPApp.h b/bluepill/src/BPApp.h index e48c4505..3b246210 100644 --- a/bluepill/src/BPApp.h +++ b/bluepill/src/BPApp.h @@ -8,7 +8,7 @@ // WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. #import -#import "bp/src/BPXCTestFile.h" +#import @class BPConfiguration; @interface BPApp : NSObject diff --git a/bluepill/src/BPApp.m b/bluepill/src/BPApp.m index ac134386..677f5c0c 100644 --- a/bluepill/src/BPApp.m +++ b/bluepill/src/BPApp.m @@ -8,9 +8,7 @@ // WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. #import "BPApp.h" -#import "bp/src/BPConstants.h" -#import "bp/src/BPConfiguration.h" -#import "bp/src/BPUtils.h" +#import @implementation BPApp @@ -94,28 +92,35 @@ @implementation BPApp return allXCTestFiles; } ++ (NSArray *)testsFromConfig:(BPConfiguration *)config + withError:(NSError *__autoreleasing *)errPtr { + NSMutableArray *loadedTests = [[NSMutableArray alloc] initWithCapacity:config.tests.count]; + for (NSString *testName in config.tests) { + BPTestPlan *testPlan = [config.tests objectForKey:testName]; + BPXCTestFile *xcTestFile = [BPXCTestFile BPXCTestFileFromBPTestPlan:testPlan withName:testName andError:errPtr]; + if (*errPtr) { + return nil; + } + [loadedTests addObject:xcTestFile]; + } + return loadedTests; +} + + (instancetype)appWithConfig:(BPConfiguration *)config withError:(NSError *__autoreleasing *)errPtr { - + BPApp *app = [[BPApp alloc] init]; NSMutableArray *allXCTestFiles = [[NSMutableArray alloc] init]; - + if (config.tests != nil && config.tests.count != 0) { [BPUtils printInfo:INFO withString:@"Using test bundles"]; - NSMutableArray *loadedTests = [[NSMutableArray alloc] initWithCapacity:config.tests.count]; - for (NSString *testName in config.tests) { - BPTestPlan *testPlan = [config.tests objectForKey:testName]; - BPXCTestFile *xcTestFile = [BPXCTestFile BPXCTestFileFromBPTestPlan:testPlan withName:testName andError:errPtr]; - if (*errPtr) - return nil; - [loadedTests addObject:xcTestFile]; + app.testBundles = [self testsFromConfig:config withError:errPtr]; + if (*errPtr) { + return nil; } - - app.testBundles = loadedTests; - return app; } - + if (config.xcTestRunDict) { NSAssert(config.xcTestRunPath, @""); [BPUtils printInfo:INFO withString:@"Using xctestrun configuration"]; @@ -126,7 +131,7 @@ + (instancetype)appWithConfig:(BPConfiguration *)config if (loadedTests == nil) { return nil; } - + [allXCTestFiles addObjectsFromArray:loadedTests]; } else if (config.appBundlePath) { NSAssert(config.appBundlePath, @"no app bundle and no xctestrun file"); @@ -135,6 +140,12 @@ + (instancetype)appWithConfig:(BPConfiguration *)config andTestBundlePath:config.testBundlePath andUITargetAppPath:config.testRunnerAppPath withError:errPtr]]; + } else if (config.isLogicTestTarget) { + BPXCTestFile *testFile = [BPXCTestFile BPXCTestFileFromXCTestBundle:config.testBundlePath + andHostAppBundle:nil + andUITargetAppPath:nil + withError:errPtr]; + [allXCTestFiles addObject:testFile]; } else { BP_SET_ERROR(errPtr, @"xctestrun file must be given, see usage."); return nil; diff --git a/bluepill/src/BPHTMLReportWriter.m b/bluepill/src/BPHTMLReportWriter.m index f5057474..38ed8f67 100644 --- a/bluepill/src/BPHTMLReportWriter.m +++ b/bluepill/src/BPHTMLReportWriter.m @@ -9,7 +9,7 @@ #import #import "BPTestReportHTML.h" -#import "bp/src/BPUtils.h" +#import #import "BPHTMLReportWriter.h" diff --git a/bluepill/src/BPPacker.h b/bluepill/src/BPPacker.h index 51e8e67d..b2fdbb4c 100644 --- a/bluepill/src/BPPacker.h +++ b/bluepill/src/BPPacker.h @@ -8,8 +8,7 @@ // WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. #import -#import "bp/src/BPConfiguration.h" -#import "bp/src/BPXCTestFile.h" +#import @interface BPPacker : NSObject diff --git a/bluepill/src/BPPacker.m b/bluepill/src/BPPacker.m index 5ec0f259..d9493a1a 100644 --- a/bluepill/src/BPPacker.m +++ b/bluepill/src/BPPacker.m @@ -7,8 +7,7 @@ // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -#import "bp/src/BPXCTestFile.h" -#import "bp/src/BPUtils.h" +#import #import "BPPacker.h" @implementation BPPacker diff --git a/bluepill/src/BPReportCollector.m b/bluepill/src/BPReportCollector.m index 211706d8..b67e0dd1 100644 --- a/bluepill/src/BPReportCollector.m +++ b/bluepill/src/BPReportCollector.m @@ -9,7 +9,7 @@ #import "BPHTMLReportWriter.h" #import "BPReportCollector.h" -#import "bp/src/BPUtils.h" +#import // Save path and mtime for reports (sort by mtime) @interface BPXMLReport:NSObject diff --git a/bluepill/src/BPRunner.h b/bluepill/src/BPRunner.h index 21d19f54..fd49e4e0 100644 --- a/bluepill/src/BPRunner.h +++ b/bluepill/src/BPRunner.h @@ -8,8 +8,7 @@ // WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. #import -#import "bp/src/BPXCTestFile.h" -#import "bp/src/BPConfiguration.h" +#import @interface BPRunner : NSObject diff --git a/bluepill/src/BPRunner.m b/bluepill/src/BPRunner.m index 5fdf6162..d013a73b 100644 --- a/bluepill/src/BPRunner.m +++ b/bluepill/src/BPRunner.m @@ -8,12 +8,7 @@ // WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. #import -#import "bp/src/BPCreateSimulatorHandler.h" -#import "bp/src/BPSimulator.h" -#import "bp/src/BPStats.h" -#import "bp/src/BPUtils.h" -#import "bp/src/BPWaitTimer.h" -#import "bp/src/SimulatorHelper.h" +#import #import "BPPacker.h" #import "BPRunner.h" #import "BPSwimlane.h" @@ -132,12 +127,14 @@ - (int)runWithBPXCTestFiles:(NSArray *)xcTestFiles { [BPUtils printInfo:ERROR withString:@"Packing failed: %@", [error localizedDescription]]; return 1; } + if (bundles.count < numSims) { [BPUtils printInfo:WARNING withString:@"Lowering number of parallel simulators from %lu to %lu because there aren't enough test bundles.", numSims, bundles.count]; numSims = bundles.count; } + if (self.config.cloneSimulator) { self.testHostSimTemplates = [bpSimulator createSimulatorAndInstallAppWithBundles:xcTestFiles]; if ([self.testHostSimTemplates count] == 0) { @@ -172,6 +169,7 @@ - (int)runWithBPXCTestFiles:(NSArray *)xcTestFiles { return -1; } } + while (1) { if (interrupted) { if (interrupted >=5) { @@ -222,6 +220,8 @@ - (int)runWithBPXCTestFiles:(NSArray *)xcTestFiles { [bundles removeObjectAtIndex:0]; } } + + // Resume in a second. Every 30 seconds, log status. sleep(1); if (seconds % 30 == 0) { NSString *listString; @@ -251,6 +251,7 @@ - (int)runWithBPXCTestFiles:(NSArray *)xcTestFiles { [self addCounters]; } + // Cleanup Devices for (int i = 0; i < [deviceList count]; i++) { NSTask *task = [self newTaskToDeleteDevice:[deviceList objectAtIndex:i] andNumber:i+1]; [task launch]; diff --git a/bluepill/src/BPSwimlane.h b/bluepill/src/BPSwimlane.h index 4bcfd60f..dd2f6a9e 100644 --- a/bluepill/src/BPSwimlane.h +++ b/bluepill/src/BPSwimlane.h @@ -8,8 +8,7 @@ // WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. #import -#import "bp/src/BPXCTestFile.h" -#import "bp/src/BPConfiguration.h" +#import @interface BPSwimlane : NSObject diff --git a/bluepill/src/BPSwimlane.m b/bluepill/src/BPSwimlane.m index 024020fd..36ed0bf3 100644 --- a/bluepill/src/BPSwimlane.m +++ b/bluepill/src/BPSwimlane.m @@ -7,7 +7,7 @@ // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -#import "bp/src/BPUtils.h" +#import #import "BPSwimlane.h" @interface BPSwimlane() diff --git a/bluepill/src/main.m b/bluepill/src/main.m index 0bd4884e..ff897113 100644 --- a/bluepill/src/main.m +++ b/bluepill/src/main.m @@ -10,13 +10,10 @@ #import #import #import -#import "bp/src/BPConfiguration.h" -#import "bp/src/BPStats.h" -#import "bp/src/BPUtils.h" -#import "bp/src/BPWriter.h" #import "BPApp.h" #import "BPReportCollector.h" #import "BPRunner.h" +#import #include #include diff --git a/bluepill/tests/BPAppTests.m b/bluepill/tests/BPAppTests.m index 467978dc..782425e0 100644 --- a/bluepill/tests/BPAppTests.m +++ b/bluepill/tests/BPAppTests.m @@ -9,11 +9,7 @@ #import #import "bluepill/src/BPApp.h" -#import "bp/src/BPConfiguration.h" -#import "bp/src/BPXCTestFile.h" -#import "bp/src/BPUtils.h" -#import "bp/src/BPConstants.h" -#import "bp/tests/BPTestHelper.h" +#import @interface BPAppTests : XCTestCase @property (nonatomic, strong) BPConfiguration* config; diff --git a/bluepill/tests/BPCLITests.m b/bluepill/tests/BPCLITests.m index ae51d43d..b9d27aa9 100644 --- a/bluepill/tests/BPCLITests.m +++ b/bluepill/tests/BPCLITests.m @@ -8,9 +8,7 @@ // WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. #import -#import "bp/src/BPConfiguration.h" -#import "bp/src/BPUtils.h" -#import "bp/tests/BPTestHelper.h" +#import @interface BPCLITests : XCTestCase diff --git a/bluepill/tests/BPIntegrationTests.m b/bluepill/tests/BPIntegrationTests.m index 84dbe4d2..356bd8cf 100644 --- a/bluepill/tests/BPIntegrationTests.m +++ b/bluepill/tests/BPIntegrationTests.m @@ -7,14 +7,12 @@ // #import -#import "bp/tests/BPTestHelper.h" -#import "bp/src/BPConfiguration.h" -#import "bp/src/BPUtils.h" #import "bluepill/src/BPRunner.h" #import "bluepill/src/BPApp.h" #import "bluepill/src/BPPacker.h" -#import "bp/src/BPXCTestFile.h" -#import "bp/src/BPConstants.h" + +#import +#import @interface BPIntegrationTests : XCTestCase @end @@ -59,6 +57,34 @@ - (void)tearDown { [super tearDown]; } +- (void)testLogicTestBundles { + BPConfiguration *config = [BPTestUtils makeUnhostedTestConfiguration]; + config.stuckTimeout = @(2); + config.testCaseTimeout = @(10); + + // Test multiple test bundles, while skipping any failing tests so that we + // can still validate that we get a success code.. + config.testBundlePath = BPTestHelper.passingLogicTestBundlePath; + config.additionalUnitTestBundles = @[BPTestHelper.logicTestBundlePath]; + config.testCasesToSkip = @[ + @"BPLogicTests/testFailingLogicTest", + @"BPLogicTests/testCrashTestCaseLogicTest", + @"BPLogicTests/testCrashExecutionLogicTest", + @"BPLogicTests/testStuckLogicTest", + @"BPLogicTests/testSlowLogicTest", + ]; + + NSString *bpPath = [BPTestHelper bpExecutablePath]; + BPRunner *runner = [BPRunner BPRunnerWithConfig:config withBpPath:bpPath]; + NSError *error; + BPApp *app = [BPApp appWithConfig:config withError:&error]; + + XCTAssert(runner != nil); + int rc = [runner runWithBPXCTestFiles:app.testBundles]; + XCTAssert(rc == 0, @"Wanted 0, got %d", rc); + XCTAssert([runner busySwimlaneCount] == 0); +} + - (void)testOneBPInstance { BPConfiguration *config = [self generateConfig]; config.numSims = @1; diff --git a/bluepill/tests/BPPackerTests.m b/bluepill/tests/BPPackerTests.m index 98826a73..ed67343d 100644 --- a/bluepill/tests/BPPackerTests.m +++ b/bluepill/tests/BPPackerTests.m @@ -10,11 +10,7 @@ #import "bluepill/src/BPRunner.h" #import "bluepill/src/BPApp.h" #import "bluepill/src/BPPacker.h" -#import "bp/tests/BPTestHelper.h" -#import "bp/src/BPConfiguration.h" -#import "bp/src/BPUtils.h" -#import "bp/src/BPXCTestFile.h" -#import "bp/src/BPConstants.h" +#import @interface BPPackerTests : XCTestCase @property (nonatomic, strong) BPConfiguration* config; diff --git a/bluepill/tests/BPRunnerTests.m b/bluepill/tests/BPRunnerTests.m index 37c4f0cc..a28489bd 100644 --- a/bluepill/tests/BPRunnerTests.m +++ b/bluepill/tests/BPRunnerTests.m @@ -9,13 +9,10 @@ #import #import "bp/tests/BPTestHelper.h" -#import "bp/src/BPConfiguration.h" -#import "bp/src/BPUtils.h" +#import #import "bluepill/src/BPRunner.h" #import "bluepill/src/BPApp.h" #import "bluepill/src/BPPacker.h" -#import "bp/src/BPXCTestFile.h" -#import "bp/src/BPConstants.h" @interface BPRunnerTests : XCTestCase @property (nonatomic, strong) BPConfiguration* config; diff --git a/bp/bp.xcodeproj/project.pbxproj b/bp/bp.xcodeproj/project.pbxproj index ccbf023c..d8c461cc 100644 --- a/bp/bp.xcodeproj/project.pbxproj +++ b/bp/bp.xcodeproj/project.pbxproj @@ -69,7 +69,7 @@ BA0096FE1DCA5D810000DD45 /* testConfigRelativePath.json in Resources */ = {isa = PBXBuildFile; fileRef = BA0096FD1DCA5D810000DD45 /* testConfigRelativePath.json */; }; BA0097001DCA61210000DD45 /* BPConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA0096FF1DCA61210000DD45 /* BPConfigurationTests.m */; }; BA0C554D1DDAE241009E1377 /* failure_retry_report.xml in Resources */ = {isa = PBXBuildFile; fileRef = BA0C554C1DDAE241009E1377 /* failure_retry_report.xml */; }; - BA1809B81DB89B5600D7D130 /* BluepillTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA1809B71DB89B5600D7D130 /* BluepillTests.m */; }; + BA1809B81DB89B5600D7D130 /* BluepillHostedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA1809B71DB89B5600D7D130 /* BluepillHostedTests.m */; }; BA180A091DBB00FA00D7D130 /* BPUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA180A081DBB00FA00D7D130 /* BPUtilsTests.m */; }; BA180A0C1DBB011200D7D130 /* testConfig.json in Resources */ = {isa = PBXBuildFile; fileRef = BA180A0B1DBB011200D7D130 /* testConfig.json */; }; BA180A141DBB088100D7D130 /* testScheme.xcscheme in Resources */ = {isa = PBXBuildFile; fileRef = BA180A131DBB088100D7D130 /* testScheme.xcscheme */; }; @@ -109,6 +109,11 @@ C4F08F75224C45750001AD2A /* BPExitStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A4D7A851DDBB156001E085D /* BPExitStatus.m */; }; C4FAC2951E5E67ED00ACC5D9 /* testConfig-busted.json in Resources */ = {isa = PBXBuildFile; fileRef = C4FAC2941E5E67ED00ACC5D9 /* testConfig-busted.json */; }; C94DE0BB4360016D3D3061D9 /* simulator-preferences.plist in Resources */ = {isa = PBXBuildFile; fileRef = C94DEF7F8BCA7AB3C9114467 /* simulator-preferences.plist */; }; + FB1C695629A6E58500BFDFB4 /* BluepillUnhostedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1C695529A6E58500BFDFB4 /* BluepillUnhostedTests.m */; }; + FBBBD8EF29A06AF6002B9115 /* BPTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */; }; + FBD772BF2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD772BE2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m */; }; + FBD772C12A3452240098CEFD /* BPTestUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FBBBD8EE29A06AF6002B9115 /* BPTestUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FBD772C22A3453010098CEFD /* BPTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -128,6 +133,19 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + FB9F19452A32F0EF00C894D3 /* Embed Libraries */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Libraries"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 018D5C1025B4FF4200B0314B /* BPIntTestCase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPIntTestCase.h; sourceTree = ""; }; 018D5C1125B4FF4200B0314B /* BPIntTestCase.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPIntTestCase.m; sourceTree = ""; }; @@ -193,7 +211,7 @@ BA0096FF1DCA61210000DD45 /* BPConfigurationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPConfigurationTests.m; sourceTree = ""; }; BA0097011DCA626F0000DD45 /* BPConfiguration+Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "BPConfiguration+Test.h"; sourceTree = ""; }; BA0C554C1DDAE241009E1377 /* failure_retry_report.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = failure_retry_report.xml; sourceTree = ""; }; - BA1809B71DB89B5600D7D130 /* BluepillTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BluepillTests.m; sourceTree = ""; }; + BA1809B71DB89B5600D7D130 /* BluepillHostedTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BluepillHostedTests.m; sourceTree = ""; }; BA1809BC1DB89B8B00D7D130 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/AppKit.framework; sourceTree = DEVELOPER_DIR; }; BA180A081DBB00FA00D7D130 /* BPUtilsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPUtilsTests.m; sourceTree = ""; }; BA180A0B1DBB011200D7D130 /* testConfig.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = testConfig.json; sourceTree = ""; }; @@ -292,9 +310,6 @@ BAFCCA541E36DBA900E33C31 /* DTXTransport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DTXTransport.h; sourceTree = ""; }; BAFCCA581E36DBA900E33C31 /* NSURLSessionDelegate-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLSessionDelegate-Protocol.h"; sourceTree = ""; }; BAFCCA6D1E36EB5500E33C31 /* XCTestManager_IDEInterface-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTestManager_IDEInterface-Protocol.h"; sourceTree = ""; }; - BAFCCA6E1E36EB5500E33C31 /* XCTestManager_ManagerInterface-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTestManager_ManagerInterface-Protocol.h"; sourceTree = ""; }; - BAFCCA6F1E36EB5500E33C31 /* XCTestManager_TestsInterface-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTestManager_TestsInterface-Protocol.h"; sourceTree = ""; }; - BAFCCA701E37298B00E33C31 /* XCTestDriverInterface-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCTestDriverInterface-Protocol.h"; sourceTree = ""; }; BAFCCA711E37298B00E33C31 /* XCTestManager_DaemonConnectionInterface-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCTestManager_DaemonConnectionInterface-Protocol.h"; sourceTree = ""; }; C41A2C701E0B2497005D9751 /* BPXCTestFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPXCTestFile.h; sourceTree = ""; }; C41A2C711E0B2497005D9751 /* BPXCTestFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = BPXCTestFile.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; @@ -314,6 +329,13 @@ C4AF1ADE2273649500618F0B /* BPVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPVersion.h; sourceTree = ""; }; C4FAC2941E5E67ED00ACC5D9 /* testConfig-busted.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "testConfig-busted.json"; sourceTree = ""; }; C94DEF7F8BCA7AB3C9114467 /* simulator-preferences.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = "simulator-preferences.plist"; sourceTree = ""; }; + FB1C695529A6E58500BFDFB4 /* BluepillUnhostedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BluepillUnhostedTests.m; sourceTree = ""; }; + FB9F19302A32E97B00C894D3 /* libBPXCTestWrapper.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libBPXCTestWrapper.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPTestUtils.m; sourceTree = ""; }; + FBBBD8EE29A06AF6002B9115 /* BPTestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPTestUtils.h; sourceTree = ""; }; + FBD772AA2A32F2300098CEFD /* BPTestCaseInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BPTestCaseInfo.m; path = ../BPXCTestWrapper/BPTestCaseInfo.m; sourceTree = ""; }; + FBD772AB2A32F2300098CEFD /* BPTestCaseInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BPTestCaseInfo.h; path = ../BPXCTestWrapper/BPTestCaseInfo.h; sourceTree = ""; }; + FBD772BE2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BluepillUnhostedBatchingTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -349,12 +371,10 @@ 7A4FB8CC1DF89A790073F268 /* src */ = { isa = PBXGroup; children = ( - C487CB1A226A36F500CC5BC2 /* BPVersion.h */, BA1949341E4AF82F00881887 /* BPTMDRunnerConnection.h */, BA1949351E4AF82F00881887 /* BPTMDRunnerConnection.m */, BA1949381E4AF83E00881887 /* BPTMDControlConnection.h */, BA1949391E4AF83E00881887 /* BPTMDControlConnection.m */, - BA954F551D6D1AB3007D011D /* PrivateHeaders */, BA34F5E21D6D75E30063B17F /* SimulatorHelper.h */, BA34F5E31D6D75E30063B17F /* SimulatorHelper.m */, 7A4933131DAD63A50060D54F /* SimulatorMonitor.h */, @@ -403,8 +423,10 @@ BA944BCF1D76A39A00A4BDA3 /* Bluepill.m */, 7A7E7BBE1DF22CE1007928F3 /* BPExecutionContext.h */, 7A7E7BBF1DF22CE1007928F3 /* BPExecutionContext.m */, - B368E55D213F8D2E00B4DEA3 /* Info.plist */, 7A7901851D5CB679004D4325 /* main.m */, + BA954F551D6D1AB3007D011D /* PrivateHeaders */, + C487CB1A226A36F500CC5BC2 /* BPVersion.h */, + B368E55D213F8D2E00B4DEA3 /* Info.plist */, ); path = src; sourceTree = ""; @@ -435,6 +457,9 @@ 7A79018D1D5D136F004D4325 /* Frameworks */ = { isa = PBXGroup; children = ( + FBD772AB2A32F2300098CEFD /* BPTestCaseInfo.h */, + FBD772AA2A32F2300098CEFD /* BPTestCaseInfo.m */, + FB9F19302A32E97B00C894D3 /* libBPXCTestWrapper.dylib */, C47411F7264F4C3F000F84D1 /* XcodeKit.framework */, BA1896B6217916F8000CEC36 /* XCTest.framework */, BA1896B321791683000CEC36 /* CoreSimulator */, @@ -491,11 +516,8 @@ BA34F5E51D6D78120063B17F /* XCTest */ = { isa = PBXGroup; children = ( - BAFCCA701E37298B00E33C31 /* XCTestDriverInterface-Protocol.h */, BAFCCA711E37298B00E33C31 /* XCTestManager_DaemonConnectionInterface-Protocol.h */, BAFCCA6D1E36EB5500E33C31 /* XCTestManager_IDEInterface-Protocol.h */, - BAFCCA6E1E36EB5500E33C31 /* XCTestManager_ManagerInterface-Protocol.h */, - BAFCCA6F1E36EB5500E33C31 /* XCTestManager_TestsInterface-Protocol.h */, BA34F5E61D6D78120063B17F /* XCTestConfiguration.h */, ); path = XCTest; @@ -562,8 +584,10 @@ BAB24F691DB5DB2300867756 /* tests */ = { isa = PBXGroup; children = ( + FBBBD8EC29A06AE7002B9115 /* Utils */, BA180A0A1DBB011200D7D130 /* Resource Files */, - BA1809B71DB89B5600D7D130 /* BluepillTests.m */, + BA1809B71DB89B5600D7D130 /* BluepillHostedTests.m */, + FBD772C02A33E1E20098CEFD /* Unhosted Tests */, C467E5491DC930D200BC80EE /* BPCLITests.m */, BA0097011DCA626F0000DD45 /* BPConfiguration+Test.h */, BA0096FF1DCA61210000DD45 /* BPConfigurationTests.m */, @@ -618,6 +642,24 @@ path = DTXConnectionServices; sourceTree = ""; }; + FBBBD8EC29A06AE7002B9115 /* Utils */ = { + isa = PBXGroup; + children = ( + FBBBD8EE29A06AF6002B9115 /* BPTestUtils.h */, + FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */, + ); + path = Utils; + sourceTree = ""; + }; + FBD772C02A33E1E20098CEFD /* Unhosted Tests */ = { + isa = PBXGroup; + children = ( + FB1C695529A6E58500BFDFB4 /* BluepillUnhostedTests.m */, + FBD772BE2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m */, + ); + path = "Unhosted Tests"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -635,6 +677,7 @@ B3520CE2214C5DE3003DC68F /* BPDeleteSimulatorHandler.h in Headers */, B368E579213F965600B4DEA3 /* BPTestCase.h in Headers */, B368E57A213F965600B4DEA3 /* BPTestClass.h in Headers */, + FBD772C12A3452240098CEFD /* BPTestUtils.h in Headers */, B368E57B213F965600B4DEA3 /* BPUtils.h in Headers */, B368E57C213F965600B4DEA3 /* BPXCTestFile.h in Headers */, B368E571213F8E8F00B4DEA3 /* BPConstants.h in Headers */, @@ -679,6 +722,7 @@ B368E556213F8D2E00B4DEA3 /* Frameworks */, B368E557213F8D2E00B4DEA3 /* Headers */, B368E558213F8D2E00B4DEA3 /* Resources */, + FB9F19452A32F0EF00C894D3 /* Embed Libraries */, ); buildRules = ( ); @@ -843,6 +887,7 @@ C4F08F75224C45750001AD2A /* BPExitStatus.m in Sources */, B3DAF83E2151CB9700210286 /* BPWaitTimer.m in Sources */, B3DAF83D2151CB7000210286 /* BPHandler.m in Sources */, + FBD772C22A3453010098CEFD /* BPTestUtils.m in Sources */, B3DAF83C2151CB4100210286 /* BPDeleteSimulatorHandler.m in Sources */, B3DAF83B2151CB3300210286 /* BPCreateSimulatorHandler.m in Sources */, BA9ADEB421657004001306F5 /* BPApplicationLaunchHandler.m in Sources */, @@ -863,6 +908,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FBD772BF2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m in Sources */, BA19493B1E4AF83E00881887 /* BPTMDControlConnection.m in Sources */, BAB24F711DB5DBED00867756 /* SimulatorHelperTests.m in Sources */, 7A4D7A811DDA5FA1001E085D /* BPTreeParserTests.m in Sources */, @@ -874,7 +920,7 @@ 7ACE1F721DD3D27D00C0FA73 /* WaitTimerTests.m in Sources */, BA180A091DBB00FA00D7D130 /* BPUtilsTests.m in Sources */, 7A4D7A871DDBB156001E085D /* BPExitStatus.m in Sources */, - BA1809B81DB89B5600D7D130 /* BluepillTests.m in Sources */, + BA1809B81DB89B5600D7D130 /* BluepillHostedTests.m in Sources */, BA1949371E4AF82F00881887 /* BPTMDRunnerConnection.m in Sources */, 7A7E7BC11DF22CE1007928F3 /* BPExecutionContext.m in Sources */, BAD558D71DB6DCB100C9A5CD /* BPWriter.m in Sources */, @@ -884,6 +930,8 @@ BAD558D51DB6DCB100C9A5CD /* BPTreeObjects.m in Sources */, BA0097001DCA61210000DD45 /* BPConfigurationTests.m in Sources */, BAB24F741DB5DFA200867756 /* BPTestHelper.m in Sources */, + FB1C695629A6E58500BFDFB4 /* BluepillUnhostedTests.m in Sources */, + FBBBD8EF29A06AF6002B9115 /* BPTestUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/bp/src/BPConfiguration.h b/bp/src/BPConfiguration.h index 3e845ab9..ebd949b9 100644 --- a/bp/src/BPConfiguration.h +++ b/bp/src/BPConfiguration.h @@ -53,6 +53,7 @@ typedef NS_ENUM(NSInteger, BPProgram) { */ @property (nonatomic, strong) NSUUID *sessionIdentifier; +@property (nonatomic) BOOL isLogicTestTarget; @property (nonatomic, strong) NSString *appBundlePath; // XCUITest sector diff --git a/bp/src/BPConfiguration.m b/bp/src/BPConfiguration.m index 92c02ae8..f72b7899 100644 --- a/bp/src/BPConfiguration.m +++ b/bp/src/BPConfiguration.m @@ -150,6 +150,8 @@ typedef NS_OPTIONS(NSUInteger, BPOptionType) { "Directory where videos of test runs will be saved. If not provided, videos are not recorded."}, {368, "keep-passing-videos", BLUEPILL_BINARY | BP_BINARY, NO, NO, no_argument, "Off", BP_VALUE | BP_BOOL, "keepPassingVideos", "Whether recorded videos should be kept if the test passed. They are deleted by default."}, + {369, "logic-test", BLUEPILL_BINARY | BP_BINARY, NO, NO, no_argument, "Off", BP_VALUE | BP_BOOL, "isLogicTestTarget", + "Will run the tests without an app host"}, {0, 0, 0, 0, 0, 0, 0} }; @@ -168,9 +170,6 @@ - (BOOL)isValid:(NSError **)errPtr { if (!self.testBundlePath) { [errors addObject:@"testBundlePath field is nil"]; } - if (!self.testHost) { - [errors addObject:@"testHost field is nil"]; - } if ([errors count] > 0) { BP_SET_ERROR(errPtr, [NSString stringWithFormat:@"Invalid BPTestPlan object, %@.", @@ -614,7 +613,7 @@ - (BOOL)processOptionsWithError:(NSError **)errPtr { } // Now check we didn't miss any require options: NSMutableArray *errors = [[NSMutableArray alloc] init]; - if (!(self.appBundlePath) && !(self.xcTestRunPath) && !(self.testPlanPath) && !(self.deleteSimUDID)) { + if (!(self.appBundlePath) && !(self.xcTestRunPath) && !(self.testPlanPath) && !(self.deleteSimUDID) && !(self.isLogicTestTarget)) { [errors addObject:@"Missing required option: -a/--app OR --xctestrun-path OR --test-plan-path"]; } if ((self.program & BP_BINARY) && !(self.testBundlePath) && !(self.testPlanPath) && !(self.deleteSimUDID)) { @@ -707,7 +706,7 @@ - (BOOL)validateConfigWithError:(NSError *__autoreleasing *)errPtr { return NO; } - if (!self.appBundlePath && !self.xcTestRunDict && !self.tests) { + if (!self.appBundlePath && !self.xcTestRunDict && !self.tests && !self.isLogicTestTarget) { BP_SET_ERROR(errPtr, @"No app bundle provided."); return NO; } diff --git a/bp/src/BPSimulator.h b/bp/src/BPSimulator.h index c1f652c9..449da64d 100644 --- a/bp/src/BPSimulator.h +++ b/bp/src/BPSimulator.h @@ -42,6 +42,16 @@ - (void)launchApplicationAndExecuteTestsWithParser:(BPTreeParser *)parser andCompletion:(void (^)(NSError *, pid_t))completion; +/** + Executes logic tests for the provided context, providing two callbacks as the execution starts and finishes. + @param parser The test log parser + @param spawnBlock Called once the process is spawned (right after it's started and the process is actually running) + @param completionBlock Called once the process completes, and all tests have either passed/failed/erred. + */ +- (void)executeLogicTestsWithParser:(BPTreeParser *)parser + onSpawn:(void (^)(NSError *, pid_t))spawnBlock + andCompletion:(void (^)(NSError *, pid_t))completionBlock; + - (void)deleteSimulatorWithCompletion:(void (^)(NSError *error, BOOL success))completion; - (void)addPhotosToSimulator; diff --git a/bp/src/BPSimulator.m b/bp/src/BPSimulator.m index 0b8aa113..8dd8bc0f 100644 --- a/bp/src/BPSimulator.m +++ b/bp/src/BPSimulator.m @@ -435,18 +435,149 @@ - (BOOL)uninstallApplicationWithError:(NSError *__autoreleasing *)errPtr { error:errPtr]; } -- (void)launchApplicationAndExecuteTestsWithParser:(BPTreeParser *)parser andCompletion:(void (^)(NSError *, pid_t))completion { - NSString *hostBundleId = [SimulatorHelper bundleIdForPath:self.config.appBundlePath]; - NSString *hostAppExecPath = [SimulatorHelper executablePathforPath:self.config.appBundlePath]; +/** + Logic tests are run directly on the simulator by spawning a new process with the XCTest executable, without any kind of host app. + */ +- (void)executeLogicTestsWithParser:(BPTreeParser *)parser + onSpawn:(void (^)(NSError *, pid_t))spawnBlock + andCompletion:(void (^)(NSError *, pid_t))completionBlock { + /* + Grab all test cases so that we can: + 1) create a timeout for the full test execution + 2) Support opting-out of tests, despite the fact that XCTest only provides an opt-in API. + */ + NSArray *testsToRun = [SimulatorHelper testsToRunWithConfig:self.config]; + NSString *testsToRunArg = testsToRun.count == self.config.allTestCases.count ? @"All" : [testsToRun componentsJoinedByString:@","]; + + /* + It can be useful to understand how to translate the public Breaking down CLI command `xcrun simctl spawn... `, which you'd + use to run a logic test from commandline, into the form that Bluepill must use, which is CoreSimulator's private `spawnWithPath`: + + When trying to run a command such as + ``` + xcrun simctl spawn -s AFF4165A-9A71-4860-8B6D-485B7D1BA2BC + /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Agents/xctest + -XCTest BPLogicTests/testPassingLogicTest /Users/.../BPLogicTests.xctest + ``` + we can break down the individual components to be handled in the following: + - part of the method signature: + - xcrun simctl // these just redirect down to CoreSimulator + - spawn // this is the the spawn method :p + - AFF4165A-9A71-4860-8B6D-485B7D1BA2BC // We're calling device.spawn. No need to respecify the device ID + - path: /Applications/.../Xcode/Agents/xctest + - options: + - -s // "standalone" option + - -w // "wait_for_debugger" option + - -a // "arch" option + - args: + - /Applications/.../Xcode/Agents/xctest // Yes, spawn redundantly requires this both the path param + an arg :) + - -XCTest + - BPLogicTests/testPassingLogicTest // The filter for which tests to actually run. `All` is the catch-all. + - /Users/.../BPLogicTests.xctest // The path to the .xctest file w/ all the module's tests. + + From `xcrun simctl spawn help`: + `simctl spawn [-w | --wait-for-debugger] [-s | --standalone] [-a | --arch=] [ ... ]` + */ + + NSString *executablePath = [[NSString alloc] initWithFormat: + @"%@/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Agents/xctest", + self.config.xcodePath]; + NSString *xctestPath = self.config.testBundlePath; + NSLog(@"%@", testsToRunArg); + NSArray *arguments = @[ + executablePath, + @"-XCTest", + testsToRunArg, + xctestPath, + ]; - // One bp instance only run one kind of xctest file. - if (self.config.testRunnerAppPath) { - hostAppExecPath = [SimulatorHelper executablePathforPath:self.config.testRunnerAppPath]; - hostBundleId = [SimulatorHelper bundleIdForPath:self.config.testRunnerAppPath]; + // Intercept stdout, stderr and post as simulator-output events + NSString *simStdoutPath = [SimulatorHelper makeStdoutFileOnDevice:self.device]; + NSString *simStdoutRelativePath = [simStdoutPath substringFromIndex:self.device.dataPath.length]; + // Environment + NSDictionary *environment = @{ + kOptionsStdoutKey: simStdoutRelativePath, + kOptionsStderrKey: simStdoutRelativePath, + }; + self.appOutput = [NSFileHandle fileHandleForReadingAtPath:simStdoutPath]; + + NSFileHandle *outputFileHandle = [NSFileHandle fileHandleForWritingAtPath:simStdoutPath]; + NSNumber *stdoutFileDescriptor = @(outputFileHandle.fileDescriptor); + + NSDictionary *options = @{ + kOptionsArgumentsKey: arguments, + kOptionsEnvironmentKey: environment, + @"standalone": @(1), + @"stdout": stdoutFileDescriptor, + @"stderr": stdoutFileDescriptor, + }; + + // Set up monitor + if (!self.monitor) { + self.monitor = [[SimulatorMonitor alloc] initWithConfiguration:self.config]; } - // Create the environment for the host application - NSMutableDictionary *argsAndEnv = [[NSMutableDictionary alloc] init]; - NSArray *argumentsArr = self.config.commandLineArguments ?: @[]; + self.monitor.device = self.device; + parser.delegate = self.monitor; + + self.appOutput.readabilityHandler = ^(NSFileHandle *handle) { + // This callback occurs on a background thread + NSData *chunk = [handle availableData]; + [parser handleChunkData:chunk]; + }; + + // To see more on how to debug the expected format/inputs of the options array, + // see the in-depth documentation in SimDevice.h. + __block typeof(self) blockSelf = self; + [self.device spawnAsyncWithPath:executablePath + options:options + terminationHandler:^(int stat_loc) { + // The naming here is confusing, but this `terminationHandler` is called once + // the xctest process COMPLETES. The `completionHandler` below is used earlier, + // once the xctest process is SPAWNED. + + // Check the location where the status code is stored; + // Handle error if there is a signal or non-zero exit code. + NSError *error; + if (WIFSIGNALED(stat_loc)) { + int signalCode = WTERMSIG(stat_loc); + // Ignore if the process was killed -- this occurs when we're killing + // a timed-out test, and shouldn't be treated as a crash. + if (signalCode != SIGKILL) { + [BPUtils printInfo:DEBUGINFO withString: @"Spawned XCTest execution failed with signal code: %@", @(signalCode)]; + error = [BPUtils errorWithSignalCode:signalCode]; + } + } else { + // A non-zero exit code could mean a failed test or something more serious, but we can't tell the difference here. + // The best we can do is log the error code as debug info. + int exitCode = WEXITSTATUS(stat_loc); + if (exitCode) { + [BPUtils printInfo:DEBUGINFO withString: @"Spawned XCTest execution failed with error code: %@", @(exitCode)]; + } + } + [blockSelf cleanUpParser:parser fileHandle:outputFileHandle]; + completionBlock(error, blockSelf.monitor.appPID); + + [outputFileHandle closeFile]; + } completionHandler:^(NSError *error, pid_t pid) { + // Again, this `completionHandler` is called once the process is done SPAWNING, + // as opposed to happening after the process itself has finished. + blockSelf.monitor.appPID = pid; + blockSelf.monitor.appState = Running; + spawnBlock(error, pid); + }]; +} + +- (void)cleanUpParser:(BPTreeParser *)parser fileHandle:(NSFileHandle *)fileHandle { + self.monitor.appState = Completed; + [parser.delegate setParserStateCompleted]; + // Post a APPCLOSED signal to the parser + [fileHandle seekToEndOfFile]; + [fileHandle writeData:[@"\nBP_APP_PROC_ENDED\n" dataUsingEncoding:NSUTF8StringEncoding]]; + [fileHandle closeFile]; +} + ++ (NSMutableArray *)commandLineArgsFromConfig:(BPConfiguration *)config { + NSArray *argumentsArr = config.commandLineArguments ?: @[]; NSMutableArray *commandLineArgs = [NSMutableArray array]; for (NSString *argument in argumentsArr) { NSArray *argumentsArray = [argument componentsSeparatedByString:@" "]; @@ -456,6 +587,21 @@ - (void)launchApplicationAndExecuteTestsWithParser:(BPTreeParser *)parser andCom } } } + return commandLineArgs; +} + +- (void)launchApplicationAndExecuteTestsWithParser:(BPTreeParser *)parser andCompletion:(void (^)(NSError *, pid_t))completion { + NSString *hostBundleId = [SimulatorHelper bundleIdForPath:self.config.appBundlePath]; + NSString *hostAppExecPath = [SimulatorHelper executablePathforPath:self.config.appBundlePath]; + + // One bp instance only run one kind of xctest file. + if (self.config.testRunnerAppPath) { + hostAppExecPath = [SimulatorHelper executablePathforPath:self.config.testRunnerAppPath]; + hostBundleId = [SimulatorHelper bundleIdForPath:self.config.testRunnerAppPath]; + } + // Create the environment for the host application + NSMutableArray *commandLineArgs = [BPSimulator commandLineArgsFromConfig:self.config]; + NSMutableDictionary *argsAndEnv = [[NSMutableDictionary alloc] init]; // These are appended by Xcode so we do that here. [commandLineArgs addObjectsFromArray:@[ @@ -532,13 +678,7 @@ - (void)launchApplicationAndExecuteTestsWithParser:(BPTreeParser *)parser andCom dispatch_source_cancel(source); }); dispatch_source_set_cancel_handler(source, ^{ - blockSelf.monitor.appState = Completed; - [parser.delegate setParserStateCompleted]; - // Post a APPCLOSED signal to the parser - NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:simStdoutPath]; - [fileHandle seekToEndOfFile]; - [fileHandle writeData:[@"\nBP_APP_PROC_ENDED\n" dataUsingEncoding:NSUTF8StringEncoding]]; - [fileHandle closeFile]; + [blockSelf cleanUpParser:parser fileHandle:[NSFileHandle fileHandleForWritingAtPath:simStdoutPath]]; }); dispatch_resume(source); self.appOutput.readabilityHandler = ^(NSFileHandle *handle) { diff --git a/bp/src/BPStats.h b/bp/src/BPStats.h index 90021d05..f63730c4 100644 --- a/bp/src/BPStats.h +++ b/bp/src/BPStats.h @@ -16,6 +16,8 @@ #define INSTALL_APPLICATION(x) [NSString stringWithFormat:@"[Attempt %lu] Install Application", (x)] #define UNINSTALL_APPLICATION(x) [NSString stringWithFormat:@"[Attempt %lu] Uninstall Application", (x)] #define LAUNCH_APPLICATION(x) [NSString stringWithFormat:@"[Attempt %lu] Launch Application", (x)] +#define SPAWN_LOGIC_TEST(x) [NSString stringWithFormat:@"[Attempt %lu] Spawn Logic Tests", (x)] +#define EXECUTE_LOGIC_TEST(x) [NSString stringWithFormat:@"[Attempt %lu] Execute Logic Tests", (x)] #define DELETE_SIMULATOR(x) [NSString stringWithFormat:@"[Attempt %lu] Delete Simulator", (x)] #define DELETE_SIMULATOR_CB(x) [NSString stringWithFormat:@"[Attempt %lu] Delete Simulator due to BAD STATE", (x)] diff --git a/bp/src/BPUtils.h b/bp/src/BPUtils.h index 209f3428..114e1621 100644 --- a/bp/src/BPUtils.h +++ b/bp/src/BPUtils.h @@ -71,7 +71,6 @@ typedef NS_ENUM(int, BPKind) { */ + (NSString *)mkstemp:(NSString *)pathTemplate withError:(NSError **)errPtr; - /*! @discussion print a message to stdout. @param kind one of the levels in BPKind @@ -80,8 +79,20 @@ typedef NS_ENUM(int, BPKind) { + (void)printInfo:(BPKind)kind withString:(NSString *)fmt, ... NS_FORMAT_FUNCTION(2,3); /*! - @discussion get an NSError * - This is not really meant to be called, use the BP_SET_ERROR macro below instead. + Creates an `NSError *` with BP-specific domain for a given signal code, updating the description accordingly. + @param signalCode The signal code. + */ ++ (NSError *)errorWithSignalCode:(NSInteger)signalCode; + +/*! + Creates an `NSError *` with BP-specific domain for a given exit code, updating the description accordingly. + @param exitCode The exit code. + */ ++ (NSError *)errorWithExitCode:(NSInteger)exitCode; + +/*! + @discussion get an `NSError *` + This is not really meant to be called, use the `BP_SET_ERROR` macro below instead. @param function The name of the function @param line The line number @param fmt a format string (a la printf), followed by var args. @@ -111,6 +122,18 @@ typedef NS_ENUM(int, BPKind) { + (BPConfiguration *)normalizeConfiguration:(BPConfiguration *)config withTestFiles:(NSArray *)xctTestFiles; +/*! + Returns an aggregated timeout interval for all tests to be run in an execution. While we will + still apply a per-test timeout, it's possible for things to fail in an XCTest execution when a test isn't + being run, and we want to make sure the execution still fails when this occurs. + + @discussion This timeout value is based on the timeout per test multiplied by the number of tests, + with an additional buffer per test. + @param config The fully setup configuration that will be used to calculate the aggregate timeout. + @return The aggregated timeout. + */ ++ (double)timeoutForAllTestsWithConfiguration:(BPConfiguration *)config; + /*! @discussion a function to determine if the given file name represents stdout. A file name is considered stdout if it is '-' or 'stdout'. @@ -173,6 +196,31 @@ typedef BOOL (^BPRunBlock)(void); */ + (NSString *)removeSwiftArgumentsFromTestName:(NSString *)testName; +/*! + * @discussion Checks for indicators that a test name is a swift test's name, i.e. has `.` or `()` + * @param testName the name of the test to check + * @return `YES` if swift, `NO` if objc + */ ++ (BOOL)isTestSwiftTest:(NSString *)testName; + +/*! + * @discussion Strips the test's bundle name if present, and adds in parenthesis. This is + * the format that consumers of Bluepill expect to provide + see in test reports. + * @param testName the name of the test to format + * @return trimmed test name + */ ++ (NSString *)formatSwiftTestForReport:(NSString *)testName; + +/*! + * @discussion XCTest requires that swift test names are fully namespaced, and don't include parens, + * contrary to what Bluepill consumers provide. + * + * @param testName the name of the test to format + * @param bundleName The name of the test's bundle + * @return trimmed test name + */ ++ (NSString *)formatSwiftTestForXCTest:(NSString *)testName withBundleName:(NSString *)bundleName; + /*! * @discussion setup the environment for weak linked frameworks * @param argc the number of arguments to the command diff --git a/bp/src/BPUtils.m b/bp/src/BPUtils.m index 3b8bf87b..40781f18 100644 --- a/bp/src/BPUtils.m +++ b/bp/src/BPUtils.m @@ -12,6 +12,7 @@ #import "BPConstants.h" #import "BPXCTestFile.h" #import "BPConfiguration.h" +#import "SimDevice.h" @implementation BPUtils @@ -113,16 +114,6 @@ + (void)printTo:(FILE*)fd kind:(BPKind)kind withString:(NSString *)txt { fflush(fd); } -+ (NSError *)BPError:(const char *)function andLine:(int)line withFormat:(NSString *)fmt, ... { - va_list args; - va_start(args, fmt); - NSString *msg = [[NSString alloc] initWithFormat:fmt arguments:args]; - va_end(args); - return [NSError errorWithDomain:BPErrorDomain - code:-1 - userInfo:@{NSLocalizedDescriptionKey: msg}]; -} - + (NSString *)findExecutablePath:(NSString *)execName { NSString *argv0 = [[[NSProcessInfo processInfo] arguments] objectAtIndex:0]; NSString *execPath = [[argv0 stringByDeletingLastPathComponent] stringByAppendingPathComponent:execName]; @@ -296,6 +287,10 @@ + (void)saveDebuggingDiagnostics:(NSString *)outputDirectory { [BPUtils runShell:cmd]; } ++ (BOOL)isTestSwiftTest:(NSString *)testName { + return [testName containsString:@"."] || [testName containsString:@"()"]; +} + + (NSString *)removeSwiftArgumentsFromTestName:(NSString *)testName { NSRange range = [testName rangeOfString:@"("]; if (range.location == NSNotFound) { @@ -304,6 +299,35 @@ + (NSString *)removeSwiftArgumentsFromTestName:(NSString *)testName { return [NSString stringWithFormat:@"%@()", [testName substringToIndex:range.location]]; } ++ (NSString *)formatSwiftTestForReport:(NSString *)testName { + NSString *formattedName = testName; + // Remove prefix of `.` + NSRange range = [testName rangeOfString:@"."]; + if (range.location != NSNotFound) { + formattedName = [formattedName substringFromIndex:range.location + 1]; + } + // Add parentheses + if (![testName hasSuffix:@"()"]) { + formattedName = [formattedName stringByAppendingString:@"()"]; + } + return formattedName; +} + ++ (NSString *)formatSwiftTestForXCTest:(NSString *)testName withBundleName:(NSString *)bundleName { + NSString *formattedName = testName; + // Remove parentheses + NSRange range = [formattedName rangeOfString:@"()"]; + if (range.location != NSNotFound) { + formattedName = [formattedName substringToIndex:range.location]; + } + // Add `.` + NSString *bundlePrefix = [bundleName stringByAppendingString:@"."]; + if (![formattedName containsString:bundlePrefix]) { + formattedName = [NSString stringWithFormat:@"%@.%@", bundleName, formattedName]; + } + return formattedName; +} + + (char *)version { return BP_VERSION; } @@ -418,6 +442,13 @@ + (NSDictionary *)loadSimpleJsonFile:(NSString *)filePath return testsToRunByFilePath; } ++ (double)timeoutForAllTestsWithConfiguration:(BPConfiguration *)config { + // Add 1 second per test + double buffer = 1.0; + NSInteger testCount = (config.testCasesToRun.count == 0 ? config.allTestCases.count : config.testCasesToRun.count) - config.testCasesToSkip.count; + return testCount * (config.testCaseTimeout.doubleValue + buffer); +} + + (double)getTotalTimeWithConfig:(BPConfiguration *)config testTimes:(NSDictionary *)testTimes andXCTestFiles:(NSArray *)xcTestFiles { @@ -438,4 +469,31 @@ + (double)getTotalTimeWithConfig:(BPConfiguration *)config return totalTime; } +#pragma mark - Errors + ++ (NSError *)errorWithSignalCode:(NSInteger)signalCode { + NSString *description = [NSString stringWithFormat:@"Process failed signal code: %@", @(signalCode)]; + return [self errorWithCode:signalCode description:description]; +} + ++ (NSError *)errorWithExitCode:(NSInteger)exitCode { + NSString *description = [NSString stringWithFormat:@"Process failed exit code: %@", @(exitCode)]; + return [self errorWithCode:exitCode description:description]; +} + ++ (NSError *)BPError:(const char *)function andLine:(int)line withFormat:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + NSString *msg = [[NSString alloc] initWithFormat:fmt arguments:args]; + va_end(args); + return [self errorWithCode:-1 description:msg]; +} + ++ (NSError *)errorWithCode:(NSInteger)code description:(NSString *)description { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: description + }; + return [NSError errorWithDomain:BPErrorDomain code:code userInfo:userInfo]; +} + @end diff --git a/bp/src/Bluepill.m b/bp/src/Bluepill.m index 82a471ad..f7558402 100644 --- a/bp/src/Bluepill.m +++ b/bp/src/Bluepill.m @@ -220,6 +220,18 @@ - (void)createContext { NSAssert(xctTestFile != nil, @"Failed to load testcases from: %@; Error: %@", context.config.testBundlePath, [error localizedDescription]); context.config.allTestCases = [[NSArray alloc] initWithArray: xctTestFile.allTestCases]; + // Right now, logic tests need to be able to make stronger assumptions around test exclusivity + // in the `allTests`, `testCasesToRun`, and `testCasesToSkip` lists. + if (context.config.isLogicTestTarget) { + // For estimating how long this will take (and setting an appropriate timeout), we need to remove any skipped tests + // that aren't actually a part of this bundle. + NSMutableSet *allTests = [NSMutableSet setWithArray:context.config.allTestCases]; + NSMutableSet *allSkippedTests = [NSMutableSet setWithArray:self.config.testCasesToSkip]; + [allSkippedTests intersectSet:allTests]; + context.config.testCasesToSkip = allSkippedTests.allObjects; + } + + context.attemptNumber = self.retries + 1; self.context = context; // Store the context on self so that it's accessible to the interrupt handler in the loop } @@ -256,7 +268,7 @@ - (void)setupExecutionWithContext:(BPExecutionContext *)context { // Set up retry counts. self.maxCreateTries = [self.config.maxCreateTries integerValue]; self.maxInstallTries = [self.config.maxInstallTries integerValue]; - + if (context.config.deleteSimUDID) { NEXT([self deleteSimulatorOnlyTaskWithContext:context]); } else { @@ -299,7 +311,10 @@ - (void)createSimulatorWithContext:(BPExecutionContext *)context { if (self.config.scriptFilePath) { [context.runner runScriptFile:self.config.scriptFilePath]; } - if (self.config.cloneSimulator) { + + if (self.config.isLogicTestTarget) { + NEXT([__self executeLogicTestsWithContext:context]) + } else if (self.config.cloneSimulator) { // launch application directly when clone simulator NEXT([__self launchApplicationWithContext:context]); } else { @@ -393,6 +408,81 @@ - (void)uninstallApplicationWithContext:(BPExecutionContext *)context { } } +- (void)executeLogicTestsWithContext:(BPExecutionContext *)context { + // We get two callbacks after trying to spawn an execution. They happen when: + // 1) Immediately after the process is spawned. + // 2) Once the process completes. + // This method sets up those two handlers, and then passes them to the runner. + + // 1) Handle process spawn + NSString *spawnStepName = SPAWN_LOGIC_TEST(context.attemptNumber); + NSString *executeTestsStepName = EXECUTE_LOGIC_TEST(context.attemptNumber); + [BPUtils printInfo:INFO withString:@"%@", spawnStepName]; + + // Now create handler for when the process completes + + [[BPStats sharedStats] startTimer:spawnStepName]; + BPWaitTimer *spawnTimer = [BPWaitTimer timerWithInterval:[context.config.launchTimeout doubleValue]]; + __block BPWaitTimer *executionTimer = [BPWaitTimer timerWithInterval:[BPUtils timeoutForAllTestsWithConfiguration:context.config]]; + // Start spawn timer; save execution timer for the spawn handler. + [spawnTimer start]; + BPApplicationLaunchHandler *spawnHandler = [BPApplicationLaunchHandler handlerWithTimer:spawnTimer]; + + __weak typeof(self) __self = self; + __weak typeof(spawnHandler) __spawnHandler = spawnHandler; + spawnHandler.beginWith = ^{ + [BPUtils printInfo:((__spawnHandler.pid > -1) ? INFO : ERROR) withString:@"Completed: %@", spawnStepName]; + }; + spawnHandler.onSuccess = ^{ + context.pid = __spawnHandler.pid; + + // At this point, we know that the execution itself has started. + [BPUtils printInfo:DEBUGINFO withString:@"XCTest execution spawned; waiting for execution to complete."]; + [[BPStats sharedStats] endTimer:spawnStepName withResult:@"INFO"]; + + // Start timer for actual xctest execution. + [BPUtils printInfo:INFO withString:@"%@", executeTestsStepName]; + [[BPStats sharedStats] startTimer:executeTestsStepName]; + [executionTimer start]; + }; + spawnHandler.onError = ^(NSError *error) { + [[BPStats sharedStats] endTimer:spawnStepName withResult:@"ERROR"]; + [BPUtils printInfo:ERROR withString:@"Could not spawn logic test execution: %@", [error localizedDescription]]; + NEXT([__self deleteSimulatorWithContext:context andStatus:BPExitStatusLaunchAppFailed]); + }; + spawnHandler.onTimeout = ^{ + [[BPStats sharedStats] addSimulatorLaunchFailure]; + [[BPStats sharedStats] endTimer:spawnStepName withResult:@"TIMEOUT"]; + [BPUtils printInfo:FAILED withString:@"Timeout: %@", spawnStepName]; + }; + + // 2) Handle execution finished + + // Now the handler for when the execution actually completes. + BPApplicationLaunchHandler *completionHandler = [BPApplicationLaunchHandler handlerWithTimer:executionTimer]; + completionHandler.onSuccess = ^{ + [BPUtils printInfo:DEBUGINFO withString:@"XCTest execution completed. Results must now be parsed."]; + [[BPStats sharedStats] endTimer:executeTestsStepName withResult:@"INFO"]; + NEXT([__self checkUnhostedProcessWithContext:context]); + }; + + completionHandler.onError = ^(NSError *error) { + [[BPStats sharedStats] endTimer:executeTestsStepName withResult:@"ERROR"]; + [BPUtils printInfo:ERROR withString:@"Spawned logic test execution failed: %@", [error localizedDescription]]; + NEXT([__self deleteSimulatorWithContext:context andStatus:BPExitStatusAppCrashed]); + }; + + completionHandler.onTimeout = ^{ + [[BPStats sharedStats] addTestRuntimeTimeout]; + [[BPStats sharedStats] endTimer:executeTestsStepName withResult:@"TIMEOUT"]; + [BPUtils printInfo:FAILED withString:@"Timeout: %@", executeTestsStepName]; + }; + + [context.runner executeLogicTestsWithParser:context.parser + onSpawn:spawnHandler.defaultHandlerBlock + andCompletion:completionHandler.defaultHandlerBlock]; +} + - (void)launchApplicationWithContext:(BPExecutionContext *)context { NSString *stepName = LAUNCH_APPLICATION(context.attemptNumber); [BPUtils printInfo:INFO withString:@"%@", stepName]; @@ -448,6 +538,7 @@ - (void)connectTestBundleAndTestDaemonWithContext:(BPExecutionContext *)context NEXT([self checkProcessWithContext:context]); } + - (void)checkProcessWithContext:(BPExecutionContext *)context { BOOL isRunning = [self isProcessRunningWithContext:context]; if (!isRunning && [context.runner isFinished]) { @@ -470,7 +561,7 @@ - (void)checkProcessWithContext:(BPExecutionContext *)context { // If it's not running and we passed the above checks (e.g., the tests are not yet completed) // then it must mean the app has crashed. // However, we have a short-circuit for tests because those may not actually run any app - if (!isRunning && context.pid > 0 && [context.runner isApplicationLaunched] && !self.config.testing_NoAppWillRun) { + if (!isRunning && context.pid > 0 && ([context.runner isApplicationLaunched] || context.config.isLogicTestTarget) && !self.config.testing_NoAppWillRun) { // The tests ended before they even got started or the process is gone for some other reason [[BPStats sharedStats] endTimer:LAUNCH_APPLICATION(context.attemptNumber) withResult:@"APP CRASHED"]; [BPUtils printInfo:ERROR withString:@"Application crashed!"]; @@ -482,6 +573,30 @@ - (void)checkProcessWithContext:(BPExecutionContext *)context { NEXT_AFTER(1, [self checkProcessWithContext:context]); } +- (void)checkUnhostedProcessWithContext:(BPExecutionContext *)context { + // Handle tests + parsing is complete + BOOL isRunning = [self isProcessRunningWithContext:context]; + if (!isRunning && [context.runner isFinished]) { + [BPUtils printInfo:INFO withString:@"Finished test execution."]; + [[BPStats sharedStats] endTimer:EXECUTE_LOGIC_TEST(context.attemptNumber) withResult:[BPExitStatusHelper stringFromExitStatus:context.exitStatus]]; + [self runnerCompletedWithContext:context]; + return; + } + // Handle Simulator Crash + if (![context.runner isSimulatorRunning]) { + [[BPStats sharedStats] endTimer:EXECUTE_LOGIC_TEST(context.attemptNumber) withResult:@"SIMULATOR CRASHED"]; + [BPUtils printInfo:ERROR withString:@"SIMULATOR CRASHED!!!"]; + context.simulatorCrashed = YES; + [[BPStats sharedStats] addSimulatorCrash]; + [self deleteSimulatorWithContext:context andStatus:BPExitStatusSimulatorCrashed]; + return; + } + + // Even though the execution has completed, the parser may not be done yet. + // If we're here, that's the case... so try again in a second. + NEXT_AFTER(1, [self checkUnhostedProcessWithContext:context]); +} + - (BOOL)isProcessRunningWithContext:(BPExecutionContext *)context { if (self.config.testing_NoAppWillRun) { return NO; @@ -520,7 +635,7 @@ - (void)runnerCompletedWithContext:(BPExecutionContext *)context { [BPUtils printInfo:INFO withString:@"Saving Diagnostics for Debugging"]; [BPUtils saveDebuggingDiagnostics:_config.outputDirectory]; } - + [self deleteSimulatorWithContext:context andStatus:[context.runner exitStatus]]; } } diff --git a/bp/src/PrivateHeaders/CoreSimulator/SimDevice.h b/bp/src/PrivateHeaders/CoreSimulator/SimDevice.h index 6619b46d..05a082da 100644 --- a/bp/src/PrivateHeaders/CoreSimulator/SimDevice.h +++ b/bp/src/PrivateHeaders/CoreSimulator/SimDevice.h @@ -11,6 +11,36 @@ typedef void (^CDUnknownBlockType)(void); typedef void (*CDUnknownFunctionPointerType)(void); @class NSArray, NSDate, NSDictionary, NSMachPort, NSMutableArray, NSMutableDictionary, NSObject, SimDeviceIO, NSString, NSUUID, SimDeviceBootInfo, SimDeviceNotificationManager, SimDevicePasteboard, SimDeviceSet, SimDeviceType, SimRuntime; +/** + A note on how to investigate expected usage for these private, class-dumped Apple APIs: + + It is often easier to get the below commands running in their commandline, `simctl` form first, in particular because + this is a public API that is (somewhat) documented. Fortunately for us, `simctl` uses `CoreSimulator` as well + as the APIs defined here in `SimDevice.h` under the hood, which we can use to our advantage to learn more about + expected usage. + + As an example, the below steps were used to learn more about the expected usage for the various `spawn...` methods + defined in this class, leveraging lldb: + + 1. Load lldb with a process such as `xcrun simctl spawn -s booted -XCTest All <.xctest_file>` + 2. It should pause the process before starting. Then add breakpoints to all variants of `spawn` from `SimDevice.h`, including: + a) `breakpoint set --selector spawnWithPath:options:terminationQueue:terminationHandler:pid:error:` + b) `breakpoint set --selector spawnWithPath:options:terminationQueue:terminationHandler:error:` + c) `breakpoint set --selector spawnWithPath:options:terminationHandler:error:` + d) `breakpoint set --selector _spawnFromSelfWithPath:options:terminationQueue:terminationHandler:error:` + e) `breakpoint set --selector _spawnFromLaunchdWithPath:options:terminationQueue:terminationHandler:error:` + f) `breakpoint set --selector _onBootstrapQueue_spawnWithPath:options:terminationQueue:terminationHandler:erro` + g) `breakpoint set --selector spawnWithPath:options:terminationQueue:terminationHandler:pid:error:` + h) `breakpoint set --selector spawnWithPath:options:terminationQueue:terminationHandler:error:` + 3. These breakpoints should be `pending`, as CoreSimulator won't have been loaded yet. Hit `c` to continue and unpause the process, + and continue to `c` through automatically triggered pauses as additional dylibs are loaded. + 4. Eventually you'll hit one of the above selectors' breakpoint. Use `register read` to print out all the registers to find what all's in frame. + 5. Then use `po ` for the register values to see what all objc classes are stored. + 6. Eventually, you'll find the values for the `path` and `options` arguments, or whatever other parameters you want to inspect. + + With this information, you can learn more how a given `simctl` command maps onto its corresponding `SimDevice` method :) + */ + @interface SimDevice : NSObject { unsigned long long _state; @@ -192,7 +222,7 @@ typedef void (*CDUnknownFunctionPointerType)(void); - (void)triggerCloudSyncWithCompletionHandler:(CDUnknownBlockType)arg1; - (void)launchApplicationAsyncWithID:(id)arg1 options:(id)arg2 completionHandler:(void (^)(NSError *, pid_t pid))arg3; - (int)spawnWithPath:(id)arg1 options:(id)arg2 terminationHandler:(CDUnknownBlockType)arg3 error:(id *)arg4; -- (void)spawnAsyncWithPath:(id)arg1 options:(id)arg2 terminationHandler:(CDUnknownBlockType)arg3 completionHandler:(CDUnknownBlockType)arg4; +- (void)spawnAsyncWithPath:(id)arg1 options:(id)arg2 terminationHandler:(void (^)(int))arg3 completionHandler:(void (^)(NSError *, pid_t pid))arg4; - (void)restoreContentsAndSettingsAsyncFromDevice:(id)arg1 completionHandler:(CDUnknownBlockType)arg2; - (void)eraseContentsAndSettingsAsyncWithCompletionHandler:(CDUnknownBlockType)arg1; - (void)renameAsync:(id)arg1 completionHandler:(CDUnknownBlockType)arg2; diff --git a/bp/src/SimulatorHelper.h b/bp/src/SimulatorHelper.h index a17227f6..9324c027 100644 --- a/bp/src/SimulatorHelper.h +++ b/bp/src/SimulatorHelper.h @@ -30,6 +30,13 @@ + (NSDictionary *)appLaunchEnvironmentWithBundleID:(NSString *)hostBundleID device:(SimDevice *)device config:(BPConfiguration *)config; +/*! + * @discussion Creates an array of all tests that should be run, filtering out any tests that should be skipped. + * @param config the configuration object. + * @return The list of tests to run. + */ + ++ (NSArray *)testsToRunWithConfig:(BPConfiguration *)config; /*! * @discussion get the path of the environment configuration file @@ -38,6 +45,13 @@ */ + (NSString *)testEnvironmentWithConfiguration:(BPConfiguration *)config; +/*! + @discussion Creates a stdout file on the provided device of the form `/tmp/stdout_stderr_` + @param device The device to create the file on. + @return the path of the stdout file. + */ ++ (NSString *)makeStdoutFileOnDevice:(SimDevice *)device; + #pragma mark - Path Helper + (NSString *)bundleIdForPath:(NSString *)path; diff --git a/bp/src/SimulatorHelper.m b/bp/src/SimulatorHelper.m index d4d5cf25..a48d78f2 100644 --- a/bp/src/SimulatorHelper.m +++ b/bp/src/SimulatorHelper.m @@ -11,6 +11,7 @@ #import "BPConfiguration.h" #import "BPUtils.h" #import "BPXCTestFile.h" +#import "SimDevice.h" #import "PrivateHeaders/XCTest/XCTestConfiguration.h" #import "PrivateHeaders/XCTest/XCTTestIdentifier.h" #import "PrivateHeaders/XCTest/XCTTestIdentifierSet.h" @@ -112,7 +113,7 @@ + (NSString *)testEnvironmentWithConfiguration:(BPConfiguration *)config { xctConfig.reportResultsToIDE = YES; xctConfig.automationFrameworkPath = [NSString stringWithFormat:@"%@/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework", config.xcodePath]; testHostPath = config.appBundlePath; - + NSString *bundleID = [self bundleIdForPath:config.appBundlePath]; xctConfig.testApplicationDependencies = config.dependencies.count > 0 ? config.dependencies : @{bundleID: config.appBundlePath}; @@ -168,6 +169,60 @@ + (NSString *)bundleIdForPath:(NSString *)path { return bundleId; } ++ (NSArray *)testsToRunWithConfig:(BPConfiguration *)config { + // First, standardize all swift test names: + NSMutableArray *allTests = [self formatTestNamesForXCTest:config.allTestCases withConfig:config]; + NSMutableArray *testsToRun = [self formatTestNamesForXCTest:config.testCasesToRun withConfig:config]; + NSArray *testsToSkip = [self formatTestNamesForXCTest:config.testCasesToSkip withConfig:config]; + + // If there's no tests to skip, we can return these back otherwise unaltered. + // Otherwise, we'll need to remove any tests from `testsToRun` that are in our skip list. + if (testsToSkip.count == 0) { + return [(testsToRun ?: allTests) copy]; + } + + // If testCasesToRun was empty/nil, we default to all tests + if (testsToRun.count == 0) { + testsToRun = allTests; + } + [testsToRun filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *testName, NSDictionary * _Nullable bindings) { + return ![testsToSkip containsObject:testName]; + }]]; + return [testsToRun copy]; +} + ++ (nullable NSMutableArray *)formatTestNamesForXCTest:(nullable NSArray *)tests + withConfig:(BPConfiguration *)config { + if (!tests) { + return nil; + } + NSMutableArray *formattedTests = [NSMutableArray array]; + NSString *bundleName = [[config.testBundlePath lastPathComponent] stringByDeletingPathExtension]; + for (NSString *testName in tests) { + if ([BPUtils isTestSwiftTest:testName]) { + [formattedTests addObject:[BPUtils formatSwiftTestForXCTest:testName withBundleName:bundleName]]; + } else { + [formattedTests addObject:testName]; + } + } + return formattedTests; +} + +// Intercept stdout, stderr and post as simulator-output events ++ (NSString *)makeStdoutFileOnDevice:(SimDevice *)device { + NSString *stdout_stderr = [NSString stringWithFormat:@"%@/tmp/stdout_stderr_%@", device.dataPath, [[device UDID] UUIDString]]; + NSString *simStdoutPath = [BPUtils mkstemp:stdout_stderr withError:nil]; + assert(simStdoutPath != nil); + + [[NSFileManager defaultManager] removeItemAtPath:simStdoutPath error:nil]; + + // Create empty file so we can tail it and the app can write to it + [[NSFileManager defaultManager] createFileAtPath:simStdoutPath + contents:nil + attributes:nil]; + return simStdoutPath; +} + + (NSString *)executablePathforPath:(NSString *)path { NSDictionary *appDic = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Info.plist"]]; NSString *appExecutable = [appDic objectForKey:(NSString *)kCFBundleExecutableKey]; diff --git a/bp/src/SimulatorMonitor.m b/bp/src/SimulatorMonitor.m index c8a5fad7..b7eec377 100644 --- a/bp/src/SimulatorMonitor.m +++ b/bp/src/SimulatorMonitor.m @@ -86,7 +86,10 @@ - (void)onTestCaseBeganWithName:(NSString *)testName inClass:(NSString *)testCla dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.maxTestExecutionTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if ([__self.currentTestName isEqualToString:testName] && [__self.currentClassName isEqualToString:testClass] && __self.testsState == Running) { [BPUtils printInfo:TIMEOUT withString:@"%10.6fs %@/%@", __self.maxTestExecutionTime, testClass, testName]; - [__self stopTestsWithErrorMessage:@"Test took too long to execute and was aborted." forTestName:testName inClass:testClass]; + [__self stopTestsWithErrorMessage:@"Test took too long to execute and was aborted." + forTestName:testName + inClass:testClass + shouldRetryTest:YES]; __self.exitStatus = BPExitStatusTestTimeout; [[BPStats sharedStats] endTimer:[NSString stringWithFormat:TEST_CASE_FORMAT, [BPStats sharedStats].attemptNumber, testClass, testName] withResult:@"ERROR"]; [[BPStats sharedStats] addTestRuntimeTimeout]; @@ -100,6 +103,9 @@ - (void)onTestCasePassedWithName:(NSString *)testName inClass:(NSString *)testCl [BPUtils printInfo:PASSED withString:@"%10.6fs %@/%@", [currentTime timeIntervalSinceDate:self.lastTestCaseStartDate], testClass, testName]; + NSLog(@"%10.6fs %@/%@", + [currentTime timeIntervalSinceDate:self.lastTestCaseStartDate], + testClass, testName); // Passing or failing means that if the simulator crashes later, we shouldn't rerun this test. [self updateExecutedTestCaseList:testName inClass:testClass]; @@ -189,7 +195,7 @@ - (void)onOutputReceived:(NSString *)output { __block NSUInteger previousOutputId = self.currentOutputId; __weak typeof(self) __self = self; - // App crashed + // App or logic test's XCTest execution crashed. if ([output isEqualToString:@"BP_APP_PROC_ENDED"]) { if (__self.testsState == Running || __self.testsState == Idle) { NSString *testClass = (__self.currentClassName ?: __self.previousClassName); @@ -208,7 +214,8 @@ - (void)onOutputReceived:(NSString *)output { } [self stopTestsWithErrorMessage:@"App Crashed" forTestName:(self.currentTestName ?: self.previousTestName) - inClass:(self.currentClassName ?: self.previousClassName)]; + inClass:(self.currentClassName ?: self.previousClassName) + shouldRetryTest:self.config.retryAppCrashTests]; self.exitStatus = BPExitStatusAppCrashed; [[BPStats sharedStats] addApplicationCrash]; } @@ -231,17 +238,18 @@ - (void)onOutputReceived:(NSString *)output { __self.exitStatus = testsReallyStarted ? BPExitStatusTestTimeout : BPExitStatusSimulatorCrashed; [__self stopTestsWithErrorMessage:@"Timed out waiting for the test to produce output. Test was aborted." forTestName:testName - inClass:testClass]; + inClass:testClass + shouldRetryTest:YES]; [[BPStats sharedStats] addTestOutputTimeout]; } }); self.lastOutput = currentTime; } -- (void)stopTestsWithErrorMessage:(NSString *)message forTestName:(NSString *)testName inClass:(NSString *)testClass { +- (void)stopTestsWithErrorMessage:(NSString *)message forTestName:(NSString *)testName inClass:(NSString *)testClass shouldRetryTest:(BOOL)shouldRetryTest { // Timeout or crash on a test means we should skip it when we rerun the tests, unless we've enabled re-running failed tests - if (!self.config.onlyRetryFailed) { + if (!shouldRetryTest) { [self updateExecutedTestCaseList:testName inClass:testClass]; } if (self.appState == Running && !self.config.testing_NoAppWillRun) { diff --git a/bp/tests/BPIntTestCase.m b/bp/tests/BPIntTestCase.m index a800a44f..7cd71e86 100644 --- a/bp/tests/BPIntTestCase.m +++ b/bp/tests/BPIntTestCase.m @@ -13,6 +13,7 @@ #import "BPIntTestCase.h" #import "BPConfiguration.h" #import "BPTestHelper.h" +#import "BPTestUtils.h" #import "BPUtils.h" #import "SimDeviceType.h" #import "SimRuntime.h" @@ -24,47 +25,8 @@ - (void)setUp { [super setUp]; self.continueAfterFailure = NO; - NSString *hostApplicationPath = [BPTestHelper sampleAppPath]; - NSString *testBundlePath = [BPTestHelper sampleAppNegativeTestsBundlePath]; - self.config = [[BPConfiguration alloc] initWithProgram:BP_BINARY]; - self.config.testBundlePath = testBundlePath; - self.config.appBundlePath = hostApplicationPath; - self.config.stuckTimeout = @40; - self.config.xcodePath = [BPUtils runShell:@"/usr/bin/xcode-select -print-path"]; - self.config.runtime = @BP_DEFAULT_RUNTIME; - self.config.repeatTestsCount = @1; - self.config.errorRetriesCount = @0; - self.config.testCaseTimeout = @20; - self.config.deviceType = @BP_DEFAULT_DEVICE_TYPE; - self.config.headlessMode = YES; - self.config.videoPaths = @[[BPTestHelper sampleVideoPath]]; - self.config.testRunnerAppPath = nil; - self.config.testing_CrashAppOnLaunch = NO; - self.config.cloneSimulator = NO; [BPUtils quietMode:[BPUtils isBuildScript]]; [BPUtils enableDebugOutput:NO]; - - NSError *err; - SimServiceContext *sc = [SimServiceContext sharedServiceContextForDeveloperDir:self.config.xcodePath error:&err]; - if (!sc) { NSLog(@"Failed to initialize SimServiceContext: %@", err); } - - for (SimDeviceType *type in [sc supportedDeviceTypes]) { - if ([[type name] isEqualToString:self.config.deviceType]) { - self.config.simDeviceType = type; - break; - } - } - - XCTAssert(self.config.simDeviceType != nil); - - for (SimRuntime *runtime in [sc supportedRuntimes]) { - if ([[runtime name] containsString:self.config.runtime]) { - self.config.simRuntime = runtime; - break; - } - } - - XCTAssert(self.config.simRuntime != nil); } @end diff --git a/bp/tests/BPReportTests.m b/bp/tests/BPReportTests.m index 496eb3fd..c849ad1d 100644 --- a/bp/tests/BPReportTests.m +++ b/bp/tests/BPReportTests.m @@ -14,13 +14,17 @@ #import "BPSimulator.h" #import "BPTestHelper.h" #import "BPUtils.h" +#import "BPTestUtils.h" @interface BPReportTests : BPIntTestCase @end @implementation BPReportTests - +- (void)setUp { + [super setUp]; + self.config = [BPTestUtils makeHostedTestConfiguration]; +} - (void)testReportWithAppCrashingTestsSet { [BPUtils enableDebugOutput:NO]; diff --git a/bp/tests/BPTestHelper.h b/bp/tests/BPTestHelper.h index b4d4ffcc..777f1d36 100644 --- a/bp/tests/BPTestHelper.h +++ b/bp/tests/BPTestHelper.h @@ -17,6 +17,13 @@ // Return the path to the test plan json file. The json is packed into the app bundle as resource + (NSString *)testPlanPath; +// Return the path to logic tests, that are run unhosted rather than on the Sampple App ++ (NSString *)logicTestBundlePath; + +// Return the path to logic tests, that are run unhosted rather than on the Sampple App +// This particular bundle will only have passing tests to make certain functionalities easier to test. ++ (NSString *)passingLogicTestBundlePath; + // Return the path to the sample app's xctest with new test cases + (NSString *)sampleAppNewTestsBundlePath; diff --git a/bp/tests/BPTestHelper.m b/bp/tests/BPTestHelper.m index 6ff7c84a..7e2c9616 100644 --- a/bp/tests/BPTestHelper.m +++ b/bp/tests/BPTestHelper.m @@ -20,6 +20,17 @@ + (NSString *)testPlanPath { return [[self sampleAppPath] stringByAppendingPathComponent:@"test_plan.json"]; } +// Return the path to logic tests, that are run unhosted rather than on the Sampple App ++ (NSString *)logicTestBundlePath { + return [[self sampleAppPath] stringByAppendingPathComponent:@"/../BPLogicTests.xctest"]; +} + +// Return the path to logic tests, that are run unhosted rather than on the Sampple App +// This particular bundle will only have passing tests to make certain functionalities easier to test. ++ (NSString *)passingLogicTestBundlePath { + return [[self sampleAppPath] stringByAppendingPathComponent:@"/../BPPassingLogicTests.xctest"]; +} + // Return the path to the sample app's xctest with new test cases + (NSString *)sampleAppNewTestsBundlePath { return [[self sampleAppPath] stringByAppendingString:@"/PlugIns/BPSampleAppNewTests.xctest"]; diff --git a/bp/tests/BluepillTests.m b/bp/tests/BluepillHostedTests.m similarity index 98% rename from bp/tests/BluepillTests.m rename to bp/tests/BluepillHostedTests.m index 1c0be3f9..85988d14 100644 --- a/bp/tests/BluepillTests.m +++ b/bp/tests/BluepillHostedTests.m @@ -16,6 +16,7 @@ #import "BPTestHelper.h" #import "BPUtils.h" #import "BPSimulator.h" +#import "BPTestUtils.h" #import "SimDevice.h" @@ -24,15 +25,14 @@ * - Exit code testing * - Report validation */ -@interface BluepillTests : BPIntTestCase +@interface BluepillHostedTests : BPIntTestCase @end -@implementation BluepillTests +@implementation BluepillHostedTests - - -- (void)tearDown { - [super tearDown]; +- (void)setUp { + [super setUp]; + self.config = [BPTestUtils makeHostedTestConfiguration]; } - (void)testAppThatCrashesOnLaunch { diff --git a/bp/tests/Unhosted Tests/BluepillUnhostedBatchingTests.m b/bp/tests/Unhosted Tests/BluepillUnhostedBatchingTests.m new file mode 100644 index 00000000..9b326f21 --- /dev/null +++ b/bp/tests/Unhosted Tests/BluepillUnhostedBatchingTests.m @@ -0,0 +1,125 @@ +// Copyright 2016 LinkedIn Corporation +// Licensed under the BSD 2-Clause License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at https://opensource.org/licenses/BSD-2-Clause +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +#import + +#import "Bluepill.h" +#import "BPIntTestCase.h" +#import "BPConfiguration.h" +#import "BPTestHelper.h" +#import "BPUtils.h" +#import "BPTestUtils.h" + +@interface BluepillUnhostedBatchingTests : BPIntTestCase +@end + +@implementation BluepillUnhostedBatchingTests + +- (void)setUp { + [super setUp]; + self.config = [BPTestUtils makeUnhostedTestConfiguration]; + self.config.numSims = @1; + self.config.stuckTimeout = @3; + self.config.testBundlePath = [BPTestHelper passingLogicTestBundlePath]; + + NSString *tempDir = NSTemporaryDirectory(); + NSError *error; + self.config.outputDirectory = [BPUtils mkdtemp:[NSString stringWithFormat:@"%@/TestLogsTempDir", tempDir] withError:&error]; +} + +- (void)testAllTests { + // This is redundant but made explicit here for test clarity + self.config.testCasesToRun = nil; + self.config.testCasesToSkip = nil; + + // Run Tests + BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusAllTestsPassed]; + + // Check that test is started in both sets of logs. + for (NSString *testCase in [BluepillUnhostedBatchingTests allTestCases]) { + [BPTestUtils checkIfTestCase:testCase bundleName:@"BPPassingLogicTests" wasRunInLog:[self.config.outputDirectory stringByAppendingPathComponent:@"1-simulator.log"]]; + } +} + +- (void)testOptInToObjcTests { + [self validateOptInToTests:@[ + @"BPPassingLogicTests/testPassingLogicTest2", + @"BPPassingLogicTests/testPassingLogicTest3", + ]]; +} + +- (void)testOptInToSwiftTests { + [self validateOptInToTests:@[ + @"SwiftLogicTests/testPassingLogicTest1()", + ]]; +} + +- (void)testOptOutOfObjcTests { + [self validateOptOutOfTests:@[ + @"BPPassingLogicTests/testPassingLogicTest2", + @"BPPassingLogicTests/testPassingLogicTest3", + ]]; +} + +- (void)testOptOutOfSwiftTests { + [self validateOptOutOfTests:@[ + @"SwiftLogicTests/testPassingLogicTest3()", + ]]; +} + +#pragma mark - Helpers + +- (void)validateOptInToTests:(NSArray *)tests { + self.config.testCasesToRun = tests; + [self validateExactlyTheseTestsAreExecuted:tests]; +} + +- (void)validateOptOutOfTests:(NSArray *)tests { + self.config.testCasesToSkip = tests; + NSArray *expectedTests = [BluepillUnhostedBatchingTests allTestsExcept:tests]; + [self validateExactlyTheseTestsAreExecuted:expectedTests]; +} + +- (void)validateExactlyTheseTestsAreExecuted:(NSArray *)tests { + // Run Tests + BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusAllTestsPassed]; + + // Check that exclusively these tests were run. + for (NSString *testCase in tests) { + NSLog(@"testCase: %@", testCase); + XCTAssert([BPTestUtils checkIfTestCase:testCase bundleName:@"BPPassingLogicTests" wasRunInLog:[self.config.outputDirectory stringByAppendingPathComponent:@"1-simulator.log"]]); + } + + // Check that "skipped" tests are not run. + for (NSString *testCase in [BluepillUnhostedBatchingTests allTestsExcept:tests]) { + XCTAssertFalse([BPTestUtils checkIfTestCase:testCase bundleName:@"BPPassingLogicTests" wasRunInLog:[self.config.outputDirectory stringByAppendingPathComponent:@"1-simulator.log"]]); + } +} + ++ (NSArray *)allTestCases { + return @[ + @"BPPassingLogicTests/testPassingLogicTest1", + @"BPPassingLogicTests/testPassingLogicTest2", + @"BPPassingLogicTests/testPassingLogicTest3", + @"BPPassingLogicTests/testPassingLogicTest4", + @"SwiftLogicTests/testPassingLogicTest1()", + @"SwiftLogicTests/testPassingLogicTest2()", + @"SwiftLogicTests/testPassingLogicTest3()", + ]; +} + ++ (NSArray *)allTestsExcept:(NSArray *)omittedTests { + NSMutableArray *mutableTests = [[self allTestCases] mutableCopy]; + [mutableTests removeObjectsInArray:omittedTests]; + return [mutableTests copy]; +} + +@end diff --git a/bp/tests/Unhosted Tests/BluepillUnhostedTests.m b/bp/tests/Unhosted Tests/BluepillUnhostedTests.m new file mode 100644 index 00000000..af056bf4 --- /dev/null +++ b/bp/tests/Unhosted Tests/BluepillUnhostedTests.m @@ -0,0 +1,176 @@ +// Copyright 2016 LinkedIn Corporation +// Licensed under the BSD 2-Clause License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at https://opensource.org/licenses/BSD-2-Clause +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +#import +#import + +#import "Bluepill.h" +#import "BPIntTestCase.h" +#import "BPConfiguration.h" +#import "BPTestHelper.h" +#import "BPUtils.h" +#import "BPTestUtils.h" + +/** + * This test suite is the integration tests to make sure logic tests are being run correctly. + * It includes validation on the following: + * - Exit code testing + * - Failure/Timeout/Crash handling + * - Retry behaviors + */ +@interface BluepillUnhostedTests : BPIntTestCase +@end + +@implementation BluepillUnhostedTests + +- (void)setUp { + [super setUp]; + self.config = [BPTestUtils makeUnhostedTestConfiguration]; + self.config.numSims = @1; + self.config.stuckTimeout = @1; + + NSString *testBundlePath = [BPTestHelper logicTestBundlePath]; + self.config.testBundlePath = testBundlePath; +} + +#pragma mark - Passing Tests + +- (void)testSinglePassingLogicTests { + self.config.testCasesToRun = @[@"BPLogicTests/testPassingLogicTest1"]; + BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusAllTestsPassed]; +} + +- (void)testMultiplePassingLogicTests { + NSString *tempDir = NSTemporaryDirectory(); + NSError *error; + NSString *outputDir = [BPUtils mkdtemp:[NSString stringWithFormat:@"%@/TestLogsTempDir", tempDir] withError:&error]; + self.config.outputDirectory = outputDir; + self.config.testCasesToRun = @[ + @"BPLogicTests/testPassingLogicTest1", + @"BPLogicTests/testPassingLogicTest2" + ]; + + // Run Tests + BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusAllTestsPassed]; + + // Check that test is started in both sets of logs. + for (NSString *testCase in self.config.testCasesToRun) { + XCTAssert([BPTestUtils checkIfTestCase:testCase bundleName:@"BPLogicTests" wasRunInLog:[outputDir stringByAppendingPathComponent:@"1-simulator.log"]]); + } +} + +# pragma mark - Failing Tests + +- (void)testFailingLogicTest { + self.config.testCasesToRun = @[@"BPLogicTests/testFailingLogicTest"]; + + BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusTestsFailed]; +} + +#pragma mark - Handling Crashes + +/* + A boring objective-c crash (such as index out of bounds on an NSArray) should be + handled smoothly by XCTest, and reported as such as a failed test. + */ +- (void)testCrashingTestCaseLogicTest { + self.config.testCasesToRun = @[@"BPLogicTests/testCrashTestCaseLogicTest"]; + BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusTestsFailed]; +} + +/* + A more aggressive crash (like doing an illegal strcpy) will crash the entire XCTest + execution. + */ +- (void)testCrashingExecutionLogicTest { + self.config.testCasesToRun = @[@"BPLogicTests/testCrashExecutionLogicTest"]; + BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusAppCrashed]; +} + +#pragma mark - Timeouts + +/* + This test validates that a test fails when the simulator has no output for over + the stuckTimeout threshold + */ +- (void)testStuckLogicTest { + self.config.testCasesToRun = @[@"BPLogicTests/testStuckLogicTest"]; + BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusTestTimeout]; +} + +/* + This test validates that a slow test will fail, even when the simulator sees + some change. + */ +- (void)testHangingLogicTest { + // `BPLogicTests/testSlowLogicTest` is designed to log a string infinitely, once a second. + // As a result, it should not "get stuck", but should eventually timeout anyway. + self.config.stuckTimeout = @1; + self.config.testCaseTimeout = @3; + self.config.testCasesToRun = @[@"BPLogicTests/testSlowLogicTest"]; + + BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusTestTimeout]; +} + +/* + The timeout should only cause a failure if an individual test exceeds the timeout, + not if the combined time sums to above the timeout. + */ +- (void)testTimeoutOnlyAppliesToTestCaseNotSuite { + // The three tests combined should exceed any timeouts, but that shouldn't be a problem. + self.config.testCaseTimeout = @2; + self.config.stuckTimeout = @2; + self.config.testCasesToRun = @[ + @"BPLogicTests/testOneSecondTest1", + @"BPLogicTests/testOneSecondTest2", + @"BPLogicTests/testOneSecondTest3", + ]; + BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusAllTestsPassed]; +} + +#pragma mark - Retries + +- (void)testRetriesFailure { + [self validateTestIsRetried:@"BPLogicTests/testFailingLogicTest"]; +} + +- (void)testRetriesCrash { + self.config.retryAppCrashTests = YES; + [self validateTestIsRetried:@"BPLogicTests/testCrashExecutionLogicTest"]; +} + +#pragma mark - Helpers + +- (void)validateTestIsRetried:(NSString *)testCase { + // Setup + NSString *tempDir = NSTemporaryDirectory(); + NSError *error; + NSString *outputDir = [BPUtils mkdtemp:[NSString stringWithFormat:@"%@/FailingTestsSetTempDir", tempDir] withError:&error]; + self.config.outputDirectory = outputDir; + self.config.errorRetriesCount = @1; + self.config.failureTolerance = @1; + self.config.testCasesToRun = @[testCase]; + + // Run Tests + __unused BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + + // Validate + XCTAssert([BPTestUtils checkIfTestCase:testCase bundleName:@"BPLogicTests" wasRunInLog:[outputDir stringByAppendingPathComponent:@"1-simulator.log"]]); + XCTAssert([BPTestUtils checkIfTestCase:testCase bundleName:@"BPLogicTests" wasRunInLog:[outputDir stringByAppendingPathComponent:@"2-simulator.log"]]); +} + +@end diff --git a/bp/tests/Utils/BPTestUtils.h b/bp/tests/Utils/BPTestUtils.h new file mode 100644 index 00000000..45717733 --- /dev/null +++ b/bp/tests/Utils/BPTestUtils.h @@ -0,0 +1,29 @@ +// Copyright 2016 LinkedIn Corporation +// Licensed under the BSD 2-Clause License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at https://opensource.org/licenses/BSD-2-Clause +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +#import +#import "BPExitStatus.h" + +@class BPConfiguration; + +NS_ASSUME_NONNULL_BEGIN + +@interface BPTestUtils : NSObject + ++ (nonnull BPConfiguration *)makeUnhostedTestConfiguration; + ++ (nonnull BPConfiguration *)makeHostedTestConfiguration; + ++ (void)assertExitStatus:(BPExitStatus)exitStatus matchesExpected:(BPExitStatus)expectedStatus; + ++ (BOOL)checkIfTestCase:(NSString *)testCase bundleName:(NSString *)bundleName wasRunInLog:(NSString *)logPath; + +@end + +NS_ASSUME_NONNULL_END diff --git a/bp/tests/Utils/BPTestUtils.m b/bp/tests/Utils/BPTestUtils.m new file mode 100644 index 00000000..4aa0a63b --- /dev/null +++ b/bp/tests/Utils/BPTestUtils.m @@ -0,0 +1,98 @@ +// Copyright 2016 LinkedIn Corporation +// Licensed under the BSD 2-Clause License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at https://opensource.org/licenses/BSD-2-Clause +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +#import "BPTestUtils.h" + +#import + +#import "BPConfiguration.h" +#import "BPTestHelper.h" +#import "BPUtils.h" +#import "SimDeviceType.h" +#import "SimRuntime.h" +#import "SimServiceContext.h" + +@implementation BPTestUtils + ++ (nonnull BPConfiguration *)makeUnhostedTestConfiguration { + BPConfiguration *config = [self makeDefaultTestConfiguration]; + config.testBundlePath = [BPTestHelper logicTestBundlePath]; + config.isLogicTestTarget = YES; + return config; +} + ++ (nonnull BPConfiguration *)makeHostedTestConfiguration { + BPConfiguration *config = [self makeDefaultTestConfiguration]; + config.appBundlePath = [BPTestHelper sampleAppPath]; + config.testBundlePath = [BPTestHelper sampleAppNegativeTestsBundlePath]; + config.isLogicTestTarget = NO; + return config; +} + ++ (nonnull BPConfiguration *)makeDefaultTestConfiguration { + BPConfiguration *config = [[BPConfiguration alloc] initWithProgram:BP_BINARY]; + config.stuckTimeout = @40; + config.xcodePath = [BPUtils runShell:@"/usr/bin/xcode-select -print-path"]; + config.runtime = @BP_DEFAULT_RUNTIME; + config.repeatTestsCount = @1; + config.errorRetriesCount = @0; + config.testCaseTimeout = @20; + config.deviceType = @BP_DEFAULT_DEVICE_TYPE; + config.headlessMode = YES; + config.videoPaths = @[[BPTestHelper sampleVideoPath]]; + config.testRunnerAppPath = nil; + config.testing_CrashAppOnLaunch = NO; + config.cloneSimulator = NO; + config.outputDirectory = @"/Users/lthrockm/Desktop/output/"; + + // Set up simulator device + runtime + NSError *err; + SimServiceContext *sc = [SimServiceContext sharedServiceContextForDeveloperDir:config.xcodePath error:&err]; + if (!sc) { NSLog(@"Failed to initialize SimServiceContext: %@", err); } + + for (SimDeviceType *type in [sc supportedDeviceTypes]) { + if ([[type name] isEqualToString:config.deviceType]) { + config.simDeviceType = type; + break; + } + } + XCTAssert(config.simDeviceType != nil); + + for (SimRuntime *runtime in [sc supportedRuntimes]) { + if ([[runtime name] containsString:config.runtime]) { + config.simRuntime = runtime; + break; + } + } + XCTAssert(config.simRuntime != nil); + + return config; +} + ++ (void)assertExitStatus:(BPExitStatus)exitStatus matchesExpected:(BPExitStatus)expectedStatus { + XCTAssert(exitStatus == expectedStatus, + @"Expected: %@ Got: %@", + [BPExitStatusHelper stringFromExitStatus:expectedStatus], + [BPExitStatusHelper stringFromExitStatus:exitStatus]); +} + ++ (BOOL)checkIfTestCase:(NSString *)testCase bundleName:(NSString *)bundleName wasRunInLog:(NSString *)logPath { + NSString *testName = testCase; + if ([BPUtils isTestSwiftTest:testName]) { + testName = [BPUtils formatSwiftTestForXCTest:testName withBundleName:bundleName]; + } + NSArray *testComponents = [testName componentsSeparatedByString:@"/"]; + NSString *expectedString = [NSString stringWithFormat:@"Test Case '-[%@ %@]' started.", testComponents[0], testComponents[1]]; + NSString *log = [NSString stringWithContentsOfFile:logPath encoding:NSUTF8StringEncoding error:nil]; + XCTAssertNotNil(log); + NSLog(@"log: %@", log); + return [log rangeOfString:expectedString].location != NSNotFound; +} + +@end diff --git a/bptestrunner/bluepill_batch_test.bzl b/bptestrunner/bluepill_batch_test.bzl index 36203270..3523d769 100644 --- a/bptestrunner/bluepill_batch_test.bzl +++ b/bptestrunner/bluepill_batch_test.bzl @@ -29,9 +29,10 @@ def _bluepill_batch_test_impl(ctx): #test_plan test_plan = struct( - test_host = test_host.basename.split( - "." + test_host.extension, - )[0] + ".app", + if test_host != None: + test_host = test_host.basename.split( + "." + test_host.extension, + )[0] + ".app", environment = test_env, arguments = test_env, test_bundle_path = bundle_info.bundle_name + bundle_info.bundle_extension, From 8ee43b44adb43fe468fac707eb81dbd999a8ec09 Mon Sep 17 00:00:00 2001 From: Lucas Statler Date: Thu, 15 Jun 2023 11:23:30 -0700 Subject: [PATCH 02/11] Setup example logic test bundles to get built in CI --- .../xcschemes/BPSampleApp.xcscheme | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPSampleApp.xcscheme b/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPSampleApp.xcscheme index 9fd67d6b..86fbfb16 100644 --- a/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPSampleApp.xcscheme +++ b/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPSampleApp.xcscheme @@ -104,6 +104,34 @@ ReferencedContainer = "container:BPSampleApp.xcodeproj"> + + + + + + + + + + + + Date: Thu, 15 Jun 2023 18:11:32 -0700 Subject: [PATCH 03/11] Add a couple more test cases --- bluepill/tests/BPIntegrationTests.m | 45 +++++++++++++++++++++++++++++ bp/tests/BPCLITests.m | 2 ++ 2 files changed, 47 insertions(+) diff --git a/bluepill/tests/BPIntegrationTests.m b/bluepill/tests/BPIntegrationTests.m index 356bd8cf..86645e4d 100644 --- a/bluepill/tests/BPIntegrationTests.m +++ b/bluepill/tests/BPIntegrationTests.m @@ -85,6 +85,32 @@ - (void)testLogicTestBundles { XCTAssert([runner busySwimlaneCount] == 0); } +- (void)testTwoBPInstancesWithLogicTestPlanJson { + [self writeLogicTestPlan]; + BPConfiguration *config = [BPTestUtils makeUnhostedTestConfiguration]; + config.numSims = @2; + config.testBundlePath = nil; + config.testRunnerAppPath = nil; + config.appBundlePath = nil; + config.testPlanPath = [BPTestHelper testPlanPath]; + + NSError *err; + [config validateConfigWithError:&err]; + BPApp *app = [BPApp appWithConfig:config withError:&err]; + NSString *bpPath = [BPTestHelper bpExecutablePath]; + BPRunner *runner = [BPRunner BPRunnerWithConfig:config withBpPath:bpPath]; + XCTAssert(runner != nil); + int rc = [runner runWithBPXCTestFiles:app.testBundles]; + XCTAssert(rc != 0); // this runs tests that fail + XCTAssertEqual(app.testBundles.count, 2); + XCTAssertTrue([app.testBundles[0].name isEqualToString:@"BPLogicTests"]); + XCTAssertEqual(app.testBundles[0].numTests, 10); + XCTAssertEqual(app.testBundles[0].skipTestIdentifiers.count, 0); + XCTAssertTrue([app.testBundles[1].name isEqualToString:@"BPPassingLogicTests"]); + XCTAssertEqual(app.testBundles[1].numTests, 207); + XCTAssertEqual(app.testBundles[1].skipTestIdentifiers.count, 0); +} + - (void)testOneBPInstance { BPConfiguration *config = [self generateConfig]; config.numSims = @1; @@ -250,6 +276,25 @@ - (void)writeTestPlan { [jsonData writeToFile:[BPTestHelper testPlanPath] atomically:YES]; } +- (void)writeLogicTestPlan { + NSDictionary *testPlan = @{ + @"tests": @{ + @"BPLogicTests": @{ + @"test_bundle_path": [BPTestHelper logicTestBundlePath], + @"environment": @{}, + @"arguments": @{} + }, + @"BPPassingLogicTests": @{ + @"test_bundle_path": [BPTestHelper passingLogicTestBundlePath], + @"environment": @{}, + @"arguments": @{} + } + } + }; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:testPlan options:0 error:nil]; + [jsonData writeToFile:[BPTestHelper testPlanPath] atomically:YES]; +} + // TODO: Enable this when we figure out issue #469 - (void)DISABLE_testTwoBPInstancesWithVideo { NSFileManager *fileManager = [NSFileManager defaultManager]; diff --git a/bp/tests/BPCLITests.m b/bp/tests/BPCLITests.m index 8ef51a7b..24a80378 100644 --- a/bp/tests/BPCLITests.m +++ b/bp/tests/BPCLITests.m @@ -43,6 +43,7 @@ - (void)testListArguments { [config saveOpt:[NSNumber numberWithInt:'N'] withArg:[NSString stringWithUTF8String:"foo"]]; [config saveOpt:[NSNumber numberWithInt:'N'] withArg:[NSString stringWithUTF8String:"bar"]]; [config saveOpt:[NSNumber numberWithInt:'N'] withArg:[NSString stringWithUTF8String:"baz"]]; + [config saveOpt:[NSNumber numberWithInt:369] withArg:@"YES"]; NSError *err; BOOL result; @@ -54,6 +55,7 @@ - (void)testListArguments { XCTAssertEqualObjects(config.errorRetriesCount, @2); XCTAssertEqualObjects(config.failureTolerance, @1); XCTAssertEqualObjects(config.numSims, @5); + XCTAssert(config.isLogicTestTarget); } - (void)testIgnoringAdditionalTestBundles { From 3de4a2a9b19efe055001ea1de390917446a4486c Mon Sep 17 00:00:00 2001 From: Lucas Statler Date: Wed, 28 Jun 2023 15:08:18 -0400 Subject: [PATCH 04/11] Lipo xctest executable when required for arch mismatches --- .../BPSampleApp.xcodeproj/project.pbxproj | 1 + .../xcschemes/BPPassingLogicTests.xcscheme | 78 +++++++++ bluepill/bluepill.xcodeproj/project.pbxproj | 8 + bluepill/src/BPApp.m | 2 +- bluepill/src/BPPacker.m | 5 + bluepill/tests/BPIntegrationTests.m | 45 ++++- .../BPLogicTestFixture_arm64 | Bin 0 -> 71744 bytes .../Info.plist | Bin 0 -> 743 bytes .../_CodeSignature/CodeResources | 101 +++++++++++ .../BPLogicTestFixture_x86_64 | Bin 0 -> 71616 bytes .../Info.plist | Bin 0 -> 744 bytes .../_CodeSignature/CodeResources | 101 +++++++++++ bp/bp.xcodeproj/project.pbxproj | 8 +- bp/src/BPConfiguration.h | 4 + bp/src/BPConstants.h | 2 +- bp/src/BPHandler.h | 2 + bp/src/BPHandler.m | 6 +- bp/src/BPSimulator.m | 42 +++-- bp/src/BPUtils.h | 21 +++ bp/src/BPUtils.m | 162 ++++++++++++++++++ bp/src/BPXCTestFile.h | 1 + bp/src/BPXCTestFile.m | 16 ++ bp/src/Bluepill.m | 30 +++- bp/src/SimulatorHelper.m | 3 +- bp/tests/BPTestHelper.h | 6 + bp/tests/BPTestHelper.m | 8 + bp/tests/BluepillHostedTests.m | 2 +- .../Unhosted Tests/BluepillUnhostedTests.m | 10 +- bptestrunner/bluepill_batch_test.bzl | 24 ++- .../bluepill_batch_test_runner.template.sh | 29 ++-- 30 files changed, 660 insertions(+), 57 deletions(-) create mode 100644 BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPPassingLogicTests.xcscheme create mode 100755 bluepill/tests/Resource Files/BPLogicTestFixture_arm64.xctest/BPLogicTestFixture_arm64 create mode 100644 bluepill/tests/Resource Files/BPLogicTestFixture_arm64.xctest/Info.plist create mode 100644 bluepill/tests/Resource Files/BPLogicTestFixture_arm64.xctest/_CodeSignature/CodeResources create mode 100755 bluepill/tests/Resource Files/BPLogicTestFixture_x86_64.xctest/BPLogicTestFixture_x86_64 create mode 100644 bluepill/tests/Resource Files/BPLogicTestFixture_x86_64.xctest/Info.plist create mode 100644 bluepill/tests/Resource Files/BPLogicTestFixture_x86_64.xctest/_CodeSignature/CodeResources diff --git a/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj b/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj index 1e6659d8..567ba61e 100644 --- a/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj +++ b/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj @@ -1031,6 +1031,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 14.4; MTL_ENABLE_DEBUG_INFO = NO; + ONLY_ACTIVE_ARCH = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; diff --git a/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPPassingLogicTests.xcscheme b/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPPassingLogicTests.xcscheme new file mode 100644 index 00000000..5625c7da --- /dev/null +++ b/BPSampleApp/BPSampleApp.xcodeproj/xcshareddata/xcschemes/BPPassingLogicTests.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bluepill/bluepill.xcodeproj/project.pbxproj b/bluepill/bluepill.xcodeproj/project.pbxproj index 03ef4457..f26a9e2c 100644 --- a/bluepill/bluepill.xcodeproj/project.pbxproj +++ b/bluepill/bluepill.xcodeproj/project.pbxproj @@ -37,6 +37,8 @@ C4D6861A2267ABEF007D4237 /* bplib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4D686192267ABEF007D4237 /* bplib.framework */; }; C4FD8C581DB6E09B000ED28C /* BPPacker.m in Sources */ = {isa = PBXBuildFile; fileRef = C4FD8C571DB6E09B000ED28C /* BPPacker.m */; }; E49235FF22EA847700395D98 /* times.json in Resources */ = {isa = PBXBuildFile; fileRef = E49235FE22EA847700395D98 /* times.json */; }; + FBA69FA72A45F889003BADBF /* BPLogicTestFixture_arm64.xctest in Resources */ = {isa = PBXBuildFile; fileRef = FBA69FA52A45F889003BADBF /* BPLogicTestFixture_arm64.xctest */; }; + FBA69FA82A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest in Resources */ = {isa = PBXBuildFile; fileRef = FBA69FA62A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -85,6 +87,8 @@ C4FD8C561DB6E09B000ED28C /* BPPacker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPPacker.h; sourceTree = ""; }; C4FD8C571DB6E09B000ED28C /* BPPacker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPPacker.m; sourceTree = ""; }; E49235FE22EA847700395D98 /* times.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = times.json; sourceTree = ""; }; + FBA69FA52A45F889003BADBF /* BPLogicTestFixture_arm64.xctest */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = BPLogicTestFixture_arm64.xctest; sourceTree = ""; }; + FBA69FA62A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = BPLogicTestFixture_x86_64.xctest; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -142,6 +146,8 @@ 0173521223679E87008BFA4E /* TEST-FinalReport.xml */, E49235FE22EA847700395D98 /* times.json */, BA9C2DB01DD67B66007CB967 /* testScheme.xcscheme */, + FBA69FA52A45F889003BADBF /* BPLogicTestFixture_arm64.xctest */, + FBA69FA62A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest */, BA9C2DAE1DD674AD007CB967 /* BPSampleAppTests.xctest */, C415174C273AE3CE00646740 /* Expected-TEST-FinalReport-for-invalid-xml.xml */, ); @@ -272,8 +278,10 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + FBA69FA72A45F889003BADBF /* BPLogicTestFixture_arm64.xctest in Resources */, BA9C2DB11DD67B66007CB967 /* testScheme.xcscheme in Resources */, BA9C2DAF1DD674AD007CB967 /* BPSampleAppTests.xctest in Resources */, + FBA69FA82A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest in Resources */, E49235FF22EA847700395D98 /* times.json in Resources */, C415174F273AEAC400646740 /* simulator in Resources */, 0173521323679E87008BFA4E /* TEST-FinalReport.xml in Resources */, diff --git a/bluepill/src/BPApp.m b/bluepill/src/BPApp.m index 677f5c0c..8a57d2ef 100644 --- a/bluepill/src/BPApp.m +++ b/bluepill/src/BPApp.m @@ -140,7 +140,7 @@ + (instancetype)appWithConfig:(BPConfiguration *)config andTestBundlePath:config.testBundlePath andUITargetAppPath:config.testRunnerAppPath withError:errPtr]]; - } else if (config.isLogicTestTarget) { + } else if (config.isLogicTestTarget && config.testBundlePath) { BPXCTestFile *testFile = [BPXCTestFile BPXCTestFileFromXCTestBundle:config.testBundlePath andHostAppBundle:nil andUITargetAppPath:nil diff --git a/bluepill/src/BPPacker.m b/bluepill/src/BPPacker.m index d9493a1a..f97846cf 100644 --- a/bluepill/src/BPPacker.m +++ b/bluepill/src/BPPacker.m @@ -141,7 +141,12 @@ @implementation BPPacker andXCTestFiles:xcTestFiles]; NSMutableArray *bundles = [[NSMutableArray alloc] init]; + + NSInteger counter = 0; for (BPXCTestFile *xctFile in xcTestFiles) { + counter += 1; + [BPUtils printInfo:INFO withString:@"%@: Looping through... counter = %@", xctFile.testBundlePath, @(counter)]; + NSArray *bundleTestsToRun = [[testsToRunByFilePath[xctFile.testBundlePath] allObjects] sortedArrayUsingSelector:@selector(compare:)]; NSNumber *estimatedBundleTime = testEstimatesByFilePath[xctFile.testBundlePath]; // If the bundle is small enough, do not split. Also do not split if the bundle is in no_split list. diff --git a/bluepill/tests/BPIntegrationTests.m b/bluepill/tests/BPIntegrationTests.m index 86645e4d..a2d24250 100644 --- a/bluepill/tests/BPIntegrationTests.m +++ b/bluepill/tests/BPIntegrationTests.m @@ -57,14 +57,55 @@ - (void)tearDown { [super tearDown]; } -- (void)testLogicTestBundles { +- (void)testArchitecture_x86_64 { + NSString *bundlePath = BPTestHelper.logicTestBundlePath_x86_64; + BPConfiguration *config = [BPTestUtils makeUnhostedTestConfiguration]; config.stuckTimeout = @(2); config.testCaseTimeout = @(10); + // Test multiple test bundles, while skipping any failing tests so that we + // can still validate that we get a success code.. + config.testBundlePath = bundlePath; + + NSString *bpPath = [BPTestHelper bpExecutablePath]; + BPRunner *runner = [BPRunner BPRunnerWithConfig:config withBpPath:bpPath]; + NSError *error; + BPApp *app = [BPApp appWithConfig:config withError:&error]; + + XCTAssert(runner != nil); + int rc = [runner runWithBPXCTestFiles:app.testBundles]; + XCTAssert(rc == 0, @"Wanted 0, got %d", rc); + XCTAssert([runner busySwimlaneCount] == 0); +} + +- (void)testArchitecture_arm64 { + NSString *bundlePath = BPTestHelper.logicTestBundlePath_arm64; + BPConfiguration *config = [BPTestUtils makeUnhostedTestConfiguration]; + config.stuckTimeout = @(2); + config.testCaseTimeout = @(10); + // Test multiple test bundles, while skipping any failing tests so that we + // can still validate that we get a success code.. + config.testBundlePath = bundlePath; + + NSString *bpPath = [BPTestHelper bpExecutablePath]; + BPRunner *runner = [BPRunner BPRunnerWithConfig:config withBpPath:bpPath]; + NSError *error; + BPApp *app = [BPApp appWithConfig:config withError:&error]; + + XCTAssert(runner != nil); + int rc = [runner runWithBPXCTestFiles:app.testBundles]; + XCTAssert(rc == 0, @"Wanted 0, got %d", rc); + XCTAssert([runner busySwimlaneCount] == 0); +} + +- (void)testLogicTestBundles { + BPConfiguration *config = [BPTestUtils makeUnhostedTestConfiguration]; + config.stuckTimeout = @(2); + config.testCaseTimeout = @(10); // Test multiple test bundles, while skipping any failing tests so that we // can still validate that we get a success code.. - config.testBundlePath = BPTestHelper.passingLogicTestBundlePath; + config.testBundlePath = BPTestHelper.logicTestBundlePath; config.additionalUnitTestBundles = @[BPTestHelper.logicTestBundlePath]; config.testCasesToSkip = @[ @"BPLogicTests/testFailingLogicTest", diff --git a/bluepill/tests/Resource Files/BPLogicTestFixture_arm64.xctest/BPLogicTestFixture_arm64 b/bluepill/tests/Resource Files/BPLogicTestFixture_arm64.xctest/BPLogicTestFixture_arm64 new file mode 100755 index 0000000000000000000000000000000000000000..8071c1ce5298f1a7f0153b3aa53e4ae5c68f9c9d GIT binary patch literal 71744 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 L2rvTQFa-V=f9E{@ literal 0 HcmV?d00001 diff --git a/bluepill/tests/Resource Files/BPLogicTestFixture_arm64.xctest/Info.plist b/bluepill/tests/Resource Files/BPLogicTestFixture_arm64.xctest/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..36f64bde08c949f0e5d66dddd57757e1c33a4927 GIT binary patch literal 743 zcmZ8d%Wl&^6rDRj2~ddJ6#9UcQl4F4*=dw6Sm8R6QqnfmZg{AQnK+YV#Moo}s2cbI zc6I)C4r2ovKq}Cshl5ps>_Mr zQ*}q%D{98NwB}hbj7M4A*2_{>3jfhV*49h9*3?TzX}v9LwRCh(0hB{)sD>zdjozV; z=nMLWe#0az!74m}O=tmw2;RV3cn=@o6MTlR@B@CrFZct0@dVD{`*;mk@FsqNDdzYU Qj?m~}I7YOZVhT9<2ZJ5uKL7v# literal 0 HcmV?d00001 diff --git a/bluepill/tests/Resource Files/BPLogicTestFixture_arm64.xctest/_CodeSignature/CodeResources b/bluepill/tests/Resource Files/BPLogicTestFixture_arm64.xctest/_CodeSignature/CodeResources new file mode 100644 index 00000000..85547178 --- /dev/null +++ b/bluepill/tests/Resource Files/BPLogicTestFixture_arm64.xctest/_CodeSignature/CodeResources @@ -0,0 +1,101 @@ + + + + + files + + Info.plist + + GbcklTveKC3Dq/Hd5o0K0x80WcA= + + + files2 + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/bluepill/tests/Resource Files/BPLogicTestFixture_x86_64.xctest/BPLogicTestFixture_x86_64 b/bluepill/tests/Resource Files/BPLogicTestFixture_x86_64.xctest/BPLogicTestFixture_x86_64 new file mode 100755 index 0000000000000000000000000000000000000000..42f14694b97dadd3c82153b51728628689317c88 GIT binary patch literal 71616 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 B|8f8T literal 0 HcmV?d00001 diff --git a/bluepill/tests/Resource Files/BPLogicTestFixture_x86_64.xctest/Info.plist b/bluepill/tests/Resource Files/BPLogicTestFixture_x86_64.xctest/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..0bfef9a060dd6e621e09a4a42dca270d20a82d6c GIT binary patch literal 744 zcmZ8d%Wl&^6rDRf+Cm|2Qz$94l=AGxvV)Z(vBGsDg{Ez&-SAKqYvN3j5#xvah!Xe# zc6v zuUsuibMwW8Yu6WVlopq6-def6dS`9*?!EgD9@k-b zn5(-nb$ilCP5%%+G%hScm2?fY^jhZv(OvE z+VtIk@$_c}!^*AWiCdHYVA(o7LDR68^%dU?ctM(UD3o&>NQz!n8Xb@ zeHH|%B{D0rV(Gg^-Be}eXj#m{8hziRrg%_H?}sl`TUv8`S0;go$)Xw$9m$*zT-o8o z>&dFE9F!G(Ls$zeNaIl+x73P|rQ#VnWi3@xm8Poc+D2Q{vUc=H0yK>tqZ*><9r}pA zpzr7>I)+(TfpvHa&!GhjB6trU;1hg?uka0iz;E~i$M6^a;VC?WWvt;U-o^xXv5P|- Pqw&FTf@nR%4DjGTC+Ou@ literal 0 HcmV?d00001 diff --git a/bluepill/tests/Resource Files/BPLogicTestFixture_x86_64.xctest/_CodeSignature/CodeResources b/bluepill/tests/Resource Files/BPLogicTestFixture_x86_64.xctest/_CodeSignature/CodeResources new file mode 100644 index 00000000..00ca198f --- /dev/null +++ b/bluepill/tests/Resource Files/BPLogicTestFixture_x86_64.xctest/_CodeSignature/CodeResources @@ -0,0 +1,101 @@ + + + + + files + + Info.plist + + a2X62OSUA6yDTeQ0W3o6GtPmrKc= + + + files2 + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/bp/bp.xcodeproj/project.pbxproj b/bp/bp.xcodeproj/project.pbxproj index d8c461cc..0918eff2 100644 --- a/bp/bp.xcodeproj/project.pbxproj +++ b/bp/bp.xcodeproj/project.pbxproj @@ -1063,6 +1063,7 @@ 7A79018A1D5CB679004D4325 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; DEPLOYMENT_LOCATION = NO; "DEVELOPER_PRIVATE_FRAMEWORKS_DIR[arch=*]" = /Library/Developer/PrivateFrameworks/; FRAMEWORK_SEARCH_PATHS = ( @@ -1102,6 +1103,7 @@ 7A79018B1D5CB679004D4325 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; DEPLOYMENT_LOCATION = NO; "DEVELOPER_PRIVATE_FRAMEWORKS_DIR[arch=*]" = /Library/Developer/PrivateFrameworks/; FRAMEWORK_SEARCH_PATHS = ( @@ -1141,7 +1143,7 @@ B368E56B213F8D2F00B4DEA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = x86_64; + ARCHS = "$(ARCHS_STANDARD)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -1178,7 +1180,7 @@ B368E56C213F8D2F00B4DEA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = x86_64; + ARCHS = "$(ARCHS_STANDARD)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -1215,6 +1217,7 @@ BAB24F6D1DB5DB2300867756 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 57Y47U492U; HEADER_SEARCH_PATHS = ( @@ -1247,6 +1250,7 @@ BAB24F6E1DB5DB2300867756 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 57Y47U492U; HEADER_SEARCH_PATHS = ( diff --git a/bp/src/BPConfiguration.h b/bp/src/BPConfiguration.h index ebd949b9..497d0732 100644 --- a/bp/src/BPConfiguration.h +++ b/bp/src/BPConfiguration.h @@ -132,6 +132,10 @@ typedef NS_ENUM(NSInteger, BPProgram) { @property (nonatomic, strong) SimDeviceType *simDeviceType; @property (nonatomic, strong) SimRuntime *simRuntime; +@property (nonatomic, strong) NSString *xctestBinaryPath; +@property (nonatomic, strong) NSString *dyldFrameworkPath; +@property (nonatomic, strong) NSDictionary *standardizedSwiftTestNames; + /** Return a structure suitable for passing to `getopt()`'s long options. diff --git a/bp/src/BPConstants.h b/bp/src/BPConstants.h index c5963e2a..21c1d5ee 100644 --- a/bp/src/BPConstants.h +++ b/bp/src/BPConstants.h @@ -10,7 +10,7 @@ #import #pragma mark - Version Constants -#define BP_DEFAULT_XCODE_VERSION "14.0" +#define BP_DEFAULT_XCODE_VERSION "14.1" #define BP_DEFAULT_RUNTIME "iOS 16.0" #define BP_DEFAULT_BASE_SDK "16.0" diff --git a/bp/src/BPHandler.h b/bp/src/BPHandler.h index 2d0ddbbc..d2a0d276 100644 --- a/bp/src/BPHandler.h +++ b/bp/src/BPHandler.h @@ -18,6 +18,8 @@ typedef void (^BasicHandlerBlock)(void); typedef void (^BasicErrorBlock)(NSError *error); +@property (nonatomic, assign, class) NSInteger timeoutErrorCode; + @property (nonatomic, strong) BPWaitTimer *timer; @property (nonatomic, copy) BasicHandlerBlock beginWith; diff --git a/bp/src/BPHandler.m b/bp/src/BPHandler.m index edeccb2b..8453c948 100644 --- a/bp/src/BPHandler.m +++ b/bp/src/BPHandler.m @@ -31,7 +31,7 @@ - (void)setup { } // call timeout block first and then execute the onError block if (__self.onError) { - NSError *error = [NSError errorWithDomain:BPErrorDomain code:-1 userInfo:nil]; + NSError *error = [NSError errorWithDomain:BPErrorDomain code:BPHandler.timeoutErrorCode userInfo:nil]; __self.onError(error); } }); @@ -63,4 +63,8 @@ - (BasicErrorBlock)defaultHandlerBlock { }; } ++ (NSInteger)timeoutErrorCode { + return 8; +} + @end diff --git a/bp/src/BPSimulator.m b/bp/src/BPSimulator.m index 8dd8bc0f..58295e8f 100644 --- a/bp/src/BPSimulator.m +++ b/bp/src/BPSimulator.m @@ -468,7 +468,6 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser - options: - -s // "standalone" option - -w // "wait_for_debugger" option - - -a // "arch" option - args: - /Applications/.../Xcode/Agents/xctest // Yes, spawn redundantly requires this both the path param + an arg :) - -XCTest @@ -478,14 +477,9 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser From `xcrun simctl spawn help`: `simctl spawn [-w | --wait-for-debugger] [-s | --standalone] [-a | --arch=] [ ... ]` */ - - NSString *executablePath = [[NSString alloc] initWithFormat: - @"%@/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Agents/xctest", - self.config.xcodePath]; NSString *xctestPath = self.config.testBundlePath; - NSLog(@"%@", testsToRunArg); NSArray *arguments = @[ - executablePath, + self.config.xctestBinaryPath, @"-XCTest", testsToRunArg, xctestPath, @@ -495,10 +489,15 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser NSString *simStdoutPath = [SimulatorHelper makeStdoutFileOnDevice:self.device]; NSString *simStdoutRelativePath = [simStdoutPath substringFromIndex:self.device.dataPath.length]; // Environment - NSDictionary *environment = @{ + NSMutableDictionary *environment = [@{ kOptionsStdoutKey: simStdoutRelativePath, kOptionsStderrKey: simStdoutRelativePath, - }; + } mutableCopy]; + if (self.config.dyldFrameworkPath) { + environment[@"DYLD_FRAMEWORK_PATH"] = self.config.dyldFrameworkPath; + } + [environment addEntriesFromDictionary:self.config.environmentVariables]; + self.appOutput = [NSFileHandle fileHandleForReadingAtPath:simStdoutPath]; NSFileHandle *outputFileHandle = [NSFileHandle fileHandleForWritingAtPath:simStdoutPath]; @@ -510,6 +509,8 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser @"standalone": @(1), @"stdout": stdoutFileDescriptor, @"stderr": stdoutFileDescriptor, +// @"stdout": @(1), +// @"stderr": @(2), }; // Set up monitor @@ -528,7 +529,7 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser // To see more on how to debug the expected format/inputs of the options array, // see the in-depth documentation in SimDevice.h. __block typeof(self) blockSelf = self; - [self.device spawnAsyncWithPath:executablePath + [self.device spawnAsyncWithPath:self.config.xctestBinaryPath options:options terminationHandler:^(int stat_loc) { // The naming here is confusing, but this `terminationHandler` is called once @@ -554,10 +555,11 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser [BPUtils printInfo:DEBUGINFO withString: @"Spawned XCTest execution failed with error code: %@", @(exitCode)]; } } - [blockSelf cleanUpParser:parser fileHandle:outputFileHandle]; + if (error) { + [blockSelf signalCloseToParser:parser fileHandle:outputFileHandle]; + } + [blockSelf cleanUpParser:parser]; completionBlock(error, blockSelf.monitor.appPID); - - [outputFileHandle closeFile]; } completionHandler:^(NSError *error, pid_t pid) { // Again, this `completionHandler` is called once the process is done SPAWNING, // as opposed to happening after the process itself has finished. @@ -567,15 +569,18 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser }]; } -- (void)cleanUpParser:(BPTreeParser *)parser fileHandle:(NSFileHandle *)fileHandle { - self.monitor.appState = Completed; - [parser.delegate setParserStateCompleted]; - // Post a APPCLOSED signal to the parser +// Posts a APPCLOSED signal to the parser, indicating a crash/kill +- (void)signalCloseToParser:(BPTreeParser *)parser fileHandle:(NSFileHandle *)fileHandle { [fileHandle seekToEndOfFile]; [fileHandle writeData:[@"\nBP_APP_PROC_ENDED\n" dataUsingEncoding:NSUTF8StringEncoding]]; [fileHandle closeFile]; } +- (void)cleanUpParser:(BPTreeParser *)parser { + self.monitor.appState = Completed; + [parser.delegate setParserStateCompleted]; +} + + (NSMutableArray *)commandLineArgsFromConfig:(BPConfiguration *)config { NSArray *argumentsArr = config.commandLineArguments ?: @[]; NSMutableArray *commandLineArgs = [NSMutableArray array]; @@ -678,7 +683,8 @@ - (void)launchApplicationAndExecuteTestsWithParser:(BPTreeParser *)parser andCom dispatch_source_cancel(source); }); dispatch_source_set_cancel_handler(source, ^{ - [blockSelf cleanUpParser:parser fileHandle:[NSFileHandle fileHandleForWritingAtPath:simStdoutPath]]; + [blockSelf signalCloseToParser:parser fileHandle:[NSFileHandle fileHandleForWritingAtPath:simStdoutPath]]; + [blockSelf cleanUpParser:parser]; }); dispatch_resume(source); self.appOutput.readabilityHandler = ^(NSFileHandle *handle) { diff --git a/bp/src/BPUtils.h b/bp/src/BPUtils.h index 114e1621..91f8cecc 100644 --- a/bp/src/BPUtils.h +++ b/bp/src/BPUtils.h @@ -29,6 +29,7 @@ typedef NS_ENUM(int, BPKind) { }; @class BPConfiguration; +@class BPExecutionContext; @interface BPUtils : NSObject @@ -274,4 +275,24 @@ typedef BOOL (^BPRunBlock)(void); testTimes:(NSDictionary *)testTimes andXCTestFiles:(NSArray *)xcTestFiles; +#pragma mark - Logic Test Architecture Helpers + +/** + We can isolate a single architecture out of a universal binary using the `lipo -extract` command. By doing so, we can + force an executable (such as XCTest) to always run w/ the architecture we expect. This is to avoid some funny business where + the architecture selected can be unexpected depending on multiple factors, such as Rosetta, xcode version, etc. + + @return the path of the new executable if possible + required, nil otherwise. In nil case, original executable should be used instead. + */ ++ (NSString *)lipoExecutableAtPath:(NSString *)path withContext:(BPExecutionContext *)context; + +/** + Lipo'ing the universal binary alone to isolate the desired architecture will result in errors. + Specifically, the newly lipo'ed binary won't be able to find any of the required frameworks + from within the original binary. So, we need to set up the `DYLD_FRAMEWORK_PATH` + in the environment to include the paths to these frameworks within the original universal + executable's binary. + */ ++ (NSString *)correctedDYLDFrameworkPathFromBinary:(NSString *)binaryPath; + @end diff --git a/bp/src/BPUtils.m b/bp/src/BPUtils.m index 40781f18..dc3f72af 100644 --- a/bp/src/BPUtils.m +++ b/bp/src/BPUtils.m @@ -13,6 +13,9 @@ #import "BPXCTestFile.h" #import "BPConfiguration.h" #import "SimDevice.h" +#import "SimDeviceType.h" +#import "BPExecutionContext.h" +#import "BPSimulator.h" @implementation BPUtils @@ -435,6 +438,7 @@ + (NSDictionary *)loadSimpleJsonFile:(NSString *)filePath [bundleTestsToRun minusSet:[[NSSet alloc] initWithArray:config.testCasesToSkip]]; } [BPUtils printInfo:INFO withString:@"Bundle: %@; All Tests count: %lu; bundleTestsToRun count: %lu", xctFile.testBundlePath, (unsigned long)[xctFile.allTestCases count], (unsigned long)[bundleTestsToRun count]]; + [BPUtils printInfo:INFO withString:@"Bundle: %@; All Tests cases: %@", xctFile.testBundlePath, xctFile.allTestCases]; if (bundleTestsToRun.count > 0) { testsToRunByFilePath[xctFile.testBundlePath] = bundleTestsToRun; } @@ -496,4 +500,162 @@ + (NSError *)errorWithCode:(NSInteger)code description:(NSString *)description { return [NSError errorWithDomain:BPErrorDomain code:code userInfo:userInfo]; } +#pragma mark - Architecture Helpers + +/** + We can isolate a single architecture out of a universal binary using the `lipo -extract` command. By doing so, we can + force an executable (such as XCTest) to always run w/ the architecture we expect. This is to avoid some funny business where + the architecture selected can be unexpected depending on multiple factors, such as Rosetta, xcode version, etc. + + @return the path of the new executable if possible + required, nil otherwise. In nil case, original executable should be used instead. + */ ++ (NSString *)lipoExecutableAtPath:(NSString *)path withContext:(BPExecutionContext *)context { + // If the executable isn't a universal binary, there's nothing we can do. If we don't + // support the test bundle type, we'll let it fail later naturally. + NSArray *executableArchitectures = [self availableArchitecturesForPath:path]; + BOOL isUniversalExecutable = [executableArchitectures containsObject:self.x86_64] && [executableArchitectures containsObject:self.arm64]; + if (!isUniversalExecutable) { + return nil; + } + // Now, get the test bundle's architecture. + NSString *bundlePath = context.config.testBundlePath; + NSString *testBundleName = [[bundlePath pathComponents].lastObject componentsSeparatedByString:@"."][0]; + NSString *testBundleBinaryPath = [bundlePath stringByAppendingPathComponent:testBundleName]; + NSArray *testBundleArchitectures = [self availableArchitecturesForPath:testBundleBinaryPath]; + BOOL isUniversalTestBundle = [testBundleArchitectures containsObject:self.x86_64] && [testBundleArchitectures containsObject:self.arm64]; + + // If the test bundle is a univeral binary, no need to lipo... xctest (regardless of the arch it's in) + // should be able to handle it. + if (isUniversalTestBundle) { + return nil; + } + + // If the test bundle's arch isn't supported by the sim, we're in an error state + NSArray *simArchitectures = [self architecturesSupportedByDevice:context.runner.device]; + if (![simArchitectures containsObject:testBundleArchitectures.firstObject]) { + return nil; + } + + // Now that we've done any error checking, we can handle our real cases, + // based on what xctest would default to vs. what we need it to do. + // Note that the universal binary will launch in the same arch as the machine, + // rather than defaulting to the arch of the parent process. + // + // 1) The current arch is x86_64 + // a) We are in Rosetta -> xctest will default to arm64 + // b) We are not in Rosetta -> xctest will default to x86 + // 2) The current arch is arm64 -> xctest will default to arm64 + // + // We handle these accordingly: + // 1a) we lipo if the test bundle is an x86_64 binary + // 1b) no-op. ... x86 will get handled automatically, and we have to fail if test bundle is arm64. + // 1c) no-op. ... arm64 will get handled automatically, and we have to fail if test bundle is x86. + BOOL isRosetta = [self.currentArchitecture isEqual:self.x86_64] && isUniversalExecutable; + if (!isRosetta || ![testBundleArchitectures.firstObject isEqual:self.x86_64]) { + return nil; + } + + // Now we lipo. + NSError *error; + NSString *fileName = [NSString stringWithFormat:@"%@xctest", NSTemporaryDirectory()]; + NSString *thinnedExecutablePath = [BPUtils mkstemp:fileName withError:&error]; + NSString *cmd = [NSString stringWithFormat:@"/usr/bin/lipo %@ -extract %@ -output %@", path, testBundleArchitectures.firstObject, thinnedExecutablePath]; + NSString *__unused output = [BPUtils runShell:cmd]; + return thinnedExecutablePath; +} + +/** + Lipo'ing the universal binary alone to isolate the desired architecture will result in errors. + Specifically, the newly lipo'ed binary won't be able to find any of the required frameworks + from within the original binary. So, we need to set up the `DYLD_FRAMEWORK_PATH` + in the environment to include the paths to these frameworks within the original universal + executable's binary. + */ ++ (NSString *)correctedDYLDFrameworkPathFromBinary:(NSString *)binaryPath { + NSString *otoolCommand = [NSString stringWithFormat:@"/usr/bin/otool -l %@", binaryPath]; + NSString *otoolInfo = [BPUtils runShell:otoolCommand]; + /** + Example output looks something like this: + + ``` + // Lots of stuff we don't care about, followed by a list of `LC_RPATH` entries + Load command 18 + cmd LC_RPATH + cmdsize 48 + path @executable_path/path/to/frameworks/ (offset 12) + // Lots more stuff we don't care about... + ``` + + We want to use a regex to extract out each of the `@executable_path/path/to/frameworks/`, + and then replace `@executable_path` with our original executable's parent directory. + */ + NSString *pattern = @"(?:^Load command \\d+\n" + "\\s*cmd LC_RPATH\n" + "\\s*cmdsize \\d+\n" + "\\s*path (@executable_path\\/.*) \\(offset \\d+\\))+"; + NSError *error; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern + options:NSRegularExpressionAnchorsMatchLines + error:&error]; + + NSMutableArray *paths = [NSMutableArray array]; + NSString *parentDirectory = [[binaryPath stringByResolvingSymlinksInPath] stringByDeletingLastPathComponent]; + if (regex) { + NSArray *matches = [regex matchesInString:otoolInfo + options:0 + range:NSMakeRange(0, otoolInfo.length)]; + for (NSTextCheckingResult *match in matches) { + // Extract the substring from the input string based on the matched range + NSString *relativePath = [otoolInfo substringWithRange:[match rangeAtIndex:1]]; + NSString *path = [relativePath stringByReplacingOccurrencesOfString:@"@executable_path" withString:parentDirectory]; + [paths addObject:path]; + } + } else { + NSLog(@"Error creating regular expression: %@", error); + } + + return [paths componentsJoinedByString:@":"]; +} + ++ (NSArray *)availableArchitecturesForPath:(NSString *)path { + NSString *cmd = [NSString stringWithFormat:@"/usr/bin/lipo -archs %@", path]; + return [[BPUtils runShell:cmd] componentsSeparatedByString:@" "]; +} + ++ (NSArray *)architecturesSupportedByDevice:(SimDevice *)device { + NSArray *simSupportedArchitectures = device.deviceType.supportedArchs; + NSMutableArray *simArchitectures = [NSMutableArray array]; + for (NSNumber *supportedArchitecture in simSupportedArchitectures) { + [simArchitectures addObject:[self architectureName:supportedArchitecture.intValue]]; + } + return [simArchitectures copy]; +} + ++ (NSString *)arm64 { + return [self architectureName:CPU_TYPE_ARM64]; +} + ++ (NSString *)x86_64 { + return [self architectureName:CPU_TYPE_X86_64]; +} + ++ (NSString *)architectureName:(integer_t)architecture { + if (architecture == CPU_TYPE_X86_64) { + return @"x86_64"; + } else if (architecture == CPU_TYPE_ARM64) { + return @"arm64"; + } + return nil; +} + ++ (NSString *)currentArchitecture { + #if TARGET_CPU_ARM64 + return [self architectureName:CPU_TYPE_ARM64]; + #elif TARGET_CPU_X86_64 + return [self architectureName:CPU_TYPE_X86_64]; + #else + return nil; + #endif +} + @end diff --git a/bp/src/BPXCTestFile.h b/bp/src/BPXCTestFile.h index 336be782..5814014b 100644 --- a/bp/src/BPXCTestFile.h +++ b/bp/src/BPXCTestFile.h @@ -20,6 +20,7 @@ @property (nonatomic, strong) NSString *testBundlePath; @property (nonatomic, strong) NSString *UITargetAppPath; @property (nonatomic, strong) NSArray *skipTestIdentifiers; +@property (nonatomic, strong) NSDictionary *standardizedSwiftTestNames; @property (nonatomic, strong) NSNumber *estimatedExecutionTime; @property (nonatomic, strong) NSDictionary *dependencies; diff --git a/bp/src/BPXCTestFile.m b/bp/src/BPXCTestFile.m index 92d90ade..9c2d6fbf 100644 --- a/bp/src/BPXCTestFile.m +++ b/bp/src/BPXCTestFile.m @@ -50,6 +50,8 @@ + (instancetype)BPXCTestFileFromXCTestBundle:(NSString *)path NSArray* testsArray = [output componentsSeparatedByString:@"\n"]; NSMutableDictionary *testClassesDict = [[NSMutableDictionary alloc] init]; NSMutableArray *allClasses = [[NSMutableArray alloc] init]; + NSMutableDictionary *standardizedSwiftTestNames = [NSMutableDictionary dictionary]; + for (NSString *testName in testsArray) { NSArray *parts = [testName componentsSeparatedByString:@"."]; @@ -57,6 +59,9 @@ + (instancetype)BPXCTestFileFromXCTestBundle:(NSString *)path continue; } BPTestClass *testClass = testClassesDict[parts[1]]; + if ([testClass isEqual:@"AppConfigurationManagerTests"]) { + NSLog(@"HERE"); + } if (!testClass) { testClass = [[BPTestClass alloc] initWithName:parts[1]]; testClassesDict[parts[1]] = testClass; @@ -65,8 +70,19 @@ + (instancetype)BPXCTestFileFromXCTestBundle:(NSString *)path if (![parts[2] containsString:@"DISABLE"]) { NSString *trimmedTestName = [BPUtils removeSwiftArgumentsFromTestName:parts[2]]; [testClass addTestCase:[[BPTestCase alloc] initWithName:trimmedTestName]]; + /* + Logic tests, which pass test names as an argument rather than an XCTestConfig, require strict + naming syntax for swift test files, of the format `./`. + Note that this module name isn't necessarily the same as the bundle name, and that this is the only + time we have access to the original module name. So, we save it here for later. + */ + NSString *standardizedTestCaseName = [NSString stringWithFormat:@"%@/%@", testClass.name, trimmedTestName]; + NSString *standardizedTestName = [BPUtils formatSwiftTestForXCTest:standardizedTestCaseName withBundleName:parts[0]]; + + standardizedSwiftTestNames[standardizedTestCaseName] = standardizedTestName; } } + xcTestFile.standardizedSwiftTestNames = [standardizedSwiftTestNames copy]; cmd = [NSString stringWithFormat:objcNmCmdline, path]; output = [BPUtils runShell:cmd]; diff --git a/bp/src/Bluepill.m b/bp/src/Bluepill.m index f7558402..bea1a958 100644 --- a/bp/src/Bluepill.m +++ b/bp/src/Bluepill.m @@ -220,11 +220,11 @@ - (void)createContext { NSAssert(xctTestFile != nil, @"Failed to load testcases from: %@; Error: %@", context.config.testBundlePath, [error localizedDescription]); context.config.allTestCases = [[NSArray alloc] initWithArray: xctTestFile.allTestCases]; - // Right now, logic tests need to be able to make stronger assumptions around test exclusivity - // in the `allTests`, `testCasesToRun`, and `testCasesToSkip` lists. if (context.config.isLogicTestTarget) { - // For estimating how long this will take (and setting an appropriate timeout), we need to remove any skipped tests - // that aren't actually a part of this bundle. + // XCTest is stricter about how swift test names are formatted + context.config.standardizedSwiftTestNames = xctTestFile.standardizedSwiftTestNames; + // For estimating how long this will take (and setting an appropriate timeout), we need to + // remove any skipped tests that aren't actually a part of this bundle. NSMutableSet *allTests = [NSMutableSet setWithArray:context.config.allTestCases]; NSMutableSet *allSkippedTests = [NSMutableSet setWithArray:self.config.testCasesToSkip]; [allSkippedTests intersectSet:allTests]; @@ -409,11 +409,21 @@ - (void)uninstallApplicationWithContext:(BPExecutionContext *)context { } - (void)executeLogicTestsWithContext:(BPExecutionContext *)context { + // First we need to make sure the archs are consistent between the xctest executable + the test bundle. + NSString *originalXCTestPath = [[NSString alloc] initWithFormat: + @"%@/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Agents/xctest", + context.config.xcodePath]; + NSString *newXCTestPath = [BPUtils lipoExecutableAtPath:originalXCTestPath withContext:context]; + if (newXCTestPath) { + context.config.dyldFrameworkPath = [BPUtils correctedDYLDFrameworkPathFromBinary:originalXCTestPath]; + } + context.config.xctestBinaryPath = newXCTestPath ?: originalXCTestPath; + // We get two callbacks after trying to spawn an execution. They happen when: // 1) Immediately after the process is spawned. // 2) Once the process completes. // This method sets up those two handlers, and then passes them to the runner. - + // 1) Handle process spawn NSString *spawnStepName = SPAWN_LOGIC_TEST(context.attemptNumber); NSString *executeTestsStepName = EXECUTE_LOGIC_TEST(context.attemptNumber); @@ -469,7 +479,14 @@ - (void)executeLogicTestsWithContext:(BPExecutionContext *)context { completionHandler.onError = ^(NSError *error) { [[BPStats sharedStats] endTimer:executeTestsStepName withResult:@"ERROR"]; [BPUtils printInfo:ERROR withString:@"Spawned logic test execution failed: %@", [error localizedDescription]]; - NEXT([__self deleteSimulatorWithContext:context andStatus:BPExitStatusAppCrashed]); + + BPExitStatus exitStatus = BPExitStatusAppCrashed; + BOOL didhanderTimeout = error.domain == BPErrorDomain && error.code == BPHandler.timeoutErrorCode; + BOOL wasprocessKilledAfterTimeout = error.domain == BPErrorDomain && error.code == SIGKILL; + if (didhanderTimeout || wasprocessKilledAfterTimeout) { + exitStatus = BPExitStatusTestTimeout; + } + NEXT([__self deleteSimulatorWithContext:context andStatus:exitStatus]); }; completionHandler.onTimeout = ^{ @@ -817,6 +834,7 @@ - (NSString *)debugDescription { } #pragma mark - BPTestBundleConnectionDelegate + - (void)_XCT_launchProcessWithPath:(NSString *)path bundleID:(NSString *)bundleID arguments:(NSArray *)arguments environmentVariables:(NSDictionary *)environment { self.context.isTestRunnerContext = YES; [self installApplicationWithContext:self.context]; diff --git a/bp/src/SimulatorHelper.m b/bp/src/SimulatorHelper.m index a48d78f2..0ad103c9 100644 --- a/bp/src/SimulatorHelper.m +++ b/bp/src/SimulatorHelper.m @@ -197,10 +197,9 @@ + (NSString *)bundleIdForPath:(NSString *)path { return nil; } NSMutableArray *formattedTests = [NSMutableArray array]; - NSString *bundleName = [[config.testBundlePath lastPathComponent] stringByDeletingPathExtension]; for (NSString *testName in tests) { if ([BPUtils isTestSwiftTest:testName]) { - [formattedTests addObject:[BPUtils formatSwiftTestForXCTest:testName withBundleName:bundleName]]; + [formattedTests addObject:config.standardizedSwiftTestNames[testName]]; } else { [formattedTests addObject:testName]; } diff --git a/bp/tests/BPTestHelper.h b/bp/tests/BPTestHelper.h index 777f1d36..d14f1401 100644 --- a/bp/tests/BPTestHelper.h +++ b/bp/tests/BPTestHelper.h @@ -24,6 +24,12 @@ // This particular bundle will only have passing tests to make certain functionalities easier to test. + (NSString *)passingLogicTestBundlePath; +// A pre-built test bundle that will always be in x86_64 ++ (NSString *)logicTestBundlePath_x86_64; + +// A pre-built test bundle that will always be in arm64 ++ (NSString *)logicTestBundlePath_arm64; + // Return the path to the sample app's xctest with new test cases + (NSString *)sampleAppNewTestsBundlePath; diff --git a/bp/tests/BPTestHelper.m b/bp/tests/BPTestHelper.m index 7e2c9616..c347084e 100644 --- a/bp/tests/BPTestHelper.m +++ b/bp/tests/BPTestHelper.m @@ -31,6 +31,14 @@ + (NSString *)passingLogicTestBundlePath { return [[self sampleAppPath] stringByAppendingPathComponent:@"/../BPPassingLogicTests.xctest"]; } ++ (NSString *)logicTestBundlePath_x86_64 { + return [[NSBundle bundleWithIdentifier:@"LI.BluepillRunnerTests"] pathForResource:@"BPLogicTestFixture_x86_64" ofType:@"xctest"]; +} + ++ (NSString *)logicTestBundlePath_arm64 { + return [[NSBundle bundleWithIdentifier:@"LI.BluepillRunnerTests"] pathForResource:@"BPLogicTestFixture_arm64" ofType:@"xctest"]; +} + // Return the path to the sample app's xctest with new test cases + (NSString *)sampleAppNewTestsBundlePath { return [[self sampleAppPath] stringByAppendingString:@"/PlugIns/BPSampleAppNewTests.xctest"]; diff --git a/bp/tests/BluepillHostedTests.m b/bp/tests/BluepillHostedTests.m index 85988d14..f842b6a6 100644 --- a/bp/tests/BluepillHostedTests.m +++ b/bp/tests/BluepillHostedTests.m @@ -194,7 +194,7 @@ - (void)testRetryOnlyFailures { - (void)testRunWithPassingTestsSet { NSString *testBundlePath = [BPTestHelper sampleAppBalancingTestsBundlePath]; self.config.testBundlePath = testBundlePath; - self.config.testCasesToSkip = @[@"BPSampleAppTests/testCase000"]; + self.config.testCasesToSkip = @[@"BPSampleAppTests/testCase000",@"BPSampleAppSwiftTests/testZPass()"]; BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; XCTAssert(exitCode == BPExitStatusAllTestsPassed); diff --git a/bp/tests/Unhosted Tests/BluepillUnhostedTests.m b/bp/tests/Unhosted Tests/BluepillUnhostedTests.m index af056bf4..f185e2bc 100644 --- a/bp/tests/Unhosted Tests/BluepillUnhostedTests.m +++ b/bp/tests/Unhosted Tests/BluepillUnhostedTests.m @@ -42,8 +42,12 @@ - (void)setUp { #pragma mark - Passing Tests - (void)testSinglePassingLogicTests { +// NSString *testBundlePath = @"/Users/lthrockm/Library/Developer/Xcode/DerivedData/Voyager-atlqmombeaxwfzfctrysmxtzoywo/Build/Products/Debug-iphonesimulator/Infra-Unit-Tests.xctest"; +// self.config.testBundlePath = testBundlePath; +// self.config.testCasesToRun = @[@"AppConfigurationManagerTests/testPassingLogicTest()"]; + self.config.testCasesToRun = @[@"BPLogicTests/testPassingLogicTest1"]; - BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + BPExitStatus exitCode = [[[Bluepill alloc] initWithConfiguration:self.config] run]; [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusAllTestsPassed]; } @@ -106,7 +110,7 @@ - (void)testCrashingExecutionLogicTest { */ - (void)testStuckLogicTest { self.config.testCasesToRun = @[@"BPLogicTests/testStuckLogicTest"]; - BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + BPExitStatus exitCode = [[[Bluepill alloc] initWithConfiguration:self.config] run]; [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusTestTimeout]; } @@ -121,7 +125,7 @@ - (void)testHangingLogicTest { self.config.testCaseTimeout = @3; self.config.testCasesToRun = @[@"BPLogicTests/testSlowLogicTest"]; - BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; + BPExitStatus exitCode = [[[Bluepill alloc] initWithConfiguration:self.config] run]; [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusTestTimeout]; } diff --git a/bptestrunner/bluepill_batch_test.bzl b/bptestrunner/bluepill_batch_test.bzl index 3523d769..85a92618 100644 --- a/bptestrunner/bluepill_batch_test.bzl +++ b/bptestrunner/bluepill_batch_test.bzl @@ -28,15 +28,21 @@ def _bluepill_batch_test_impl(ctx): runfiles.append(test_host) #test_plan - test_plan = struct( - if test_host != None: + if test_host: + test_plan = struct( test_host = test_host.basename.split( "." + test_host.extension, )[0] + ".app", - environment = test_env, - arguments = test_env, - test_bundle_path = bundle_info.bundle_name + bundle_info.bundle_extension, - ) + environment = test_env, + arguments = test_env, + test_bundle_path = bundle_info.bundle_name + bundle_info.bundle_extension, + ) + else: + test_plan = struct( + environment = test_env, + arguments = test_env, + test_bundle_path = bundle_info.bundle_name + bundle_info.bundle_extension, + ) test_plans[test_target.label.name] = test_plan # Write test plan json. @@ -46,16 +52,20 @@ def _bluepill_batch_test_impl(ctx): content = struct(tests = test_plans).to_json(), ) runfiles.append(test_plan_file) + print("lthrockm debug - " + str(struct(tests = test_plans).to_json())) # Write the shell script. substitutions = { "test_bundle_paths": " ".join(test_bundle_paths), - "test_host_paths": " ".join(test_host_paths), "bp_test_plan": test_plan_file.short_path, "bp_path": ctx.executable._bp_exec.short_path, "bluepill_path": ctx.executable._bluepill_exec.short_path, "target_name": ctx.attr.name, } + if len(test_host_paths) > 0: + substitutions["test_host_paths"] = " ".join(test_host_paths) + + if ctx.attr.config_file: runfiles.append(ctx.file.config_file) substitutions["bp_config_file"] = ctx.file.config_file.path diff --git a/bptestrunner/bluepill_batch_test_runner.template.sh b/bptestrunner/bluepill_batch_test_runner.template.sh index 3bdc0d7f..769053d9 100644 --- a/bptestrunner/bluepill_batch_test_runner.template.sh +++ b/bptestrunner/bluepill_batch_test_runner.template.sh @@ -24,6 +24,7 @@ rm -rf $BP_WORKING_FOLDER mkdir $BP_WORKING_FOLDER # Extract test bundles + for test_bundle in ${TEST_BUNDLE_PATHS[@]}; do if [[ $test_bundle == *.zip ]]; then tar -C $BP_WORKING_FOLDER -xzf $test_bundle @@ -37,19 +38,21 @@ for test_bundle in ${TEST_BUNDLE_PATHS[@]}; do done # Clone and extract test hosts -for test_host in ${TEST_HOST_PATHS[@]}; do - if [[ "$test_host" == *.ipa ]]; then - TEST_HOST_NAME=$(basename_without_extension "${test_host}") - unzip -qq -d "$BP_WORKING_FOLDER" "$test_host" - cp -cr "${BP_WORKING_FOLDER}/Payload/${TEST_HOST_NAME}.app" ${BP_WORKING_FOLDER} - elif [[ $test_host == *.app ]]; then - cp -cr $test_host $BP_WORKING_FOLDER - chmod -R ug+w "$BP_WORKING_FOLDER/$(basename "$test_host")" - else - echo "$test_host is not an ipa file or app bundle." - exit 1 - fi -done +if [[-n "$TEST_HOST_PATHS"]] then + for test_host in ${TEST_HOST_PATHS[@]}; do + if [[ "$test_host" == *.ipa ]]; then + TEST_HOST_NAME=$(basename_without_extension "${test_host}") + unzip -qq -d "$BP_WORKING_FOLDER" "$test_host" + cp -cr "${BP_WORKING_FOLDER}/Payload/${TEST_HOST_NAME}.app" ${BP_WORKING_FOLDER} + elif [[ $test_host == *.app ]]; then + cp -cr $test_host $BP_WORKING_FOLDER + chmod -R ug+w "$BP_WORKING_FOLDER/$(basename "$test_host")" + else + echo "$test_host is not an ipa file or app bundle." + exit 1 + fi + done +fi # Copy config file to bp working folder if [ -f "$BP_CONFIG_FILE" ]; then From 7acb1c9deeea0b8c122846b3a1211613946d826b Mon Sep 17 00:00:00 2001 From: Lucas Statler Date: Wed, 28 Jun 2023 15:59:43 -0400 Subject: [PATCH 05/11] Basic cleanup --- BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj | 1 - bluepill/src/BPPacker.m | 4 ---- bp/bp.xcodeproj/project.pbxproj | 8 ++------ bp/src/BPConstants.h | 2 +- bp/src/BPSimulator.m | 2 -- bp/src/BPUtils.m | 1 - bp/src/BPXCTestFile.m | 4 ---- bp/src/Bluepill.m | 4 ++-- bp/tests/BluepillHostedTests.m | 2 +- bp/tests/Unhosted Tests/BluepillUnhostedTests.m | 4 ---- bptestrunner/bluepill_batch_test.bzl | 2 +- 11 files changed, 7 insertions(+), 27 deletions(-) diff --git a/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj b/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj index 567ba61e..1e6659d8 100644 --- a/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj +++ b/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj @@ -1031,7 +1031,6 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 14.4; MTL_ENABLE_DEBUG_INFO = NO; - ONLY_ACTIVE_ARCH = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; diff --git a/bluepill/src/BPPacker.m b/bluepill/src/BPPacker.m index f97846cf..bc363880 100644 --- a/bluepill/src/BPPacker.m +++ b/bluepill/src/BPPacker.m @@ -142,11 +142,7 @@ @implementation BPPacker NSMutableArray *bundles = [[NSMutableArray alloc] init]; - NSInteger counter = 0; for (BPXCTestFile *xctFile in xcTestFiles) { - counter += 1; - [BPUtils printInfo:INFO withString:@"%@: Looping through... counter = %@", xctFile.testBundlePath, @(counter)]; - NSArray *bundleTestsToRun = [[testsToRunByFilePath[xctFile.testBundlePath] allObjects] sortedArrayUsingSelector:@selector(compare:)]; NSNumber *estimatedBundleTime = testEstimatesByFilePath[xctFile.testBundlePath]; // If the bundle is small enough, do not split. Also do not split if the bundle is in no_split list. diff --git a/bp/bp.xcodeproj/project.pbxproj b/bp/bp.xcodeproj/project.pbxproj index 0918eff2..d8c461cc 100644 --- a/bp/bp.xcodeproj/project.pbxproj +++ b/bp/bp.xcodeproj/project.pbxproj @@ -1063,7 +1063,6 @@ 7A79018A1D5CB679004D4325 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD)"; DEPLOYMENT_LOCATION = NO; "DEVELOPER_PRIVATE_FRAMEWORKS_DIR[arch=*]" = /Library/Developer/PrivateFrameworks/; FRAMEWORK_SEARCH_PATHS = ( @@ -1103,7 +1102,6 @@ 7A79018B1D5CB679004D4325 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD)"; DEPLOYMENT_LOCATION = NO; "DEVELOPER_PRIVATE_FRAMEWORKS_DIR[arch=*]" = /Library/Developer/PrivateFrameworks/; FRAMEWORK_SEARCH_PATHS = ( @@ -1143,7 +1141,7 @@ B368E56B213F8D2F00B4DEA3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD)"; + ARCHS = x86_64; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -1180,7 +1178,7 @@ B368E56C213F8D2F00B4DEA3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD)"; + ARCHS = x86_64; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_ENABLE_OBJC_WEAK = YES; @@ -1217,7 +1215,6 @@ BAB24F6D1DB5DB2300867756 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD)"; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 57Y47U492U; HEADER_SEARCH_PATHS = ( @@ -1250,7 +1247,6 @@ BAB24F6E1DB5DB2300867756 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD)"; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 57Y47U492U; HEADER_SEARCH_PATHS = ( diff --git a/bp/src/BPConstants.h b/bp/src/BPConstants.h index 21c1d5ee..c5963e2a 100644 --- a/bp/src/BPConstants.h +++ b/bp/src/BPConstants.h @@ -10,7 +10,7 @@ #import #pragma mark - Version Constants -#define BP_DEFAULT_XCODE_VERSION "14.1" +#define BP_DEFAULT_XCODE_VERSION "14.0" #define BP_DEFAULT_RUNTIME "iOS 16.0" #define BP_DEFAULT_BASE_SDK "16.0" diff --git a/bp/src/BPSimulator.m b/bp/src/BPSimulator.m index 58295e8f..ad9c8270 100644 --- a/bp/src/BPSimulator.m +++ b/bp/src/BPSimulator.m @@ -509,8 +509,6 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser @"standalone": @(1), @"stdout": stdoutFileDescriptor, @"stderr": stdoutFileDescriptor, -// @"stdout": @(1), -// @"stderr": @(2), }; // Set up monitor diff --git a/bp/src/BPUtils.m b/bp/src/BPUtils.m index dc3f72af..38aca2e2 100644 --- a/bp/src/BPUtils.m +++ b/bp/src/BPUtils.m @@ -438,7 +438,6 @@ + (NSDictionary *)loadSimpleJsonFile:(NSString *)filePath [bundleTestsToRun minusSet:[[NSSet alloc] initWithArray:config.testCasesToSkip]]; } [BPUtils printInfo:INFO withString:@"Bundle: %@; All Tests count: %lu; bundleTestsToRun count: %lu", xctFile.testBundlePath, (unsigned long)[xctFile.allTestCases count], (unsigned long)[bundleTestsToRun count]]; - [BPUtils printInfo:INFO withString:@"Bundle: %@; All Tests cases: %@", xctFile.testBundlePath, xctFile.allTestCases]; if (bundleTestsToRun.count > 0) { testsToRunByFilePath[xctFile.testBundlePath] = bundleTestsToRun; } diff --git a/bp/src/BPXCTestFile.m b/bp/src/BPXCTestFile.m index 9c2d6fbf..08d7dd7c 100644 --- a/bp/src/BPXCTestFile.m +++ b/bp/src/BPXCTestFile.m @@ -51,7 +51,6 @@ + (instancetype)BPXCTestFileFromXCTestBundle:(NSString *)path NSMutableDictionary *testClassesDict = [[NSMutableDictionary alloc] init]; NSMutableArray *allClasses = [[NSMutableArray alloc] init]; NSMutableDictionary *standardizedSwiftTestNames = [NSMutableDictionary dictionary]; - for (NSString *testName in testsArray) { NSArray *parts = [testName componentsSeparatedByString:@"."]; @@ -59,9 +58,6 @@ + (instancetype)BPXCTestFileFromXCTestBundle:(NSString *)path continue; } BPTestClass *testClass = testClassesDict[parts[1]]; - if ([testClass isEqual:@"AppConfigurationManagerTests"]) { - NSLog(@"HERE"); - } if (!testClass) { testClass = [[BPTestClass alloc] initWithName:parts[1]]; testClassesDict[parts[1]] = testClass; diff --git a/bp/src/Bluepill.m b/bp/src/Bluepill.m index bea1a958..e16c2df6 100644 --- a/bp/src/Bluepill.m +++ b/bp/src/Bluepill.m @@ -482,8 +482,8 @@ - (void)executeLogicTestsWithContext:(BPExecutionContext *)context { BPExitStatus exitStatus = BPExitStatusAppCrashed; BOOL didhanderTimeout = error.domain == BPErrorDomain && error.code == BPHandler.timeoutErrorCode; - BOOL wasprocessKilledAfterTimeout = error.domain == BPErrorDomain && error.code == SIGKILL; - if (didhanderTimeout || wasprocessKilledAfterTimeout) { + BOOL wasProcessKilledAfterTimeout = error.domain == BPErrorDomain && error.code == SIGKILL; + if (didhanderTimeout || wasProcessKilledAfterTimeout) { exitStatus = BPExitStatusTestTimeout; } NEXT([__self deleteSimulatorWithContext:context andStatus:exitStatus]); diff --git a/bp/tests/BluepillHostedTests.m b/bp/tests/BluepillHostedTests.m index f842b6a6..85988d14 100644 --- a/bp/tests/BluepillHostedTests.m +++ b/bp/tests/BluepillHostedTests.m @@ -194,7 +194,7 @@ - (void)testRetryOnlyFailures { - (void)testRunWithPassingTestsSet { NSString *testBundlePath = [BPTestHelper sampleAppBalancingTestsBundlePath]; self.config.testBundlePath = testBundlePath; - self.config.testCasesToSkip = @[@"BPSampleAppTests/testCase000",@"BPSampleAppSwiftTests/testZPass()"]; + self.config.testCasesToSkip = @[@"BPSampleAppTests/testCase000"]; BPExitStatus exitCode = [[[Bluepill alloc ] initWithConfiguration:self.config] run]; XCTAssert(exitCode == BPExitStatusAllTestsPassed); diff --git a/bp/tests/Unhosted Tests/BluepillUnhostedTests.m b/bp/tests/Unhosted Tests/BluepillUnhostedTests.m index f185e2bc..764665d5 100644 --- a/bp/tests/Unhosted Tests/BluepillUnhostedTests.m +++ b/bp/tests/Unhosted Tests/BluepillUnhostedTests.m @@ -42,10 +42,6 @@ - (void)setUp { #pragma mark - Passing Tests - (void)testSinglePassingLogicTests { -// NSString *testBundlePath = @"/Users/lthrockm/Library/Developer/Xcode/DerivedData/Voyager-atlqmombeaxwfzfctrysmxtzoywo/Build/Products/Debug-iphonesimulator/Infra-Unit-Tests.xctest"; -// self.config.testBundlePath = testBundlePath; -// self.config.testCasesToRun = @[@"AppConfigurationManagerTests/testPassingLogicTest()"]; - self.config.testCasesToRun = @[@"BPLogicTests/testPassingLogicTest1"]; BPExitStatus exitCode = [[[Bluepill alloc] initWithConfiguration:self.config] run]; [BPTestUtils assertExitStatus:exitCode matchesExpected:BPExitStatusAllTestsPassed]; diff --git a/bptestrunner/bluepill_batch_test.bzl b/bptestrunner/bluepill_batch_test.bzl index 85a92618..90b70d84 100644 --- a/bptestrunner/bluepill_batch_test.bzl +++ b/bptestrunner/bluepill_batch_test.bzl @@ -27,6 +27,7 @@ def _bluepill_batch_test_impl(ctx): test_host_paths.append("\"{}\"".format(test_host.short_path)) runfiles.append(test_host) + #test_plan if test_host: test_plan = struct( @@ -52,7 +53,6 @@ def _bluepill_batch_test_impl(ctx): content = struct(tests = test_plans).to_json(), ) runfiles.append(test_plan_file) - print("lthrockm debug - " + str(struct(tests = test_plans).to_json())) # Write the shell script. substitutions = { From 743c5988c58f52221273387d0bb36f4958047b3a Mon Sep 17 00:00:00 2001 From: Lucas Statler Date: Thu, 13 Jul 2023 12:00:04 -0700 Subject: [PATCH 06/11] Fix swift tests in Rosetta --- .../BPSampleApp.xcodeproj/project.pbxproj | 2 + bluepill/bluepill.xcodeproj/project.pbxproj | 4 + bluepill/src/BPPacker.m | 6 +- bluepill/tests/BPIntegrationTests.m | 21 ++++ .../BPPassingLogicTests | Bin 0 -> 358432 bytes .../Info.plist | Bin 0 -> 737 bytes .../_CodeSignature/CodeResources | 101 ++++++++++++++++++ bp/src/BPConstants.h | 2 +- bp/src/BPSimulator.m | 6 ++ bp/src/BPTreeParser.m | 3 +- bp/src/BPUtils.m | 13 +++ bp/tests/BPTestHelper.h | 7 +- bp/tests/BPTestHelper.m | 4 + .../bluepill_batch_test_runner.template.sh | 4 +- 14 files changed, 166 insertions(+), 7 deletions(-) create mode 100755 bluepill/tests/Resource Files/BPLogicTestFixture_swift_x86_64.xctest/BPPassingLogicTests create mode 100644 bluepill/tests/Resource Files/BPLogicTestFixture_swift_x86_64.xctest/Info.plist create mode 100644 bluepill/tests/Resource Files/BPLogicTestFixture_swift_x86_64.xctest/_CodeSignature/CodeResources diff --git a/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj b/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj index 1e6659d8..147225b4 100644 --- a/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj +++ b/BPSampleApp/BPSampleApp.xcodeproj/project.pbxproj @@ -964,6 +964,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -1021,6 +1022,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; diff --git a/bluepill/bluepill.xcodeproj/project.pbxproj b/bluepill/bluepill.xcodeproj/project.pbxproj index f26a9e2c..ecda15fc 100644 --- a/bluepill/bluepill.xcodeproj/project.pbxproj +++ b/bluepill/bluepill.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ E49235FF22EA847700395D98 /* times.json in Resources */ = {isa = PBXBuildFile; fileRef = E49235FE22EA847700395D98 /* times.json */; }; FBA69FA72A45F889003BADBF /* BPLogicTestFixture_arm64.xctest in Resources */ = {isa = PBXBuildFile; fileRef = FBA69FA52A45F889003BADBF /* BPLogicTestFixture_arm64.xctest */; }; FBA69FA82A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest in Resources */ = {isa = PBXBuildFile; fileRef = FBA69FA62A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest */; }; + FBF6B19D2A5F272500D7A64B /* BPLogicTestFixture_swift_x86_64.xctest in Resources */ = {isa = PBXBuildFile; fileRef = FBF6B19C2A5F272500D7A64B /* BPLogicTestFixture_swift_x86_64.xctest */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -89,6 +90,7 @@ E49235FE22EA847700395D98 /* times.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = times.json; sourceTree = ""; }; FBA69FA52A45F889003BADBF /* BPLogicTestFixture_arm64.xctest */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = BPLogicTestFixture_arm64.xctest; sourceTree = ""; }; FBA69FA62A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = BPLogicTestFixture_x86_64.xctest; sourceTree = ""; }; + FBF6B19C2A5F272500D7A64B /* BPLogicTestFixture_swift_x86_64.xctest */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = BPLogicTestFixture_swift_x86_64.xctest; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -148,6 +150,7 @@ BA9C2DB01DD67B66007CB967 /* testScheme.xcscheme */, FBA69FA52A45F889003BADBF /* BPLogicTestFixture_arm64.xctest */, FBA69FA62A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest */, + FBF6B19C2A5F272500D7A64B /* BPLogicTestFixture_swift_x86_64.xctest */, BA9C2DAE1DD674AD007CB967 /* BPSampleAppTests.xctest */, C415174C273AE3CE00646740 /* Expected-TEST-FinalReport-for-invalid-xml.xml */, ); @@ -282,6 +285,7 @@ BA9C2DB11DD67B66007CB967 /* testScheme.xcscheme in Resources */, BA9C2DAF1DD674AD007CB967 /* BPSampleAppTests.xctest in Resources */, FBA69FA82A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest in Resources */, + FBF6B19D2A5F272500D7A64B /* BPLogicTestFixture_swift_x86_64.xctest in Resources */, E49235FF22EA847700395D98 /* times.json in Resources */, C415174F273AEAC400646740 /* simulator in Resources */, 0173521323679E87008BFA4E /* TEST-FinalReport.xml in Resources */, diff --git a/bluepill/src/BPPacker.m b/bluepill/src/BPPacker.m index bc363880..ee10fabc 100644 --- a/bluepill/src/BPPacker.m +++ b/bluepill/src/BPPacker.m @@ -166,7 +166,11 @@ @implementation BPPacker i++; } // Make a bundle out of current xctFile - BPXCTestFile *bundle = [self makeBundle:xctFile withTests:bundleTestsToRun startAt:startIndex numTests:(i-startIndex) estimatedTime:[NSNumber numberWithDouble:splitExecTime]]; + NSInteger numTests = i - startIndex; + if (numTests <= 0) { + break; + } + BPXCTestFile *bundle = [self makeBundle:xctFile withTests:bundleTestsToRun startAt:startIndex numTests:numTests estimatedTime:[NSNumber numberWithDouble:splitExecTime]]; [bundles addObject:bundle]; } } diff --git a/bluepill/tests/BPIntegrationTests.m b/bluepill/tests/BPIntegrationTests.m index a2d24250..4a1954c7 100644 --- a/bluepill/tests/BPIntegrationTests.m +++ b/bluepill/tests/BPIntegrationTests.m @@ -78,6 +78,27 @@ - (void)testArchitecture_x86_64 { XCTAssert([runner busySwimlaneCount] == 0); } +- (void)testArchitectureWithSwiftTests_x86_64 { + NSString *bundlePath = BPTestHelper.logicTestBundlePath_swift_x86_64; + + BPConfiguration *config = [BPTestUtils makeUnhostedTestConfiguration]; + config.stuckTimeout = @(2); + config.testCaseTimeout = @(10); + // Test multiple test bundles, while skipping any failing tests so that we + // can still validate that we get a success code.. + config.testBundlePath = bundlePath; + + NSString *bpPath = [BPTestHelper bpExecutablePath]; + BPRunner *runner = [BPRunner BPRunnerWithConfig:config withBpPath:bpPath]; + NSError *error; + BPApp *app = [BPApp appWithConfig:config withError:&error]; + + XCTAssert(runner != nil); + int rc = [runner runWithBPXCTestFiles:app.testBundles]; + XCTAssert(rc == 0, @"Wanted 0, got %d", rc); + XCTAssert([runner busySwimlaneCount] == 0); +} + - (void)testArchitecture_arm64 { NSString *bundlePath = BPTestHelper.logicTestBundlePath_arm64; diff --git a/bluepill/tests/Resource Files/BPLogicTestFixture_swift_x86_64.xctest/BPPassingLogicTests b/bluepill/tests/Resource Files/BPLogicTestFixture_swift_x86_64.xctest/BPPassingLogicTests new file mode 100755 index 0000000000000000000000000000000000000000..9547ccde0815cd8623045200c36c868e062bfdf5 GIT binary patch literal 358432 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
}nrxnuG&^qGWZvNfJ#@>Ho;Om-nZnUJ-* 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 literal 0 HcmV?d00001 diff --git a/bluepill/tests/Resource Files/BPLogicTestFixture_swift_x86_64.xctest/Info.plist b/bluepill/tests/Resource Files/BPLogicTestFixture_swift_x86_64.xctest/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..a54d7fbf444cc94e8b64479792561753e6370d27 GIT binary patch literal 737 zcmZ8d%Wl&^6rDS~3KZhjh0;PFlt<|T%MMYC!~)@(NP)C9wNs#~VojV$GV0i4*-k~^ z2iWleth!|b5?g+NEkDo&8x{zDfr)D?72e!?&trVgZAF|VsjeR>Q-DvMK6Ccm*!c?= z$1hzjDHD_G)Rn8#*Jh??uiuzoSiHHkcG;Q0Qq#Y3liKmnbXKvcH z+;tH|j1M;}S#BZC5;sTl@ir49VB~kK=b8Go<;r#_d}+C_e9@+RdCkwKY_O08{b0+< z&CNzlUXuB=N&?0QjdHpAu)Iy>HC5BLNs`Dsb3!#Np(**y_bNB&917V!(iQ7J_yxb=4@MZ{Wqcn$#unbi9`4|M9H8vOa9q(! IUh{&*KgpTm^#A|> literal 0 HcmV?d00001 diff --git a/bluepill/tests/Resource Files/BPLogicTestFixture_swift_x86_64.xctest/_CodeSignature/CodeResources b/bluepill/tests/Resource Files/BPLogicTestFixture_swift_x86_64.xctest/_CodeSignature/CodeResources new file mode 100644 index 00000000..7c6131f6 --- /dev/null +++ b/bluepill/tests/Resource Files/BPLogicTestFixture_swift_x86_64.xctest/_CodeSignature/CodeResources @@ -0,0 +1,101 @@ + + + + + files + + Info.plist + + 7n39r9N7BbD0yWNlG44KMr5a3U0= + + + files2 + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/bp/src/BPConstants.h b/bp/src/BPConstants.h index c5963e2a..21c1d5ee 100644 --- a/bp/src/BPConstants.h +++ b/bp/src/BPConstants.h @@ -10,7 +10,7 @@ #import #pragma mark - Version Constants -#define BP_DEFAULT_XCODE_VERSION "14.0" +#define BP_DEFAULT_XCODE_VERSION "14.1" #define BP_DEFAULT_RUNTIME "iOS 16.0" #define BP_DEFAULT_BASE_SDK "16.0" diff --git a/bp/src/BPSimulator.m b/bp/src/BPSimulator.m index ad9c8270..2e58190b 100644 --- a/bp/src/BPSimulator.m +++ b/bp/src/BPSimulator.m @@ -495,6 +495,9 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser } mutableCopy]; if (self.config.dyldFrameworkPath) { environment[@"DYLD_FRAMEWORK_PATH"] = self.config.dyldFrameworkPath; + environment[@"DYLD_LIBRARY_PATH"] = self.config.dyldFrameworkPath; + [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] set DYLD_FRAMEWORK_PATH = %@", environment[@"DYLD_FRAMEWORK_PATH"]]; + } [environment addEntriesFromDictionary:self.config.environmentVariables]; @@ -523,6 +526,9 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser NSData *chunk = [handle availableData]; [parser handleChunkData:chunk]; }; + + + [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] options = %@", options]; // To see more on how to debug the expected format/inputs of the options array, // see the in-depth documentation in SimDevice.h. diff --git a/bp/src/BPTreeParser.m b/bp/src/BPTreeParser.m index 1c84d6ec..1a0879b4 100644 --- a/bp/src/BPTreeParser.m +++ b/bp/src/BPTreeParser.m @@ -125,7 +125,8 @@ - (void)handleChunkData:(nonnull NSData *)chunk { } - (void)parseLine:(nullable NSString *)line { - [BPUtils printInfo:DEBUGINFO withString:@"[OUTPUT] %@", line]; +// [BPUtils printInfo:DEBUGINFO withString:@"[OUTPUT] %@", line]; + [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] %@", line]; [self onOutputReceived:line]; if (!line || ![line length]) { return; diff --git a/bp/src/BPUtils.m b/bp/src/BPUtils.m index 38aca2e2..07affe8c 100644 --- a/bp/src/BPUtils.m +++ b/bp/src/BPUtils.m @@ -514,6 +514,7 @@ + (NSString *)lipoExecutableAtPath:(NSString *)path withContext:(BPExecutionCont NSArray *executableArchitectures = [self availableArchitecturesForPath:path]; BOOL isUniversalExecutable = [executableArchitectures containsObject:self.x86_64] && [executableArchitectures containsObject:self.arm64]; if (!isUniversalExecutable) { + [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] !isUniversalExecutable"]; return nil; } // Now, get the test bundle's architecture. @@ -526,12 +527,14 @@ + (NSString *)lipoExecutableAtPath:(NSString *)path withContext:(BPExecutionCont // If the test bundle is a univeral binary, no need to lipo... xctest (regardless of the arch it's in) // should be able to handle it. if (isUniversalTestBundle) { + [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] !isUniversalTestBundle"]; return nil; } // If the test bundle's arch isn't supported by the sim, we're in an error state NSArray *simArchitectures = [self architecturesSupportedByDevice:context.runner.device]; if (![simArchitectures containsObject:testBundleArchitectures.firstObject]) { + [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] ![simArchitectures containsObject:testBundleArchitectures.firstObject]"]; return nil; } @@ -550,6 +553,10 @@ + (NSString *)lipoExecutableAtPath:(NSString *)path withContext:(BPExecutionCont // 1b) no-op. ... x86 will get handled automatically, and we have to fail if test bundle is arm64. // 1c) no-op. ... arm64 will get handled automatically, and we have to fail if test bundle is x86. BOOL isRosetta = [self.currentArchitecture isEqual:self.x86_64] && isUniversalExecutable; + [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] currentArchitecture = %@", self.currentArchitecture]; + [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] isUniversalExecutable = %@", @(isUniversalExecutable)]; + [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] testBundleArchitectures = %@", testBundleArchitectures]; + [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] isRosetta = %@", @(isRosetta)]; if (!isRosetta || ![testBundleArchitectures.firstObject isEqual:self.x86_64]) { return nil; } @@ -573,6 +580,9 @@ + (NSString *)lipoExecutableAtPath:(NSString *)path withContext:(BPExecutionCont + (NSString *)correctedDYLDFrameworkPathFromBinary:(NSString *)binaryPath { NSString *otoolCommand = [NSString stringWithFormat:@"/usr/bin/otool -l %@", binaryPath]; NSString *otoolInfo = [BPUtils runShell:otoolCommand]; + [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] otoolInfo = %@", otoolInfo]; + + // /usr/bin/otool -l /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Agents/xctest /** Example output looks something like this: @@ -613,6 +623,9 @@ + (NSString *)correctedDYLDFrameworkPathFromBinary:(NSString *)binaryPath { NSLog(@"Error creating regular expression: %@", error); } + + [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] dyldFrameworkPath = %@", [paths componentsJoinedByString:@":"]]; + return [paths componentsJoinedByString:@":"]; } diff --git a/bp/tests/BPTestHelper.h b/bp/tests/BPTestHelper.h index d14f1401..aa7ee7a7 100644 --- a/bp/tests/BPTestHelper.h +++ b/bp/tests/BPTestHelper.h @@ -17,16 +17,19 @@ // Return the path to the test plan json file. The json is packed into the app bundle as resource + (NSString *)testPlanPath; -// Return the path to logic tests, that are run unhosted rather than on the Sampple App +// Return the path to logic tests, that are run unhosted rather than on the Sample App + (NSString *)logicTestBundlePath; -// Return the path to logic tests, that are run unhosted rather than on the Sampple App +// Return the path to logic tests, that are run unhosted rather than on the Sample App // This particular bundle will only have passing tests to make certain functionalities easier to test. + (NSString *)passingLogicTestBundlePath; // A pre-built test bundle that will always be in x86_64 + (NSString *)logicTestBundlePath_x86_64; +// A pre-built test bundle containing swift tests that will always be in x86_64. ++ (NSString *)logicTestBundlePath_swift_x86_64; + // A pre-built test bundle that will always be in arm64 + (NSString *)logicTestBundlePath_arm64; diff --git a/bp/tests/BPTestHelper.m b/bp/tests/BPTestHelper.m index c347084e..d50da15c 100644 --- a/bp/tests/BPTestHelper.m +++ b/bp/tests/BPTestHelper.m @@ -35,6 +35,10 @@ + (NSString *)logicTestBundlePath_x86_64 { return [[NSBundle bundleWithIdentifier:@"LI.BluepillRunnerTests"] pathForResource:@"BPLogicTestFixture_x86_64" ofType:@"xctest"]; } ++ (NSString *)logicTestBundlePath_swift_x86_64 { + return [[NSBundle bundleWithIdentifier:@"LI.BluepillRunnerTests"] pathForResource:@"BPLogicTestFixture_swift_x86_64" ofType:@"xctest"]; +} + + (NSString *)logicTestBundlePath_arm64 { return [[NSBundle bundleWithIdentifier:@"LI.BluepillRunnerTests"] pathForResource:@"BPLogicTestFixture_arm64" ofType:@"xctest"]; } diff --git a/bptestrunner/bluepill_batch_test_runner.template.sh b/bptestrunner/bluepill_batch_test_runner.template.sh index 769053d9..21c770a2 100644 --- a/bptestrunner/bluepill_batch_test_runner.template.sh +++ b/bptestrunner/bluepill_batch_test_runner.template.sh @@ -37,8 +37,8 @@ for test_bundle in ${TEST_BUNDLE_PATHS[@]}; do fi done -# Clone and extract test hosts -if [[-n "$TEST_HOST_PATHS"]] then +# Clone and extract test hosts (won't be set for logic tests) +if [ ! -z ${test_host_paths+x} ]; then for test_host in ${TEST_HOST_PATHS[@]}; do if [[ "$test_host" == *.ipa ]]; then TEST_HOST_NAME=$(basename_without_extension "${test_host}") From 493058c5c15447aad70500993b3ab451aa3cdbc3 Mon Sep 17 00:00:00 2001 From: Lucas Statler Date: Fri, 14 Jul 2023 15:34:48 -0700 Subject: [PATCH 07/11] not currently injecting correctly --- BPXCTestWrapper/BPShimLogger.h | 25 + BPXCTestWrapper/BPShimLogger.m | 73 +++ BPXCTestWrapper/BPTestCaseInfo.h | 21 + BPXCTestWrapper/BPTestCaseInfo.m | 49 ++ .../BPXCTestWrapper.xcodeproj/project.pbxproj | 438 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcschemes/BPXCTestWrapper.xcscheme | 67 +++ BPXCTestWrapper/BPXCTestWrapperConstants.h | 20 + BPXCTestWrapper/BPXCTestWrapperConstants.m | 24 + BPXCTestWrapper/Info.plist | 26 ++ BPXCTestWrapper/Internal/BPLoggingUtils.h | 20 + BPXCTestWrapper/Internal/BPLoggingUtils.m | 20 + .../Internal/BPTestCaseInfo+Internal.h | 19 + BPXCTestWrapper/Internal/BPXCTestUtils.h | 18 + BPXCTestWrapper/Internal/BPXCTestUtils.m | 97 ++++ BPXCTestWrapper/Internal/main.m | 60 +++ BPXCTestWrapper/PrivateHeaders/CDStructures.h | 57 +++ .../Protocols/NSObject-Protocol.h | 33 ++ .../Protocols/XCTActivity-Protocol.h | 15 + .../Protocols/XCTIssueHandling-Protocol.h | 16 + .../Protocols/XCTWaiterDelegate-Protocol.h | 17 + .../Protocols/XCTestObservation-Protocol.h | 27 ++ .../_XCTestObservationInternal-Protocol.h | 17 + .../_XCTestObservationPrivate-Protocol.h | 17 + BPXCTestWrapper/PrivateHeaders/XCTest.h | 55 +++ BPXCTestWrapper/PrivateHeaders/XCTestCase.h | 189 ++++++++ BPXCTestWrapper/PrivateHeaders/XCTestLog.h | 59 +++ .../PrivateHeaders/XCTestObserver.h | 23 + BPXCTestWrapper/PrivateHeaders/XCTestRun.h | 73 +++ BPXCTestWrapper/PrivateHeaders/XCTestSuite.h | 70 +++ .../PrivateHeaders/XCTestSuiteRun.h | 33 ++ BPXCTestWrapper/XCTestLog+Shim.h | 12 + BPXCTestWrapper/XCTestLog+Shim.m | 111 +++++ Bluepill.xcworkspace/contents.xcworkspacedata | 3 + bluepill/bluepill.xcodeproj/project.pbxproj | 14 + .../xcshareddata/xcschemes/bluepill.xcscheme | 14 + bluepill/libBPXCTestWrapper.dylib | Bin 0 -> 78960 bytes .../Info.plist | Bin 743 -> 743 bytes .../Info.plist | Bin 737 -> 737 bytes .../Info.plist | Bin 744 -> 744 bytes bp/bp.xcodeproj/project.pbxproj | 34 +- .../xcshareddata/xcschemes/bp.xcscheme | 14 + bp/libBPXCTestWrapper.dylib | Bin 0 -> 78960 bytes bp/src/BPSimulator.m | 96 +++- bp/src/BPUtils.h | 2 + bp/src/BPUtils.m | 21 +- bp/src/SimulatorHelper.h | 18 + bp/src/SimulatorHelper.m | 35 +- 49 files changed, 2031 insertions(+), 36 deletions(-) create mode 100644 BPXCTestWrapper/BPShimLogger.h create mode 100644 BPXCTestWrapper/BPShimLogger.m create mode 100644 BPXCTestWrapper/BPTestCaseInfo.h create mode 100644 BPXCTestWrapper/BPTestCaseInfo.m create mode 100644 BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.pbxproj create mode 100644 BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 BPXCTestWrapper/BPXCTestWrapper.xcodeproj/xcshareddata/xcschemes/BPXCTestWrapper.xcscheme create mode 100644 BPXCTestWrapper/BPXCTestWrapperConstants.h create mode 100644 BPXCTestWrapper/BPXCTestWrapperConstants.m create mode 100644 BPXCTestWrapper/Info.plist create mode 100644 BPXCTestWrapper/Internal/BPLoggingUtils.h create mode 100644 BPXCTestWrapper/Internal/BPLoggingUtils.m create mode 100644 BPXCTestWrapper/Internal/BPTestCaseInfo+Internal.h create mode 100644 BPXCTestWrapper/Internal/BPXCTestUtils.h create mode 100644 BPXCTestWrapper/Internal/BPXCTestUtils.m create mode 100644 BPXCTestWrapper/Internal/main.m create mode 100644 BPXCTestWrapper/PrivateHeaders/CDStructures.h create mode 100644 BPXCTestWrapper/PrivateHeaders/Protocols/NSObject-Protocol.h create mode 100644 BPXCTestWrapper/PrivateHeaders/Protocols/XCTActivity-Protocol.h create mode 100644 BPXCTestWrapper/PrivateHeaders/Protocols/XCTIssueHandling-Protocol.h create mode 100644 BPXCTestWrapper/PrivateHeaders/Protocols/XCTWaiterDelegate-Protocol.h create mode 100644 BPXCTestWrapper/PrivateHeaders/Protocols/XCTestObservation-Protocol.h create mode 100644 BPXCTestWrapper/PrivateHeaders/Protocols/_XCTestObservationInternal-Protocol.h create mode 100644 BPXCTestWrapper/PrivateHeaders/Protocols/_XCTestObservationPrivate-Protocol.h create mode 100644 BPXCTestWrapper/PrivateHeaders/XCTest.h create mode 100644 BPXCTestWrapper/PrivateHeaders/XCTestCase.h create mode 100644 BPXCTestWrapper/PrivateHeaders/XCTestLog.h create mode 100644 BPXCTestWrapper/PrivateHeaders/XCTestObserver.h create mode 100644 BPXCTestWrapper/PrivateHeaders/XCTestRun.h create mode 100644 BPXCTestWrapper/PrivateHeaders/XCTestSuite.h create mode 100644 BPXCTestWrapper/PrivateHeaders/XCTestSuiteRun.h create mode 100644 BPXCTestWrapper/XCTestLog+Shim.h create mode 100644 BPXCTestWrapper/XCTestLog+Shim.m create mode 100755 bluepill/libBPXCTestWrapper.dylib create mode 100755 bp/libBPXCTestWrapper.dylib diff --git a/BPXCTestWrapper/BPShimLogger.h b/BPXCTestWrapper/BPShimLogger.h new file mode 100644 index 00000000..d8fe93bb --- /dev/null +++ b/BPXCTestWrapper/BPShimLogger.h @@ -0,0 +1,25 @@ +//// +//// BPShimLogger.h +//// Experiment +//// +//// Created by Lucas Throckmorton on 5/30/23. +//// +// +//#import +// +//@interface BPShimLogger : NSObject +// +//#pragma mark - Properties +// +//@property (nonatomic, copy, nonnull, class, readonly) NSString *outputPathEnvironmentKey; +//@property (nonatomic, strong, nonnull, class, readonly) BPShimLogger *defaultLogger; +// +//#pragma mark - Methods +// +//- (void)log:(nonnull NSString *)text; +//- (void)tearDown; +// +//#pragma mark - Unavailable +//- (nonnull instancetype)init NS_UNAVAILABLE; +// +//@end diff --git a/BPXCTestWrapper/BPShimLogger.m b/BPXCTestWrapper/BPShimLogger.m new file mode 100644 index 00000000..5a219e96 --- /dev/null +++ b/BPXCTestWrapper/BPShimLogger.m @@ -0,0 +1,73 @@ +//// +//// BPShimLogger.m +//// Experiment +//// +//// Created by Lucas Throckmorton on 5/30/23. +//// +// +//#import "BPShimLogger.h" +// +//#import +// +//@interface BPShimLogger () +// +//@property (nonatomic, strong, nonnull) NSString *outputPath; +//@property (nonatomic, strong, nonnull) NSFileHandle *fileHandle; +// +//@end +// +//@implementation BPShimLogger +// +//#pragma mark - init +// +//- (nonnull instancetype)initWithOutputPath:(NSString *)outputPath { +// if (self = [super init]) { +// _outputPath = outputPath; +// [self createLogFileAtPath:outputPath]; +// } +// return self; +//} +// +//#pragma mark - Properties +// +//+ (NSString *)outputPathEnvironmentKey { +// return @"BPSHIM_OUTPUT_PATH"; +//} +// +//+ (BPShimLogger *)defaultLogger { +// // Grab and then unset env variable to avoid impacting future processes +// NSString *outputPath = NSProcessInfo.processInfo.environment[BPShimLogger.outputPathEnvironmentKey]; +// unsetenv(BPShimLogger.outputPathEnvironmentKey.UTF8String); +// +// static BPShimLogger * instance; +// static dispatch_once_t onceToken; +// dispatch_once(&onceToken, ^{ +// instance = [[self alloc] initWithOutputPath:outputPath]; +// }); +// return instance; +//} +// +//#pragma mark - public +// +//- (void)log:(NSString *)text { +// // First to console +// NSLog(@"__EXPERIMENT: %@", text); +// // Then to file +// NSString *line = [NSString stringWithFormat:@"%@\n", text]; +// [self.fileHandle seekToEndOfFile]; +// [self.fileHandle writeData:[line dataUsingEncoding:NSUTF8StringEncoding]]; +//} +// +//- (void)tearDown { +// // TODO: output EOF stuff +// // TODO: close file handle if needed. +//} +// +//#pragma mark - private +// +//- (void)createLogFileAtPath:(NSString *)outputPath { +// [[NSFileManager defaultManager] createFileAtPath:outputPath contents:nil attributes:nil]; +// self.fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:outputPath]; +//} +// +//@end diff --git a/BPXCTestWrapper/BPTestCaseInfo.h b/BPXCTestWrapper/BPTestCaseInfo.h new file mode 100644 index 00000000..21aec073 --- /dev/null +++ b/BPXCTestWrapper/BPTestCaseInfo.h @@ -0,0 +1,21 @@ +// +// BPTestCaseInfo.h +// BPXCTestWrapper +// +// Created by Lucas Throckmorton on 6/8/23. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface BPTestCaseInfo : NSObject + +@property (nonatomic, copy, nonnull) NSString *moduleName; +@property (nonatomic, copy, nonnull) NSString *className; +@property (nonatomic, copy, nonnull) NSString *methodName; +@property (nonatomic, copy, nonnull) NSString *fullNamespace; + +@end + +NS_ASSUME_NONNULL_END diff --git a/BPXCTestWrapper/BPTestCaseInfo.m b/BPXCTestWrapper/BPTestCaseInfo.m new file mode 100644 index 00000000..c50858e9 --- /dev/null +++ b/BPXCTestWrapper/BPTestCaseInfo.m @@ -0,0 +1,49 @@ +// +// BPTestCaseInfo.m +// BPXCTestWrapper +// +// Created by Lucas Throckmorton on 6/8/23. +// + +#import "BPTestCaseInfo+Internal.h" + +@implementation BPTestCaseInfo + +- (instancetype)initWithModuleName:(NSString *)moduleName + className:(NSString *)className + methodName:(NSString *)methodName { + if (self = [super init]) { + _moduleName = moduleName; + _className = className; + _methodName = methodName; + _fullNamespace = [NSString stringWithFormat:@"%@.%@/%@", moduleName, className, methodName]; + } + return self; +} + + ++ (instancetype)infoFromTestCase:(XCTestCase *)testCase { + NSString *moduleName = @"LTHROCKM - TODO"; + NSString *className = NSStringFromClass(testCase.class); + NSString *methodName = [testCase respondsToSelector:@selector(languageAgnosticTestMethodName)] ? [testCase languageAgnosticTestMethodName] : NSStringFromSelector([testCase.invocation selector]); + return [[BPTestCaseInfo alloc] initWithModuleName: moduleName className:className methodName:methodName]; + +} + +#pragma mark - NSCoding + +- (void)encodeWithCoder:(nonnull NSCoder *)coder { + [coder encodeObject:self.moduleName forKey:@"moduleName"]; + [coder encodeObject:self.className forKey:@"className"]; + [coder encodeObject:self.methodName forKey:@"methodName"]; + [coder encodeObject:self.fullNamespace forKey:@"fullNamespace"]; +} + +- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder { + return [self initWithModuleName:[coder decodeObjectForKey:@"moduleName"] + className:[coder decodeObjectForKey:@"className"] + methodName:[coder decodeObjectForKey:@"methodName"]]; + +} + +@end diff --git a/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.pbxproj b/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.pbxproj new file mode 100644 index 00000000..a6d0e1c0 --- /dev/null +++ b/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.pbxproj @@ -0,0 +1,438 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + FB019EA02A25612E00F25BE5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FB019E9F2A25612E00F25BE5 /* main.m */; }; + FB21F0212A61FBF100682AC7 /* XCTestCase.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105ED2A2595B50056706D /* XCTestCase.h */; }; + FB21F0222A61FBF400682AC7 /* XCTestRun.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105EC2A2595B50056706D /* XCTestRun.h */; }; + FB21F0232A61FBF600682AC7 /* XCTest.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105EB2A2595B50056706D /* XCTest.h */; }; + FB21F0242A61FBF900682AC7 /* XCTestLog.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105EA2A2595B50056706D /* XCTestLog.h */; }; + FB21F0252A61FBFB00682AC7 /* CDStructures.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105E92A2595B50056706D /* CDStructures.h */; }; + FB21F0262A61FBFD00682AC7 /* XCTestSuite.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105E82A2595B50056706D /* XCTestSuite.h */; }; + FB21F0272A61FC0000682AC7 /* XCTestSuiteRun.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105E72A2595B50056706D /* XCTestSuiteRun.h */; }; + FB21F0282A61FC0200682AC7 /* XCTestObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105E62A2595B50056706D /* XCTestObserver.h */; }; + FB21F0292A61FC0500682AC7 /* XCTActivity-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105EF2A2595B50056706D /* XCTActivity-Protocol.h */; }; + FB21F02A2A61FC0800682AC7 /* XCTIssueHandling-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105F02A2595B50056706D /* XCTIssueHandling-Protocol.h */; }; + FB21F02B2A61FC0A00682AC7 /* XCTestObservation-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105F12A2595B50056706D /* XCTestObservation-Protocol.h */; }; + FB21F02C2A61FC0C00682AC7 /* _XCTestObservationPrivate-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105F22A2595B50056706D /* _XCTestObservationPrivate-Protocol.h */; }; + FB21F02D2A61FC0E00682AC7 /* XCTWaiterDelegate-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105F32A2595B50056706D /* XCTWaiterDelegate-Protocol.h */; }; + FB21F02E2A61FC1100682AC7 /* NSObject-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105F42A2595B50056706D /* NSObject-Protocol.h */; }; + FB21F02F2A61FC1300682AC7 /* _XCTestObservationInternal-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105F52A2595B50056706D /* _XCTestObservationInternal-Protocol.h */; }; + FB21F0322A61FC6B00682AC7 /* BPTestCaseInfo+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0302A61FC6B00682AC7 /* BPTestCaseInfo+Internal.h */; }; + FB317F8A2A259F5000A9FADF /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB317F892A259F5000A9FADF /* XCTest.framework */; }; + FB9F19262A3296A100C894D3 /* BPXCTestUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FB9F19242A3296A100C894D3 /* BPXCTestUtils.h */; }; + FB9F19272A3296A100C894D3 /* BPXCTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9F19252A3296A100C894D3 /* BPXCTestUtils.m */; }; + FB9F192A2A329AFB00C894D3 /* BPLoggingUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FB9F19282A329AFB00C894D3 /* BPLoggingUtils.h */; }; + FB9F192B2A329AFB00C894D3 /* BPLoggingUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9F19292A329AFB00C894D3 /* BPLoggingUtils.m */; }; + FB9F192E2A32A7D000C894D3 /* BPTestCaseInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = FB9F192C2A32A7D000C894D3 /* BPTestCaseInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FB9F192F2A32A7D000C894D3 /* BPTestCaseInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9F192D2A32A7D000C894D3 /* BPTestCaseInfo.m */; }; + FBF6B1A02A609B6D00D7A64B /* BPXCTestWrapperConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = FBF6B19E2A609B6D00D7A64B /* BPXCTestWrapperConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FBF6B1A12A609B6D00D7A64B /* BPXCTestWrapperConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = FBF6B19F2A609B6D00D7A64B /* BPXCTestWrapperConstants.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + FB0105E62A2595B50056706D /* XCTestObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTestObserver.h; sourceTree = ""; }; + FB0105E72A2595B50056706D /* XCTestSuiteRun.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTestSuiteRun.h; sourceTree = ""; }; + FB0105E82A2595B50056706D /* XCTestSuite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTestSuite.h; sourceTree = ""; }; + FB0105E92A2595B50056706D /* CDStructures.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDStructures.h; sourceTree = ""; }; + FB0105EA2A2595B50056706D /* XCTestLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTestLog.h; sourceTree = ""; }; + FB0105EB2A2595B50056706D /* XCTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTest.h; sourceTree = ""; }; + FB0105EC2A2595B50056706D /* XCTestRun.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTestRun.h; sourceTree = ""; }; + FB0105ED2A2595B50056706D /* XCTestCase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTestCase.h; sourceTree = ""; }; + FB0105EF2A2595B50056706D /* XCTActivity-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCTActivity-Protocol.h"; sourceTree = ""; }; + FB0105F02A2595B50056706D /* XCTIssueHandling-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCTIssueHandling-Protocol.h"; sourceTree = ""; }; + FB0105F12A2595B50056706D /* XCTestObservation-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCTestObservation-Protocol.h"; sourceTree = ""; }; + FB0105F22A2595B50056706D /* _XCTestObservationPrivate-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "_XCTestObservationPrivate-Protocol.h"; sourceTree = ""; }; + FB0105F32A2595B50056706D /* XCTWaiterDelegate-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCTWaiterDelegate-Protocol.h"; sourceTree = ""; }; + FB0105F42A2595B50056706D /* NSObject-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject-Protocol.h"; sourceTree = ""; }; + FB0105F52A2595B50056706D /* _XCTestObservationInternal-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "_XCTestObservationInternal-Protocol.h"; sourceTree = ""; }; + FB019E982A25609F00F25BE5 /* libBPXCTestWrapper.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libBPXCTestWrapper.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + FB019E9F2A25612E00F25BE5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + FB21F0202A61F78D00682AC7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + FB21F0302A61FC6B00682AC7 /* BPTestCaseInfo+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BPTestCaseInfo+Internal.h"; sourceTree = ""; }; + FB317F892A259F5000A9FADF /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + FB9F19242A3296A100C894D3 /* BPXCTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPXCTestUtils.h; sourceTree = ""; }; + FB9F19252A3296A100C894D3 /* BPXCTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPXCTestUtils.m; sourceTree = ""; }; + FB9F19282A329AFB00C894D3 /* BPLoggingUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPLoggingUtils.h; sourceTree = ""; }; + FB9F19292A329AFB00C894D3 /* BPLoggingUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPLoggingUtils.m; sourceTree = ""; }; + FB9F192C2A32A7D000C894D3 /* BPTestCaseInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPTestCaseInfo.h; sourceTree = ""; }; + FB9F192D2A32A7D000C894D3 /* BPTestCaseInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPTestCaseInfo.m; sourceTree = ""; }; + FBF6B19E2A609B6D00D7A64B /* BPXCTestWrapperConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPXCTestWrapperConstants.h; sourceTree = ""; }; + FBF6B19F2A609B6D00D7A64B /* BPXCTestWrapperConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPXCTestWrapperConstants.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + FB019E962A25609F00F25BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FB317F8A2A259F5000A9FADF /* XCTest.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + FB0105E52A2595B50056706D /* PrivateHeaders */ = { + isa = PBXGroup; + children = ( + FB0105E62A2595B50056706D /* XCTestObserver.h */, + FB0105E72A2595B50056706D /* XCTestSuiteRun.h */, + FB0105E82A2595B50056706D /* XCTestSuite.h */, + FB0105E92A2595B50056706D /* CDStructures.h */, + FB0105EA2A2595B50056706D /* XCTestLog.h */, + FB0105EB2A2595B50056706D /* XCTest.h */, + FB0105EC2A2595B50056706D /* XCTestRun.h */, + FB0105ED2A2595B50056706D /* XCTestCase.h */, + FB0105EE2A2595B50056706D /* Protocols */, + ); + path = PrivateHeaders; + sourceTree = ""; + }; + FB0105EE2A2595B50056706D /* Protocols */ = { + isa = PBXGroup; + children = ( + FB0105EF2A2595B50056706D /* XCTActivity-Protocol.h */, + FB0105F02A2595B50056706D /* XCTIssueHandling-Protocol.h */, + FB0105F12A2595B50056706D /* XCTestObservation-Protocol.h */, + FB0105F22A2595B50056706D /* _XCTestObservationPrivate-Protocol.h */, + FB0105F32A2595B50056706D /* XCTWaiterDelegate-Protocol.h */, + FB0105F42A2595B50056706D /* NSObject-Protocol.h */, + FB0105F52A2595B50056706D /* _XCTestObservationInternal-Protocol.h */, + ); + path = Protocols; + sourceTree = ""; + }; + FB019E8F2A25609F00F25BE5 = { + isa = PBXGroup; + children = ( + FB21F0202A61F78D00682AC7 /* Info.plist */, + FBF6B19E2A609B6D00D7A64B /* BPXCTestWrapperConstants.h */, + FBF6B19F2A609B6D00D7A64B /* BPXCTestWrapperConstants.m */, + FB9F192C2A32A7D000C894D3 /* BPTestCaseInfo.h */, + FB9F192D2A32A7D000C894D3 /* BPTestCaseInfo.m */, + FB21F0342A61FC7D00682AC7 /* Internal */, + FB0105E52A2595B50056706D /* PrivateHeaders */, + FB019E992A25609F00F25BE5 /* Products */, + FB317F882A259F5000A9FADF /* Frameworks */, + ); + sourceTree = ""; + }; + FB019E992A25609F00F25BE5 /* Products */ = { + isa = PBXGroup; + children = ( + FB019E982A25609F00F25BE5 /* libBPXCTestWrapper.dylib */, + ); + name = Products; + sourceTree = ""; + }; + FB21F0342A61FC7D00682AC7 /* Internal */ = { + isa = PBXGroup; + children = ( + FB019E9F2A25612E00F25BE5 /* main.m */, + FB9F19242A3296A100C894D3 /* BPXCTestUtils.h */, + FB9F19252A3296A100C894D3 /* BPXCTestUtils.m */, + FB9F19282A329AFB00C894D3 /* BPLoggingUtils.h */, + FB9F19292A329AFB00C894D3 /* BPLoggingUtils.m */, + FB21F0302A61FC6B00682AC7 /* BPTestCaseInfo+Internal.h */, + ); + path = Internal; + sourceTree = ""; + }; + FB317F882A259F5000A9FADF /* Frameworks */ = { + isa = PBXGroup; + children = ( + FB317F892A259F5000A9FADF /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + FB019E942A25609F00F25BE5 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + FB21F0232A61FBF600682AC7 /* XCTest.h in Headers */, + FB21F0282A61FC0200682AC7 /* XCTestObserver.h in Headers */, + FB21F02F2A61FC1300682AC7 /* _XCTestObservationInternal-Protocol.h in Headers */, + FB21F0292A61FC0500682AC7 /* XCTActivity-Protocol.h in Headers */, + FB21F0252A61FBFB00682AC7 /* CDStructures.h in Headers */, + FB21F0262A61FBFD00682AC7 /* XCTestSuite.h in Headers */, + FB21F02B2A61FC0A00682AC7 /* XCTestObservation-Protocol.h in Headers */, + FB9F192A2A329AFB00C894D3 /* BPLoggingUtils.h in Headers */, + FB9F192E2A32A7D000C894D3 /* BPTestCaseInfo.h in Headers */, + FB9F19262A3296A100C894D3 /* BPXCTestUtils.h in Headers */, + FBF6B1A02A609B6D00D7A64B /* BPXCTestWrapperConstants.h in Headers */, + FB21F02E2A61FC1100682AC7 /* NSObject-Protocol.h in Headers */, + FB21F02C2A61FC0C00682AC7 /* _XCTestObservationPrivate-Protocol.h in Headers */, + FB21F02A2A61FC0800682AC7 /* XCTIssueHandling-Protocol.h in Headers */, + FB21F0212A61FBF100682AC7 /* XCTestCase.h in Headers */, + FB21F0272A61FC0000682AC7 /* XCTestSuiteRun.h in Headers */, + FB21F0242A61FBF900682AC7 /* XCTestLog.h in Headers */, + FB21F0322A61FC6B00682AC7 /* BPTestCaseInfo+Internal.h in Headers */, + FB21F02D2A61FC0E00682AC7 /* XCTWaiterDelegate-Protocol.h in Headers */, + FB21F0222A61FBF400682AC7 /* XCTestRun.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + FB019E972A25609F00F25BE5 /* BPXCTestWrapper */ = { + isa = PBXNativeTarget; + buildConfigurationList = FB019E9C2A25609F00F25BE5 /* Build configuration list for PBXNativeTarget "BPXCTestWrapper" */; + buildPhases = ( + FB019E942A25609F00F25BE5 /* Headers */, + FB019E952A25609F00F25BE5 /* Sources */, + FB019E962A25609F00F25BE5 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BPXCTestWrapper; + productName = Experiment; + productReference = FB019E982A25609F00F25BE5 /* libBPXCTestWrapper.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + FB019E902A25609F00F25BE5 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1410; + TargetAttributes = { + FB019E972A25609F00F25BE5 = { + CreatedOnToolsVersion = 14.1; + }; + }; + }; + buildConfigurationList = FB019E932A25609F00F25BE5 /* Build configuration list for PBXProject "BPXCTestWrapper" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = FB019E8F2A25609F00F25BE5; + productRefGroup = FB019E992A25609F00F25BE5 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + FB019E972A25609F00F25BE5 /* BPXCTestWrapper */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + FB019E952A25609F00F25BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FB9F192B2A329AFB00C894D3 /* BPLoggingUtils.m in Sources */, + FB9F192F2A32A7D000C894D3 /* BPTestCaseInfo.m in Sources */, + FB9F19272A3296A100C894D3 /* BPXCTestUtils.m in Sources */, + FB019EA02A25612E00F25BE5 /* main.m in Sources */, + FBF6B1A12A609B6D00D7A64B /* BPXCTestWrapperConstants.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + FB019E9A2A25609F00F25BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + 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_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + "IPHONEOS_DEPLOYMENT_TARGET[sdk=*]" = 16.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; + }; + name = Debug; + }; + FB019E9B2A25609F00F25BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + 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_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + "IPHONEOS_DEPLOYMENT_TARGET[arch=*]" = 16.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; + }; + name = Release; + }; + FB019E9D2A25609F00F25BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 57Y47U492U; + DRIVERKIT_DEPLOYMENT_TARGET = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; + PRODUCT_BUNDLE_IDENTIFIER = com.linkedin.bpXCTestWrapper; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + TVOS_DEPLOYMENT_TARGET = ""; + WATCHOS_DEPLOYMENT_TARGET = ""; + }; + name = Debug; + }; + FB019E9E2A25609F00F25BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 57Y47U492U; + DRIVERKIT_DEPLOYMENT_TARGET = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; + PRODUCT_BUNDLE_IDENTIFIER = com.linkedin.bpXCTestWrapper; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + TVOS_DEPLOYMENT_TARGET = ""; + WATCHOS_DEPLOYMENT_TARGET = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + FB019E932A25609F00F25BE5 /* Build configuration list for PBXProject "BPXCTestWrapper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FB019E9A2A25609F00F25BE5 /* Debug */, + FB019E9B2A25609F00F25BE5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FB019E9C2A25609F00F25BE5 /* Build configuration list for PBXNativeTarget "BPXCTestWrapper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FB019E9D2A25609F00F25BE5 /* Debug */, + FB019E9E2A25609F00F25BE5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = FB019E902A25609F00F25BE5 /* Project object */; +} diff --git a/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1dd251a9 --- /dev/null +++ b/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/xcshareddata/xcschemes/BPXCTestWrapper.xcscheme b/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/xcshareddata/xcschemes/BPXCTestWrapper.xcscheme new file mode 100644 index 00000000..c72ae5d6 --- /dev/null +++ b/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/xcshareddata/xcschemes/BPXCTestWrapper.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BPXCTestWrapper/BPXCTestWrapperConstants.h b/BPXCTestWrapper/BPXCTestWrapperConstants.h new file mode 100644 index 00000000..3b961a0c --- /dev/null +++ b/BPXCTestWrapper/BPXCTestWrapperConstants.h @@ -0,0 +1,20 @@ +// +// BPXCTestWrapperConstants.h +// BPXCTestWrapper +// +// Created by Lucas Throckmorton on 7/13/23. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface BPXCTestWrapperConstants : NSObject + ++ (NSString *)dylibName; ++ (NSString *)testBundleEnvironmentKey; ++ (NSString *)outputPathEnvironmentKey; + +@end + +NS_ASSUME_NONNULL_END diff --git a/BPXCTestWrapper/BPXCTestWrapperConstants.m b/BPXCTestWrapper/BPXCTestWrapperConstants.m new file mode 100644 index 00000000..98269e42 --- /dev/null +++ b/BPXCTestWrapper/BPXCTestWrapperConstants.m @@ -0,0 +1,24 @@ +// +// BPXCTestWrapperConstants.m +// BPXCTestWrapper +// +// Created by Lucas Throckmorton on 7/13/23. +// + +#import "BPXCTestWrapperConstants.h" + +@implementation BPXCTestWrapperConstants + ++ (NSString *)dylibName { + return @"libBPXCTestWrapper.dylib"; +} + ++ (NSString *)testBundleEnvironmentKey { + return @"BP_XCTEST_WRAPPER__LOGIC_TEST_BUNDLE"; +} + ++ (NSString *)outputPathEnvironmentKey { + return @"BP_XCTEST_WRAPPER__TEST_CASE_OUTPUT"; +} + +@end diff --git a/BPXCTestWrapper/Info.plist b/BPXCTestWrapper/Info.plist new file mode 100644 index 00000000..520851d4 --- /dev/null +++ b/BPXCTestWrapper/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2018 LinkedIn. All rights reserved. + NSPrincipalClass + + + diff --git a/BPXCTestWrapper/Internal/BPLoggingUtils.h b/BPXCTestWrapper/Internal/BPLoggingUtils.h new file mode 100644 index 00000000..28130d5e --- /dev/null +++ b/BPXCTestWrapper/Internal/BPLoggingUtils.h @@ -0,0 +1,20 @@ +// +// BPLoggingUtils.h +// BPXCTestWrapper +// +// Created by Lucas Throckmorton on 6/8/23. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface BPLoggingUtils : NSObject + ++ (void)log:(NSString *)message; + ++ (void)logError:(NSString *)errorMessage; + +@end + +NS_ASSUME_NONNULL_END diff --git a/BPXCTestWrapper/Internal/BPLoggingUtils.m b/BPXCTestWrapper/Internal/BPLoggingUtils.m new file mode 100644 index 00000000..20253a60 --- /dev/null +++ b/BPXCTestWrapper/Internal/BPLoggingUtils.m @@ -0,0 +1,20 @@ +// +// BPLoggingUtils.m +// BPXCTestWrapper +// +// Created by Lucas Throckmorton on 6/8/23. +// + +#import "BPLoggingUtils.h" + +@implementation BPLoggingUtils + ++ (void)log:(NSString *)message { + NSLog(@"[BPXCTestWrapper] %@", message); +} + ++ (void)logError:(NSString *)errorMessage { + NSLog(@"[BPXCTestWrapper] Error: %@", errorMessage); +} + +@end diff --git a/BPXCTestWrapper/Internal/BPTestCaseInfo+Internal.h b/BPXCTestWrapper/Internal/BPTestCaseInfo+Internal.h new file mode 100644 index 00000000..294665ab --- /dev/null +++ b/BPXCTestWrapper/Internal/BPTestCaseInfo+Internal.h @@ -0,0 +1,19 @@ +// +// BPTestCaseInfo+Internal.h +// BPXCTestWrapper +// +// Created by Lucas Throckmorton on 7/14/23. +// + +#import +#import "XCTestCase.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface BPTestCaseInfo (Internal) + ++ (instancetype)infoFromTestCase:(XCTestCase *)testCase; + +@end + +NS_ASSUME_NONNULL_END diff --git a/BPXCTestWrapper/Internal/BPXCTestUtils.h b/BPXCTestWrapper/Internal/BPXCTestUtils.h new file mode 100644 index 00000000..d279f28e --- /dev/null +++ b/BPXCTestWrapper/Internal/BPXCTestUtils.h @@ -0,0 +1,18 @@ +// +// BPTestSuiteUtils.h +// BPXCTestWrapper +// +// Created by Lucas Throckmorton on 6/8/23. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface BPXCTestUtils : NSObject + ++ (void)logAllTestsInBundleWithPath:(NSString *)bundlePath toFile:(NSString *)outputPath; + +@end + +NS_ASSUME_NONNULL_END diff --git a/BPXCTestWrapper/Internal/BPXCTestUtils.m b/BPXCTestWrapper/Internal/BPXCTestUtils.m new file mode 100644 index 00000000..7c5ceca6 --- /dev/null +++ b/BPXCTestWrapper/Internal/BPXCTestUtils.m @@ -0,0 +1,97 @@ +// +// BPXCTestUtils.m +// BPXCTestWrapper +// +// Created by Lucas Throckmorton on 6/8/23. +// + +#import "BPXCTestUtils.h" + +#import +#import "XCTestSuite.h" +#import "XCTestCase.h" +#import "BPLoggingUtils.h" +#import "BPTestCaseInfo+Internal.h" + +@implementation BPXCTestUtils + ++ (void)logAllTestsInBundleWithPath:(NSString *)bundlePath toFile:(NSString *)outputPath { + NSArray *testCases = [self enumerateTestCasesInBundleWithPath:bundlePath]; + // Encode the test data + NSError *encodingError; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:testCases requiringSecureCoding:NO error:&encodingError]; + // Write to file. + NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:outputPath]; + [fileHandle writeData:data]; +// [testCases.description writeToFile:outputPath atomically:YES encoding:NSUTF8StringEncoding error:nil]; + [fileHandle closeFile]; + + NSString *output = [NSString stringWithFormat:@"Wrote to file: %@.", outputPath]; + [BPLoggingUtils log:output]; +} + ++ (NSArray *)enumerateTestCasesInBundleWithPath:(NSString *)bundlePath { + NSBundle *bundle = [NSBundle bundleWithPath:bundlePath]; + if (!bundle || !bundle.executablePath) { + // Log error... + [BPLoggingUtils logError:[NSString stringWithFormat:@"Unable to get executable path from bundle: %@", bundle]]; + return @[]; + } + return [self enumerateTestCasesInBundle:bundle]; +} + + +//static void listBundle(NSString *testBundlePath, NSString *outputFile) ++ (NSArray *)enumerateTestCasesInBundle:(NSBundle *)bundle { + /** + We need to remove the XCTest preference before continuing so that + we can open the bundle without actually executing it. If you start + to see logs resembling test output, it means this hasn't been done. + + TLDR: opening an .xctest file will normally cause the linker to load + the XCTest framework, which will trigger `XCTestSuite.initialize` + and start running the tests. + */ + +// NSLog(@"INJECTION - %@", [NSUserDefaults.standardUserDefaults objectForKey:@"XCTest"]); + [NSUserDefaults.standardUserDefaults removeObjectForKey:@"XCTest"]; + [NSUserDefaults.standardUserDefaults synchronize]; + + /** + We must actually open the test bundle so that all of the test cases are loaded into memory. + We use `dlopen` here instead of `NSBundle.loadAndReturnError` for more informative messages on error. + */ + if (dlopen(bundle.executablePath.UTF8String, RTLD_LAZY) == NULL) { + [BPLoggingUtils logError:[NSString stringWithFormat:@"Unable to open test bundle's executable path - %@", bundle.executablePath]]; + return @[]; + } + + [NSUserDefaults.standardUserDefaults setObject:@"None" forKey:@"XCTest"]; + + /** + Note that `XCTestSuite`, `XCTestCase`, etc all subclass `XCTest`, so to enumerate all tests in the current + bundle, we'll want to start `XCTestSuite.allTests`, and expand out all nested `XCTestSuite`s, adding + the suite's `.tests` to our array. + */ + NSMutableArray *testList = [NSMutableArray array]; + NSMutableArray *queue = [@[XCTestSuite.allTests] mutableCopy]; + while (queue.count) { + XCTest *test = queue.firstObject; + [queue removeObjectAtIndex:0]; + // If it's another nested XCTestSuite, keep going deeper! + // If it's an XCTestCase, we've hit a leaf and can just add it to our `testList`. + if ([test isKindOfClass:XCTestSuite.class]) { + XCTestSuite *testSuite = (XCTestSuite *)test; + [queue addObjectsFromArray:testSuite.tests]; + } else if ([test isKindOfClass:XCTestCase.class]) { + XCTestCase *testCase = (XCTestCase *)test; + [BPLoggingUtils log:[NSString stringWithFormat:@"testCase: %@", testCase]]; + [testList addObject:[BPTestCaseInfo infoFromTestCase:testCase]]; + } else { + [BPLoggingUtils logError:@"Found a currently unhandled XCTest type while enumerating tests"]; + } + } + return testList; +} + +@end diff --git a/BPXCTestWrapper/Internal/main.m b/BPXCTestWrapper/Internal/main.m new file mode 100644 index 00000000..f2df5aed --- /dev/null +++ b/BPXCTestWrapper/Internal/main.m @@ -0,0 +1,60 @@ +// +// main.m +// BPXCTestWrapper +// +// Created by Lucas Throckmorton on 5/29/23. +// + +#import +#import +#import + +#import "BPXCTestUtils.h" +#import "BPLoggingUtils.h" +#import "BPXCTestWrapperConstants.h" + +//void tearDown(int signal) { +//} + +/** + This wrapper around XCTest hijacks the process when two key env variables are set, hooking into the xctest process to + instead get information on what tests exist in the test suite. This allows Bluepill to do two main things: + + 1) It can create an aggregate timeout based on the number of tests to run + 2) It allows us to support opting-out of tests, rather than just opting in, as xctest provides no explicit opt-out api. + + When `BP_XCTEST_WRAPPER__LOGIC_TEST_BUNDLE` and `BP_XCTEST_WRAPPER__TEST_CASE_OUTPUT` are set, + the entire process will only output an encoded file with test case info at the path specified by `BP_XCTEST_WRAPPER__TEST_CASE_OUTPUT`, + and **no tests will actually be run**. + + When they are not set, tests will be run as normal with no other side effects. + */ +__attribute__((constructor)) +static void didLoad() { + [BPLoggingUtils log:@"Booting up the wrapper."]; + + #if !TARGET_OS_IOS + [BPLoggingUtils log:@"Returning."]; + return; + #endif + + // Grab relavent info from environment + NSString *bundlePath = NSProcessInfo.processInfo.environment[BPXCTestWrapperConstants.testBundleEnvironmentKey]; + NSString *outputPath = NSProcessInfo.processInfo.environment[BPXCTestWrapperConstants.outputPathEnvironmentKey]; + // Reset DYLD_INSERT_LIBRARIES and other env variables to avoid impacting future processes + unsetenv("DYLD_INSERT_LIBRARIES"); + unsetenv(BPXCTestWrapperConstants.testBundleEnvironmentKey.UTF8String); + unsetenv(BPXCTestWrapperConstants.outputPathEnvironmentKey.UTF8String); + + if (!bundlePath || !outputPath) { + return; + } + [BPLoggingUtils log:[NSString stringWithFormat:@"Will enumerate all testCases in bundle at %@", bundlePath]]; + [BPXCTestUtils logAllTestsInBundleWithPath:bundlePath toFile:outputPath]; + // Once tests are logged, we want the process to end. + exit(0); +} + +//__attribute__((destructor)) static void pluginDidUnload() { +// tearDown(0); +//} diff --git a/BPXCTestWrapper/PrivateHeaders/CDStructures.h b/BPXCTestWrapper/PrivateHeaders/CDStructures.h new file mode 100644 index 00000000..55bc3957 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/CDStructures.h @@ -0,0 +1,57 @@ +// +// Generated by class-dump 3.5 (64 bit). +// +// class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard. +// + +#pragma mark Blocks + +typedef void (^CDUnknownBlockType)(void); // return type and parameters are unknown + +#pragma mark Named Structures + +struct DTXMachMessage { + struct { + struct { + unsigned int _field1; + unsigned int _field2; + unsigned int _field3; + unsigned int _field4; + unsigned int _field5; + int _field6; + } _field1; + unsigned int _field2; + } _field1; + char _field2[32672]; + char _field3[68]; +}; + +//struct DTXMessageHeader { +// unsigned int _field1; +// unsigned int _field2; +// unsigned short _field3; +// unsigned short _field4; +// unsigned int _field5; +// struct DTXMessageRoutingInfo _field6; +//}; + +struct DTXMessageRoutingInfo { + unsigned int _field1; + unsigned int _field2; + unsigned int _field3; + unsigned int :1; + unsigned int :31; +}; + +struct __va_list_tag { + unsigned int _field1; + unsigned int _field2; + void *_field3; + void *_field4; +}; + +//struct mach_timebase_info { +// unsigned int numer; +// unsigned int denom; +//}; + diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/NSObject-Protocol.h b/BPXCTestWrapper/PrivateHeaders/Protocols/NSObject-Protocol.h new file mode 100644 index 00000000..3320b3c9 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/Protocols/NSObject-Protocol.h @@ -0,0 +1,33 @@ +// +// Generated by class-dump 3.5 (64 bit). +// +// class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard. +// + +@class NSString, Protocol; + +@protocol NSObject +@property(readonly, copy) NSString *description; +@property(readonly) Class superclass; +@property(readonly) unsigned long long hash; +- (struct _NSZone *)zone; +- (unsigned long long)retainCount; +- (id)autorelease; +- (oneway void)release; +- (id)retain; +- (BOOL)respondsToSelector:(SEL)arg1; +- (BOOL)conformsToProtocol:(Protocol *)arg1; +- (BOOL)isMemberOfClass:(Class)arg1; +- (BOOL)isKindOfClass:(Class)arg1; +- (BOOL)isProxy; +- (id)performSelector:(SEL)arg1 withObject:(id)arg2 withObject:(id)arg3; +- (id)performSelector:(SEL)arg1 withObject:(id)arg2; +- (id)performSelector:(SEL)arg1; +- (id)self; +- (Class)class; +- (BOOL)isEqual:(id)arg1; + +@optional +@property(readonly, copy) NSString *debugDescription; +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/XCTActivity-Protocol.h b/BPXCTestWrapper/PrivateHeaders/Protocols/XCTActivity-Protocol.h new file mode 100644 index 00000000..74becde5 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/Protocols/XCTActivity-Protocol.h @@ -0,0 +1,15 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +@import Foundation; + +@class NSString, XCTAttachment; + +@protocol XCTActivity +@property(readonly, copy) NSString *name; +- (void)addAttachment:(XCTAttachment *)arg1; +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/XCTIssueHandling-Protocol.h b/BPXCTestWrapper/PrivateHeaders/Protocols/XCTIssueHandling-Protocol.h new file mode 100644 index 00000000..4dc2a6e1 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/Protocols/XCTIssueHandling-Protocol.h @@ -0,0 +1,16 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +@import Foundation; + +@class XCTExpectedFailureContext, XCTIssue; + +@protocol XCTIssueHandling +- (void)expectFailureWithContext:(XCTExpectedFailureContext *)arg1 inBlock:(void (^)(void))arg2; +- (void)expectFailureWithContext:(XCTExpectedFailureContext *)arg1; +- (void)handleIssue:(XCTIssue *)arg1; +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/XCTWaiterDelegate-Protocol.h b/BPXCTestWrapper/PrivateHeaders/Protocols/XCTWaiterDelegate-Protocol.h new file mode 100644 index 00000000..ca59b949 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/Protocols/XCTWaiterDelegate-Protocol.h @@ -0,0 +1,17 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +@import Foundation; + +@class NSArray, XCTWaiter, XCTestExpectation; + +@protocol XCTWaiterDelegate +- (void)nestedWaiter:(XCTWaiter *)arg1 wasInterruptedByTimedOutWaiter:(XCTWaiter *)arg2; +- (void)waiter:(XCTWaiter *)arg1 didFulfillInvertedExpectation:(XCTestExpectation *)arg2; +- (void)waiter:(XCTWaiter *)arg1 fulfillmentDidViolateOrderingConstraintsForExpectation:(XCTestExpectation *)arg2 requiredExpectation:(XCTestExpectation *)arg3; +- (void)waiter:(XCTWaiter *)arg1 didTimeoutWithUnfulfilledExpectations:(NSArray *)arg2; +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/XCTestObservation-Protocol.h b/BPXCTestWrapper/PrivateHeaders/Protocols/XCTestObservation-Protocol.h new file mode 100644 index 00000000..46528735 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/Protocols/XCTestObservation-Protocol.h @@ -0,0 +1,27 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +//#import "NSObject-Protocol.h" + +@class NSBundle, NSString, XCTExpectedFailure, XCTIssue, XCTestCase, XCTestSuite; + +@protocol XCTestObservation + +@optional +- (void)testCase:(XCTestCase *)arg1 didFailWithDescription:(NSString *)arg2 inFile:(NSString *)arg3 atLine:(unsigned long long)arg4; +- (void)testSuite:(XCTestSuite *)arg1 didFailWithDescription:(NSString *)arg2 inFile:(NSString *)arg3 atLine:(unsigned long long)arg4; +- (void)testCaseDidFinish:(XCTestCase *)arg1; +- (void)testCase:(XCTestCase *)arg1 didRecordExpectedFailure:(XCTExpectedFailure *)arg2; +- (void)testCase:(XCTestCase *)arg1 didRecordIssue:(XCTIssue *)arg2; +- (void)testCaseWillStart:(XCTestCase *)arg1; +- (void)testSuiteDidFinish:(XCTestSuite *)arg1; +- (void)testSuite:(XCTestSuite *)arg1 didRecordExpectedFailure:(XCTExpectedFailure *)arg2; +- (void)testSuite:(XCTestSuite *)arg1 didRecordIssue:(XCTIssue *)arg2; +- (void)testSuiteWillStart:(XCTestSuite *)arg1; +- (void)testBundleDidFinish:(NSBundle *)arg1; +- (void)testBundleWillStart:(NSBundle *)arg1; +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/_XCTestObservationInternal-Protocol.h b/BPXCTestWrapper/PrivateHeaders/Protocols/_XCTestObservationInternal-Protocol.h new file mode 100644 index 00000000..0aee3569 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/Protocols/_XCTestObservationInternal-Protocol.h @@ -0,0 +1,17 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +#import "_XCTestObservationPrivate-Protocol.h" + +@class NSArray, NSNumber, NSString, XCTSourceCodeContext, XCTestCase, XCTestRun; + +@protocol _XCTestObservationInternal <_XCTestObservationPrivate> + +@optional +- (void)_testCase:(XCTestRun *)arg1 didMeasureValues:(NSArray *)arg2 forPerformanceMetricID:(NSString *)arg3 name:(NSString *)arg4 unitsOfMeasurement:(NSString *)arg5 baselineName:(NSString *)arg6 baselineAverage:(NSNumber *)arg7 maxPercentRegression:(NSNumber *)arg8 maxPercentRelativeStandardDeviation:(NSNumber *)arg9 maxRegression:(NSNumber *)arg10 maxStandardDeviation:(NSNumber *)arg11 file:(NSString *)arg12 line:(unsigned long long)arg13 polarity:(long long)arg14; +- (void)testCase:(XCTestCase *)arg1 wasSkippedWithDescription:(NSString *)arg2 sourceCodeContext:(XCTSourceCodeContext *)arg3; +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/_XCTestObservationPrivate-Protocol.h b/BPXCTestWrapper/PrivateHeaders/Protocols/_XCTestObservationPrivate-Protocol.h new file mode 100644 index 00000000..3dfc04d4 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/Protocols/_XCTestObservationPrivate-Protocol.h @@ -0,0 +1,17 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +#import "XCTestObservation-Protocol.h" + +@class XCActivityRecord, XCTContext; + +@protocol _XCTestObservationPrivate + +@optional +- (void)_context:(XCTContext *)arg1 didFinishActivity:(XCActivityRecord *)arg2; +- (void)_context:(XCTContext *)arg1 willStartActivity:(XCActivityRecord *)arg2; +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/XCTest.h b/BPXCTestWrapper/PrivateHeaders/XCTest.h new file mode 100644 index 00000000..155a225b --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/XCTest.h @@ -0,0 +1,55 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +@import Foundation; + +#import "XCTIssueHandling-Protocol.h" + +@class NSString, XCTExpectedFailureContextManager, XCTTestIdentifier, XCTestObservationCenter, XCTestRun; + +@interface XCTest : NSObject +{ + XCTExpectedFailureContextManager *_expectedFailureContextManager; + XCTestRun *_testRun; + XCTestObservationCenter *_observationCenter; +} + +- (void)expectFailureWithContext:(id)arg1 inBlock:(id)arg2; +- (void)expectFailureWithContext:(id)arg1; +- (void)_checkForExpectedFailureMatchingIssue:(id)arg1; +- (id)expectedFailureContextManager; +- (void)handleIssue:(id)arg1; +- (long long)defaultExecutionOrderCompare:(id)arg1; +@property(readonly) NSString *nameForLegacyLogging; +@property(readonly) NSString *languageAgnosticTestMethodName; +@property(readonly) NSString *languageAgnosticTestClassName; +- (_Bool)tearDownWithError:(id *)arg1; +- (void)tearDown; +- (void)setUp; +- (_Bool)setUpWithError:(id *)arg1; +- (_Bool)_shouldRerunTest; +- (void)runTest; +- (void)performTest:(id)arg1; +@property(retain) XCTestObservationCenter *observationCenter; // @synthesize observationCenter=_observationCenter; +@property(readonly) XCTestRun *testRun; // @synthesize testRun=_testRun; +@property(readonly) Class testRunClass; +@property(readonly) Class _requiredTestRunBaseClass; +@property(readonly, copy) NSString *name; +@property(readonly) unsigned long long testCaseCount; +- (id)_duplicate; +@property(readonly) NSString *_methodNameForReporting; +@property(readonly) NSString *_classNameForReporting; +- (void)removeTestsWithIdentifierInSet:(id)arg1; + +// Remaining properties +@property(readonly, copy) NSString *debugDescription; +@property(readonly, copy) NSString *description; +@property(readonly) NSUInteger hash; +@property(readonly, getter=_identifier) XCTTestIdentifier *identifier; // @dynamic identifier; +@property(readonly) Class superclass; + +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/XCTestCase.h b/BPXCTestWrapper/PrivateHeaders/XCTestCase.h new file mode 100644 index 00000000..89c788d2 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/XCTestCase.h @@ -0,0 +1,189 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +#import "XCTest.h" + +#import "XCTActivity-Protocol.h" +#import "XCTWaiterDelegate-Protocol.h" + +@class MXMInstrument, NSArray, NSDictionary, NSInvocation, NSMutableArray, NSMutableDictionary, NSMutableSet, NSObject, NSString, NSThread, XCTAttachmentManager, XCTIssue, XCTSkippedTestContext, XCTTestIdentifier, XCTWaiter, XCTestCaseRun; +@protocol OS_dispatch_source; + +@interface XCTestCase : XCTest +{ + _Bool _continueAfterFailure; + _Bool __preciseTimeoutsEnabled; + _Bool _isMeasuringMetrics; + _Bool __didMeasureMetrics; + _Bool __didStartMeasuring; + _Bool __didStopMeasuring; + _Bool _hasDequeuedTeardownBlocks; + _Bool _hasReportedFailuresToTestCaseRun; + _Bool _canHandleInterruptions; + _Bool _shouldHaltWhenReceivesControl; + _Bool _shouldSetShouldHaltWhenReceivesControl; + _Bool _hasAttemptedToCaptureScreenshotOnFailure; + XCTTestIdentifier *_identifier; + NSInvocation *_invocation; + double _executionTimeAllowance; + NSArray *_activePerformanceMetricIDs; + unsigned long long _startWallClockTime; + struct time_value _startUserTime; + struct time_value _startSystemTime; + unsigned long long _measuringIteration; + MXMInstrument *_instrument; + long long _runLoopNestingCount; + NSMutableArray *_teardownBlocks; + NSMutableArray *_primaryThreadBlocks; + XCTAttachmentManager *_attachmentManager; + NSDictionary *_activityAggregateStatistics; + NSObject *_timeoutSource; + unsigned long long _signpostID; + NSThread *_primaryThread; + NSMutableSet *_previousIssuesAssociatedWithSwiftErrors; + NSMutableArray *_enqueuedIssues; + NSMutableArray *_expectations; + XCTWaiter *_currentWaiter; + XCTSkippedTestContext *_skippedTestContext; + XCTestCaseRun *_testCaseRun; + NSMutableDictionary *__perfMetricsForID; +} + ++ (id)_baselineDictionary; ++ (_Bool)_treatMissingBaselinesAsTestFailures; ++ (id)defaultMeasureOptions; ++ (id)defaultMetrics; ++ (id)defaultPerformanceMetrics; ++ (_Bool)_reportPerformanceFailuresForLargeImprovements; ++ (id)testInvocations; ++ (_Bool)isInheritingTestCases; ++ (id)bundle; ++ (id)testCaseWithSelector:(SEL)arg1; ++ (_Bool)_isDiscoverable; ++ (id)testCaseWithInvocation:(id)arg1; ++ (void)tearDown; ++ (void)setUp; ++ (id)defaultTestSuite; ++ (id)allTestMethodInvocations; ++ (void)_allTestMethodInvocations:(id)arg1; ++ (id)testMethodInvocations; ++ (id)allSubclassesOutsideXCTest; ++ (id)allSubclasses; ++ (id)_allSubclasses; + +@property(retain) NSMutableDictionary *_perfMetricsForID; // @synthesize _perfMetricsForID=__perfMetricsForID; +@property(retain) XCTestCaseRun *testCaseRun; // @synthesize testCaseRun=_testCaseRun; +@property(nonatomic) _Bool shouldSetShouldHaltWhenReceivesControl; // @synthesize shouldSetShouldHaltWhenReceivesControl=_shouldSetShouldHaltWhenReceivesControl; +@property(nonatomic) _Bool shouldHaltWhenReceivesControl; // @synthesize shouldHaltWhenReceivesControl=_shouldHaltWhenReceivesControl; +@property(retain) NSThread *primaryThread; // @synthesize primaryThread=_primaryThread; +@property(copy) NSDictionary *activityAggregateStatistics; // @synthesize activityAggregateStatistics=_activityAggregateStatistics; +@property long long runLoopNestingCount; // @synthesize runLoopNestingCount=_runLoopNestingCount; +@property _Bool _didStopMeasuring; // @synthesize _didStopMeasuring=__didStopMeasuring; +@property _Bool _didStartMeasuring; // @synthesize _didStartMeasuring=__didStartMeasuring; +@property _Bool _didMeasureMetrics; // @synthesize _didMeasureMetrics=__didMeasureMetrics; +@property(nonatomic) _Bool _preciseTimeoutsEnabled; // @synthesize _preciseTimeoutsEnabled=__preciseTimeoutsEnabled; +@property(readonly) double _effectiveExecutionTimeAllowance; +- (void)_resetTimer; +- (void)_stopTimeoutTimer; +- (void)_startTimeoutTimer; +- (void)_exceededExecutionTimeAllowance; +@property unsigned long long maxDurationInMinutes; +@property double executionTimeAllowance; // @synthesize executionTimeAllowance=_executionTimeAllowance; +- (void)addAttachment:(id)arg1; +- (void)runActivityNamed:(id)arg1 inScope:(id)arg2; +- (void)startActivityWithTitle:(id)arg1 block:(id)arg2; +- (void)startActivityWithTitle:(id)arg1 type:(id)arg2 block:(id)arg3; +- (void)measureMetrics:(id)arg1 automaticallyStartMeasuring:(_Bool)arg2 forBlock:(id)arg3; +- (void)registerDefaultMetrics; +- (id)baselinesDictionaryForTest; +- (void)_logAndReportPerformanceMetrics:(id)arg1 perfMetricResultsForIDs:(id)arg2 withBaselinesForTest:(id)arg3; +- (void)_logAndReportPerformanceMetrics:(id)arg1 perfMetricResultsForIDs:(id)arg2 withBaselinesForTest:(id)arg3 defaultBaselinesForPerfMetricID:(id)arg4; +- (void)registerMetricID:(id)arg1 name:(id)arg2 unitString:(id)arg3 polarity:(long long)arg4; +- (void)registerMetricID:(id)arg1 name:(id)arg2 unitString:(id)arg3; +- (void)registerMetricID:(id)arg1 name:(id)arg2 unit:(id)arg3; +- (void)reportMetric:(id)arg1 reportFailures:(_Bool)arg2; +- (void)reportMeasurements:(id)arg1 forMetricID:(id)arg2 reportFailures:(_Bool)arg3; +- (void)_recordValues:(id)arg1 forPerformanceMetricID:(id)arg2 name:(id)arg3 unitsOfMeasurement:(id)arg4 baselineName:(id)arg5 baselineAverage:(id)arg6 maxPercentRegression:(id)arg7 maxPercentRelativeStandardDeviation:(id)arg8 maxRegression:(id)arg9 maxStandardDeviation:(id)arg10 file:(id)arg11 line:(unsigned long long)arg12 polarity:(long long)arg13; +- (void)measureWithMetrics:(id)arg1 options:(id)arg2 block:(id)arg3; +- (void)measureWithMetrics:(id)arg1 block:(id)arg2; +- (void)measureWithOptions:(id)arg1 block:(id)arg2; +- (void)measureBlock:(id)arg1; +- (void)stopMeasuring; +- (void)startMeasuring; +@property(readonly) id minimumOperatingSystemVersion; +- (void)_logMemoryGraphDataFromFilePath:(id)arg1 withTitle:(id)arg2; +- (void)_logMemoryGraphData:(id)arg1 withTitle:(id)arg2; +- (unsigned long long)numberOfTestIterationsForTestWithSelector:(SEL)arg1; +- (id)addAdditionalIterationsBasedOnOptions:(id)arg1; +- (void)afterTestIteration:(unsigned long long)arg1 selector:(SEL)arg2; +- (void)beforeTestIteration:(unsigned long long)arg1 selector:(SEL)arg2; +- (void)tearDownTestWithSelector:(SEL)arg1; +- (void)setUpTestWithSelector:(SEL)arg1; +- (void)_addTeardownBlock:(id)arg1; +- (void)addTeardownBlock:(id)arg1; +- (void)_purgeTeardownBlocks; +- (void)performTest:(id)arg1; +- (_Bool)_shouldRerunTest; +- (void)_reportFailuresAtFile:(id)arg1 line:(unsigned long long)arg2 forTestAssertionsInScope:(id)arg3; +- (void)invokeTest; +- (Class)testRunClass; +- (Class)_requiredTestRunBaseClass; +- (void)recordIssue:(id)arg1; +@property _Bool continueAfterFailure; // @synthesize continueAfterFailure=_continueAfterFailure; +@property(retain) NSInvocation *invocation; // @synthesize invocation=_invocation; +- (void)dealloc; +@property(readonly, copy) NSString *description; +- (_Bool)isEqual:(id)arg1; +- (long long)defaultExecutionOrderCompare:(id)arg1; +- (id)nameForLegacyLogging; +@property(readonly, copy) NSString *name; +- (id)languageAgnosticTestMethodName; +- (id)_identifier; +- (unsigned long long)testCaseCount; +- (id)bundle; +- (id)initWithSelector:(SEL)arg1; +- (id)_duplicate; +- (id)initWithInvocation:(id)arg1; +- (id)init; +- (void)removeUIInterruptionMonitor:(id)arg1; +- (id)addUIInterruptionMonitorWithDescription:(id)arg1 handler:(id)arg2; +- (void)nestedWaiter:(id)arg1 wasInterruptedByTimedOutWaiter:(id)arg2; +- (void)waiter:(id)arg1 didFulfillInvertedExpectation:(id)arg2; +- (void)waiter:(id)arg1 fulfillmentDidViolateOrderingConstraintsForExpectation:(id)arg2 requiredExpectation:(id)arg3; +- (void)waiter:(id)arg1 didTimeoutWithUnfulfilledExpectations:(id)arg2; +- (id)expectationForPredicate:(id)arg1 evaluatedWithObject:(id)arg2 handler:(id)arg3; +- (id)expectationForNotification:(id)arg1 object:(id)arg2 notificationCenter:(id)arg3 handler:(id)arg4; +- (id)expectationForNotification:(id)arg1 object:(id)arg2 handler:(id)arg3; +- (id)keyValueObservingExpectationForObject:(id)arg1 keyPath:(id)arg2 handler:(id)arg3; +- (id)keyValueObservingExpectationForObject:(id)arg1 keyPath:(id)arg2 expectedValue:(id)arg3; +- (id)expectationWithDescription:(id)arg1; +- (void)waitForExpectations:(id)arg1 timeout:(double)arg2 enforceOrder:(_Bool)arg3; +- (void)waitForExpectations:(id)arg1 timeout:(double)arg2; +- (void)waitForExpectationsWithTimeout:(double)arg1 handler:(id)arg2; +- (id)_expectationForDistributedNotification:(id)arg1 object:(id)arg2 handler:(id)arg3; +- (id)_expectationForDarwinNotification:(id)arg1; +- (void)recordFailureWithDescription:(id)arg1 inFile:(id)arg2 atLine:(unsigned long long)arg3 expected:(_Bool)arg4; +- (void)_interruptOrMarkForLaterInterruption; +- (_Bool)_caughtUnhandledDeveloperExceptionPermittingControlFlowInterruptions:(_Bool)arg1 caughtInterruptionException:(_Bool *)arg2 whileExecutingBlock:(id)arg3; +- (_Bool)_caughtUnhandledDeveloperExceptionPermittingControlFlowInterruptions:(_Bool)arg1 whileExecutingBlock:(id)arg2; +- (id)_issueWithFailureScreenshotAttachedToIssue:(id)arg1; +- (void)_handleIssue:(id)arg1; +- (void)_dequeueIssues; +- (void)_enqueueIssue:(id)arg1; +- (void)_recordIssue:(id)arg1; +- (_Bool)_isDuplicateOfIssueAssociatedWithSameSwiftError:(id)arg1; +- (void)expectFailureWithContext:(id)arg1; +@property(copy) XCTIssue *candidateIssueForCurrentThread; +- (id)_storageKeyForCandidateIssue; +- (void)handleIssue:(id)arg1; + +// Remaining properties +@property(readonly, copy) NSString *debugDescription; +@property(readonly) NSUInteger hash; +@property(readonly) Class superclass; + +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/XCTestLog.h b/BPXCTestWrapper/PrivateHeaders/XCTestLog.h new file mode 100644 index 00000000..f9ebe091 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/XCTestLog.h @@ -0,0 +1,59 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +#import "XCTestObserver.h" + +#import "_XCTestObservationInternal-Protocol.h" +#import "CDStructures.h" + +@class NSFileHandle, NSString, XCTestSuite, XCTestCase; + +@interface XCTestLog : XCTestObserver <_XCTestObservationInternal> +{ +} + +// Currently Handled + +- (void)testSuiteWillStart:(XCTestSuite *)suite; +- (void)testSuiteDidFinish:(XCTestCase *)testCase; +- (void)testCaseWillStart:(XCTestCase *)testCase; +- (void)testCaseDidFinish:(XCTestCase *)testCase; +- (void)testCase:(XCTestCase *)testCase +didFailWithDescription:(NSString *)description + inFile:(NSString *)file + atLine:(NSUInteger)line; +- (void)testCase:(XCTestCase *)testCase +wasSkippedWithDescription:(NSString *)description + inFile:(NSString *)file + atLine:(NSUInteger)line; + +// TODO: @lthrockm - add support + +- (void)testSuite:(id)arg1 didRecordExpectedFailure:(id)arg2; +- (void)testSuite:(id)arg1 didFailWithDescription:(id)arg2 inFile:(id)arg3 atLine:(unsigned long long)arg4; + +// Other + ++ (id)_messageForTest:(id)arg1 didMeasureValues:(id)arg2 forPerformanceMetricID:(id)arg3 name:(id)arg4 unitsOfMeasurement:(id)arg5 baselineName:(id)arg6 baselineAverage:(id)arg7 maxPercentRegression:(id)arg8 maxPercentRelativeStandardDeviation:(id)arg9 maxRegression:(id)arg10 maxStandardDeviation:(id)arg11 file:(id)arg12 line:(unsigned long long)arg13 polarity:(long long)arg14; +- (void)_context:(id)arg1 willStartActivity:(id)arg2; +- (void)testCase:(id)arg1 didRecordExpectedFailure:(id)arg2; +- (void)_testCase:(id)arg1 didMeasureValues:(id)arg2 forPerformanceMetricID:(id)arg3 name:(id)arg4 unitsOfMeasurement:(id)arg5 baselineName:(id)arg6 baselineAverage:(id)arg7 maxPercentRegression:(id)arg8 maxPercentRelativeStandardDeviation:(id)arg9 maxRegression:(id)arg10 maxStandardDeviation:(id)arg11 file:(id)arg12 line:(unsigned long long)arg13 polarity:(long long)arg14; +- (void)_testWithName:(id)arg1 didRecordExpectedFailure:(id)arg2; +- (void)_testDidFail:(id)arg1 withDescription:(id)arg2 inFile:(id)arg3 atLine:(unsigned long long)arg4; +- (void)logTestMessage:(id)arg1; +- (void)testLogWithFormat:(id)arg1 arguments:(struct __va_list_tag [1])arg2; +- (void)testLogWithFormat:(id)arg1; +@property(readonly) NSFileHandle *logFileHandle; +- (id)dateFormatter; + +// Remaining properties +@property(readonly, copy) NSString *debugDescription; +@property(readonly, copy) NSString *description; +@property(readonly) NSUInteger hash; +@property(readonly) Class superclass; + +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/XCTestObserver.h b/BPXCTestWrapper/PrivateHeaders/XCTestObserver.h new file mode 100644 index 00000000..3e09a3c3 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/XCTestObserver.h @@ -0,0 +1,23 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +#import + +@interface XCTestObserver : NSObject +{ +} + +- (void)testCaseDidFail:(id)arg1 withDescription:(id)arg2 inFile:(id)arg3 atLine:(unsigned long long)arg4; +- (void)testCaseDidStop:(id)arg1; +- (void)testCaseDidStart:(id)arg1; +- (void)testSuiteDidFail:(id)arg1 withDescription:(id)arg2 inFile:(id)arg3 atLine:(unsigned long long)arg4; +- (void)testSuiteDidStop:(id)arg1; +- (void)testSuiteDidStart:(id)arg1; +- (void)stopObserving; +- (void)startObserving; + +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/XCTestRun.h b/BPXCTestWrapper/PrivateHeaders/XCTestRun.h new file mode 100644 index 00000000..223c7db6 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/XCTestRun.h @@ -0,0 +1,73 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +#import + +@class NSDate, XCTIssue, XCTest, XCTestObservationCenter; + +@interface XCTestRun : NSObject +{ + _Bool _hasBeenSkipped; + _Bool _hasStarted; + _Bool _hasStopped; + _Bool _hasExpectedFailure; + XCTest *_test; + unsigned long long _executionCount; + unsigned long long _failureCount; + unsigned long long _unexpectedExceptionCount; + double _startTimeInterval; + double _stopTimeInterval; + XCTIssue *_candidateIssue; + unsigned long long __runCount; + unsigned long long _executionCountBeforeCrash; + unsigned long long _skipCountBeforeCrash; + unsigned long long _expectedFailureCountBeforeCrash; + unsigned long long _failureCountBeforeCrash; + unsigned long long _unexpectedExceptionCountBeforeCrash; +} + ++ (id)testRunWithTest:(id)arg1; +//- (void).cxx_destruct; +@property unsigned long long unexpectedExceptionCountBeforeCrash; // @synthesize unexpectedExceptionCountBeforeCrash=_unexpectedExceptionCountBeforeCrash; +@property unsigned long long failureCountBeforeCrash; // @synthesize failureCountBeforeCrash=_failureCountBeforeCrash; +@property unsigned long long expectedFailureCountBeforeCrash; // @synthesize expectedFailureCountBeforeCrash=_expectedFailureCountBeforeCrash; +@property unsigned long long skipCountBeforeCrash; // @synthesize skipCountBeforeCrash=_skipCountBeforeCrash; +@property unsigned long long executionCountBeforeCrash; // @synthesize executionCountBeforeCrash=_executionCountBeforeCrash; +@property unsigned long long _runCount; // @synthesize _runCount=__runCount; +@property(retain) XCTIssue *candidateIssue; // @synthesize candidateIssue=_candidateIssue; +@property _Bool hasExpectedFailure; // @synthesize hasExpectedFailure=_hasExpectedFailure; +@property _Bool hasStopped; // @synthesize hasStopped=_hasStopped; +@property _Bool hasStarted; // @synthesize hasStarted=_hasStarted; +@property double stopTimeInterval; // @synthesize stopTimeInterval=_stopTimeInterval; +@property double startTimeInterval; // @synthesize startTimeInterval=_startTimeInterval; +@property _Bool hasBeenSkipped; // @synthesize hasBeenSkipped=_hasBeenSkipped; +@property unsigned long long unexpectedExceptionCount; // @synthesize unexpectedExceptionCount=_unexpectedExceptionCount; +@property unsigned long long failureCount; // @synthesize failureCount=_failureCount; +@property unsigned long long executionCount; // @synthesize executionCount=_executionCount; +@property(readonly) XCTest *test; // @synthesize test=_test; +- (void)_handleIssue:(id)arg1; +- (void)_recordIssue:(id)arg1; +- (void)recordIssue:(id)arg1; +- (void)recordExpectedFailure:(id)arg1; +- (void)recordSkipWithDescription:(id)arg1 sourceCodeContext:(id)arg2; +- (unsigned long long)expectedFailureCount; +@property(readonly) unsigned long long skipCount; +@property(readonly) _Bool hasSucceeded; +@property(readonly) unsigned long long testCaseCount; +@property(readonly) unsigned long long totalFailureCount; +- (void)stop; +- (void)start; +@property(readonly, copy) NSDate *stopDate; +@property(readonly, copy) NSDate *startDate; +@property(readonly) double testDuration; +@property(readonly) double totalDuration; +@property(readonly) XCTestObservationCenter *observationCenter; +- (id)description; +- (id)initWithTest:(id)arg1; +- (void)recordFailureWithDescription:(id)arg1 inFile:(id)arg2 atLine:(unsigned long long)arg3 expected:(_Bool)arg4; + +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/XCTestSuite.h b/BPXCTestWrapper/PrivateHeaders/XCTestSuite.h new file mode 100644 index 00000000..2cb75449 --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/XCTestSuite.h @@ -0,0 +1,70 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +#import "XCTest.h" + +typedef void (^CDUnknownBlockType)(void); +typedef void (*CDUnknownFunctionPointerType)(void); + +@class NSArray, NSDictionary, NSMutableArray, NSMutableDictionary, NSString, XCTTestIdentifier, XCTestConfiguration; + +@interface XCTestSuite : XCTest +{ + XCTTestIdentifier *_identifier; + NSString *_name; + NSMutableArray *_mutableTests; + XCTestConfiguration *_testConfiguration; + NSMutableDictionary *_mutableActivityAggregateStatistics; +} + ++ (NSArray *)allTests; + ++ (id)testClassSuitesForTestIdentifiers:(id)arg1 skippingTestIdentifiers:(id)arg2 randomNumberGenerator:(CDUnknownBlockType)arg3; ++ (id)testSuiteForTestConfiguration:(id)arg1; ++ (id)defaultTestSuite; ++ (id)testSuiteForTestCaseClass:(Class)arg1; ++ (id)testSuiteForTestCaseWithName:(id)arg1; ++ (id)testSuiteForBundlePath:(id)arg1; ++ (id)suiteForBundleCache; ++ (void)invalidateCache; ++ (id)_suiteForBundleCache; ++ (id)emptyTestSuiteNamedFromPath:(id)arg1; ++ (id)testSuiteWithName:(id)arg1; + +@property(readonly, copy) NSArray *tests; + +//- (void).cxx_destruct; +@property(readonly) NSMutableDictionary *mutableActivityAggregateStatistics; // @synthesize mutableActivityAggregateStatistics=_mutableActivityAggregateStatistics; +@property(retain) XCTestConfiguration *testConfiguration; // @synthesize testConfiguration=_testConfiguration; +@property(retain) NSMutableArray *mutableTests; // @synthesize mutableTests=_mutableTests; +@property(copy) NSString *name; // @synthesize name=_name; +- (id)_identifier; +- (id)_initWithTestConfiguration:(id)arg1; +- (void)_applyRandomExecutionOrderingWithGenerator:(CDUnknownBlockType)arg1; +- (void)_sortTestsUsingDefaultExecutionOrdering; +- (long long)defaultExecutionOrderCompare:(id)arg1; +@property(readonly) NSDictionary *activityAggregateStatistics; +- (void)_mergeActivityStatistics:(id)arg1; +- (void)runTestBasedOnRerunPolicy:(id)arg1 testRun:(id)arg2; +- (void)performTest:(id)arg1; +- (void)_performProtectedSectionForTest:(id)arg1 testSection:(CDUnknownBlockType)arg2; +- (void)_recordUnexpectedFailureForTestRun:(id)arg1 description:(id)arg2 exception:(id)arg3; +- (void)handleIssue:(id)arg1; +- (void)recordFailureWithDescription:(id)arg1 inFile:(id)arg2 atLine:(unsigned long long)arg3 expected:(_Bool)arg4; +- (Class)testRunClass; +- (Class)_requiredTestRunBaseClass; +- (_Bool)isEqual:(id)arg1; +- (unsigned long long)testCaseCount; +- (void)setTests:(id)arg1; +- (void)addTest:(id)arg1; +- (id)_testSuiteWithIdentifier:(id)arg1; +- (id)description; +- (id)initWithName:(id)arg1; +- (id)init; +- (void)removeTestsWithIdentifierInSet:(id)arg1; + +@end + diff --git a/BPXCTestWrapper/PrivateHeaders/XCTestSuiteRun.h b/BPXCTestWrapper/PrivateHeaders/XCTestSuiteRun.h new file mode 100644 index 00000000..56c3c18b --- /dev/null +++ b/BPXCTestWrapper/PrivateHeaders/XCTestSuiteRun.h @@ -0,0 +1,33 @@ +// +// Generated by class-dump 3.5 (64 bit) (Debug version compiled May 11 2021 09:30:43). +// +// Copyright (C) 1997-2019 Steve Nygard. +// + +#import "XCTestRun.h" + +@class NSArray, NSMutableArray; + +@interface XCTestSuiteRun : XCTestRun +{ + NSMutableArray *_mutableTestRuns; +} + +//- (void).cxx_destruct; +@property(readonly) NSMutableArray *mutableTestRuns; // @synthesize mutableTestRuns=_mutableTestRuns; +- (void)recordExpectedFailure:(id)arg1; +- (void)_handleIssue:(id)arg1; +- (double)testDuration; +- (unsigned long long)unexpectedExceptionCount; +- (unsigned long long)failureCount; +- (unsigned long long)expectedFailureCount; +- (unsigned long long)skipCount; +- (unsigned long long)executionCount; +- (void)addTestRun:(id)arg1; +@property(readonly, copy) NSArray *testRuns; +- (void)stop; +- (void)start; +- (id)initWithTest:(id)arg1; + +@end + diff --git a/BPXCTestWrapper/XCTestLog+Shim.h b/BPXCTestWrapper/XCTestLog+Shim.h new file mode 100644 index 00000000..a690790c --- /dev/null +++ b/BPXCTestWrapper/XCTestLog+Shim.h @@ -0,0 +1,12 @@ +//// +//// XCTestLog+Shim.h +//// Experiment +//// +//// Created by Lucas Throckmorton on 5/29/23. +//// +// +//#import "XCTestLog.h" +// +//@interface XCTestLog (Shim) +// +//@end diff --git a/BPXCTestWrapper/XCTestLog+Shim.m b/BPXCTestWrapper/XCTestLog+Shim.m new file mode 100644 index 00000000..06fc4622 --- /dev/null +++ b/BPXCTestWrapper/XCTestLog+Shim.m @@ -0,0 +1,111 @@ +//// +//// XCTestLog+Shim.m +//// Experiment +//// +//// Created by Lucas Throckmorton on 5/29/23. +//// +// +//#import "XCTestLog+Shim.h" +// +//#import +//#import +//#import "XCTestSuiteRun.h" +//#import "XCTestCase.h" +// +//#import "BPShimLogger.h" +// +//static NSString *swizzlePrefix = @"bpLogicTestShim"; +// +//#pragma mark - Private Interface +// +//@interface XCTestLog (Shim_Private) +// +//@property (nonatomic, strong, nullable) NSTimer *testSuiteTimer; +//@property (nonatomic, strong, nullable) NSTimeZone *testCaseTimer; +// +//@end +// +//#pragma mark - Implementation +// +//@implementation XCTestLog (Shim) +// +//#pragma mark - constants +// +//+ (NSArray *)swizzledTestLogMethodNames { +// return @[ +// @"testSuiteWillStart:", +// @"testSuiteDidFinish:", +// @"testCaseWillStart:", +// @"testCaseDidFinish:", +// @"testCaseDidFail:withDescription:inFile:atLine:", +// @"testCase:wasSkippedWithDescription:inFile:atLine:", +// ]; +//} +// +//#pragma mark - load +// +//+ (void)load { +// static dispatch_once_t onceToken; +// dispatch_once(&onceToken, ^{ +// Class class = [self class]; +// for (NSString *methodName in self.swizzledTestLogMethodNames) { +// NSString *newMethodName = [NSString stringWithFormat:@"%@_%@", swizzlePrefix, methodName]; +// SEL originalSelector = NSSelectorFromString(methodName); +// SEL swizzledSelector = NSSelectorFromString(newMethodName); +// +// Method originalMethod = class_getInstanceMethod(class, originalSelector); +// Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); +// +// method_exchangeImplementations(originalMethod, swizzledMethod); +// } +// }); +//} +// +//#pragma mark - XCTestLog Lifecycle +// +//// TODO: set up timer per test suite and per test case +// +//- (void)bpLogicTestShim_testSuiteWillStart:(XCTestSuite *)suite { +// [BPShimLogger.defaultLogger log:@"testSuiteWillStart: called"]; +// [self bpLogicTestShim_testSuiteWillStart:suite]; +//} +// +//- (void)bpLogicTestShim_testSuiteDidFinish:(XCTestSuite *)suite { +// [BPShimLogger.defaultLogger log:@"testSuiteDidFinish: called"]; +// [self bpLogicTestShim_testSuiteDidFinish:suite]; +//} +// +//- (void)bpLogicTestShim_testCaseWillStart:(XCTestCase *)testCase { +// [BPShimLogger.defaultLogger log:@"testCaseWillStart: called"]; +// [self bpLogicTestShim_testCaseWillStart:testCase]; +//} +// +//- (void)bpLogicTestShim_testCaseDidFinish:(XCTestCase *)testCase { +// [BPShimLogger.defaultLogger log:@"testCaseDidFinish: called"]; +// [self bpLogicTestShim_testCaseDidFinish:testCase]; +// +//} +// +//- (void)bpLogicTestShim_testCase:(XCTestCase *)testCase +// didFailWithDescription:(NSString *)description +// inFile:(NSString *)file +// atLine:(NSUInteger)line { +// [BPShimLogger.defaultLogger log:@"testCase:didFailWithDescription:inFile:atLine: called"]; +// [self bpLogicTestShim_testCase:testCase +// didFailWithDescription:description +// inFile:file +// atLine:line]; +//} +// +//- (void)bpLogicTestShim_testCase:(XCTestCase *)testCase +// wasSkippedWithDescription:(NSString *)description +// inFile:(NSString *)file +// atLine:(unsigned long long)line { +// [BPShimLogger.defaultLogger log:@"testCase:wasSkippedWithDescription:inFile:atLine: called"]; +// [self bpLogicTestShim_testCase:testCase +// wasSkippedWithDescription:description +// inFile:file +// atLine:line]; +//} +// +//@end diff --git a/Bluepill.xcworkspace/contents.xcworkspacedata b/Bluepill.xcworkspace/contents.xcworkspacedata index f5ad27e0..80b8f74f 100644 --- a/Bluepill.xcworkspace/contents.xcworkspacedata +++ b/Bluepill.xcworkspace/contents.xcworkspacedata @@ -1,6 +1,9 @@ + + diff --git a/bluepill/bluepill.xcodeproj/project.pbxproj b/bluepill/bluepill.xcodeproj/project.pbxproj index ecda15fc..0cacde31 100644 --- a/bluepill/bluepill.xcodeproj/project.pbxproj +++ b/bluepill/bluepill.xcodeproj/project.pbxproj @@ -40,6 +40,8 @@ FBA69FA72A45F889003BADBF /* BPLogicTestFixture_arm64.xctest in Resources */ = {isa = PBXBuildFile; fileRef = FBA69FA52A45F889003BADBF /* BPLogicTestFixture_arm64.xctest */; }; FBA69FA82A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest in Resources */ = {isa = PBXBuildFile; fileRef = FBA69FA62A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest */; }; FBF6B19D2A5F272500D7A64B /* BPLogicTestFixture_swift_x86_64.xctest in Resources */ = {isa = PBXBuildFile; fileRef = FBF6B19C2A5F272500D7A64B /* BPLogicTestFixture_swift_x86_64.xctest */; }; + FBF6B1B82A61CE4200D7A64B /* libBPXCTestWrapper.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FBF6B1B72A61CE4200D7A64B /* libBPXCTestWrapper.dylib */; }; + FBF6B1B92A61CE4200D7A64B /* libBPXCTestWrapper.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FBF6B1B72A61CE4200D7A64B /* libBPXCTestWrapper.dylib */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -91,6 +93,7 @@ FBA69FA52A45F889003BADBF /* BPLogicTestFixture_arm64.xctest */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = BPLogicTestFixture_arm64.xctest; sourceTree = ""; }; FBA69FA62A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = BPLogicTestFixture_x86_64.xctest; sourceTree = ""; }; FBF6B19C2A5F272500D7A64B /* BPLogicTestFixture_swift_x86_64.xctest */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = BPLogicTestFixture_swift_x86_64.xctest; sourceTree = ""; }; + FBF6B1B72A61CE4200D7A64B /* libBPXCTestWrapper.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libBPXCTestWrapper.dylib; path = "../../../../Library/Developer/Xcode/DerivedData/Bluepill-bzfbmrywvaiilwcxmhvmpqnjsiso/Build/Products/Debug/libBPXCTestWrapper.dylib"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -100,6 +103,7 @@ files = ( C4D6861A2267ABEF007D4237 /* bplib.framework in Frameworks */, B3109F792151F72F00B9309C /* CoreSimulator.framework in Frameworks */, + FBF6B1B92A61CE4200D7A64B /* libBPXCTestWrapper.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -108,6 +112,7 @@ buildActionMask = 2147483647; files = ( C45F9E82267E8A8800969CC3 /* bplib.framework in Frameworks */, + FBF6B1B82A61CE4200D7A64B /* libBPXCTestWrapper.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -117,6 +122,7 @@ B3380AED2150BD8600752E1B /* Frameworks */ = { isa = PBXGroup; children = ( + FBF6B1B72A61CE4200D7A64B /* libBPXCTestWrapper.dylib */, C4D686192267ABEF007D4237 /* bplib.framework */, B3380AEE2150BD8700752E1B /* CoreSimulator.framework */, BA1896B821791A14000CEC36 /* XCTest.framework */, @@ -521,6 +527,10 @@ DEVELOPER_PRIVATE_FRAMEWORKS_DIR = ""; DEVELOPMENT_TEAM = 57Y47U492U; HEADER_SEARCH_PATHS = "\"$(SRCROOT)/..\""; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); MACOSX_DEPLOYMENT_TARGET = 10.15; OTHER_LDFLAGS = ( "-weak_framework", @@ -543,6 +553,10 @@ DEVELOPER_PRIVATE_FRAMEWORKS_DIR = ""; DEVELOPMENT_TEAM = 57Y47U492U; HEADER_SEARCH_PATHS = "\"$(SRCROOT)/..\""; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); MACOSX_DEPLOYMENT_TARGET = 10.15; OTHER_LDFLAGS = ( "-weak_framework", diff --git a/bluepill/bluepill.xcodeproj/xcshareddata/xcschemes/bluepill.xcscheme b/bluepill/bluepill.xcodeproj/xcshareddata/xcschemes/bluepill.xcscheme index 58639eb0..ce415051 100644 --- a/bluepill/bluepill.xcodeproj/xcshareddata/xcschemes/bluepill.xcscheme +++ b/bluepill/bluepill.xcodeproj/xcshareddata/xcschemes/bluepill.xcscheme @@ -62,6 +62,20 @@ ReferencedContainer = "container:../bp/bp.xcodeproj"> + + + + tE-94Wz%A(gznSk?M@Mp? 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 g1DXNNfM!55pc&8%Xa+O`ngPv#WvRz83m2^+U; zUAgoH)SWNj#>7Np-1!8yh?=-N!~cKhKW`mx9hWo0@W|-c_{8ME)bvbrZhk1ck`z5F zZ_sVcq=%9sOB+G8FDVDQou@~wNnM&I;U8g!Iu&MHD%UT1%*bzPdWl&yCAYd0Nl6=; zTd*Coew(S+$In%+@5i}XrMl;8IU~TX*k+j-)KNKZi&@O{%#{3#{Gw`Mg-;5e9S3Sq_xoLWT&Uu#`hxXaOywb+n6)Q4L+7Cb~iQ=mii&U=fx< sh7z2?1zf>3+`$t(!z;YQ2YliX?#4Yhj3;pvuj3S^xQH3H8V@+~1&c79Y5)KL delta 445 zcmaFP`kZxwih{V5w2Yj*f})awvWlvjdc1(JQ)y;Sif>|aMrK~BfAGWz<;hBn!lLm4 zLe6eZrFkhish%mRc_o=?nW;sS7!^2UV8RpEgw^xIIAN(p#hLkeQ7$3Q`MCv|IjKeQ z0{ku^0Xc~!Y57IDU_0Uve80rp)OZ0wgaAT+u!}cXb)-uOkO@*4?h+D_oS%{!1Es;z z@d5(And*6&xuv=O!BE|Cp`I?OWtqvTZi%^>IhA2XMy^I?hC!)$lUFgyO%7rbRt`4P zGYE0=bMXl_)H7Hp3v^>3Vxj8P}5#>9x?{W3KzR&mr?Nl?^k3toaWr5xlWy`=>&6F#O?dO^S;mfo@RHm`+7zgiH?rN#wUlSre|j77lw1|NwI1d zO|qj~*9F@s{2n#FD1C{vs4Dy=Oqsj{h8 zN{(mMAF}m&tfnyo9p}acF{%~4V20Rr$8xDjJdI(tsZFh_wX6K1e=VsZu`9GhiI>l$ zByvU#lGH6~UTRGLUsT(dYnN^{(U?dmsQr{_p9V!))_tGar)lSu8Y=NCe((|MmK~e; zL5ZNdUI9KHw9+a2M{ueK?B8v4m4t!#Xx_8KIje9Qy{}c%noA delta 487 zcmYL^yGz4R6vpqlASeip77={LT3>^qsYL|`F@{35t74I(zK_HA(e}}H5#@XP`Ui%F`H=`e8XX&-(7CWw zrB*Rxm}P2{HT809v=)JIkIr>3C#9-gv`DH*>$6rDSqQO_DSiwT^P}cyd0zLF{@-MKOGz+WTo|pPCx+D}F%JDM3Q-NP8fnI wSb!BM00kdT;0&(e79QXcUf~@+;S0Xu2P2H}1fIr=SjIb8$0dA#S?vY$e^lm}-~a#s diff --git a/bluepill/tests/Resource Files/BPLogicTestFixture_x86_64.xctest/Info.plist b/bluepill/tests/Resource Files/BPLogicTestFixture_x86_64.xctest/Info.plist index 0bfef9a060dd6e621e09a4a42dca270d20a82d6c..78869ce642c5f936f755545a82e914fb44121fb7 100644 GIT binary patch delta 499 zcmZ9HOD_Xa7>3Vxq6roZBJR}Ps_CiGTFRIpxgd37(}J zbX_&+o+wLVCa88qc~`S_y5E>o#YxinBg|2!%xp{HdIXOd`kJa0nMF6GMt3C2SwnRT zwqsWBa@FeSg~E0JIFl8sd9Io_0_?JFmY6{uh2z$k#Z1rKkbcp>mb6YSmlYUwj<(Yx zJ!KkAsU|bd6|U>=RMU6R4BbegaGinR;yX-pG$3SG?PI3NwCtu>#q}JjR2Jh(ocG^h zrJ`-ovLD6&z;;MH5|s&0qT8{kkRV&s+9bJHEE$UvpZ!lP3+v*lL?Y38I9O%_k`$0y z1C->WYHfJB#pgw8kI|aMrK~BfAGWz<;hBn!lLm4 zLe6eZrFkhish%mRc_o=?nW;sS7!^2UV8RpEgw^xIIAN(p#hLkeQ7$3Q`MCv|IjKeQ z0{ku^0Xc~!Y57IDU_0Uve80rp)OZ0wgaAT+u!}cXb)-uOkO@*4?h+D_oS%{!1Es;z z@d5(And*6&xuv=O!BE|Cp`I?OWtqvTZi%^>IhA2XMy^I?hC!)$lUFgyPYz}hRt`4P zGYE0=bMXl_)H7Hp3v^>Yks!gzu48RIKP>GzDE8NV{IGjTHMF_|#AF?lm3 RGNm!)G8HkEOg_P+005Nok5>Qy diff --git a/bp/bp.xcodeproj/project.pbxproj b/bp/bp.xcodeproj/project.pbxproj index d8c461cc..1f9e69f0 100644 --- a/bp/bp.xcodeproj/project.pbxproj +++ b/bp/bp.xcodeproj/project.pbxproj @@ -114,6 +114,9 @@ FBD772BF2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD772BE2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m */; }; FBD772C12A3452240098CEFD /* BPTestUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FBBBD8EE29A06AF6002B9115 /* BPTestUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; FBD772C22A3453010098CEFD /* BPTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */; }; + FBF6B1BB2A61CE4800D7A64B /* libBPXCTestWrapper.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FBF6B1BA2A61CE4800D7A64B /* libBPXCTestWrapper.dylib */; }; + FBF6B1BC2A61CE4800D7A64B /* libBPXCTestWrapper.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FBF6B1BA2A61CE4800D7A64B /* libBPXCTestWrapper.dylib */; }; + FBF6B1BD2A61CE4800D7A64B /* libBPXCTestWrapper.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FBF6B1BA2A61CE4800D7A64B /* libBPXCTestWrapper.dylib */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -176,7 +179,6 @@ 7A7901821D5CB679004D4325 /* bp */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = bp; sourceTree = BUILT_PRODUCTS_DIR; }; 7A7901851D5CB679004D4325 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 7A79018C1D5CF113004D4325 /* bp.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = bp.xcconfig; sourceTree = ""; }; - 7A79018E1D5D136F004D4325 /* CoreSimulator.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreSimulator.framework; path = Library/PrivateFrameworks/CoreSimulator.framework; sourceTree = DEVELOPER_DIR; }; 7A7D66001DB679820015C937 /* BPStats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPStats.h; sourceTree = ""; }; 7A7D66011DB679820015C937 /* BPStats.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPStats.m; sourceTree = ""; }; 7A7E7BAE1DF2066B007928F3 /* BPHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPHandler.h; sourceTree = ""; }; @@ -216,7 +218,6 @@ BA180A081DBB00FA00D7D130 /* BPUtilsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPUtilsTests.m; sourceTree = ""; }; BA180A0B1DBB011200D7D130 /* testConfig.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = testConfig.json; sourceTree = ""; }; BA180A131DBB088100D7D130 /* testScheme.xcscheme */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = testScheme.xcscheme; sourceTree = ""; }; - BA1896B321791683000CEC36 /* CoreSimulator */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = CoreSimulator; path = ../../../../../Library/Developer/PrivateFrameworks/CoreSimulator.framework/Versions/A/CoreSimulator; sourceTree = ""; }; BA1896B6217916F8000CEC36 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; BA1949341E4AF82F00881887 /* BPTMDRunnerConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPTMDRunnerConnection.h; sourceTree = ""; }; BA1949351E4AF82F00881887 /* BPTMDRunnerConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPTMDRunnerConnection.m; sourceTree = ""; }; @@ -330,12 +331,10 @@ C4FAC2941E5E67ED00ACC5D9 /* testConfig-busted.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "testConfig-busted.json"; sourceTree = ""; }; C94DEF7F8BCA7AB3C9114467 /* simulator-preferences.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = "simulator-preferences.plist"; sourceTree = ""; }; FB1C695529A6E58500BFDFB4 /* BluepillUnhostedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BluepillUnhostedTests.m; sourceTree = ""; }; - FB9F19302A32E97B00C894D3 /* libBPXCTestWrapper.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libBPXCTestWrapper.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPTestUtils.m; sourceTree = ""; }; FBBBD8EE29A06AF6002B9115 /* BPTestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPTestUtils.h; sourceTree = ""; }; - FBD772AA2A32F2300098CEFD /* BPTestCaseInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BPTestCaseInfo.m; path = ../BPXCTestWrapper/BPTestCaseInfo.m; sourceTree = ""; }; - FBD772AB2A32F2300098CEFD /* BPTestCaseInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BPTestCaseInfo.h; path = ../BPXCTestWrapper/BPTestCaseInfo.h; sourceTree = ""; }; FBD772BE2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BluepillUnhostedBatchingTests.m; sourceTree = ""; }; + FBF6B1BA2A61CE4800D7A64B /* libBPXCTestWrapper.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libBPXCTestWrapper.dylib; path = "../../../../Library/Developer/Xcode/DerivedData/Bluepill-bzfbmrywvaiilwcxmhvmpqnjsiso/Build/Products/Debug/libBPXCTestWrapper.dylib"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -347,6 +346,7 @@ B324B91D1F280AD100AAE2BC /* CoreSimulator.framework in Frameworks */, 7AB913001D5E209800621608 /* AppKit.framework in Frameworks */, 7AB913011D5E209800621608 /* Foundation.framework in Frameworks */, + FBF6B1BB2A61CE4800D7A64B /* libBPXCTestWrapper.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -355,6 +355,7 @@ buildActionMask = 2147483647; files = ( BA1896B5217916A5000CEC36 /* CoreSimulator.framework in Frameworks */, + FBF6B1BD2A61CE4800D7A64B /* libBPXCTestWrapper.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -362,6 +363,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + FBF6B1BC2A61CE4800D7A64B /* libBPXCTestWrapper.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -457,16 +459,12 @@ 7A79018D1D5D136F004D4325 /* Frameworks */ = { isa = PBXGroup; children = ( - FBD772AB2A32F2300098CEFD /* BPTestCaseInfo.h */, - FBD772AA2A32F2300098CEFD /* BPTestCaseInfo.m */, - FB9F19302A32E97B00C894D3 /* libBPXCTestWrapper.dylib */, + FBF6B1BA2A61CE4800D7A64B /* libBPXCTestWrapper.dylib */, C47411F7264F4C3F000F84D1 /* XcodeKit.framework */, BA1896B6217916F8000CEC36 /* XCTest.framework */, - BA1896B321791683000CEC36 /* CoreSimulator */, B324B91C1F280AD100AAE2BC /* CoreSimulator.framework */, BA1809BC1DB89B8B00D7D130 /* AppKit.framework */, 7AB912FE1D5E209800621608 /* AppKit.framework */, - 7A79018E1D5D136F004D4325 /* CoreSimulator.framework */, 7A58D2E51D62689B002F6B6D /* DVTFoundation.framework */, 7AB912FF1D5E209800621608 /* Foundation.framework */, ); @@ -1079,6 +1077,10 @@ "$(SRCROOT)/src", ); INFOPLIST_FILE = "$(SRCROOT)/src/Info.plist"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); MACOSX_DEPLOYMENT_TARGET = 11.0; OTHER_CFLAGS = "-DBP_USE_PRIVATE_FRAMEWORKS"; OTHER_LDFLAGS = ( @@ -1118,6 +1120,10 @@ "$(SRCROOT)/src", ); INFOPLIST_FILE = "$(SRCROOT)/src/Info.plist"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); MACOSX_DEPLOYMENT_TARGET = 11.0; OTHER_CFLAGS = "-DBP_USE_PRIVATE_FRAMEWORKS"; OTHER_LDFLAGS = ( @@ -1163,6 +1169,10 @@ "$(SRCROOT)/src", ); INFOPLIST_FILE = "$(SRCROOT)/src/Info.plist"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 11.0; OTHER_LDFLAGS = ""; @@ -1200,6 +1210,10 @@ "$(SRCROOT)/src", ); INFOPLIST_FILE = "$(SRCROOT)/src/Info.plist"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 11.0; OTHER_LDFLAGS = ""; diff --git a/bp/bp.xcodeproj/xcshareddata/xcschemes/bp.xcscheme b/bp/bp.xcodeproj/xcshareddata/xcschemes/bp.xcscheme index 0ea5c1cc..94651b55 100644 --- a/bp/bp.xcodeproj/xcshareddata/xcschemes/bp.xcscheme +++ b/bp/bp.xcodeproj/xcshareddata/xcschemes/bp.xcscheme @@ -20,6 +20,20 @@ ReferencedContainer = "container:bp.xcodeproj"> + + + + tE-94Wz%A(gznSk?M@Mp? zw|&3%$8QcC&HU$E=9~X4XMCh{^6ba&etM`7dHF(!LvS98bN^H!wuwUIgg61`E(qQ3 zX4mRwS(N!vnF$5s0uKJ#_=D}NV2`i&EG2k~{h8SGXN{H0I6iN{ z6O9Hvorc}t`HPihBReoL!;H35nWeqtE~~vavcFHbktY5e{$kzXY=7UYP?j6mfr%&f zX9Q#ZSitB+nxlV@SfVUfGJhsE{n=BuoBX(a1`jYhf9`XY#VR&5vE7e}-R^jBqd(}w zFl-B<(C%*+`>SIE!uCEjeNjsDb`))KyKAeOtK2m!8k*GXtt2UqO8J?UQuokFbC*^C zx4S)LnYI)98-lJpDl^A1%`cwPQjBrjW*x}(_=aFlhm#ve_~7$G&NDfVX``5i;&)?~3H>hjv@lCP$Bg{?D@M8k% z*Dfe1KV~V?Ud~r?YQq#GomlJTVb1?h5Q`IziTqWP5gomstDR9J5HZ^1{GxFuE#*h$ z7L_01`9;`tO%jZ^T>T*Xl|0(x8`-M-g;gR5Tm7Lpm%nH{Y(#STtK6m>nf*%mbNKUb z@TdjA9$y{%o5ArBw)ta;O1M9Ko|s1#*!`_zf0fK1;e-1dca_uMj&G@Y*QrDhKA1lm zceM7n-Sx{FE^yT@Q>%#(L+qfJQ^sLlG$>pSSt=ZX&q|!ciz-(dCGlvaBoOj?0wn=| zE1lJit81E#Xlzx)6Go>M`?^sm7GNaGjva!Z$wHjTxbiU}4!|eP9rC^UaUp(-bj}`p z9Iy5vjmXk*H=lF z;*|3&50T=e7D{gO7UX{wdF#NO2D}I-`J&i`_@s$GDiw+|Po%qKsc9K4SsIE5F(~|@pv7OhkZM(aGvnEd z(mbL1NA(P?xW!f6A<`;H4(w0nt3XJ-e z_+EtZ;6!>lV;UcHoQ#v?rN0N~%cw*jngPv#W zGy|Fe&46Y=GoTsJ4E&=r&{Eg;X5E%|oOOLIV|9J|>k@G!JqPOgT>I;WT}cYRuD>Ul zY(55=L_As7UvWFW6Brc9*N#@Ac|(0qB+E#=yfq?Wska@8GM#R5M#gAn9P|h|Cs5*wwurUe{4I4{dDGOZ(PpH#7@#jhe>d=0tY1c4uJ$zw>;Lk_EEEwB!8RU>7}Uq zT6QQ3k>nTx7fJ^h3#U*9v5n!2Axh^;Os(iav&;5fCPeZOls(~k7Ux4ae^6{v7E-v<7-J;a3qvcBWTqCbwyoY{3`U(Xfj3m2?@rM@25dB`KAOt z19h7o>qT&D_x%eBrk`4A&LTOrElh%9D_u zj#i#PRcYE2xREjS%b4E7x0@8x?~as}jw(-M@KmHcy$H*cV>hWKdd5z6XBsuumDyyPd>_Xe+xV`fda_ac1tm{$^sgd+(9q4mW>uG%VDZQCeZvpGg zQK7?E3y63aYtYE}ijpXM5GEcsW9j(wP>zS>46~72TQ`H=P zh5)S>+*}?HvVn0lmKKT1Db__ez5Wsj(CKSI8%US3ZvY>C= z87?KvPZ+!msPTei`SzS+rcru|Z_l`XoUPnTGBG%=Uqd~83V!ZL1NvwZ(&!m>ji4O} ztRitSB`uf$xX#GIS0-RH@5%ap*HFr_*P$#&@xxGvcc8C^(ETIH=cyop0yz*X>Xn-@ zRJmAIc?TQbtPFR71JqHrcQwUxEekhG;V@cvn_8sG_QLpn`T<7OuF3eEH&oU4aLRER z=_h(_OF6!-a&T9+NxG_$1r_(e4r?6MzlpN_j+JOLUAbm*UB{_F2GQnQr7{+*MA;tc z<5dazC(ql&))od39Sp5?8r~r*AEi z9V^+sMcO#o=2&H;u4uG5xM65P3MN;(hOctn_a|KEGA}b&B9DOS)vo)-ua;5iyPyrM zsnJkzHf7btWeR2c9UsAlh`*ioU=;*SL469FW;iyw@mAQ&VNK%d; zN;B7iVb^XN2m9uz=3s@OL0b0ni%4m?R2oiJk#8(stW14T*M}KFSF?GtUJ1sMvs4h+ z0--DRjbW8(OZUg7cM4HeUEd8|sZ>h#%Dpm|IN|}hH#!VckY@FmWZaxPGz)$SN9d+D zGzhV!WNdem4ch%+1K+rVtKi@+ zDQi}iSEU>gl^cQRp%&sq?)y-0(YV<_(!u#e+2K;hpRRRP zy(UulmE6V+Cm%nAywT;lVuO-DB;|Lq{Krzht&ystKVK3RtCZRx)av?M_RDoH<=Djf zaj8$y)v4&{_xmYFtBOaKVe^!7bgKgLn{oss=%MwnTh@Li708|cQTTltx|c*hm7*CW z!T_130v}}IQK$wJ=aH1-Kh-8}5Iw#~hEQ=6WXT7yQ3fNj2!C7M3plw4J~*^V2fd^mZn@+Rf7*Gr{l*%d3PwJ{mO15}oBTt}e4=Qg>- zw!&`T88lDnkA}W&PH9$gI7N7p@+pfD;W5?-N5>4-KK-YMPV zkC{YSnbg0Iy>3xnrJD3QLwRk#kf`u0uT++D$o*k|&kjk&D%kDAUs?#M;#GKrULpe+pEOWYSG!MX^7Qu%$y)xPfV$f4mD37; zZ2g!recKLvDp!P?;GuZ~A)vOjpBR zig0|tlc+dKwWJ!2M@ypUtdzr${ws==$SrxfkjgNOT{03{Uxz@z-HoJQ#HdnJ~ekR&S+zNsEncqbMF(iy8?wD$Xvzs z%KPKe`aZV4gRJGsPgkrHr0n}FyH3jJZ~BUL%65~KU&->2s(D03wj1_qr2G<=uTXMq z>RtO&j!r7YmObV0s@!PGv4(Q}6$9w<bZ%F{1bkk&A3{hY0_?@j(_+|xfxPd{Rv+m)we%5guH zZ0Vt!6Ub3^znH$wN!k9M{c`ASAZ7E93_k(6gTlmp2Nk2~1EB9=%F@6~lwC{Neuo?V zRG%m)h1>c*7tIyd&MNvlcQ`piRX;$qCR{tv>DyuVB87;q=m9ABGRz1-ny7eMI&XpV z<@=#{uT)I*>`eYM;!NLDlKb^}spM=-t+kJu`}h8eH!)|aB?#*Lr&6gTdKxpql$(Fg z^jG{6tghV`htxVB>~dT-5ZY{k{ab4GB>6d6ejX`53+3mD^7AD5d5ZjW%FnOL&$;rm zSbmnu&uaNuD?gXYPuf$aW}h!V>+v}KZLHCdKLOctd$KFe&DpCFn@}v9V59RsL z`NbbnsqHAg1LY&g*CYRXkn0WRBtvaQHp40-I2utUB9`2!!OR|CovrwdxKbDDx91k4P}m4MWB5rvYn>L&@=-(~HiUwP zAiWw-RMsry&$XQ%r#Fs=FM_c^w=*8>@K8-Yr>e$DPkA{vb|6-#5sY^l5&Tj;*ewp?R5+G{V)YrO~H8iXlV1m${n6Dt{m_?!<65xAO~-K$nsH8#3d;t7Wp=Pj$j^NuL1ZfU5kcjZ!&;+m=^mwQD^b7M<0 zbm~Lx?TEQ0<_{p`#%5+<89mX}&{Pxh(c^phkfZj)xvm3xwLs=|gqB zpvM!1dU9%&Ki07{6zTNDpx_C%$MI}YReLZLjrqM)>GJfD@CP^GnF{*Nzrdq8hBp?9 zNKfRVhMxVS@lu{1L*0lJZ!5VEB$b} z%9#yKP4ZdFnR9FAHq5=qVwXdhCP(CuX8JZqu3@$~x-3{NXB0KL5womZcp}~o{|3WX zi${H^bS1_%lN>R=7WdObo=upU5krlbay+nvxi4rM*U32pdRImKG$E>D$|orBM*Lyf z&l@2!nnO!z)_Y>1PCuTh33QiB=3!#G^9DjugOr4t^63E6>jF7Pm#Nv9-6r%1hN{mK z@wG&aNUhQ4i3c!nBSvRv1CJYXBt*M|-i}Bp=)b}cEzL_8DgLQPZR5)miFmpNA|?i- zAPbN}Q)xqSE~ZEocsS5&MA5aF*G6!IAMK*2N@WeIw_WK{Oau{VS`F-dE zf1BTkoKr$FsHo*~N!eLz7M#_xVDbEx;&V#4j2%GH+u`vCi#%TH(xUcAC?1ZEJCMT< zo<7S}2`ipcqM|#eM8>{kYPe-mUfy71>FjC8<&JgQ;g;x1Pn&jJ?ubf{$WTX2V^e9e`=JXg&kLA?C>2aJM&*==AI(zBf z6CJ&0(Wg@=JM~-{@VNX2meXImblC0hWVvc@w){SptM+EgA7;7YAzS`5%M~Bl@>f`{ zc*vH&%km->epdWnT0{b;e3t{|?CqP$@_8i0VV5_vT=jdld=1N0zh=wZSk8~DXZgQ{ z<;Sb)v*deNK6e6n-eM9s#jFYBUt#(GVEI+sC{ri~Ii2$@g}XR4pM#~+O8U@;WGy|Fe&46Y=GoTsJ3}^;41DXNNfM!55pc&8%Xa+O` zngPv#WGy|Fe&46Y=GoTsJ3}^;41DXNNfM!55 zpc(i(F|Y~s&f(JU*uCoO#lqcLAvP0i;!wpK!Y1GjcN^r*{y9%1Nirff=*dRA3u?}P&~}#c`Q-ScjF~vd~AatS9{mU=f(@g_JyQaC`NlJ zP1_N&9!>yyM}qoyo(sj(9M=b2Kgo!TMt<)6r3vJ#S^m2Wd8zbG_Vj)P@^uu;-{-WH z)9GhJ2(7aj7fDW>Qu#iD zUS)KUPtu@x=(HZn&w#b)p!td7*~Iaz`zzy7e%2i{2CMKv<~@22l@^Ku9*6rG{}ZPX z?xz99H*=cLsoB3DvEHLxzKs1p!R23KY|f9Ftk=NuO0NGHF8|+L{v=~_oUCO(w=-`a zaB9x4Hy96bdLyTW?EhxQ<~Y2M{TnR*2B!-+eSzgooW9NW*Kr&>IlYO~Z?N7HPJhb! z?TqW$@7s)@<941PJEySMeBZ-~jK9RG`96q~7@y4PDV(0lsgu($bBb50;3(`h-y31R zM?%$8IXd+mC4tT)Q*#YB3e za^4nxX1)8_pBa~#+Us?0rhw2YeqY8!^>(nH>Bmg%{svjE@iY7#ka|w4Tpk^=|DBA4 z80e+!I{G;1v5uv?dk?Cng_;4)fM!55pc&8%Xa+O`ngPv#WGy|Fe&46Y=GoTsJ3}^;41DXNNfM!55pc&8%Xa+O`ngPv#W{-GFfBAO10C{Kv5sDzL_CUZ*9vzBt668U^<;UO9SXgbW( z<-Me(jy#l3nuO>2@fhhOJa=BuD#YZgao&RSDV+OpqVoJ{IA`I!4CfU%e~9xDobThD zJ4xi%;cUhE9h{>$o%z7|V)D20MZt~vV$$n4r%e`<>TrHzvMAVz^DTIG7Kno80+H8S zAST^aAoAbFiP-a(B7S#6Q+=qNlBQV1A8cP533b*4Jke-oQIiocys=P3xZO~#ibOo! zrEYgL7KwXfROapSxIHnW%P&zh=J8(c_I6zEZu9s9Bu|&L$2}3BaQgzGun`n)WfE## z?{#|ufsj|YRX&c&P^TwqtTbZrNbn+0Aa1a*Guqx{1mQ%gGHQy4jmUg13x#8DuWE(V zA?%M{;1Bv%v~lak%OgfK918lP&7pK7rI71qmk}f8@gras5vWQBIZ=6b@XC+H)R(Oj zHDZks@Dqu3yMv*iCl>1Td)?kpxSQ*V5#+A+8@_5YC8ZhLTvI$q$G#k-aQ`{fRu4-ca8VnknqlPB%={2I! zWx=)(c#@rHbcVsN*W>Ll)t9@PtJ37GXkCvnpXG^*&B^T+ujWk_S0ymP^D&d!F^5`W z{y-JF$mqs;bbRz*Bvbhp`eQL$#SI3N|waH<={G;4Uk zTT$=W)mim^BctA(d0g=>XphR8do4BJm6uiZ3s4-9Dzc-5J7#zywV{nc@m^lO7&2tT z$6V{y4NV%%-!W-_p0kwxYaGiT$lpJ;aHycr=@cula2|;TacrjIsf}@^dLUkGX1s&( zU8eplrT-%10a}o8e8hP0G=)zrQ2uw&%Q$z#_Jf5 zG9F-jfbkgPA}pQMzM(UezgEVLMG9ZXcx1l9+Zhj(DSSKQ9gH7jJjD0|#$$}<9E$qD zfqEZ}5Dkpg`&xvEF;?$25#lDs>ir}_Ji=JLA4G`vflryeG(r?HZrrBqTNtbN&!E|akM9-g zeK~R~AbfN|=i~d3`%HlJIUD{Ra9e(!=-zy%R3p=3%lLofaJ&n{f=NCL=Q^Bp5N@!^ z$=;kVFSEUgP5TR_b=uyPe+d1rSo-tCX$qVDc{Z?=L#&v1wmwv$x90 z-dz8#WP1~v_D-9ElR>$l3u-dtbrXL}Qy_V)U%aN6dzQV8g>Uyw`>c5WCeMwck3wose7S_bMB12hKIW z$GCr7NAvo|nEcaJ9it2mN*eHC>Jz5^F|VIju)T>%PWA(o!9fYxTjgYLUT=TK_9iBM zvR{wuHXWwDDSr+6)Su?{MI58Bd3|vLJEf)isI+17XXWow$gTW+*M@Hd&c)x*eC0oM ztm4naX8hkMCN}Mdusx*1w71I1-rSFDW_uHx_ErPB3V`iRitM_T8<2U8a(5LZZ?mxCLHuoPt1vcxZ5MH)n@^5WFb<=d{sP}UTLGR%rJjnYM zdd(MM_1-NZRx?)b;}YVVjMaOz&@YVD`?7?1n6Y|~l@PyStlmQ<1a3Wu={U{tbsX~e zpGS`nC);qT4KG!gXB+ktHhI*BueITwHv1!1*A~U*dcQ=dW-+hx2)y zdvLyp^Cg_4IA6y38qQzid>!W-ILC1Q2B+26)4-;`XOXx2d;w*4zptR|Rh$#~he$YW zw*jgfGyc}kyr)s!nDHmLU8w#bSAY5EmSz1-uGBLB{?8FWrb3P&G8M7|$tBMYX8eZ0 zUwre>cIzkCJ)DN732q`Cvgnk9EjfybYdCqrB6Ib%cB}t9;q?+d_1A-z?1K6E}%NK0|s%dhlu|Qt;A! zcM2j$%(zjV4mXE9-GUrK7%#WBFpH74cpyL(N5dZP1hp@qzLDDFn9kr9k{8WDUwVp5-Nn_bhyMySE<^ZV*U z9()B74#aWGxz_J%2?k_=+kIaBit4I*H;oVX!m_e6%jCFH0ofOGXADhI?QjJ*`0<58 zrxA={J+%guWfY3X!toe2+HT}lSE^b39RpUgx)rre?#$7S8}M>T&DJsr;@kABwXC73 zxvHVYF2!PZa z%c`pDQDbg@GQLK4{j#R!2{bb3$W>bwsX2Nw!$}VL1U~G8*<>MyyKGfCAeVuFEHaDL z_l>mt@XlZYwe%8qaJ`Jh%nZ`(OzSu-p^ZnhBoOPsS6r8OmiR-_l2&|K8ukYQ=}dKF z#yBq#nFF=BQ`r5~`&%QPNOwuCvB97(Ya=DA>EZw!!PkPmT2IVVQqA=gwO-NI+8OEI zxWVK12R3@UIy*LWhQAhEAN5B=CDn0%z*n*?7&9WBhR=_MFj^c8;mcT~tHc&pajVp= zHCp5CMgDL{C}>3eo$&y^0*y?dt9EeFqF^Y}=?Ro{En4VaxB$A={c&+9=e3pBYO5x# z@a83#m#M6QWYsr&CS&+yD#$^Tc9OkDNCo8}OK9(v&)2(+@z#LfOPy?;U0=XX&S+jp zZ<3sUk4+NKlh3?VS}t7U7Zc12xkIpDyezs(<%@KY`}bId6mnM=eWpa%hT65tL(pbi4&dp1=bgC z$jC+`&TfY^R;aISE!GcA;#q)e#PibkX(R!x0bYbhF z($X^*8y?@{#YWrW`K4lew{xK34&%sp*EI#_?-*R!u)g=9;S+yUbl|zOZaKc@qi>B@ zGC`)cz2$#S`OU@WzjgSlzwPbni2QbOk#FO|8;)D~VECCkPyg4oKl$d)yHheh|Ru`nce$Ush*!Ic;iMqbIw{~>*-TrcNyL02>vwnEq)}O7vx$1uJVI|kp zpK{9&g2#S;^S$q$c;ff z`sCWP22TI4UFSBP-~H*5_d-w2=qkJMXEUz;-gA5R?tJ63>i=uyYqn2){*_hBH=nV( z(Eq`Vg_m9S;ak%xo#(ILF?!{LcV19a^Vj<=Hma%{=2$&WoV^LGy|Fe&46Y=GoTsJ3}^;41DXNNfM!55 zpc&8%Xa+O`ngPv#WGy|Fe&46Y=GoTsJ3}^;4 g1DXNNfM!55pc&8%Xa+O`ngPv#W +#import @interface BPSimulator() @@ -441,6 +443,7 @@ - (BOOL)uninstallApplicationWithError:(NSError *__autoreleasing *)errPtr { - (void)executeLogicTestsWithParser:(BPTreeParser *)parser onSpawn:(void (^)(NSError *, pid_t))spawnBlock andCompletion:(void (^)(NSError *, pid_t))completionBlock { + [self collectTestSuiteInfo]; /* Grab all test cases so that we can: 1) create a timeout for the full test execution @@ -542,23 +545,7 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser // Check the location where the status code is stored; // Handle error if there is a signal or non-zero exit code. - NSError *error; - if (WIFSIGNALED(stat_loc)) { - int signalCode = WTERMSIG(stat_loc); - // Ignore if the process was killed -- this occurs when we're killing - // a timed-out test, and shouldn't be treated as a crash. - if (signalCode != SIGKILL) { - [BPUtils printInfo:DEBUGINFO withString: @"Spawned XCTest execution failed with signal code: %@", @(signalCode)]; - error = [BPUtils errorWithSignalCode:signalCode]; - } - } else { - // A non-zero exit code could mean a failed test or something more serious, but we can't tell the difference here. - // The best we can do is log the error code as debug info. - int exitCode = WEXITSTATUS(stat_loc); - if (exitCode) { - [BPUtils printInfo:DEBUGINFO withString: @"Spawned XCTest execution failed with error code: %@", @(exitCode)]; - } - } + NSError *error = [BPSimulator errorFromStatusLocation:stat_loc]; if (error) { [blockSelf signalCloseToParser:parser fileHandle:outputFileHandle]; } @@ -573,6 +560,81 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser }]; } +- (void)collectTestSuiteInfo { + NSString *xctestPath = self.config.testBundlePath; + NSArray *arguments = @[ + self.config.xctestBinaryPath, + @"-XCTest", + @"All", + xctestPath, + ]; + + NSString *testSuiteInfoOutputPath = [SimulatorHelper makeTestWrapperOutputFileOnDevice:self.device]; + // Intercept stdout, stderr and post as simulator-output events + NSString *simStdoutPath = [SimulatorHelper makeStdoutFileOnDevice:self.device]; + NSString *simStdoutRelativePath = [simStdoutPath substringFromIndex:self.device.dataPath.length]; + + // Environment + NSMutableDictionary *environment = [[SimulatorHelper logicTestEnvironmentWithConfig:self.config stdoutRelativePath:simStdoutRelativePath] mutableCopy]; + + environment[@"DYLD_INSERT_LIBRARIES"] = [BPUtils findBPXCTestWrapperDYLIB]; + environment[BPXCTestWrapperConstants.outputPathEnvironmentKey] = testSuiteInfoOutputPath; + + NSFileHandle *outputFileHandle = [NSFileHandle fileHandleForWritingAtPath:simStdoutPath]; + NSNumber *stdoutFileDescriptor = @(outputFileHandle.fileDescriptor); + + NSDictionary *options = @{ + kOptionsArgumentsKey: arguments, + kOptionsEnvironmentKey: environment, + @"standalone": @(1), + @"stdout": @(0), // stdoutFileDescriptor, + @"stderr": @(1), // stdoutFileDescriptor, + }; + + // To see more on how to debug the expected format/inputs of the options array, + // see the in-depth documentation in SimDevice.h. + __block typeof(self) blockSelf = self; + [self.device spawnAsyncWithPath:self.config.xctestBinaryPath + options:options + terminationHandler:^(int stat_loc) { + NSError *error = [BPSimulator errorFromStatusLocation:stat_loc]; + if (error) { + // LTHROCKM - TODO: Handle error + } + NSError *unarchiveError; + NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:testSuiteInfoOutputPath]; + NSData *testData = [fileHandle readDataToEndOfFile]; + NSArray *testCaseInfo = [NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:BPTestCaseInfo.class + fromData:testData + error:&unarchiveError]; + [fileHandle closeFile]; + // LTHROCKM - TODO: Call completion w/ enumerated test cases... + } completionHandler:^(NSError *error, pid_t pid) { + // LTHROCKM - TODO: Anything here? Maybe an execution timer, etc. + }]; +} + ++ (NSError *)errorFromStatusLocation:(int)stat_loc { + NSError *error; + if (WIFSIGNALED(stat_loc)) { + int signalCode = WTERMSIG(stat_loc); + // Ignore if the process was killed -- this occurs when we're killing + // a timed-out test, and shouldn't be treated as a crash. + if (signalCode != SIGKILL) { + [BPUtils printInfo:DEBUGINFO withString: @"Spawned XCTest execution failed with signal code: %@", @(signalCode)]; + return [BPUtils errorWithSignalCode:signalCode]; + } + } else { + // A non-zero exit code could mean a failed test or something more serious, but we can't tell the difference here. + // The best we can do is log the error code as debug info. + int exitCode = WEXITSTATUS(stat_loc); + if (exitCode) { + [BPUtils printInfo:DEBUGINFO withString: @"Spawned XCTest execution failed with error code: %@", @(exitCode)]; + } + } + return nil; +} + // Posts a APPCLOSED signal to the parser, indicating a crash/kill - (void)signalCloseToParser:(BPTreeParser *)parser fileHandle:(NSFileHandle *)fileHandle { [fileHandle seekToEndOfFile]; diff --git a/bp/src/BPUtils.h b/bp/src/BPUtils.h index 91f8cecc..a6396cec 100644 --- a/bp/src/BPUtils.h +++ b/bp/src/BPUtils.h @@ -51,6 +51,8 @@ typedef NS_ENUM(int, BPKind) { */ + (BOOL)isBuildScript; ++ (NSString *)findBPXCTestWrapperDYLIB; + + (NSString *)findExecutablePath:(NSString *)execName; /*! diff --git a/bp/src/BPUtils.m b/bp/src/BPUtils.m index 07affe8c..036265fc 100644 --- a/bp/src/BPUtils.m +++ b/bp/src/BPUtils.m @@ -16,6 +16,7 @@ #import "SimDeviceType.h" #import "BPExecutionContext.h" #import "BPSimulator.h" +#import @implementation BPUtils @@ -126,6 +127,22 @@ + (NSString *)findExecutablePath:(NSString *)execName { return execPath; } ++ (NSString *)findBPXCTestWrapperDYLIB { + NSString *argv0 = [[[NSProcessInfo processInfo] arguments] objectAtIndex:0]; + NSString *path = [[argv0 stringByDeletingLastPathComponent] stringByAppendingPathComponent:BPXCTestWrapperConstants.dylibName]; + if ([[NSFileManager defaultManager] isReadableFileAtPath:path]) { + return path; + } + // The executable may also be in derived data, accessible from the app's current working directory. + NSString *buildProductsDir = [NSFileManager.defaultManager.currentDirectoryPath stringByDeletingLastPathComponent]; + NSString *iPhoneSimDir = [buildProductsDir stringByAppendingPathComponent:@"Debug-iphonesimulator"]; + path = [iPhoneSimDir stringByAppendingPathComponent:BPXCTestWrapperConstants.dylibName]; + if ([[NSFileManager defaultManager] isReadableFileAtPath:path]) { + return path; + } + return nil; +} + + (NSString *)mkdtemp:(NSString *)template withError:(NSError **)errPtr { char *dir = strdup([[template stringByAppendingString:@"_XXXXXX"] UTF8String]); if (mkdtemp(dir) == NULL) { @@ -622,10 +639,6 @@ + (NSString *)correctedDYLDFrameworkPathFromBinary:(NSString *)binaryPath { } else { NSLog(@"Error creating regular expression: %@", error); } - - - [BPUtils printInfo:INFO withString:@"[LTHROCKM DEBUG] dyldFrameworkPath = %@", [paths componentsJoinedByString:@":"]]; - return [paths componentsJoinedByString:@":"]; } diff --git a/bp/src/SimulatorHelper.h b/bp/src/SimulatorHelper.h index 9324c027..48e4ee8e 100644 --- a/bp/src/SimulatorHelper.h +++ b/bp/src/SimulatorHelper.h @@ -20,6 +20,16 @@ */ + (BOOL)loadFrameworksWithXcodePath:(NSString *)xcodePath; +/*! + * @discussion get xctest launch environment + * @param hostBundleID the bundleID of the host app + * @param device the device to run test + * @param config the configuration object + * @return returns the app launch environment as a dictionary + */ ++ (NSDictionary *)logicTestEnvironmentWithConfig:(BPConfiguration *)config + stdoutRelativePath:(NSString *)path; + /*! * @discussion get app launch environment * @param hostBundleID the bundleID of the host app @@ -52,6 +62,14 @@ */ + (NSString *)makeStdoutFileOnDevice:(SimDevice *)device; +/*! + @discussion Creates an output file on the provided device of the form `/tmp/BPXCTestWrapper_testInfo_`. + This is meant to store the test info output generated by the injected test wrapper in an xctest execution. + @param device The device to create the file on. + @return the path of the output file. + */ ++ (NSString *)makeTestWrapperOutputFileOnDevice:(SimDevice *)device; + #pragma mark - Path Helper + (NSString *)bundleIdForPath:(NSString *)path; diff --git a/bp/src/SimulatorHelper.m b/bp/src/SimulatorHelper.m index 0ad103c9..b627778e 100644 --- a/bp/src/SimulatorHelper.m +++ b/bp/src/SimulatorHelper.m @@ -62,6 +62,22 @@ + (BOOL)loadFrameworksWithXcodePath:(NSString *)xcodePath { return YES; } ++ (NSDictionary *)logicTestEnvironmentWithConfig:(BPConfiguration *)config + stdoutRelativePath:(NSString *)path { + NSMutableDictionary *environment = [@{ + kOptionsStdoutKey: path, + kOptionsStderrKey: path, + } mutableCopy]; + if (config.dyldFrameworkPath) { + environment[@"DYLD_FRAMEWORK_PATH"] = config.dyldFrameworkPath; + // DYLD_LIBRARY_PATH is required specifically for swift tests, which require libXCTestSwiftSupport, + // which must be findable in the library path. + environment[@"DYLD_LIBRARY_PATH"] = config.dyldFrameworkPath; + } + [environment addEntriesFromDictionary:config.environmentVariables]; + return [environment copy]; +} + + (NSDictionary *)appLaunchEnvironmentWithBundleID:(NSString *)hostBundleID device:(SimDevice *)device config:(BPConfiguration *)config { @@ -210,16 +226,25 @@ + (NSString *)bundleIdForPath:(NSString *)path { // Intercept stdout, stderr and post as simulator-output events + (NSString *)makeStdoutFileOnDevice:(SimDevice *)device { NSString *stdout_stderr = [NSString stringWithFormat:@"%@/tmp/stdout_stderr_%@", device.dataPath, [[device UDID] UUIDString]]; - NSString *simStdoutPath = [BPUtils mkstemp:stdout_stderr withError:nil]; - assert(simStdoutPath != nil); + return [self createFileWithPathTemplate:stdout_stderr]; +} + ++ (NSString *)makeTestWrapperOutputFileOnDevice:(SimDevice *)device { + NSString *stdout_stderr = [NSString stringWithFormat:@"%@/tmp/BPXCTestWrapper_testInfo_%@", device.dataPath, [[device UDID] UUIDString]]; + return [self createFileWithPathTemplate:stdout_stderr]; +} + ++ (NSString *)createFileWithPathTemplate:(NSString *)pathTemplate { + NSString *fullPath = [BPUtils mkstemp:pathTemplate withError:nil]; + assert(fullPath != nil); - [[NSFileManager defaultManager] removeItemAtPath:simStdoutPath error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:fullPath error:nil]; // Create empty file so we can tail it and the app can write to it - [[NSFileManager defaultManager] createFileAtPath:simStdoutPath + [[NSFileManager defaultManager] createFileAtPath:fullPath contents:nil attributes:nil]; - return simStdoutPath; + return fullPath; } + (NSString *)executablePathforPath:(NSString *)path { From 8e0280a0d920b494c7c3e0c8a89273b8d5beceba Mon Sep 17 00:00:00 2001 From: Lucas Statler Date: Sun, 16 Jul 2023 22:22:43 -0700 Subject: [PATCH 08/11] it's mostly working :thumbsup: --- .../BPTestCaseInfo.h | 2 +- .../BPTestCaseInfo.m | 2 +- .../BPTestInspector.xcodeproj/project.pbxproj | 418 +++++++++++++++++ .../contents.xcworkspacedata | 2 +- .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcschemes/BPMacTestInspector.xcscheme | 67 +++ .../xcschemes/BPTestInspector.xcscheme | 16 +- .../BPTestInspectorConstants.h | 6 +- .../BPTestInspectorConstants.m | 10 +- .../Configs/BPMacTestInspector.xcconfig | 8 + .../Configs/BPTestInspector.xcconfig | 9 + .../Internal/BPLoggingUtils.h | 2 +- .../Internal/BPLoggingUtils.m | 6 +- .../Internal/BPTestCaseInfo+Internal.h | 4 +- .../Internal/BPXCTestUtils.h | 2 +- .../Internal/BPXCTestUtils.m | 9 +- .../Internal}/PrivateHeaders/CDStructures.h | 0 .../Protocols/NSObject-Protocol.h | 0 .../Protocols/XCTActivity-Protocol.h | 0 .../Protocols/XCTIssueHandling-Protocol.h | 0 .../Protocols/XCTWaiterDelegate-Protocol.h | 0 .../Protocols/XCTestObservation-Protocol.h | 0 .../_XCTestObservationInternal-Protocol.h | 0 .../_XCTestObservationPrivate-Protocol.h | 0 .../Internal}/PrivateHeaders/XCTest.h | 0 .../Internal}/PrivateHeaders/XCTestCase.h | 0 .../Internal}/PrivateHeaders/XCTestLog.h | 0 .../Internal}/PrivateHeaders/XCTestObserver.h | 0 .../Internal}/PrivateHeaders/XCTestRun.h | 0 .../Internal}/PrivateHeaders/XCTestSuite.h | 0 .../Internal}/PrivateHeaders/XCTestSuiteRun.h | 0 .../Internal/main.m | 14 +- BPXCTestWrapper/BPShimLogger.h | 25 - BPXCTestWrapper/BPShimLogger.m | 73 --- .../BPXCTestWrapper.xcodeproj/project.pbxproj | 438 ------------------ BPXCTestWrapper/Info.plist | 26 -- BPXCTestWrapper/XCTestLog+Shim.h | 12 - BPXCTestWrapper/XCTestLog+Shim.m | 111 ----- Bluepill.xcworkspace/contents.xcworkspacedata | 2 +- Configs/BPMacTestInspector.xcconfig | 8 + Configs/BPTestInspector.xcconfig | 9 + bluepill/bluepill.xcodeproj/project.pbxproj | 20 +- bluepill/libBPMacTestInspector.dylib | Bin 0 -> 173584 bytes bp/bp.xcodeproj/project.pbxproj | 24 +- bp/libBPMacTestInspector.dylib | Bin 0 -> 173584 bytes bp/src/BPSimulator.m | 35 +- bp/src/BPUtils.h | 2 +- bp/src/BPUtils.m | 8 +- bp/src/SimulatorHelper.h | 2 +- bp/src/SimulatorHelper.m | 2 +- 50 files changed, 618 insertions(+), 756 deletions(-) rename {BPXCTestWrapper => BPTestInspector}/BPTestCaseInfo.h (95%) rename {BPXCTestWrapper => BPTestInspector}/BPTestCaseInfo.m (98%) create mode 100644 BPTestInspector/BPTestInspector.xcodeproj/project.pbxproj rename {BPXCTestWrapper/BPXCTestWrapper.xcodeproj => BPTestInspector/BPTestInspector.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (53%) rename {BPXCTestWrapper/BPXCTestWrapper.xcodeproj => BPTestInspector/BPTestInspector.xcodeproj}/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) create mode 100644 BPTestInspector/BPTestInspector.xcodeproj/xcshareddata/xcschemes/BPMacTestInspector.xcscheme rename BPXCTestWrapper/BPXCTestWrapper.xcodeproj/xcshareddata/xcschemes/BPXCTestWrapper.xcscheme => BPTestInspector/BPTestInspector.xcodeproj/xcshareddata/xcschemes/BPTestInspector.xcscheme (81%) rename BPXCTestWrapper/BPXCTestWrapperConstants.h => BPTestInspector/BPTestInspectorConstants.h (71%) rename BPXCTestWrapper/BPXCTestWrapperConstants.m => BPTestInspector/BPTestInspectorConstants.m (62%) create mode 100644 BPTestInspector/Configs/BPMacTestInspector.xcconfig create mode 100644 BPTestInspector/Configs/BPTestInspector.xcconfig rename {BPXCTestWrapper => BPTestInspector}/Internal/BPLoggingUtils.h (93%) rename {BPXCTestWrapper => BPTestInspector}/Internal/BPLoggingUtils.m (64%) rename {BPXCTestWrapper => BPTestInspector}/Internal/BPTestCaseInfo+Internal.h (80%) rename {BPXCTestWrapper => BPTestInspector}/Internal/BPXCTestUtils.h (93%) rename {BPXCTestWrapper => BPTestInspector}/Internal/BPXCTestUtils.m (93%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/CDStructures.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/Protocols/NSObject-Protocol.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/Protocols/XCTActivity-Protocol.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/Protocols/XCTIssueHandling-Protocol.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/Protocols/XCTWaiterDelegate-Protocol.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/Protocols/XCTestObservation-Protocol.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/Protocols/_XCTestObservationInternal-Protocol.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/Protocols/_XCTestObservationPrivate-Protocol.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/XCTest.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/XCTestCase.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/XCTestLog.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/XCTestObserver.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/XCTestRun.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/XCTestSuite.h (100%) rename {BPXCTestWrapper => BPTestInspector/Internal}/PrivateHeaders/XCTestSuiteRun.h (100%) rename {BPXCTestWrapper => BPTestInspector}/Internal/main.m (86%) delete mode 100644 BPXCTestWrapper/BPShimLogger.h delete mode 100644 BPXCTestWrapper/BPShimLogger.m delete mode 100644 BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.pbxproj delete mode 100644 BPXCTestWrapper/Info.plist delete mode 100644 BPXCTestWrapper/XCTestLog+Shim.h delete mode 100644 BPXCTestWrapper/XCTestLog+Shim.m create mode 100644 Configs/BPMacTestInspector.xcconfig create mode 100644 Configs/BPTestInspector.xcconfig create mode 100755 bluepill/libBPMacTestInspector.dylib create mode 100755 bp/libBPMacTestInspector.dylib diff --git a/BPXCTestWrapper/BPTestCaseInfo.h b/BPTestInspector/BPTestCaseInfo.h similarity index 95% rename from BPXCTestWrapper/BPTestCaseInfo.h rename to BPTestInspector/BPTestCaseInfo.h index 21aec073..351ed130 100644 --- a/BPXCTestWrapper/BPTestCaseInfo.h +++ b/BPTestInspector/BPTestCaseInfo.h @@ -1,6 +1,6 @@ // // BPTestCaseInfo.h -// BPXCTestWrapper +// BPTestInspector // // Created by Lucas Throckmorton on 6/8/23. // diff --git a/BPXCTestWrapper/BPTestCaseInfo.m b/BPTestInspector/BPTestCaseInfo.m similarity index 98% rename from BPXCTestWrapper/BPTestCaseInfo.m rename to BPTestInspector/BPTestCaseInfo.m index c50858e9..e0fcf625 100644 --- a/BPXCTestWrapper/BPTestCaseInfo.m +++ b/BPTestInspector/BPTestCaseInfo.m @@ -1,6 +1,6 @@ // // BPTestCaseInfo.m -// BPXCTestWrapper +// BPTestInspector // // Created by Lucas Throckmorton on 6/8/23. // diff --git a/BPTestInspector/BPTestInspector.xcodeproj/project.pbxproj b/BPTestInspector/BPTestInspector.xcodeproj/project.pbxproj new file mode 100644 index 00000000..21dc13d3 --- /dev/null +++ b/BPTestInspector/BPTestInspector.xcodeproj/project.pbxproj @@ -0,0 +1,418 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + FB21F07F2A62286400682AC7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FB21F07E2A62286400682AC7 /* main.m */; }; + FB21F0862A62297F00682AC7 /* BPLoggingUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0812A62297F00682AC7 /* BPLoggingUtils.h */; }; + FB21F0872A62297F00682AC7 /* BPTestCaseInfo+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0822A62297F00682AC7 /* BPTestCaseInfo+Internal.h */; }; + FB21F0882A62297F00682AC7 /* BPXCTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FB21F0832A62297F00682AC7 /* BPXCTestUtils.m */; }; + FB21F0892A62297F00682AC7 /* BPLoggingUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FB21F0842A62297F00682AC7 /* BPLoggingUtils.m */; }; + FB21F08A2A62297F00682AC7 /* BPXCTestUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0852A62297F00682AC7 /* BPXCTestUtils.h */; }; + FB21F08F2A62298B00682AC7 /* BPTestCaseInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = FB21F08B2A62298A00682AC7 /* BPTestCaseInfo.m */; }; + FB21F0902A62298B00682AC7 /* BPTestCaseInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = FB21F08B2A62298A00682AC7 /* BPTestCaseInfo.m */; }; + FB21F0912A62298B00682AC7 /* BPTestInspectorConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F08C2A62298A00682AC7 /* BPTestInspectorConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FB21F0922A62298B00682AC7 /* BPTestInspectorConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F08C2A62298A00682AC7 /* BPTestInspectorConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FB21F0932A62298B00682AC7 /* BPTestCaseInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F08D2A62298B00682AC7 /* BPTestCaseInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FB21F0942A62298B00682AC7 /* BPTestCaseInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F08D2A62298B00682AC7 /* BPTestCaseInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FB21F0952A62298B00682AC7 /* BPTestInspectorConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = FB21F08E2A62298B00682AC7 /* BPTestInspectorConstants.m */; }; + FB21F0962A62298B00682AC7 /* BPTestInspectorConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = FB21F08E2A62298B00682AC7 /* BPTestInspectorConstants.m */; }; + FB21F0A82A622A0200682AC7 /* XCTestObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0982A622A0200682AC7 /* XCTestObserver.h */; }; + FB21F0A92A622A0200682AC7 /* XCTestSuiteRun.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0992A622A0200682AC7 /* XCTestSuiteRun.h */; }; + FB21F0AA2A622A0200682AC7 /* XCTestSuite.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F09A2A622A0200682AC7 /* XCTestSuite.h */; }; + FB21F0AB2A622A0200682AC7 /* CDStructures.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F09B2A622A0200682AC7 /* CDStructures.h */; }; + FB21F0AC2A622A0200682AC7 /* XCTestLog.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F09C2A622A0200682AC7 /* XCTestLog.h */; }; + FB21F0AD2A622A0200682AC7 /* XCTest.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F09D2A622A0200682AC7 /* XCTest.h */; }; + FB21F0AE2A622A0200682AC7 /* XCTestRun.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F09E2A622A0200682AC7 /* XCTestRun.h */; }; + FB21F0AF2A622A0200682AC7 /* XCTestCase.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F09F2A622A0200682AC7 /* XCTestCase.h */; }; + FB21F0B02A622A0200682AC7 /* XCTActivity-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0A12A622A0200682AC7 /* XCTActivity-Protocol.h */; }; + FB21F0B12A622A0200682AC7 /* XCTIssueHandling-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0A22A622A0200682AC7 /* XCTIssueHandling-Protocol.h */; }; + FB21F0B22A622A0200682AC7 /* XCTestObservation-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0A32A622A0200682AC7 /* XCTestObservation-Protocol.h */; }; + FB21F0B32A622A0200682AC7 /* _XCTestObservationPrivate-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0A42A622A0200682AC7 /* _XCTestObservationPrivate-Protocol.h */; }; + FB21F0B42A622A0200682AC7 /* XCTWaiterDelegate-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0A52A622A0200682AC7 /* XCTWaiterDelegate-Protocol.h */; }; + FB21F0B52A622A0200682AC7 /* NSObject-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0A62A622A0200682AC7 /* NSObject-Protocol.h */; }; + FB21F0B62A622A0200682AC7 /* _XCTestObservationInternal-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0A72A622A0200682AC7 /* _XCTestObservationInternal-Protocol.h */; }; + FB21F0B92A622AA000682AC7 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB21F0B82A622AA000682AC7 /* XCTest.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + AA017F411BD7776B00F45E9D /* libBPTestInspector.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libBPTestInspector.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + AA56EAE21EEF08720062C2BC /* libBPMacTestInspector.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libBPMacTestInspector.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + EED62ED820D12209006E86E5 /* BPMacTestInspector.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = BPMacTestInspector.xcconfig; sourceTree = ""; }; + EED62ED920D12209006E86E5 /* BPTestInspector.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = BPTestInspector.xcconfig; sourceTree = ""; }; + FB21F07E2A62286400682AC7 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + FB21F0812A62297F00682AC7 /* BPLoggingUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPLoggingUtils.h; sourceTree = ""; }; + FB21F0822A62297F00682AC7 /* BPTestCaseInfo+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "BPTestCaseInfo+Internal.h"; sourceTree = ""; }; + FB21F0832A62297F00682AC7 /* BPXCTestUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPXCTestUtils.m; sourceTree = ""; }; + FB21F0842A62297F00682AC7 /* BPLoggingUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPLoggingUtils.m; sourceTree = ""; }; + FB21F0852A62297F00682AC7 /* BPXCTestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPXCTestUtils.h; sourceTree = ""; }; + FB21F08B2A62298A00682AC7 /* BPTestCaseInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPTestCaseInfo.m; sourceTree = ""; }; + FB21F08C2A62298A00682AC7 /* BPTestInspectorConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPTestInspectorConstants.h; sourceTree = ""; }; + FB21F08D2A62298B00682AC7 /* BPTestCaseInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPTestCaseInfo.h; sourceTree = ""; }; + FB21F08E2A62298B00682AC7 /* BPTestInspectorConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPTestInspectorConstants.m; sourceTree = ""; }; + FB21F0982A622A0200682AC7 /* XCTestObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XCTestObserver.h; sourceTree = ""; }; + FB21F0992A622A0200682AC7 /* XCTestSuiteRun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XCTestSuiteRun.h; sourceTree = ""; }; + FB21F09A2A622A0200682AC7 /* XCTestSuite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XCTestSuite.h; sourceTree = ""; }; + FB21F09B2A622A0200682AC7 /* CDStructures.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDStructures.h; sourceTree = ""; }; + FB21F09C2A622A0200682AC7 /* XCTestLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XCTestLog.h; sourceTree = ""; }; + FB21F09D2A622A0200682AC7 /* XCTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XCTest.h; sourceTree = ""; }; + FB21F09E2A622A0200682AC7 /* XCTestRun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XCTestRun.h; sourceTree = ""; }; + FB21F09F2A622A0200682AC7 /* XCTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XCTestCase.h; sourceTree = ""; }; + FB21F0A12A622A0200682AC7 /* XCTActivity-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTActivity-Protocol.h"; sourceTree = ""; }; + FB21F0A22A622A0200682AC7 /* XCTIssueHandling-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTIssueHandling-Protocol.h"; sourceTree = ""; }; + FB21F0A32A622A0200682AC7 /* XCTestObservation-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTestObservation-Protocol.h"; sourceTree = ""; }; + FB21F0A42A622A0200682AC7 /* _XCTestObservationPrivate-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "_XCTestObservationPrivate-Protocol.h"; sourceTree = ""; }; + FB21F0A52A622A0200682AC7 /* XCTWaiterDelegate-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTWaiterDelegate-Protocol.h"; sourceTree = ""; }; + FB21F0A62A622A0200682AC7 /* NSObject-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject-Protocol.h"; sourceTree = ""; }; + FB21F0A72A622A0200682AC7 /* _XCTestObservationInternal-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "_XCTestObservationInternal-Protocol.h"; sourceTree = ""; }; + FB21F0B82A622AA000682AC7 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + AA017F3E1BD7776B00F45E9D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FB21F0B92A622AA000682AC7 /* XCTest.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AA56EADF1EEF08720062C2BC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + AA017F381BD7776B00F45E9D = { + isa = PBXGroup; + children = ( + FB21F08D2A62298B00682AC7 /* BPTestCaseInfo.h */, + FB21F08B2A62298A00682AC7 /* BPTestCaseInfo.m */, + FB21F08C2A62298A00682AC7 /* BPTestInspectorConstants.h */, + FB21F08E2A62298B00682AC7 /* BPTestInspectorConstants.m */, + FB21F0802A62296A00682AC7 /* Internal */, + EED62ED420D1212D006E86E5 /* Configs */, + AA017F421BD7776B00F45E9D /* Products */, + FB21F0B72A622AA000682AC7 /* Frameworks */, + ); + sourceTree = ""; + }; + AA017F421BD7776B00F45E9D /* Products */ = { + isa = PBXGroup; + children = ( + AA017F411BD7776B00F45E9D /* libBPTestInspector.dylib */, + AA56EAE21EEF08720062C2BC /* libBPMacTestInspector.dylib */, + ); + name = Products; + sourceTree = ""; + }; + EED62ED420D1212D006E86E5 /* Configs */ = { + isa = PBXGroup; + children = ( + EED62ED820D12209006E86E5 /* BPMacTestInspector.xcconfig */, + EED62ED920D12209006E86E5 /* BPTestInspector.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + FB21F0802A62296A00682AC7 /* Internal */ = { + isa = PBXGroup; + children = ( + FB21F07E2A62286400682AC7 /* main.m */, + FB21F0812A62297F00682AC7 /* BPLoggingUtils.h */, + FB21F0842A62297F00682AC7 /* BPLoggingUtils.m */, + FB21F0822A62297F00682AC7 /* BPTestCaseInfo+Internal.h */, + FB21F0852A62297F00682AC7 /* BPXCTestUtils.h */, + FB21F0832A62297F00682AC7 /* BPXCTestUtils.m */, + FB21F0972A622A0200682AC7 /* PrivateHeaders */, + ); + path = Internal; + sourceTree = ""; + }; + FB21F0972A622A0200682AC7 /* PrivateHeaders */ = { + isa = PBXGroup; + children = ( + FB21F0982A622A0200682AC7 /* XCTestObserver.h */, + FB21F0992A622A0200682AC7 /* XCTestSuiteRun.h */, + FB21F09A2A622A0200682AC7 /* XCTestSuite.h */, + FB21F09B2A622A0200682AC7 /* CDStructures.h */, + FB21F09C2A622A0200682AC7 /* XCTestLog.h */, + FB21F09D2A622A0200682AC7 /* XCTest.h */, + FB21F09E2A622A0200682AC7 /* XCTestRun.h */, + FB21F09F2A622A0200682AC7 /* XCTestCase.h */, + FB21F0A02A622A0200682AC7 /* Protocols */, + ); + path = PrivateHeaders; + sourceTree = ""; + }; + FB21F0A02A622A0200682AC7 /* Protocols */ = { + isa = PBXGroup; + children = ( + FB21F0A12A622A0200682AC7 /* XCTActivity-Protocol.h */, + FB21F0A22A622A0200682AC7 /* XCTIssueHandling-Protocol.h */, + FB21F0A32A622A0200682AC7 /* XCTestObservation-Protocol.h */, + FB21F0A42A622A0200682AC7 /* _XCTestObservationPrivate-Protocol.h */, + FB21F0A52A622A0200682AC7 /* XCTWaiterDelegate-Protocol.h */, + FB21F0A62A622A0200682AC7 /* NSObject-Protocol.h */, + FB21F0A72A622A0200682AC7 /* _XCTestObservationInternal-Protocol.h */, + ); + path = Protocols; + sourceTree = ""; + }; + FB21F0B72A622AA000682AC7 /* Frameworks */ = { + isa = PBXGroup; + children = ( + FB21F0B82A622AA000682AC7 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + AA017F3F1BD7776B00F45E9D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + FB21F0B12A622A0200682AC7 /* XCTIssueHandling-Protocol.h in Headers */, + FB21F0AA2A622A0200682AC7 /* XCTestSuite.h in Headers */, + FB21F0AB2A622A0200682AC7 /* CDStructures.h in Headers */, + FB21F0B42A622A0200682AC7 /* XCTWaiterDelegate-Protocol.h in Headers */, + FB21F08A2A62297F00682AC7 /* BPXCTestUtils.h in Headers */, + FB21F0A92A622A0200682AC7 /* XCTestSuiteRun.h in Headers */, + FB21F0A82A622A0200682AC7 /* XCTestObserver.h in Headers */, + FB21F0AE2A622A0200682AC7 /* XCTestRun.h in Headers */, + FB21F0862A62297F00682AC7 /* BPLoggingUtils.h in Headers */, + FB21F0B22A622A0200682AC7 /* XCTestObservation-Protocol.h in Headers */, + FB21F0AC2A622A0200682AC7 /* XCTestLog.h in Headers */, + FB21F0B52A622A0200682AC7 /* NSObject-Protocol.h in Headers */, + FB21F0B32A622A0200682AC7 /* _XCTestObservationPrivate-Protocol.h in Headers */, + FB21F0B62A622A0200682AC7 /* _XCTestObservationInternal-Protocol.h in Headers */, + FB21F0AF2A622A0200682AC7 /* XCTestCase.h in Headers */, + FB21F0932A62298B00682AC7 /* BPTestCaseInfo.h in Headers */, + FB21F0AD2A622A0200682AC7 /* XCTest.h in Headers */, + FB21F0B02A622A0200682AC7 /* XCTActivity-Protocol.h in Headers */, + FB21F0872A62297F00682AC7 /* BPTestCaseInfo+Internal.h in Headers */, + FB21F0912A62298B00682AC7 /* BPTestInspectorConstants.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AA56EAE01EEF08720062C2BC /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + FB21F0942A62298B00682AC7 /* BPTestCaseInfo.h in Headers */, + FB21F0922A62298B00682AC7 /* BPTestInspectorConstants.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + AA017F401BD7776B00F45E9D /* BPTestInspector */ = { + isa = PBXNativeTarget; + buildConfigurationList = AA017F451BD7776B00F45E9D /* Build configuration list for PBXNativeTarget "BPTestInspector" */; + buildPhases = ( + AA017F3D1BD7776B00F45E9D /* Sources */, + AA017F3E1BD7776B00F45E9D /* Frameworks */, + AA017F3F1BD7776B00F45E9D /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BPTestInspector; + productName = Shimulator; + productReference = AA017F411BD7776B00F45E9D /* libBPTestInspector.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + AA56EAE11EEF08720062C2BC /* BPMacTestInspector */ = { + isa = PBXNativeTarget; + buildConfigurationList = AA56EAEA1EEF08720062C2BC /* Build configuration list for PBXNativeTarget "BPMacTestInspector" */; + buildPhases = ( + AA56EADE1EEF08720062C2BC /* Sources */, + AA56EADF1EEF08720062C2BC /* Frameworks */, + AA56EAE01EEF08720062C2BC /* Headers */, + 71B9E0DC268C7A4600D40A91 /* Print Shim Path */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BPMacTestInspector; + productName = Maculator; + productReference = AA56EAE21EEF08720062C2BC /* libBPMacTestInspector.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + AA017F391BD7776B00F45E9D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0700; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + AA017F401BD7776B00F45E9D = { + CreatedOnToolsVersion = 7.0.1; + }; + AA56EAE11EEF08720062C2BC = { + CreatedOnToolsVersion = 9.0; + }; + }; + }; + buildConfigurationList = AA017F3C1BD7776B00F45E9D /* Build configuration list for PBXProject "BPTestInspector" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = AA017F381BD7776B00F45E9D; + productRefGroup = AA017F421BD7776B00F45E9D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + AA017F401BD7776B00F45E9D /* BPTestInspector */, + AA56EAE11EEF08720062C2BC /* BPMacTestInspector */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 71B9E0DC268C7A4600D40A91 /* Print Shim Path */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Print Shim Path"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo SHIM_DYLIB_PATH=$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + AA017F3D1BD7776B00F45E9D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FB21F0882A62297F00682AC7 /* BPXCTestUtils.m in Sources */, + FB21F08F2A62298B00682AC7 /* BPTestCaseInfo.m in Sources */, + FB21F07F2A62286400682AC7 /* main.m in Sources */, + FB21F0892A62297F00682AC7 /* BPLoggingUtils.m in Sources */, + FB21F0952A62298B00682AC7 /* BPTestInspectorConstants.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AA56EADE1EEF08720062C2BC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FB21F0962A62298B00682AC7 /* BPTestInspectorConstants.m in Sources */, + FB21F0902A62298B00682AC7 /* BPTestCaseInfo.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + AA017F431BD7776B00F45E9D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "-"; + }; + name = Debug; + }; + AA017F441BD7776B00F45E9D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "-"; + }; + name = Release; + }; + AA017F461BD7776B00F45E9D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EED62ED920D12209006E86E5 /* BPTestInspector.xcconfig */; + buildSettings = { + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + }; + name = Debug; + }; + AA017F471BD7776B00F45E9D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EED62ED920D12209006E86E5 /* BPTestInspector.xcconfig */; + buildSettings = { + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + }; + name = Release; + }; + AA56EAE81EEF08720062C2BC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EED62ED820D12209006E86E5 /* BPMacTestInspector.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + AA56EAE91EEF08720062C2BC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EED62ED820D12209006E86E5 /* BPMacTestInspector.xcconfig */; + buildSettings = { + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + AA017F3C1BD7776B00F45E9D /* Build configuration list for PBXProject "BPTestInspector" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AA017F431BD7776B00F45E9D /* Debug */, + AA017F441BD7776B00F45E9D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + AA017F451BD7776B00F45E9D /* Build configuration list for PBXNativeTarget "BPTestInspector" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AA017F461BD7776B00F45E9D /* Debug */, + AA017F471BD7776B00F45E9D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + AA56EAEA1EEF08720062C2BC /* Build configuration list for PBXNativeTarget "BPMacTestInspector" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AA56EAE81EEF08720062C2BC /* Debug */, + AA56EAE91EEF08720062C2BC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = AA017F391BD7776B00F45E9D /* Project object */; +} diff --git a/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/BPTestInspector/BPTestInspector.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 53% rename from BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to BPTestInspector/BPTestInspector.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1dd251a9..6291fb33 100644 --- a/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/BPTestInspector/BPTestInspector.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:BPTestInspector.xcodeproj"> diff --git a/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/BPTestInspector/BPTestInspector.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to BPTestInspector/BPTestInspector.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/BPTestInspector/BPTestInspector.xcodeproj/xcshareddata/xcschemes/BPMacTestInspector.xcscheme b/BPTestInspector/BPTestInspector.xcodeproj/xcshareddata/xcschemes/BPMacTestInspector.xcscheme new file mode 100644 index 00000000..c56ec4cc --- /dev/null +++ b/BPTestInspector/BPTestInspector.xcodeproj/xcshareddata/xcschemes/BPMacTestInspector.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/xcshareddata/xcschemes/BPXCTestWrapper.xcscheme b/BPTestInspector/BPTestInspector.xcodeproj/xcshareddata/xcschemes/BPTestInspector.xcscheme similarity index 81% rename from BPXCTestWrapper/BPXCTestWrapper.xcodeproj/xcshareddata/xcschemes/BPXCTestWrapper.xcscheme rename to BPTestInspector/BPTestInspector.xcodeproj/xcshareddata/xcschemes/BPTestInspector.xcscheme index c72ae5d6..5e481eb8 100644 --- a/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/xcshareddata/xcschemes/BPXCTestWrapper.xcscheme +++ b/BPTestInspector/BPTestInspector.xcodeproj/xcshareddata/xcschemes/BPTestInspector.xcscheme @@ -14,10 +14,10 @@ buildForAnalyzing = "YES"> + BlueprintIdentifier = "AA017F401BD7776B00F45E9D" + BuildableName = "libBPTestInspector.dylib" + BlueprintName = "BPTestInspector" + ReferencedContainer = "container:BPTestInspector.xcodeproj"> @@ -50,10 +50,10 @@ + BlueprintIdentifier = "AA017F401BD7776B00F45E9D" + BuildableName = "libBPTestInspector.dylib" + BlueprintName = "BPTestInspector" + ReferencedContainer = "container:BPTestInspector.xcodeproj"> diff --git a/BPXCTestWrapper/BPXCTestWrapperConstants.h b/BPTestInspector/BPTestInspectorConstants.h similarity index 71% rename from BPXCTestWrapper/BPXCTestWrapperConstants.h rename to BPTestInspector/BPTestInspectorConstants.h index 3b961a0c..d89a13ca 100644 --- a/BPXCTestWrapper/BPXCTestWrapperConstants.h +++ b/BPTestInspector/BPTestInspectorConstants.h @@ -1,6 +1,6 @@ // -// BPXCTestWrapperConstants.h -// BPXCTestWrapper +// BPTestInspectorConstants.h +// BPTestInspector // // Created by Lucas Throckmorton on 7/13/23. // @@ -9,7 +9,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface BPXCTestWrapperConstants : NSObject +@interface BPTestInspectorConstants : NSObject + (NSString *)dylibName; + (NSString *)testBundleEnvironmentKey; diff --git a/BPXCTestWrapper/BPXCTestWrapperConstants.m b/BPTestInspector/BPTestInspectorConstants.m similarity index 62% rename from BPXCTestWrapper/BPXCTestWrapperConstants.m rename to BPTestInspector/BPTestInspectorConstants.m index 98269e42..6effe2f0 100644 --- a/BPXCTestWrapper/BPXCTestWrapperConstants.m +++ b/BPTestInspector/BPTestInspectorConstants.m @@ -1,16 +1,16 @@ // -// BPXCTestWrapperConstants.m -// BPXCTestWrapper +// BPTestInspectorConstants.m +// BPTestInspector // // Created by Lucas Throckmorton on 7/13/23. // -#import "BPXCTestWrapperConstants.h" +#import "BPTestInspectorConstants.h" -@implementation BPXCTestWrapperConstants +@implementation BPTestInspectorConstants + (NSString *)dylibName { - return @"libBPXCTestWrapper.dylib"; + return @"libBPTestInspector.dylib"; } + (NSString *)testBundleEnvironmentKey { diff --git a/BPTestInspector/Configs/BPMacTestInspector.xcconfig b/BPTestInspector/Configs/BPMacTestInspector.xcconfig new file mode 100644 index 00000000..371f806f --- /dev/null +++ b/BPTestInspector/Configs/BPMacTestInspector.xcconfig @@ -0,0 +1,8 @@ +CURRENT_PROJECT_VERSION = 1 +DYLIB_CURRENT_VERSION = 1 +DYLIB_COMPATIBILITY_VERSION = 1 +PRODUCT_NAME = BPMacTestInspector +EXECUTABLE_PREFIX = lib +SDKROOT = macosx +MACOSX_DEPLOYMENT_TARGET = 11.0 +COPY_PHASE_STRIP = NO diff --git a/BPTestInspector/Configs/BPTestInspector.xcconfig b/BPTestInspector/Configs/BPTestInspector.xcconfig new file mode 100644 index 00000000..964e63f0 --- /dev/null +++ b/BPTestInspector/Configs/BPTestInspector.xcconfig @@ -0,0 +1,9 @@ +CURRENT_PROJECT_VERSION = 1 +DYLIB_CURRENT_VERSION = 1 +DYLIB_COMPATIBILITY_VERSION = 1 +PRODUCT_NAME = BPTestInspector +EXECUTABLE_PREFIX = lib +SDKROOT = iphonesimulator +IPHONEOS_DEPLOYMENT_TARGET = 16.0 +COPY_PHASE_STRIP = NO +CODE_SIGN_IDENTITY = - // LTHROCKM - may need code signing for hosted tests diff --git a/BPXCTestWrapper/Internal/BPLoggingUtils.h b/BPTestInspector/Internal/BPLoggingUtils.h similarity index 93% rename from BPXCTestWrapper/Internal/BPLoggingUtils.h rename to BPTestInspector/Internal/BPLoggingUtils.h index 28130d5e..890ea9a5 100644 --- a/BPXCTestWrapper/Internal/BPLoggingUtils.h +++ b/BPTestInspector/Internal/BPLoggingUtils.h @@ -1,6 +1,6 @@ // // BPLoggingUtils.h -// BPXCTestWrapper +// BPTestInspector // // Created by Lucas Throckmorton on 6/8/23. // diff --git a/BPXCTestWrapper/Internal/BPLoggingUtils.m b/BPTestInspector/Internal/BPLoggingUtils.m similarity index 64% rename from BPXCTestWrapper/Internal/BPLoggingUtils.m rename to BPTestInspector/Internal/BPLoggingUtils.m index 20253a60..199d69f9 100644 --- a/BPXCTestWrapper/Internal/BPLoggingUtils.m +++ b/BPTestInspector/Internal/BPLoggingUtils.m @@ -1,6 +1,6 @@ // // BPLoggingUtils.m -// BPXCTestWrapper +// BPTestInspector // // Created by Lucas Throckmorton on 6/8/23. // @@ -10,11 +10,11 @@ @implementation BPLoggingUtils + (void)log:(NSString *)message { - NSLog(@"[BPXCTestWrapper] %@", message); + NSLog(@"[BPTestInspector] %@", message); } + (void)logError:(NSString *)errorMessage { - NSLog(@"[BPXCTestWrapper] Error: %@", errorMessage); + NSLog(@"[BPTestInspector] Error: %@", errorMessage); } @end diff --git a/BPXCTestWrapper/Internal/BPTestCaseInfo+Internal.h b/BPTestInspector/Internal/BPTestCaseInfo+Internal.h similarity index 80% rename from BPXCTestWrapper/Internal/BPTestCaseInfo+Internal.h rename to BPTestInspector/Internal/BPTestCaseInfo+Internal.h index 294665ab..2b3eaf5c 100644 --- a/BPXCTestWrapper/Internal/BPTestCaseInfo+Internal.h +++ b/BPTestInspector/Internal/BPTestCaseInfo+Internal.h @@ -1,11 +1,11 @@ // // BPTestCaseInfo+Internal.h -// BPXCTestWrapper +// BPTestInspector // // Created by Lucas Throckmorton on 7/14/23. // -#import +#import #import "XCTestCase.h" NS_ASSUME_NONNULL_BEGIN diff --git a/BPXCTestWrapper/Internal/BPXCTestUtils.h b/BPTestInspector/Internal/BPXCTestUtils.h similarity index 93% rename from BPXCTestWrapper/Internal/BPXCTestUtils.h rename to BPTestInspector/Internal/BPXCTestUtils.h index d279f28e..e8e3363b 100644 --- a/BPXCTestWrapper/Internal/BPXCTestUtils.h +++ b/BPTestInspector/Internal/BPXCTestUtils.h @@ -1,6 +1,6 @@ // // BPTestSuiteUtils.h -// BPXCTestWrapper +// BPTestInspector // // Created by Lucas Throckmorton on 6/8/23. // diff --git a/BPXCTestWrapper/Internal/BPXCTestUtils.m b/BPTestInspector/Internal/BPXCTestUtils.m similarity index 93% rename from BPXCTestWrapper/Internal/BPXCTestUtils.m rename to BPTestInspector/Internal/BPXCTestUtils.m index 7c5ceca6..bcfe8f0a 100644 --- a/BPXCTestWrapper/Internal/BPXCTestUtils.m +++ b/BPTestInspector/Internal/BPXCTestUtils.m @@ -1,6 +1,6 @@ // // BPXCTestUtils.m -// BPXCTestWrapper +// BPTestInspector // // Created by Lucas Throckmorton on 6/8/23. // @@ -22,8 +22,8 @@ + (void)logAllTestsInBundleWithPath:(NSString *)bundlePath toFile:(NSString *)ou NSData *data = [NSKeyedArchiver archivedDataWithRootObject:testCases requiringSecureCoding:NO error:&encodingError]; // Write to file. NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:outputPath]; - [fileHandle writeData:data]; -// [testCases.description writeToFile:outputPath atomically:YES encoding:NSUTF8StringEncoding error:nil]; +// [fileHandle writeData:data]; + [testCases.description writeToFile:outputPath atomically:YES encoding:NSUTF8StringEncoding error:nil]; [fileHandle closeFile]; NSString *output = [NSString stringWithFormat:@"Wrote to file: %@.", outputPath]; @@ -63,6 +63,9 @@ + (void)logAllTestsInBundleWithPath:(NSString *)bundlePath toFile:(NSString *)ou */ if (dlopen(bundle.executablePath.UTF8String, RTLD_LAZY) == NULL) { [BPLoggingUtils logError:[NSString stringWithFormat:@"Unable to open test bundle's executable path - %@", bundle.executablePath]]; + + [BPLoggingUtils logError:@"What's the error???"]; + fprintf(stderr, "%s\n", dlerror()); return @[]; } diff --git a/BPXCTestWrapper/PrivateHeaders/CDStructures.h b/BPTestInspector/Internal/PrivateHeaders/CDStructures.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/CDStructures.h rename to BPTestInspector/Internal/PrivateHeaders/CDStructures.h diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/NSObject-Protocol.h b/BPTestInspector/Internal/PrivateHeaders/Protocols/NSObject-Protocol.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/Protocols/NSObject-Protocol.h rename to BPTestInspector/Internal/PrivateHeaders/Protocols/NSObject-Protocol.h diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/XCTActivity-Protocol.h b/BPTestInspector/Internal/PrivateHeaders/Protocols/XCTActivity-Protocol.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/Protocols/XCTActivity-Protocol.h rename to BPTestInspector/Internal/PrivateHeaders/Protocols/XCTActivity-Protocol.h diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/XCTIssueHandling-Protocol.h b/BPTestInspector/Internal/PrivateHeaders/Protocols/XCTIssueHandling-Protocol.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/Protocols/XCTIssueHandling-Protocol.h rename to BPTestInspector/Internal/PrivateHeaders/Protocols/XCTIssueHandling-Protocol.h diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/XCTWaiterDelegate-Protocol.h b/BPTestInspector/Internal/PrivateHeaders/Protocols/XCTWaiterDelegate-Protocol.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/Protocols/XCTWaiterDelegate-Protocol.h rename to BPTestInspector/Internal/PrivateHeaders/Protocols/XCTWaiterDelegate-Protocol.h diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/XCTestObservation-Protocol.h b/BPTestInspector/Internal/PrivateHeaders/Protocols/XCTestObservation-Protocol.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/Protocols/XCTestObservation-Protocol.h rename to BPTestInspector/Internal/PrivateHeaders/Protocols/XCTestObservation-Protocol.h diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/_XCTestObservationInternal-Protocol.h b/BPTestInspector/Internal/PrivateHeaders/Protocols/_XCTestObservationInternal-Protocol.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/Protocols/_XCTestObservationInternal-Protocol.h rename to BPTestInspector/Internal/PrivateHeaders/Protocols/_XCTestObservationInternal-Protocol.h diff --git a/BPXCTestWrapper/PrivateHeaders/Protocols/_XCTestObservationPrivate-Protocol.h b/BPTestInspector/Internal/PrivateHeaders/Protocols/_XCTestObservationPrivate-Protocol.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/Protocols/_XCTestObservationPrivate-Protocol.h rename to BPTestInspector/Internal/PrivateHeaders/Protocols/_XCTestObservationPrivate-Protocol.h diff --git a/BPXCTestWrapper/PrivateHeaders/XCTest.h b/BPTestInspector/Internal/PrivateHeaders/XCTest.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/XCTest.h rename to BPTestInspector/Internal/PrivateHeaders/XCTest.h diff --git a/BPXCTestWrapper/PrivateHeaders/XCTestCase.h b/BPTestInspector/Internal/PrivateHeaders/XCTestCase.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/XCTestCase.h rename to BPTestInspector/Internal/PrivateHeaders/XCTestCase.h diff --git a/BPXCTestWrapper/PrivateHeaders/XCTestLog.h b/BPTestInspector/Internal/PrivateHeaders/XCTestLog.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/XCTestLog.h rename to BPTestInspector/Internal/PrivateHeaders/XCTestLog.h diff --git a/BPXCTestWrapper/PrivateHeaders/XCTestObserver.h b/BPTestInspector/Internal/PrivateHeaders/XCTestObserver.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/XCTestObserver.h rename to BPTestInspector/Internal/PrivateHeaders/XCTestObserver.h diff --git a/BPXCTestWrapper/PrivateHeaders/XCTestRun.h b/BPTestInspector/Internal/PrivateHeaders/XCTestRun.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/XCTestRun.h rename to BPTestInspector/Internal/PrivateHeaders/XCTestRun.h diff --git a/BPXCTestWrapper/PrivateHeaders/XCTestSuite.h b/BPTestInspector/Internal/PrivateHeaders/XCTestSuite.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/XCTestSuite.h rename to BPTestInspector/Internal/PrivateHeaders/XCTestSuite.h diff --git a/BPXCTestWrapper/PrivateHeaders/XCTestSuiteRun.h b/BPTestInspector/Internal/PrivateHeaders/XCTestSuiteRun.h similarity index 100% rename from BPXCTestWrapper/PrivateHeaders/XCTestSuiteRun.h rename to BPTestInspector/Internal/PrivateHeaders/XCTestSuiteRun.h diff --git a/BPXCTestWrapper/Internal/main.m b/BPTestInspector/Internal/main.m similarity index 86% rename from BPXCTestWrapper/Internal/main.m rename to BPTestInspector/Internal/main.m index f2df5aed..1c4d5483 100644 --- a/BPXCTestWrapper/Internal/main.m +++ b/BPTestInspector/Internal/main.m @@ -1,6 +1,6 @@ // // main.m -// BPXCTestWrapper +// BPTestInspector // // Created by Lucas Throckmorton on 5/29/23. // @@ -11,7 +11,7 @@ #import "BPXCTestUtils.h" #import "BPLoggingUtils.h" -#import "BPXCTestWrapperConstants.h" +#import "BPTestInspectorConstants.h" //void tearDown(int signal) { //} @@ -37,14 +37,14 @@ static void didLoad() { [BPLoggingUtils log:@"Returning."]; return; #endif - + // Grab relavent info from environment - NSString *bundlePath = NSProcessInfo.processInfo.environment[BPXCTestWrapperConstants.testBundleEnvironmentKey]; - NSString *outputPath = NSProcessInfo.processInfo.environment[BPXCTestWrapperConstants.outputPathEnvironmentKey]; + NSString *bundlePath = NSProcessInfo.processInfo.environment[BPTestInspectorConstants.testBundleEnvironmentKey]; + NSString *outputPath = NSProcessInfo.processInfo.environment[BPTestInspectorConstants.outputPathEnvironmentKey]; // Reset DYLD_INSERT_LIBRARIES and other env variables to avoid impacting future processes unsetenv("DYLD_INSERT_LIBRARIES"); - unsetenv(BPXCTestWrapperConstants.testBundleEnvironmentKey.UTF8String); - unsetenv(BPXCTestWrapperConstants.outputPathEnvironmentKey.UTF8String); + unsetenv(BPTestInspectorConstants.testBundleEnvironmentKey.UTF8String); + unsetenv(BPTestInspectorConstants.outputPathEnvironmentKey.UTF8String); if (!bundlePath || !outputPath) { return; diff --git a/BPXCTestWrapper/BPShimLogger.h b/BPXCTestWrapper/BPShimLogger.h deleted file mode 100644 index d8fe93bb..00000000 --- a/BPXCTestWrapper/BPShimLogger.h +++ /dev/null @@ -1,25 +0,0 @@ -//// -//// BPShimLogger.h -//// Experiment -//// -//// Created by Lucas Throckmorton on 5/30/23. -//// -// -//#import -// -//@interface BPShimLogger : NSObject -// -//#pragma mark - Properties -// -//@property (nonatomic, copy, nonnull, class, readonly) NSString *outputPathEnvironmentKey; -//@property (nonatomic, strong, nonnull, class, readonly) BPShimLogger *defaultLogger; -// -//#pragma mark - Methods -// -//- (void)log:(nonnull NSString *)text; -//- (void)tearDown; -// -//#pragma mark - Unavailable -//- (nonnull instancetype)init NS_UNAVAILABLE; -// -//@end diff --git a/BPXCTestWrapper/BPShimLogger.m b/BPXCTestWrapper/BPShimLogger.m deleted file mode 100644 index 5a219e96..00000000 --- a/BPXCTestWrapper/BPShimLogger.m +++ /dev/null @@ -1,73 +0,0 @@ -//// -//// BPShimLogger.m -//// Experiment -//// -//// Created by Lucas Throckmorton on 5/30/23. -//// -// -//#import "BPShimLogger.h" -// -//#import -// -//@interface BPShimLogger () -// -//@property (nonatomic, strong, nonnull) NSString *outputPath; -//@property (nonatomic, strong, nonnull) NSFileHandle *fileHandle; -// -//@end -// -//@implementation BPShimLogger -// -//#pragma mark - init -// -//- (nonnull instancetype)initWithOutputPath:(NSString *)outputPath { -// if (self = [super init]) { -// _outputPath = outputPath; -// [self createLogFileAtPath:outputPath]; -// } -// return self; -//} -// -//#pragma mark - Properties -// -//+ (NSString *)outputPathEnvironmentKey { -// return @"BPSHIM_OUTPUT_PATH"; -//} -// -//+ (BPShimLogger *)defaultLogger { -// // Grab and then unset env variable to avoid impacting future processes -// NSString *outputPath = NSProcessInfo.processInfo.environment[BPShimLogger.outputPathEnvironmentKey]; -// unsetenv(BPShimLogger.outputPathEnvironmentKey.UTF8String); -// -// static BPShimLogger * instance; -// static dispatch_once_t onceToken; -// dispatch_once(&onceToken, ^{ -// instance = [[self alloc] initWithOutputPath:outputPath]; -// }); -// return instance; -//} -// -//#pragma mark - public -// -//- (void)log:(NSString *)text { -// // First to console -// NSLog(@"__EXPERIMENT: %@", text); -// // Then to file -// NSString *line = [NSString stringWithFormat:@"%@\n", text]; -// [self.fileHandle seekToEndOfFile]; -// [self.fileHandle writeData:[line dataUsingEncoding:NSUTF8StringEncoding]]; -//} -// -//- (void)tearDown { -// // TODO: output EOF stuff -// // TODO: close file handle if needed. -//} -// -//#pragma mark - private -// -//- (void)createLogFileAtPath:(NSString *)outputPath { -// [[NSFileManager defaultManager] createFileAtPath:outputPath contents:nil attributes:nil]; -// self.fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:outputPath]; -//} -// -//@end diff --git a/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.pbxproj b/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.pbxproj deleted file mode 100644 index a6d0e1c0..00000000 --- a/BPXCTestWrapper/BPXCTestWrapper.xcodeproj/project.pbxproj +++ /dev/null @@ -1,438 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 56; - objects = { - -/* Begin PBXBuildFile section */ - FB019EA02A25612E00F25BE5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FB019E9F2A25612E00F25BE5 /* main.m */; }; - FB21F0212A61FBF100682AC7 /* XCTestCase.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105ED2A2595B50056706D /* XCTestCase.h */; }; - FB21F0222A61FBF400682AC7 /* XCTestRun.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105EC2A2595B50056706D /* XCTestRun.h */; }; - FB21F0232A61FBF600682AC7 /* XCTest.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105EB2A2595B50056706D /* XCTest.h */; }; - FB21F0242A61FBF900682AC7 /* XCTestLog.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105EA2A2595B50056706D /* XCTestLog.h */; }; - FB21F0252A61FBFB00682AC7 /* CDStructures.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105E92A2595B50056706D /* CDStructures.h */; }; - FB21F0262A61FBFD00682AC7 /* XCTestSuite.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105E82A2595B50056706D /* XCTestSuite.h */; }; - FB21F0272A61FC0000682AC7 /* XCTestSuiteRun.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105E72A2595B50056706D /* XCTestSuiteRun.h */; }; - FB21F0282A61FC0200682AC7 /* XCTestObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105E62A2595B50056706D /* XCTestObserver.h */; }; - FB21F0292A61FC0500682AC7 /* XCTActivity-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105EF2A2595B50056706D /* XCTActivity-Protocol.h */; }; - FB21F02A2A61FC0800682AC7 /* XCTIssueHandling-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105F02A2595B50056706D /* XCTIssueHandling-Protocol.h */; }; - FB21F02B2A61FC0A00682AC7 /* XCTestObservation-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105F12A2595B50056706D /* XCTestObservation-Protocol.h */; }; - FB21F02C2A61FC0C00682AC7 /* _XCTestObservationPrivate-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105F22A2595B50056706D /* _XCTestObservationPrivate-Protocol.h */; }; - FB21F02D2A61FC0E00682AC7 /* XCTWaiterDelegate-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105F32A2595B50056706D /* XCTWaiterDelegate-Protocol.h */; }; - FB21F02E2A61FC1100682AC7 /* NSObject-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105F42A2595B50056706D /* NSObject-Protocol.h */; }; - FB21F02F2A61FC1300682AC7 /* _XCTestObservationInternal-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB0105F52A2595B50056706D /* _XCTestObservationInternal-Protocol.h */; }; - FB21F0322A61FC6B00682AC7 /* BPTestCaseInfo+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0302A61FC6B00682AC7 /* BPTestCaseInfo+Internal.h */; }; - FB317F8A2A259F5000A9FADF /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB317F892A259F5000A9FADF /* XCTest.framework */; }; - FB9F19262A3296A100C894D3 /* BPXCTestUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FB9F19242A3296A100C894D3 /* BPXCTestUtils.h */; }; - FB9F19272A3296A100C894D3 /* BPXCTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9F19252A3296A100C894D3 /* BPXCTestUtils.m */; }; - FB9F192A2A329AFB00C894D3 /* BPLoggingUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FB9F19282A329AFB00C894D3 /* BPLoggingUtils.h */; }; - FB9F192B2A329AFB00C894D3 /* BPLoggingUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9F19292A329AFB00C894D3 /* BPLoggingUtils.m */; }; - FB9F192E2A32A7D000C894D3 /* BPTestCaseInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = FB9F192C2A32A7D000C894D3 /* BPTestCaseInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; - FB9F192F2A32A7D000C894D3 /* BPTestCaseInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9F192D2A32A7D000C894D3 /* BPTestCaseInfo.m */; }; - FBF6B1A02A609B6D00D7A64B /* BPXCTestWrapperConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = FBF6B19E2A609B6D00D7A64B /* BPXCTestWrapperConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; - FBF6B1A12A609B6D00D7A64B /* BPXCTestWrapperConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = FBF6B19F2A609B6D00D7A64B /* BPXCTestWrapperConstants.m */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - FB0105E62A2595B50056706D /* XCTestObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTestObserver.h; sourceTree = ""; }; - FB0105E72A2595B50056706D /* XCTestSuiteRun.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTestSuiteRun.h; sourceTree = ""; }; - FB0105E82A2595B50056706D /* XCTestSuite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTestSuite.h; sourceTree = ""; }; - FB0105E92A2595B50056706D /* CDStructures.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDStructures.h; sourceTree = ""; }; - FB0105EA2A2595B50056706D /* XCTestLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTestLog.h; sourceTree = ""; }; - FB0105EB2A2595B50056706D /* XCTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTest.h; sourceTree = ""; }; - FB0105EC2A2595B50056706D /* XCTestRun.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTestRun.h; sourceTree = ""; }; - FB0105ED2A2595B50056706D /* XCTestCase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCTestCase.h; sourceTree = ""; }; - FB0105EF2A2595B50056706D /* XCTActivity-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCTActivity-Protocol.h"; sourceTree = ""; }; - FB0105F02A2595B50056706D /* XCTIssueHandling-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCTIssueHandling-Protocol.h"; sourceTree = ""; }; - FB0105F12A2595B50056706D /* XCTestObservation-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCTestObservation-Protocol.h"; sourceTree = ""; }; - FB0105F22A2595B50056706D /* _XCTestObservationPrivate-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "_XCTestObservationPrivate-Protocol.h"; sourceTree = ""; }; - FB0105F32A2595B50056706D /* XCTWaiterDelegate-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCTWaiterDelegate-Protocol.h"; sourceTree = ""; }; - FB0105F42A2595B50056706D /* NSObject-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject-Protocol.h"; sourceTree = ""; }; - FB0105F52A2595B50056706D /* _XCTestObservationInternal-Protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "_XCTestObservationInternal-Protocol.h"; sourceTree = ""; }; - FB019E982A25609F00F25BE5 /* libBPXCTestWrapper.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libBPXCTestWrapper.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; - FB019E9F2A25612E00F25BE5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - FB21F0202A61F78D00682AC7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - FB21F0302A61FC6B00682AC7 /* BPTestCaseInfo+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BPTestCaseInfo+Internal.h"; sourceTree = ""; }; - FB317F892A259F5000A9FADF /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; - FB9F19242A3296A100C894D3 /* BPXCTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPXCTestUtils.h; sourceTree = ""; }; - FB9F19252A3296A100C894D3 /* BPXCTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPXCTestUtils.m; sourceTree = ""; }; - FB9F19282A329AFB00C894D3 /* BPLoggingUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPLoggingUtils.h; sourceTree = ""; }; - FB9F19292A329AFB00C894D3 /* BPLoggingUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPLoggingUtils.m; sourceTree = ""; }; - FB9F192C2A32A7D000C894D3 /* BPTestCaseInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPTestCaseInfo.h; sourceTree = ""; }; - FB9F192D2A32A7D000C894D3 /* BPTestCaseInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPTestCaseInfo.m; sourceTree = ""; }; - FBF6B19E2A609B6D00D7A64B /* BPXCTestWrapperConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPXCTestWrapperConstants.h; sourceTree = ""; }; - FBF6B19F2A609B6D00D7A64B /* BPXCTestWrapperConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPXCTestWrapperConstants.m; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - FB019E962A25609F00F25BE5 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - FB317F8A2A259F5000A9FADF /* XCTest.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - FB0105E52A2595B50056706D /* PrivateHeaders */ = { - isa = PBXGroup; - children = ( - FB0105E62A2595B50056706D /* XCTestObserver.h */, - FB0105E72A2595B50056706D /* XCTestSuiteRun.h */, - FB0105E82A2595B50056706D /* XCTestSuite.h */, - FB0105E92A2595B50056706D /* CDStructures.h */, - FB0105EA2A2595B50056706D /* XCTestLog.h */, - FB0105EB2A2595B50056706D /* XCTest.h */, - FB0105EC2A2595B50056706D /* XCTestRun.h */, - FB0105ED2A2595B50056706D /* XCTestCase.h */, - FB0105EE2A2595B50056706D /* Protocols */, - ); - path = PrivateHeaders; - sourceTree = ""; - }; - FB0105EE2A2595B50056706D /* Protocols */ = { - isa = PBXGroup; - children = ( - FB0105EF2A2595B50056706D /* XCTActivity-Protocol.h */, - FB0105F02A2595B50056706D /* XCTIssueHandling-Protocol.h */, - FB0105F12A2595B50056706D /* XCTestObservation-Protocol.h */, - FB0105F22A2595B50056706D /* _XCTestObservationPrivate-Protocol.h */, - FB0105F32A2595B50056706D /* XCTWaiterDelegate-Protocol.h */, - FB0105F42A2595B50056706D /* NSObject-Protocol.h */, - FB0105F52A2595B50056706D /* _XCTestObservationInternal-Protocol.h */, - ); - path = Protocols; - sourceTree = ""; - }; - FB019E8F2A25609F00F25BE5 = { - isa = PBXGroup; - children = ( - FB21F0202A61F78D00682AC7 /* Info.plist */, - FBF6B19E2A609B6D00D7A64B /* BPXCTestWrapperConstants.h */, - FBF6B19F2A609B6D00D7A64B /* BPXCTestWrapperConstants.m */, - FB9F192C2A32A7D000C894D3 /* BPTestCaseInfo.h */, - FB9F192D2A32A7D000C894D3 /* BPTestCaseInfo.m */, - FB21F0342A61FC7D00682AC7 /* Internal */, - FB0105E52A2595B50056706D /* PrivateHeaders */, - FB019E992A25609F00F25BE5 /* Products */, - FB317F882A259F5000A9FADF /* Frameworks */, - ); - sourceTree = ""; - }; - FB019E992A25609F00F25BE5 /* Products */ = { - isa = PBXGroup; - children = ( - FB019E982A25609F00F25BE5 /* libBPXCTestWrapper.dylib */, - ); - name = Products; - sourceTree = ""; - }; - FB21F0342A61FC7D00682AC7 /* Internal */ = { - isa = PBXGroup; - children = ( - FB019E9F2A25612E00F25BE5 /* main.m */, - FB9F19242A3296A100C894D3 /* BPXCTestUtils.h */, - FB9F19252A3296A100C894D3 /* BPXCTestUtils.m */, - FB9F19282A329AFB00C894D3 /* BPLoggingUtils.h */, - FB9F19292A329AFB00C894D3 /* BPLoggingUtils.m */, - FB21F0302A61FC6B00682AC7 /* BPTestCaseInfo+Internal.h */, - ); - path = Internal; - sourceTree = ""; - }; - FB317F882A259F5000A9FADF /* Frameworks */ = { - isa = PBXGroup; - children = ( - FB317F892A259F5000A9FADF /* XCTest.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - FB019E942A25609F00F25BE5 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - FB21F0232A61FBF600682AC7 /* XCTest.h in Headers */, - FB21F0282A61FC0200682AC7 /* XCTestObserver.h in Headers */, - FB21F02F2A61FC1300682AC7 /* _XCTestObservationInternal-Protocol.h in Headers */, - FB21F0292A61FC0500682AC7 /* XCTActivity-Protocol.h in Headers */, - FB21F0252A61FBFB00682AC7 /* CDStructures.h in Headers */, - FB21F0262A61FBFD00682AC7 /* XCTestSuite.h in Headers */, - FB21F02B2A61FC0A00682AC7 /* XCTestObservation-Protocol.h in Headers */, - FB9F192A2A329AFB00C894D3 /* BPLoggingUtils.h in Headers */, - FB9F192E2A32A7D000C894D3 /* BPTestCaseInfo.h in Headers */, - FB9F19262A3296A100C894D3 /* BPXCTestUtils.h in Headers */, - FBF6B1A02A609B6D00D7A64B /* BPXCTestWrapperConstants.h in Headers */, - FB21F02E2A61FC1100682AC7 /* NSObject-Protocol.h in Headers */, - FB21F02C2A61FC0C00682AC7 /* _XCTestObservationPrivate-Protocol.h in Headers */, - FB21F02A2A61FC0800682AC7 /* XCTIssueHandling-Protocol.h in Headers */, - FB21F0212A61FBF100682AC7 /* XCTestCase.h in Headers */, - FB21F0272A61FC0000682AC7 /* XCTestSuiteRun.h in Headers */, - FB21F0242A61FBF900682AC7 /* XCTestLog.h in Headers */, - FB21F0322A61FC6B00682AC7 /* BPTestCaseInfo+Internal.h in Headers */, - FB21F02D2A61FC0E00682AC7 /* XCTWaiterDelegate-Protocol.h in Headers */, - FB21F0222A61FBF400682AC7 /* XCTestRun.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - FB019E972A25609F00F25BE5 /* BPXCTestWrapper */ = { - isa = PBXNativeTarget; - buildConfigurationList = FB019E9C2A25609F00F25BE5 /* Build configuration list for PBXNativeTarget "BPXCTestWrapper" */; - buildPhases = ( - FB019E942A25609F00F25BE5 /* Headers */, - FB019E952A25609F00F25BE5 /* Sources */, - FB019E962A25609F00F25BE5 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = BPXCTestWrapper; - productName = Experiment; - productReference = FB019E982A25609F00F25BE5 /* libBPXCTestWrapper.dylib */; - productType = "com.apple.product-type.library.dynamic"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - FB019E902A25609F00F25BE5 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastUpgradeCheck = 1410; - TargetAttributes = { - FB019E972A25609F00F25BE5 = { - CreatedOnToolsVersion = 14.1; - }; - }; - }; - buildConfigurationList = FB019E932A25609F00F25BE5 /* Build configuration list for PBXProject "BPXCTestWrapper" */; - compatibilityVersion = "Xcode 14.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = FB019E8F2A25609F00F25BE5; - productRefGroup = FB019E992A25609F00F25BE5 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - FB019E972A25609F00F25BE5 /* BPXCTestWrapper */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - FB019E952A25609F00F25BE5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - FB9F192B2A329AFB00C894D3 /* BPLoggingUtils.m in Sources */, - FB9F192F2A32A7D000C894D3 /* BPTestCaseInfo.m in Sources */, - FB9F19272A3296A100C894D3 /* BPXCTestUtils.m in Sources */, - FB019EA02A25612E00F25BE5 /* main.m in Sources */, - FBF6B1A12A609B6D00D7A64B /* BPXCTestWrapperConstants.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - FB019E9A2A25609F00F25BE5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - 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_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - "IPHONEOS_DEPLOYMENT_TARGET[sdk=*]" = 16.0; - MACOSX_DEPLOYMENT_TARGET = 13.0; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; - }; - name = Debug; - }; - FB019E9B2A25609F00F25BE5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - 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_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - "IPHONEOS_DEPLOYMENT_TARGET[arch=*]" = 16.0; - MACOSX_DEPLOYMENT_TARGET = 13.0; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = macosx; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; - }; - name = Release; - }; - FB019E9D2A25609F00F25BE5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 57Y47U492U; - DRIVERKIT_DEPLOYMENT_TARGET = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - EXECUTABLE_PREFIX = lib; - INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - MACOSX_DEPLOYMENT_TARGET = 13.0; - PRODUCT_BUNDLE_IDENTIFIER = com.linkedin.bpXCTestWrapper; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - TVOS_DEPLOYMENT_TARGET = ""; - WATCHOS_DEPLOYMENT_TARGET = ""; - }; - name = Debug; - }; - FB019E9E2A25609F00F25BE5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 57Y47U492U; - DRIVERKIT_DEPLOYMENT_TARGET = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - EXECUTABLE_PREFIX = lib; - INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - MACOSX_DEPLOYMENT_TARGET = 13.0; - PRODUCT_BUNDLE_IDENTIFIER = com.linkedin.bpXCTestWrapper; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - TVOS_DEPLOYMENT_TARGET = ""; - WATCHOS_DEPLOYMENT_TARGET = ""; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - FB019E932A25609F00F25BE5 /* Build configuration list for PBXProject "BPXCTestWrapper" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - FB019E9A2A25609F00F25BE5 /* Debug */, - FB019E9B2A25609F00F25BE5 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - FB019E9C2A25609F00F25BE5 /* Build configuration list for PBXNativeTarget "BPXCTestWrapper" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - FB019E9D2A25609F00F25BE5 /* Debug */, - FB019E9E2A25609F00F25BE5 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = FB019E902A25609F00F25BE5 /* Project object */; -} diff --git a/BPXCTestWrapper/Info.plist b/BPXCTestWrapper/Info.plist deleted file mode 100644 index 520851d4..00000000 --- a/BPXCTestWrapper/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSHumanReadableCopyright - Copyright © 2018 LinkedIn. All rights reserved. - NSPrincipalClass - - - diff --git a/BPXCTestWrapper/XCTestLog+Shim.h b/BPXCTestWrapper/XCTestLog+Shim.h deleted file mode 100644 index a690790c..00000000 --- a/BPXCTestWrapper/XCTestLog+Shim.h +++ /dev/null @@ -1,12 +0,0 @@ -//// -//// XCTestLog+Shim.h -//// Experiment -//// -//// Created by Lucas Throckmorton on 5/29/23. -//// -// -//#import "XCTestLog.h" -// -//@interface XCTestLog (Shim) -// -//@end diff --git a/BPXCTestWrapper/XCTestLog+Shim.m b/BPXCTestWrapper/XCTestLog+Shim.m deleted file mode 100644 index 06fc4622..00000000 --- a/BPXCTestWrapper/XCTestLog+Shim.m +++ /dev/null @@ -1,111 +0,0 @@ -//// -//// XCTestLog+Shim.m -//// Experiment -//// -//// Created by Lucas Throckmorton on 5/29/23. -//// -// -//#import "XCTestLog+Shim.h" -// -//#import -//#import -//#import "XCTestSuiteRun.h" -//#import "XCTestCase.h" -// -//#import "BPShimLogger.h" -// -//static NSString *swizzlePrefix = @"bpLogicTestShim"; -// -//#pragma mark - Private Interface -// -//@interface XCTestLog (Shim_Private) -// -//@property (nonatomic, strong, nullable) NSTimer *testSuiteTimer; -//@property (nonatomic, strong, nullable) NSTimeZone *testCaseTimer; -// -//@end -// -//#pragma mark - Implementation -// -//@implementation XCTestLog (Shim) -// -//#pragma mark - constants -// -//+ (NSArray *)swizzledTestLogMethodNames { -// return @[ -// @"testSuiteWillStart:", -// @"testSuiteDidFinish:", -// @"testCaseWillStart:", -// @"testCaseDidFinish:", -// @"testCaseDidFail:withDescription:inFile:atLine:", -// @"testCase:wasSkippedWithDescription:inFile:atLine:", -// ]; -//} -// -//#pragma mark - load -// -//+ (void)load { -// static dispatch_once_t onceToken; -// dispatch_once(&onceToken, ^{ -// Class class = [self class]; -// for (NSString *methodName in self.swizzledTestLogMethodNames) { -// NSString *newMethodName = [NSString stringWithFormat:@"%@_%@", swizzlePrefix, methodName]; -// SEL originalSelector = NSSelectorFromString(methodName); -// SEL swizzledSelector = NSSelectorFromString(newMethodName); -// -// Method originalMethod = class_getInstanceMethod(class, originalSelector); -// Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); -// -// method_exchangeImplementations(originalMethod, swizzledMethod); -// } -// }); -//} -// -//#pragma mark - XCTestLog Lifecycle -// -//// TODO: set up timer per test suite and per test case -// -//- (void)bpLogicTestShim_testSuiteWillStart:(XCTestSuite *)suite { -// [BPShimLogger.defaultLogger log:@"testSuiteWillStart: called"]; -// [self bpLogicTestShim_testSuiteWillStart:suite]; -//} -// -//- (void)bpLogicTestShim_testSuiteDidFinish:(XCTestSuite *)suite { -// [BPShimLogger.defaultLogger log:@"testSuiteDidFinish: called"]; -// [self bpLogicTestShim_testSuiteDidFinish:suite]; -//} -// -//- (void)bpLogicTestShim_testCaseWillStart:(XCTestCase *)testCase { -// [BPShimLogger.defaultLogger log:@"testCaseWillStart: called"]; -// [self bpLogicTestShim_testCaseWillStart:testCase]; -//} -// -//- (void)bpLogicTestShim_testCaseDidFinish:(XCTestCase *)testCase { -// [BPShimLogger.defaultLogger log:@"testCaseDidFinish: called"]; -// [self bpLogicTestShim_testCaseDidFinish:testCase]; -// -//} -// -//- (void)bpLogicTestShim_testCase:(XCTestCase *)testCase -// didFailWithDescription:(NSString *)description -// inFile:(NSString *)file -// atLine:(NSUInteger)line { -// [BPShimLogger.defaultLogger log:@"testCase:didFailWithDescription:inFile:atLine: called"]; -// [self bpLogicTestShim_testCase:testCase -// didFailWithDescription:description -// inFile:file -// atLine:line]; -//} -// -//- (void)bpLogicTestShim_testCase:(XCTestCase *)testCase -// wasSkippedWithDescription:(NSString *)description -// inFile:(NSString *)file -// atLine:(unsigned long long)line { -// [BPShimLogger.defaultLogger log:@"testCase:wasSkippedWithDescription:inFile:atLine: called"]; -// [self bpLogicTestShim_testCase:testCase -// wasSkippedWithDescription:description -// inFile:file -// atLine:line]; -//} -// -//@end diff --git a/Bluepill.xcworkspace/contents.xcworkspacedata b/Bluepill.xcworkspace/contents.xcworkspacedata index 80b8f74f..a563c0e0 100644 --- a/Bluepill.xcworkspace/contents.xcworkspacedata +++ b/Bluepill.xcworkspace/contents.xcworkspacedata @@ -2,7 +2,7 @@ + location = "group:BPTestInspector/BPTestInspector.xcodeproj"> diff --git a/Configs/BPMacTestInspector.xcconfig b/Configs/BPMacTestInspector.xcconfig new file mode 100644 index 00000000..371f806f --- /dev/null +++ b/Configs/BPMacTestInspector.xcconfig @@ -0,0 +1,8 @@ +CURRENT_PROJECT_VERSION = 1 +DYLIB_CURRENT_VERSION = 1 +DYLIB_COMPATIBILITY_VERSION = 1 +PRODUCT_NAME = BPMacTestInspector +EXECUTABLE_PREFIX = lib +SDKROOT = macosx +MACOSX_DEPLOYMENT_TARGET = 11.0 +COPY_PHASE_STRIP = NO diff --git a/Configs/BPTestInspector.xcconfig b/Configs/BPTestInspector.xcconfig new file mode 100644 index 00000000..964e63f0 --- /dev/null +++ b/Configs/BPTestInspector.xcconfig @@ -0,0 +1,9 @@ +CURRENT_PROJECT_VERSION = 1 +DYLIB_CURRENT_VERSION = 1 +DYLIB_COMPATIBILITY_VERSION = 1 +PRODUCT_NAME = BPTestInspector +EXECUTABLE_PREFIX = lib +SDKROOT = iphonesimulator +IPHONEOS_DEPLOYMENT_TARGET = 16.0 +COPY_PHASE_STRIP = NO +CODE_SIGN_IDENTITY = - // LTHROCKM - may need code signing for hosted tests diff --git a/bluepill/bluepill.xcodeproj/project.pbxproj b/bluepill/bluepill.xcodeproj/project.pbxproj index 0cacde31..5b523931 100644 --- a/bluepill/bluepill.xcodeproj/project.pbxproj +++ b/bluepill/bluepill.xcodeproj/project.pbxproj @@ -37,11 +37,11 @@ C4D6861A2267ABEF007D4237 /* bplib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4D686192267ABEF007D4237 /* bplib.framework */; }; C4FD8C581DB6E09B000ED28C /* BPPacker.m in Sources */ = {isa = PBXBuildFile; fileRef = C4FD8C571DB6E09B000ED28C /* BPPacker.m */; }; E49235FF22EA847700395D98 /* times.json in Resources */ = {isa = PBXBuildFile; fileRef = E49235FE22EA847700395D98 /* times.json */; }; + FB21F0BF2A622FFE00682AC7 /* libBPMacTestInspector.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB21F0BE2A622FFD00682AC7 /* libBPMacTestInspector.dylib */; }; + FB21F0C02A622FFE00682AC7 /* libBPMacTestInspector.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB21F0BE2A622FFD00682AC7 /* libBPMacTestInspector.dylib */; }; FBA69FA72A45F889003BADBF /* BPLogicTestFixture_arm64.xctest in Resources */ = {isa = PBXBuildFile; fileRef = FBA69FA52A45F889003BADBF /* BPLogicTestFixture_arm64.xctest */; }; FBA69FA82A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest in Resources */ = {isa = PBXBuildFile; fileRef = FBA69FA62A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest */; }; FBF6B19D2A5F272500D7A64B /* BPLogicTestFixture_swift_x86_64.xctest in Resources */ = {isa = PBXBuildFile; fileRef = FBF6B19C2A5F272500D7A64B /* BPLogicTestFixture_swift_x86_64.xctest */; }; - FBF6B1B82A61CE4200D7A64B /* libBPXCTestWrapper.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FBF6B1B72A61CE4200D7A64B /* libBPXCTestWrapper.dylib */; }; - FBF6B1B92A61CE4200D7A64B /* libBPXCTestWrapper.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FBF6B1B72A61CE4200D7A64B /* libBPXCTestWrapper.dylib */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -90,10 +90,10 @@ C4FD8C561DB6E09B000ED28C /* BPPacker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPPacker.h; sourceTree = ""; }; C4FD8C571DB6E09B000ED28C /* BPPacker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPPacker.m; sourceTree = ""; }; E49235FE22EA847700395D98 /* times.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = times.json; sourceTree = ""; }; + FB21F0BE2A622FFD00682AC7 /* libBPMacTestInspector.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libBPMacTestInspector.dylib; sourceTree = ""; }; FBA69FA52A45F889003BADBF /* BPLogicTestFixture_arm64.xctest */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = BPLogicTestFixture_arm64.xctest; sourceTree = ""; }; FBA69FA62A45F889003BADBF /* BPLogicTestFixture_x86_64.xctest */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = BPLogicTestFixture_x86_64.xctest; sourceTree = ""; }; FBF6B19C2A5F272500D7A64B /* BPLogicTestFixture_swift_x86_64.xctest */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = BPLogicTestFixture_swift_x86_64.xctest; sourceTree = ""; }; - FBF6B1B72A61CE4200D7A64B /* libBPXCTestWrapper.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libBPXCTestWrapper.dylib; path = "../../../../Library/Developer/Xcode/DerivedData/Bluepill-bzfbmrywvaiilwcxmhvmpqnjsiso/Build/Products/Debug/libBPXCTestWrapper.dylib"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -103,7 +103,7 @@ files = ( C4D6861A2267ABEF007D4237 /* bplib.framework in Frameworks */, B3109F792151F72F00B9309C /* CoreSimulator.framework in Frameworks */, - FBF6B1B92A61CE4200D7A64B /* libBPXCTestWrapper.dylib in Frameworks */, + FB21F0C02A622FFE00682AC7 /* libBPMacTestInspector.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -111,8 +111,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + FB21F0BF2A622FFE00682AC7 /* libBPMacTestInspector.dylib in Frameworks */, C45F9E82267E8A8800969CC3 /* bplib.framework in Frameworks */, - FBF6B1B82A61CE4200D7A64B /* libBPXCTestWrapper.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -122,7 +122,7 @@ B3380AED2150BD8600752E1B /* Frameworks */ = { isa = PBXGroup; children = ( - FBF6B1B72A61CE4200D7A64B /* libBPXCTestWrapper.dylib */, + FB21F0BE2A622FFD00682AC7 /* libBPMacTestInspector.dylib */, C4D686192267ABEF007D4237 /* bplib.framework */, B3380AEE2150BD8700752E1B /* CoreSimulator.framework */, BA1896B821791A14000CEC36 /* XCTest.framework */, @@ -381,6 +381,10 @@ HEADER_SEARCH_PATHS = "\"$(SRCROOT)/..\""; INFOPLIST_FILE = tests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = LI.BluepillRunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -398,6 +402,10 @@ HEADER_SEARCH_PATHS = "\"$(SRCROOT)/..\""; INFOPLIST_FILE = tests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = LI.BluepillRunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/bluepill/libBPMacTestInspector.dylib b/bluepill/libBPMacTestInspector.dylib new file mode 100755 index 0000000000000000000000000000000000000000..0ed8f75feb3897025c723728361023f0900b0411 GIT binary patch literal 173584 zcmeI53wRsVmH4l02TUNrJOY725GJ81B$ksniSZ^uvTVl=eqkw(7P1pr8rw=FjiMPj zF{G_R0>m3&4NWQA(l#v%B`Hh8e$b_KX|t4uKzGZtKq;juq$O-&f$~adgZG@7bEVNp zvgvmF?f&Y%cV;?>610h72ONe73FBifqgljq!#bl^E0Sm&F zf$jV%C{0F@6BA$pOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>N zfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>N zfC(@GCh*@&;PDUN_y`vMyI|qJI0m+pVH=q!#MJ_q`wMXfY*Rt0D8c4cL9;6V!<#SS z;{z~FpzSzQLVrWuXpjJzGhTVUrTayhl}kD6Q6VzfG87a=H`2X&w#pgrr3OoN0a>67 z&ojuTN{WYL#$?4)2Gm$mO__53cq{+PG!rYVOrT6%wQ36br}eH4D{<8rh(&dyV7zN6 z-ZK;rW!wj)lA?rlBNa{blYw)*b&D*;F49X`#>?v~R4R(uzi=$1>xocYb;kQ8#hXI` zq&!YM9503|81Ll8mS;6}M9Lo{o-vdxj8}WHD^PaCBaM|&yl6bsui~zC_OI0u59RUp>wYk>;@FzXP}xhlL&iSh zCi|@@v5=w<#e21wk~C5!QhmnCyJ>JJ z+v8c<+$Y7;)mTdHgK5+`-fdS|wtL70WqUk0ev9o_b3Va{&Up1(tU%HUWqUl(N|k9S z`9!}H>{&@!& z*kk$xY}n2lo7eri<>@7TH_rY3t2b<#l%5N9ZrE_NR;!cDvdP?4SB~E+?D6nHNo!!k zSlD;r`UafBvs|9QkZ!1PPkXdC6-o_xT2gRz+@Ph_>7EuXoq(Ml)e<#*vevUgP3cgh zd;B&%gv0ptuw~0$_Jg$taE7Sy*{K)v`y`yY`F>UxYZ~pI*d88h+4juw+$_cz zk6%lvV~@$_$+v;{=fj2^ldzw$txu7R`yJESu$lUw!*Q|9tl|$Qzyz286JP>NfC(@G zCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@G zCcp%k!2hoVmbVW7ruF&>WtBp#{t*dN?83~f;9Kci;R^;@x4d4}F}$yB`0=*kXIigs zexh}_zIFSg!|?ant;4OAt=s3TX7Q0j*Tdh!w+{cP_3FLGg7)EXW&7}zRqez2)Yk2b zA3&4s^KZlK`Y8wDujSi^f88;BxMTS3IeSN^grTB!*zax~e!g|f!&R60F8BSlPq8|P z-3WBI4nN&G{M?~hvx!FzRYBf1ysv%x;!3meU$qZ^bm$DI+`8A8+&YZn=#)ti^>?3w zW?yWB_O>rRdJMMmE8pmIQ1=$pwGKam!tf)9_Q9Y2W7oGYo(k~}J&5A=`M09Y^;4Fc zy?dm6`0X~cf0dV+y&Iiz7X(CxM5=B0+1BAdfZtZLy&2bo+Bf&iVZ`b0Bte-9J8CH^*=A^-#&amavVL7?d5XVqZZ81J^>xO0^P5l@&)K= zJ8**40v0^q=(BCZ4}<3KEN0MUv+BoCb%(tQhA@aRwnMn#m)nP5Ya8C%KKvH!MWA%K zZ?#3rY`=B;(n@QWey=y@;jxSb7ReOR=;cOCc=%081ONbT5{^jHTUBGS^>ZaLq_9csvY+FZ^+I^q--8 zKa_Vsc_WnXg7O!k3}(2_BZF&B+)#fj)Sq-TSC4C7u>K*aPl5gGEkbO8@(9{MeGJMM zd^9?G7L->(`D;-2LRo?Gtx&!d?7dL_I+UxRekqi{3gv@PKNHG3p+Wvufz?M{OWXXgb{Og!mv=U`H;W=h zrrPfdG%H=pgWb!6VurV7hSxL0E82stJzf4wI^46}!LFt*5!WK=nA(XeOwFZcW(gOa zY7w*8myX4-peI9N6`IQHr5_j1g%XBN3V%pf+i*E(XTYyTaBZ!*V95+*z(S@*Sf3M9 zn-hc4l$MCA3F8uVNN8yznKrsZ#(+Z+O+*c0ExlYBH3nL=R6Jxfh*&7mpN7SWzW#)! z8&PwGR7bAE(ZnDuqQXT$0@mW0%ccZI!Vn$V!8T+E+mIVIgD9fr{GiLG8?K{lNojF}hm0D86&?Eopwv`Dwu>a{s$uIajKuKc!eS)ZfKqP2 zrr2WNOASJ??Nuc^QmD>hQu23f&|BRZ2$<`kt7rLVb7 z1>V|*df}a0hdGF1u&@ZdGs#qgT3Y7S)zy+WYUj-rw((eN&d!2!9j;vhXE98eqYTM) z((x58Tpy21v~l|tt#!vToT4y?90T^2^(VppZm7Q0C0rl4MA-(Yesa|WSIAY4(@e>< z|5!^U*OB97Yfn<&fsI0v{j?-l%SZkx(w`?4luP~1q{r#so}~Wkq(9NFgtF9shx8Lm z=pP{caV7K*kK_M1>2Vz-rdzgH`CiJWZnb3m^yZ3OVggKn2`~XBzyz286JP>NfC(@G zCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@G zCcp%k025#WOn?b60Vco%m;e)C0!)AjFaaj;ClQ!R@1d`zd=}*kDECsnjPfAm`0#+4 zwAmY!?}?Z1x3}v`m2vIAbN#=JQ_9C#kIyH-Cmx)?#M1cbeeTk3A=TGA>N`p9rCdG~ z!M?5^W0V^2`}BN(5#my{S+-a_cSCgrZ1R26Td7~yQv3M)24t~{o-Z&`LcfOeUn!v< zApN&W=<%sK*pCPC`8kljOLo5``5BU5CHZZVe^2s>HjdcenP!yShvagngG! z`5XwD%i|dNVv^6zaGTQjAN3i_M!rA1lt+f_4$*!tW#_%BlqSD^renWI?F7eo_h+PL z?0k(>mxwFXoHGF?zyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO z1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaaiz zBXH8W?o8#>WM!aQtn~LRP1g4K@L$p$XbSkue~qjBLEoCweLbx`{vLnQovcb$CTCts zAx`otU5(59ioe|#2q@E)&OpFOMHBtv($xLOjGSDc?dn~th7FNfbMFZ0I+}yNoVV2P z9HrzMd2j?w6|Hw|SW)9iV@L^y!UL+vY|EPLEq8;7X%W}^18O27){Bu-ypf?;MA40O zuhJV$MAVcRITa!){uaNM(2Y>Su=?tcg>=0~?Nd`~BCLwNl~YGVO2EkEU|LdI+%yuP zsB|a-Y79oCL2!|(f&rV|2*qMr7;C1=8tPaeom5kEQR9{xEomrW>;l%#wAZFoJ*g!k zdQh`BQY%e#)#y%X5D%Cnv_#0z;?b}Y){;XINhX)If5d_cvG-bdLPD88q^oU-K21z) zOXx|n>k@}9YAeJomKE*K#_pi18=}g-m#D6{LLADEczKbCTU-_5t!wbn3})JroHS|D zwXWM-cepaHi6@%>P_fS?#AMjY>n*$2NvwK)G6OP0^E=pb1>w<1lld``tq zkSCueMAcTSf8Rmn0dm)|`SMvH_m#WErw`v_nxShAe8}VL zFzBb-WYkZGZ4zvlQ0{c-(O({q)8Ii5=r3ioFXnI8qrW_U1LQB|QvOoE4eZai?eBjg zIg^2gA-xQ;+bmfhla9-mG5%Qm>cHLvMKPK|F2+xuZ>LYR#xG^r|7*z~W#nhq)-{J7L>(O7H|9?mRQkMQ1hreBq{&K!}fc&K_{h``S zD0kZIQAU3`zdT3&Qbs)(O7%M~BH@%F^H2zFm+0a{fB?cx(SjS^7KM zx9ibg&Uba>FJp#1O8!!o{?7L8di0m`X^Q-%Ed8DB+x6%#=ii&iU&_+o z*}l|&7wp}*V<y_bnNlG1F7&bwDlb;`(3S=vdEV$ z%dGIbnj=o3L9tkc-+uGH5NE%V=J%~LKwsgV)pTD_*u|X3xL?hS+y>acZ|C0v0jJY!>W|$2?r0)vtc)519aaBsMvrX@>DSs*@Kb>@od+MGyprp*7YI=+0hiyfwOrR zM@YKKDbkWlV>hZc~-OPV1 z)9JZp3p(2w$nS;C)*6oz{B(D70Oq88JF9s)9Q;kfIhQ)8AX`_RY^E#Oey#BJwD}s_ zp~d3y%>H#N?QMZz35xE;v&Cg$Y^lw=L@*)3r@iWI&Z@JlP=)nUO{cw zuI%+hUX-2JFPkl2=Zakqv&Z|`*RR~kFt%OZZZA6Z$TNtPg;QA0!1`)Y?6L^=f0#ae z>4d%TW8EcJ%mZ7fFZ)20nWj*7Ed-lYmYtX^#LtuN zG6BB~e4OOX8o`Z7;RS5=d`NW{)zzty`Z)`F)j4$wBJ<{{3l>~7XWrc2UUgx;x-eWD zSvX(pd-2vAy0805{i5rPt%nxtcb@ss`nx7Q`oOerzWLb6FCE>vFWb(4v2e?fd!+N* z>g4ps>n1GQxu>UdZRUrMoO#>q!%r=|<8=QKyqg{~{kr@0%t`&ny|?1k-+yuIPpeK@ zyXm^)Dskbe0q12_M}=kNN*RocGS`u0{l_vDl7%DPrf7~OR5rYo*~;lb^# z!?V6Mp8g#13;*Z!g_}K>>c?HaY*negvB zkNd;7kG|0Pb9ek_!8w=oM85vQo3FgG{O;e37yr-IADO!F8_{PbeR8s|9h<+g3ShpnurmphGwkjdEJ59NHlCLnRwiyb?VR(2tmOxWHcNypyRWn z5xAX?_C?jyA`e=Dv2Ol%>5@erDokADvEnT9n5`_C_~A>(BMc0!)AjFaajO z1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO z1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO z1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%n85!!0*`^HUdJKNl^@S zqd@{>&UgnGSh~yMEjO4@rlM6*Au?9M`cxF%NcZa5DrY<|z2{wa8D)FCN{T06o@2#R z2Gkh5PY$9wRORen-6Bh|i}X^K5%R_kmGov-^W97dcnhX8 z-Y+TM910-iapK{4FQ#Ry8nkj`tRdw}t|s?Cg__jrkd{Jr4s=lqO%$r}(?z-JPc1Ykmq*BFBA<8y-7& z#wtp`W;@MF#;%e$(zJJeQ2!$E?ow0b z9Pb*6x04)EE_Vj8$?(2b8Op46#uHmD;{oXd@>ub3erknHffw9%UeesuX3b9ULU;<0 zp`7HZK1+5%hMP?Ant3zMRLHG*%fge^Q=XU>hL@(o>*;VSd_`T2`9@SLQne77S}g9h zrbaL1&!bFf>c0rFAIb+HAAua%W1HI^65>mc*Z(W{ohZcdQ0|5e)77+9K#uQjhZpRX zOoxBsIKasA6Ody&XLRVD_j?zea?KYzu07cK%&T*u&J7!m)@pTdG>6wIX z1%5AC0~^LNj~mxF;1r(a@&tx-LyddnJ8)Z4aCO|ErPk@577bn;ZoX%%rcc&-R#>l0 z(>;Eh9%5qrdV73v5G0qd_Jg$taHg*D*{K)v`y`yY`F>UxYZ~pI*d88h`Qy1+j4>X+ zmQu$alh2cH1M$y?4LK%ZKVw^;A{qBPrn6x~edZT%Tr9&HPE3FaFaajO1egF5U;<2l z2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z3H)y&u%=B^o#QV5+1_&HS+Oiv-f1h3ZgD;N{>t)Q?{-z}I(p@VT}S<{8{aFh*!^yK z`R;eh%XXvxp*3Zz4xCrE3(LOCp}a-h2!AKOj=4coiqbKo;&t1bt* zy4mW-E9Ix`J_xo)uk=A59=h}E;A?Li$BVw-1D}^64>wtU2fz=O3GBdjVAX@E+hWsw zmUOp~?lzllkaU|!_amE5_9G7ccz@-C&?jJUW!bKyz*qLA(VOeTWf0>%=+`%Gbt|9_ z_d}U4XLl*o9fUgU@5|*=cE4hBZ-n?h;QpgKL(rb6@E$?-<*rQ6IVe|>T$bs9zvVww zoIrAUrss5&t4OZM^qc}R?(4Vam#q?I=RAw$Cv4?Cw(^LroV1m@ZRJ`juMd=&$Lm{5 ze`p?Swey4-zZ?Ff{mAGjbi-V;F!g;Xbs%-^%hvO0_CPZf(_Ispu88#XF>ThP@aBtbkqyw5R~0ez7^~np?nsUtDwFO z%2T0y5b7_0@~Kd+g!(y9u7dIos6QFXCqVfSl+S>2C6vv@IYCu7+7fzF4I5gDR(dpc zE35p$=0H$c+2iZ(Ztj8ALS0MS{IGTy>Kd1KHnlg4B1Wd#?+Y|5UCV>r%Y$Nuw`PXd zGs7#|gRMPX{!2RCv)#e2rY;fJBI%ggi7P_QrD0|X7jc z7rTWLhE589NLSl%`DSOpuSKGXeqpV2Tp2Y6TC`L=WHbm^&}NQMh=>~2B5GIfT983| zi8|CEqKQFRvSdWHgb->Xr*G&Z19Qca*)s!pHNpa&nA)5ejHa|iTum4dL}+OvnKrsZ z#(+Z+O+*b53nlu~uoltRpU`w88pZ*2dAxE;oj#^g@o4JiVDEzUIuHy*7BiAqV$Vv?&f46|L#lD{!gksyTO13*tosA)s z{1`!Rb!Q-8q+qXA&+^aeoVCIpqlh*awTRNzP7!T>%icK)ytNJW!aKJPa}dQ~VG(*~ z@~;N9w9Kokt0iyL&YLT2$UjB; z^Q3}uslS=@l_m6FCp|3H%S+P#JEWJ-8IVR&{{ZRjYhTeu>K`7*|8dgexNfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XB zzyz286Zn$|+(pj~xS#TeDSv|U7b!nP`MZ?k^9g3sW^Yiw*ImAk-mWWE#_5U(X zDIaIOe6Q@2@E&+f%@qoiNc;Bbz1C0C?TETNuHd}eA zPd6y=;S2aaXiV2EvGQSRzZ^tl5hVRhCG@LE|J4%u%SpehgdU#vWA@`r(%(;c9p%qb z-cI>Dl=o47l8qzwcP4E1B)Qz_;F5IkIZU!__X65a3gycvm-|(=|3j+(3gvSDzD@EH z%6C#O$05gi71g_?J&nttd?n>GD4$GrQOf05<#Qlpjx3NZ9*=I=#!K}X%L3o$JyyzO zEba5-*mN zfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>N zfC(@GCcp%k025#WOn?b60Vco%m;e(fP2i++-I>a%$;v>rSn2Osnyl^d;lHFi&=l~Q z{~A~MgT6JV`+8b>{5}4pJ6V;iOwPQNLZ0MRx*C`H6@R-g5KyKooq>RniYEHSrK$Um z89BK?+ts^P4I3iUeeVeAI+}yNoVV2P9HrzMd2j?w6|Hw|SW)9iV@L^y!UL+vY|EPL zEq8;7X%W}^18O27){Bu-ypf?;MA40OuhJV$MAVcRITa!){uaNM(2Y>Su=?tcg>=0~ z?Nd`~BCLwNl~YGVO2EkEU|LdI+%yuPsB|a-Y79oCL2!|(f&rV|2*qMr7;C1=8tPae zom5kEQR9{xEomrW>;l%#wAZFoJ*g!kdQh`BQY%e#)#y%X5D%Cnv_#0z;?b}Y){;XI zNhX)If5d_cvFF+{A9r^9vvY?x5)bm-AvotApld|mpR_X)F$j`3F@yq$-JLE5A>A%V0Z`Y%LCCxWa%l1i@{@Wb>(jN8b zFXyZG$zRISKk4wd>(O7%Z>Ph7g2#`Pr9T{YW_1a{gRS z{!%XGZ`Y&0oNw2Yzm!Y)+x6%#=jR*AU&^KY?RxZ=^ZDK6FXdAHc0Kyb`Tue9mvSk8 zyB_`J_2NzPmvSk8sXt{>{`pl0GMvW$JnZuYT;9crrwJjS%X1FN+qRh9 z5iKOk=jrs5ET6-36UYz~!hbC8*HP!ouRn8T8v?3z|BEpIuVJf z&56NiN=wAmgmH;FG^Uf5Hj-(hJ7f$von}*iQ{aJ z2~9VmVLW1T>>S3q_)bOkIB+O*`2#^dKHdDC1dly{6b~8DU2CqHPCur=tk>Gr6i`Z? zqGQ|3^B`x{))@%;I{nQ`eqg6zyRWgiUFq)W3U>Ls+RdMI;Mm8Xbf|y1*|?_*)Z=x} z`fcZ9nNH6&ThQ6gKz=W5w$^x*;HSHr12E<1+gZ)a;owhH&P$bZhPQRq$!5Be?bixl zPn)l?9a=0N&+K2f(%u#bmar%QlWWcVNv#c}R&$o@iA z6Bo{i?a|&;C^h71QU}!-{v766g%c3S&^tBK6f#1dMrv_(?-hN$@zl_U!B8|B+YsIu z9~g`$*C*EMQC;&irlYZlr!8Tqskj=6!pQWRgoZyIZuB@hRMTtPHmSYoe$aJ<%vmFQ z9VN9j#p$yXc!tM3p;UZ9oikEyXxdm;+W(R3@n7(aH;=X4R3cByEWs?6qF*$NnB|{_ z|7VU9IVsd=g_r1$dm-d+N6zn+_`U4E{o9dsAsP4jX}fY|uO{-6?7Vu}Z22lz>}vRz zy~^eN*H-=nL(@{>3?@(5u`Y^mb4}^PmrmFVPh!Sj+2wBov!D#W3zVM+Is9r~?y-J_ zsF(+~P+#_eC^Jo=?85);SY_GaFEYQ#xXZFX2}AW*$(vQejb7muZ1#Lebr;pusge3Q z3wqT#bqgZ%=BW!7Tr_9i+}>VwVZFLATpL+9U+jDF)*HI7`$_$x>x`|37VCGO`Ox~i zCO!JVv~Rxo*vT&)-MKH@&VR9R%aD7d^V{m=^v3HZEZe!Kr*m!QhmV|j+w8+nExhA& z|B-9P3;V>sexv@zYckJVv48E-|M8YQdckcJn|M2;{{&AJI z@3p?Y70*5S(CqXroW*6$Ba+B{9f-F@$>)w^;5s?p8Le+>#n|e^W0x= zyFghxUi?2--%$30SC;?%rkD5XpE~c28xkK}_1nPk-LF1;;q)E%-Bxqysr&xi_~ZFc z`{dFI&n6#z>a5>6cYgniX9ClnZ2Pct z>F$T_+uZxq1CgIzdh>N(>Fv1n$MdGZVGloES%>w9FRfn>hkeDOi#Nt&?m_rw3Ab~L ztLN0zR=dr+O1Mv1T)jNlGP}O|;w2Loo!ivq$NK_zG8WYhclYwf_BOw}dbY>oOD1Eg z$I}#Sa(CmGbT`ECc$zz_-PHqzk! z6=#vhY-P#B4_`VSdB|Vx$2NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C j0!)AjFaajO1egF5U;<2l2`~XBzyz286JP@WFA(@oXJj|s literal 0 HcmV?d00001 diff --git a/bp/bp.xcodeproj/project.pbxproj b/bp/bp.xcodeproj/project.pbxproj index 1f9e69f0..d91dbe10 100644 --- a/bp/bp.xcodeproj/project.pbxproj +++ b/bp/bp.xcodeproj/project.pbxproj @@ -110,13 +110,13 @@ C4FAC2951E5E67ED00ACC5D9 /* testConfig-busted.json in Resources */ = {isa = PBXBuildFile; fileRef = C4FAC2941E5E67ED00ACC5D9 /* testConfig-busted.json */; }; C94DE0BB4360016D3D3061D9 /* simulator-preferences.plist in Resources */ = {isa = PBXBuildFile; fileRef = C94DEF7F8BCA7AB3C9114467 /* simulator-preferences.plist */; }; FB1C695629A6E58500BFDFB4 /* BluepillUnhostedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1C695529A6E58500BFDFB4 /* BluepillUnhostedTests.m */; }; + FB21F0BB2A622FF600682AC7 /* libBPMacTestInspector.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB21F0BA2A622FF600682AC7 /* libBPMacTestInspector.dylib */; }; + FB21F0BC2A622FF600682AC7 /* libBPMacTestInspector.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB21F0BA2A622FF600682AC7 /* libBPMacTestInspector.dylib */; }; + FB21F0BD2A622FF600682AC7 /* libBPMacTestInspector.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB21F0BA2A622FF600682AC7 /* libBPMacTestInspector.dylib */; }; FBBBD8EF29A06AF6002B9115 /* BPTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */; }; FBD772BF2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD772BE2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m */; }; FBD772C12A3452240098CEFD /* BPTestUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FBBBD8EE29A06AF6002B9115 /* BPTestUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; FBD772C22A3453010098CEFD /* BPTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */; }; - FBF6B1BB2A61CE4800D7A64B /* libBPXCTestWrapper.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FBF6B1BA2A61CE4800D7A64B /* libBPXCTestWrapper.dylib */; }; - FBF6B1BC2A61CE4800D7A64B /* libBPXCTestWrapper.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FBF6B1BA2A61CE4800D7A64B /* libBPXCTestWrapper.dylib */; }; - FBF6B1BD2A61CE4800D7A64B /* libBPXCTestWrapper.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FBF6B1BA2A61CE4800D7A64B /* libBPXCTestWrapper.dylib */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -331,10 +331,10 @@ C4FAC2941E5E67ED00ACC5D9 /* testConfig-busted.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "testConfig-busted.json"; sourceTree = ""; }; C94DEF7F8BCA7AB3C9114467 /* simulator-preferences.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = "simulator-preferences.plist"; sourceTree = ""; }; FB1C695529A6E58500BFDFB4 /* BluepillUnhostedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BluepillUnhostedTests.m; sourceTree = ""; }; + FB21F0BA2A622FF600682AC7 /* libBPMacTestInspector.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libBPMacTestInspector.dylib; sourceTree = ""; }; FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPTestUtils.m; sourceTree = ""; }; FBBBD8EE29A06AF6002B9115 /* BPTestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPTestUtils.h; sourceTree = ""; }; FBD772BE2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BluepillUnhostedBatchingTests.m; sourceTree = ""; }; - FBF6B1BA2A61CE4800D7A64B /* libBPXCTestWrapper.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libBPXCTestWrapper.dylib; path = "../../../../Library/Developer/Xcode/DerivedData/Bluepill-bzfbmrywvaiilwcxmhvmpqnjsiso/Build/Products/Debug/libBPXCTestWrapper.dylib"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -342,11 +342,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + FB21F0BB2A622FF600682AC7 /* libBPMacTestInspector.dylib in Frameworks */, C487CB1C226A663C00CC5BC2 /* bplib.framework in Frameworks */, B324B91D1F280AD100AAE2BC /* CoreSimulator.framework in Frameworks */, 7AB913001D5E209800621608 /* AppKit.framework in Frameworks */, 7AB913011D5E209800621608 /* Foundation.framework in Frameworks */, - FBF6B1BB2A61CE4800D7A64B /* libBPXCTestWrapper.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -354,8 +354,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + FB21F0BD2A622FF600682AC7 /* libBPMacTestInspector.dylib in Frameworks */, BA1896B5217916A5000CEC36 /* CoreSimulator.framework in Frameworks */, - FBF6B1BD2A61CE4800D7A64B /* libBPXCTestWrapper.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -363,7 +363,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - FBF6B1BC2A61CE4800D7A64B /* libBPXCTestWrapper.dylib in Frameworks */, + FB21F0BC2A622FF600682AC7 /* libBPMacTestInspector.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -459,7 +459,7 @@ 7A79018D1D5D136F004D4325 /* Frameworks */ = { isa = PBXGroup; children = ( - FBF6B1BA2A61CE4800D7A64B /* libBPXCTestWrapper.dylib */, + FB21F0BA2A622FF600682AC7 /* libBPMacTestInspector.dylib */, C47411F7264F4C3F000F84D1 /* XcodeKit.framework */, BA1896B6217916F8000CEC36 /* XCTest.framework */, B324B91C1F280AD100AAE2BC /* CoreSimulator.framework */, @@ -1237,6 +1237,10 @@ ); INFOPLIST_FILE = tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "\"$(DEVELOPER_DIR)/Library/PrivateFrameworks\""; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); MACOSX_DEPLOYMENT_TARGET = 11.0; OTHER_CFLAGS = "-DBP_USE_PRIVATE_FRAMEWORKS"; OTHER_LDFLAGS = ( @@ -1269,6 +1273,10 @@ ); INFOPLIST_FILE = tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "\"$(DEVELOPER_DIR)/Library/PrivateFrameworks\""; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); MACOSX_DEPLOYMENT_TARGET = 11.0; OTHER_CFLAGS = "-DBP_USE_PRIVATE_FRAMEWORKS"; OTHER_LDFLAGS = ( diff --git a/bp/libBPMacTestInspector.dylib b/bp/libBPMacTestInspector.dylib new file mode 100755 index 0000000000000000000000000000000000000000..0ed8f75feb3897025c723728361023f0900b0411 GIT binary patch literal 173584 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 literal 0 HcmV?d00001 diff --git a/bp/src/BPSimulator.m b/bp/src/BPSimulator.m index a41daf80..57de2f2b 100644 --- a/bp/src/BPSimulator.m +++ b/bp/src/BPSimulator.m @@ -18,8 +18,8 @@ #import "BPWaitTimer.h" #import "PrivateHeaders/CoreSimulator/CoreSimulator.h" #import "SimulatorHelper.h" -#import -#import +#import +#import @interface BPSimulator() @@ -444,6 +444,8 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser onSpawn:(void (^)(NSError *, pid_t))spawnBlock andCompletion:(void (^)(NSError *, pid_t))completionBlock { [self collectTestSuiteInfo]; + return; + /* Grab all test cases so that we can: 1) create a timeout for the full test execution @@ -577,8 +579,9 @@ - (void)collectTestSuiteInfo { // Environment NSMutableDictionary *environment = [[SimulatorHelper logicTestEnvironmentWithConfig:self.config stdoutRelativePath:simStdoutRelativePath] mutableCopy]; - environment[@"DYLD_INSERT_LIBRARIES"] = [BPUtils findBPXCTestWrapperDYLIB]; - environment[BPXCTestWrapperConstants.outputPathEnvironmentKey] = testSuiteInfoOutputPath; + environment[@"DYLD_INSERT_LIBRARIES"] = [BPUtils findBPTestInspectorDYLIB]; + environment[BPTestInspectorConstants.outputPathEnvironmentKey] = testSuiteInfoOutputPath; + environment[BPTestInspectorConstants.testBundleEnvironmentKey] = xctestPath; NSFileHandle *outputFileHandle = [NSFileHandle fileHandleForWritingAtPath:simStdoutPath]; NSNumber *stdoutFileDescriptor = @(outputFileHandle.fileDescriptor); @@ -587,8 +590,10 @@ - (void)collectTestSuiteInfo { kOptionsArgumentsKey: arguments, kOptionsEnvironmentKey: environment, @"standalone": @(1), - @"stdout": @(0), // stdoutFileDescriptor, - @"stderr": @(1), // stdoutFileDescriptor, + @"stdout": @(0), + @"stderr": @(1), +// @"stdout": stdoutFileDescriptor, +// @"stderr": stdoutFileDescriptor, }; // To see more on how to debug the expected format/inputs of the options array, @@ -601,13 +606,17 @@ - (void)collectTestSuiteInfo { if (error) { // LTHROCKM - TODO: Handle error } - NSError *unarchiveError; - NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:testSuiteInfoOutputPath]; - NSData *testData = [fileHandle readDataToEndOfFile]; - NSArray *testCaseInfo = [NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:BPTestCaseInfo.class - fromData:testData - error:&unarchiveError]; - [fileHandle closeFile]; + NSString *content = [NSString stringWithContentsOfFile:testSuiteInfoOutputPath encoding:NSUTF8StringEncoding error:nil]; + NSLog(@"[LTHROCKM DEBUG] testCaseInfo: %@", content); + +// NSError *unarchiveError; +// NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:testSuiteInfoOutputPath]; +// NSData *testData = [fileHandle readDataToEndOfFile]; +// NSArray *testCaseInfo = [NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:BPTestCaseInfo.class +// fromData:testData +// error:&unarchiveError]; +// [fileHandle closeFile]; +// NSLog(@"[LTHROCKM DEBUG] testCaseInfo: %@", testCaseInfo); // LTHROCKM - TODO: Call completion w/ enumerated test cases... } completionHandler:^(NSError *error, pid_t pid) { // LTHROCKM - TODO: Anything here? Maybe an execution timer, etc. diff --git a/bp/src/BPUtils.h b/bp/src/BPUtils.h index a6396cec..8725980a 100644 --- a/bp/src/BPUtils.h +++ b/bp/src/BPUtils.h @@ -51,7 +51,7 @@ typedef NS_ENUM(int, BPKind) { */ + (BOOL)isBuildScript; -+ (NSString *)findBPXCTestWrapperDYLIB; ++ (NSString *)findBPTestInspectorDYLIB; + (NSString *)findExecutablePath:(NSString *)execName; diff --git a/bp/src/BPUtils.m b/bp/src/BPUtils.m index 036265fc..d50a6940 100644 --- a/bp/src/BPUtils.m +++ b/bp/src/BPUtils.m @@ -16,7 +16,7 @@ #import "SimDeviceType.h" #import "BPExecutionContext.h" #import "BPSimulator.h" -#import +#import @implementation BPUtils @@ -127,16 +127,16 @@ + (NSString *)findExecutablePath:(NSString *)execName { return execPath; } -+ (NSString *)findBPXCTestWrapperDYLIB { ++ (NSString *)findBPTestInspectorDYLIB { NSString *argv0 = [[[NSProcessInfo processInfo] arguments] objectAtIndex:0]; - NSString *path = [[argv0 stringByDeletingLastPathComponent] stringByAppendingPathComponent:BPXCTestWrapperConstants.dylibName]; + NSString *path = [[argv0 stringByDeletingLastPathComponent] stringByAppendingPathComponent:BPTestInspectorConstants.dylibName]; if ([[NSFileManager defaultManager] isReadableFileAtPath:path]) { return path; } // The executable may also be in derived data, accessible from the app's current working directory. NSString *buildProductsDir = [NSFileManager.defaultManager.currentDirectoryPath stringByDeletingLastPathComponent]; NSString *iPhoneSimDir = [buildProductsDir stringByAppendingPathComponent:@"Debug-iphonesimulator"]; - path = [iPhoneSimDir stringByAppendingPathComponent:BPXCTestWrapperConstants.dylibName]; + path = [iPhoneSimDir stringByAppendingPathComponent:BPTestInspectorConstants.dylibName]; if ([[NSFileManager defaultManager] isReadableFileAtPath:path]) { return path; } diff --git a/bp/src/SimulatorHelper.h b/bp/src/SimulatorHelper.h index 48e4ee8e..a7f1ffdf 100644 --- a/bp/src/SimulatorHelper.h +++ b/bp/src/SimulatorHelper.h @@ -63,7 +63,7 @@ + (NSString *)makeStdoutFileOnDevice:(SimDevice *)device; /*! - @discussion Creates an output file on the provided device of the form `/tmp/BPXCTestWrapper_testInfo_`. + @discussion Creates an output file on the provided device of the form `/tmp/BPTestInspector_testInfo_`. This is meant to store the test info output generated by the injected test wrapper in an xctest execution. @param device The device to create the file on. @return the path of the output file. diff --git a/bp/src/SimulatorHelper.m b/bp/src/SimulatorHelper.m index b627778e..8826c192 100644 --- a/bp/src/SimulatorHelper.m +++ b/bp/src/SimulatorHelper.m @@ -230,7 +230,7 @@ + (NSString *)makeStdoutFileOnDevice:(SimDevice *)device { } + (NSString *)makeTestWrapperOutputFileOnDevice:(SimDevice *)device { - NSString *stdout_stderr = [NSString stringWithFormat:@"%@/tmp/BPXCTestWrapper_testInfo_%@", device.dataPath, [[device UDID] UUIDString]]; + NSString *stdout_stderr = [NSString stringWithFormat:@"%@/tmp/BPTestInspector_testInfo_%@", device.dataPath, [[device UDID] UUIDString]]; return [self createFileWithPathTemplate:stdout_stderr]; } From 4ac0f43672a9119f3baa3543ff0ae0ea1176c22d Mon Sep 17 00:00:00 2001 From: Lucas Statler Date: Mon, 17 Jul 2023 19:17:49 -0700 Subject: [PATCH 09/11] progress on processing test info --- BPTestInspector/BPTestCaseInfo.h | 22 ++++-- BPTestInspector/BPTestCaseInfo.m | 62 +++++++++++---- .../BPTestInspector.xcodeproj/project.pbxproj | 6 +- .../Internal/BPTestCaseInfo+Internal.h | 8 +- BPTestInspector/Internal/BPXCTestUtils.m | 11 +-- bp/bp.xcodeproj/project.pbxproj | 14 ++++ .../xcshareddata/xcschemes/bp.xcscheme | 22 +++++- bp/src/BPConfiguration.h | 3 +- bp/src/BPSimulator.h | 10 +++ bp/src/BPSimulator.m | 45 ++++++----- bp/src/BPStats.h | 1 + bp/src/BPTestInspectionHandler.h | 26 +++++++ bp/src/BPTestInspectionHandler.m | 43 +++++++++++ bp/src/Bluepill.m | 77 ++++++++++++++++--- bp/tests/Unhosted Tests/BPTestCaseInfoTests.m | 41 ++++++++++ 15 files changed, 327 insertions(+), 64 deletions(-) create mode 100644 bp/src/BPTestInspectionHandler.h create mode 100644 bp/src/BPTestInspectionHandler.m create mode 100644 bp/tests/Unhosted Tests/BPTestCaseInfoTests.m diff --git a/BPTestInspector/BPTestCaseInfo.h b/BPTestInspector/BPTestCaseInfo.h index 351ed130..e57dfe2f 100644 --- a/BPTestInspector/BPTestCaseInfo.h +++ b/BPTestInspector/BPTestCaseInfo.h @@ -9,12 +9,24 @@ NS_ASSUME_NONNULL_BEGIN -@interface BPTestCaseInfo : NSObject +@interface BPTestCaseInfo : NSObject -@property (nonatomic, copy, nonnull) NSString *moduleName; -@property (nonatomic, copy, nonnull) NSString *className; -@property (nonatomic, copy, nonnull) NSString *methodName; -@property (nonatomic, copy, nonnull) NSString *fullNamespace; +@property (nonatomic, copy, nonnull, readonly) NSString *className; +@property (nonatomic, copy, nonnull, readonly) NSString *methodName; + +/** + The name of the test, formatted correctly for XCTest (regardless of Obj-C vs Swift) + + @example `MyTestClass/MyTestCase` in Obj-C, or `MyTestModule.MyTestClass/MyTestCase` in Swift + */ +@property (nonatomic, copy, nonnull, readonly) NSString *standardizedFullName; +/** + The name of the test, formatted according to how BP consumers expect to list the test + in the opt-in or skip lists, or how they should be displayed in the generated test results. + + @example `MyTestClass/MyTestCase` in Obj-C, or `MyTestClass/MyTestCase()` in Swift + */ +@property (nonatomic, copy, nonnull, readonly) NSString *prettifiedFullName; @end diff --git a/BPTestInspector/BPTestCaseInfo.m b/BPTestInspector/BPTestCaseInfo.m index e0fcf625..020daa09 100644 --- a/BPTestInspector/BPTestCaseInfo.m +++ b/BPTestInspector/BPTestCaseInfo.m @@ -6,43 +6,73 @@ // #import "BPTestCaseInfo+Internal.h" +#import "XCTestCase.h" @implementation BPTestCaseInfo -- (instancetype)initWithModuleName:(NSString *)moduleName - className:(NSString *)className - methodName:(NSString *)methodName { +#pragma mark - Initializers + +- (instancetype)initWithClassName:(NSString *)className + methodName:(NSString *)methodName { if (self = [super init]) { - _moduleName = moduleName; - _className = className; - _methodName = methodName; - _fullNamespace = [NSString stringWithFormat:@"%@.%@/%@", moduleName, className, methodName]; + _className = [className copy]; + _methodName = [methodName copy]; } return self; } - + (instancetype)infoFromTestCase:(XCTestCase *)testCase { - NSString *moduleName = @"LTHROCKM - TODO"; NSString *className = NSStringFromClass(testCase.class); NSString *methodName = [testCase respondsToSelector:@selector(languageAgnosticTestMethodName)] ? [testCase languageAgnosticTestMethodName] : NSStringFromSelector([testCase.invocation selector]); - return [[BPTestCaseInfo alloc] initWithModuleName: moduleName className:className methodName:methodName]; + return [[BPTestCaseInfo alloc] initWithClassName:className methodName:methodName]; +} + +#pragma mark - Properties +- (NSString *)standardizedFullName { + return [NSString stringWithFormat:@"%@/%@", self.className, self.methodName]; } -#pragma mark - NSCoding +- (NSString *)prettifiedFullName { + /* + If the class name contains a `.`, this is a Swift test case, and needs extra formatting. + Otherwise, we in Obj-C, it's unchanged from the `standardizedFullName` + */ + NSArray *classComponents = [self.className componentsSeparatedByString:@"."]; + if (classComponents.count < 2) { + return self.standardizedFullName; + } + return [NSString stringWithFormat:@"%@/%@()", classComponents[1], self.methodName]; +} + +#pragma mark - Overrides + +- (NSString *)description { + return self.standardizedFullName; +} + +- (BOOL)isEqual:(id)object { + if (!object || ![object isMemberOfClass:BPTestCaseInfo.class]) { + return NO; + } + BPTestCaseInfo *other = (BPTestCaseInfo *)object; + return [self.className isEqual:other.className] && [self.methodName isEqual:other.methodName]; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} - (void)encodeWithCoder:(nonnull NSCoder *)coder { - [coder encodeObject:self.moduleName forKey:@"moduleName"]; [coder encodeObject:self.className forKey:@"className"]; [coder encodeObject:self.methodName forKey:@"methodName"]; - [coder encodeObject:self.fullNamespace forKey:@"fullNamespace"]; } - (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder { - return [self initWithModuleName:[coder decodeObjectForKey:@"moduleName"] - className:[coder decodeObjectForKey:@"className"] - methodName:[coder decodeObjectForKey:@"methodName"]]; + return [self initWithClassName:[coder decodeObjectOfClass:NSString.class forKey:@"className"] + methodName:[coder decodeObjectOfClass:NSString.class forKey:@"methodName"]]; } diff --git a/BPTestInspector/BPTestInspector.xcodeproj/project.pbxproj b/BPTestInspector/BPTestInspector.xcodeproj/project.pbxproj index 21dc13d3..b24fdb2a 100644 --- a/BPTestInspector/BPTestInspector.xcodeproj/project.pbxproj +++ b/BPTestInspector/BPTestInspector.xcodeproj/project.pbxproj @@ -9,7 +9,7 @@ /* Begin PBXBuildFile section */ FB21F07F2A62286400682AC7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FB21F07E2A62286400682AC7 /* main.m */; }; FB21F0862A62297F00682AC7 /* BPLoggingUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0812A62297F00682AC7 /* BPLoggingUtils.h */; }; - FB21F0872A62297F00682AC7 /* BPTestCaseInfo+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0822A62297F00682AC7 /* BPTestCaseInfo+Internal.h */; }; + FB21F0872A62297F00682AC7 /* BPTestCaseInfo+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0822A62297F00682AC7 /* BPTestCaseInfo+Internal.h */; settings = {ATTRIBUTES = (Public, ); }; }; FB21F0882A62297F00682AC7 /* BPXCTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FB21F0832A62297F00682AC7 /* BPXCTestUtils.m */; }; FB21F0892A62297F00682AC7 /* BPLoggingUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FB21F0842A62297F00682AC7 /* BPLoggingUtils.m */; }; FB21F08A2A62297F00682AC7 /* BPXCTestUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0852A62297F00682AC7 /* BPXCTestUtils.h */; }; @@ -37,6 +37,7 @@ FB21F0B52A622A0200682AC7 /* NSObject-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0A62A622A0200682AC7 /* NSObject-Protocol.h */; }; FB21F0B62A622A0200682AC7 /* _XCTestObservationInternal-Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0A72A622A0200682AC7 /* _XCTestObservationInternal-Protocol.h */; }; FB21F0B92A622AA000682AC7 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB21F0B82A622AA000682AC7 /* XCTest.framework */; }; + FB21F0DF2A65106E00682AC7 /* BPTestCaseInfo+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FB21F0822A62297F00682AC7 /* BPTestCaseInfo+Internal.h */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -210,6 +211,7 @@ buildActionMask = 2147483647; files = ( FB21F0942A62298B00682AC7 /* BPTestCaseInfo.h in Headers */, + FB21F0DF2A65106E00682AC7 /* BPTestCaseInfo+Internal.h in Headers */, FB21F0922A62298B00682AC7 /* BPTestInspectorConstants.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -341,6 +343,7 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "-"; + GCC_OPTIMIZATION_LEVEL = 0; }; name = Debug; }; @@ -349,6 +352,7 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "-"; + GCC_OPTIMIZATION_LEVEL = 0; }; name = Release; }; diff --git a/BPTestInspector/Internal/BPTestCaseInfo+Internal.h b/BPTestInspector/Internal/BPTestCaseInfo+Internal.h index 2b3eaf5c..f8dc473f 100644 --- a/BPTestInspector/Internal/BPTestCaseInfo+Internal.h +++ b/BPTestInspector/Internal/BPTestCaseInfo+Internal.h @@ -6,7 +6,8 @@ // #import -#import "XCTestCase.h" + +@class XCTestCase; NS_ASSUME_NONNULL_BEGIN @@ -14,6 +15,11 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)infoFromTestCase:(XCTestCase *)testCase; +#pragma mark - Testing + +- (instancetype)initWithClassName:(NSString *)className + methodName:(NSString *)methodName; + @end NS_ASSUME_NONNULL_END diff --git a/BPTestInspector/Internal/BPXCTestUtils.m b/BPTestInspector/Internal/BPXCTestUtils.m index bcfe8f0a..2eedf79f 100644 --- a/BPTestInspector/Internal/BPXCTestUtils.m +++ b/BPTestInspector/Internal/BPXCTestUtils.m @@ -19,15 +19,13 @@ + (void)logAllTestsInBundleWithPath:(NSString *)bundlePath toFile:(NSString *)ou NSArray *testCases = [self enumerateTestCasesInBundleWithPath:bundlePath]; // Encode the test data NSError *encodingError; - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:testCases requiringSecureCoding:NO error:&encodingError]; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:testCases requiringSecureCoding:YES error:&encodingError]; // Write to file. NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:outputPath]; -// [fileHandle writeData:data]; - [testCases.description writeToFile:outputPath atomically:YES encoding:NSUTF8StringEncoding error:nil]; + [fileHandle writeData:data]; [fileHandle closeFile]; - - NSString *output = [NSString stringWithFormat:@"Wrote to file: %@.", outputPath]; - [BPLoggingUtils log:output]; + + [BPLoggingUtils log:[NSString stringWithFormat:@"Wrote to file: %@.", outputPath]]; } + (NSArray *)enumerateTestCasesInBundleWithPath:(NSString *)bundlePath { @@ -40,7 +38,6 @@ + (void)logAllTestsInBundleWithPath:(NSString *)bundlePath toFile:(NSString *)ou return [self enumerateTestCasesInBundle:bundle]; } - //static void listBundle(NSString *testBundlePath, NSString *outputFile) + (NSArray *)enumerateTestCasesInBundle:(NSBundle *)bundle { /** diff --git a/bp/bp.xcodeproj/project.pbxproj b/bp/bp.xcodeproj/project.pbxproj index d91dbe10..81a29967 100644 --- a/bp/bp.xcodeproj/project.pbxproj +++ b/bp/bp.xcodeproj/project.pbxproj @@ -113,6 +113,10 @@ FB21F0BB2A622FF600682AC7 /* libBPMacTestInspector.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB21F0BA2A622FF600682AC7 /* libBPMacTestInspector.dylib */; }; FB21F0BC2A622FF600682AC7 /* libBPMacTestInspector.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB21F0BA2A622FF600682AC7 /* libBPMacTestInspector.dylib */; }; FB21F0BD2A622FF600682AC7 /* libBPMacTestInspector.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB21F0BA2A622FF600682AC7 /* libBPMacTestInspector.dylib */; }; + FB21F0DE2A65103F00682AC7 /* BPTestCaseInfoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FB21F0DD2A65103F00682AC7 /* BPTestCaseInfoTests.m */; }; + FB53751F2A66288300496432 /* BPTestInspectionHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = FB53751D2A66288300496432 /* BPTestInspectionHandler.h */; }; + FB5375202A66288300496432 /* BPTestInspectionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = FB53751E2A66288300496432 /* BPTestInspectionHandler.m */; }; + FB5375212A66288300496432 /* BPTestInspectionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = FB53751E2A66288300496432 /* BPTestInspectionHandler.m */; }; FBBBD8EF29A06AF6002B9115 /* BPTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */; }; FBD772BF2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD772BE2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m */; }; FBD772C12A3452240098CEFD /* BPTestUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FBBBD8EE29A06AF6002B9115 /* BPTestUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -332,6 +336,9 @@ C94DEF7F8BCA7AB3C9114467 /* simulator-preferences.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = "simulator-preferences.plist"; sourceTree = ""; }; FB1C695529A6E58500BFDFB4 /* BluepillUnhostedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BluepillUnhostedTests.m; sourceTree = ""; }; FB21F0BA2A622FF600682AC7 /* libBPMacTestInspector.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libBPMacTestInspector.dylib; sourceTree = ""; }; + FB21F0DD2A65103F00682AC7 /* BPTestCaseInfoTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPTestCaseInfoTests.m; sourceTree = ""; }; + FB53751D2A66288300496432 /* BPTestInspectionHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPTestInspectionHandler.h; sourceTree = ""; }; + FB53751E2A66288300496432 /* BPTestInspectionHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPTestInspectionHandler.m; sourceTree = ""; }; FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPTestUtils.m; sourceTree = ""; }; FBBBD8EE29A06AF6002B9115 /* BPTestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPTestUtils.h; sourceTree = ""; }; FBD772BE2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BluepillUnhostedBatchingTests.m; sourceTree = ""; }; @@ -415,6 +422,8 @@ 7A7E7BAF1DF2066B007928F3 /* BPHandler.m */, 7A7E7BB11DF20F81007928F3 /* BPApplicationLaunchHandler.h */, 7A7E7BB21DF20F81007928F3 /* BPApplicationLaunchHandler.m */, + FB53751D2A66288300496432 /* BPTestInspectionHandler.h */, + FB53751E2A66288300496432 /* BPTestInspectionHandler.m */, 7A7E7BB61DF2173C007928F3 /* BPCreateSimulatorHandler.h */, 7A7E7BB71DF2173C007928F3 /* BPCreateSimulatorHandler.m */, 7A7E7BBA1DF21749007928F3 /* BPDeleteSimulatorHandler.h */, @@ -652,6 +661,7 @@ FBD772C02A33E1E20098CEFD /* Unhosted Tests */ = { isa = PBXGroup; children = ( + FB21F0DD2A65103F00682AC7 /* BPTestCaseInfoTests.m */, FB1C695529A6E58500BFDFB4 /* BluepillUnhostedTests.m */, FBD772BE2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m */, ); @@ -676,6 +686,7 @@ B368E579213F965600B4DEA3 /* BPTestCase.h in Headers */, B368E57A213F965600B4DEA3 /* BPTestClass.h in Headers */, FBD772C12A3452240098CEFD /* BPTestUtils.h in Headers */, + FB53751F2A66288300496432 /* BPTestInspectionHandler.h in Headers */, B368E57B213F965600B4DEA3 /* BPUtils.h in Headers */, B368E57C213F965600B4DEA3 /* BPXCTestFile.h in Headers */, B368E571213F8E8F00B4DEA3 /* BPConstants.h in Headers */, @@ -870,6 +881,7 @@ BA19493A1E4AF83E00881887 /* BPTMDControlConnection.m in Sources */, 7ADBB1471DCBBC0E00DC4E8D /* BPTreeAssembler.m in Sources */, 7A4D7A861DDBB156001E085D /* BPExitStatus.m in Sources */, + FB5375202A66288300496432 /* BPTestInspectionHandler.m in Sources */, BA944BD01D76A39A00A4BDA3 /* Bluepill.m in Sources */, 7A564C0E1DA817DE001BCEC2 /* BPTreeObjects.m in Sources */, 7A7901861D5CB679004D4325 /* main.m in Sources */, @@ -880,6 +892,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FB5375212A66288300496432 /* BPTestInspectionHandler.m in Sources */, C4D686182267A8C9007D4237 /* BPTestHelper.m in Sources */, C47B2DB3225813C70068C5CA /* BPWriter.m in Sources */, C4F08F75224C45750001AD2A /* BPExitStatus.m in Sources */, @@ -929,6 +942,7 @@ BA0097001DCA61210000DD45 /* BPConfigurationTests.m in Sources */, BAB24F741DB5DFA200867756 /* BPTestHelper.m in Sources */, FB1C695629A6E58500BFDFB4 /* BluepillUnhostedTests.m in Sources */, + FB21F0DE2A65103F00682AC7 /* BPTestCaseInfoTests.m in Sources */, FBBBD8EF29A06AF6002B9115 /* BPTestUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/bp/bp.xcodeproj/xcshareddata/xcschemes/bp.xcscheme b/bp/bp.xcodeproj/xcshareddata/xcschemes/bp.xcscheme index 94651b55..f3be982d 100644 --- a/bp/bp.xcodeproj/xcshareddata/xcschemes/bp.xcscheme +++ b/bp/bp.xcodeproj/xcshareddata/xcschemes/bp.xcscheme @@ -28,10 +28,24 @@ buildForAnalyzing = "YES"> + BlueprintIdentifier = "AA56EAE11EEF08720062C2BC" + BuildableName = "libBPMacTestInspector.dylib" + BlueprintName = "BPMacTestInspector" + ReferencedContainer = "container:../BPTestInspector/BPTestInspector.xcodeproj"> + + + + diff --git a/bp/src/BPConfiguration.h b/bp/src/BPConfiguration.h index 497d0732..5b15aad9 100644 --- a/bp/src/BPConfiguration.h +++ b/bp/src/BPConfiguration.h @@ -12,6 +12,7 @@ @class SimDeviceType; @class SimRuntime; +@class BPTestCaseInfo; /** TestPlans populated by an outside build system @@ -132,11 +133,11 @@ typedef NS_ENUM(NSInteger, BPProgram) { @property (nonatomic, strong) SimDeviceType *simDeviceType; @property (nonatomic, strong) SimRuntime *simRuntime; +@property (nonatomic, strong) NSArray *allTests; @property (nonatomic, strong) NSString *xctestBinaryPath; @property (nonatomic, strong) NSString *dyldFrameworkPath; @property (nonatomic, strong) NSDictionary *standardizedSwiftTestNames; - /** Return a structure suitable for passing to `getopt()`'s long options. diff --git a/bp/src/BPSimulator.h b/bp/src/BPSimulator.h index 449da64d..5713fb60 100644 --- a/bp/src/BPSimulator.h +++ b/bp/src/BPSimulator.h @@ -15,6 +15,7 @@ @class BPConfiguration; @class BPTreeParser; @class SimDevice; +@class BPTestCaseInfo; @interface BPSimulator : NSObject @@ -42,6 +43,15 @@ - (void)launchApplicationAndExecuteTestsWithParser:(BPTreeParser *)parser andCompletion:(void (^)(NSError *, pid_t))completion; +/** + Launches a process which inspects a test bundle, and calls a completion block with a list of all tests in the bundle. + + @discussion The only way to do this is to actually launch a test execution with an injected Bluepill module. This module + then checks what test cases exist at runtime, and writes these cases to a file before ending the process (such that no tests are actually run). + We then read that file, and pass the results to the completion handler provided. + */ +- (void)collectTestSuiteInfoWithCompletion:(void (^)(NSArray *, NSError *))completionBlock; + /** Executes logic tests for the provided context, providing two callbacks as the execution starts and finishes. @param parser The test log parser diff --git a/bp/src/BPSimulator.m b/bp/src/BPSimulator.m index 57de2f2b..ff6bc64b 100644 --- a/bp/src/BPSimulator.m +++ b/bp/src/BPSimulator.m @@ -443,8 +443,11 @@ - (BOOL)uninstallApplicationWithError:(NSError *__autoreleasing *)errPtr { - (void)executeLogicTestsWithParser:(BPTreeParser *)parser onSpawn:(void (^)(NSError *, pid_t))spawnBlock andCompletion:(void (^)(NSError *, pid_t))completionBlock { - [self collectTestSuiteInfo]; - return; +// NSError *error; +// [self collectTestSuiteInfoWithCompletion:^(NSArray *testCases) { +// <#code#> +// } error:&error]; +// return; /* Grab all test cases so that we can: @@ -562,7 +565,7 @@ - (void)executeLogicTestsWithParser:(BPTreeParser *)parser }]; } -- (void)collectTestSuiteInfo { +- (void)collectTestSuiteInfoWithCompletion:(void (^)(NSArray *, NSError *))completionBlock { NSString *xctestPath = self.config.testBundlePath; NSArray *arguments = @[ self.config.xctestBinaryPath, @@ -590,12 +593,14 @@ - (void)collectTestSuiteInfo { kOptionsArgumentsKey: arguments, kOptionsEnvironmentKey: environment, @"standalone": @(1), - @"stdout": @(0), - @"stderr": @(1), +// @"stdout": @(1), +// @"stderr": @(2), // @"stdout": stdoutFileDescriptor, // @"stderr": stdoutFileDescriptor, }; + NSLog(@"[LTHROCKM DEBUG] simStdoutPath: %@", simStdoutPath); + // To see more on how to debug the expected format/inputs of the options array, // see the in-depth documentation in SimDevice.h. __block typeof(self) blockSelf = self; @@ -604,22 +609,24 @@ - (void)collectTestSuiteInfo { terminationHandler:^(int stat_loc) { NSError *error = [BPSimulator errorFromStatusLocation:stat_loc]; if (error) { - // LTHROCKM - TODO: Handle error + completionBlock(nil, error); + return; } - NSString *content = [NSString stringWithContentsOfFile:testSuiteInfoOutputPath encoding:NSUTF8StringEncoding error:nil]; - NSLog(@"[LTHROCKM DEBUG] testCaseInfo: %@", content); - -// NSError *unarchiveError; -// NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:testSuiteInfoOutputPath]; -// NSData *testData = [fileHandle readDataToEndOfFile]; -// NSArray *testCaseInfo = [NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:BPTestCaseInfo.class -// fromData:testData -// error:&unarchiveError]; -// [fileHandle closeFile]; -// NSLog(@"[LTHROCKM DEBUG] testCaseInfo: %@", testCaseInfo); - // LTHROCKM - TODO: Call completion w/ enumerated test cases... + // Retrieve test data + NSError *unarchiveError; + NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:testSuiteInfoOutputPath]; + NSData *testData = [fileHandle readDataToEndOfFile]; + NSArray *testBundleInfo = [NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:BPTestCaseInfo.class + fromData:testData + error:&unarchiveError]; + [fileHandle closeFile]; + // Cleanup + Completion + [NSFileManager.defaultManager removeItemAtPath:testSuiteInfoOutputPath error:nil]; + completionBlock(testBundleInfo, unarchiveError); } completionHandler:^(NSError *error, pid_t pid) { - // LTHROCKM - TODO: Anything here? Maybe an execution timer, etc. + if (error) { + completionBlock(nil, error); + } }]; } diff --git a/bp/src/BPStats.h b/bp/src/BPStats.h index f63730c4..34dc9a51 100644 --- a/bp/src/BPStats.h +++ b/bp/src/BPStats.h @@ -16,6 +16,7 @@ #define INSTALL_APPLICATION(x) [NSString stringWithFormat:@"[Attempt %lu] Install Application", (x)] #define UNINSTALL_APPLICATION(x) [NSString stringWithFormat:@"[Attempt %lu] Uninstall Application", (x)] #define LAUNCH_APPLICATION(x) [NSString stringWithFormat:@"[Attempt %lu] Launch Application", (x)] +#define TEST_INSPECTION(x) [NSString stringWithFormat:@"[Attempt %lu] Inspect Tests", (x)] #define SPAWN_LOGIC_TEST(x) [NSString stringWithFormat:@"[Attempt %lu] Spawn Logic Tests", (x)] #define EXECUTE_LOGIC_TEST(x) [NSString stringWithFormat:@"[Attempt %lu] Execute Logic Tests", (x)] #define DELETE_SIMULATOR(x) [NSString stringWithFormat:@"[Attempt %lu] Delete Simulator", (x)] diff --git a/bp/src/BPTestInspectionHandler.h b/bp/src/BPTestInspectionHandler.h new file mode 100644 index 00000000..2c079325 --- /dev/null +++ b/bp/src/BPTestInspectionHandler.h @@ -0,0 +1,26 @@ +// +// BPTestInspectionHandler.h +// bp +// +// Created by Lucas Throckmorton on 7/17/23. +// Copyright © 2023 LinkedIn. All rights reserved. +// + + +#import +#import + +@class BPTestCaseInfo; + +typedef void (^TestInspectionBlock)(NSArray *testBundleInfo, NSError *error); +typedef void (^TestInspectionSuccessBlock)(NSArray *testBundleInfo); + +@interface BPTestInspectionHandler : BPHandler + +@property (nonatomic, copy) TestInspectionSuccessBlock onSuccess; + +- (TestInspectionBlock)defaultHandlerBlock; + + + +@end diff --git a/bp/src/BPTestInspectionHandler.m b/bp/src/BPTestInspectionHandler.m new file mode 100644 index 00000000..153232d2 --- /dev/null +++ b/bp/src/BPTestInspectionHandler.m @@ -0,0 +1,43 @@ +// +// BPTestInspectionHandler.m +// bp +// +// Created by Lucas Throckmorton on 7/17/23. +// Copyright © 2023 LinkedIn. All rights reserved. +// + +#import "BPTestInspectionHandler.h" + +#import + +@implementation BPTestInspectionHandler + +@dynamic onSuccess; + +- (TestInspectionBlock)defaultHandlerBlock { + return ^(NSArray *testBundleInfo, NSError *error) { + dispatch_once(&self->onceToken, ^{ + [self.timer cancelTimer]; + + self.error = error; + + if (self.beginWith) { + self.beginWith(); + } + + if (error) { + if (self.onError) { + self.onError(error); + } + } else if (self.onSuccess) { + self.onSuccess(testBundleInfo); + } + + if (self.endWith) { + self.endWith(); + } + }); + }; +} + +@end diff --git a/bp/src/Bluepill.m b/bp/src/Bluepill.m index e16c2df6..68106c31 100644 --- a/bp/src/Bluepill.m +++ b/bp/src/Bluepill.m @@ -21,6 +21,7 @@ #import #import "BPTMDControlConnection.h" #import "BPTMDRunnerConnection.h" +#import "BPTestInspectionHandler.h" #import "BPXCTestFile.h" #import @@ -312,15 +313,7 @@ - (void)createSimulatorWithContext:(BPExecutionContext *)context { [context.runner runScriptFile:self.config.scriptFilePath]; } - if (self.config.isLogicTestTarget) { - NEXT([__self executeLogicTestsWithContext:context]) - } else if (self.config.cloneSimulator) { - // launch application directly when clone simulator - NEXT([__self launchApplicationWithContext:context]); - } else { - // Install application when test without clone - NEXT([__self installApplicationWithContext:context]); - }; + NEXT([__self enumerateTestsWithContext:context]); }; handler.onError = ^(NSError *error) { @@ -408,7 +401,7 @@ - (void)uninstallApplicationWithContext:(BPExecutionContext *)context { } } -- (void)executeLogicTestsWithContext:(BPExecutionContext *)context { +- (void)adaptXCTestIfRequiredWithContext:(BPExecutionContext *)context { // First we need to make sure the archs are consistent between the xctest executable + the test bundle. NSString *originalXCTestPath = [[NSString alloc] initWithFormat: @"%@/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Agents/xctest", @@ -418,6 +411,70 @@ - (void)executeLogicTestsWithContext:(BPExecutionContext *)context { context.config.dyldFrameworkPath = [BPUtils correctedDYLDFrameworkPathFromBinary:originalXCTestPath]; } context.config.xctestBinaryPath = newXCTestPath ?: originalXCTestPath; +} + +- (void)enumerateTestsWithContext:(BPExecutionContext *)context { + NSString *stepName = TEST_INSPECTION(context.attemptNumber); + [BPUtils printInfo:INFO withString:@"%@", stepName]; + + // Now create handler for when the process completes + + [[BPStats sharedStats] startTimer:stepName]; + BPWaitTimer *timer = [BPWaitTimer timerWithInterval:[context.config.launchTimeout doubleValue]]; + [timer start]; + + // Set up completion handler for when we load the tests. + BPHandler *completionHandler = [BPTestInspectionHandler handlerWithTimer:timer]; + __weak typeof(self) __self = self; + completionHandler.onSuccess = ^{ + // Note, we can't actually handle the tests themselves here... that will + // need to be handled in a wrapping block below. + [BPUtils printInfo:DEBUGINFO withString:@"Test bundle inspected for tests."]; + [[BPStats sharedStats] endTimer:stepName withResult:@"INFO"]; + + if (self.config.isLogicTestTarget) { + NEXT([__self executeLogicTestsWithContext:context]) + } else if (self.config.cloneSimulator) { + // launch application directly when clone simulator + NEXT([__self launchApplicationWithContext:context]); + } else { + // Install application when test without clone + NEXT([__self installApplicationWithContext:context]); + }; + }; + + completionHandler.onError = ^(NSError *error) { + [[BPStats sharedStats] endTimer:stepName withResult:@"ERROR"]; + [BPUtils printInfo:ERROR withString:@"Spawned logic test execution failed: %@", [error localizedDescription]]; + + BPExitStatus exitStatus = BPExitStatusLaunchAppFailed; + NEXT([__self deleteSimulatorWithContext:context andStatus:exitStatus]); + }; + + completionHandler.onTimeout = ^{ + [[BPStats sharedStats] endTimer:stepName withResult:@"TIMEOUT"]; + [BPUtils printInfo:FAILED withString:@"Timeout: %@", stepName]; + }; + + // If test cases are already enumerated, skip straight to executing tests. + if (context.config.allTestCases) { + completionHandler.defaultHandlerBlock(nil); + return; + } + + // Otherwise, make sure XCTest binary is formatted correctly for current arch. Then get test info. + [self adaptXCTestIfRequiredWithContext:context]; + [context.runner collectTestSuiteInfoWithCompletion:^(NSArray *testBundleInfo, NSError *error) { + if (testBundleInfo) { + __self.config.allTests = testBundleInfo; + context.config.allTests = testBundleInfo; + } + completionHandler.defaultHandlerBlock(error); + }]; +} + +- (void)executeLogicTestsWithContext:(BPExecutionContext *)context { + [self adaptXCTestIfRequiredWithContext:context]; // We get two callbacks after trying to spawn an execution. They happen when: // 1) Immediately after the process is spawned. diff --git a/bp/tests/Unhosted Tests/BPTestCaseInfoTests.m b/bp/tests/Unhosted Tests/BPTestCaseInfoTests.m new file mode 100644 index 00000000..670d32ae --- /dev/null +++ b/bp/tests/Unhosted Tests/BPTestCaseInfoTests.m @@ -0,0 +1,41 @@ +// +// BPTestCaseInfoTests.m +// BPTestInspector +// +// Created by Lucas Throckmorton on 7/16/23. +// Copyright © 2023 Facebook. All rights reserved. +// + +#import +#import + +#import + +@interface BPTestCaseInfoTests : XCTestCase + +@end + +@implementation BPTestCaseInfoTests + +- (void)testArchiving { + // Mock data + BPTestCaseInfo *info1 = [[BPTestCaseInfo alloc] initWithClassName:@"Class" methodName:@"Method1"]; + BPTestCaseInfo *info2 = [[BPTestCaseInfo alloc] initWithClassName:@"Class" methodName:@"Method2"]; + BPTestCaseInfo *info3 = [[BPTestCaseInfo alloc] initWithClassName:@"Class" methodName:@"Method3"]; + NSArray *testCasesIn = @[info1, info2, info3]; + // Archive + NSError *error; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:testCasesIn requiringSecureCoding:NO error:&error]; + // Unarchive + NSArray *testCasesOut = [NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:BPTestCaseInfo.class + fromData:data + error:&error]; + // Validate + XCTAssertNotNil(testCasesOut); + XCTAssertEqual(testCasesIn.count, testCasesOut.count); + for (int i = 0; i < testCasesIn.count; i++) { + XCTAssertEqualObjects(testCasesIn[i], testCasesOut[i]); + } +} + +@end From 1d86548b81fad791453c9aadbfa8232f76813976 Mon Sep 17 00:00:00 2001 From: Lucas Statler Date: Mon, 17 Jul 2023 21:14:41 -0700 Subject: [PATCH 10/11] just before adding libBPTestInspector.dylib as a direct dependency --- bluepill/libBPXCTestWrapper.dylib | Bin 78960 -> 0 bytes bp/libBPXCTestWrapper.dylib | Bin 78960 -> 0 bytes bp/src/BPConfiguration.h | 2 +- bp/src/BPSimulator.m | 6 ------ bp/src/BPXCTestFile.m | 12 ++++++------ bp/src/Bluepill.m | 31 ++++++++++++++++++------------ bp/src/SimulatorHelper.m | 8 +++----- 7 files changed, 29 insertions(+), 30 deletions(-) delete mode 100755 bluepill/libBPXCTestWrapper.dylib delete mode 100755 bp/libBPXCTestWrapper.dylib diff --git a/bluepill/libBPXCTestWrapper.dylib b/bluepill/libBPXCTestWrapper.dylib deleted file mode 100755 index 6bfbd3bde8da2e961e6d8ac7f558d97501bd33fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78960 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 g1DXNNfM!55pc&8%Xa+O`ngPv#W *allTests; +@property (nonatomic, strong) NSDictionary *allTests; @property (nonatomic, strong) NSString *xctestBinaryPath; @property (nonatomic, strong) NSString *dyldFrameworkPath; @property (nonatomic, strong) NSDictionary *standardizedSwiftTestNames; diff --git a/bp/src/BPSimulator.m b/bp/src/BPSimulator.m index ff6bc64b..e9e2d5d7 100644 --- a/bp/src/BPSimulator.m +++ b/bp/src/BPSimulator.m @@ -443,12 +443,6 @@ - (BOOL)uninstallApplicationWithError:(NSError *__autoreleasing *)errPtr { - (void)executeLogicTestsWithParser:(BPTreeParser *)parser onSpawn:(void (^)(NSError *, pid_t))spawnBlock andCompletion:(void (^)(NSError *, pid_t))completionBlock { -// NSError *error; -// [self collectTestSuiteInfoWithCompletion:^(NSArray *testCases) { -// <#code#> -// } error:&error]; -// return; - /* Grab all test cases so that we can: 1) create a timeout for the full test execution diff --git a/bp/src/BPXCTestFile.m b/bp/src/BPXCTestFile.m index 08d7dd7c..dd4d32e4 100644 --- a/bp/src/BPXCTestFile.m +++ b/bp/src/BPXCTestFile.m @@ -50,7 +50,7 @@ + (instancetype)BPXCTestFileFromXCTestBundle:(NSString *)path NSArray* testsArray = [output componentsSeparatedByString:@"\n"]; NSMutableDictionary *testClassesDict = [[NSMutableDictionary alloc] init]; NSMutableArray *allClasses = [[NSMutableArray alloc] init]; - NSMutableDictionary *standardizedSwiftTestNames = [NSMutableDictionary dictionary]; +// NSMutableDictionary *standardizedSwiftTestNames = [NSMutableDictionary dictionary]; for (NSString *testName in testsArray) { NSArray *parts = [testName componentsSeparatedByString:@"."]; @@ -72,13 +72,13 @@ + (instancetype)BPXCTestFileFromXCTestBundle:(NSString *)path Note that this module name isn't necessarily the same as the bundle name, and that this is the only time we have access to the original module name. So, we save it here for later. */ - NSString *standardizedTestCaseName = [NSString stringWithFormat:@"%@/%@", testClass.name, trimmedTestName]; - NSString *standardizedTestName = [BPUtils formatSwiftTestForXCTest:standardizedTestCaseName withBundleName:parts[0]]; - - standardizedSwiftTestNames[standardizedTestCaseName] = standardizedTestName; +// NSString *standardizedTestCaseName = [NSString stringWithFormat:@"%@/%@", testClass.name, trimmedTestName]; +// NSString *standardizedTestName = [BPUtils formatSwiftTestForXCTest:standardizedTestCaseName withBundleName:parts[0]]; +// +// standardizedSwiftTestNames[standardizedTestCaseName] = standardizedTestName; } } - xcTestFile.standardizedSwiftTestNames = [standardizedSwiftTestNames copy]; +// xcTestFile.standardizedSwiftTestNames = [standardizedSwiftTestNames copy]; cmd = [NSString stringWithFormat:objcNmCmdline, path]; output = [BPUtils runShell:cmd]; diff --git a/bp/src/Bluepill.m b/bp/src/Bluepill.m index 68106c31..0cd97461 100644 --- a/bp/src/Bluepill.m +++ b/bp/src/Bluepill.m @@ -24,6 +24,7 @@ #import "BPTestInspectionHandler.h" #import "BPXCTestFile.h" #import +#import // CoreSimulator #import "PrivateHeaders/CoreSimulator/SimDevice.h" @@ -401,7 +402,7 @@ - (void)uninstallApplicationWithContext:(BPExecutionContext *)context { } } -- (void)adaptXCTestIfRequiredWithContext:(BPExecutionContext *)context { +- (void)adaptXCTestExecutableIfRequiredWithContext:(BPExecutionContext *)context { // First we need to make sure the archs are consistent between the xctest executable + the test bundle. NSString *originalXCTestPath = [[NSString alloc] initWithFormat: @"%@/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Agents/xctest", @@ -424,13 +425,23 @@ - (void)enumerateTestsWithContext:(BPExecutionContext *)context { [timer start]; // Set up completion handler for when we load the tests. - BPHandler *completionHandler = [BPTestInspectionHandler handlerWithTimer:timer]; + BPTestInspectionHandler *completionHandler = [BPTestInspectionHandler handlerWithTimer:timer]; __weak typeof(self) __self = self; - completionHandler.onSuccess = ^{ + completionHandler.onSuccess = ^(NSArray *testBundleInfo) { // Note, we can't actually handle the tests themselves here... that will // need to be handled in a wrapping block below. [BPUtils printInfo:DEBUGINFO withString:@"Test bundle inspected for tests."]; [[BPStats sharedStats] endTimer:stepName withResult:@"INFO"]; + + // Save the test info. + if (testBundleInfo) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + for (BPTestCaseInfo *info in testBundleInfo) { + dictionary[info.prettifiedFullName] = info; + } + __self.config.allTests = [dictionary copy]; + context.config.allTests = [dictionary copy]; + } if (self.config.isLogicTestTarget) { NEXT([__self executeLogicTestsWithContext:context]) @@ -457,24 +468,20 @@ - (void)enumerateTestsWithContext:(BPExecutionContext *)context { }; // If test cases are already enumerated, skip straight to executing tests. - if (context.config.allTestCases) { - completionHandler.defaultHandlerBlock(nil); + if (context.config.allTests) { + completionHandler.defaultHandlerBlock(context.config.allTests.allValues, nil); return; } // Otherwise, make sure XCTest binary is formatted correctly for current arch. Then get test info. - [self adaptXCTestIfRequiredWithContext:context]; + [self adaptXCTestExecutableIfRequiredWithContext:context]; [context.runner collectTestSuiteInfoWithCompletion:^(NSArray *testBundleInfo, NSError *error) { - if (testBundleInfo) { - __self.config.allTests = testBundleInfo; - context.config.allTests = testBundleInfo; - } - completionHandler.defaultHandlerBlock(error); + completionHandler.defaultHandlerBlock(testBundleInfo, error); }]; } - (void)executeLogicTestsWithContext:(BPExecutionContext *)context { - [self adaptXCTestIfRequiredWithContext:context]; + [self adaptXCTestExecutableIfRequiredWithContext:context]; // We get two callbacks after trying to spawn an execution. They happen when: // 1) Immediately after the process is spawned. diff --git a/bp/src/SimulatorHelper.m b/bp/src/SimulatorHelper.m index 8826c192..32e74cb1 100644 --- a/bp/src/SimulatorHelper.m +++ b/bp/src/SimulatorHelper.m @@ -15,6 +15,7 @@ #import "PrivateHeaders/XCTest/XCTestConfiguration.h" #import "PrivateHeaders/XCTest/XCTTestIdentifier.h" #import "PrivateHeaders/XCTest/XCTTestIdentifierSet.h" +#import @implementation SimulatorHelper @@ -214,11 +215,8 @@ + (NSString *)bundleIdForPath:(NSString *)path { } NSMutableArray *formattedTests = [NSMutableArray array]; for (NSString *testName in tests) { - if ([BPUtils isTestSwiftTest:testName]) { - [formattedTests addObject:config.standardizedSwiftTestNames[testName]]; - } else { - [formattedTests addObject:testName]; - } + BPTestCaseInfo *info = config.allTests[testName]; + [formattedTests addObject:info.standardizedFullName]; } return formattedTests; } From a2ff9b0fa7ab777ba46ffbae2754449c3324ea2d Mon Sep 17 00:00:00 2001 From: Lucas Statler Date: Tue, 15 Aug 2023 15:09:15 -0700 Subject: [PATCH 11/11] Need to fix linking issue for build script. Note - lib location will change, and utils will need to be updated accordingly --- bp/bp.xcodeproj/project.pbxproj | 6 ++++++ bp/libBPTestInspector.dylib | Bin 0 -> 179504 bytes scripts/bluepill.sh | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100755 bp/libBPTestInspector.dylib diff --git a/bp/bp.xcodeproj/project.pbxproj b/bp/bp.xcodeproj/project.pbxproj index 81a29967..67004d56 100644 --- a/bp/bp.xcodeproj/project.pbxproj +++ b/bp/bp.xcodeproj/project.pbxproj @@ -117,6 +117,8 @@ FB53751F2A66288300496432 /* BPTestInspectionHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = FB53751D2A66288300496432 /* BPTestInspectionHandler.h */; }; FB5375202A66288300496432 /* BPTestInspectionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = FB53751E2A66288300496432 /* BPTestInspectionHandler.m */; }; FB5375212A66288300496432 /* BPTestInspectionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = FB53751E2A66288300496432 /* BPTestInspectionHandler.m */; }; + FB5375232A66494100496432 /* libBPTestInspector.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB5375222A66494000496432 /* libBPTestInspector.dylib */; }; + FB5375242A66494100496432 /* libBPTestInspector.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB5375222A66494000496432 /* libBPTestInspector.dylib */; }; FBBBD8EF29A06AF6002B9115 /* BPTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */; }; FBD772BF2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD772BE2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m */; }; FBD772C12A3452240098CEFD /* BPTestUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = FBBBD8EE29A06AF6002B9115 /* BPTestUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -339,6 +341,7 @@ FB21F0DD2A65103F00682AC7 /* BPTestCaseInfoTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPTestCaseInfoTests.m; sourceTree = ""; }; FB53751D2A66288300496432 /* BPTestInspectionHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPTestInspectionHandler.h; sourceTree = ""; }; FB53751E2A66288300496432 /* BPTestInspectionHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPTestInspectionHandler.m; sourceTree = ""; }; + FB5375222A66494000496432 /* libBPTestInspector.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libBPTestInspector.dylib; sourceTree = ""; }; FBBBD8ED29A06AF6002B9115 /* BPTestUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPTestUtils.m; sourceTree = ""; }; FBBBD8EE29A06AF6002B9115 /* BPTestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPTestUtils.h; sourceTree = ""; }; FBD772BE2A33E1DA0098CEFD /* BluepillUnhostedBatchingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BluepillUnhostedBatchingTests.m; sourceTree = ""; }; @@ -351,6 +354,7 @@ files = ( FB21F0BB2A622FF600682AC7 /* libBPMacTestInspector.dylib in Frameworks */, C487CB1C226A663C00CC5BC2 /* bplib.framework in Frameworks */, + FB5375232A66494100496432 /* libBPTestInspector.dylib in Frameworks */, B324B91D1F280AD100AAE2BC /* CoreSimulator.framework in Frameworks */, 7AB913001D5E209800621608 /* AppKit.framework in Frameworks */, 7AB913011D5E209800621608 /* Foundation.framework in Frameworks */, @@ -363,6 +367,7 @@ files = ( FB21F0BD2A622FF600682AC7 /* libBPMacTestInspector.dylib in Frameworks */, BA1896B5217916A5000CEC36 /* CoreSimulator.framework in Frameworks */, + FB5375242A66494100496432 /* libBPTestInspector.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -468,6 +473,7 @@ 7A79018D1D5D136F004D4325 /* Frameworks */ = { isa = PBXGroup; children = ( + FB5375222A66494000496432 /* libBPTestInspector.dylib */, FB21F0BA2A622FF600682AC7 /* libBPMacTestInspector.dylib */, C47411F7264F4C3F000F84D1 /* XcodeKit.framework */, BA1896B6217916F8000CEC36 /* XCTest.framework */, diff --git a/bp/libBPTestInspector.dylib b/bp/libBPTestInspector.dylib new file mode 100755 index 0000000000000000000000000000000000000000..2ba6e426330eabf169464d62b0897c717e9f47a5 GIT binary patch literal 179504 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 z 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 literal 0 HcmV?d00001 diff --git a/scripts/bluepill.sh b/scripts/bluepill.sh index 3ab8bc00..9d49938a 100755 --- a/scripts/bluepill.sh +++ b/scripts/bluepill.sh @@ -57,7 +57,7 @@ bluepill_build() TAG=$(git describe --always --tags) DST="Bluepill-$TAG" mkdir -p "build/$DST/bin" - cp build/Build/Products/Release/{bp,bluepill} "build/$DST/bin" + cp build/Build/Products/Release/{bp,bluepill,libBPMacTestInspector.dylib} "build/$DST/bin" ## build the man page mkdir -p "build/$DST/man/man1" /usr/bin/python scripts/man.py "build/$DST/man/man1/bluepill.1"