Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Xcode 6 and iOS 8. #259

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
14 changes: 12 additions & 2 deletions Integration Tests/Tests/SLElementVisibilityTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,19 @@
#import "SLIntegrationTest.h"
#import "SLUIAElement+Subclassing.h"

// So that Subliminal may continue to be built using Xcode 5/the iOS 7.1 SDK.
#ifndef kCFCoreFoundationVersionNumber_iOS_7_1
#define kCFCoreFoundationVersionNumber_iOS_7_1 847.24
#endif

/**
Subliminal's implementation of -isVisible does not rely upon UIAutomation,
because UIAElement.isVisible() has a number of bugs as exercised in
-testViewIsNotVisibleIfItIsHiddenEvenInTableViewCell
-testAccessibilityElementIsNotVisibleIfContainerIsHiddenEvenInTableViewCell
-testViewIsVisibleIfItsCenterIsCoveredByClearRegion
-testViewIsNotVisibleIfCenterAndUpperLeftHandCornerAreCovered
-testViewIsNotVisibleIfCenterAndAnyCornerAreCovered
-testAccessibilityElementIsNotVisibleIfItsCenterIsCoveredByView

Subliminal's implementation otherwise attempts to conform to UIAutomation's
definition of visibility, as demonstrated by the below test cases.
Expand Down Expand Up @@ -345,7 +351,11 @@ - (void)testAccessibilityElementIsNotVisibleIfItIsOffscreen {
}

- (void)testAccessibilityElementIsNotVisibleIfItsCenterIsCoveredByView {
SLAssertFalse([_testElement uiaIsVisible], @"UIAutomation should say that the element is not visible.");
if (kCFCoreFoundationVersionNumber <= kCFCoreFoundationVersionNumber_iOS_7_1) {
SLAssertFalse([_testElement uiaIsVisible], @"UIAutomation should say that the element is not visible.");
} else {
SLAssertTrue([_testElement uiaIsVisible], @"UIAutomation should say that the element is not visible, but it doesn't.");
}
SLAssertFalse([_testElement isVisible], @"Subliminal should say that the element is not visible.");

// the test view is the container of the test element
Expand Down
17 changes: 13 additions & 4 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,14 @@ DOCSET_DIR = "#{ENV['HOME']}/Library/Developer/Shared/Documentation/DocSets"
DOCSET_NAME = "com.inkling.Subliminal.docset"
DOCSET_VERSION = "1.1.0"

SUPPORTED_SDKS = [ "6.1", "7.1" ]
USING_XCODE_6 = `xcrun xcodebuild -version | head -n 1`.start_with?("Xcode 6")

if USING_XCODE_6
SUPPORTED_SDKS = [ "7.1", "8.0" ]
else
SUPPORTED_SDKS = [ "6.1", "7.1" ]
end

TEST_SDK = ENV["TEST_SDK"]
if TEST_SDK
raise "Test SDK #{TEST_SDK} is not supported." unless SUPPORTED_SDKS.include?(TEST_SDK)
Expand Down Expand Up @@ -485,8 +492,9 @@ namespace :test do

# Use system so we see the tests' output
results_dir = fresh_results_dir!("iphone", sdk)
# Use the 3.5" iPhone Retina because that can support both our target SDKs
if system("#{base_test_command} -output \"#{results_dir}\" -sim_device 'iPhone Retina (3.5-inch)' -sim_version #{sdk}")
# Use the iPhone 4s because that can support all our target SDKs
device_type = USING_XCODE_6 ? "iPhone 4s" : "iPhone Retina (3.5-inch)"
if system("#{base_test_command} -output \"#{results_dir}\" -sim_device '#{device_type}' -sim_version #{sdk}")
puts "iPhone integration tests succeeded on iOS #{sdk}.\n\n"
else
puts "iPhone integration tests failed on iOS #{sdk}.\n\n"
Expand All @@ -512,7 +520,8 @@ namespace :test do

# Use system so we see the tests' output
results_dir = fresh_results_dir!("ipad", sdk)
if system("#{base_test_command} -output \"#{results_dir}\" -sim_device 'iPad' -sim_version #{sdk}")
# Use the iPad Retina because that can support all our target SDKs
if system("#{base_test_command} -output \"#{results_dir}\" -sim_device 'iPad Retina' -sim_version #{sdk}")
puts "iPad integration tests succeeded on iOS #{sdk}.\n\n"
else
puts "iPad integration tests failed on iOS #{sdk}.\n\n"
Expand Down
85 changes: 55 additions & 30 deletions Sources/Classes/Internal/Terminal/SLTerminal.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@

#import "SLTerminal.h"

// So that Subliminal may continue to be built using Xcode 5/the iOS 7.1 SDK,
// and also so that this file may be imported by `subliminal-instrument`, an OS X
// CLI tool, as part of the SLLogger framework.
#ifndef kCFCoreFoundationVersionNumber_iOS_7_1
#define kCFCoreFoundationVersionNumber_iOS_7_1 847.24
#endif

NSString *const SLTerminalJavaScriptException = @"SLTerminalJavaScriptException";

Expand Down Expand Up @@ -114,11 +120,23 @@ - (BOOL)currentQueueIsEvalQueue
return dispatch_get_specific(kEvalQueueIdentifier) != NULL;
}

#if TARGET_IPHONE_SIMULATOR
// in the simulator, UIAutomation uses a target-specific plist in ~/Library/Application Support/iPhone Simulator/[system version]/Library/Preferences/[bundle ID].plist
// _not_ the NSUserDefaults plist, in the sandboxed Library
// see http://stackoverflow.com/questions/4977673/reading-preferences-set-by-uiautomations-uiaapplication-setpreferencesvaluefork
- (NSString *)simulatorPreferencesPath {
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_MAC
/**
In Simulators below iOS 8, UIAutomation does not use the `NSUserDefaults` plist
found in the sandboxed Library, but rather a target-specific plist found at the
host domain level i.e. at `~/Library/Application Support/iPhone Simulator/[system version]/Library/Preferences/`.
See http://stackoverflow.com/questions/4977673/reading-preferences-set-by-uiautomations-uiaapplication-setpreferencesvaluefork .

On devices, and in Simulators as of iOS 8, UIAutomation uses the same plist as
`NSUserDefaults`.

The Mac conditional allows `subliminal-instrument`, an OS X CLI tool, to import
this file as part of the SLLogger framework.
*/
- (NSString *)preIOS8SimulatorPreferencesPath {
NSAssert(kCFCoreFoundationVersionNumber <= kCFCoreFoundationVersionNumber_iOS_7_1,
@"This method must only be called below iOS 8.");

static NSString *path = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Expand All @@ -127,6 +145,7 @@ - (NSString *)simulatorPreferencesPath {

// 1. get into the simulator's app support directory by fetching the sandboxed Library's path
NSString *userDirectoryPath = [(NSURL *)[[[NSFileManager defaultManager] URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject] path];

// 2. get out of our application directory, back to the root support directory for this system version
plistRootPath = [userDirectoryPath substringToIndex:([userDirectoryPath rangeOfString:@"Applications"].location)];

Expand All @@ -139,7 +158,7 @@ - (NSString *)simulatorPreferencesPath {
});
return path;
}
#endif // TARGET_IPHONE_SIMULATOR
#endif // TARGET_IPHONE_SIMULATOR || TARGET_OS_MAC


#pragma mark - Communication
Expand Down Expand Up @@ -188,36 +207,42 @@ - (id)eval:(NSString *)script {
}

// Step 1: Write the script to UIAutomation
BOOL targetIsSimulator = NO;
#if TARGET_IPHONE_SIMULATOR
NSMutableDictionary *prefs = [NSMutableDictionary dictionaryWithContentsOfFile:[self simulatorPreferencesPath]];
if (!prefs) {
prefs = [NSMutableDictionary dictionary];
}
[prefs setObject:@( _scriptIndex ) forKey:SLTerminalPreferencesKeyScriptIndex];
[prefs setObject:script forKey:SLTerminalPreferencesKeyScript];
[prefs removeObjectForKey:SLTerminalPreferencesKeyResultIndex];
[prefs removeObjectForKey:SLTerminalPreferencesKeyResult];
[prefs removeObjectForKey:SLTerminalPreferencesKeyException];
[prefs writeToFile:[self simulatorPreferencesPath] atomically:YES];
#else
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@( _scriptIndex ) forKey:SLTerminalPreferencesKeyScriptIndex];
[defaults setObject:script forKey:SLTerminalPreferencesKeyScript];
[defaults removeObjectForKey:SLTerminalPreferencesKeyResultIndex];
[defaults removeObjectForKey:SLTerminalPreferencesKeyResult];
[defaults removeObjectForKey:SLTerminalPreferencesKeyException];
[defaults synchronize];
targetIsSimulator = YES;
#endif

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

