Skip to content
This repository has been archived by the owner on Jan 13, 2022. It is now read-only.

Commit

Permalink
add device agnostic tests & modify snapshot window fallback
Browse files Browse the repository at this point in the history
Use UIApplication.keyWindow instead of a manually-created & installed
window when snapshotting a view w/o a window.  This prevents keyWindow
from being `nil` when generating device-agnostic reference image
filenames.

Accessing keyWindow is done through a category, so as to always throw an
assertion if it is nil, instructing developers to make sure their tests
have a host application w/ a key window.
  • Loading branch information
bgerstle committed Jan 3, 2016
1 parent f731c4b commit e437a75
Show file tree
Hide file tree
Showing 17 changed files with 350 additions and 13 deletions.
8 changes: 8 additions & 0 deletions FBSnapshotTestCase.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
B32447DC1AB78B5E00B1D6FF /* square_with_text.png in Resources */ = {isa = PBXBuildFile; fileRef = B32447D91AB78B5E00B1D6FF /* square_with_text.png */; };
B32447DD1AB78B5E00B1D6FF /* square-copy.png in Resources */ = {isa = PBXBuildFile; fileRef = B32447DA1AB78B5E00B1D6FF /* square-copy.png */; };
B32447DE1AB78B5E00B1D6FF /* square.png in Resources */ = {isa = PBXBuildFile; fileRef = B32447DB1AB78B5E00B1D6FF /* square.png */; };
BC45D5211C2AEFCE007C72F3 /* UIApplication+StrictKeyWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = BC45D51F1C2AEFCE007C72F3 /* UIApplication+StrictKeyWindow.h */; };
BC45D5221C2AEFCE007C72F3 /* UIApplication+StrictKeyWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = BC45D5201C2AEFCE007C72F3 /* UIApplication+StrictKeyWindow.m */; };
E5C2CD621B1F399A00669887 /* square_with_pixel.png in Resources */ = {isa = PBXBuildFile; fileRef = E5C2CD611B1F399A00669887 /* square_with_pixel.png */; };
F0D698F51B204E120005CAC9 /* SwiftSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0D698F41B204E120005CAC9 /* SwiftSupport.swift */; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -59,6 +61,8 @@
B32447D91AB78B5E00B1D6FF /* square_with_text.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = square_with_text.png; sourceTree = "<group>"; };
B32447DA1AB78B5E00B1D6FF /* square-copy.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "square-copy.png"; sourceTree = "<group>"; };
B32447DB1AB78B5E00B1D6FF /* square.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = square.png; sourceTree = "<group>"; };
BC45D51F1C2AEFCE007C72F3 /* UIApplication+StrictKeyWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIApplication+StrictKeyWindow.h"; sourceTree = "<group>"; };
BC45D5201C2AEFCE007C72F3 /* UIApplication+StrictKeyWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIApplication+StrictKeyWindow.m"; sourceTree = "<group>"; };
E5C2CD611B1F399A00669887 /* square_with_pixel.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = square_with_pixel.png; sourceTree = "<group>"; };
F0D698F41B204E120005CAC9 /* SwiftSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftSupport.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -91,6 +95,8 @@
133564131B59C3F500A4E4BF /* UIImage+Diff.m */,
133564141B59C3F500A4E4BF /* UIImage+Snapshot.h */,
133564151B59C3F500A4E4BF /* UIImage+Snapshot.m */,
BC45D51F1C2AEFCE007C72F3 /* UIApplication+StrictKeyWindow.h */,
BC45D5201C2AEFCE007C72F3 /* UIApplication+StrictKeyWindow.m */,
);
path = Categories;
sourceTree = "<group>";
Expand Down Expand Up @@ -167,6 +173,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
BC45D5211C2AEFCE007C72F3 /* UIApplication+StrictKeyWindow.h in Headers */,
B31988281AB7849400B0A900 /* FBSnapshotTestCase.h in Headers */,
13CBB39D1AEE013900B6ADBA /* FBSnapshotTestCasePlatform.h in Headers */,
B319882A1AB7849400B0A900 /* FBSnapshotTestController.h in Headers */,
Expand Down Expand Up @@ -279,6 +286,7 @@
buildActionMask = 2147483647;
files = (
133564171B59C3F500A4E4BF /* UIImage+Compare.m in Sources */,
BC45D5221C2AEFCE007C72F3 /* UIApplication+StrictKeyWindow.m in Sources */,
B31988291AB7849400B0A900 /* FBSnapshotTestCase.m in Sources */,
133564191B59C3F500A4E4BF /* UIImage+Diff.m in Sources */,
1335641B1B59C3F500A4E4BF /* UIImage+Snapshot.m in Sources */,
Expand Down
20 changes: 20 additions & 0 deletions FBSnapshotTestCase/Categories/UIApplication+StrictKeyWindow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/

#import <UIKit/UIKit.h>

@interface UIApplication (StrictKeyWindow)

/**
@return The receiver's @c keyWindow. Raises an assertion if @c nil.
*/
- (UIWindow *)fb_strictKeyWindow;

@end
27 changes: 27 additions & 0 deletions FBSnapshotTestCase/Categories/UIApplication+StrictKeyWindow.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/

#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>

@implementation UIApplication (StrictKeyWindow)

- (UIWindow *)fb_strictKeyWindow
{
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
if (!keyWindow) {
[NSException raise:@"FBSnapshotTestCaseNilKeyWindowException"
format:@"Snapshot tests must be hosted by an application with a key window. Please ensure your test"
" host sets up a key window at launch (either via storyboards or programmatically) and doesn't"
" do anything to remove it while snapshot tests are running."];
}
return keyWindow;
}

@end
20 changes: 15 additions & 5 deletions FBSnapshotTestCase/Categories/UIImage+Snapshot.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

#import <FBSnapshotTestCase/UIImage+Snapshot.h>
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>

@implementation UIImage (Snapshot)

Expand Down Expand Up @@ -41,22 +42,31 @@ + (UIImage *)fb_imageForView:(UIView *)view
{
CGRect bounds = view.bounds;
NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view);
NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view);
NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view);

