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 Dec 29, 2015
1 parent f731c4b commit c2b8911
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 7 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 recdeiver's @c keyWindow. Raises an assertion if @c nil.
*/
- (UIWindow *)fb_strictKeyWindow;

@end
26 changes: 26 additions & 0 deletions FBSnapshotTestCase/Categories/UIApplication+StrictKeyWindow.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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,7 @@
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 */; };
F02D88451B1F246300BFBC1D /* FBSnapshotTestCaseSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F02D88441B1F246300BFBC1D /* FBSnapshotTestCaseSwiftTests.swift */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -41,6 +42,7 @@
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>"; };
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 @@ -122,6 +124,7 @@
isa = PBXGroup;
children = (
B30449211AB794320067C75D /* FBSnapshotTestCaseDemoTests.m */,
BC97FF041C17E4C900FA40E3 /* FBSnapshotTestCaseDeviceAgnosticTests.m */,
F02D88441B1F246300BFBC1D /* FBSnapshotTestCaseSwiftTests.swift */,
B304491F1AB794320067C75D /* Supporting Files */,
);
Expand Down Expand Up @@ -313,6 +316,7 @@
buildActionMask = 2147483647;
files = (
B30449221AB794320067C75D /* FBSnapshotTestCaseDemoTests.m in Sources */,
BC97FF051C17E4C900FA40E3 /* FBSnapshotTestCaseDeviceAgnosticTests.m in Sources */,
F02D88451B1F246300BFBC1D /* FBSnapshotTestCaseSwiftTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2013, 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/FBSnapshotTestCase.h>

@interface FBSnapshotTestCaseDeviceAgnosticTests : FBSnapshotTestCase

@end

@implementation FBSnapshotTestCaseDeviceAgnosticTests

- (void)setUp
{
[super setUp];
self.deviceAgnostic = YES;
self.recordMode = NO;
}

- (void)testSuccessfulComparisonOfDeviceAgnosticReferenceImages
{
UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
redView.backgroundColor = [UIColor redColor];
FBSnapshotVerifyView(redView, nil);
FBSnapshotVerifyLayer(redView.layer, nil);
}

@end
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c2b8911

Please sign in to comment.