Browse files

Adding some documentation, improving GHVerifyView macro, using GHUDebug

  • Loading branch information...
1 parent da68b11 commit a8732c9c60b710260a085bc735f11c3f10a514ea @johnboiles johnboiles committed Oct 27, 2011
View
15 Classes/GHTest/NSException+GHTestFailureExceptions.m
@@ -244,6 +244,21 @@ + (NSException *)ghu_failureInRaise:(NSString *)expression
return [self ghu_failureInFile:filename atLine:lineNumber reason:reason];
}
++ (NSException *)ghu_failureWithName:(NSString *)name
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ reason:(NSString *)reason {
+ NSDictionary *userInfo =
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInteger:lineNumber], GHTestLineNumberKey,
+ filename, GHTestFilenameKey,
+ nil];
+
+ return [self exceptionWithName:name
+ reason:reason
+ userInfo:userInfo];
+}
+
@end
NSString *GHComposeString(NSString *formatString, ...) {
View
4 Classes/GHTestMacros.h
@@ -1042,6 +1042,10 @@ continue; \
inFile:(NSString *)filename
atLine:(int)lineNumber
withDescription:(NSString *)formatString, ...;
++ (NSException *)ghu_failureWithName:(NSString *)name
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ reason:(NSString *)reason;
@end
// SENTE_END
View
2 Classes/GHUnit.h
@@ -29,7 +29,6 @@
#import "GHTestCase.h"
#import "GHAsyncTestCase.h"
-#import "GHViewTestCase.h"
#import "GHTestSuite.h"
#import "GHTestMacros.h"
#import "GHTestRunner.h"
@@ -45,6 +44,7 @@
#if TARGET_OS_IPHONE
#import "GHUnitIOSAppDelegate.h"
+#import "GHViewTestCase.h"
#endif
#ifdef DEBUG
View
71 Classes/GHViewTestCase.h
@@ -30,28 +30,87 @@
#import "GHTestCase.h"
#import <UIKit/UIKit.h>
-/*!
+/*!
Assert that a view has not changed. Raises exception if the view has changed or if
no image exists from previous test runs.
@param view The view to verify
*/
#define GHVerifyView(view) \
do { \
-[self verifyView:view inFilename:[NSString stringWithUTF8String:__FILE__] atLineNumber:__LINE__];\
+if (![self isKindOfClass:[GHViewTestCase class]]) \
+[[NSException ghu_failureWithName:@"GHInvalidTestException" \
+inFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+reason:@"GHVerifyView can only be called from within a GHViewTestCase class"] raise]; \
+[self verifyView:view filename:[NSString stringWithUTF8String:__FILE__] lineNumber:__LINE__]; \
} while (0)
+/*!
+ View verification test case.
-@interface GHViewTestCase : GHTestCase {
+ Supports GHVerifyView, which renders a view and compares it against a saved
+ image from a previous test run.
+
+ @interface MyViewTest : GHViewTestCase { }
+ @end
+
+ @implementation MyViewTest
+
+ - (void)testMyView {
+ MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
+ GHVerifyView(myView);
+ }
+
+ @end
+
+ */
+@interface GHViewTestCase : GHTestCase {
NSInteger imageVerifyCount_;
}
+/*!
+ Clear all test images in the documents directory
+ */
++ (void)clearTestImages;
+
+/*!
+ Save an image to the documents directory as filename
+
+ @param image Image to save
+ @param filename Filename for the saved image
+ */
++ (void)saveToDocumentsWithImage:(UIImage *)image filename:(NSString *)filename;
+
+/*!
+ Whether the test class should be run as a part of command line tests.
+ By default this is YES since there are some small differences in rendering
+ from the command line vs rendering in the simulator
+
+ @result YES if this test class is disabled for command line tests
+ */
- (BOOL)isCLIDisabled;
-- (void)verifyView:(UIView *)view inFilename:(NSString *)filename atLineNumber:(int)lineNumber;
+/*!
+ Size for a given view. Subclasses can override this to provide custom sizes
+ for views before rendering. The default implementation returns contentSize
+ for scrollviews and returns self.frame.size for all other views.
-+ (void)clearTestImages;
+ @param view View for which to calculate the size
+ @result Size at which the view should be rendered
+ */
+- (CGSize)sizeForView:(UIView *)view;
-+ (void)saveToDocumentsWithImage:(UIImage *)image filename:(NSString *)filename;
+/*!
+ Called from the GHVerifyView macro. This method should not be called manually.
+ Verifies that a view hasn't changed since the last time it was approved. Raises
+ a GHViewChangeException if the view has changed. Raises a GHViewUnavailableException
+ if there is no image from a previous run.
+
+ @param view View to verify
+ @param filename Filename of the call to GHVerifyView
+ @param lineNumber Line number of the call to GHVerifyView
+ */
+- (void)verifyView:(UIView *)view filename:(NSString *)filename lineNumber:(int)lineNumber;
@end
View
46 Classes/GHViewTestCase.m
@@ -28,6 +28,7 @@
//
#import "GHViewTestCase.h"
+#import "GHUnit.h"
#import <QuartzCore/QuartzCore.h>
@interface GHViewTestCase ()
@@ -53,7 +54,7 @@ + (void)createImagesDirectory {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
[fileManager createDirectoryAtPath:[self imagesDirectory] withIntermediateDirectories:YES attributes:nil error:&error];
- if (error) NSLog(@"Unable to create directory %@", [self imagesDirectory]);
+ if (error) GHUDebug(@"Unable to create directory %@", [self imagesDirectory]);
}
+ (UIImage *)imageWithView:(UIView *)view {
@@ -69,23 +70,23 @@ + (UIImage *)imageWithView:(UIView *)view {
+ (void)saveToDocumentsWithImage:(UIImage *)image filename:(NSString *)filename {
NSString *filePath = [self pathForFilename:filename];
- NSLog(@"Saving test image to %@", filePath);
+ GHUDebug(@"Saving view test image to %@", filePath);
// Save image as PNG
[self createImagesDirectory];
BOOL saved = [UIImagePNGRepresentation(image) writeToFile:filePath atomically:YES];
- if (!saved) NSLog(@"Unable to save image to %@", filePath);
+ if (!saved) GHUDebug(@"Unable to save image to %@", filePath);
}
+ (UIImage *)readImageWithFilename:(NSString *)filename {
NSString* filePath = [self pathForFilename:filename];
- NSLog(@"Trying to load image at path %@", filePath);
+ GHUDebug(@"Trying to load image at path %@", filePath);
// First look in the documents directory for the image
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
// Otherwise look in the app bundle
- if (image) NSLog(@"Found image in documents directory");
+ if (image) GHUDebug(@"Found image in documents directory");
if (!image) {
image = [UIImage imageNamed:filename];
- if (image) NSLog(@"Found image in app bundle");
+ if (image) GHUDebug(@"Found image in app bundle");
}
return image;
}
@@ -99,7 +100,7 @@ + (void)clearTestImages {
for (NSString *file in [fileManager contentsOfDirectoryAtPath:directory error:&error]) {
BOOL success = [fileManager removeItemAtPath:[NSString stringWithFormat:@"%@/%@", directory, file] error:&error];
if (!success || error) {
- NSLog(@"Unable to delete file %@%@", directory, file);
+ GHUDebug(@"Unable to delete file %@%@", directory, file);
}
}
}
@@ -112,13 +113,12 @@ + (BOOL)compareImage:(UIImage *)image withNewImage:(UIImage *)newImage {
CFDataRef newImageData = (CFDataRef)UIImagePNGRepresentation(newImage);
const UInt32 *imagePixels = (const UInt32*)CFDataGetBytePtr(imageData);
const UInt32 *newImagePixels = (const UInt32*)CFDataGetBytePtr(newImageData);
- if (CFDataGetLength(imageData) != CFDataGetLength(newImageData)) NSLog(@"WARNING: images are different lengths");
for (int j = 0; j < CFDataGetLength(imageData) / 4; j++)
{
// XOR the pixels here?
if (imagePixels[j] != newImagePixels[j])
{
- NSLog(@"imagePixels[%d]: %x newImagePixels[%d]: %x", j, imagePixels[j], j, newImagePixels[j]);
+ GHUDebug(@"imagePixels[%d]: %x newImagePixels[%d]: %x", j, imagePixels[j], j, newImagePixels[j]);
return NO;
}
}
@@ -131,17 +131,18 @@ + (BOOL)compareImage:(UIImage *)image withNewImage:(UIImage *)newImage {
} pixel;
+ (BOOL)compareImage2:(UIImage *)image withNewImage:(UIImage *)newImage {
+ if (!image || !newImage) return NO;
// If the images are different sizes, just fail
if ((image.size.width != newImage.size.width) || (image.size.height != newImage.size.height)) {
- NSLog(@"Images are differnt sizes");
+ GHUDebug(@"Images are differnt sizes");
return NO;
}
// Allocate a buffer big enough to hold all the pixels
pixel *imagePixels = (pixel *) calloc(1, image.size.width * image.size.height * sizeof(pixel));
pixel *newImagePixels = (pixel *) calloc(1, image.size.width * image.size.height * sizeof(pixel));
if (!imagePixels || !newImagePixels) {
- NSLog(@"Unable to create pixel array for image comparieson.");
+ GHUDebug(@"Unable to create pixel array for image comparieson.");
return NO;
}
CGContextRef imageContext = CGBitmapContextCreate((void *)imagePixels,
@@ -161,7 +162,7 @@ + (BOOL)compareImage2:(UIImage *)image withNewImage:(UIImage *)newImage {
kCGImageAlphaPremultipliedLast
);
if (!imageContext || !imageContext) {
- NSLog(@"Unable to create image contexts for image comparison");
+ GHUDebug(@"Unable to create image contexts for image comparison");
return NO;
}
// Draw the image in the bitmap
@@ -205,20 +206,27 @@ - (BOOL)isCLIDisabled {
return YES;
}
-- (void)verifyView:(UIView *)view inFilename:(NSString *)filename atLineNumber:(int)lineNumber {
+- (CGSize)sizeForView:(UIView *)view {
+ // If the view is a UIScrollView, return the contet
+ if ([view isKindOfClass:[UIScrollView class]]) {
+ UIScrollView *scrollView = (UIScrollView *)view;
+ return scrollView.contentSize;
+ }
+ return view.frame.size;
+}
+
+- (void)verifyView:(UIView *)view filename:(NSString *)filename lineNumber:(int)lineNumber {
// Fail if the view is nil
if (!view) [[NSException ghu_failureInFile:filename atLine:lineNumber withDescription:@"View cannot be nil in GHVerifyView"] raise];
// Fail if the view has CGSizeZero
if (CGSizeEqualToSize(view.frame.size, CGSizeZero)) [[NSException ghu_failureInFile:filename atLine:lineNumber withDescription:@"View must have a nonzero size in GHVerifyView"] raise];
+
// View testing file names have the format [test class name]-[test selector name]-[# of verify in selector]-[view class name]
NSString *imageFilename = [NSString stringWithFormat:@"%@-%@-%d-%@.png", NSStringFromClass([self class]), NSStringFromSelector(currentSelector_), imageVerifyCount_, NSStringFromClass([view class])];
UIImage *originalViewImage = [[self class] readImageWithFilename:imageFilename];
- // If the view is a UIScrollView, size it to the content size
- if ([view isKindOfClass:[UIScrollView class]]) {
- UIScrollView *scrollView = (UIScrollView *)view;
- view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, scrollView.contentSize.width, scrollView.contentSize.height);
- }
+ CGSize viewSize = [self sizeForView:view];
+ view.frame = CGRectMake(0, 0, viewSize.width, viewSize.height);
UIImage *newViewImage = [[self class] imageWithView:view];
NSMutableDictionary *exceptionDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
@@ -228,7 +236,7 @@ - (void)verifyView:(UIView *)view inFilename:(NSString *)filename atLineNumber:(
filename, GHTestFilenameKey,
nil];
if (!originalViewImage) {
- NSLog(@"No image available for filename %@", filename);
+ GHUDebug(@"No image available for filename %@", filename);
[[NSException exceptionWithName:@"GHViewUnavailableException" reason:@"No image saved for view" userInfo:exceptionDictionary] raise];
} else if (![[self class] compareImage2:originalViewImage withNewImage:newViewImage]) {
[exceptionDictionary setObject:originalViewImage forKey:@"OriginalImage"];

0 comments on commit a8732c9

Please sign in to comment.