// If the input view is already a UIWindow, then just use that. Otherwise wrap in a window.
UIWindow *window = [view isKindOfClass:[UIWindow class]] ? (UIWindow *)view : view.window;
if (window == nil) {
window = [[UIWindow alloc] initWithFrame:bounds];
BOOL removeFromSuperview = NO;
if (!window) {
window = [[UIApplication sharedApplication] fb_strictKeyWindow];
}

if (!view.window) {
[window addSubview:view];
[window makeKeyAndVisible];
removeFromSuperview = YES;
}

UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
[view layoutIfNeeded];
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];

UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

if (removeFromSuperview) {
[view removeFromSuperview];
}

return snapshot;
}

Expand Down
2 changes: 1 addition & 1 deletion FBSnapshotTestCase/FBSnapshotTestCase.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
/**
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
@param layer The layer to snapshot
@param identifier An optional identifier, used is there are multiple snapshot tests in a given -test method.
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
@param suffixes An NSOrderedSet of strings for the different suffixes
@param tolerance The percentage of pixels that can differ and still count as an 'identical' layer
*/
Expand Down
4 changes: 3 additions & 1 deletion FBSnapshotTestCase/FBSnapshotTestCasePlatform.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>
#import <UIKit/UIKit.h>

BOOL FBSnapshotTestCaseIs64Bit(void)
Expand All @@ -34,7 +35,8 @@ BOOL FBSnapshotTestCaseIs64Bit(void)
NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName)
{
UIDevice *device = [UIDevice currentDevice];
CGSize screenSize = [[UIApplication sharedApplication] keyWindow].bounds.size;
UIWindow *keyWindow = [[UIApplication sharedApplication] fb_strictKeyWindow];
CGSize screenSize = keyWindow.bounds.size;
NSString *os = device.systemVersion;

fileName = [NSString stringWithFormat:@"%@_%@%@_%.0fx%.0f", fileName, device.model, os, screenSize.width, screenSize.height];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
B30449351AB795230067C75D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = B304492C1AB795230067C75D /* InfoPlist.strings */; };
B30449361AB795230067C75D /* FBAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B304492F1AB795230067C75D /* FBAppDelegate.m */; };
B30449391AB795230067C75D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B30449341AB795230067C75D /* Images.xcassets */; };
BC97FF051C17E4C900FA40E3 /* FBSnapshotTestCaseDeviceAgnosticTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC97FF041C17E4C900FA40E3 /* FBSnapshotTestCaseDeviceAgnosticTests.m */; };
BCF12A251C396B2600789343 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BCF12A241C396B2600789343 /* Launch Screen.storyboard */; };
F02D88451B1F246300BFBC1D /* FBSnapshotTestCaseSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F02D88441B1F246300BFBC1D /* FBSnapshotTestCaseSwiftTests.swift */; };
/* End PBXBuildFile section */

