Project import
diff --git a/FBSnapshotTestCase.modulemap b/FBSnapshotTestCase.modulemap
new file mode 100644
index 0000000..733e78b
--- /dev/null
+++ b/FBSnapshotTestCase.modulemap
@@ -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"
+}
+
diff --git a/FBSnapshotTestCase/Categories/UIImage+Compare.h b/FBSnapshotTestCase/Categories/UIImage+Compare.h
new file mode 100644
index 0000000..9091d62
--- /dev/null
+++ b/FBSnapshotTestCase/Categories/UIImage+Compare.h
@@ -0,0 +1,37 @@
+//
+//  Created by Gabriel Handford on 3/1/09.
+//  Copyright 2009-2013. All rights reserved.
+//  Created by John Boiles on 10/20/11.
+//  Copyright (c) 2011. All rights reserved
+//  Modified by Felix Schulze on 2/11/13.
+//  Copyright 2013. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface UIImage (Compare)
+
+- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance;
+
+@end
diff --git a/FBSnapshotTestCase/Categories/UIImage+Compare.m b/FBSnapshotTestCase/Categories/UIImage+Compare.m
new file mode 100644
index 0000000..c997f57
--- /dev/null
+++ b/FBSnapshotTestCase/Categories/UIImage+Compare.m
@@ -0,0 +1,134 @@
+//
+//  Created by Gabriel Handford on 3/1/09.
+//  Copyright 2009-2013. All rights reserved.
+//  Created by John Boiles on 10/20/11.
+//  Copyright (c) 2011. All rights reserved
+//  Modified by Felix Schulze on 2/11/13.
+//  Copyright 2013. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <FBSnapshotTestCase/UIImage+Compare.h>
+
+// This makes debugging much more fun
+typedef union {
+    uint32_t raw;
+    unsigned char bytes[4];
+    struct {
+        char red;
+        char green;
+        char blue;
+        char alpha;
+    } __attribute__ ((packed)) pixels;
+} FBComparePixel;
+
+@implementation UIImage (Compare)
+
+- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
+{
+  NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size.");
+  
+  CGSize referenceImageSize = CGSizeMake(CGImageGetWidth(self.CGImage), CGImageGetHeight(self.CGImage));
+  CGSize imageSize = CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));
+    
+  // The images have the equal size, so we could use the smallest amount of bytes because of byte padding
+  size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(self.CGImage), CGImageGetBytesPerRow(image.CGImage));
+  size_t referenceImageSizeBytes = referenceImageSize.height * minBytesPerRow;
+  void *referenceImagePixels = calloc(1, referenceImageSizeBytes);
+  void *imagePixels = calloc(1, referenceImageSizeBytes);
+
+  if (!referenceImagePixels || !imagePixels) {
+    free(referenceImagePixels);
+    free(imagePixels);
+    return NO;
+  }
+  
+  CGContextRef referenceImageContext = CGBitmapContextCreate(referenceImagePixels,
+                                                             referenceImageSize.width,
+                                                             referenceImageSize.height,
+                                                             CGImageGetBitsPerComponent(self.CGImage),
+                                                             minBytesPerRow,
+                                                             CGImageGetColorSpace(self.CGImage),
+                                                             (CGBitmapInfo)kCGImageAlphaPremultipliedLast
+                                                             );
+  CGContextRef imageContext = CGBitmapContextCreate(imagePixels,
+                                                    imageSize.width,
+                                                    imageSize.height,
+                                                    CGImageGetBitsPerComponent(image.CGImage),
+                                                    minBytesPerRow,
+                                                    CGImageGetColorSpace(image.CGImage),
+                                                    (CGBitmapInfo)kCGImageAlphaPremultipliedLast
+                                                    );
+
+  if (!referenceImageContext || !imageContext) {
+    CGContextRelease(referenceImageContext);
+    CGContextRelease(imageContext);
+    free(referenceImagePixels);
+    free(imagePixels);
+    return NO;
+  }
+
+  CGContextDrawImage(referenceImageContext, CGRectMake(0, 0, referenceImageSize.width, referenceImageSize.height), self.CGImage);
+  CGContextDrawImage(imageContext, CGRectMake(0, 0, imageSize.width, imageSize.height), image.CGImage);
+
+  CGContextRelease(referenceImageContext);
+  CGContextRelease(imageContext);
+
+  BOOL imageEqual = YES;
+
+  // Do a fast compare if we can
+  if (tolerance == 0) {
+    imageEqual = (memcmp(referenceImagePixels, imagePixels, referenceImageSizeBytes) == 0);
+  } else {
+    // Go through each pixel in turn and see if it is different
+    const NSInteger pixelCount = referenceImageSize.width * referenceImageSize.height;
+
+    FBComparePixel *p1 = referenceImagePixels;
+    FBComparePixel *p2 = imagePixels;
+
+    NSInteger numDiffPixels = 0;
+    for (int n = 0; n < pixelCount; ++n) {
+      // If this pixel is different, increment the pixel diff count and see
+      // if we have hit our limit.
+      if (p1->raw != p2->raw) {
+        numDiffPixels ++;
+
+        CGFloat percent = (CGFloat)numDiffPixels / pixelCount;
+        if (percent > tolerance) {
+          imageEqual = NO;
+          break;
+        }
+      }
+
+      p1++;
+      p2++;
+    }
+  }
+
+  free(referenceImagePixels);
+  free(imagePixels);
+
+  return imageEqual;
+}
+
+@end
diff --git a/FBSnapshotTestCase/Categories/UIImage+Diff.h b/FBSnapshotTestCase/Categories/UIImage+Diff.h
new file mode 100644
index 0000000..a0863f3
--- /dev/null
+++ b/FBSnapshotTestCase/Categories/UIImage+Diff.h
@@ -0,0 +1,37 @@
+//
+//  Created by Gabriel Handford on 3/1/09.
+//  Copyright 2009-2013. All rights reserved.
+//  Created by John Boiles on 10/20/11.
+//  Copyright (c) 2011. All rights reserved
+//  Modified by Felix Schulze on 2/11/13.
+//  Copyright 2013. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface UIImage (Diff)
+
+- (UIImage *)fb_diffWithImage:(UIImage *)image;
+
+@end
diff --git a/FBSnapshotTestCase/Categories/UIImage+Diff.m b/FBSnapshotTestCase/Categories/UIImage+Diff.m
new file mode 100644
index 0000000..ebb72fe
--- /dev/null
+++ b/FBSnapshotTestCase/Categories/UIImage+Diff.m
@@ -0,0 +1,56 @@
+//
+//  Created by Gabriel Handford on 3/1/09.
+//  Copyright 2009-2013. All rights reserved.
+//  Created by John Boiles on 10/20/11.
+//  Copyright (c) 2011. All rights reserved
+//  Modified by Felix Schulze on 2/11/13.
+//  Copyright 2013. All rights reserved.
+//
+//  Permission is hereby granted, free of charge, to any person
+//  obtaining a copy of this software and associated documentation
+//  files (the "Software"), to deal in the Software without
+//  restriction, including without limitation the rights to use,
+//  copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the
+//  Software is furnished to do so, subject to the following
+//  conditions:
+//
+//  The above copyright notice and this permission notice shall be
+//  included in all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+//  OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <FBSnapshotTestCase/UIImage+Diff.h>
+
+@implementation UIImage (Diff)
+
+- (UIImage *)fb_diffWithImage:(UIImage *)image
+{
+  if (!image) {
+    return nil;
+  }
+  CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height));
+  UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0);
+  CGContextRef context = UIGraphicsGetCurrentContext();
+  [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
+  CGContextSetAlpha(context, 0.5);
+  CGContextBeginTransparencyLayer(context, NULL);
+  [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
+  CGContextSetBlendMode(context, kCGBlendModeDifference);
+  CGContextSetFillColorWithColor(context,[UIColor whiteColor].CGColor);
+  CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height));
+  CGContextEndTransparencyLayer(context);
+  UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext();
+  UIGraphicsEndImageContext();
+  return returnImage;
+}
+
+@end
diff --git a/FBSnapshotTestCase/Categories/UIImage+Snapshot.h b/FBSnapshotTestCase/Categories/UIImage+Snapshot.h
new file mode 100644
index 0000000..b0d5b26
--- /dev/null
+++ b/FBSnapshotTestCase/Categories/UIImage+Snapshot.h
@@ -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
diff --git a/FBSnapshotTestCase/Categories/UIImage+Snapshot.m b/FBSnapshotTestCase/Categories/UIImage+Snapshot.m
new file mode 100644
index 0000000..c792077
--- /dev/null
+++ b/FBSnapshotTestCase/Categories/UIImage+Snapshot.m
@@ -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
diff --git a/FBSnapshotTestCase/FBSnapshotTestCase.h b/FBSnapshotTestCase/FBSnapshotTestCase.h
new file mode 100644
index 0000000..54e301e
--- /dev/null
+++ b/FBSnapshotTestCase/FBSnapshotTestCase.h
@@ -0,0 +1,200 @@
+/*
+ *  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/FBSnapshotTestCasePlatform.h>
+
+#import <QuartzCore/QuartzCore.h>
+
+#import <UIKit/UIKit.h>
+
+#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
+ @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' view
+ */
+#define FBSnapshotVerifyViewWithOptions(view__, identifier__, suffixes__, tolerance__) \
+  FBSnapshotVerifyViewOrLayerWithOptions(View, view__, identifier__, suffixes__, tolerance__)
+
+#define FBSnapshotVerifyView(view__, identifier__) \
+  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.
+ @param layer The layer to snapshot
+ @param identifier An optional identifier, used is 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
+ */
+#define FBSnapshotVerifyLayerWithOptions(layer__, identifier__, suffixes__, tolerance__) \
+  FBSnapshotVerifyViewOrLayerWithOptions(Layer, layer__, identifier__, suffixes__, tolerance__)
+
+#define FBSnapshotVerifyLayer(layer__, identifier__) \
+  FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
+
+
+#define FBSnapshotVerifyViewOrLayerWithOptions(what__, viewOrLayer__, identifier__, suffixes__, tolerance__) \
+{ \
+  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
+ {
+    [super setUp];
+    self.recordMode = YES;
+ }
+ @endcode
+ */
+@interface FBSnapshotTestCase : XCTestCase
+
+/**
+ When YES, the test macros will save reference images, rather than performing an actual test.
+ */
+@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
+ @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 tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
+ @param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
+ @returns YES if the comparison (or saving of the reference image) succeeded.
+ */
+- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
+      referenceImagesDirectory:(NSString *)referenceImagesDirectory
+                    identifier:(NSString *)identifier
+                     tolerance:(CGFloat)tolerance
+                         error:(NSError **)errorPtr;
+
+/**
+ Performs the comparison or records a snapshot of the view if recordMode is YES.
+ @param view The view to snapshot
+ @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 tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
+ @param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
+ @returns YES if the comparison (or saving of the reference image) succeeded.
+ */
+- (BOOL)compareSnapshotOfView:(UIView *)view
+     referenceImagesDirectory:(NSString *)referenceImagesDirectory
+                   identifier:(NSString *)identifier
+                    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
diff --git a/FBSnapshotTestCase/FBSnapshotTestCase.m b/FBSnapshotTestCase/FBSnapshotTestCase.m
new file mode 100644
index 0000000..3ee351f
--- /dev/null
+++ b/FBSnapshotTestCase/FBSnapshotTestCase.m
@@ -0,0 +1,136 @@
+/*
+ *  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/FBSnapshotTestCase.h>
+#import <FBSnapshotTestCase/FBSnapshotTestController.h>
+
+@implementation FBSnapshotTestCase
+{
+  FBSnapshotTestController *_snapshotController;
+}
+
+#pragma mark - Overrides
+
+- (void)setUp
+{
+  [super setUp];
+  _snapshotController = [[FBSnapshotTestController alloc] initWithTestName:NSStringFromClass([self class])];
+}
+
+- (void)tearDown
+{
+  _snapshotController = nil;
+  [super tearDown];
+}
+
+- (BOOL)recordMode
+{
+  return _snapshotController.recordMode;
+}
+
+- (void)setRecordMode:(BOOL)recordMode
+{
+  NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
+  _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
+                     tolerance:(CGFloat)tolerance
+                         error:(NSError **)errorPtr
+{
+  return [self _compareSnapshotOfViewOrLayer:layer
+                    referenceImagesDirectory:referenceImagesDirectory
+                                  identifier:identifier
+                                   tolerance:tolerance
+                                       error:errorPtr];
+}
+
+- (BOOL)compareSnapshotOfView:(UIView *)view
+     referenceImagesDirectory:(NSString *)referenceImagesDirectory
+                   identifier:(NSString *)identifier
+                    tolerance:(CGFloat)tolerance
+                        error:(NSError **)errorPtr
+{
+  return [self _compareSnapshotOfViewOrLayer:view
+                    referenceImagesDirectory:referenceImagesDirectory
+                                  identifier:identifier
+                                   tolerance:tolerance
+                                       error:errorPtr];
+}
+
+- (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
+                           identifier:(NSString *)identifier
+                            tolerance:(CGFloat)tolerance
+                                error:(NSError **)errorPtr
+{
+  _snapshotController.referenceImagesDirectory = referenceImagesDirectory;
+  return [_snapshotController compareSnapshotOfViewOrLayer:viewOrLayer
+                                                  selector:self.invocation.selector
+                                                identifier:identifier
+                                                 tolerance:tolerance
+                                                     error:errorPtr];
+}
+
+@end
diff --git a/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h b/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h
new file mode 100644
index 0000000..e04acf2
--- /dev/null
+++ b/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h
@@ -0,0 +1,44 @@
+/*
+ *  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 <Foundation/Foundation.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ Returns a Boolean value that indicates whether the snapshot test is running in 64Bit.
+ This method is a convenience for creating the suffixes set based on the architecture
+ that the test is running.
+ 
+ @returns @c YES if the test is running in 64bit, otherwise @c NO.
+ */
+BOOL FBSnapshotTestCaseIs64Bit(void);
+
+/**
+ Returns a default set of strings that is used to append a suffix based on the architectures.
+ @warning Do not modify this function, you can create your own and use it with @c FBSnapshotVerifyViewWithOptions()
+ 
+ @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
+}
+#endif
diff --git a/FBSnapshotTestCase/FBSnapshotTestCasePlatform.m b/FBSnapshotTestCase/FBSnapshotTestCasePlatform.m
new file mode 100644
index 0000000..4f6fb01
--- /dev/null
+++ b/FBSnapshotTestCase/FBSnapshotTestCasePlatform.m
@@ -0,0 +1,49 @@
+/*
+ *  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/FBSnapshotTestCasePlatform.h>
+#import <UIKit/UIKit.h>
+
+BOOL FBSnapshotTestCaseIs64Bit(void)
+{
+#if __LP64__
+  return YES;
+#else
+  return NO;
+#endif
+}
+
+NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void)
+{
+  NSMutableOrderedSet *suffixesSet = [[NSMutableOrderedSet alloc] init];
+  [suffixesSet addObject:@"_32"];
+  [suffixesSet addObject:@"_64"];
+  if (FBSnapshotTestCaseIs64Bit()) {
+    return [suffixesSet reversedOrderedSet];
+  } 
+  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;
+}
\ No newline at end of file
diff --git a/FBSnapshotTestCase/FBSnapshotTestController.h b/FBSnapshotTestCase/FBSnapshotTestController.h
new file mode 100644
index 0000000..5719aba
--- /dev/null
+++ b/FBSnapshotTestCase/FBSnapshotTestController.h
@@ -0,0 +1,151 @@
+/*
+ *  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 <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+typedef NS_ENUM(NSInteger, FBSnapshotTestControllerErrorCode) {
+  FBSnapshotTestControllerErrorCodeUnknown,
+  FBSnapshotTestControllerErrorCodeNeedsRecord,
+  FBSnapshotTestControllerErrorCodePNGCreationFailed,
+  FBSnapshotTestControllerErrorCodeImagesDifferentSizes,
+  FBSnapshotTestControllerErrorCodeImagesDifferent,
+};
+/**
+ Errors returned by the methods of FBSnapshotTestController use this domain.
+ */
+extern NSString *const FBSnapshotTestControllerErrorDomain;
+
+/**
+ Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
+ */
+extern NSString *const FBReferenceImageFilePathKey;
+
+/**
+ Provides the heavy-lifting for FBSnapshotTestCase. It loads and saves images, along with performing the actual pixel-
+ by-pixel comparison of images.
+ Instances are initialized with the test class, and directories to read and write to.
+ */
+@interface FBSnapshotTestController : NSObject
+
+/**
+ 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.
+ */
+- (instancetype)initWithTestClass:(Class)testClass;
+
+/**
+ Designated initializer.
+ @param testName The name of the tests.
+ @returns An instance of FBSnapshotTestController.
+ */
+- (instancetype)initWithTestName:(NSString *)testName;
+
+/**
+ Performs the comparison of the layer.
+ @param layer The Layer to snapshot.
+ @param selector The test method being run.
+ @param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
+ @param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
+ @returns YES if the comparison (or saving of the reference image) succeeded.
+ */
+- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
+                      selector:(SEL)selector
+                    identifier:(NSString *)identifier
+                         error:(NSError **)errorPtr;
+
+/**
+ Performs the comparison of the view.
+ @param view The view to snapshot.
+ @param selector The test method being run.
+ @param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
+ @param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
+ @returns YES if the comparison (or saving of the reference image) succeeded.
+ */
+- (BOOL)compareSnapshotOfView:(UIView *)view
+                     selector:(SEL)selector
+                   identifier:(NSString *)identifier
+                        error:(NSError **)errorPtr;
+
+/**
+ Performs the comparison of a view or layer.
+ @param view The view or layer to snapshot.
+ @param selector The test method being run.
+ @param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
+ @param tolerance The percentage of pixels that can differ and still be considered 'identical'
+ @param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
+ @returns YES if the comparison (or saving of the reference image) succeeded.
+ */
+- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
+                            selector:(SEL)selector
+                          identifier:(NSString *)identifier
+                           tolerance:(CGFloat)tolerance
+                               error:(NSError **)errorPtr;
+
+/**
+ Loads 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 nil, the error will be something useful.
+ @returns An image.
+ */
+- (UIImage *)referenceImageForSelector:(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.
+ @param image The image to test against the reference.
+ @param tolerance The percentage of pixels that can differ and still be considered 'identical'
+ @param errorPtr An error that indicates why the comparison failed if it does.
+ @returns YES if the comparison succeeded and the images are the same(ish).
+ */
+- (BOOL)compareReferenceImage:(UIImage *)referenceImage
+                      toImage:(UIImage *)image
+                    tolerance:(CGFloat)tolerance
+                        error:(NSError **)errorPtr;
+
+/**
+ Saves the reference image and the test image to `failedOutputDirectory`.
+ @param referenceImage The reference (correct) image.
+ @param testImage The image to test against the reference.
+ @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 that indicates why the comparison failed if it does.
+ @returns YES if the save succeeded.
+ */
+- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
+                       testImage:(UIImage *)testImage
+                        selector:(SEL)selector
+                      identifier:(NSString *)identifier
+                           error:(NSError **)errorPtr;
+@end
diff --git a/FBSnapshotTestCase/FBSnapshotTestController.m b/FBSnapshotTestCase/FBSnapshotTestController.m
new file mode 100644
index 0000000..4cebe10
--- /dev/null
+++ b/FBSnapshotTestCase/FBSnapshotTestController.m
@@ -0,0 +1,356 @@
+/*
+ *  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/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 - Initializers
+
+- (instancetype)initWithTestClass:(Class)testClass;
+{
+  return [self initWithTestName:NSStringFromClass(testClass)];
+}
+
+- (instancetype)initWithTestName:(NSString *)testName
+{
+  if (self = [super init]) {
+    _testName = [testName copy];
+    _deviceAgnostic = NO;
+    
+    _fileManager = [[NSFileManager alloc] init];
+  }
+  return self;
+}
+
+#pragma mark - Overrides
+
+- (NSString *)description
+{
+  return [NSString stringWithFormat:@"%@ %@", [super description], _referenceImagesDirectory];
+}
+
+#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
+                                 error:(NSError **)errorPtr
+{
+  NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
+  UIImage *image = [UIImage imageWithContentsOfFile:filePath];
+  if (nil == image && NULL != errorPtr) {
+    BOOL exists = [_fileManager fileExistsAtPath:filePath];
+    if (!exists) {
+      *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
+                                      code:FBSnapshotTestControllerErrorCodeNeedsRecord
+                                  userInfo:@{
+               FBReferenceImageFilePathKey: filePath,
+                 NSLocalizedDescriptionKey: @"Unable to load reference image.",
+          NSLocalizedFailureReasonErrorKey: @"Reference image not found. You need to run the test in record mode",
+                   }];
+    } else {
+      *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
+                                      code:FBSnapshotTestControllerErrorCodeUnknown
+                                  userInfo:nil];
+    }
+  }
+  return image;
+}
+
+- (BOOL)compareReferenceImage:(UIImage *)referenceImage
+                      toImage:(UIImage *)image
+                    tolerance:(CGFloat)tolerance
+                        error:(NSError **)errorPtr
+{
+  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;
+  }
+  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
+                       testImage:(UIImage *)testImage
+                        selector:(SEL)selector
+                      identifier:(NSString *)identifier
+                           error:(NSError **)errorPtr
+{
+  NSData *referencePNGData = UIImagePNGRepresentation(referenceImage);
+  NSData *testPNGData = UIImagePNGRepresentation(testImage);
+
+  NSString *referencePath = [self _failedFilePathForSelector:selector
+                                                  identifier:identifier
+                                                fileNameType:FBTestSnapshotFileNameTypeFailedReference];
+
+  NSError *creationError = nil;
+  BOOL didCreateDir = [_fileManager createDirectoryAtPath:[referencePath stringByDeletingLastPathComponent]
+                              withIntermediateDirectories:YES
+                                               attributes:nil
+                                                    error:&creationError];
+  if (!didCreateDir) {
+    if (NULL != errorPtr) {
+      *errorPtr = creationError;
+    }
+    return NO;
+  }
+
+  if (![referencePNGData writeToFile:referencePath options:NSDataWritingAtomic error:errorPtr]) {
+    return NO;
+  }
+
+  NSString *testPath = [self _failedFilePathForSelector:selector
+                                             identifier:identifier
+                                           fileNameType:FBTestSnapshotFileNameTypeFailedTest];
+
+  if (![testPNGData writeToFile:testPath options:NSDataWritingAtomic error:errorPtr]) {
+    return NO;
+  }
+
+  NSString *diffPath = [self _failedFilePathForSelector:selector
+                                             identifier:identifier
+                                           fileNameType:FBTestSnapshotFileNameTypeFailedTestDiff];
+
+  UIImage *diffImage = [referenceImage fb_diffWithImage:testImage];
+  NSData *diffImageData = UIImagePNGRepresentation(diffImage);
+
+  if (![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) {
+    return NO;
+  }
+
+  NSLog(@"If you have Kaleidoscope installed you can run this command to see an image diff:\n"
+        @"ksdiff \"%@\" \"%@\"", referencePath, testPath);
+
+  return YES;
+}
+
+#pragma mark - Private API
+
+- (NSString *)_fileNameForSelector:(SEL)selector
+                        identifier:(NSString *)identifier
+                      fileNameType:(FBTestSnapshotFileNameType)fileNameType
+{
+  NSString *fileName = nil;
+  switch (fileNameType) {
+    case FBTestSnapshotFileNameTypeFailedReference:
+      fileName = @"reference_";
+      break;
+    case FBTestSnapshotFileNameTypeFailedTest:
+      fileName = @"failed_";
+      break;
+    case FBTestSnapshotFileNameTypeFailedTestDiff:
+      fileName = @"diff_";
+      break;
+    default:
+      fileName = @"";
+      break;
+  }
+  fileName = [fileName stringByAppendingString:NSStringFromSelector(selector)];
+  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]];
+  }
+  fileName = [fileName stringByAppendingPathExtension:@"png"];
+  return fileName;
+}
+
+- (NSString *)_referenceFilePathForSelector:(SEL)selector
+                                 identifier:(NSString *)identifier
+{
+  NSString *fileName = [self _fileNameForSelector:selector
+                                       identifier:identifier
+                                     fileNameType:FBTestSnapshotFileNameTypeReference];
+  NSString *filePath = [_referenceImagesDirectory stringByAppendingPathComponent:_testName];
+  filePath = [filePath stringByAppendingPathComponent:fileName];
+  return filePath;
+}
+
+- (NSString *)_failedFilePathForSelector:(SEL)selector
+                              identifier:(NSString *)identifier
+                            fileNameType:(FBTestSnapshotFileNameType)fileNameType
+{
+  NSString *fileName = [self _fileNameForSelector:selector
+                                       identifier:identifier
+                                     fileNameType:fileNameType];
+  NSString *folderPath = NSTemporaryDirectory();
+  if (getenv("IMAGE_DIFF_DIR")) {
+    folderPath = @(getenv("IMAGE_DIFF_DIR"));
+  }
+  NSString *filePath = [folderPath stringByAppendingPathComponent:_testName];
+  filePath = [filePath stringByAppendingPathComponent:fileName];
+  return filePath;
+}
+
+- (BOOL)_performPixelComparisonWithViewOrLayer:(id)viewOrLayer
+                                      selector:(SEL)selector
+                                    identifier:(NSString *)identifier
+                                     tolerance:(CGFloat)tolerance
+                                         error:(NSError **)errorPtr
+{
+  UIImage *referenceImage = [self referenceImageForSelector:selector identifier:identifier error:errorPtr];
+  if (nil != referenceImage) {
+    UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
+    BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr];
+    if (!imagesSame) {
+      [self saveFailedReferenceImage:referenceImage
+                           testImage:snapshot
+                            selector:selector
+                          identifier:identifier
+                               error:errorPtr];
+    }
+    return imagesSame;
+  }
+  return NO;
+}
+
+- (BOOL)_recordSnapshotOfViewOrLayer:(id)viewOrLayer
+                            selector:(SEL)selector
+                          identifier:(NSString *)identifier
+                               error:(NSError **)errorPtr
+{
+  UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
+  return [self _saveReferenceImage:snapshot selector:selector identifier:identifier error:errorPtr];
+}
+
+- (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
+{
+  if ([viewOrLayer isKindOfClass:[UIView class]]) {
+    if (_usesDrawViewHierarchyInRect) {
+      return [UIImage fb_imageForView:viewOrLayer];
+    } else {
+      return [UIImage fb_imageForViewLayer:viewOrLayer];
+    }
+  } else if ([viewOrLayer isKindOfClass:[CALayer class]]) {
+    return [UIImage fb_imageForLayer:viewOrLayer];
+  } else {
+    [NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer];
+  }
+  return nil;
+}
+
+@end
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..2dd780c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD License
+
+For the FBSnapshotTestCase software
+
+Copyright (c) 2013, Facebook, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+  * Neither the name Facebook nor the names of its contributors may be used to
+    endorse or promote products derived from this software without specific
+    prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..bc23b83
--- /dev/null
+++ b/README.md
@@ -0,0 +1,97 @@
+FBSnapshotTestCase
+======================
+
+[![Build Status](https://travis-ci.org/facebook/ios-snapshot-test-case.svg)](https://travis-ci.org/facebook/ios-snapshot-test-case) [![Cocoa Pod Version](https://cocoapod-badges.herokuapp.com/v/FBSnapshotTestCase/badge.svg)](http://cocoadocs.org/docsets/FBSnapshotTestCase/)
+
+What it does
+------------
+
+A "snapshot test case" takes a configured `UIView` or `CALayer` and uses the
+`renderInContext:` method to get an image snapshot of its contents. It
+compares this snapshot to a "reference image" stored in your source code
+repository and fails the test if the two images don't match.
+
+Why?
+----
+
+At Facebook we write a lot of UI code. As you might imagine, each type of
+feed story is rendered using a subclass of `UIView`. There are a lot of edge
+cases that we want to handle correctly:
+
+- What if there is more text than can fit in the space available?
+- What if an image doesn't match the size of an image view?
+- What should the highlighted state look like?
+
+It's straightforward to test logic code, but less obvious how you should test
+views. You can do a lot of rectangle asserts, but these are hard to understand
+or visualize. Looking at an image diff shows you exactly what changed and how
+it will look to users.
+
+We developed `FBSnapshotTestCase` to make snapshot tests easy.
+
+Installation with CocoaPods
+---------------------------
+
+1. Add the following lines to your Podfile:
+
+     ```
+     target "Tests" do
+       pod 'FBSnapshotTestCase'
+     end
+     ```
+
+   If you support iOS 7 use `FBSnapshotTestCase/Core` instead, which doesn't contain Swift support.
+
+   Replace "Tests" with the name of your test project.
+
+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|
+|:---|:----|
+|`FB_REFERENCE_IMAGE_DIR`|`$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages`|
+
+
+![](FBSnapshotTestCaseDemo/Scheme_FB_REFERENCE_IMAGE_DIR.png)
+
+Creating a snapshot test
+------------------------
+
+1. Subclass `FBSnapshotTestCase` instead of `XCTestCase`.
+2. From within your test, use `FBSnapshotVerifyView`.
+3. Run the test once with `self.recordMode = YES;` in the test's `-setUp`
+   method. (This creates the reference images on disk.)
+4. Remove the line enabling record mode and run the test.
+
+Features
+--------
+
+- Automatically names reference images on disk according to test class and
+  selector.
+- Prints a descriptive error message to the console on failure. (Bonus:
+  failure message includes a one-line command to see an image diff if
+  you have [Kaleidoscope](http://www.kaleidoscopeapp.com) installed.)
+- 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
+-----
+
+Your unit test must be an "application test", not a "logic test." (That is, it
+must be run within the Simulator so that it has access to UIKit.) In Xcode 5
+and later new projects only offer application tests, but older projects will
+have separate targets for the two types.
+
+Authors
+-------
+
+`FBSnapshotTestCase` was written at Facebook by
+[Jonathan Dann](https://facebook.com/j.p.dann) with significant contributions by
+[Todd Krabach](https://facebook.com/toddkrabach).
+
+License
+-------
+
+`FBSnapshotTestCase` is BSD-licensed. See `LICENSE`.