aboutsummaryrefslogtreecommitdiff
path: root/UnitTesting/GTMSenTestCase.m
diff options
context:
space:
mode:
Diffstat (limited to 'UnitTesting/GTMSenTestCase.m')
-rw-r--r--UnitTesting/GTMSenTestCase.m308
1 files changed, 228 insertions, 80 deletions
diff --git a/UnitTesting/GTMSenTestCase.m b/UnitTesting/GTMSenTestCase.m
index 856516c..8d45dab 100644
--- a/UnitTesting/GTMSenTestCase.m
+++ b/UnitTesting/GTMSenTestCase.m
@@ -6,9 +6,9 @@
// 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
@@ -17,38 +17,76 @@
//
#import "GTMSenTestCase.h"
+#import <unistd.h>
-#if GTM_IPHONE_SDK
+#if !GTM_IPHONE_SDK
+#import "GTMGarbageCollection.h"
+#endif // !GTM_IPHONE_SDK
+#if GTM_IPHONE_SDK
#import <stdarg.h>
+@interface NSException (GTMSenTestPrivateAdditions)
++ (NSException *)failureInFile:(NSString *)filename
+ atLine:(int)lineNumber
+ reason:(NSString *)reason;
+@end
+
+@implementation NSException (GTMSenTestPrivateAdditions)
++ (NSException *)failureInFile:(NSString *)filename
+ atLine:(int)lineNumber
+ reason:(NSString *)reason {
+ NSDictionary *userInfo =
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInteger:lineNumber], SenTestLineNumberKey,
+ filename, SenTestFilenameKey,
+ nil];
+
+ return [self exceptionWithName:SenTestFailureException
+ reason:reason
+ userInfo:userInfo];
+}
+@end
+
@implementation NSException (GTMSenTestAdditions)
-+ (NSException *)failureInFile:(NSString *)filename
- atLine:(int)lineNumber
++ (NSException *)failureInFile:(NSString *)filename
+ atLine:(int)lineNumber
withDescription:(NSString *)formatString, ... {
- va_list vl;
- va_start(vl, formatString);
- NSString *reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
- va_end(vl);
- reason = [NSString stringWithFormat:@"%@:%d: error: %@", filename, lineNumber, reason];
- return [NSException exceptionWithName:SenTestFailureException
- reason:reason
- userInfo:nil];
-}
-
-+ (NSException *)failureInCondition:(NSString *)condition
- isTrue:(BOOL)isTrue
- inFile:(NSString *)filename
- atLine:(int)lineNumber
+
+ NSString *testDescription = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ testDescription =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+
+ NSString *reason = testDescription;
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
+}
+
++ (NSException *)failureInCondition:(NSString *)condition
+ isTrue:(BOOL)isTrue
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
withDescription:(NSString *)formatString, ... {
- va_list vl;
- va_start(vl, formatString);
- NSString *reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
- va_end(vl);
- reason = [NSString stringWithFormat:@"condition '%@' is %s : %@",
- condition, isTrue ? "TRUE" : "FALSE", reason];
- return [self failureInFile:filename atLine:lineNumber withDescription:reason];
+
+ NSString *testDescription = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ testDescription =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+
+ NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@",
+ condition, isTrue ? "TRUE" : "FALSE", testDescription];
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
}
+ (NSException *)failureInEqualityBetweenObject:(id)left
@@ -56,55 +94,99 @@
inFile:(NSString *)filename
atLine:(int)lineNumber
withDescription:(NSString *)formatString, ... {
- va_list vl;
- va_start(vl, formatString);
- NSString *reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
- va_end(vl);
- reason = [NSString stringWithFormat:@"%@ != %@ : %@",
- left, right, reason];
- return [self failureInFile:filename atLine:lineNumber withDescription:reason];
-}
-
-+ (NSException *)failureInEqualityBetweenValue:(NSValue *)left
- andValue:(NSValue *)right
- withAccuracy:(NSValue *)accuracy
- inFile:(NSString *)filename
+
+ NSString *testDescription = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ testDescription =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+
+ NSString *reason =
+ [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@",
+ [left description], [right description], testDescription];
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
+}
+
++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left
+ andValue:(NSValue *)right
+ withAccuracy:(NSValue *)accuracy
+ inFile:(NSString *)filename
atLine:(int)lineNumber
withDescription:(NSString *)formatString, ... {
- va_list vl;
- va_start(vl, formatString);
- NSString *reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
- va_end(vl);
- reason = [NSString stringWithFormat:@"%@ != %@ with accuracy %@ : %@",
- left, right, accuracy, reason];
- return [self failureInFile:filename atLine:lineNumber withDescription:reason];
+
+ NSString *testDescription = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ testDescription =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+
+ NSString *reason;
+ if (accuracy) {
+ reason =
+ [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@",
+ left, right, testDescription];
+ } else {
+ reason =
+ [NSString stringWithFormat:@"'%@' should be equal to '%@' +/-'%@'. %@",
+ left, right, accuracy, testDescription];
+ }
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
}
-+ (NSException *)failureInRaise:(NSString *)expression
- inFile:(NSString *)filename
++ (NSException *)failureInRaise:(NSString *)expression
+ inFile:(NSString *)filename
atLine:(int)lineNumber
withDescription:(NSString *)formatString, ... {
- va_list vl;
- va_start(vl, formatString);
- NSString *reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
- va_end(vl);
- reason = [NSString stringWithFormat:@"failure in raise %@ : %@",
- expression, reason];
- return [self failureInFile:filename atLine:lineNumber withDescription:reason];
-}
-
-+ (NSException *)failureInRaise:(NSString *)expression
- exception:(NSException *)exception
- inFile:(NSString *)filename
- atLine:(int)lineNumber
+
+ NSString *testDescription = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ testDescription =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+
+ NSString *reason = [NSString stringWithFormat:@"'%@' should raise. %@",
+ expression, testDescription];
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
+}
+
++ (NSException *)failureInRaise:(NSString *)expression
+ exception:(NSException *)exception
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
withDescription:(NSString *)formatString, ... {
- va_list vl;
- va_start(vl, formatString);
- NSString *reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
- va_end(vl);
- reason = [NSString stringWithFormat:@"failure in raise %@ (%@) : %@",
- expression, exception, reason];
- return [self failureInFile:filename atLine:lineNumber withDescription:reason];
+
+ NSString *testDescription = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ testDescription =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+
+ NSString *reason;
+ if ([[exception name] isEqualToString:SenTestFailureException]) {
+ // it's our exception, assume it has the right description on it.
+ reason = [exception reason];
+ } else {
+ // not one of our exception, use the exceptions reason and our description
+ reason = [NSString stringWithFormat:@"'%@' raised '%@'. %@",
+ expression, [exception reason], testDescription];
+ }
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
}
@end
@@ -114,17 +196,20 @@ NSString *STComposeString(NSString *formatString, ...) {
if (formatString) {
va_list vl;
va_start(vl, formatString);
- reason = [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ reason =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
va_end(vl);
}
return reason;
}
-NSString * const SenTestFailureException = @"SenTestFailureException";
+NSString *const SenTestFailureException = @"SenTestFailureException";
+NSString *const SenTestFilenameKey = @"SenTestFilenameKey";
+NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
-@interface SenTestCase (SenTestCasePrivate)
+@interface SenTestCase (SenTestCasePrivate)
// our method of logging errors
-- (void)printError:(NSString *)error;
++ (void)printException:(NSException *)exception fromTestName:(NSString *)name;
@end
@implementation SenTestCase
@@ -140,17 +225,26 @@ NSString * const SenTestFailureException = @"SenTestFailureException";
@try {
[self invokeTest];
} @catch (NSException *exception) {
- [self printError:[exception reason]];
+ [[self class] printException:exception
+ fromTestName:NSStringFromSelector(sel)];
[exception raise];
}
}
-- (void)printError:(NSString *)error {
- if ([error rangeOfString:@"error:"].location == NSNotFound) {
- fprintf(stderr, "error: %s\n", [error UTF8String]);
- } else {
- fprintf(stderr, "%s\n", [error UTF8String]);
++ (void)printException:(NSException *)exception fromTestName:(NSString *)name {
+ NSDictionary *userInfo = [exception userInfo];
+ NSString *filename = [userInfo objectForKey:SenTestFilenameKey];
+ NSNumber *lineNumber = [userInfo objectForKey:SenTestLineNumberKey];
+ NSString *className = NSStringFromClass([self class]);
+ if ([filename length] == 0) {
+ filename = @"Unknown.m";
}
+ fprintf(stderr, "%s:%ld: error: -[%s %s] : %s\n",
+ [filename UTF8String],
+ (long)[lineNumber integerValue],
+ [className UTF8String],
+ [name UTF8String],
+ [[exception reason] UTF8String]);
fflush(stderr);
}
@@ -189,7 +283,7 @@ NSString * const SenTestFailureException = @"SenTestFailureException";
}
@end
-#endif // GTM_IPHONE_SDK
+#endif // GTM_IPHONE_SDK
@implementation GTMTestCase : SenTestCase
- (void)invokeTest {
@@ -197,7 +291,7 @@ NSString * const SenTestFailureException = @"SenTestFailureException";
if (devLogClass) {
[devLogClass performSelector:@selector(enableTracking)];
[devLogClass performSelector:@selector(verifyNoMoreLogsExpected)];
-
+
}
[super invokeTest];
if (devLogClass) {
@@ -206,3 +300,57 @@ NSString * const SenTestFailureException = @"SenTestFailureException";
}
}
@end
+
+// Leak detection
+#if !GTM_IPHONE_DEVICE
+// Don't want to get leaks on the iPhone Device as the device doesn't
+// have 'leaks'. The simulator does though.
+
+static void _GTMRunLeaks(void) {
+ // This is an atexit handler. It runs leaks for us to check if we are
+ // leaking anything in our tests.
+ const char* cExclusionsEnv = getenv("GTM_LEAKS_SYMBOLS_TO_IGNORE");
+ NSMutableString *exclusions = [NSMutableString string];
+ if (cExclusionsEnv) {
+ NSString *exclusionsEnv = [NSString stringWithUTF8String:cExclusionsEnv];
+ NSArray *exclusionsArray = [exclusionsEnv componentsSeparatedByString:@","];
+ NSEnumerator *exclusionsEnum = [exclusionsArray objectEnumerator];
+ NSString *exclusion;
+ NSCharacterSet *wcSet = [NSCharacterSet whitespaceCharacterSet];
+ while ((exclusion = [exclusionsEnum nextObject])) {
+ exclusion = [exclusion stringByTrimmingCharactersInSet:wcSet];
+ [exclusions appendFormat:@"-exclude \"%@\" ", exclusion];
+ }
+ }
+ NSString *string
+ = [NSString stringWithFormat:@"/usr/bin/leaks %@%d"
+ @"| /usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'",
+ exclusions, getpid()];
+ int ret = system([string UTF8String]);
+ if (ret) {
+ fprintf(stderr, "%s:%d: Error: Unable to run leaks. 'system' returned: %d",
+ __FILE__, __LINE__, ret);
+ fflush(stderr);
+ }
+}
+
+static __attribute__((constructor)) void _GTMInstallLeaks(void) {
+ BOOL checkLeaks = YES;
+#if !GTM_IPHONE_SDK
+ checkLeaks = GTMIsGarbageCollectionEnabled() ? NO : YES;
+#endif // !GTM_IPHONE_SDK
+ if (checkLeaks) {
+ checkLeaks = getenv("GTM_ENABLE_LEAKS") ? YES : NO;
+ if (checkLeaks) {
+ if (checkLeaks) {
+ fprintf(stderr, "Leak Checking Enabled\n");
+ fflush(stderr);
+ _GTMDevAssert(atexit(&_GTMRunLeaks) == 0,
+ @"Unable to install _GTMRunLeaks as an atexit handler (%d)",
+ errno);
+ }
+ }
+ }
+}
+
+#endif // !GTM_IPHONE_DEVICE