Expand All @@ -30,7 +32,6 @@
42711825EC840732F7632DB1 /* Pods-FBSnapshotTestCaseDemoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FBSnapshotTestCaseDemoTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-FBSnapshotTestCaseDemoTests/Pods-FBSnapshotTestCaseDemoTests.release.xcconfig"; sourceTree = "<group>"; };
89032B249B2E1256F91C7504 /* Pods-FBSnapshotTestCaseDemoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FBSnapshotTestCaseDemoTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FBSnapshotTestCaseDemoTests/Pods-FBSnapshotTestCaseDemoTests.debug.xcconfig"; sourceTree = "<group>"; };
B30449021AB794320067C75D /* FBSnapshotTestCaseDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FBSnapshotTestCaseDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
B30449061AB794320067C75D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B30449071AB794320067C75D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
B304491B1AB794320067C75D /* FBSnapshotTestCaseDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FBSnapshotTestCaseDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B30449201AB794320067C75D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand All @@ -41,6 +42,8 @@
B30449321AB795230067C75D /* FBSnapshotTestCaseDemo-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "FBSnapshotTestCaseDemo-Info.plist"; sourceTree = "<group>"; };
B30449331AB795230067C75D /* FBSnapshotTestCaseDemo-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBSnapshotTestCaseDemo-Prefix.pch"; sourceTree = "<group>"; };
B30449341AB795230067C75D /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
BC97FF041C17E4C900FA40E3 /* FBSnapshotTestCaseDeviceAgnosticTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSnapshotTestCaseDeviceAgnosticTests.m; sourceTree = "<group>"; };
BCF12A241C396B2600789343 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = "<group>"; };
F02D88441B1F246300BFBC1D /* FBSnapshotTestCaseSwiftTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FBSnapshotTestCaseSwiftTests.swift; sourceTree = "<group>"; };
F2F94786ED7FC097D31026B6 /* Pods_FBSnapshotTestCaseDemoTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FBSnapshotTestCaseDemoTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -105,14 +108,14 @@
B30449331AB795230067C75D /* FBSnapshotTestCaseDemo-Prefix.pch */,
B30449341AB795230067C75D /* Images.xcassets */,
B30449051AB794320067C75D /* Supporting Files */,
BCF12A241C396B2600789343 /* Launch Screen.storyboard */,
);
path = FBSnapshotTestCaseDemo;
sourceTree = "<group>";
};
B30449051AB794320067C75D /* Supporting Files */ = {
isa = PBXGroup;
children = (
B30449061AB794320067C75D /* Info.plist */,
B30449071AB794320067C75D /* main.m */,
);
name = "Supporting Files";
Expand All @@ -122,6 +125,7 @@
isa = PBXGroup;
children = (
B30449211AB794320067C75D /* FBSnapshotTestCaseDemoTests.m */,
BC97FF041C17E4C900FA40E3 /* FBSnapshotTestCaseDeviceAgnosticTests.m */,
F02D88441B1F246300BFBC1D /* FBSnapshotTestCaseSwiftTests.swift */,
B304491F1AB794320067C75D /* Supporting Files */,
);
Expand Down Expand Up @@ -236,6 +240,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BCF12A251C396B2600789343 /* Launch Screen.storyboard in Resources */,
B30449391AB795230067C75D /* Images.xcassets in Resources */,
B30449351AB795230067C75D /* InfoPlist.strings in Resources */,
);
Expand Down Expand Up @@ -313,6 +318,7 @@
buildActionMask = 2147483647;
files = (
B30449221AB794320067C75D /* FBSnapshotTestCaseDemoTests.m in Sources */,
BC97FF051C17E4C900FA40E3 /* FBSnapshotTestCaseDeviceAgnosticTests.m in Sources */,
F02D88451B1F246300BFBC1D /* FBSnapshotTestCaseSwiftTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -421,6 +427,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = "Brand Assets";
INFOPLIST_FILE = "FBSnapshotTestCaseDemo/FBSnapshotTestCaseDemo-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -431,6 +438,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = "Brand Assets";
INFOPLIST_FILE = "FBSnapshotTestCaseDemo/FBSnapshotTestCaseDemo-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
Expand All @@ -62,15 +62,18 @@
ReferencedContainer = "container:FBSnapshotTestCaseDemo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
Expand All @@ -93,10 +96,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>Launch Screen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIRequiresFullScreen</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"images" : [
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "1x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Loading

0 comments on commit e437a75

Please sign in to comment.