Skip to content
Open
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