Created
May 19, 2017 15:28
-
-
Save JohnCoates/605319ab31c0aa05c77973481aeae4b2 to your computer and use it in GitHub Desktop.
Fixes UI code coverage on builds run from xcode build
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import ObjectiveC.runtime; | |
@import MachO.dyld; | |
@import Foundation; | |
@interface IDELaunchParametersSnapshot : NSObject | |
+ (id)launchParametersWithSchemeIdentifier:(id)schemeIdentifier | |
launcherIdentifier:(id)launcherIdentifier | |
debuggerIdentifier:(id)debuggerIdentifier | |
launchStyle:(int)launchStyle | |
runnableLocation:(id)runnableLocation | |
debugProcessAsUID:(unsigned int)debugProcessAsUID | |
workingDirectory:(id)workingDirectory | |
commandLineArgs:(id)commandLineArgs | |
environmentVariables:(id)environmentVariables | |
architecture:(id)architecture | |
platformIdentifier:(id)platformIdentifier | |
buildConfiguration:(id)buildConfiguration | |
buildableProduct:(id)buildableProduct | |
deviceAppDataPackage:(id)deviceAppDataPackage | |
allowLocationSimulation:(BOOL)allowLocationSimulation | |
locationScenarioReference:(id)locationScenarioReference | |
showNonLocalizedStrings:(BOOL)showNonLocalizedStrings | |
language:(id)language | |
region:(id)region | |
routingCoverageFileReference:(id)routingCoverageFileReference | |
enableGPUFrameCaptureMode:(int)enableGPUFrameCaptureMode | |
enableGPUValidationMode:(int)enableGPUValidationMode | |
debugXPCServices:(BOOL)debugXPCServices | |
debugAppExtensions:(BOOL)debugAppExtensions | |
internalIOSLaunchStyle:(int)internalIOSLaunchStyle | |
internalIOSSubstitutionApp:(id)internalIOSSubstitutionApp | |
launchAutomaticallySubstyle:(unsigned long long)launchAutomaticallySubstyle; | |
@end | |
@interface IDELaunchParametersSnapshot (FixUICoverage) | |
@end | |
extern NSString * const IDEDefaultLauncherIdentifier; | |
extern NSString * const IDEDefaultDebuggerIdentifier; | |
@implementation IDELaunchParametersSnapshot (FixUICoverage) | |
+ (void)load { | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
SEL originalSelector = @selector(launchParametersWithSchemeIdentifier:launcherIdentifier:debuggerIdentifier:launchStyle:runnableLocation:debugProcessAsUID:workingDirectory:commandLineArgs:environmentVariables:architecture:platformIdentifier:buildConfiguration:buildableProduct:deviceAppDataPackage:allowLocationSimulation:locationScenarioReference:showNonLocalizedStrings:language:region:routingCoverageFileReference:enableGPUFrameCaptureMode:enableGPUValidationMode:debugXPCServices:debugAppExtensions:internalIOSLaunchStyle:internalIOSSubstitutionApp:launchAutomaticallySubstyle:); | |
SEL hookSelector = @selector(hook_launchParametersWithSchemeIdentifier:launcherIdentifier:debuggerIdentifier:launchStyle:runnableLocation:debugProcessAsUID:workingDirectory:commandLineArgs:environmentVariables:architecture:platformIdentifier:buildConfiguration:buildableProduct:deviceAppDataPackage:allowLocationSimulation:locationScenarioReference:showNonLocalizedStrings:language:region:routingCoverageFileReference:enableGPUFrameCaptureMode:enableGPUValidationMode:debugXPCServices:debugAppExtensions:internalIOSLaunchStyle:internalIOSSubstitutionApp:launchAutomaticallySubstyle:); | |
SEL storedOriginalSelector = @selector(original_launchParametersWithSchemeIdentifier:launcherIdentifier:debuggerIdentifier:launchStyle:runnableLocation:debugProcessAsUID:workingDirectory:commandLineArgs:environmentVariables:architecture:platformIdentifier:buildConfiguration:buildableProduct:deviceAppDataPackage:allowLocationSimulation:locationScenarioReference:showNonLocalizedStrings:language:region:routingCoverageFileReference:enableGPUFrameCaptureMode:enableGPUValidationMode:debugXPCServices:debugAppExtensions:internalIOSLaunchStyle:internalIOSSubstitutionApp:launchAutomaticallySubstyle:); | |
Class instanceClass = self.class; | |
Class metaClass = objc_getMetaClass(object_getClassName(instanceClass)); | |
unsigned int methodCount = 0; | |
Method *methods = class_copyMethodList(metaClass, &methodCount); | |
const char *targetSelectorName = sel_getName(originalSelector); | |
BOOL foundSelector = FALSE; | |
for (int i=0; i < methodCount; i++) { | |
Method method = methods[i]; | |
SEL selector = method_getName(method); | |
const char *name = sel_getName(selector); | |
if (strcmp(name, targetSelectorName) == 0) { | |
foundSelector = TRUE; | |
break; | |
} | |
} | |
if (!foundSelector) { | |
NSLog(@"ERROR: Not implementing UI Coverage fix, couldn't find target selector %s", targetSelectorName); | |
return; | |
} | |
Method originalMethod = class_getInstanceMethod(metaClass, originalSelector); | |
Method hookMethod = class_getInstanceMethod(metaClass, hookSelector); | |
const char *originalTypeEncoding = method_getTypeEncoding(originalMethod); | |
const char *hookedTypeEncoding = method_getTypeEncoding(hookMethod); | |
if (strcmp(originalTypeEncoding, hookedTypeEncoding) != 0) { | |
NSLog(@"ERROR: Not implementing UI Coverage fix, target type encoding %s doesn't match hook type encoding: %s", | |
originalTypeEncoding, hookedTypeEncoding); | |
return; | |
} | |
IMP originalImplementation = method_getImplementation(originalMethod); | |
IMP hookImplementation = method_getImplementation(hookMethod); | |
class_replaceMethod(metaClass, storedOriginalSelector, originalImplementation, originalTypeEncoding); | |
class_replaceMethod(metaClass, originalSelector, hookImplementation, hookedTypeEncoding); | |
NSLog(@"Implemented UI Coverage fix"); | |
}); | |
} | |
// MARK: - Hook | |
+ (id)hook_launchParametersWithSchemeIdentifier:(id)schemeIdentifier | |
launcherIdentifier:(id)launcherIdentifier | |
debuggerIdentifier:(id)debuggerIdentifier | |
launchStyle:(int)launchStyle | |
runnableLocation:(id)runnableLocation | |
debugProcessAsUID:(unsigned int)debugProcessAsUID | |
workingDirectory:(id)workingDirectory | |
commandLineArgs:(id)commandLineArgs | |
environmentVariables:(id)environmentVariables | |
architecture:(id)architecture | |
platformIdentifier:(id)platformIdentifier | |
buildConfiguration:(id)buildConfiguration | |
buildableProduct:(id)buildableProduct | |
deviceAppDataPackage:(id)deviceAppDataPackage | |
allowLocationSimulation:(BOOL)allowLocationSimulation | |
locationScenarioReference:(id)locationScenarioReference | |
showNonLocalizedStrings:(BOOL)showNonLocalizedStrings | |
language:(id)language | |
region:(id)region | |
routingCoverageFileReference:(id)routingCoverageFileReference | |
enableGPUFrameCaptureMode:(int)enableGPUFrameCaptureMode | |
enableGPUValidationMode:(int)enableGPUValidationMode | |
debugXPCServices:(BOOL)debugXPCServices | |
debugAppExtensions:(BOOL)debugAppExtensions | |
internalIOSLaunchStyle:(int)internalIOSLaunchStyle | |
internalIOSSubstitutionApp:(id)internalIOSSubstitutionApp | |
launchAutomaticallySubstyle:(unsigned long long)launchAutomaticallySubstyle { | |
NSLog(@"UI Coverage Fix: Using LLDB Launcher & Debugger"); | |
id newLauncher = IDEDefaultLauncherIdentifier; | |
id newDebugger = IDEDefaultDebuggerIdentifier; | |
NSLog(@"launcherIdentifier original: %@, new: %@", launcherIdentifier, newLauncher); | |
NSLog(@"debuggerIdentifier original: %@, new: %@", debuggerIdentifier, newDebugger); | |
return [self original_launchParametersWithSchemeIdentifier:schemeIdentifier | |
launcherIdentifier:newLauncher | |
debuggerIdentifier:newDebugger | |
launchStyle:launchStyle | |
runnableLocation:runnableLocation | |
debugProcessAsUID:debugProcessAsUID | |
workingDirectory:workingDirectory | |
commandLineArgs:commandLineArgs | |
environmentVariables:environmentVariables | |
architecture:architecture | |
platformIdentifier:platformIdentifier | |
buildConfiguration:buildConfiguration | |
buildableProduct:buildableProduct | |
deviceAppDataPackage:deviceAppDataPackage | |
allowLocationSimulation:allowLocationSimulation | |
locationScenarioReference:locationScenarioReference | |
showNonLocalizedStrings:showNonLocalizedStrings | |
language:language | |
region:region | |
routingCoverageFileReference:routingCoverageFileReference | |
enableGPUFrameCaptureMode:enableGPUFrameCaptureMode | |
enableGPUValidationMode:enableGPUValidationMode | |
debugXPCServices:debugXPCServices | |
debugAppExtensions:debugAppExtensions | |
internalIOSLaunchStyle:internalIOSLaunchStyle | |
internalIOSSubstitutionApp:internalIOSSubstitutionApp | |
launchAutomaticallySubstyle:launchAutomaticallySubstyle]; | |
} | |
// MARK: - Original gets stored here | |
+ (id)original_launchParametersWithSchemeIdentifier:(id)schemeIdentifier | |
launcherIdentifier:(id)launcherIdentifier | |
debuggerIdentifier:(id)debuggerIdentifier | |
launchStyle:(int)launchStyle | |
runnableLocation:(id)runnableLocation | |
debugProcessAsUID:(unsigned int)debugProcessAsUID | |
workingDirectory:(id)workingDirectory | |
commandLineArgs:(id)commandLineArgs | |
environmentVariables:(id)environmentVariables | |
architecture:(id)architecture | |
platformIdentifier:(id)platformIdentifier | |
buildConfiguration:(id)buildConfiguration | |
buildableProduct:(id)buildableProduct | |
deviceAppDataPackage:(id)deviceAppDataPackage | |
allowLocationSimulation:(BOOL)allowLocationSimulation | |
locationScenarioReference:(id)locationScenarioReference | |
showNonLocalizedStrings:(BOOL)showNonLocalizedStrings | |
language:(id)language | |
region:(id)region | |
routingCoverageFileReference:(id)routingCoverageFileReference | |
enableGPUFrameCaptureMode:(int)enableGPUFrameCaptureMode | |
enableGPUValidationMode:(int)enableGPUValidationMode | |
debugXPCServices:(BOOL)debugXPCServices | |
debugAppExtensions:(BOOL)debugAppExtensions | |
internalIOSLaunchStyle:(int)internalIOSLaunchStyle | |
internalIOSSubstitutionApp:(id)internalIOSSubstitutionApp | |
launchAutomaticallySubstyle:(unsigned long long)launchAutomaticallySubstyle { | |
[NSException raise:@"FailedHook" format:@"Hook failed: This method should never be run."]; | |
return nil; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment