123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- //
- // Licensed under the terms in License.txt
- //
- // Copyright 2010 Allen Ding. All rights reserved.
- //
- #import "KWHaveMatcher.h"
- #import "KWCountType.h"
- #import "KWFormatter.h"
- #import "KWInvocationCapturer.h"
- #import "KWObjCUtilities.h"
- #import "KWStringUtilities.h"
- static NSString * const MatchVerifierKey = @"MatchVerifierKey";
- static NSString * const CountTypeKey = @"CountTypeKey";
- static NSString * const CountKey = @"CountKey";
- @interface KWHaveMatcher()
- #pragma mark - Properties
- @property (nonatomic, assign) KWCountType countType;
- @property (nonatomic, assign) NSUInteger count;
- @property (nonatomic, strong) NSInvocation *invocation;
- @property (nonatomic, assign) NSUInteger actualCount;
- @end
- @implementation KWHaveMatcher
- #pragma mark - Getting Matcher Strings
- + (NSArray *)matcherStrings {
- return @[
- @"haveCountOf:",
- @"haveCountOfAtLeast:",
- @"haveCountOfAtMost:",
- @"haveLengthOf:",
- @"haveLengthOfAtLeast:",
- @"haveLengthOfAtMost:",
- @"have:itemsForInvocation:",
- @"haveAtLeast:itemsForInvocation:",
- @"haveAtMost:itemsForInvocation:",
- ];
- }
- #pragma mark - Matching
- - (id)targetObject {
- if (self.invocation == nil)
- return self.subject;
- SEL selector = [self.invocation selector];
- if ([self.subject respondsToSelector:selector]) {
- NSMethodSignature *signature = [self.subject methodSignatureForSelector:selector];
- if (!KWObjCTypeIsObject([signature methodReturnType]))
- [NSException raise:@"KWMatcherEception" format:@"a valid collection was not specified"];
- __unsafe_unretained id object = nil;
- [self.invocation invokeWithTarget:self.subject];
- [self.invocation getReturnValue:&object];
- return object;
- } else if (KWSelectorParameterCount(selector) == 0) {
- return self.subject;
- } else {
- return nil;
- }
- }
- - (BOOL)evaluate {
- id targetObject = [self targetObject];
- if ([targetObject respondsToSelector:@selector(count)])
- self.actualCount = [targetObject count];
- else if ([targetObject respondsToSelector:@selector(length)])
- self.actualCount = [targetObject length];
- else
- self.actualCount = 0;
- switch (self.countType) {
- case KWCountTypeExact:
- return self.actualCount == self.count;
- case KWCountTypeAtLeast:
- return self.actualCount >= self.count;
- case KWCountTypeAtMost:
- return self.actualCount <= self.count;
- }
- assert(0 && "should never reach here");
- return NO;
- }
- #pragma mark - Getting Failure Messages
- - (NSString *)verbPhrase {
- switch (self.countType) {
- case KWCountTypeExact:
- return @"have";
- case KWCountTypeAtLeast:
- return @"have at least";
- case KWCountTypeAtMost:
- return @"have at most";
- }
- assert(0 && "should never reach here");
- return nil;
- }
- - (NSString *)itemPhrase {
- if (self.invocation == nil)
- return @"items";
- else
- return NSStringFromSelector([self.invocation selector]);
- }
- - (NSString *)actualCountPhrase {
- if (self.actualCount == 1)
- return @"1 item";
- else
- return [NSString stringWithFormat:@"%u items", (unsigned)self.actualCount];
- }
- - (NSString *)failureMessageForShould {
- return [NSString stringWithFormat:@"expected subject to %@ %u %@, got %@",
- [self verbPhrase],
- (unsigned)self.count,
- [self itemPhrase],
- [self actualCountPhrase]];
- }
- - (NSString *)failureMessageForShouldNot {
- return [NSString stringWithFormat:@"expected subject not to %@ %u %@",
- [self verbPhrase],
- (unsigned)self.count,
- [self itemPhrase]];
- }
- #pragma mark - Description
- - (NSString *)description {
- return [NSString stringWithFormat:@"%@ %u %@", [self verbPhrase], (unsigned)self.count, [self itemPhrase]];
- }
- #pragma mark - Configuring Matchers
- - (void)haveCountOf:(NSUInteger)aCount {
- self.count = aCount;
- self.countType = KWCountTypeExact;
- }
- - (void)haveLengthOf:(NSUInteger)aCount {
- [self haveCountOf:aCount];
- }
- - (void)haveCountOfAtLeast:(NSUInteger)aCount {
- self.count = aCount;
- self.countType = KWCountTypeAtLeast;
- }
- - (void)haveLengthOfAtLeast:(NSUInteger)aCount {
- [self haveCountOfAtLeast:aCount];
- }
- - (void)haveCountOfAtMost:(NSUInteger)aCount {
- self.count = aCount;
- self.countType = KWCountTypeAtMost;
- }
- - (void)haveLengthOfAtMost:(NSUInteger)aCount {
- [self haveCountOfAtMost:aCount];
- }
- - (void)have:(NSUInteger)aCount itemsForInvocation:(NSInvocation *)anInvocation {
- self.count = aCount;
- self.countType = KWCountTypeExact;
- self.invocation = anInvocation;
- }
- - (void)haveAtLeast:(NSUInteger)aCount itemsForInvocation:(NSInvocation *)anInvocation {
- self.count = aCount;
- self.countType = KWCountTypeAtLeast;
- self.invocation = anInvocation;
- }
- - (void)haveAtMost:(NSUInteger)aCount itemsForInvocation:(NSInvocation *)anInvocation {
- self.count = aCount;
- self.countType = KWCountTypeAtMost;
- self.invocation = anInvocation;
- }
- #pragma mark - Capturing Invocations
- + (NSMethodSignature *)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer methodSignatureForSelector:(SEL)aSelector {
- KWMatchVerifier *verifier = (anInvocationCapturer.userInfo)[MatchVerifierKey];
- if ([verifier.subject respondsToSelector:aSelector])
- return [verifier.subject methodSignatureForSelector:aSelector];
- // Arbitrary selectors are allowed as expectation expression terminals when
- // the subject itself is a collection, so return a dummy method signature.
- NSString *encoding = KWEncodingForDefaultMethod();
- return [NSMethodSignature signatureWithObjCTypes:[encoding UTF8String]];
- }
- + (void)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer didCaptureInvocation:(NSInvocation *)anInvocation {
- NSDictionary *userInfo = anInvocationCapturer.userInfo;
- id verifier = userInfo[MatchVerifierKey];
- KWCountType countType = [userInfo[CountTypeKey] unsignedIntegerValue];
- NSUInteger count = [userInfo[CountKey] unsignedIntegerValue];
- switch (countType) {
- case KWCountTypeExact:
- [verifier have:count itemsForInvocation:anInvocation];
- break;
- case KWCountTypeAtLeast:
- [verifier haveAtLeast:count itemsForInvocation:anInvocation];
- break;
- case KWCountTypeAtMost:
- [verifier haveAtMost:count itemsForInvocation:anInvocation];
- break;
- }
- }
- @end
- #pragma mark - Verifying
- @implementation KWMatchVerifier(KWHaveMatcherAdditions)
- #pragma mark - Invocation Capturing Methods
- - (NSDictionary *)userInfoForHaveMatcherWithCountType:(KWCountType)aCountType count:(NSUInteger)aCount {
- return @{MatchVerifierKey: self,
- CountTypeKey: @(aCountType),
- CountKey: @(aCount)};
- }
- - (id)have:(NSUInteger)aCount {
- NSDictionary *userInfo = [self userInfoForHaveMatcherWithCountType:KWCountTypeExact count:aCount];
- return [KWInvocationCapturer invocationCapturerWithDelegate:[KWHaveMatcher class] userInfo:userInfo];
- }
- - (id)haveAtLeast:(NSUInteger)aCount {
- NSDictionary *userInfo = [self userInfoForHaveMatcherWithCountType:KWCountTypeAtLeast count:aCount];
- return [KWInvocationCapturer invocationCapturerWithDelegate:[KWHaveMatcher class] userInfo:userInfo];
- }
- - (id)haveAtMost:(NSUInteger)aCount {
- NSDictionary *userInfo = [self userInfoForHaveMatcherWithCountType:KWCountTypeAtMost count:aCount];
- return [KWInvocationCapturer invocationCapturerWithDelegate:[KWHaveMatcher class] userInfo:userInfo];
- }
- @end
|