main
1//
2// Licensed under the terms in License.txt
3//
4// Copyright 2010 Allen Ding. All rights reserved.
5//
6
7#import "KWTestCase.h"
8#import <objc/runtime.h>
9#import "KWDeviceInfo.h"
10#import "KWExistVerifier.h"
11#import "KWFailure.h"
12#import "KWIntercept.h"
13#import "KWMatcherFactory.h"
14#import "KWMatchVerifier.h"
15#import "KWAsyncVerifier.h"
16#import "KWObjCUtilities.h"
17#import "KWStringUtilities.h"
18#import "KWVerifying.h"
19#import "NSMethodSignature+KiwiAdditions.h"
20
21@interface KWTestCase()
22
23#pragma mark -
24#pragma mark Properties
25
26@property (nonatomic, readonly) KWMatcherFactory *matcherFactory;
27@property (nonatomic, readonly) NSMutableArray *verifiers;
28@property (nonatomic, readonly) NSMutableArray *failures;
29@end
30
31@implementation KWTestCase
32
33#pragma mark -
34#pragma mark Initializing
35
36// Initializer used by the SenTestingKit test suite to initialize a test case
37// for each test invocation returned in +testInvocations.
38- (id)initWithInvocation:(NSInvocation *)anInvocation {
39 if ((self = [super initWithInvocation:anInvocation])) {
40 matcherFactory = [[KWMatcherFactory alloc] init];
41 verifiers = [[NSMutableArray alloc] init];
42 failures = [[NSMutableArray alloc] init];
43 }
44
45 return self;
46}
47
48- (void)dealloc {
49 [matcherFactory release];
50 [verifiers release];
51 [failures release];
52 [super dealloc];
53}
54
55#pragma mark -
56#pragma mark Properties
57
58@synthesize verifiers;
59@synthesize matcherFactory;
60@synthesize failures;
61
62#pragma mark -
63#pragma mark Configuring Example Environments
64
65- (void)setUpExampleEnvironment {
66 [self.matcherFactory registerMatcherClassesWithNamespacePrefix:@"KW"];
67}
68
69- (void)tearDownExampleEnvironment {
70 KWClearStubsAndSpies();
71}
72
73#pragma mark -
74#pragma mark Marking Pending Examples
75
76- (void)markPendingWithCallSite:(KWCallSite *)aCallSite {
77 KWFailure *failure = [KWFailure failureWithCallSite:aCallSite format:@"PENDING"];
78 [self reportFailure:failure];
79}
80
81- (void)markPendingWithCallSite:(KWCallSite *)aCallSite :(NSString *)aDescription {
82 KWFailure *failure = [KWFailure failureWithCallSite:aCallSite format:@"PENDING (%@)", aDescription];
83 [self reportFailure:failure];
84}
85
86#pragma mark -
87#pragma mark Adding Verifiers
88
89- (id)addVerifier:(id<KWVerifying>)aVerifier {
90 if (![self.verifiers containsObject:aVerifier])
91 [self.verifiers addObject:aVerifier];
92
93 return aVerifier;
94}
95
96- (id)addExistVerifierWithExpectationType:(KWExpectationType)anExpectationType callSite:(KWCallSite *)aCallSite {
97 id verifier = [KWExistVerifier existVerifierWithExpectationType:anExpectationType callSite:aCallSite reporter:self];
98 [self.verifiers addObject:verifier];
99 return verifier;
100}
101
102- (id)addMatchVerifierWithExpectationType:(KWExpectationType)anExpectationType callSite:(KWCallSite *)aCallSite {
103 id verifier = [KWMatchVerifier matchVerifierWithExpectationType:anExpectationType callSite:aCallSite matcherFactory:self.matcherFactory reporter:self];
104 [self.verifiers addObject:verifier];
105 return verifier;
106}
107
108- (id)addAsyncVerifierWithExpectationType:(KWExpectationType)anExpectationType callSite:(KWCallSite *)aCallSite timeout:(NSInteger)timeout {
109 id verifier = [KWAsyncVerifier asyncVerifierWithExpectationType:anExpectationType callSite:aCallSite matcherFactory:self.matcherFactory reporter:self probeTimeout:timeout];
110 [self.verifiers addObject:verifier];
111 return verifier;
112}
113
114#pragma mark -
115#pragma mark Reporting Failures
116
117+ (KWFailure *)tidiedFailureWithFailure:(KWFailure *)aFailure {
118 if ([KWDeviceInfo isSimulator]) {
119 // \uff1a is the unicode for a fill width colon, as opposed to a
120 // regular :. This escape is performed so that Xcode doesn't truncate
121 // the error in the build results window, which is nice for build
122 // tests.
123 NSString *escapedMessage = [aFailure.message stringByReplacingOccurrencesOfString:@":" withString:@"\uff1a"];
124 return [KWFailure failureWithCallSite:aFailure.callSite message:escapedMessage];
125 } else {
126 return aFailure;
127 }
128}
129
130- (void)reportFailure:(KWFailure *)aFailure; {
131 [self.failures addObject:aFailure];
132 KWFailure *tidiedFailure = [[self class] tidiedFailureWithFailure:aFailure];
133 [self failWithException:[tidiedFailure exceptionValue]];
134}
135
136#pragma mark -
137#pragma mark Getting Invocations
138
139// Called by the SenTestingKit test suite to get an array of invocations that
140// should be run on instances of test cases.
141+ (NSArray *)testInvocations {
142 // Examples are methods returning void with no parameters in the receiver
143 // that begin with "it" followed by an uppercase word.
144 NSMutableArray *exampleInvocations = [[[NSMutableArray alloc] init] autorelease];
145 unsigned int methodCount = 0;
146 Method *methods = class_copyMethodList([self class], &methodCount);
147
148 for (unsigned int i = 0; i < methodCount; i++) {
149 SEL selector = method_getName(methods[i]);
150 NSString *selectorString = NSStringFromSelector(selector);
151
152 if (KWStringHasStrictWordPrefix(selectorString, @"it")) {
153 const char *encoding = method_getTypeEncoding(methods[i]);
154 NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:encoding];
155
156 if ([signature numberOfMessageArguments] > 0 ||
157 !KWObjCTypeEqualToObjCType([signature methodReturnType], @encode(void)))
158 continue;
159
160 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
161 [invocation setSelector:selector];
162 [exampleInvocations addObject:invocation];
163 }
164 }
165
166 free(methods);
167 return exampleInvocations;
168}
169
170#pragma mark -
171#pragma mark Running Test Cases
172
173// Called by the SenTestingKit test suite when it is time to run the test.
174- (void)invokeTest {
175 NSAutoreleasePool *subPool = [[NSAutoreleasePool alloc] init];
176 [self setUpExampleEnvironment];
177
178 @try {
179 [super invokeTest];
180
181 for (id<KWVerifying> verifier in self.verifiers)
182 [verifier exampleWillEnd];
183 } @catch (NSException *exception) {
184 [self failWithException:exception];
185 }
186
187 [self tearDownExampleEnvironment];
188 [subPool release];
189}
190
191@end