if (targetIsSimulator && (kCFCoreFoundationVersionNumber <= kCFCoreFoundationVersionNumber_iOS_7_1)) {
NSMutableDictionary *prefs = [NSMutableDictionary dictionaryWithContentsOfFile:[self preIOS8SimulatorPreferencesPath]];
if (!prefs) {
prefs = [NSMutableDictionary dictionary];
}
[prefs setObject:@( _scriptIndex ) forKey:SLTerminalPreferencesKeyScriptIndex];
[prefs setObject:script forKey:SLTerminalPreferencesKeyScript];
[prefs removeObjectForKey:SLTerminalPreferencesKeyResultIndex];
[prefs removeObjectForKey:SLTerminalPreferencesKeyResult];
[prefs removeObjectForKey:SLTerminalPreferencesKeyException];
[prefs writeToFile:[self preIOS8SimulatorPreferencesPath] atomically:YES];
} else {
[defaults setObject:@( _scriptIndex ) forKey:SLTerminalPreferencesKeyScriptIndex];
[defaults setObject:script forKey:SLTerminalPreferencesKeyScript];
[defaults removeObjectForKey:SLTerminalPreferencesKeyResultIndex];
[defaults removeObjectForKey:SLTerminalPreferencesKeyResult];
[defaults removeObjectForKey:SLTerminalPreferencesKeyException];
[defaults synchronize];
}

