From 5cb90002e264e088939467ba60d0a60e6f43b756 Mon Sep 17 00:00:00 2001 From: Matteo Mecucci Date: Tue, 7 Jun 2016 15:41:36 +0200 Subject: [PATCH 1/2] Add support for using a scale different from the main screen's one. --- .../Categories/UIImage+Compare.m | 6 ++--- FBSnapshotTestCase/Categories/UIImage+Diff.m | 2 +- .../Categories/UIImage+Snapshot.h | 6 ++--- .../Categories/UIImage+Snapshot.m | 12 ++++----- FBSnapshotTestCase/FBSnapshotTestCase.h | 5 ++++ FBSnapshotTestCase/FBSnapshotTestCase.m | 12 ++++++++- FBSnapshotTestCase/FBSnapshotTestController.h | 5 ++++ FBSnapshotTestCase/FBSnapshotTestController.m | 26 ++++++++++++------- 8 files changed, 51 insertions(+), 23 deletions(-) diff --git a/FBSnapshotTestCase/Categories/UIImage+Compare.m b/FBSnapshotTestCase/Categories/UIImage+Compare.m index c997f57..a2b20b7 100644 --- a/FBSnapshotTestCase/Categories/UIImage+Compare.m +++ b/FBSnapshotTestCase/Categories/UIImage+Compare.m @@ -46,11 +46,11 @@ @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)); - + + NSAssert(CGSizeEqualToSize(referenceImageSize, imageSize), @"Images must be same size."); + // 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; diff --git a/FBSnapshotTestCase/Categories/UIImage+Diff.m b/FBSnapshotTestCase/Categories/UIImage+Diff.m index ebb72fe..c72ba3b 100644 --- a/FBSnapshotTestCase/Categories/UIImage+Diff.m +++ b/FBSnapshotTestCase/Categories/UIImage+Diff.m @@ -38,7 +38,7 @@ - (UIImage *)fb_diffWithImage:(UIImage *)image return nil; } CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height)); - UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0); + UIGraphicsBeginImageContextWithOptions(imageSize, YES, self.scale); CGContextRef context = UIGraphicsGetCurrentContext(); [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)]; CGContextSetAlpha(context, 0.5); diff --git a/FBSnapshotTestCase/Categories/UIImage+Snapshot.h b/FBSnapshotTestCase/Categories/UIImage+Snapshot.h index b0d5b26..e345b06 100644 --- a/FBSnapshotTestCase/Categories/UIImage+Snapshot.h +++ b/FBSnapshotTestCase/Categories/UIImage+Snapshot.h @@ -13,12 +13,12 @@ @interface UIImage (Snapshot) /// Uses renderInContext: to get a snapshot of the layer. -+ (UIImage *)fb_imageForLayer:(CALayer *)layer; ++ (UIImage *)fb_imageForLayer:(CALayer *)layer scale:(CGFloat)scale; /// Uses renderInContext: to get a snapshot of the view layer. -+ (UIImage *)fb_imageForViewLayer:(UIView *)view; ++ (UIImage *)fb_imageForViewLayer:(UIView *)view scale:(CGFloat)scale; /// Uses drawViewHierarchyInRect: to get a snapshot of the view and adds the view into a window if needed. -+ (UIImage *)fb_imageForView:(UIView *)view; ++ (UIImage *)fb_imageForView:(UIView *)view scale:(CGFloat)scale; @end diff --git a/FBSnapshotTestCase/Categories/UIImage+Snapshot.m b/FBSnapshotTestCase/Categories/UIImage+Snapshot.m index 968091b..8a912d2 100644 --- a/FBSnapshotTestCase/Categories/UIImage+Snapshot.m +++ b/FBSnapshotTestCase/Categories/UIImage+Snapshot.m @@ -13,13 +13,13 @@ @implementation UIImage (Snapshot) -+ (UIImage *)fb_imageForLayer:(CALayer *)layer ++ (UIImage *)fb_imageForLayer:(CALayer *)layer scale:(CGFloat)scale { 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); + UIGraphicsBeginImageContextWithOptions(bounds.size, NO, scale); CGContextRef context = UIGraphicsGetCurrentContext(); NSAssert1(context, @"Could not generate context for layer %@", layer); CGContextSaveGState(context); @@ -32,13 +32,13 @@ + (UIImage *)fb_imageForLayer:(CALayer *)layer return snapshot; } -+ (UIImage *)fb_imageForViewLayer:(UIView *)view ++ (UIImage *)fb_imageForViewLayer:(UIView *)view scale:(CGFloat)scale { [view layoutIfNeeded]; - return [self fb_imageForLayer:view.layer]; + return [self fb_imageForLayer:view.layer scale:scale]; } -+ (UIImage *)fb_imageForView:(UIView *)view ++ (UIImage *)fb_imageForView:(UIView *)view scale:(CGFloat)scale { CGRect bounds = view.bounds; NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view); @@ -56,7 +56,7 @@ + (UIImage *)fb_imageForView:(UIView *)view removeFromSuperview = YES; } - UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0); + UIGraphicsBeginImageContextWithOptions(bounds.size, NO, scale); [view layoutIfNeeded]; [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES]; diff --git a/FBSnapshotTestCase/FBSnapshotTestCase.h b/FBSnapshotTestCase/FBSnapshotTestCase.h index 159a724..533fac8 100644 --- a/FBSnapshotTestCase/FBSnapshotTestCase.h +++ b/FBSnapshotTestCase/FBSnapshotTestCase.h @@ -145,6 +145,11 @@ */ @property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect; +/** + When set to values different than 0, it uses a specific scale for generating and comparing images rather than using the main screen's default scale. + */ +@property (readwrite, nonatomic, assign) NSUInteger manualScale; + - (void)setUp NS_REQUIRES_SUPER; - (void)tearDown NS_REQUIRES_SUPER; diff --git a/FBSnapshotTestCase/FBSnapshotTestCase.m b/FBSnapshotTestCase/FBSnapshotTestCase.m index 3ee351f..d25126c 100644 --- a/FBSnapshotTestCase/FBSnapshotTestCase.m +++ b/FBSnapshotTestCase/FBSnapshotTestCase.m @@ -63,6 +63,17 @@ - (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect _snapshotController.usesDrawViewHierarchyInRect = usesDrawViewHierarchyInRect; } +- (void)setManualScale:(NSUInteger)manualScale +{ + NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__); + _snapshotController.manualScale = manualScale; +} + +- (NSUInteger)manualScale +{ + return _snapshotController.manualScale; +} + #pragma mark - Public API - (BOOL)compareSnapshotOfLayer:(CALayer *)layer @@ -116,7 +127,6 @@ - (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir return [[NSBundle bundleForClass:self.class].resourcePath stringByAppendingPathComponent:@"ReferenceImages"]; } - #pragma mark - Private API - (BOOL)_compareSnapshotOfViewOrLayer:(id)viewOrLayer diff --git a/FBSnapshotTestCase/FBSnapshotTestController.h b/FBSnapshotTestCase/FBSnapshotTestController.h index a0285ad..2dca8d3 100644 --- a/FBSnapshotTestCase/FBSnapshotTestController.h +++ b/FBSnapshotTestCase/FBSnapshotTestController.h @@ -71,6 +71,11 @@ extern NSString *const FBDiffedImageKey; */ @property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory; +/** + When set to values different than 0, it uses a specific scale for generating and comparing images rather than using the main screen's default scale. + */ +@property (readwrite, nonatomic, assign) NSUInteger manualScale; + /** @param testClass The subclass of FBSnapshotTestCase that is using this controller. @returns An instance of FBSnapshotTestController. diff --git a/FBSnapshotTestCase/FBSnapshotTestController.m b/FBSnapshotTestCase/FBSnapshotTestController.m index 74c5a0a..02f320d 100644 --- a/FBSnapshotTestCase/FBSnapshotTestController.m +++ b/FBSnapshotTestCase/FBSnapshotTestController.m @@ -105,7 +105,8 @@ - (UIImage *)referenceImageForSelector:(SEL)selector { NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier]; UIImage *image = [UIImage imageWithContentsOfFile:filePath]; - if (nil == image && NULL != errorPtr) { + CGFloat scale = (self.manualScale > 0 ?: [[UIScreen mainScreen] scale]); + if ((nil == image && NULL != errorPtr) || (image && image.scale != scale)) { BOOL exists = [_fileManager fileExistsAtPath:filePath]; if (!exists) { *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain @@ -129,7 +130,13 @@ - (BOOL)compareReferenceImage:(UIImage *)referenceImage tolerance:(CGFloat)tolerance error:(NSError **)errorPtr { - BOOL sameImageDimensions = CGSizeEqualToSize(referenceImage.size, image.size); + CGSize referenceImagePixelSize = referenceImage.size; + referenceImagePixelSize = CGSizeMake(referenceImagePixelSize.width * referenceImage.scale, + referenceImagePixelSize.height * referenceImage.scale); + CGSize imagePixelSize = image.size; + imagePixelSize = CGSizeMake(imagePixelSize.width * image.scale, + imagePixelSize.height * image.scale); + BOOL sameImageDimensions = CGSizeEqualToSize(referenceImagePixelSize, imagePixelSize); if (sameImageDimensions && [referenceImage fb_compareWithImage:image tolerance:tolerance]) { return YES; } @@ -137,7 +144,7 @@ - (BOOL)compareReferenceImage:(UIImage *)referenceImage if (NULL != errorPtr) { NSString *errorDescription = sameImageDimensions ? @"Images different" : @"Images different sizes"; NSString *errorReason = sameImageDimensions ? [NSString stringWithFormat:@"image pixels differed by more than %.2f%% from the reference image", tolerance * 100] - : [NSString stringWithFormat:@"referenceImage:%@, image:%@", NSStringFromCGSize(referenceImage.size), NSStringFromCGSize(image.size)]; + : [NSString stringWithFormat:@"referenceImage:%@, image:%@", NSStringFromCGSize(referenceImagePixelSize), NSStringFromCGSize(imagePixelSize)]; FBSnapshotTestControllerErrorCode errorCode = sameImageDimensions ? FBSnapshotTestControllerErrorCodeImagesDifferent : FBSnapshotTestControllerErrorCodeImagesDifferentSizes; *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain @@ -236,9 +243,10 @@ - (NSString *)_fileNameForSelector:(SEL)selector if (self.isDeviceAgnostic) { fileName = FBDeviceAgnosticNormalizedFileName(fileName); } - - if ([[UIScreen mainScreen] scale] > 1) { - fileName = [fileName stringByAppendingFormat:@"@%.fx", [[UIScreen mainScreen] scale]]; + + CGFloat scale = (self.manualScale > 0 ?: [[UIScreen mainScreen] scale]); + if (1 < scale) { + fileName = [fileName stringByAppendingFormat:@"@%.fx", scale]; } fileName = [fileName stringByAppendingPathExtension:@"png"]; return fileName; @@ -343,12 +351,12 @@ - (UIImage *)_imageForViewOrLayer:(id)viewOrLayer { if ([viewOrLayer isKindOfClass:[UIView class]]) { if (_usesDrawViewHierarchyInRect) { - return [UIImage fb_imageForView:viewOrLayer]; + return [UIImage fb_imageForView:viewOrLayer scale:self.manualScale]; } else { - return [UIImage fb_imageForViewLayer:viewOrLayer]; + return [UIImage fb_imageForViewLayer:viewOrLayer scale:self.manualScale]; } } else if ([viewOrLayer isKindOfClass:[CALayer class]]) { - return [UIImage fb_imageForLayer:viewOrLayer]; + return [UIImage fb_imageForLayer:viewOrLayer scale:self.manualScale]; } else { [NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer]; } From e9331bd73d7122a095476a63c51c275c6f3bc72a Mon Sep 17 00:00:00 2001 From: Borys Zhdanov Date: Mon, 30 Oct 2017 15:57:26 +0100 Subject: [PATCH 2/2] Fixing manualScale bug * Manual scale wasn't propagated properly to image capture and reference image selection methods --- FBSnapshotTestCase/FBSnapshotTestController.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FBSnapshotTestCase/FBSnapshotTestController.m b/FBSnapshotTestCase/FBSnapshotTestController.m index 02f320d..acdc46e 100644 --- a/FBSnapshotTestCase/FBSnapshotTestController.m +++ b/FBSnapshotTestCase/FBSnapshotTestController.m @@ -105,7 +105,7 @@ - (UIImage *)referenceImageForSelector:(SEL)selector { NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier]; UIImage *image = [UIImage imageWithContentsOfFile:filePath]; - CGFloat scale = (self.manualScale > 0 ?: [[UIScreen mainScreen] scale]); + CGFloat scale = (self.manualScale > 0 ? self.manualScale : [[UIScreen mainScreen] scale]); if ((nil == image && NULL != errorPtr) || (image && image.scale != scale)) { BOOL exists = [_fileManager fileExistsAtPath:filePath]; if (!exists) { @@ -244,7 +244,7 @@ - (NSString *)_fileNameForSelector:(SEL)selector fileName = FBDeviceAgnosticNormalizedFileName(fileName); } - CGFloat scale = (self.manualScale > 0 ?: [[UIScreen mainScreen] scale]); + CGFloat scale = (self.manualScale > 0 ? self.manualScale : [[UIScreen mainScreen] scale]); if (1 < scale) { fileName = [fileName stringByAppendingFormat:@"@%.fx", scale]; }