aboutsummaryrefslogtreecommitdiff
path: root/UnitTesting/GTMNSObject+UnitTesting.m
diff options
context:
space:
mode:
Diffstat (limited to 'UnitTesting/GTMNSObject+UnitTesting.m')
-rw-r--r--UnitTesting/GTMNSObject+UnitTesting.m1047
1 files changed, 0 insertions, 1047 deletions
diff --git a/UnitTesting/GTMNSObject+UnitTesting.m b/UnitTesting/GTMNSObject+UnitTesting.m
deleted file mode 100644
index 5b60a6d..0000000
--- a/UnitTesting/GTMNSObject+UnitTesting.m
+++ /dev/null
@@ -1,1047 +0,0 @@
-//
-// GTMNSObject+UnitTesting.m
-//
-// An informal protocol for doing advanced unittesting with objects.
-//
-// Copyright 2006-2008 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy
-// of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-//
-
-#import "GTMNSObject+UnitTesting.h"
-#import "GTMSystemVersion.h"
-
-#if GTM_IPHONE_SDK
-#import <UIKit/UIKit.h>
-#else
-#import <AppKit/AppKit.h>
-#endif
-
-NSString *const GTMUnitTestingEncodedObjectNotification
- = @"GTMUnitTestingEncodedObjectNotification";
-NSString *const GTMUnitTestingEncoderKey = @"GTMUnitTestingEncoderKey";
-
-#if GTM_IPHONE_SDK
-// No UTIs on iPhone. Only two we need.
-const CFStringRef kUTTypePNG = CFSTR("public.png");
-const CFStringRef kUTTypeJPEG = CFSTR("public.jpeg");
-#endif
-
-// This class exists so that we can locate our bundle using [NSBundle
-// bundleForClass:]. We don't use [NSBundle mainBundle] because when we are
-// being run as a unit test, we aren't the mainBundle
-@interface GTMUnitTestingAdditionsBundleFinder : NSObject {
- // Nothing here
-}
-// or here
-@end
-
-@implementation GTMUnitTestingAdditionsBundleFinder
-// Nothing here. We're just interested in the name for finding our bundle.
-@end
-
-#if GTM_IPHONE_SDK
-static NSString *iOS_Idiom(void) {
- UIUserInterfaceIdiom idiom;
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_2
- idiom = [[UIDevice currentDevice] userInterfaceIdiom];
-#else
- idiom = UI_USER_INTERFACE_IDIOM();
-#endif
- if (idiom == UIUserInterfaceIdiomPad) {
- return @"iPad";
- }
- _GTMDevAssert((idiom == UIUserInterfaceIdiomPhone),
- @"unknown idiom %zd", idiom);
- return @"iPhone";
-}
-#endif // GTM_IPHONE_SDK
-
-BOOL GTMIsObjectImageEqualToImageNamed(id object,
- NSString* filename,
- NSString **error) {
- NSString *failString = nil;
- if (error) {
- *error = nil;
- }
- BOOL isGood = [object respondsToSelector:@selector(gtm_unitTestImage)];
- if (isGood) {
- if ([object gtm_areSystemSettingsValidForDoingImage]) {
- NSString *aPath = [object gtm_pathForImageNamed:filename];
- CGImageRef diff = nil;
- isGood = aPath != nil;
- if (isGood) {
- isGood = [object gtm_compareWithImageAt:aPath diffImage:&diff];
- }
- if (!isGood) {
- if (aPath) {
- filename = [filename stringByAppendingString:@"_Failed"];
- }
- BOOL aSaved = [object gtm_saveToImageNamed:filename];
- NSString *fileNameWithExtension
- = [NSString stringWithFormat:@"%@.%@",
- filename, [object gtm_imageExtension]];
- NSString *fullSavePath = [object gtm_saveToPathForImageNamed:filename];
- if (NO == aSaved) {
- if (!aPath) {
- failString = [NSString stringWithFormat:@"File %@ did not exist in "
- @"bundle. Tried to save as %@ and failed.",
- fileNameWithExtension, fullSavePath];
- } else {
- failString = [NSString stringWithFormat:@"Object image different "
- @"than file %@. Tried to save as %@ and failed.",
- aPath, fullSavePath];
- }
- } else {
- if (!aPath) {
- failString = [NSString stringWithFormat:@"File %@ did not exist in "
- @" bundle. Saved to %@", fileNameWithExtension,
- fullSavePath];
- } else {
- NSString *diffPath = [filename stringByAppendingString:@"_Diff"];
- diffPath = [object gtm_saveToPathForImageNamed:diffPath];
- NSData *data = nil;
- if (diff) {
- data = [object gtm_imageDataForImage:diff];
- }
- if ([data writeToFile:diffPath atomically:YES]) {
- failString = [NSString stringWithFormat:@"Object image different "
- @"than file\n%@\nSaved image to\n%@\n"
- @"Saved diff to\n%@\n",
- aPath, fullSavePath, diffPath];
- } else {
- failString = [NSString stringWithFormat:@"Object image different "
- @"than file\n%@\nSaved image to\n%@\nUnable to save "
- @"diff. Most likely the image and diff are "
- @"different sizes.",
- aPath, fullSavePath];
- }
- }
- }
- }
- CGImageRelease(diff);
- } else {
- failString = @"systemSettings not valid for taking image"; // COV_NF_LINE
- }
- } else {
- if (object == nil) {
- failString = @"Testing a nil image.";
- } else {
- failString = @"Object does not conform to GTMUnitTestingImaging protocol";
- }
- }
- if (error) {
- *error = failString;
- }
- return isGood;
-}
-
-BOOL GTMIsObjectStateEqualToStateNamed(id object,
- NSString* filename,
- NSString **error) {
- NSString *failString = nil;
- if (error) {
- *error = nil;
- }
- BOOL isGood = [object conformsToProtocol:@protocol(GTMUnitTestingEncoding)];
- if (isGood) {
- NSString *aPath = [object gtm_pathForStateNamed:filename];
- isGood = aPath != nil;
- if (isGood) {
- isGood = [object gtm_compareWithStateAt:aPath];
- }
- if (!isGood) {
- if (aPath) {
- filename = [filename stringByAppendingString:@"_Failed"];
- }
- BOOL aSaved = [object gtm_saveToStateNamed:filename];
- NSString *fileNameWithExtension = [NSString stringWithFormat:@"%@.%@",
- filename, [object gtm_stateExtension]];
- NSString *fullSavePath = [object gtm_saveToPathForStateNamed:filename];
- if (NO == aSaved) {
- if (!aPath) {
- failString = [NSString stringWithFormat:@"File %@ did not exist in "
- @"bundle. Tried to save as %@ and failed.",
- fileNameWithExtension, fullSavePath];
- } else {
- failString = [NSString stringWithFormat:@"Object state different "
- @"than file %@. Tried to save as %@ and failed.",
- aPath, fullSavePath];
- }
- } else {
- if (!aPath) {
- failString = [NSString stringWithFormat:@"File %@ did not exist in "
- @ "bundle. Saved to %@", fileNameWithExtension,
- fullSavePath];
- } else {
- failString = [NSString stringWithFormat:@"Object state different "
- @"than file %@. Saved to %@", aPath, fullSavePath];
- }
- }
- }
- } else {
- failString = @"Object does not conform to GTMUnitTestingEncoding protocol";
- }
- if (error) {
- *error = failString;
- }
- return isGood;
-}
-
-CGContextRef GTMCreateUnitTestBitmapContextOfSizeWithData(CGSize size,
- unsigned char **data) {
- CGContextRef context = NULL;
- size_t height = size.height;
- size_t width = size.width;
- size_t bytesPerRow = width * 4;
- size_t bitsPerComponent = 8;
- CGColorSpaceRef cs = NULL;
-#if GTM_IPHONE_SDK
- cs = CGColorSpaceCreateDeviceRGB();
-#else
- cs = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
-#endif
- _GTMDevAssert(cs, @"Couldn't create colorspace");
- CGBitmapInfo info
- = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault;
- if (data) {
- *data = (unsigned char*)calloc(bytesPerRow, height);
- _GTMDevAssert(*data, @"Couldn't create bitmap");
- }
- context = CGBitmapContextCreate(data ? *data : NULL, width, height,
- bitsPerComponent, bytesPerRow, cs, info);
- _GTMDevAssert(context, @"Couldn't create an context");
- if (!data) {
- CGContextClearRect(context, CGRectMake(0, 0, size.width, size.height));
- }
- CGContextSetRenderingIntent(context, kCGRenderingIntentRelativeColorimetric);
- CGContextSetInterpolationQuality(context, kCGInterpolationNone);
- CGContextSetShouldAntialias(context, NO);
- CGContextSetAllowsAntialiasing(context, NO);
- CGContextSetShouldSmoothFonts(context, NO);
- CGColorSpaceRelease(cs);
- return context;
-}
-
-@interface NSObject (GTMUnitTestingAdditionsPrivate)
-/// Find the path for a file named name.extension in your bundle.
-// Searches for the following:
-// "name.extension",
-// "name.arch.extension",
-// "name.arch.OSVersionMajor.extension"
-// "name.arch.OSVersionMajor.OSVersionMinor.extension"
-// "name.arch.OSVersionMajor.OSVersionMinor.OSVersion.bugfix.extension"
-// "name.arch.OSVersionMajor.extension"
-// "name.OSVersionMajor.arch.extension"
-// "name.OSVersionMajor.OSVersionMinor.arch.extension"
-// "name.OSVersionMajor.OSVersionMinor.OSVersion.bugfix.arch.extension"
-// "name.OSVersionMajor.extension"
-// "name.OSVersionMajor.OSVersionMinor.extension"
-// "name.OSVersionMajor.OSVersionMinor.OSVersion.bugfix.extension"
-// Do not include the ".extension" extension on your name.
-//
-// Args:
-// name: The name for the file you would like to find.
-// extension: the extension for the file you would like to find
-//
-// Returns:
-// the path if the file exists in your bundle
-// or nil if no file is found
-//
-- (NSString *)gtm_pathForFileNamed:(NSString*)name
- extension:(NSString*)extension;
-- (NSString *)gtm_saveToPathForFileNamed:(NSString*)name
- extension:(NSString*)extension;
-- (CGImageRef)gtm_unitTestImage;
-// Returns nil if there is no override
-- (NSString *)gtm_getOverrideDefaultUnitTestSaveToDirectory;
-@end
-
-// This is a keyed coder for storing unit test state data. It is used only by
-// the GTMUnitTestingAdditions category. Most of the work is done in
-// encodeObject:forKey:.
-@interface GTMUnitTestingKeyedCoder : NSCoder {
- NSMutableDictionary *dictionary_; // storage for data (STRONG)
-}
-
-// get the data stored in coder.
-//
-// Returns:
-// NSDictionary with currently stored data.
-- (NSDictionary*)dictionary;
-@end
-
-// Small utility function for checking to see if a is b +/- 1.
-GTM_INLINE BOOL almostEqual(unsigned char a, unsigned char b) {
- unsigned char diff = a > b ? a - b : b - a;
- BOOL notEqual = diff < 2;
- return notEqual;
-}
-
-@implementation GTMUnitTestingKeyedCoder
-
-// Set up storage for coder. Stores type and version.
-// Version 1
-//
-// Returns:
-// self
-- (id)init {
- self = [super init];
- if (self != nil) {
- dictionary_ = [[NSMutableDictionary alloc] initWithCapacity:2];
- [dictionary_ setObject:@"GTMUnitTestingArchive" forKey:@"$GTMArchive"];
-
- // Version number can be changed here.
- [dictionary_ setObject:[NSNumber numberWithInt:1] forKey:@"$GTMVersion"];
- }
- return self;
-}
-
-// Standard dealloc
-- (void)dealloc {
- [dictionary_ release];
- [super dealloc];
-}
-
-// Utility function for checking for a key value. We don't want duplicate keys
-// in any of our dictionaries as we may be writing over data stored by previous
-// objects.
-//
-// Arguments:
-// key - key to check for in dictionary
-- (void)checkForKey:(NSString*)key {
- _GTMDevAssert(![dictionary_ objectForKey:key],
- @"Key already exists for %@", key);
-}
-
-// Key routine for the encoder. We store objects in our dictionary based on
-// their key. As we encode objects we send out notifications to let other
-// classes doing tests add their specific data to the base types. If we can't
-// encode the object (it doesn't support gtm_unitTestEncodeState) and we don't
-// get any info back from the notifier, we attempt to store it's description.
-//
-// Arguments:
-// objv - object to be encoded
-// key - key to encode it with
-//
-- (void)encodeObject:(id)objv forKey:(NSString *)key {
- // Sanity checks
- if (!objv) return;
- [self checkForKey:key];
-
- // Set up a new dictionary for the current object
- NSMutableDictionary *curDictionary = dictionary_;
- dictionary_ = [[NSMutableDictionary alloc] initWithCapacity:0];
-
- // If objv responds to gtm_unitTestEncodeState get it to record
- // its data.
- if ([objv respondsToSelector:@selector(gtm_unitTestEncodeState:)]) {
- [objv gtm_unitTestEncodeState:self];
- }
-
- // We then send out a notification to let other folks
- // add data for this object
- NSDictionary *notificationDict
- = [NSDictionary dictionaryWithObject:self forKey:GTMUnitTestingEncoderKey];
- NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
- [nc postNotificationName:GTMUnitTestingEncodedObjectNotification
- object:objv
- userInfo:notificationDict];
-
- // If we got anything from the object, or from the notification, store it in
- // our dictionary. Otherwise store the description.
- if ([dictionary_ count] > 0) {
- [curDictionary setObject:dictionary_ forKey:key];
- } else {
- NSString *description = [objv description];
- // If description has a pointer value in it, we don't want to store it
- // as the pointer value can change from run to run
- if (description && [description rangeOfString:@"0x"].length == 0) {
- [curDictionary setObject:description forKey:key];
- } else {
- _GTMDevAssert(NO, @"Unable to encode forKey: %@", key); // COV_NF_LINE
- }
- }
- [dictionary_ release];
- dictionary_ = curDictionary;
-}
-
-// Basic encoding methods for POD types.
-//
-// Arguments:
-// *v - value to encode
-// key - key to encode it in
-
-- (void)encodeBool:(BOOL)boolv forKey:(NSString *)key {
- [self checkForKey:key];
- [dictionary_ setObject:[NSNumber numberWithBool:boolv] forKey:key];
-}
-
-- (void)encodeInt:(int)intv forKey:(NSString *)key {
- [self checkForKey:key];
- [dictionary_ setObject:[NSNumber numberWithInt:intv] forKey:key];
-}
-
-- (void)encodeInt32:(int32_t)intv forKey:(NSString *)key {
- [self checkForKey:key];
- [dictionary_ setObject:[NSNumber numberWithLong:intv] forKey:key];
-}
-
-- (void)encodeInt64:(int64_t)intv forKey:(NSString *)key {
- [self checkForKey:key];
- [dictionary_ setObject:[NSNumber numberWithLongLong:intv] forKey:key];
-}
-
-- (void)encodeFloat:(float)realv forKey:(NSString *)key {
- [self checkForKey:key];
- [dictionary_ setObject:[NSNumber numberWithFloat:realv] forKey:key];
-}
-
-- (void)encodeDouble:(double)realv forKey:(NSString *)key {
- [self checkForKey:key];
- [dictionary_ setObject:[NSNumber numberWithDouble:realv] forKey:key];
-}
-
-- (void)encodeBytes:(const uint8_t *)bytesp
- length:(NSUInteger)lenv
- forKey:(NSString *)key {
- [self checkForKey:key];
- [dictionary_ setObject:[NSData dataWithBytes:bytesp
- length:lenv]
- forKey:key];
-}
-
-// Get our storage back as an NSDictionary
-//
-// Returns:
-// NSDictionary containing our encoded info
--(NSDictionary*)dictionary {
- return [[dictionary_ retain] autorelease];
-}
-
-@end
-
-static NSString *gGTMUnitTestSaveToDirectory = nil;
-
-@implementation NSObject (GTMUnitTestingAdditions)
-
-+ (void)gtm_setUnitTestSaveToDirectory:(NSString*)path {
- @synchronized([self class]) {
- [gGTMUnitTestSaveToDirectory autorelease];
- gGTMUnitTestSaveToDirectory = [path copy];
- }
-}
-
-+ (NSString *)gtm_getUnitTestSaveToDirectory {
- NSString *result = nil;
- @synchronized([self class]) {
- if (!gGTMUnitTestSaveToDirectory) {
-#if GTM_IPHONE_SDK
- // About the only thing safe for the sandbox is the documents directory.
- NSArray *documentsDirs
- = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
- NSUserDomainMask, YES);
- gGTMUnitTestSaveToDirectory = [documentsDirs objectAtIndex:0];
-#else
- NSArray *desktopDirs
- = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory,
- NSUserDomainMask,
- YES);
- gGTMUnitTestSaveToDirectory = [desktopDirs objectAtIndex:0];
-#endif
- // Did we get overridden?
- NSString *override = [self gtm_getOverrideDefaultUnitTestSaveToDirectory];
- if (override) {
- gGTMUnitTestSaveToDirectory = override;
- }
- [gGTMUnitTestSaveToDirectory retain];
- }
- result = gGTMUnitTestSaveToDirectory;
- }
-
- return result;
-}
-
-// Return nil if there is no override
-- (NSString *)gtm_getOverrideDefaultUnitTestSaveToDirectory {
- NSString *result = nil;
-
- // If we have an environment variable that ends in "BUILD_NUMBER" odds are
- // we're on an automated build system, so use the build products dir as an
- // override instead of writing on the desktop.
- NSDictionary *env = [[NSProcessInfo processInfo] environment];
- NSString *key;
- GTM_FOREACH_KEY(key, env) {
- if ([key hasSuffix:@"BUILD_NUMBER"]) {
- break;
- }
- }
- if (key) {
- result = [env objectForKey:@"BUILT_PRODUCTS_DIR"];
- }
-
- if (result && [result length] == 0) {
- result = nil;
- }
- return result;
-}
-
-/// Find the path for a file named name.extension in your bundle.
-// Searches for the following:
-// "name.CompilerSDK.OSVersionMajor.OSVersionMinor.OSVersionBugFix.arch.extension"
-// "name.CompilerSDK.OSVersionMajor.OSVersionMinor.arch.extension"
-// "name.CompilerSDK.OSVersionMajor.arch.extension"
-// "name.CompilerSDK.arch.extension"
-// "name.CompilerSDK.OSVersionMajor.OSVersionMinor.OSVersionBugFix.extension"
-// "name.CompilerSDK.OSVersionMajor.OSVersionMinor.extension"
-// "name.CompilerSDK.OSVersionMajor.extension"
-// "name.CompilerSDK.extension"
-// "name.OSVersionMajor.OSVersionMinor.OSVersionBugFix.arch.extension"
-// "name.OSVersionMajor.OSVersionMinor.arch.extension"
-// "name.OSVersionMajor.arch.extension"
-// "name.arch.extension"
-// "name.OSVersionMajor.OSVersionMinor.OSVersionBugFix.extension"
-// "name.OSVersionMajor.OSVersionMinor.extension"
-// "name.OSVersionMajor.extension"
-// "name.extension"
-// Do not include the ".extension" extension on your name.
-//
-// Args:
-// name: The name for the file you would like to find.
-// extension: the extension for the file you would like to find
-//
-// Returns:
-// the path if the file exists in your bundle
-// or nil if no file is found
-//
-- (NSString *)gtm_pathForFileNamed:(NSString*)name
- extension:(NSString*)extension {
- NSString *thePath = nil;
- Class bundleClass = [GTMUnitTestingAdditionsBundleFinder class];
- NSBundle *myBundle = [NSBundle bundleForClass:bundleClass];
- _GTMDevAssert(myBundle,
- @"Couldn't find bundle for class: %@ searching for file:%@.%@",
- NSStringFromClass(bundleClass), name, extension);
- // System Version
- SInt32 major, minor, bugFix;
- [GTMSystemVersion getMajor:&major minor:&minor bugFix:&bugFix];
- NSString *systemVersions[4];
- systemVersions[0] = [NSString stringWithFormat:@".%d.%d.%d",
- (int)major, (int)minor, (int)bugFix];
- systemVersions[1]
- = [NSString stringWithFormat:@".%d.%d", (int)major, (int)minor];
- systemVersions[2] = [NSString stringWithFormat:@".%d", (int)major];
- systemVersions[3] = @"";
- // Architecture
- NSString *architecture[2];
-#if GTM_IPHONE_SDK
- architecture[0]
- = [NSString stringWithFormat:@".%@", iOS_Idiom()];
-#else
- architecture[0]
- = [NSString stringWithFormat:@".%@",
- [GTMSystemVersion runtimeArchitecture]];
-#endif
- architecture[1] = @"";
- // Compiler SDK
- // Some times Apple changes how things work based on the SDK built against.
- NSString *sdks[2];
-#if GTM_MACOS_SDK
-# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7
- sdks[0] = @".10_7_SDK";
-# elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
- sdks[0] = @".10_6_SDK";
-# elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
- sdks[0] = @".10_5_SDK";
-# elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4
- sdks[0] = @".10_4_SDK";
-# elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3
- sdks[0] = @".10_3_SDK";
-# elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_2
- sdks[0] = @".10_2_SDK";
-# elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_1
- sdks[0] = @".10_1_SDK";
-# else
- sdks[0] = @".10_0_SDK";
-# endif
-#else // !GTM_MACOS_SDK
-# if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_5_0
- sdks[0] = @".5_0_SDK";
-# elif __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_3
- sdks[0] = @".4_3_SDK";
-# else
- sdks[0] = @"";
-# endif
-#endif // GTM_MACOS_SDK
- sdks[1] = @"";
-
- // Note that we are searching for the most exact match first.
- for (size_t i = 0;
- !thePath && i < sizeof(sdks) / sizeof(*sdks);
- ++i) {
- for (size_t j = 0;
- !thePath && j < sizeof(architecture) / sizeof(*architecture);
- j++) {
- for (size_t k = 0;
- !thePath && k < sizeof(systemVersions) / sizeof(*systemVersions);
- k++) {
- NSString *fullName = [NSString stringWithFormat:@"%@%@%@%@",
- name, sdks[i], systemVersions[k], architecture[j]];
- thePath = [myBundle pathForResource:fullName ofType:extension];
- }
- }
- }
-
- return thePath;
-}
-
-- (NSString *)gtm_saveToPathForFileNamed:(NSString*)name
- extension:(NSString*)extension {
-#if GTM_IPHONE_SDK
- NSString *systemArchitecture = iOS_Idiom();
-#else
- NSString *systemArchitecture = [GTMSystemVersion runtimeArchitecture];
-#endif
-
- SInt32 major, minor, bugFix;
- [GTMSystemVersion getMajor:&major minor:&minor bugFix:&bugFix];
-
- // We don't include the CompilerSDK in here because it is not something that
- // that is commonly needed.
- NSString *fullName = [NSString stringWithFormat:@"%@.%d.%d.%d.%@",
- name, (int)major, (int)minor, (int)bugFix, systemArchitecture];
-
- NSString *basePath = [[self class] gtm_getUnitTestSaveToDirectory];
- return [[basePath stringByAppendingPathComponent:fullName]
- stringByAppendingPathExtension:extension];
-}
-
-#pragma mark UnitTestImage
-
-// Checks to see that system settings are valid for doing an image comparison.
-// To be overridden by subclasses.
-// Returns:
-// YES if we can do image comparisons for this object type.
-- (BOOL)gtm_areSystemSettingsValidForDoingImage {
- return YES;
-}
-
-- (CFStringRef)gtm_imageUTI {
-#if GTM_IPHONE_SDK
- return kUTTypePNG;
-#else
- // Currently can't use PNG on Leopard. (10.5.2)
- // Radar:5844618 PNG importer/exporter in ImageIO is lossy
- return kUTTypeTIFF;
-#endif
-}
-
-// Return the extension to be used for saving unittest images
-//
-// Returns
-// An extension (e.g. "png")
-- (NSString*)gtm_imageExtension {
- CFStringRef uti = [self gtm_imageUTI];
-#if GTM_IPHONE_SDK
- if (CFEqual(uti, kUTTypePNG)) {
- return @"png";
- } else if (CFEqual(uti, kUTTypeJPEG)) {
- return @"jpg";
- } else {
- _GTMDevAssert(NO, @"Illegal UTI for iPhone");
- }
- return nil;
-#else
- CFStringRef extension
- = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension);
- _GTMDevAssert(extension, @"No extension for uti: %@", uti);
-
- return GTMCFAutorelease(extension);
-#endif
-}
-
-// Return image data in the format expected for gtm_imageExtension
-// So for a "png" extension I would expect "png" data
-//
-// Returns
-// NSData for image
-- (NSData*)gtm_imageDataForImage:(CGImageRef)image {
- NSData *data = nil;
-#if GTM_IPHONE_SDK
- // iPhone support
- UIImage *uiImage = [UIImage imageWithCGImage:image];
- CFStringRef uti = [self gtm_imageUTI];
- if (CFEqual(uti, kUTTypePNG)) {
- data = UIImagePNGRepresentation(uiImage);
- } else if (CFEqual(uti, kUTTypeJPEG)) {
- data = UIImageJPEGRepresentation(uiImage, 1.0f);
- } else {
- _GTMDevAssert(NO, @"Illegal UTI for iPhone");
- }
-#else
- data = [NSMutableData data];
- CGImageDestinationRef dest
- = CGImageDestinationCreateWithData((CFMutableDataRef)data,
- [self gtm_imageUTI],
- 1,
- NULL);
- // LZW Compression for TIFF
- NSDictionary *tiffDict
- = [NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber gtm_numberWithUnsignedInteger:NSTIFFCompressionLZW],
- (const NSString*)kCGImagePropertyTIFFCompression,
- nil];
- NSDictionary *destProps
- = [NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithFloat:1.0f],
- (const NSString*)kCGImageDestinationLossyCompressionQuality,
- tiffDict,
- (const NSString*)kCGImagePropertyTIFFDictionary,
- nil];
- CGImageDestinationAddImage(dest, image, (CFDictionaryRef)destProps);
- CGImageDestinationFinalize(dest);
- CFRelease(dest);
-#endif
- return data;
-
-}
-
-// Save the unitTestImage to an image file with name |name| at
-// ~/Desktop/|name|.extension.
-//
-// Note: When running under Pulse automation output is redirected to the
-// Pulse base directory.
-//
-// Args:
-// name: The name for the image file you would like saved.
-//
-// Returns:
-// YES if the file was successfully saved.
-//
-- (BOOL)gtm_saveToImageNamed:(NSString*)name {
- NSString *newPath = [self gtm_saveToPathForImageNamed:name];
- return [self gtm_saveToImageAt:newPath];
-}
-
-// Save unitTestImage of |self| to an image file at path |path|.
-//
-// Args:
-// name: The name for the image file you would like saved.
-//
-// Returns:
-// YES if the file was successfully saved.
-//
-- (BOOL)gtm_saveToImageAt:(NSString*)path {
- if (!path) return NO;
- NSData *data = [self gtm_imageRepresentation];
- return [data writeToFile:path atomically:YES];
-}
-
-// Generates a CGImageRef from the image at |path|
-// Args:
-// path: The path to the image.
-//
-// Returns:
-// A CGImageRef that you own, or nil if no image at path
-- (CGImageRef)gtm_imageWithContentsOfFile:(NSString*)path {
- CGImageRef imageRef = nil;
-#if GTM_IPHONE_SDK
- UIImage *image = [UIImage imageWithContentsOfFile:path];
- if (image) {
- imageRef = CGImageRetain(image.CGImage);
- }
-#else
- CFURLRef url = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)path,
- kCFURLPOSIXPathStyle, NO);
- if (url) {
- CGImageSourceRef imageSource = CGImageSourceCreateWithURL(url, NULL);
- CFRelease(url);
- if (imageSource) {
- imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
- CFRelease(imageSource);
- }
- }
-#endif
- return (CGImageRef)GTMCFAutorelease(imageRef);
-}
-
-/// Compares unitTestImage of |self| to the image located at |path|
-//
-// Args:
-// path: the path to the image file you want to compare against.
-// If diff is non-nil, it will contain an auto-released diff of the images.
-//
-// Returns:
-// YES if they are equal, NO is they are not
-// If diff is non-nil, it will contain an auto-released diff of the images.
-//
-- (BOOL)gtm_compareWithImageAt:(NSString*)path diffImage:(CGImageRef*)diff {
- BOOL answer = NO;
- if (diff) {
- *diff = nil;
- }
- CGImageRef fileRep = [self gtm_imageWithContentsOfFile:path];
- _GTMDevAssert(fileRep, @"Unable to create imagerep from %@", path);
-
- CGImageRef imageRep = [self gtm_unitTestImage];
- _GTMDevAssert(imageRep, @"Unable to create imagerep for %@", self);
-
- size_t fileHeight = CGImageGetHeight(fileRep);
- size_t fileWidth = CGImageGetWidth(fileRep);
- size_t imageHeight = CGImageGetHeight(imageRep);
- size_t imageWidth = CGImageGetWidth(imageRep);
- if (fileHeight == imageHeight && fileWidth == imageWidth) {
- // if all the sizes are equal, run through the bytes and compare
- // them for equality.
- // Do an initial fast check, if this fails and the caller wants a
- // diff, we'll do the slow path and create the diff. The diff path
- // could be optimized, but probably not necessary at this point.
- answer = YES;
-
- CGSize imageSize = CGSizeMake(fileWidth, fileHeight);
- CGRect imageRect = CGRectMake(0, 0, fileWidth, fileHeight);
- unsigned char *fileData;
- unsigned char *imageData;
- CGContextRef fileContext
- = GTMCreateUnitTestBitmapContextOfSizeWithData(imageSize, &fileData);
- _GTMDevAssert(fileContext, @"Unable to create filecontext");
- CGContextDrawImage(fileContext, imageRect, fileRep);
- CGContextRef imageContext
- = GTMCreateUnitTestBitmapContextOfSizeWithData(imageSize, &imageData);
- _GTMDevAssert(imageContext, @"Unable to create imageContext");
- CGContextDrawImage(imageContext, imageRect, imageRep);
-
- size_t fileBytesPerRow = CGBitmapContextGetBytesPerRow(fileContext);
- size_t imageBytesPerRow = CGBitmapContextGetBytesPerRow(imageContext);
- size_t row, col;
-
- _GTMDevAssert(imageWidth * 4 <= imageBytesPerRow,
- @"We expect image data to be 32bit RGBA");
-
- for (row = 0; row < fileHeight && answer; row++) {
- answer = memcmp(fileData + fileBytesPerRow * row,
- imageData + imageBytesPerRow * row,
- imageWidth * 4) == 0;
- }
- if (!answer && diff) {
- answer = YES;
- unsigned char *diffData;
- CGContextRef diffContext
- = GTMCreateUnitTestBitmapContextOfSizeWithData(imageSize, &diffData);
- _GTMDevAssert(diffContext, @"Can't make diff context");
- size_t diffRowBytes = CGBitmapContextGetBytesPerRow(diffContext);
- for (row = 0; row < imageHeight; row++) {
- uint32_t *imageRow = (uint32_t*)(imageData + imageBytesPerRow * row);
- uint32_t *fileRow = (uint32_t*)(fileData + fileBytesPerRow * row);
- uint32_t* diffRow = (uint32_t*)(diffData + diffRowBytes * row);
- for (col = 0; col < imageWidth; col++) {
- uint32_t imageColor = imageRow[col];
- uint32_t fileColor = fileRow[col];
-
- unsigned char imageAlpha = imageColor & 0xF;
- unsigned char imageBlue = imageColor >> 8 & 0xF;
- unsigned char imageGreen = imageColor >> 16 & 0xF;
- unsigned char imageRed = imageColor >> 24 & 0xF;
- unsigned char fileAlpha = fileColor & 0xF;
- unsigned char fileBlue = fileColor >> 8 & 0xF;
- unsigned char fileGreen = fileColor >> 16 & 0xF;
- unsigned char fileRed = fileColor >> 24 & 0xF;
-
- // Check to see if color is almost right.
- // No matter how hard I've tried, I've still gotten occasionally
- // screwed over by colorspaces not mapping correctly, and small
- // sampling errors coming in. This appears to work for most cases.
- // Almost equal is defined to check within 1% on all components.
- BOOL equal = almostEqual(imageRed, fileRed) &&
- almostEqual(imageGreen, fileGreen) &&
- almostEqual(imageBlue, fileBlue) &&
- almostEqual(imageAlpha, fileAlpha);
- answer &= equal;
- if (diff) {
- uint32_t newColor;
- if (equal) {
- newColor = (((uint32_t)imageRed) << 24) +
- (((uint32_t)imageGreen) << 16) +
- (((uint32_t)imageBlue) << 8) +
- (((uint32_t)imageAlpha) / 2);
- } else {
- newColor = 0xFF0000FF;
- }
- diffRow[col] = newColor;
- }
- }
- }
- *diff = CGBitmapContextCreateImage(diffContext);
- free(diffData);
- CFRelease(diffContext);
- }
- free(fileData);
- CFRelease(fileContext);
- free(imageData);
- CFRelease(imageContext);
- }
- return answer;
-}
-
-// Find the path for an image by name in your bundle.
-// Do not include the extension on your name.
-//
-// Args:
-// name: The name for the image file you would like to find.
-//
-// Returns:
-// the path if the image exists in your bundle
-// or nil if no image to be found
-//
-- (NSString *)gtm_pathForImageNamed:(NSString*)name {
- return [self gtm_pathForFileNamed:name
- extension:[self gtm_imageExtension]];
-}
-
-- (NSString *)gtm_saveToPathForImageNamed:(NSString*)name {
- return [self gtm_saveToPathForFileNamed:name
- extension:[self gtm_imageExtension]];
-}
-
-// Gives us a representation of unitTestImage of |self|.
-//
-// Returns:
-// a representation of image if successful
-// nil if failed
-//
-- (NSData *)gtm_imageRepresentation {
- CGImageRef imageRep = [self gtm_unitTestImage];
- NSData *data = [self gtm_imageDataForImage:imageRep];
- _GTMDevAssert(data, @"unable to create %@ from %@",
- [self gtm_imageExtension], self);
- return data;
-}
-
-#pragma mark UnitTestState
-
-// Return the extension to be used for saving unittest states
-//
-// Returns
-// An extension (e.g. "gtmUTState")
-- (NSString*)gtm_stateExtension {
- return @"gtmUTState";
-}
-
-// Save the encoded unit test state to a state file with name |name| at
-// ~/Desktop/|name|.extension.
-//
-// Note: When running under Pulse automation output is redirected to the
-// Pulse base directory.
-//
-// Args:
-// name: The name for the state file you would like saved.
-//
-// Returns:
-// YES if the file was successfully saved.
-//
-- (BOOL)gtm_saveToStateNamed:(NSString*)name {
- NSString *newPath = [self gtm_saveToPathForStateNamed:name];
- return [self gtm_saveToStateAt:newPath];
-}
-
-// Save encoded unit test state of |self| to a state file at path |path|.
-//
-// Args:
-// name: The name for the state file you would like saved.
-//
-// Returns:
-// YES if the file was successfully saved.
-//
-- (BOOL)gtm_saveToStateAt:(NSString*)path {
- if (!path) return NO;
- NSDictionary *dictionary = [self gtm_stateRepresentation];
- return [dictionary writeToFile:path atomically:YES];
-}
-
-// Compares encoded unit test state of |self| to the state file located at
-// |path|
-//
-// Args:
-// path: the path to the state file you want to compare against.
-//
-// Returns:
-// YES if they are equal, NO is they are not
-//
-- (BOOL)gtm_compareWithStateAt:(NSString*)path {
- NSDictionary *masterDict = [NSDictionary dictionaryWithContentsOfFile:path];
- _GTMDevAssert(masterDict, @"Unable to create dictionary from %@", path);
- NSDictionary *selfDict = [self gtm_stateRepresentation];
- return [selfDict isEqual: masterDict];
-}
-
-// Find the path for a state by name in your bundle.
-// Do not include the extension.
-//
-// Args:
-// name: The name for the state file you would like to find.
-//
-// Returns:
-// the path if the state exists in your bundle
-// or nil if no state to be found
-//
-- (NSString *)gtm_pathForStateNamed:(NSString*)name {
- return [self gtm_pathForFileNamed:name extension:[self gtm_stateExtension]];
-}
-
-- (NSString *)gtm_saveToPathForStateNamed:(NSString*)name {
- return [self gtm_saveToPathForFileNamed:name
- extension:[self gtm_stateExtension]];
-}
-
-// Gives us the encoded unit test state |self|
-//
-// Returns:
-// the encoded state if successful
-// nil if failed
-//
-- (NSDictionary *)gtm_stateRepresentation {
- NSDictionary *dictionary = nil;
- if ([self conformsToProtocol:@protocol(GTMUnitTestingEncoding)]) {
- id<GTMUnitTestingEncoding> encoder = (id<GTMUnitTestingEncoding>)self;
- GTMUnitTestingKeyedCoder *archiver;
- archiver = [[[GTMUnitTestingKeyedCoder alloc] init] autorelease];
- [encoder gtm_unitTestEncodeState:archiver];
- dictionary = [archiver dictionary];
- }
- return dictionary;
-}
-
-// Encodes the state of an object in a manner suitable for comparing
-// against a master state file so we can determine whether the
-// object is in a suitable state. Encode data in the coder in the same
-// manner that you would encode data in any other Keyed NSCoder subclass.
-//
-// Arguments:
-// inCoder - the coder to encode our state into
-- (void)gtm_unitTestEncodeState:(NSCoder*)inCoder {
- // All impls of gtm_unitTestEncodeState
- // should be calling [super gtm_unitTestEncodeState] as their first action.
- _GTMDevAssert([inCoder isKindOfClass:[GTMUnitTestingKeyedCoder class]],
- @"Coder must be of kind GTMUnitTestingKeyedCoder");
-
- // If the object has a delegate, give it a chance to respond
- if ([self respondsToSelector:@selector(delegate)]) {
- id delegate = [self performSelector:@selector(delegate)];
- if (delegate &&
- [delegate respondsToSelector:@selector(gtm_unitTestEncoderWillEncode:inCoder:)]) {
- [delegate gtm_unitTestEncoderWillEncode:self inCoder:inCoder];
- }
- }
-}
-
-@end