summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m428
1 files changed, 428 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m b/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m
new file mode 100644
index 0000000000..162f01e97c
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m
@@ -0,0 +1,428 @@
+//
+// GTMSenTestCase.m
+//
+// Copyright 2007-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 "GTMSenTestCase.h"
+
+#import <unistd.h>
+#if GTM_IPHONE_SIMULATOR
+#import <objc/message.h>
+#endif
+
+#import "GTMObjC2Runtime.h"
+#import "GTMUnitTestDevLog.h"
+
+#if GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
+#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
+ withDescription:(NSString *)formatString, ... {
+
+ 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, ... {
+
+ 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 ? "false" : "true", testDescription];
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
+}
+
++ (NSException *)failureInEqualityBetweenObject:(id)left
+ andObject:(id)right
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+
+ 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, ... {
+
+ 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
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+
+ 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, ... {
+
+ 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
+
+NSString *STComposeString(NSString *formatString, ...) {
+ NSString *reason = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ reason =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+ return reason;
+}
+
+NSString *const SenTestFailureException = @"SenTestFailureException";
+NSString *const SenTestFilenameKey = @"SenTestFilenameKey";
+NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
+
+@interface SenTestCase (SenTestCasePrivate)
+// our method of logging errors
++ (void)printException:(NSException *)exception fromTestName:(NSString *)name;
+@end
+
+@implementation SenTestCase
++ (id)testCaseWithInvocation:(NSInvocation *)anInvocation {
+ return [[[self alloc] initWithInvocation:anInvocation] autorelease];
+}
+
+- (id)initWithInvocation:(NSInvocation *)anInvocation {
+ if ((self = [super init])) {
+ invocation_ = [anInvocation retain];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [invocation_ release];
+ [super dealloc];
+}
+
+- (void)failWithException:(NSException*)exception {
+ [exception raise];
+}
+
+- (void)setUp {
+}
+
+- (void)performTest {
+ @try {
+ [self invokeTest];
+ } @catch (NSException *exception) {
+ [[self class] printException:exception
+ fromTestName:NSStringFromSelector([self selector])];
+ [exception raise];
+ }
+}
+
+- (NSInvocation *)invocation {
+ return invocation_;
+}
+
+- (SEL)selector {
+ return [invocation_ selector];
+}
+
++ (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);
+}
+
+- (void)invokeTest {
+ NSException *e = nil;
+ @try {
+ // Wrap things in autorelease pools because they may
+ // have an STMacro in their dealloc which may get called
+ // when the pool is cleaned up
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ // We don't log exceptions here, instead we let the person that called
+ // this log the exception. This ensures they are only logged once but the
+ // outer layers get the exceptions to report counts, etc.
+ @try {
+ [self setUp];
+ @try {
+ NSInvocation *invocation = [self invocation];
+#if GTM_IPHONE_SIMULATOR
+ // We don't call [invocation invokeWithTarget:self]; because of
+ // Radar 8081169: NSInvalidArgumentException can't be caught
+ // It turns out that on iOS4 (and 3.2) exceptions thrown inside an
+ // [invocation invoke] on the simulator cannot be caught.
+ // http://openradar.appspot.com/8081169
+ objc_msgSend(self, [invocation selector]);
+#else
+ [invocation invokeWithTarget:self];
+#endif
+ } @catch (NSException *exception) {
+ e = [exception retain];
+ }
+ [self tearDown];
+ } @catch (NSException *exception) {
+ e = [exception retain];
+ }
+ [pool release];
+ } @catch (NSException *exception) {
+ e = [exception retain];
+ }
+ if (e) {
+ [e autorelease];
+ [e raise];
+ }
+}
+
+- (void)tearDown {
+}
+
+- (NSString *)description {
+ // This matches the description OCUnit would return to you
+ return [NSString stringWithFormat:@"-[%@ %@]", [self class],
+ NSStringFromSelector([self selector])];
+}
+
+// Used for sorting methods below
+static int MethodSort(id a, id b, void *context) {
+ NSInvocation *invocationA = a;
+ NSInvocation *invocationB = b;
+ const char *nameA = sel_getName([invocationA selector]);
+ const char *nameB = sel_getName([invocationB selector]);
+ return strcmp(nameA, nameB);
+}
+
+
++ (NSArray *)testInvocations {
+ NSMutableArray *invocations = nil;
+ // Need to walk all the way up the parent classes collecting methods (in case
+ // a test is a subclass of another test).
+ Class senTestCaseClass = [SenTestCase class];
+ for (Class currentClass = self;
+ currentClass && (currentClass != senTestCaseClass);
+ currentClass = class_getSuperclass(currentClass)) {
+ unsigned int methodCount;
+ Method *methods = class_copyMethodList(currentClass, &methodCount);
+ if (methods) {
+ // This handles disposing of methods for us even if an exception should fly.
+ [NSData dataWithBytesNoCopy:methods
+ length:sizeof(Method) * methodCount];
+ if (!invocations) {
+ invocations = [NSMutableArray arrayWithCapacity:methodCount];
+ }
+ for (size_t i = 0; i < methodCount; ++i) {
+ Method currMethod = methods[i];
+ SEL sel = method_getName(currMethod);
+ char *returnType = NULL;
+ const char *name = sel_getName(sel);
+ // If it starts with test, takes 2 args (target and sel) and returns
+ // void run it.
+ if (strstr(name, "test") == name) {
+ returnType = method_copyReturnType(currMethod);
+ if (returnType) {
+ // This handles disposing of returnType for us even if an
+ // exception should fly. Length +1 for the terminator, not that
+ // the length really matters here, as we never reference inside
+ // the data block.
+ [NSData dataWithBytesNoCopy:returnType
+ length:strlen(returnType) + 1];
+ }
+ }
+ // TODO: If a test class is a subclass of another, and they reuse the
+ // same selector name (ie-subclass overrides it), this current loop
+ // and test here will cause cause it to get invoked twice. To fix this
+ // the selector would have to be checked against all the ones already
+ // added, so it only gets done once.
+ if (returnType // True if name starts with "test"
+ && strcmp(returnType, @encode(void)) == 0
+ && method_getNumberOfArguments(currMethod) == 2) {
+ NSMethodSignature *sig = [self instanceMethodSignatureForSelector:sel];
+ NSInvocation *invocation
+ = [NSInvocation invocationWithMethodSignature:sig];
+ [invocation setSelector:sel];
+ [invocations addObject:invocation];
+ }
+ }
+ }
+ }
+ // Match SenTestKit and run everything in alphbetical order.
+ [invocations sortUsingFunction:MethodSort context:nil];
+ return invocations;
+}
+
+@end
+
+#endif // GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
+
+@implementation GTMTestCase : SenTestCase
+- (void)invokeTest {
+ NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
+ Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog");
+ if (devLogClass) {
+ [devLogClass performSelector:@selector(enableTracking)];
+ [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)];
+
+ }
+ [super invokeTest];
+ if (devLogClass) {
+ [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)];
+ [devLogClass performSelector:@selector(disableTracking)];
+ }
+ [localPool drain];
+}
+
++ (BOOL)isAbstractTestCase {
+ NSString *name = NSStringFromClass(self);
+ return [name rangeOfString:@"AbstractTest"].location != NSNotFound;
+}
+
++ (NSArray *)testInvocations {
+ NSArray *invocations = nil;
+ if (![self isAbstractTestCase]) {
+ invocations = [super testInvocations];
+ }
+ return invocations;
+}
+
+@end