main
1//
2// Licensed under the terms in License.txt
3//
4// Copyright 2010 Allen Ding. All rights reserved.
5//
6
7#import "KWMock.h"
8#import <objc/runtime.h>
9#import "KWFormatter.h"
10#import "KWMessagePattern.h"
11#import "KWMessageSpying.h"
12#import "KWStringUtilities.h"
13#import "KWStub.h"
14#import "KWWorkarounds.h"
15#import "NSInvocation+KiwiAdditions.h"
16#import "KWCaptureSpy.h"
17
18static NSString * const ExpectOrStubTagKey = @"ExpectOrStubTagKey";
19static NSString * const StubTag = @"StubTag";
20static NSString * const ExpectTag = @"ExpectTag";
21static NSString * const StubValueKey = @"StubValueKey";
22static NSString * const StubSecondValueKey = @"StubSecondValueKey";
23static NSString * const ChangeStubValueAfterTimesKey = @"ChangeStubValueAfterTimesKey";
24
25@interface KWMock()
26
27#pragma mark -
28#pragma mark Initializing
29
30- (id)initAsNullMock:(BOOL)nullMockFlag withName:(NSString *)aName forClass:(Class)aClass protocol:(Protocol *)aProtocol;
31
32#pragma mark -
33#pragma mark Properties
34
35@property (nonatomic, readonly) NSMutableArray *stubs;
36@property (nonatomic, readonly) NSMutableArray *expectedMessagePatterns;
37@property (nonatomic, readonly) NSMutableDictionary *messageSpies;
38
39
40#pragma mark -
41#pragma mark Handling Invocations
42
43- (BOOL)processReceivedInvocation:(NSInvocation *)invocation;
44
45@end
46
47@implementation KWMock
48
49#pragma mark -
50#pragma mark Initializing
51
52- (id)init {
53 // May already have been initialized since stubbing -init is allowed!
54 if (self.stubs != nil) {
55 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
56 [self expectMessagePattern:messagePattern];
57 NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd];
58
59 if ([self processReceivedInvocation:invocation]) {
60 id result = nil;
61 [invocation getReturnValue:&result];
62 return result;
63 } else {
64 return self;
65 }
66 }
67
68 return [self initAsNullMock:NO withName:nil forClass:nil protocol:nil];
69}
70
71- (id)initForClass:(Class)aClass {
72 return [self initAsNullMock:NO withName:nil forClass:aClass protocol:nil];
73}
74
75- (id)initForProtocol:(Protocol *)aProtocol {
76 return [self initAsNullMock:NO withName:nil forClass:nil protocol:aProtocol];
77}
78
79- (id)initWithName:(NSString *)aName forClass:(Class)aClass {
80 return [self initAsNullMock:NO withName:aName forClass:aClass protocol:nil];
81}
82
83- (id)initWithName:(NSString *)aName forProtocol:(Protocol *)aProtocol {
84 return [self initAsNullMock:NO withName:aName forClass:nil protocol:aProtocol];
85}
86
87- (id)initAsNullMockForClass:(Class)aClass {
88 return [self initAsNullMock:YES withName:nil forClass:aClass protocol:nil];
89}
90
91- (id)initAsNullMockForProtocol:(Protocol *)aProtocol {
92 return [self initAsNullMock:YES withName:nil forClass:nil protocol:aProtocol];
93}
94
95- (id)initAsNullMockWithName:(NSString *)aName forClass:(Class)aClass {
96 return [self initAsNullMock:YES withName:aName forClass:aClass protocol:nil];
97}
98
99- (id)initAsNullMockWithName:(NSString *)aName forProtocol:(Protocol *)aProtocol {
100 return [self initAsNullMock:YES withName:aName forClass:nil protocol:aProtocol];
101}
102
103- (id)initAsNullMock:(BOOL)nullMockFlag withName:(NSString *)aName forClass:(Class)aClass protocol:(Protocol *)aProtocol {
104 if ((self = [super init])) {
105 isNullMock = nullMockFlag;
106 mockName = [aName copy];
107 mockedClass = aClass;
108 mockedProtocol = aProtocol;
109 stubs = [[NSMutableArray alloc] init];
110 expectedMessagePatterns = [[NSMutableArray alloc] init];
111 messageSpies = [[NSMutableDictionary alloc] init];
112 }
113
114 return self;
115}
116
117- (id)initAsPartialMockForObject:(id)object {
118 return [self initAsPartialMockWithName:nil forObject:object];
119}
120
121- (id)initAsPartialMockWithName:(NSString *)aName forObject:(id)object {
122 if ((self = [self initAsNullMock:YES withName:aName forClass:[object class] protocol:nil])) {
123 isPartialMock = YES;
124 mockedObject = [object retain];
125 }
126 return self;
127}
128
129+ (id)mockForClass:(Class)aClass {
130 return [[[self alloc] initForClass:aClass] autorelease];
131}
132
133+ (id)mockForProtocol:(Protocol *)aProtocol {
134 return [[[self alloc] initForProtocol:aProtocol] autorelease];
135}
136
137+ (id)mockWithName:(NSString *)aName forClass:(Class)aClass {
138 return [[[self alloc] initWithName:aName forClass:aClass] autorelease];
139}
140
141+ (id)mockWithName:(NSString *)aName forProtocol:(Protocol *)aProtocol {
142 return [[[self alloc] initWithName:aName forProtocol:aProtocol] autorelease];
143}
144
145+ (id)nullMockForClass:(Class)aClass {
146 return [[[self alloc] initAsNullMockForClass:aClass] autorelease];
147}
148
149+ (id)nullMockForProtocol:(Protocol *)aProtocol {
150 return [[[self alloc] initAsNullMockForProtocol:aProtocol] autorelease];
151}
152
153+ (id)nullMockWithName:(NSString *)aName forClass:(Class)aClass {
154 return [[[self alloc] initAsNullMockWithName:aName forClass:aClass] autorelease];
155}
156
157+ (id)nullMockWithName:(NSString *)aName forProtocol:(Protocol *)aProtocol {
158 return [[[self alloc] initAsNullMockWithName:aName forProtocol:aProtocol] autorelease];
159}
160
161+ (id)partialMockWithName:(NSString *)aName forObject:(id)object {
162 return [[[self alloc] initAsPartialMockWithName:aName forObject:object] autorelease];
163}
164
165+ (id)partialMockForObject:(id)object {
166 return [[[self alloc] initAsPartialMockForObject:object] autorelease];
167}
168
169- (void)dealloc {
170 [mockedObject release];
171 [mockName release];
172 [stubs release];
173 [expectedMessagePatterns release];
174 [messageSpies release];
175 [super dealloc];
176}
177
178#pragma mark -
179#pragma mark Properties
180
181@synthesize isPartialMock;
182@synthesize isNullMock;
183@synthesize mockName;
184@synthesize mockedObject;
185@synthesize mockedClass;
186@synthesize mockedProtocol;
187@synthesize stubs;
188@synthesize expectedMessagePatterns;
189@synthesize messageSpies;
190
191#pragma mark -
192#pragma mark Getting Transitive Closure For Mocked Protocols
193
194- (NSSet *)mockedProtocolTransitiveClosureSet {
195 if (self.mockedProtocol == nil)
196 return nil;
197
198 NSMutableSet *protocolSet = [NSMutableSet set];
199 NSMutableArray *protocolQueue = [NSMutableArray array];
200 [protocolQueue addObject:self.mockedProtocol];
201
202 do {
203 Protocol *protocol = [protocolQueue lastObject];
204 [protocolSet addObject:protocol];
205 [protocolQueue removeLastObject];
206
207 unsigned int count = 0;
208 Protocol **protocols = (Protocol **)protocol_copyProtocolList(protocol, &count);
209
210 if (count == 0)
211 continue;
212
213 for (unsigned int i = 0; i < count; ++i)
214 [protocolQueue addObject:protocols[i]];
215
216 free(protocols);
217 } while ([protocolQueue count] != 0);
218
219 return protocolSet;
220}
221
222#pragma mark -
223#pragma mark Stubbing Methods
224
225- (void)removeStubWithMessagePattern:(KWMessagePattern *)messagePattern {
226 KWStub *stub = [self currentStubWithMessagePattern:messagePattern];
227 if (stub) {
228 [self.stubs removeObject:stub];
229 }
230}
231
232- (KWStub *)currentStubWithMessagePattern:(KWMessagePattern *)messagePattern {
233 NSUInteger stubCount = [self.stubs count];
234
235 for (NSUInteger i = 0; i < stubCount; ++i) {
236 KWStub *stub = (self.stubs)[i];
237
238 if ([stub.messagePattern isEqualToMessagePattern:messagePattern]) {
239 return stub;
240 }
241 }
242 return nil;
243}
244
245- (void)stub:(SEL)aSelector {
246 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
247 [self stubMessagePattern:messagePattern andReturn:nil];
248}
249
250- (void)stub:(SEL)aSelector withBlock:(id (^)(NSArray *params))block {
251 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
252 [self stubMessagePattern:messagePattern withBlock:block];
253}
254
255- (void)stub:(SEL)aSelector withArguments:(id)firstArgument, ... {
256 va_list argumentList;
257 va_start(argumentList, firstArgument);
258 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
259 [self stubMessagePattern:messagePattern andReturn:nil];
260}
261
262- (void)stub:(SEL)aSelector andReturn:(id)aValue {
263 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
264 [self stubMessagePattern:messagePattern andReturn:aValue];
265}
266
267- (void)stub:(SEL)aSelector andReturn:(id)aValue withArguments:(id)firstArgument, ... {
268 va_list argumentList;
269 va_start(argumentList, firstArgument);
270 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
271 [self stubMessagePattern:messagePattern andReturn:aValue];
272}
273
274- (id)stub {
275 NSDictionary *userInfo = @{ExpectOrStubTagKey: StubTag};
276 return [KWInvocationCapturer invocationCapturerWithDelegate:self userInfo:userInfo];
277}
278
279- (id)stubAndReturn:(id)aValue {
280 NSDictionary *userInfo = @{ExpectOrStubTagKey: StubTag,
281 StubValueKey: aValue};
282 return [KWInvocationCapturer invocationCapturerWithDelegate:self userInfo:userInfo];
283}
284
285- (id)stubAndReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue {
286 NSDictionary *userInfo = @{ExpectOrStubTagKey: StubTag, StubValueKey: aValue, ChangeStubValueAfterTimesKey: times, StubSecondValueKey: aSecondValue};
287 return [KWInvocationCapturer invocationCapturerWithDelegate:self userInfo:userInfo];
288}
289
290- (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue {
291 [self stubMessagePattern:aMessagePattern andReturn:aValue overrideExisting:YES];
292}
293
294- (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue overrideExisting:(BOOL)overrideExisting {
295 [self expectMessagePattern:aMessagePattern];
296 KWStub *existingStub = [self currentStubWithMessagePattern:aMessagePattern];
297 if (existingStub) {
298 if (overrideExisting) {
299 [self.stubs removeObject:existingStub];
300 } else {
301 return;
302 }
303 }
304 KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern value:aValue];
305 [self.stubs addObject:stub];
306}
307
308- (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block {
309 [self expectMessagePattern:aMessagePattern];
310 [self removeStubWithMessagePattern:aMessagePattern];
311 KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern block:block];
312 [self.stubs addObject:stub];
313}
314
315- (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue {
316 [self expectMessagePattern:aMessagePattern];
317 [self removeStubWithMessagePattern:aMessagePattern];
318 KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern value:aValue times:times afterThatReturn:aSecondValue];
319 [self.stubs addObject:stub];
320}
321
322- (void)clearStubs {
323 [self.stubs removeAllObjects];
324}
325
326#pragma mark -
327#pragma mark Spying on Messages
328
329- (void)addMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
330 [self expectMessagePattern:aMessagePattern];
331 NSMutableArray *messagePatternSpies = (self.messageSpies)[aMessagePattern];
332
333 if (messagePatternSpies == nil) {
334 messagePatternSpies = [[NSMutableArray alloc] init];
335 (self.messageSpies)[aMessagePattern] = messagePatternSpies;
336 [messagePatternSpies release];
337 }
338 NSValue *spyWrapper = [NSValue valueWithNonretainedObject:aSpy];
339
340 if (![messagePatternSpies containsObject:spyWrapper])
341 [messagePatternSpies addObject:spyWrapper];
342}
343
344- (void)removeMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
345 NSValue *spyWrapper = [NSValue valueWithNonretainedObject:aSpy];
346 NSMutableArray *messagePatternSpies = (self.messageSpies)[aMessagePattern];
347 [messagePatternSpies removeObject:spyWrapper];
348}
349
350#pragma mark -
351#pragma mark Expecting Message Patterns
352
353- (void)expect:(SEL)aSelector {
354 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
355 [self expectMessagePattern:messagePattern];
356}
357
358- (void)expect:(SEL)aSelector withArguments:(id)firstArgument, ... {
359 va_list argumentList;
360 va_start(argumentList, firstArgument);
361 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
362 [self expectMessagePattern:messagePattern];
363}
364
365- (id)expect {
366 NSDictionary *userInfo = @{ExpectOrStubTagKey: ExpectTag};
367 return [KWInvocationCapturer invocationCapturerWithDelegate:self userInfo:userInfo];
368}
369
370- (void)expectMessagePattern:(KWMessagePattern *)aMessagePattern {
371 if (![self.expectedMessagePatterns containsObject:aMessagePattern])
372 [self.expectedMessagePatterns addObject:aMessagePattern];
373}
374
375#pragma mark -
376#pragma mark Capturing Invocations
377
378- (NSMethodSignature *)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer methodSignatureForSelector:(SEL)aSelector {
379 return [self methodSignatureForSelector:aSelector];
380}
381
382- (void)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer didCaptureInvocation:(NSInvocation *)anInvocation {
383 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternFromInvocation:anInvocation];
384 NSString *tag = (anInvocationCapturer.userInfo)[ExpectOrStubTagKey];
385 if ([tag isEqualToString:StubTag]) {
386 id value = (anInvocationCapturer.userInfo)[StubValueKey];
387 if (!(anInvocationCapturer.userInfo)[StubSecondValueKey]) {
388 [self stubMessagePattern:messagePattern andReturn:value];
389 } else {
390 id times = (anInvocationCapturer.userInfo)[ChangeStubValueAfterTimesKey];
391 id secondValue = (anInvocationCapturer.userInfo)[StubSecondValueKey];
392 [self stubMessagePattern:messagePattern andReturn:value times:times afterThatReturn:secondValue];
393 }
394 } else {
395 [self expectMessagePattern:messagePattern];
396 }
397}
398
399#pragma mark -
400#pragma mark Handling Invocations
401
402- (NSString *)namePhrase {
403 if (self.mockName == nil)
404 return @"mock";
405 else
406 return [NSString stringWithFormat:@"mock \"%@\"", self.mockName];
407}
408
409- (BOOL)processReceivedInvocation:(NSInvocation *)invocation {
410 for (KWMessagePattern *messagePattern in self.messageSpies) {
411 if ([messagePattern matchesInvocation:invocation]) {
412 NSArray *spies = (self.messageSpies)[messagePattern];
413
414 for (NSValue *spyWrapper in spies) {
415 id spy = [spyWrapper nonretainedObjectValue];
416 [spy object:self didReceiveInvocation:invocation];
417 }
418 }
419 }
420
421 for (KWStub *stub in self.stubs) {
422 if ([stub processInvocation:invocation])
423 return YES;
424 }
425
426 return NO;
427}
428
429- (NSMethodSignature *)mockedProtocolMethodSignatureForSelector:(SEL)aSelector {
430 NSSet *protocols = [self mockedProtocolTransitiveClosureSet];
431
432 for (Protocol *protocol in protocols) {
433 struct objc_method_description description = protocol_getMethodDescription(protocol, aSelector, NO, YES);
434
435 if (description.types == nil)
436 description = protocol_getMethodDescription(protocol, aSelector, YES, YES);
437
438 if (description.types != nil)
439 return [NSMethodSignature signatureWithObjCTypes:description.types];
440 }
441
442 return nil;
443}
444
445- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
446 NSMethodSignature *methodSignature = [self.mockedClass instanceMethodSignatureForSelector:aSelector];
447
448 if (methodSignature != nil)
449 return methodSignature;
450
451 methodSignature = [self mockedProtocolMethodSignatureForSelector:aSelector];
452
453 if (methodSignature != nil)
454 return methodSignature;
455
456 NSString *encoding = KWEncodingForVoidMethod();
457 return [NSMethodSignature signatureWithObjCTypes:[encoding UTF8String]];
458}
459
460- (void)forwardInvocation:(NSInvocation *)anInvocation {
461#if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
462 @try {
463#endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
464
465 if ([self processReceivedInvocation:anInvocation])
466 return;
467
468 if (isPartialMock)
469 [anInvocation invokeWithTarget:self.mockedObject];
470
471 if (self.isNullMock)
472 return;
473
474 for (KWMessagePattern *expectedMessagePattern in self.expectedMessagePatterns) {
475 if ([expectedMessagePattern matchesInvocation:anInvocation])
476 return;
477 }
478
479 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternFromInvocation:anInvocation];
480 [NSException raise:@"KWMockException" format:@"%@ received unexpected message -%@",
481 [self namePhrase],
482 [messagePattern stringValue]];
483
484#if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
485 } @catch (NSException *exception) {
486 KWSetExceptionFromAcrossInvocationBoundary(exception);
487 }
488#endif // #if KW_TARGET_HAS_INVOCATION_EXCEPTION_BUG
489}
490
491#pragma mark -
492#pragma mark Testing Objects
493
494- (BOOL)mockedClassHasAncestorClass:(Class)aClass {
495 Class currentClass = self.mockedClass;
496
497 while (currentClass != nil) {
498 if (currentClass == aClass)
499 return YES;
500
501 currentClass = [currentClass superclass];
502 }
503
504 return NO;
505}
506
507- (BOOL)mockedClassRespondsToSelector:(SEL)aSelector {
508 return [self.mockedClass instancesRespondToSelector:aSelector];
509}
510
511- (BOOL)mockedClassConformsToProtocol:(Protocol *)aProtocol {
512 return [self.mockedClass conformsToProtocol:aProtocol];
513}
514
515- (BOOL)mockedProtocolRespondsToSelector:(SEL)aSelector {
516 NSSet *protocols = [self mockedProtocolTransitiveClosureSet];
517
518 for (Protocol *protocol in protocols) {
519 struct objc_method_description description = protocol_getMethodDescription(protocol, aSelector, NO, YES);
520
521 if (description.types == nil)
522 description = protocol_getMethodDescription(protocol, aSelector, YES, YES);
523
524 if (description.types != nil)
525 return YES;
526 }
527
528 return NO;
529}
530
531- (BOOL)mockedProtocolConformsToProtocol:(Protocol *)aProtocol {
532 if (self.mockedProtocol == nil)
533 return NO;
534
535 return protocol_isEqual(self.mockedProtocol, aProtocol) || protocol_conformsToProtocol(self.mockedProtocol, aProtocol);
536}
537
538- (BOOL)isKindOfClass:(Class)aClass {
539 return [self mockedClassHasAncestorClass:aClass] || [super isKindOfClass:aClass];
540}
541
542- (BOOL)isMemberOfClass:(Class)aClass {
543 return self.mockedClass == aClass || [super isMemberOfClass:aClass];
544}
545
546- (BOOL)respondsToSelector:(SEL)aSelector {
547 return [self mockedClassRespondsToSelector:aSelector] ||
548 [self mockedProtocolRespondsToSelector:aSelector] ||
549 [super respondsToSelector:aSelector];
550}
551
552- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
553 return [self mockedClassConformsToProtocol:aProtocol] ||
554 [self mockedProtocolConformsToProtocol:aProtocol] ||
555 [super conformsToProtocol:aProtocol];
556}
557
558#pragma mark -
559#pragma mark Whitelisted NSObject Methods
560
561- (BOOL)isEqual:(id)anObject {
562 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
563 [self expectMessagePattern:messagePattern];
564 NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd messageArguments:&anObject];
565
566 if ([self processReceivedInvocation:invocation]) {
567 BOOL result = NO;
568 [invocation getReturnValue:&result];
569 return result;
570 } else {
571 return [super isEqual:anObject];
572 }
573}
574
575- (NSUInteger)hash {
576 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
577 [self expectMessagePattern:messagePattern];
578 NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd];
579
580 if ([self processReceivedInvocation:invocation]) {
581 NSUInteger result = 0;
582 [invocation getReturnValue:&result];
583 return result;
584 } else {
585 return [super hash];
586 }
587}
588
589- (NSString *)description {
590 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
591 [self expectMessagePattern:messagePattern];
592 NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd];
593
594 if ([self processReceivedInvocation:invocation]) {
595 NSString *result = nil;
596 [invocation getReturnValue:&result];
597 return result;
598 } else {
599 return [super description];
600 }
601}
602
603- (id)copy {
604 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
605 [self expectMessagePattern:messagePattern];
606 NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd];
607
608 if ([self processReceivedInvocation:invocation]) {
609 id result = nil;
610 [invocation getReturnValue:&result];
611 return result;
612 } else {
613 return [super copy];
614 }
615}
616
617- (id)mutableCopy {
618 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
619 [self expectMessagePattern:messagePattern];
620 NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd];
621
622 if ([self processReceivedInvocation:invocation]) {
623 id result = nil;
624 [invocation getReturnValue:&result];
625 return result;
626 } else {
627 return [super mutableCopy];
628 }
629}
630
631#pragma mark -
632#pragma mark Key-Value Coding Support
633
634static id valueForKeyImplementation(id self, SEL _cmd, id key) {
635 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
636 [self expectMessagePattern:messagePattern];
637 NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd messageArguments:&key];
638
639 if ([self processReceivedInvocation:invocation]) {
640 id result = nil;
641 [invocation getReturnValue:&result];
642 return result;
643 } else {
644 return nil;
645 }
646}
647
648- (id)valueForKey:(NSString *)key {
649 return valueForKeyImplementation(self, _cmd, key);
650}
651
652- (id)valueForKeyPath:(NSString *)keyPath {
653 return valueForKeyImplementation(self, _cmd, keyPath);
654}
655
656static void setValueForKeyImplementation(id self, SEL _cmd, id a, id b) {
657 KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:_cmd];
658 [self expectMessagePattern:messagePattern];
659 NSInvocation *invocation = [NSInvocation invocationWithTarget:self selector:_cmd messageArguments:&a, &b];
660
661 [self processReceivedInvocation:invocation];
662}
663
664- (void)setValue:(id)value forKey:(NSString *)key {
665 setValueForKeyImplementation(self, _cmd, value, key);
666}
667
668- (void)setValue:(id)value forKeyPath:(NSString *)keyPath {
669 setValueForKeyImplementation(self, _cmd, value, keyPath);
670}
671
672@end