xref: /aosp_15_r20/external/google-breakpad/src/common/mac/testing/GTMSenTestCase.m (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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