KWMock.m 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. //
  2. // Licensed under the terms in License.txt
  3. //
  4. // Copyright 2010 Allen Ding. All rights reserved.
  5. //
  6. #import "KWMock.h"
  7. #import <objc/runtime.h>
  8. #import "KWFormatter.h"
  9. #import "KWMessagePattern.h"
  10. #import "KWMessageSpying.h"
  11. #import "KWStringUtilities.h"
  12. #import "KWStub.h"
  13. #import "KWWorkarounds.h"
  14. #import "NSInvocation+KiwiAdditions.h"
  15. #import "KWCaptureSpy.h"
  16. static NSString * const ExpectOrStubTagKey = @"ExpectOrStubTagKey";
  17. static NSString * const StubTag = @"StubTag";
  18. static NSString * const ExpectTag = @"ExpectTag";
  19. static NSString * const StubValueKey = @"StubValueKey";
  20. static NSString * const StubSecondValueKey = @"StubSecondValueKey";
  21. static NSString * const ChangeStubValueAfterTimesKey = @"ChangeStubValueAfterTimesKey";
  22. @interface KWMock()
  23. @property (nonatomic, readonly) NSMutableArray *stubs;
  24. @property (nonatomic, readonly) NSMutableArray *expectedMessagePatterns;
  25. @property (nonatomic, readonly) NSMapTable *messageSpies;
  26. @end
  27. @implementation KWMock
  28. #pragma mark - Initializing
  29. - (id)init {
  30. // May already have been initialized since stubbing -init is allowed!
  31. if (self.stubs != nil) {
  32. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
  33. [self expectMessagePattern:messagePattern];
  34. NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd];
  35. if ([self processReceivedInvocation:invocation]) {
  36. __unsafe_unretained id result = nil;
  37. [invocation getReturnValue:&result];
  38. return result;
  39. } else {
  40. return self;
  41. }
  42. }
  43. return [self initAsNullMock:NO withName:nil forClass:nil protocol:nil];
  44. }
  45. - (id)initForClass:(Class)aClass {
  46. return [self initAsNullMock:NO withName:nil forClass:aClass protocol:nil];
  47. }
  48. - (id)initForProtocol:(Protocol *)aProtocol {
  49. return [self initAsNullMock:NO withName:nil forClass:nil protocol:aProtocol];
  50. }
  51. - (id)initWithName:(NSString *)aName forClass:(Class)aClass {
  52. return [self initAsNullMock:NO withName:aName forClass:aClass protocol:nil];
  53. }
  54. - (id)initWithName:(NSString *)aName forProtocol:(Protocol *)aProtocol {
  55. return [self initAsNullMock:NO withName:aName forClass:nil protocol:aProtocol];
  56. }
  57. - (id)initAsNullMockForClass:(Class)aClass {
  58. return [self initAsNullMock:YES withName:nil forClass:aClass protocol:nil];
  59. }
  60. - (id)initAsNullMockForProtocol:(Protocol *)aProtocol {
  61. return [self initAsNullMock:YES withName:nil forClass:nil protocol:aProtocol];
  62. }
  63. - (id)initAsNullMockWithName:(NSString *)aName forClass:(Class)aClass {
  64. return [self initAsNullMock:YES withName:aName forClass:aClass protocol:nil];
  65. }
  66. - (id)initAsNullMockWithName:(NSString *)aName forProtocol:(Protocol *)aProtocol {
  67. return [self initAsNullMock:YES withName:aName forClass:nil protocol:aProtocol];
  68. }
  69. - (id)initAsNullMock:(BOOL)nullMockFlag withName:(NSString *)aName forClass:(Class)aClass protocol:(Protocol *)aProtocol {
  70. self = [super init];
  71. if (self) {
  72. _isNullMock = nullMockFlag;
  73. _mockName = [aName copy];
  74. _mockedClass = aClass;
  75. _mockedProtocol = aProtocol;
  76. _stubs = [[NSMutableArray alloc] init];
  77. _expectedMessagePatterns = [[NSMutableArray alloc] init];
  78. _messageSpies = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory];
  79. }
  80. return self;
  81. }
  82. - (id)initAsPartialMockForObject:(id)object {
  83. return [self initAsPartialMockWithName:nil forObject:object];
  84. }
  85. - (id)initAsPartialMockWithName:(NSString *)aName forObject:(id)object {
  86. self = [self initAsNullMock:YES withName:aName forClass:[object class] protocol:nil];
  87. if (self) {
  88. _isPartialMock = YES;
  89. _mockedObject = object;
  90. }
  91. return self;
  92. }
  93. + (id)mockForClass:(Class)aClass {
  94. return [[self alloc] initForClass:aClass];
  95. }
  96. + (id)mockForProtocol:(Protocol *)aProtocol {
  97. return [[self alloc] initForProtocol:aProtocol];
  98. }
  99. + (id)mockWithName:(NSString *)aName forClass:(Class)aClass {
  100. return [[self alloc] initWithName:aName forClass:aClass];
  101. }
  102. + (id)mockWithName:(NSString *)aName forProtocol:(Protocol *)aProtocol {
  103. return [[self alloc] initWithName:aName forProtocol:aProtocol];
  104. }
  105. + (id)nullMockForClass:(Class)aClass {
  106. return [[self alloc] initAsNullMockForClass:aClass];
  107. }
  108. + (id)nullMockForProtocol:(Protocol *)aProtocol {
  109. return [[self alloc] initAsNullMockForProtocol:aProtocol];
  110. }
  111. + (id)nullMockWithName:(NSString *)aName forClass:(Class)aClass {
  112. return [[self alloc] initAsNullMockWithName:aName forClass:aClass];
  113. }
  114. + (id)nullMockWithName:(NSString *)aName forProtocol:(Protocol *)aProtocol {
  115. return [[self alloc] initAsNullMockWithName:aName forProtocol:aProtocol];
  116. }
  117. + (id)partialMockWithName:(NSString *)aName forObject:(id)object {
  118. return [[self alloc] initAsPartialMockWithName:aName forObject:object];
  119. }
  120. + (id)partialMockForObject:(id)object {
  121. return [[self alloc] initAsPartialMockForObject:object];
  122. }
  123. #pragma mark - Getting Transitive Closure For Mocked Protocols
  124. - (NSSet *)mockedProtocolTransitiveClosureSet {
  125. if (self.mockedProtocol == nil)
  126. return nil;
  127. NSMutableSet *protocolSet = [NSMutableSet set];
  128. NSMutableArray *protocolQueue = [NSMutableArray array];
  129. [protocolQueue addObject:self.mockedProtocol];
  130. do {
  131. Protocol *protocol = [protocolQueue lastObject];
  132. [protocolSet addObject:protocol];
  133. [protocolQueue removeLastObject];
  134. unsigned int count = 0;
  135. Protocol *__unsafe_unretained*protocols = protocol_copyProtocolList(protocol, &count);
  136. if (count == 0)
  137. continue;
  138. for (unsigned int i = 0; i < count; ++i)
  139. [protocolQueue addObject:protocols[i]];
  140. free(protocols);
  141. } while ([protocolQueue count] != 0);
  142. return protocolSet;
  143. }
  144. #pragma mark - Stubbing Methods
  145. - (void)removeStubWithMessagePattern:(KWMessagePattern *)messagePattern {
  146. KWStub *stub = [self currentStubWithMessagePattern:messagePattern];
  147. if (stub) {
  148. [self.stubs removeObject:stub];
  149. }
  150. }
  151. - (KWStub *)currentStubWithMessagePattern:(KWMessagePattern *)messagePattern {
  152. NSUInteger stubCount = [self.stubs count];
  153. for (NSUInteger i = 0; i < stubCount; ++i) {
  154. KWStub *stub = (self.stubs)[i];
  155. if ([stub.messagePattern isEqualToMessagePattern:messagePattern]) {
  156. return stub;
  157. }
  158. }
  159. return nil;
  160. }
  161. - (void)stub:(SEL)aSelector {
  162. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
  163. [self stubMessagePattern:messagePattern andReturn:nil];
  164. }
  165. - (void)stub:(SEL)aSelector withBlock:(id (^)(NSArray *params))block {
  166. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
  167. [self stubMessagePattern:messagePattern withBlock:block];
  168. }
  169. - (void)stub:(SEL)aSelector withArguments:(id)firstArgument, ... {
  170. va_list argumentList;
  171. va_start(argumentList, firstArgument);
  172. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
  173. [self stubMessagePattern:messagePattern andReturn:nil];
  174. }
  175. - (void)stub:(SEL)aSelector andReturn:(id)aValue {
  176. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
  177. [self stubMessagePattern:messagePattern andReturn:aValue];
  178. }
  179. - (void)stub:(SEL)aSelector andReturn:(id)aValue withArguments:(id)firstArgument, ... {
  180. va_list argumentList;
  181. va_start(argumentList, firstArgument);
  182. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
  183. [self stubMessagePattern:messagePattern andReturn:aValue];
  184. }
  185. - (id)stub {
  186. NSDictionary *userInfo = @{ExpectOrStubTagKey: StubTag};
  187. return [KWInvocationCapturer invocationCapturerWithDelegate:self userInfo:userInfo];
  188. }
  189. - (id)stubAndReturn:(id)aValue {
  190. NSDictionary *userInfo = @{ExpectOrStubTagKey: StubTag,
  191. StubValueKey: aValue};
  192. return [KWInvocationCapturer invocationCapturerWithDelegate:self userInfo:userInfo];
  193. }
  194. - (id)stubAndReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue {
  195. NSDictionary *userInfo = @{ExpectOrStubTagKey: StubTag, StubValueKey: aValue, ChangeStubValueAfterTimesKey: times, StubSecondValueKey: aSecondValue};
  196. return [KWInvocationCapturer invocationCapturerWithDelegate:self userInfo:userInfo];
  197. }
  198. - (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue {
  199. [self stubMessagePattern:aMessagePattern andReturn:aValue overrideExisting:YES];
  200. }
  201. - (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue overrideExisting:(BOOL)overrideExisting {
  202. [self expectMessagePattern:aMessagePattern];
  203. KWStub *existingStub = [self currentStubWithMessagePattern:aMessagePattern];
  204. if (existingStub) {
  205. if (overrideExisting) {
  206. [self.stubs removeObject:existingStub];
  207. } else {
  208. return;
  209. }
  210. }
  211. KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern value:aValue];
  212. [self.stubs addObject:stub];
  213. }
  214. - (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block {
  215. [self expectMessagePattern:aMessagePattern];
  216. [self removeStubWithMessagePattern:aMessagePattern];
  217. KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern block:block];
  218. [self.stubs addObject:stub];
  219. }
  220. - (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue {
  221. [self expectMessagePattern:aMessagePattern];
  222. [self removeStubWithMessagePattern:aMessagePattern];
  223. KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern value:aValue times:times afterThatReturn:aSecondValue];
  224. [self.stubs addObject:stub];
  225. }
  226. - (void)clearStubs {
  227. [self.stubs removeAllObjects];
  228. }
  229. #pragma mark - Spying on Messages
  230. - (void)addMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
  231. [self expectMessagePattern:aMessagePattern];
  232. NSMutableArray *messagePatternSpies = [self.messageSpies objectForKey:aMessagePattern];
  233. if (messagePatternSpies == nil) {
  234. messagePatternSpies = [[NSMutableArray alloc] init];
  235. [self.messageSpies setObject:messagePatternSpies forKey:aMessagePattern];
  236. }
  237. if (![messagePatternSpies containsObject:aSpy])
  238. [messagePatternSpies addObject:aSpy];
  239. }
  240. - (void)removeMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
  241. NSMutableArray *messagePatternSpies = [self.messageSpies objectForKey:aMessagePattern];
  242. [messagePatternSpies removeObject:aSpy];
  243. }
  244. #pragma mark - Expecting Message Patterns
  245. - (void)expect:(SEL)aSelector {
  246. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
  247. [self expectMessagePattern:messagePattern];
  248. }
  249. - (void)expect:(SEL)aSelector withArguments:(id)firstArgument, ... {
  250. va_list argumentList;
  251. va_start(argumentList, firstArgument);
  252. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
  253. [self expectMessagePattern:messagePattern];
  254. }
  255. - (id)expect {
  256. NSDictionary *userInfo = @{ExpectOrStubTagKey: ExpectTag};
  257. return [KWInvocationCapturer invocationCapturerWithDelegate:self userInfo:userInfo];
  258. }
  259. - (void)expectMessagePattern:(KWMessagePattern *)aMessagePattern {
  260. if (![self.expectedMessagePatterns containsObject:aMessagePattern])
  261. [self.expectedMessagePatterns addObject:aMessagePattern];
  262. }
  263. #pragma mark - Capturing Invocations
  264. - (NSMethodSignature *)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer methodSignatureForSelector:(SEL)aSelector {
  265. return [self methodSignatureForSelector:aSelector];
  266. }
  267. - (void)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer didCaptureInvocation:(NSInvocation *)anInvocation {
  268. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternFromInvocation:anInvocation];
  269. NSString *tag = (anInvocationCapturer.userInfo)[ExpectOrStubTagKey];
  270. if ([tag isEqualToString:StubTag]) {
  271. id value = (anInvocationCapturer.userInfo)[StubValueKey];
  272. if (!(anInvocationCapturer.userInfo)[StubSecondValueKey]) {
  273. [self stubMessagePattern:messagePattern andReturn:value];
  274. } else {
  275. id times = (anInvocationCapturer.userInfo)[ChangeStubValueAfterTimesKey];
  276. id secondValue = (anInvocationCapturer.userInfo)[StubSecondValueKey];
  277. [self stubMessagePattern:messagePattern andReturn:value times:times afterThatReturn:secondValue];
  278. }
  279. } else {
  280. [self expectMessagePattern:messagePattern];
  281. }
  282. }
  283. #pragma mark - Handling Invocations
  284. - (NSString *)namePhrase {
  285. if (self.mockName == nil)
  286. return @"mock";
  287. else
  288. return [NSString stringWithFormat:@"mock \"%@\"", self.mockName];
  289. }
  290. - (BOOL)processReceivedInvocation:(NSInvocation *)invocation {
  291. for (KWMessagePattern *messagePattern in self.messageSpies) {
  292. if ([messagePattern matchesInvocation:invocation]) {
  293. NSArray *spies = [self.messageSpies objectForKey:messagePattern];
  294. for (id<KWMessageSpying> spy in spies) {
  295. [spy object:self didReceiveInvocation:invocation];
  296. }
  297. }
  298. }
  299. for (KWStub *stub in self.stubs) {
  300. if ([stub processInvocation:invocation])
  301. return YES;
  302. }
  303. return NO;
  304. }
  305. - (NSMethodSignature *)mockedProtocolMethodSignatureForSelector:(SEL)aSelector {
  306. NSSet *protocols = [self mockedProtocolTransitiveClosureSet];
  307. for (Protocol *protocol in protocols) {
  308. struct objc_method_description description = protocol_getMethodDescription(protocol, aSelector, NO, YES);
  309. if (description.types == nil)
  310. description = protocol_getMethodDescription(protocol, aSelector, YES, YES);
  311. if (description.types != nil)
  312. return [NSMethodSignature signatureWithObjCTypes:description.types];
  313. }
  314. return nil;
  315. }
  316. - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
  317. NSMethodSignature *methodSignature = [self.mockedClass instanceMethodSignatureForSelector:aSelector];
  318. if (methodSignature != nil)
  319. return methodSignature;
  320. methodSignature = [self mockedProtocolMethodSignatureForSelector:aSelector];
  321. if (methodSignature != nil)
  322. return methodSignature;
  323. NSString *encoding = KWEncodingForDefaultMethod();
  324. return [NSMethodSignature signatureWithObjCTypes:[encoding UTF8String]];
  325. }
  326. - (void)forwardInvocation:(NSInvocation *)anInvocation {
  327. #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
  328. @try {
  329. #endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
  330. if ([self processReceivedInvocation:anInvocation])
  331. return;
  332. if (self.isPartialMock)
  333. [anInvocation invokeWithTarget:self.mockedObject];
  334. if (self.isNullMock)
  335. return;
  336. for (KWMessagePattern *expectedMessagePattern in self.expectedMessagePatterns) {
  337. if ([expectedMessagePattern matchesInvocation:anInvocation])
  338. return;
  339. }
  340. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternFromInvocation:anInvocation];
  341. [NSException raise:@"KWMockException" format:@"%@ received unexpected message -%@",
  342. [self namePhrase],
  343. [messagePattern stringValue]];
  344. #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
  345. } @catch (NSException *exception) {
  346. KWSetExceptionFromAcrossInvocationBoundary(exception);
  347. }
  348. #endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
  349. }
  350. #pragma mark - Testing Objects
  351. - (BOOL)mockedClassHasAncestorClass:(Class)aClass {
  352. Class currentClass = self.mockedClass;
  353. while (currentClass != nil) {
  354. if (currentClass == aClass)
  355. return YES;
  356. currentClass = [currentClass superclass];
  357. }
  358. return NO;
  359. }
  360. - (BOOL)mockedClassRespondsToSelector:(SEL)aSelector {
  361. return [self.mockedClass instancesRespondToSelector:aSelector];
  362. }
  363. - (BOOL)mockedClassConformsToProtocol:(Protocol *)aProtocol {
  364. return [self.mockedClass conformsToProtocol:aProtocol];
  365. }
  366. - (BOOL)mockedProtocolRespondsToSelector:(SEL)aSelector {
  367. NSSet *protocols = [self mockedProtocolTransitiveClosureSet];
  368. for (Protocol *protocol in protocols) {
  369. struct objc_method_description description = protocol_getMethodDescription(protocol, aSelector, NO, YES);
  370. if (description.types == nil)
  371. description = protocol_getMethodDescription(protocol, aSelector, YES, YES);
  372. if (description.types != nil)
  373. return YES;
  374. }
  375. return NO;
  376. }
  377. - (BOOL)mockedProtocolConformsToProtocol:(Protocol *)aProtocol {
  378. if (self.mockedProtocol == nil)
  379. return NO;
  380. return protocol_isEqual(self.mockedProtocol, aProtocol) || protocol_conformsToProtocol(self.mockedProtocol, aProtocol);
  381. }
  382. - (BOOL)isKindOfClass:(Class)aClass {
  383. return [self mockedClassHasAncestorClass:aClass] || [super isKindOfClass:aClass];
  384. }
  385. - (BOOL)isMemberOfClass:(Class)aClass {
  386. return self.mockedClass == aClass || [super isMemberOfClass:aClass];
  387. }
  388. - (BOOL)respondsToSelector:(SEL)aSelector {
  389. return [self mockedClassRespondsToSelector:aSelector] ||
  390. [self mockedProtocolRespondsToSelector:aSelector] ||
  391. [super respondsToSelector:aSelector];
  392. }
  393. - (BOOL)conformsToProtocol:(Protocol *)aProtocol {
  394. return [self mockedClassConformsToProtocol:aProtocol] ||
  395. [self mockedProtocolConformsToProtocol:aProtocol] ||
  396. [super conformsToProtocol:aProtocol];
  397. }
  398. #pragma mark - Whitelisted NSObject Methods
  399. - (BOOL)isEqual:(id)anObject {
  400. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
  401. [self expectMessagePattern:messagePattern];
  402. NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd messageArguments:&anObject];
  403. if ([self processReceivedInvocation:invocation]) {
  404. BOOL result = NO;
  405. [invocation getReturnValue:&result];
  406. return result;
  407. } else {
  408. return [super isEqual:anObject];
  409. }
  410. }
  411. - (NSUInteger)hash {
  412. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
  413. [self expectMessagePattern:messagePattern];
  414. NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd];
  415. if ([self processReceivedInvocation:invocation]) {
  416. NSUInteger result = 0;
  417. [invocation getReturnValue:&result];
  418. return result;
  419. } else {
  420. return [super hash];
  421. }
  422. }
  423. - (NSString *)description {
  424. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
  425. [self expectMessagePattern:messagePattern];
  426. NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd];
  427. if ([self processReceivedInvocation:invocation]) {
  428. __unsafe_unretained NSString *result = nil;
  429. [invocation getReturnValue:&result];
  430. return result;
  431. } else {
  432. return [super description];
  433. }
  434. }
  435. - (id)copy {
  436. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
  437. [self expectMessagePattern:messagePattern];
  438. NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd];
  439. if ([self processReceivedInvocation:invocation]) {
  440. __unsafe_unretained id result = nil;
  441. [invocation getReturnValue:&result];
  442. return result;
  443. } else {
  444. return [super copy];
  445. }
  446. }
  447. - (id)mutableCopy {
  448. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
  449. [self expectMessagePattern:messagePattern];
  450. NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd];
  451. if ([self processReceivedInvocation:invocation]) {
  452. __unsafe_unretained id result = nil;
  453. [invocation getReturnValue:&result];
  454. return result;
  455. } else {
  456. return [super mutableCopy];
  457. }
  458. }
  459. #pragma mark -
  460. #pragma mark Key-Value Coding Support
  461. static id valueForKeyImplementation(id self, SEL _cmd, id key) {
  462. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
  463. [self expectMessagePattern:messagePattern];
  464. NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd messageArguments:&key];
  465. if ([self processReceivedInvocation:invocation]) {
  466. __unsafe_unretained id result = nil;
  467. [invocation getReturnValue:&result];
  468. return result;
  469. } else {
  470. return nil;
  471. }
  472. }
  473. - (id)valueForKey:(NSString *)key {
  474. return valueForKeyImplementation(self, _cmd, key);
  475. }
  476. - (id)valueForKeyPath:(NSString *)keyPath {
  477. return valueForKeyImplementation(self, _cmd, keyPath);
  478. }
  479. static void setValueForKeyImplementation(id self, SEL _cmd, id a, id b) {
  480. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
  481. [self expectMessagePattern:messagePattern];
  482. NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd messageArguments:&a, &b];
  483. [self processReceivedInvocation:invocation];
  484. }
  485. - (void)setValue:(id)value forKey:(NSString *)key {
  486. setValueForKeyImplementation(self, _cmd, value, key);
  487. }
  488. - (void)setValue:(id)value forKeyPath:(NSString *)keyPath {
  489. setValueForKeyImplementation(self, _cmd, value, keyPath);
  490. }
  491. @end