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