KWMatchVerifier.m 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. //
  2. // Licensed under the terms in License.txt
  3. //
  4. // Copyright 2010 Allen Ding. All rights reserved.
  5. //
  6. #import "KWMatchVerifier.h"
  7. #import "KWCallSite.h"
  8. #import "KWExample.h"
  9. #import "KWFailure.h"
  10. #import "KWFormatter.h"
  11. #import "KWInvocationCapturer.h"
  12. #import "KWMatcherFactory.h"
  13. #import "KWReporting.h"
  14. #import "KWStringUtilities.h"
  15. #import "KWWorkarounds.h"
  16. #import "NSInvocation+KiwiAdditions.h"
  17. #import "NSMethodSignature+KiwiAdditions.h"
  18. @interface KWMatchVerifier()
  19. #pragma mark - Properties
  20. @property (nonatomic, readwrite, strong) id<KWMatching> endOfExampleMatcher;
  21. @property (nonatomic, readwrite, strong) id<KWMatching> matcher;
  22. @property (nonatomic, readwrite, strong) KWExample *example;
  23. @property (nonatomic, strong) KWCallSite *callSite;
  24. @end
  25. @implementation KWMatchVerifier
  26. #pragma mark - Initializing
  27. - (id)initForShouldWithCallSite:(KWCallSite *)aCallSite matcherFactory:(KWMatcherFactory *)aMatcherFactory reporter:(id<KWReporting>)aReporter {
  28. return [self initWithExpectationType:KWExpectationTypeShould callSite:aCallSite matcherFactory:aMatcherFactory reporter:aReporter];
  29. }
  30. - (id)initForShouldNotWithCallSite:(KWCallSite *)aCallSite matcherFactory:(KWMatcherFactory *)aMatcherFactory reporter:(id<KWReporting>)aReporter {
  31. return [self initWithExpectationType:KWExpectationTypeShouldNot callSite:aCallSite matcherFactory:aMatcherFactory reporter:aReporter];
  32. }
  33. - (id)initWithExpectationType:(KWExpectationType)anExpectationType callSite:(KWCallSite *)aCallSite matcherFactory:(KWMatcherFactory *)aMatcherFactory reporter:(id<KWReporting>)aReporter {
  34. self = [super init];
  35. if (self) {
  36. _expectationType = anExpectationType;
  37. _callSite = aCallSite;
  38. _matcherFactory = aMatcherFactory;
  39. _reporter = aReporter;
  40. _example = (KWExample *)aReporter;
  41. }
  42. return self;
  43. }
  44. + (id<KWVerifying>)matchVerifierWithExpectationType:(KWExpectationType)anExpectationType callSite:(KWCallSite *)aCallSite matcherFactory:(KWMatcherFactory *)aMatcherFactory reporter:(id<KWReporting>)aReporter {
  45. return [[self alloc] initWithExpectationType:anExpectationType callSite:aCallSite matcherFactory:aMatcherFactory reporter:aReporter];
  46. }
  47. - (NSString *)descriptionForAnonymousItNode {
  48. NSString *typeString = @"";
  49. switch (self.expectationType) {
  50. case KWExpectationTypeShould:
  51. typeString = @"should";
  52. break;
  53. case KWExpectationTypeShouldNot:
  54. typeString = @"should not";
  55. }
  56. id<KWMatching> actualMatcher = (self.endOfExampleMatcher == nil) ? self.matcher : self.endOfExampleMatcher;
  57. return [NSString stringWithFormat:@"%@ %@", typeString, actualMatcher];
  58. }
  59. #pragma mark - Verifying
  60. - (void)verifyWithMatcher:(id<KWMatching>)aMatcher {
  61. BOOL specFailed = NO;
  62. NSString *failureMessage = nil;
  63. @try {
  64. BOOL matchResult = [aMatcher evaluate];
  65. if (self.expectationType == KWExpectationTypeShould && !matchResult) {
  66. failureMessage = [aMatcher failureMessageForShould];
  67. specFailed = YES;
  68. } else if (self.expectationType == KWExpectationTypeShouldNot && matchResult) {
  69. failureMessage = [aMatcher failureMessageForShouldNot];
  70. specFailed = YES;
  71. }
  72. } @catch (NSException *exception) {
  73. failureMessage = [exception description];
  74. specFailed = YES;
  75. }
  76. @finally {
  77. if (specFailed) {
  78. KWFailure *failure = [KWFailure failureWithCallSite:self.callSite message:failureMessage];
  79. [self.reporter reportFailure:failure];
  80. }
  81. }
  82. }
  83. #pragma mark - Ending Examples
  84. - (void)exampleWillEnd {
  85. if (self.endOfExampleMatcher) {
  86. [self verifyWithMatcher:self.endOfExampleMatcher];
  87. }
  88. }
  89. #pragma mark - Handling Invocations
  90. - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
  91. NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
  92. if (signature != nil)
  93. return signature;
  94. signature = [self.matcherFactory methodSignatureForMatcherSelector:aSelector];
  95. if (signature != nil)
  96. return signature;
  97. // Return a dummy method signature so that problems can be handled in
  98. // -forwardInvocation:.
  99. NSString *encoding = KWEncodingForDefaultMethod();
  100. return [NSMethodSignature signatureWithObjCTypes:[encoding UTF8String]];
  101. }
  102. - (void)forwardInvocation:(NSInvocation *)anInvocation {
  103. #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
  104. @try {
  105. #endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
  106. self.matcher = (id<KWMatching>)[self.matcherFactory matcherFromInvocation:anInvocation subject:self.subject];
  107. if (self.matcher == nil) {
  108. KWFailure *failure = [KWFailure failureWithCallSite:self.callSite format:@"could not create matcher for -%@",
  109. NSStringFromSelector(anInvocation.selector)];
  110. [self.reporter reportFailure:failure];
  111. }
  112. if (self.expectationType == KWExpectationTypeShouldNot && [self.matcher respondsToSelector:@selector(setWillEvaluateAgainstNegativeExpectation:)]) {
  113. [self.matcher setWillEvaluateAgainstNegativeExpectation:YES];
  114. }
  115. if (self.example.unresolvedVerifier == self) {
  116. self.example.unresolvedVerifier = nil;
  117. }
  118. [anInvocation invokeWithTarget:self.matcher];
  119. #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
  120. // A matcher might have set an exception within the -invokeWithTarget, so
  121. // raise if one was set.
  122. NSException *exception = KWGetAndClearExceptionFromAcrossInvocationBoundary();
  123. [exception raise];
  124. #endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
  125. if ([self.matcher respondsToSelector:@selector(shouldBeEvaluatedAtEndOfExample)] && [self.matcher shouldBeEvaluatedAtEndOfExample]) {
  126. self.endOfExampleMatcher = self.matcher;
  127. self.matcher = nil;
  128. }
  129. else {
  130. [self verifyWithMatcher:self.matcher];
  131. }
  132. #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
  133. } @catch (NSException *exception) {
  134. KWFailure *failure = [KWFailure failureWithCallSite:self.callSite format:[exception reason]];
  135. [self.reporter reportFailure:failure];
  136. return;
  137. }
  138. #endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
  139. }
  140. @end