// Step 2: Wait for the result
NSDictionary *resultPrefs = nil;
while (1) {
#if TARGET_IPHONE_SIMULATOR
resultPrefs = [NSDictionary dictionaryWithContentsOfFile:[self simulatorPreferencesPath]];
#else
[defaults synchronize];
resultPrefs = [defaults dictionaryRepresentation];
#endif
if (targetIsSimulator && (kCFCoreFoundationVersionNumber <= kCFCoreFoundationVersionNumber_iOS_7_1)) {
resultPrefs = [NSDictionary dictionaryWithContentsOfFile:[self preIOS8SimulatorPreferencesPath]];
} else {
[defaults synchronize];
resultPrefs = [defaults dictionaryRepresentation];
}

if (resultPrefs[SLTerminalPreferencesKeyResultIndex]) {
NSAssert([resultPrefs[SLTerminalPreferencesKeyResultIndex] intValue] == _scriptIndex, @"Result index is out of sync with script index");
Expand Down
11 changes: 10 additions & 1 deletion Sources/Classes/SLTestController.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@

#import <objc/runtime.h>

// So that Subliminal may continue to be built using Xcode 5/the iOS 7.1 SDK.
#ifndef kCFCoreFoundationVersionNumber_iOS_7_1
#define kCFCoreFoundationVersionNumber_iOS_7_1 847.24
#endif

const unsigned int SLTestControllerRandomSeed = UINT_MAX;

Expand Down Expand Up @@ -278,7 +282,12 @@ - (void)warnIfAccessibilityInspectorIsEnabled {
NSString *userDirectoryPath = [(NSURL *)[[[NSFileManager defaultManager] URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject] path];

// 2. get out of our application directory, back to the root support directory for this system version
NSString *plistRootPath = [userDirectoryPath substringToIndex:([userDirectoryPath rangeOfString:@"Applications"].location)];
NSString *plistRootPath = nil;
if (kCFCoreFoundationVersionNumber <= kCFCoreFoundationVersionNumber_iOS_7_1) {
plistRootPath = [userDirectoryPath substringToIndex:([userDirectoryPath rangeOfString:@"Applications"].location)];
} else {
plistRootPath = [userDirectoryPath substringToIndex:([userDirectoryPath rangeOfString:@"data"].location)];
}

// 3. locate, relative to here, the Accessibility preferences
NSString *relativePlistPath = @"Library/Preferences/com.apple.Accessibility.plist";
Expand Down
14 changes: 13 additions & 1 deletion Subliminal.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1210,7 +1210,7 @@
isa = PBXProject;
attributes = {
LastTestingUpgradeCheck = 0510;
LastUpgradeCheck = 0500;
LastUpgradeCheck = 0600;
ORGANIZATIONNAME = Inkling;
};
buildConfigurationList = F0695D8516011515000B05D0 /* Build configuration list for PBXProject "Subliminal" */;
Expand Down Expand Up @@ -1627,8 +1627,10 @@
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
Expand Down Expand Up @@ -1662,8 +1664,10 @@
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
Expand Down Expand Up @@ -1767,6 +1771,10 @@
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Unit Tests/Subliminal Unit Tests-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"SENTEST_IGNORE_DEPRECATION_WARNING=1",
);
GCC_WARN_UNDECLARED_SELECTOR = NO;
HEADER_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -1790,6 +1798,10 @@
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Unit Tests/Subliminal Unit Tests-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"SENTEST_IGNORE_DEPRECATION_WARNING=1",
);
GCC_WARN_UNDECLARED_SELECTOR = NO;
HEADER_SEARCH_PATHS = (
"$(inherited)",
Expand Down
Loading