- Update Pod
This commit is contained in:
1 parent
c240fe6257
commit
29704c7514
112 files changed
+1526
-2621
No files matched your search
@@ -0,0 +1,15 @@
|
||||
framework module FBSnapshotTestCase {
|
||||
umbrella header "FBSnapshotTestCase.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
|
||||
header "FBSnapshotTestCase.h"
|
||||
header "FBSnapshotTestCasePlatform.h"
|
||||
header "FBSnapshotTestController.h"
|
||||
|
||||
private header "UIImage+Compare.h"
|
||||
private header "UIImage+Diff.h"
|
||||
private header "UIImage+Snapshot.h"
|
||||
}
|
||||
|
||||
+1
-1
@@ -32,6 +32,6 @@
|
||||
|
||||
@interface UIImage (Compare)
|
||||
|
||||
- (BOOL)compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance;
|
||||
- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance;
|
||||
|
||||
@end
|
||||
+3
-3
@@ -28,7 +28,7 @@
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import "UIImage+Compare.h"
|
||||
#import <FBSnapshotTestCase/UIImage+Compare.h>
|
||||
|
||||
// This makes debugging much more fun
|
||||
typedef union {
|
||||
@@ -44,7 +44,7 @@ typedef union {
|
||||
|
||||
@implementation UIImage (Compare)
|
||||
|
||||
- (BOOL)compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
|
||||
- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
|
||||
{
|
||||
NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size.");
|
||||
|
||||
@@ -101,7 +101,7 @@ typedef union {
|
||||
imageEqual = (memcmp(referenceImagePixels, imagePixels, referenceImageSizeBytes) == 0);
|
||||
} else {
|
||||
// Go through each pixel in turn and see if it is different
|
||||
const NSInteger pixelCount = self.size.width * self.size.height;
|
||||
const NSInteger pixelCount = referenceImageSize.width * referenceImageSize.height;
|
||||
|
||||
FBComparePixel *p1 = referenceImagePixels;
|
||||
FBComparePixel *p2 = imagePixels;
|
||||
+1
-1
@@ -32,6 +32,6 @@
|
||||
|
||||
@interface UIImage (Diff)
|
||||
|
||||
- (UIImage *)diffWithImage:(UIImage *)image;
|
||||
- (UIImage *)fb_diffWithImage:(UIImage *)image;
|
||||
|
||||
@end
|
||||
+2
-2
@@ -28,11 +28,11 @@
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import "UIImage+Diff.h"
|
||||
#import <FBSnapshotTestCase/UIImage+Diff.h>
|
||||
|
||||
@implementation UIImage (Diff)
|
||||
|
||||
- (UIImage *)diffWithImage:(UIImage *)image
|
||||
- (UIImage *)fb_diffWithImage:(UIImage *)image
|
||||
{
|
||||
if (!image) {
|
||||
return nil;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 UIImage (Snapshot)
|
||||
|
||||
/// Uses renderInContext: to get a snapshot of the layer.
|
||||
+ (UIImage *)fb_imageForLayer:(CALayer *)layer;
|
||||
|
||||
/// Uses renderInContext: to get a snapshot of the view layer.
|
||||
+ (UIImage *)fb_imageForViewLayer:(UIView *)view;
|
||||
|
||||
/// Uses drawViewHierarchyInRect: to get a snapshot of the view and adds the view into a window if needed.
|
||||
+ (UIImage *)fb_imageForView:(UIView *)view;
|
||||
|
||||
@end
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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/UIImage+Snapshot.h>
|
||||
|
||||
@implementation UIImage (Snapshot)
|
||||
|
||||
+ (UIImage *)fb_imageForLayer:(CALayer *)layer
|
||||
{
|
||||
CGRect bounds = layer.bounds;
|
||||
NSAssert1(CGRectGetWidth(bounds), @"Zero width for layer %@", layer);
|
||||
NSAssert1(CGRectGetHeight(bounds), @"Zero height for layer %@", layer);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
NSAssert1(context, @"Could not generate context for layer %@", layer);
|
||||
CGContextSaveGState(context);
|
||||
[layer layoutIfNeeded];
|
||||
[layer renderInContext:context];
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
+ (UIImage *)fb_imageForViewLayer:(UIView *)view
|
||||
{
|
||||
[view layoutIfNeeded];
|
||||
return [self fb_imageForLayer:view.layer];
|
||||
}
|
||||
|
||||
+ (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);
|
||||
|
||||
UIWindow *window = view.window;
|
||||
if (window == nil) {
|
||||
window = [[UIWindow alloc] initWithFrame:bounds];
|
||||
[window addSubview:view];
|
||||
[window makeKeyAndVisible];
|
||||
}
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
|
||||
[view layoutIfNeeded];
|
||||
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
|
||||
|
||||
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
@end
|
||||
+106
-33
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Facebook, Inc.
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
@@ -16,6 +16,21 @@
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
/*
|
||||
There are three ways of setting reference image directories.
|
||||
|
||||
1. Set the preprocessor macro FB_REFERENCE_IMAGE_DIR to a double quoted
|
||||
c-string with the path.
|
||||
2. Set an environment variable named FB_REFERENCE_IMAGE_DIR with the path. This
|
||||
takes precedence over the preprocessor macro to allow for run-time override.
|
||||
3. Keep everything unset, which will cause the reference images to be looked up
|
||||
inside the bundle holding the current test, in the
|
||||
Resources/ReferenceImages_* directories.
|
||||
*/
|
||||
#ifndef FB_REFERENCE_IMAGE_DIR
|
||||
#define FB_REFERENCE_IMAGE_DIR ""
|
||||
#endif
|
||||
|
||||
/**
|
||||
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
|
||||
@param view The view to snapshot
|
||||
@@ -24,25 +39,11 @@
|
||||
@param tolerance The percentage of pixels that can differ and still count as an 'identical' view
|
||||
*/
|
||||
#define FBSnapshotVerifyViewWithOptions(view__, identifier__, suffixes__, tolerance__) \
|
||||
{ \
|
||||
NSString *envReferenceImageDirectory = [NSProcessInfo processInfo].environment[@"FB_REFERENCE_IMAGE_DIR"]; \
|
||||
NSError *error__ = nil; \
|
||||
BOOL comparisonSuccess__; \
|
||||
XCTAssertTrue((suffixes__.count > 0), @"Suffixes set cannot be empty %@", suffixes__); \
|
||||
XCTAssertNotNil(envReferenceImageDirectory, @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.");\
|
||||
for (NSString *suffix__ in suffixes__) { \
|
||||
NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", envReferenceImageDirectory, suffix__]; \
|
||||
comparisonSuccess__ = [self compareSnapshotOfView:(view__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \
|
||||
if (comparisonSuccess__ || self.recordMode) break; \
|
||||
} \
|
||||
XCTAssertTrue(comparisonSuccess__, @"Snapshot comparison failed: %@", error__); \
|
||||
XCTAssertFalse(self.recordMode, @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!"); \
|
||||
}
|
||||
FBSnapshotVerifyViewOrLayerWithOptions(View, view__, identifier__, suffixes__, tolerance__)
|
||||
|
||||
#define FBSnapshotVerifyView(view__, identifier__) \
|
||||
{ \
|
||||
FBSnapshotVerifyViewWithOptions(view__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0); \
|
||||
}
|
||||
FBSnapshotVerifyViewWithOptions(view__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
|
||||
|
||||
|
||||
/**
|
||||
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
|
||||
@@ -52,32 +53,64 @@ FBSnapshotVerifyViewWithOptions(view__, identifier__, FBSnapshotTestCaseDefaultS
|
||||
@param tolerance The percentage of pixels that can differ and still count as an 'identical' layer
|
||||
*/
|
||||
#define FBSnapshotVerifyLayerWithOptions(layer__, identifier__, suffixes__, tolerance__) \
|
||||
{ \
|
||||
NSString *envReferenceImageDirectory = [NSProcessInfo processInfo].environment[@"FB_REFERENCE_IMAGE_DIR"]; \
|
||||
NSError *error__ = nil; \
|
||||
BOOL comparisonSuccess__; \
|
||||
XCTAssertTrue((suffixes__.count > 0), @"Suffixes set cannot be empty %@", suffixes__); \
|
||||
XCTAssertNotNil(envReferenceImageDirectory, @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.");\
|
||||
for (NSString *suffix__ in suffixes__) { \
|
||||
NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", envReferenceImageDirectory, suffix__]; \
|
||||
comparisonSuccess__ = [self compareSnapshotOfLayer:(layer__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \
|
||||
if (comparisonSuccess__ || self.recordMode) break; \
|
||||
} \
|
||||
XCTAssertTrue(comparisonSuccess__, @"Snapshot comparison failed: %@", error__); \
|
||||
XCTAssertFalse(self.recordMode, @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!"); \
|
||||
}
|
||||
FBSnapshotVerifyViewOrLayerWithOptions(Layer, layer__, identifier__, suffixes__, tolerance__)
|
||||
|
||||
#define FBSnapshotVerifyLayer(layer__, identifier__) \
|
||||
FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
|
||||
|
||||
|
||||
#define FBSnapshotVerifyViewOrLayerWithOptions(what__, viewOrLayer__, identifier__, suffixes__, tolerance__) \
|
||||
{ \
|
||||
FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0); \
|
||||
NSString *referenceImageDirectory = [self getReferenceImageDirectoryWithDefault:(@ FB_REFERENCE_IMAGE_DIR)]; \
|
||||
XCTAssertNotNil(referenceImageDirectory, @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.");\
|
||||
XCTAssertTrue((suffixes__.count > 0), @"Suffixes set cannot be empty %@", suffixes__); \
|
||||
\
|
||||
BOOL testSuccess__ = NO; \
|
||||
NSError *error__ = nil; \
|
||||
NSMutableArray *errors__ = [NSMutableArray array]; \
|
||||
\
|
||||
if (self.recordMode) { \
|
||||
\
|
||||
NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffixes__.firstObject]; \
|
||||
BOOL referenceImageSaved__ = [self compareSnapshotOf ## what__ :(viewOrLayer__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \
|
||||
if (!referenceImageSaved__) { \
|
||||
[errors__ addObject:error__]; \
|
||||
} \
|
||||
} else { \
|
||||
\
|
||||
for (NSString *suffix__ in suffixes__) { \
|
||||
NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffix__]; \
|
||||
BOOL referenceImageAvailable = [self referenceImageRecordedInDirectory:referenceImagesDirectory__ identifier:(identifier__) error:&error__]; \
|
||||
\
|
||||
if (referenceImageAvailable) { \
|
||||
BOOL comparisonSuccess__ = [self compareSnapshotOf ## what__ :(viewOrLayer__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \
|
||||
[errors__ removeAllObjects]; \
|
||||
if (comparisonSuccess__) { \
|
||||
testSuccess__ = YES; \
|
||||
break; \
|
||||
} else { \
|
||||
[errors__ addObject:error__]; \
|
||||
} \
|
||||
} else { \
|
||||
[errors__ addObject:error__]; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
XCTAssertTrue(testSuccess__, @"Snapshot comparison failed: %@", errors__.firstObject); \
|
||||
XCTAssertFalse(self.recordMode, @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!"); \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
The base class of view snapshotting tests. If you have small UI component, it's often easier to configure it in a test
|
||||
and compare an image of the view to a reference image that write lots of complex layout-code tests.
|
||||
|
||||
In order to flip the tests in your subclass to record the reference images set @c recordMode to @c YES.
|
||||
|
||||
@attention When recording, the reference image directory should be explicitly
|
||||
set, otherwise the images may be written to somewhere inside the
|
||||
simulator directory.
|
||||
|
||||
For example:
|
||||
@code
|
||||
- (void)setUp
|
||||
@@ -94,6 +127,26 @@ FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaul
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign) BOOL recordMode;
|
||||
|
||||
/**
|
||||
When @c YES appends the name of the device model and OS to the snapshot file name.
|
||||
The default value is @c NO.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic;
|
||||
|
||||
/**
|
||||
When YES, renders a snapshot of the complete view hierarchy as visible onscreen.
|
||||
There are several things that do not work if renderInContext: is used.
|
||||
- UIVisualEffect #70
|
||||
- UIAppearance #91
|
||||
- Size Classes #92
|
||||
|
||||
@attention If the view does't belong to a UIWindow, it will create one and add the view as a subview.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
|
||||
|
||||
- (void)setUp NS_REQUIRES_SUPER;
|
||||
- (void)tearDown NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
Performs the comparison or records a snapshot of the layer if recordMode is YES.
|
||||
@param layer The Layer to snapshot
|
||||
@@ -124,4 +177,24 @@ FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaul
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Checks if reference image with identifier based name exists in the reference images directory.
|
||||
@param referenceImagesDirectory The directory in which reference images are stored.
|
||||
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if reference image exists.
|
||||
*/
|
||||
- (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Returns the reference image directory.
|
||||
|
||||
Helper function used to implement the assert macros.
|
||||
|
||||
@param dir directory to use if environment variable not specified. Ignored if null or empty.
|
||||
*/
|
||||
- (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir;
|
||||
|
||||
@end
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Facebook, Inc.
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
@@ -8,15 +8,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import "FBSnapshotTestCase.h"
|
||||
|
||||
#import "FBSnapshotTestController.h"
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestCase.h>
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
|
||||
|
||||
@implementation FBSnapshotTestCase
|
||||
{
|
||||
FBSnapshotTestController *_snapshotController;
|
||||
}
|
||||
|
||||
#pragma mark - Overrides
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
@@ -40,6 +41,30 @@
|
||||
_snapshotController.recordMode = recordMode;
|
||||
}
|
||||
|
||||
- (BOOL)isDeviceAgnostic
|
||||
{
|
||||
return _snapshotController.deviceAgnostic;
|
||||
}
|
||||
|
||||
- (void)setDeviceAgnostic:(BOOL)deviceAgnostic
|
||||
{
|
||||
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||
_snapshotController.deviceAgnostic = deviceAgnostic;
|
||||
}
|
||||
|
||||
- (BOOL)usesDrawViewHierarchyInRect
|
||||
{
|
||||
return _snapshotController.usesDrawViewHierarchyInRect;
|
||||
}
|
||||
|
||||
- (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect
|
||||
{
|
||||
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||
_snapshotController.usesDrawViewHierarchyInRect = usesDrawViewHierarchyInRect;
|
||||
}
|
||||
|
||||
#pragma mark - Public API
|
||||
|
||||
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
@@ -66,8 +91,33 @@
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Private API
|
||||
- (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||
_snapshotController.referenceImagesDirectory = referenceImagesDirectory;
|
||||
UIImage *referenceImage = [_snapshotController referenceImageForSelector:self.invocation.selector
|
||||
identifier:identifier
|
||||
error:errorPtr];
|
||||
|
||||
return (referenceImage != nil);
|
||||
}
|
||||
|
||||
- (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir
|
||||
{
|
||||
NSString *envReferenceImageDirectory = [NSProcessInfo processInfo].environment[@"FB_REFERENCE_IMAGE_DIR"];
|
||||
if (envReferenceImageDirectory) {
|
||||
return envReferenceImageDirectory;
|
||||
}
|
||||
if (dir && dir.length > 0) {
|
||||
return dir;
|
||||
}
|
||||
return [[NSBundle bundleForClass:self.class].resourcePath stringByAppendingPathComponent:@"ReferenceImages"];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private API
|
||||
|
||||
- (BOOL)_compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||
|
||||
+8
@@ -30,6 +30,14 @@ BOOL FBSnapshotTestCaseIs64Bit(void);
|
||||
@returns An @c NSOrderedSet object containing strings that are appended to the reference images directory.
|
||||
*/
|
||||
NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void);
|
||||
|
||||
/**
|
||||
Returns a fully «normalized» file name.
|
||||
Strips punctuation and spaces and replaces them with @c _. Also appends the device model, running OS and screen size to the file name.
|
||||
|
||||
@returns An @c NSString object containing the passed @c fileName with the device model, OS and screen size appended at the end.
|
||||
*/
|
||||
NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+18
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
BOOL FBSnapshotTestCaseIs64Bit(void)
|
||||
{
|
||||
@@ -29,3 +30,20 @@ NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void)
|
||||
}
|
||||
return [suffixesSet copy];
|
||||
}
|
||||
|
||||
NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName)
|
||||
{
|
||||
UIDevice *device = [UIDevice currentDevice];
|
||||
CGSize screenSize = [[UIApplication sharedApplication] keyWindow].bounds.size;
|
||||
NSString *os = device.systemVersion;
|
||||
|
||||
fileName = [NSString stringWithFormat:@"%@_%@%@_%.0fx%.0f", fileName, device.model, os, screenSize.width, screenSize.height];
|
||||
|
||||
NSMutableCharacterSet *invalidCharacters = [NSMutableCharacterSet new];
|
||||
[invalidCharacters formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
[invalidCharacters formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
|
||||
NSArray *validComponents = [fileName componentsSeparatedByCharactersInSet:invalidCharacters];
|
||||
fileName = [validComponents componentsJoinedByString:@"_"];
|
||||
|
||||
return fileName;
|
||||
}
|
||||
+18
-21
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Facebook, Inc.
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
@@ -37,9 +37,25 @@ extern NSString *const FBReferenceImageFilePathKey;
|
||||
|
||||
/**
|
||||
Record snapshots.
|
||||
**/
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign) BOOL recordMode;
|
||||
|
||||
/**
|
||||
When @c YES appends the name of the device model and OS to the snapshot file name.
|
||||
The default value is @c NO.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic;
|
||||
|
||||
/**
|
||||
Uses drawViewHierarchyInRect:afterScreenUpdates: to draw the image instead of renderInContext:
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
|
||||
|
||||
/**
|
||||
The directory in which referfence images are stored.
|
||||
*/
|
||||
@property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory;
|
||||
|
||||
/**
|
||||
@param testClass The subclass of FBSnapshotTestCase that is using this controller.
|
||||
@returns An instance of FBSnapshotTestController.
|
||||
@@ -53,7 +69,6 @@ extern NSString *const FBReferenceImageFilePathKey;
|
||||
*/
|
||||
- (instancetype)initWithTestName:(NSString *)testName;
|
||||
|
||||
|
||||
/**
|
||||
Performs the comparison of the layer.
|
||||
@param layer The Layer to snapshot.
|
||||
@@ -95,12 +110,6 @@ extern NSString *const FBReferenceImageFilePathKey;
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
|
||||
/**
|
||||
The directory in which referfence images are stored.
|
||||
*/
|
||||
@property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory;
|
||||
|
||||
/**
|
||||
Loads a reference image.
|
||||
@param selector The test method being run.
|
||||
@@ -112,18 +121,6 @@ extern NSString *const FBReferenceImageFilePathKey;
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Saves a reference image.
|
||||
@param selector The test method being run.
|
||||
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
|
||||
@param errorPtr An error, if this methods returns NO, the error will be something useful.
|
||||
@returns An image.
|
||||
*/
|
||||
- (BOOL)saveReferenceImage:(UIImage *)image
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Performs a pixel-by-pixel comparison of the two images with an allowable margin of error.
|
||||
@param referenceImage The reference (correct) image.
|
||||
|
||||
+137
-165
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Facebook, Inc.
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
@@ -8,25 +8,31 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#import "FBSnapshotTestController.h"
|
||||
|
||||
#import "UIImage+Compare.h"
|
||||
#import "UIImage+Diff.h"
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
|
||||
#import <FBSnapshotTestCase/UIImage+Compare.h>
|
||||
#import <FBSnapshotTestCase/UIImage+Diff.h>
|
||||
#import <FBSnapshotTestCase/UIImage+Snapshot.h>
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NSString *const FBSnapshotTestControllerErrorDomain = @"FBSnapshotTestControllerErrorDomain";
|
||||
|
||||
NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey";
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
|
||||
FBTestSnapshotFileNameTypeReference,
|
||||
FBTestSnapshotFileNameTypeFailedReference,
|
||||
FBTestSnapshotFileNameTypeFailedTest,
|
||||
FBTestSnapshotFileNameTypeFailedTestDiff,
|
||||
};
|
||||
|
||||
@implementation FBSnapshotTestController
|
||||
{
|
||||
NSString *_testName;
|
||||
NSFileManager *_fileManager;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Lifecycle
|
||||
#pragma mark - Initializers
|
||||
|
||||
- (instancetype)initWithTestClass:(Class)testClass;
|
||||
{
|
||||
@@ -35,23 +41,60 @@ NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey";
|
||||
|
||||
- (instancetype)initWithTestName:(NSString *)testName
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
if (self = [super init]) {
|
||||
_testName = [testName copy];
|
||||
_deviceAgnostic = NO;
|
||||
|
||||
_fileManager = [[NSFileManager alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Properties
|
||||
#pragma mark - Overrides
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"%@ %@", [super description], _referenceImagesDirectory];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Public API
|
||||
#pragma mark - Public API
|
||||
|
||||
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
return [self compareSnapshotOfViewOrLayer:layer
|
||||
selector:selector
|
||||
identifier:identifier
|
||||
tolerance:0
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
return [self compareSnapshotOfViewOrLayer:view
|
||||
selector:selector
|
||||
identifier:identifier
|
||||
tolerance:0
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
if (self.recordMode) {
|
||||
return [self _recordSnapshotOfViewOrLayer:viewOrLayer selector:selector identifier:identifier error:errorPtr];
|
||||
} else {
|
||||
return [self _performPixelComparisonWithViewOrLayer:viewOrLayer selector:selector identifier:identifier tolerance:tolerance error:errorPtr];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage *)referenceImageForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
@@ -78,42 +121,33 @@ NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey";
|
||||
return image;
|
||||
}
|
||||
|
||||
- (BOOL)saveReferenceImage:(UIImage *)image
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
|
||||
toImage:(UIImage *)image
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
BOOL didWrite = NO;
|
||||
if (nil != image) {
|
||||
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
|
||||
NSData *pngData = UIImagePNGRepresentation(image);
|
||||
if (nil != pngData) {
|
||||
NSError *creationError = nil;
|
||||
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:&creationError];
|
||||
if (!didCreateDir) {
|
||||
if (NULL != errorPtr) {
|
||||
*errorPtr = creationError;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
didWrite = [pngData writeToFile:filePath options:NSDataWritingAtomic error:errorPtr];
|
||||
if (didWrite) {
|
||||
NSLog(@"Reference image save at: %@", filePath);
|
||||
}
|
||||
} else {
|
||||
if (nil != errorPtr) {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodePNGCreationFailed
|
||||
userInfo:@{
|
||||
FBReferenceImageFilePathKey: filePath,
|
||||
}];
|
||||
}
|
||||
if (CGSizeEqualToSize(referenceImage.size, image.size)) {
|
||||
BOOL imagesEqual = [referenceImage fb_compareWithImage:image tolerance:tolerance];
|
||||
if (NULL != errorPtr) {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodeImagesDifferent
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"Images different",
|
||||
}];
|
||||
}
|
||||
return imagesEqual;
|
||||
}
|
||||
return didWrite;
|
||||
if (NULL != errorPtr) {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodeImagesDifferentSizes
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"Images different sizes",
|
||||
NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"referenceImage:%@, image:%@",
|
||||
NSStringFromCGSize(referenceImage.size),
|
||||
NSStringFromCGSize(image.size)],
|
||||
}];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
|
||||
@@ -157,7 +191,7 @@ NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey";
|
||||
identifier:identifier
|
||||
fileNameType:FBTestSnapshotFileNameTypeFailedTestDiff];
|
||||
|
||||
UIImage *diffImage = [referenceImage diffWithImage:testImage];
|
||||
UIImage *diffImage = [referenceImage fb_diffWithImage:testImage];
|
||||
NSData *diffImageData = UIImagePNGRepresentation(diffImage);
|
||||
|
||||
if (![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) {
|
||||
@@ -170,44 +204,7 @@ NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey";
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
|
||||
toImage:(UIImage *)image
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr {
|
||||
if (CGSizeEqualToSize(referenceImage.size, image.size)) {
|
||||
|
||||
BOOL imagesEqual = [referenceImage compareWithImage:image tolerance:tolerance];
|
||||
if (NULL != errorPtr) {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodeImagesDifferent
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"Images different",
|
||||
}];
|
||||
}
|
||||
return imagesEqual;
|
||||
}
|
||||
if (NULL != errorPtr) {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodeImagesDifferentSizes
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"Images different sizes",
|
||||
NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"referenceImage:%@, image:%@",
|
||||
NSStringFromCGSize(referenceImage.size),
|
||||
NSStringFromCGSize(image.size)],
|
||||
}];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Private API
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
|
||||
FBTestSnapshotFileNameTypeReference,
|
||||
FBTestSnapshotFileNameTypeFailedReference,
|
||||
FBTestSnapshotFileNameTypeFailedTest,
|
||||
FBTestSnapshotFileNameTypeFailedTestDiff,
|
||||
};
|
||||
#pragma mark - Private API
|
||||
|
||||
- (NSString *)_fileNameForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
@@ -232,6 +229,11 @@ typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
|
||||
if (0 < identifier.length) {
|
||||
fileName = [fileName stringByAppendingFormat:@"_%@", identifier];
|
||||
}
|
||||
|
||||
if (self.isDeviceAgnostic) {
|
||||
fileName = FBDeviceAgnosticNormalizedFileName(fileName);
|
||||
}
|
||||
|
||||
if ([[UIScreen mainScreen] scale] > 1) {
|
||||
fileName = [fileName stringByAppendingFormat:@"@%.fx", [[UIScreen mainScreen] scale]];
|
||||
}
|
||||
@@ -239,7 +241,8 @@ typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
- (NSString *)_referenceFilePathForSelector:(SEL)selector identifier:(NSString *)identifier
|
||||
- (NSString *)_referenceFilePathForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
{
|
||||
NSString *fileName = [self _fileNameForSelector:selector
|
||||
identifier:identifier
|
||||
@@ -265,46 +268,6 @@ typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
return [self compareSnapshotOfViewOrLayer:layer
|
||||
selector:selector
|
||||
identifier:identifier
|
||||
tolerance:0
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
return [self compareSnapshotOfViewOrLayer:view
|
||||
selector:selector
|
||||
identifier:identifier
|
||||
tolerance:0
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
if (self.recordMode) {
|
||||
return [self _recordSnapshotOfViewOrLayer:viewOrLayer selector:selector identifier:identifier error:errorPtr];
|
||||
} else {
|
||||
return [self _performPixelComparisonWithViewOrLayer:viewOrLayer selector:selector identifier:identifier tolerance:tolerance error:errorPtr];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Private API
|
||||
|
||||
- (BOOL)_performPixelComparisonWithViewOrLayer:(id)viewOrLayer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
@@ -313,7 +276,7 @@ typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
|
||||
{
|
||||
UIImage *referenceImage = [self referenceImageForSelector:selector identifier:identifier error:errorPtr];
|
||||
if (nil != referenceImage) {
|
||||
UIImage *snapshot = [self _snapshotViewOrLayer:viewOrLayer];
|
||||
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
|
||||
BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr];
|
||||
if (!imagesSame) {
|
||||
[self saveFailedReferenceImage:referenceImage
|
||||
@@ -332,53 +295,62 @@ typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
UIImage *snapshot = [self _snapshotViewOrLayer:viewOrLayer];
|
||||
return [self saveReferenceImage:snapshot selector:selector identifier:identifier error:errorPtr];
|
||||
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
|
||||
return [self _saveReferenceImage:snapshot selector:selector identifier:identifier error:errorPtr];
|
||||
}
|
||||
|
||||
- (UIImage *)_snapshotViewOrLayer:(id)viewOrLayer
|
||||
- (BOOL)_saveReferenceImage:(UIImage *)image
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
BOOL didWrite = NO;
|
||||
if (nil != image) {
|
||||
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
|
||||
NSData *pngData = UIImagePNGRepresentation(image);
|
||||
if (nil != pngData) {
|
||||
NSError *creationError = nil;
|
||||
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:&creationError];
|
||||
if (!didCreateDir) {
|
||||
if (NULL != errorPtr) {
|
||||
*errorPtr = creationError;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
didWrite = [pngData writeToFile:filePath options:NSDataWritingAtomic error:errorPtr];
|
||||
if (didWrite) {
|
||||
NSLog(@"Reference image save at: %@", filePath);
|
||||
}
|
||||
} else {
|
||||
if (nil != errorPtr) {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodePNGCreationFailed
|
||||
userInfo:@{
|
||||
FBReferenceImageFilePathKey: filePath,
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
return didWrite;
|
||||
}
|
||||
|
||||
- (UIImage *)_imageForViewOrLayer:(id)viewOrLayer
|
||||
{
|
||||
CALayer *layer = nil;
|
||||
|
||||
if ([viewOrLayer isKindOfClass:[UIView class]]) {
|
||||
return [self _renderView:viewOrLayer];
|
||||
if (_usesDrawViewHierarchyInRect) {
|
||||
return [UIImage fb_imageForView:viewOrLayer];
|
||||
} else {
|
||||
return [UIImage fb_imageForViewLayer:viewOrLayer];
|
||||
}
|
||||
} else if ([viewOrLayer isKindOfClass:[CALayer class]]) {
|
||||
layer = (CALayer *)viewOrLayer;
|
||||
[layer layoutIfNeeded];
|
||||
return [self _renderLayer:layer];
|
||||
return [UIImage fb_imageForLayer:viewOrLayer];
|
||||
} else {
|
||||
[NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (UIImage *)_renderLayer:(CALayer *)layer
|
||||
{
|
||||
CGRect bounds = layer.bounds;
|
||||
|
||||
NSAssert1(CGRectGetWidth(bounds), @"Zero width for layer %@", layer);
|
||||
NSAssert1(CGRectGetHeight(bounds), @"Zero height for layer %@", layer);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
NSAssert1(context, @"Could not generate context for layer %@", layer);
|
||||
|
||||
CGContextSaveGState(context);
|
||||
{
|
||||
[layer renderInContext:context];
|
||||
}
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
- (UIImage *)_renderView:(UIView *)view
|
||||
{
|
||||
[view layoutIfNeeded];
|
||||
return [self _renderLayer:view.layer];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -10,40 +10,48 @@
|
||||
|
||||
public extension FBSnapshotTestCase {
|
||||
public func FBSnapshotVerifyView(view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) {
|
||||
let envReferenceImageDirectory = NSProcessInfo.processInfo().environment["FB_REFERENCE_IMAGE_DIR"] as? String
|
||||
var error: NSError?
|
||||
|
||||
if let envReferenceImageDirectory = envReferenceImageDirectory {
|
||||
for suffix in suffixes {
|
||||
let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
|
||||
let comparisonSuccess = compareSnapshotOfView(view, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0, error: &error)
|
||||
if comparisonSuccess || recordMode {
|
||||
break
|
||||
}
|
||||
|
||||
assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line)
|
||||
assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
|
||||
}
|
||||
} else {
|
||||
assert(false, message: "Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.", file: file, line: line)
|
||||
}
|
||||
FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes)
|
||||
}
|
||||
|
||||
public func FBSnapshotVerifyLayer(layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) {
|
||||
let envReferenceImageDirectory = NSProcessInfo.processInfo().environment["FB_REFERENCE_IMAGE_DIR"] as? String
|
||||
FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes)
|
||||
}
|
||||
|
||||
private func FBSnapshotVerifyViewOrLayer(viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) {
|
||||
let envReferenceImageDirectory = self.getReferenceImageDirectoryWithDefault(FB_REFERENCE_IMAGE_DIR)
|
||||
var error: NSError?
|
||||
var comparisonSuccess = false
|
||||
|
||||
if let envReferenceImageDirectory = envReferenceImageDirectory {
|
||||
for suffix in suffixes {
|
||||
let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
|
||||
comparisonSuccess = compareSnapshotOfLayer(layer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0, error: &error)
|
||||
if viewOrLayer.isKindOfClass(UIView) {
|
||||
do {
|
||||
try compareSnapshotOfView(viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0)
|
||||
comparisonSuccess = true
|
||||
} catch let error1 as NSError {
|
||||
error = error1
|
||||
comparisonSuccess = false
|
||||
}
|
||||
} else if viewOrLayer.isKindOfClass(CALayer) {
|
||||
do {
|
||||
try compareSnapshotOfLayer(viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0)
|
||||
comparisonSuccess = true
|
||||
} catch let error1 as NSError {
|
||||
error = error1
|
||||
comparisonSuccess = false
|
||||
}
|
||||
} else {
|
||||
assertionFailure("Only UIView and CALayer classes can be snapshotted")
|
||||
}
|
||||
|
||||
assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
|
||||
|
||||
if comparisonSuccess || recordMode {
|
||||
break
|
||||
}
|
||||
|
||||
assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line)
|
||||
assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
|
||||
}
|
||||
} else {
|
||||
XCTFail("Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.")
|
||||
|
||||
+3
-3
@@ -44,9 +44,7 @@ Installation with CocoaPods
|
||||
|
||||
Replace "Tests" with the name of your test project.
|
||||
|
||||
2. Define `FB_REFERENCE_IMAGE_DIR` in your scheme. This should
|
||||
point to the directory where you want reference images to be stored. At Facebook,
|
||||
we normally use this:
|
||||
2. There are [three ways](https://github.com/facebook/ios-snapshot-test-case/blob/master/FBSnapshotTestCase/FBSnapshotTestCase.h#L19-L29) of setting reference image directories, the recommended one is to define `FB_REFERENCE_IMAGE_DIR` in your scheme. This should point to the directory where you want reference images to be stored. At Facebook, we normally use this:
|
||||
|
||||
|Name|Value|
|
||||
|:---|:----|
|
||||
@@ -75,6 +73,8 @@ Features
|
||||
- Supply an optional "identifier" if you want to perform multiple snapshots
|
||||
in a single test method.
|
||||
- Support for `CALayer` via `FBSnapshotVerifyLayer`.
|
||||
- `usesDrawViewHierarchyInRect` to handle cases like `UIVisualEffect`, `UIAppearance` and Size Classes.
|
||||
- `isDeviceAgnostic` to allow appending the device model (`iPhone`, `iPad`, `iPod Touch`, etc), OS version and screen size to the images (allowing to have multiple tests for the same «snapshot» for different `OS`s and devices).
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
Reference in new issue
Block a user