1*9712c20fSFrederick Mayle// 2*9712c20fSFrederick Mayle// GTMSenTestCase.m 3*9712c20fSFrederick Mayle// 4*9712c20fSFrederick Mayle// Copyright 2007-2008 Google LLC 5*9712c20fSFrederick Mayle// 6*9712c20fSFrederick Mayle// Licensed under the Apache License, Version 2.0 (the "License"); you may not 7*9712c20fSFrederick Mayle// use this file except in compliance with the License. You may obtain a copy 8*9712c20fSFrederick Mayle// of the License at 9*9712c20fSFrederick Mayle// 10*9712c20fSFrederick Mayle// http://www.apache.org/licenses/LICENSE-2.0 11*9712c20fSFrederick Mayle// 12*9712c20fSFrederick Mayle// Unless required by applicable law or agreed to in writing, software 13*9712c20fSFrederick Mayle// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14*9712c20fSFrederick Mayle// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15*9712c20fSFrederick Mayle// License for the specific language governing permissions and limitations under 16*9712c20fSFrederick Mayle// the License. 17*9712c20fSFrederick Mayle// 18*9712c20fSFrederick Mayle 19*9712c20fSFrederick Mayle#import "GTMSenTestCase.h" 20*9712c20fSFrederick Mayle 21*9712c20fSFrederick Mayle#import <unistd.h> 22*9712c20fSFrederick Mayle#if GTM_IPHONE_SIMULATOR 23*9712c20fSFrederick Mayle#import <objc/message.h> 24*9712c20fSFrederick Mayle#endif 25*9712c20fSFrederick Mayle 26*9712c20fSFrederick Mayle#import "GTMObjC2Runtime.h" 27*9712c20fSFrederick Mayle#import "GTMUnitTestDevLog.h" 28*9712c20fSFrederick Mayle 29*9712c20fSFrederick Mayle#if GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST 30*9712c20fSFrederick Mayle#import <stdarg.h> 31*9712c20fSFrederick Mayle 32*9712c20fSFrederick Mayle@interface NSException (GTMSenTestPrivateAdditions) 33*9712c20fSFrederick Mayle+ (NSException *)failureInFile:(NSString *)filename 34*9712c20fSFrederick Mayle atLine:(int)lineNumber 35*9712c20fSFrederick Mayle reason:(NSString *)reason; 36*9712c20fSFrederick Mayle@end 37*9712c20fSFrederick Mayle 38*9712c20fSFrederick Mayle@implementation NSException (GTMSenTestPrivateAdditions) 39*9712c20fSFrederick Mayle+ (NSException *)failureInFile:(NSString *)filename 40*9712c20fSFrederick Mayle atLine:(int)lineNumber 41*9712c20fSFrederick Mayle reason:(NSString *)reason { 42*9712c20fSFrederick Mayle NSDictionary *userInfo = 43*9712c20fSFrederick Mayle [NSDictionary dictionaryWithObjectsAndKeys: 44*9712c20fSFrederick Mayle [NSNumber numberWithInteger:lineNumber], SenTestLineNumberKey, 45*9712c20fSFrederick Mayle filename, SenTestFilenameKey, 46*9712c20fSFrederick Mayle nil]; 47*9712c20fSFrederick Mayle 48*9712c20fSFrederick Mayle return [self exceptionWithName:SenTestFailureException 49*9712c20fSFrederick Mayle reason:reason 50*9712c20fSFrederick Mayle userInfo:userInfo]; 51*9712c20fSFrederick Mayle} 52*9712c20fSFrederick Mayle@end 53*9712c20fSFrederick Mayle 54*9712c20fSFrederick Mayle@implementation NSException (GTMSenTestAdditions) 55*9712c20fSFrederick Mayle 56*9712c20fSFrederick Mayle+ (NSException *)failureInFile:(NSString *)filename 57*9712c20fSFrederick Mayle atLine:(int)lineNumber 58*9712c20fSFrederick Mayle withDescription:(NSString *)formatString, ... { 59*9712c20fSFrederick Mayle 60*9712c20fSFrederick Mayle NSString *testDescription = @""; 61*9712c20fSFrederick Mayle if (formatString) { 62*9712c20fSFrederick Mayle va_list vl; 63*9712c20fSFrederick Mayle va_start(vl, formatString); 64*9712c20fSFrederick Mayle testDescription = 65*9712c20fSFrederick Mayle [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 66*9712c20fSFrederick Mayle va_end(vl); 67*9712c20fSFrederick Mayle } 68*9712c20fSFrederick Mayle 69*9712c20fSFrederick Mayle NSString *reason = testDescription; 70*9712c20fSFrederick Mayle 71*9712c20fSFrederick Mayle return [self failureInFile:filename atLine:lineNumber reason:reason]; 72*9712c20fSFrederick Mayle} 73*9712c20fSFrederick Mayle 74*9712c20fSFrederick Mayle+ (NSException *)failureInCondition:(NSString *)condition 75*9712c20fSFrederick Mayle isTrue:(BOOL)isTrue 76*9712c20fSFrederick Mayle inFile:(NSString *)filename 77*9712c20fSFrederick Mayle atLine:(int)lineNumber 78*9712c20fSFrederick Mayle withDescription:(NSString *)formatString, ... { 79*9712c20fSFrederick Mayle 80*9712c20fSFrederick Mayle NSString *testDescription = @""; 81*9712c20fSFrederick Mayle if (formatString) { 82*9712c20fSFrederick Mayle va_list vl; 83*9712c20fSFrederick Mayle va_start(vl, formatString); 84*9712c20fSFrederick Mayle testDescription = 85*9712c20fSFrederick Mayle [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 86*9712c20fSFrederick Mayle va_end(vl); 87*9712c20fSFrederick Mayle } 88*9712c20fSFrederick Mayle 89*9712c20fSFrederick Mayle NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@", 90*9712c20fSFrederick Mayle condition, isTrue ? "false" : "true", testDescription]; 91*9712c20fSFrederick Mayle 92*9712c20fSFrederick Mayle return [self failureInFile:filename atLine:lineNumber reason:reason]; 93*9712c20fSFrederick Mayle} 94*9712c20fSFrederick Mayle 95*9712c20fSFrederick Mayle+ (NSException *)failureInEqualityBetweenObject:(id)left 96*9712c20fSFrederick Mayle andObject:(id)right 97*9712c20fSFrederick Mayle inFile:(NSString *)filename 98*9712c20fSFrederick Mayle atLine:(int)lineNumber 99*9712c20fSFrederick Mayle withDescription:(NSString *)formatString, ... { 100*9712c20fSFrederick Mayle 101*9712c20fSFrederick Mayle NSString *testDescription = @""; 102*9712c20fSFrederick Mayle if (formatString) { 103*9712c20fSFrederick Mayle va_list vl; 104*9712c20fSFrederick Mayle va_start(vl, formatString); 105*9712c20fSFrederick Mayle testDescription = 106*9712c20fSFrederick Mayle [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 107*9712c20fSFrederick Mayle va_end(vl); 108*9712c20fSFrederick Mayle } 109*9712c20fSFrederick Mayle 110*9712c20fSFrederick Mayle NSString *reason = 111*9712c20fSFrederick Mayle [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", 112*9712c20fSFrederick Mayle [left description], [right description], testDescription]; 113*9712c20fSFrederick Mayle 114*9712c20fSFrederick Mayle return [self failureInFile:filename atLine:lineNumber reason:reason]; 115*9712c20fSFrederick Mayle} 116*9712c20fSFrederick Mayle 117*9712c20fSFrederick Mayle+ (NSException *)failureInEqualityBetweenValue:(NSValue *)left 118*9712c20fSFrederick Mayle andValue:(NSValue *)right 119*9712c20fSFrederick Mayle withAccuracy:(NSValue *)accuracy 120*9712c20fSFrederick Mayle inFile:(NSString *)filename 121*9712c20fSFrederick Mayle atLine:(int)lineNumber 122*9712c20fSFrederick Mayle withDescription:(NSString *)formatString, ... { 123*9712c20fSFrederick Mayle 124*9712c20fSFrederick Mayle NSString *testDescription = @""; 125*9712c20fSFrederick Mayle if (formatString) { 126*9712c20fSFrederick Mayle va_list vl; 127*9712c20fSFrederick Mayle va_start(vl, formatString); 128*9712c20fSFrederick Mayle testDescription = 129*9712c20fSFrederick Mayle [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 130*9712c20fSFrederick Mayle va_end(vl); 131*9712c20fSFrederick Mayle } 132*9712c20fSFrederick Mayle 133*9712c20fSFrederick Mayle NSString *reason; 134*9712c20fSFrederick Mayle if (accuracy) { 135*9712c20fSFrederick Mayle reason = 136*9712c20fSFrederick Mayle [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", 137*9712c20fSFrederick Mayle left, right, testDescription]; 138*9712c20fSFrederick Mayle } else { 139*9712c20fSFrederick Mayle reason = 140*9712c20fSFrederick Mayle [NSString stringWithFormat:@"'%@' should be equal to '%@' +/-'%@'. %@", 141*9712c20fSFrederick Mayle left, right, accuracy, testDescription]; 142*9712c20fSFrederick Mayle } 143*9712c20fSFrederick Mayle 144*9712c20fSFrederick Mayle return [self failureInFile:filename atLine:lineNumber reason:reason]; 145*9712c20fSFrederick Mayle} 146*9712c20fSFrederick Mayle 147*9712c20fSFrederick Mayle+ (NSException *)failureInRaise:(NSString *)expression 148*9712c20fSFrederick Mayle inFile:(NSString *)filename 149*9712c20fSFrederick Mayle atLine:(int)lineNumber 150*9712c20fSFrederick Mayle withDescription:(NSString *)formatString, ... { 151*9712c20fSFrederick Mayle 152*9712c20fSFrederick Mayle NSString *testDescription = @""; 153*9712c20fSFrederick Mayle if (formatString) { 154*9712c20fSFrederick Mayle va_list vl; 155*9712c20fSFrederick Mayle va_start(vl, formatString); 156*9712c20fSFrederick Mayle testDescription = 157*9712c20fSFrederick Mayle [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 158*9712c20fSFrederick Mayle va_end(vl); 159*9712c20fSFrederick Mayle } 160*9712c20fSFrederick Mayle 161*9712c20fSFrederick Mayle NSString *reason = [NSString stringWithFormat:@"'%@' should raise. %@", 162*9712c20fSFrederick Mayle expression, testDescription]; 163*9712c20fSFrederick Mayle 164*9712c20fSFrederick Mayle return [self failureInFile:filename atLine:lineNumber reason:reason]; 165*9712c20fSFrederick Mayle} 166*9712c20fSFrederick Mayle 167*9712c20fSFrederick Mayle+ (NSException *)failureInRaise:(NSString *)expression 168*9712c20fSFrederick Mayle exception:(NSException *)exception 169*9712c20fSFrederick Mayle inFile:(NSString *)filename 170*9712c20fSFrederick Mayle atLine:(int)lineNumber 171*9712c20fSFrederick Mayle withDescription:(NSString *)formatString, ... { 172*9712c20fSFrederick Mayle 173*9712c20fSFrederick Mayle NSString *testDescription = @""; 174*9712c20fSFrederick Mayle if (formatString) { 175*9712c20fSFrederick Mayle va_list vl; 176*9712c20fSFrederick Mayle va_start(vl, formatString); 177*9712c20fSFrederick Mayle testDescription = 178*9712c20fSFrederick Mayle [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 179*9712c20fSFrederick Mayle va_end(vl); 180*9712c20fSFrederick Mayle } 181*9712c20fSFrederick Mayle 182*9712c20fSFrederick Mayle NSString *reason; 183*9712c20fSFrederick Mayle if ([[exception name] isEqualToString:SenTestFailureException]) { 184*9712c20fSFrederick Mayle // it's our exception, assume it has the right description on it. 185*9712c20fSFrederick Mayle reason = [exception reason]; 186*9712c20fSFrederick Mayle } else { 187*9712c20fSFrederick Mayle // not one of our exception, use the exceptions reason and our description 188*9712c20fSFrederick Mayle reason = [NSString stringWithFormat:@"'%@' raised '%@'. %@", 189*9712c20fSFrederick Mayle expression, [exception reason], testDescription]; 190*9712c20fSFrederick Mayle } 191*9712c20fSFrederick Mayle 192*9712c20fSFrederick Mayle return [self failureInFile:filename atLine:lineNumber reason:reason]; 193*9712c20fSFrederick Mayle} 194*9712c20fSFrederick Mayle 195*9712c20fSFrederick Mayle@end 196*9712c20fSFrederick Mayle 197*9712c20fSFrederick MayleNSString *STComposeString(NSString *formatString, ...) { 198*9712c20fSFrederick Mayle NSString *reason = @""; 199*9712c20fSFrederick Mayle if (formatString) { 200*9712c20fSFrederick Mayle va_list vl; 201*9712c20fSFrederick Mayle va_start(vl, formatString); 202*9712c20fSFrederick Mayle reason = 203*9712c20fSFrederick Mayle [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 204*9712c20fSFrederick Mayle va_end(vl); 205*9712c20fSFrederick Mayle } 206*9712c20fSFrederick Mayle return reason; 207*9712c20fSFrederick Mayle} 208*9712c20fSFrederick Mayle 209*9712c20fSFrederick MayleNSString *const SenTestFailureException = @"SenTestFailureException"; 210*9712c20fSFrederick MayleNSString *const SenTestFilenameKey = @"SenTestFilenameKey"; 211*9712c20fSFrederick MayleNSString *const SenTestLineNumberKey = @"SenTestLineNumberKey"; 212*9712c20fSFrederick Mayle 213*9712c20fSFrederick Mayle@interface SenTestCase (SenTestCasePrivate) 214*9712c20fSFrederick Mayle// our method of logging errors 215*9712c20fSFrederick Mayle+ (void)printException:(NSException *)exception fromTestName:(NSString *)name; 216*9712c20fSFrederick Mayle@end 217*9712c20fSFrederick Mayle 218*9712c20fSFrederick Mayle@implementation SenTestCase 219*9712c20fSFrederick Mayle+ (id)testCaseWithInvocation:(NSInvocation *)anInvocation { 220*9712c20fSFrederick Mayle return [[[self alloc] initWithInvocation:anInvocation] autorelease]; 221*9712c20fSFrederick Mayle} 222*9712c20fSFrederick Mayle 223*9712c20fSFrederick Mayle- (id)initWithInvocation:(NSInvocation *)anInvocation { 224*9712c20fSFrederick Mayle if ((self = [super init])) { 225*9712c20fSFrederick Mayle invocation_ = [anInvocation retain]; 226*9712c20fSFrederick Mayle } 227*9712c20fSFrederick Mayle return self; 228*9712c20fSFrederick Mayle} 229*9712c20fSFrederick Mayle 230*9712c20fSFrederick Mayle- (void)dealloc { 231*9712c20fSFrederick Mayle [invocation_ release]; 232*9712c20fSFrederick Mayle [super dealloc]; 233*9712c20fSFrederick Mayle} 234*9712c20fSFrederick Mayle 235*9712c20fSFrederick Mayle- (void)failWithException:(NSException*)exception { 236*9712c20fSFrederick Mayle [exception raise]; 237*9712c20fSFrederick Mayle} 238*9712c20fSFrederick Mayle 239*9712c20fSFrederick Mayle- (void)setUp { 240*9712c20fSFrederick Mayle} 241*9712c20fSFrederick Mayle 242*9712c20fSFrederick Mayle- (void)performTest { 243*9712c20fSFrederick Mayle @try { 244*9712c20fSFrederick Mayle [self invokeTest]; 245*9712c20fSFrederick Mayle } @catch (NSException *exception) { 246*9712c20fSFrederick Mayle [[self class] printException:exception 247*9712c20fSFrederick Mayle fromTestName:NSStringFromSelector([self selector])]; 248*9712c20fSFrederick Mayle [exception raise]; 249*9712c20fSFrederick Mayle } 250*9712c20fSFrederick Mayle} 251*9712c20fSFrederick Mayle 252*9712c20fSFrederick Mayle- (NSInvocation *)invocation { 253*9712c20fSFrederick Mayle return invocation_; 254*9712c20fSFrederick Mayle} 255*9712c20fSFrederick Mayle 256*9712c20fSFrederick Mayle- (SEL)selector { 257*9712c20fSFrederick Mayle return [invocation_ selector]; 258*9712c20fSFrederick Mayle} 259*9712c20fSFrederick Mayle 260*9712c20fSFrederick Mayle+ (void)printException:(NSException *)exception fromTestName:(NSString *)name { 261*9712c20fSFrederick Mayle NSDictionary *userInfo = [exception userInfo]; 262*9712c20fSFrederick Mayle NSString *filename = [userInfo objectForKey:SenTestFilenameKey]; 263*9712c20fSFrederick Mayle NSNumber *lineNumber = [userInfo objectForKey:SenTestLineNumberKey]; 264*9712c20fSFrederick Mayle NSString *className = NSStringFromClass([self class]); 265*9712c20fSFrederick Mayle if ([filename length] == 0) { 266*9712c20fSFrederick Mayle filename = @"Unknown.m"; 267*9712c20fSFrederick Mayle } 268*9712c20fSFrederick Mayle fprintf(stderr, "%s:%ld: error: -[%s %s] : %s\n", 269*9712c20fSFrederick Mayle [filename UTF8String], 270*9712c20fSFrederick Mayle (long)[lineNumber integerValue], 271*9712c20fSFrederick Mayle [className UTF8String], 272*9712c20fSFrederick Mayle [name UTF8String], 273*9712c20fSFrederick Mayle [[exception reason] UTF8String]); 274*9712c20fSFrederick Mayle fflush(stderr); 275*9712c20fSFrederick Mayle} 276*9712c20fSFrederick Mayle 277*9712c20fSFrederick Mayle- (void)invokeTest { 278*9712c20fSFrederick Mayle NSException *e = nil; 279*9712c20fSFrederick Mayle @try { 280*9712c20fSFrederick Mayle // Wrap things in autorelease pools because they may 281*9712c20fSFrederick Mayle // have an STMacro in their dealloc which may get called 282*9712c20fSFrederick Mayle // when the pool is cleaned up 283*9712c20fSFrederick Mayle NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 284*9712c20fSFrederick Mayle // We don't log exceptions here, instead we let the person that called 285*9712c20fSFrederick Mayle // this log the exception. This ensures they are only logged once but the 286*9712c20fSFrederick Mayle // outer layers get the exceptions to report counts, etc. 287*9712c20fSFrederick Mayle @try { 288*9712c20fSFrederick Mayle [self setUp]; 289*9712c20fSFrederick Mayle @try { 290*9712c20fSFrederick Mayle NSInvocation *invocation = [self invocation]; 291*9712c20fSFrederick Mayle#if GTM_IPHONE_SIMULATOR 292*9712c20fSFrederick Mayle // We don't call [invocation invokeWithTarget:self]; because of 293*9712c20fSFrederick Mayle // Radar 8081169: NSInvalidArgumentException can't be caught 294*9712c20fSFrederick Mayle // It turns out that on iOS4 (and 3.2) exceptions thrown inside an 295*9712c20fSFrederick Mayle // [invocation invoke] on the simulator cannot be caught. 296*9712c20fSFrederick Mayle // http://openradar.appspot.com/8081169 297*9712c20fSFrederick Mayle objc_msgSend(self, [invocation selector]); 298*9712c20fSFrederick Mayle#else 299*9712c20fSFrederick Mayle [invocation invokeWithTarget:self]; 300*9712c20fSFrederick Mayle#endif 301*9712c20fSFrederick Mayle } @catch (NSException *exception) { 302*9712c20fSFrederick Mayle e = [exception retain]; 303*9712c20fSFrederick Mayle } 304*9712c20fSFrederick Mayle [self tearDown]; 305*9712c20fSFrederick Mayle } @catch (NSException *exception) { 306*9712c20fSFrederick Mayle e = [exception retain]; 307*9712c20fSFrederick Mayle } 308*9712c20fSFrederick Mayle [pool release]; 309*9712c20fSFrederick Mayle } @catch (NSException *exception) { 310*9712c20fSFrederick Mayle e = [exception retain]; 311*9712c20fSFrederick Mayle } 312*9712c20fSFrederick Mayle if (e) { 313*9712c20fSFrederick Mayle [e autorelease]; 314*9712c20fSFrederick Mayle [e raise]; 315*9712c20fSFrederick Mayle } 316*9712c20fSFrederick Mayle} 317*9712c20fSFrederick Mayle 318*9712c20fSFrederick Mayle- (void)tearDown { 319*9712c20fSFrederick Mayle} 320*9712c20fSFrederick Mayle 321*9712c20fSFrederick Mayle- (NSString *)description { 322*9712c20fSFrederick Mayle // This matches the description OCUnit would return to you 323*9712c20fSFrederick Mayle return [NSString stringWithFormat:@"-[%@ %@]", [self class], 324*9712c20fSFrederick Mayle NSStringFromSelector([self selector])]; 325*9712c20fSFrederick Mayle} 326*9712c20fSFrederick Mayle 327*9712c20fSFrederick Mayle// Used for sorting methods below 328*9712c20fSFrederick Maylestatic int MethodSort(id a, id b, void *context) { 329*9712c20fSFrederick Mayle NSInvocation *invocationA = a; 330*9712c20fSFrederick Mayle NSInvocation *invocationB = b; 331*9712c20fSFrederick Mayle const char *nameA = sel_getName([invocationA selector]); 332*9712c20fSFrederick Mayle const char *nameB = sel_getName([invocationB selector]); 333*9712c20fSFrederick Mayle return strcmp(nameA, nameB); 334*9712c20fSFrederick Mayle} 335*9712c20fSFrederick Mayle 336*9712c20fSFrederick Mayle 337*9712c20fSFrederick Mayle+ (NSArray *)testInvocations { 338*9712c20fSFrederick Mayle NSMutableArray *invocations = nil; 339*9712c20fSFrederick Mayle // Need to walk all the way up the parent classes collecting methods (in case 340*9712c20fSFrederick Mayle // a test is a subclass of another test). 341*9712c20fSFrederick Mayle Class senTestCaseClass = [SenTestCase class]; 342*9712c20fSFrederick Mayle for (Class currentClass = self; 343*9712c20fSFrederick Mayle currentClass && (currentClass != senTestCaseClass); 344*9712c20fSFrederick Mayle currentClass = class_getSuperclass(currentClass)) { 345*9712c20fSFrederick Mayle unsigned int methodCount; 346*9712c20fSFrederick Mayle Method *methods = class_copyMethodList(currentClass, &methodCount); 347*9712c20fSFrederick Mayle if (methods) { 348*9712c20fSFrederick Mayle // This handles disposing of methods for us even if an exception should fly. 349*9712c20fSFrederick Mayle [NSData dataWithBytesNoCopy:methods 350*9712c20fSFrederick Mayle length:sizeof(Method) * methodCount]; 351*9712c20fSFrederick Mayle if (!invocations) { 352*9712c20fSFrederick Mayle invocations = [NSMutableArray arrayWithCapacity:methodCount]; 353*9712c20fSFrederick Mayle } 354*9712c20fSFrederick Mayle for (size_t i = 0; i < methodCount; ++i) { 355*9712c20fSFrederick Mayle Method currMethod = methods[i]; 356*9712c20fSFrederick Mayle SEL sel = method_getName(currMethod); 357*9712c20fSFrederick Mayle char *returnType = NULL; 358*9712c20fSFrederick Mayle const char *name = sel_getName(sel); 359*9712c20fSFrederick Mayle // If it starts with test, takes 2 args (target and sel) and returns 360*9712c20fSFrederick Mayle // void run it. 361*9712c20fSFrederick Mayle if (strstr(name, "test") == name) { 362*9712c20fSFrederick Mayle returnType = method_copyReturnType(currMethod); 363*9712c20fSFrederick Mayle if (returnType) { 364*9712c20fSFrederick Mayle // This handles disposing of returnType for us even if an 365*9712c20fSFrederick Mayle // exception should fly. Length +1 for the terminator, not that 366*9712c20fSFrederick Mayle // the length really matters here, as we never reference inside 367*9712c20fSFrederick Mayle // the data block. 368*9712c20fSFrederick Mayle [NSData dataWithBytesNoCopy:returnType 369*9712c20fSFrederick Mayle length:strlen(returnType) + 1]; 370*9712c20fSFrederick Mayle } 371*9712c20fSFrederick Mayle } 372*9712c20fSFrederick Mayle // TODO: If a test class is a subclass of another, and they reuse the 373*9712c20fSFrederick Mayle // same selector name (ie-subclass overrides it), this current loop 374*9712c20fSFrederick Mayle // and test here will cause cause it to get invoked twice. To fix this 375*9712c20fSFrederick Mayle // the selector would have to be checked against all the ones already 376*9712c20fSFrederick Mayle // added, so it only gets done once. 377*9712c20fSFrederick Mayle if (returnType // True if name starts with "test" 378*9712c20fSFrederick Mayle && strcmp(returnType, @encode(void)) == 0 379*9712c20fSFrederick Mayle && method_getNumberOfArguments(currMethod) == 2) { 380*9712c20fSFrederick Mayle NSMethodSignature *sig = [self instanceMethodSignatureForSelector:sel]; 381*9712c20fSFrederick Mayle NSInvocation *invocation 382*9712c20fSFrederick Mayle = [NSInvocation invocationWithMethodSignature:sig]; 383*9712c20fSFrederick Mayle [invocation setSelector:sel]; 384*9712c20fSFrederick Mayle [invocations addObject:invocation]; 385*9712c20fSFrederick Mayle } 386*9712c20fSFrederick Mayle } 387*9712c20fSFrederick Mayle } 388*9712c20fSFrederick Mayle } 389*9712c20fSFrederick Mayle // Match SenTestKit and run everything in alphbetical order. 390*9712c20fSFrederick Mayle [invocations sortUsingFunction:MethodSort context:nil]; 391*9712c20fSFrederick Mayle return invocations; 392*9712c20fSFrederick Mayle} 393*9712c20fSFrederick Mayle 394*9712c20fSFrederick Mayle@end 395*9712c20fSFrederick Mayle 396*9712c20fSFrederick Mayle#endif // GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST 397*9712c20fSFrederick Mayle 398*9712c20fSFrederick Mayle@implementation GTMTestCase : SenTestCase 399*9712c20fSFrederick Mayle- (void)invokeTest { 400*9712c20fSFrederick Mayle NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; 401*9712c20fSFrederick Mayle Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog"); 402*9712c20fSFrederick Mayle if (devLogClass) { 403*9712c20fSFrederick Mayle [devLogClass performSelector:@selector(enableTracking)]; 404*9712c20fSFrederick Mayle [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; 405*9712c20fSFrederick Mayle 406*9712c20fSFrederick Mayle } 407*9712c20fSFrederick Mayle [super invokeTest]; 408*9712c20fSFrederick Mayle if (devLogClass) { 409*9712c20fSFrederick Mayle [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; 410*9712c20fSFrederick Mayle [devLogClass performSelector:@selector(disableTracking)]; 411*9712c20fSFrederick Mayle } 412*9712c20fSFrederick Mayle [localPool drain]; 413*9712c20fSFrederick Mayle} 414*9712c20fSFrederick Mayle 415*9712c20fSFrederick Mayle+ (BOOL)isAbstractTestCase { 416*9712c20fSFrederick Mayle NSString *name = NSStringFromClass(self); 417*9712c20fSFrederick Mayle return [name rangeOfString:@"AbstractTest"].location != NSNotFound; 418*9712c20fSFrederick Mayle} 419*9712c20fSFrederick Mayle 420*9712c20fSFrederick Mayle+ (NSArray *)testInvocations { 421*9712c20fSFrederick Mayle NSArray *invocations = nil; 422*9712c20fSFrederick Mayle if (![self isAbstractTestCase]) { 423*9712c20fSFrederick Mayle invocations = [super testInvocations]; 424*9712c20fSFrederick Mayle } 425*9712c20fSFrederick Mayle return invocations; 426*9712c20fSFrederick Mayle} 427*9712c20fSFrederick Mayle 428*9712c20fSFrederick Mayle@end 429