]> git.wincent.com - WOTest.git/blob - WOProtocolStub.m
Fix object-to-pointer comparisons on Leopard
[WOTest.git] / WOProtocolStub.m
1 //
2 //  WOProtocolStub.m
3 //  WOTest
4 //
5 //  Created by Wincent Colaiuta on 10 February 2006.
6 //
7 //  Copyright 2006-2007 Wincent Colaiuta.
8 //  This program is free software: you can redistribute it and/or modify
9 //  it under the terms of the GNU General Public License as published by
10 //  the Free Software Foundation, either version 3 of the License, or
11 //  (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 //
21
22 // class header
23 #import "WOProtocolStub.h"
24
25 // system headers
26
27 #import <objc/Protocol.h>
28
29 // framework headers
30
31 #import "NSInvocation+WOTest.h"
32 #import "NSMethodSignature+WOTest.h"
33 #import "NSObject+WOTest.h"
34 #import "WOProtocolMock.h"  /* for WOStringFromProtocol() */
35
36 @implementation WOProtocolStub
37
38 #pragma mark -
39 #pragma mark Creation
40
41 /*! Factory method. */
42 + (id)stubForProtocol:(Protocol *)aProtocol withDelegate:(id)aDelegate
43 {
44     NSParameterAssert(aProtocol != NULL);
45     return [[[self alloc] initWithProtocol:aProtocol delegate:aDelegate] autorelease];
46 }
47
48 /*! Designated initializer. */
49 - (id)initWithProtocol:(Protocol *)aProtocol delegate:(id)aDelegate
50 {
51     NSParameterAssert(aProtocol != NULL);
52     if ((self = [super init]))
53     {
54         mockedProtocol  = aProtocol;
55         delegate        = aDelegate;
56     }
57     return self;
58 }
59
60 #pragma mark -
61 #pragma mark NSProxy
62
63 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
64 {
65     // avoid an infinite loop (docs warn "Be sure to avoid an infinite loop when necessary by checking that aSelector isn't the
66         // selector for this method itself and by not sending any message that might invoke this method.")
67     if (aSelector == _cmd) return nil;
68
69         Protocol *protocol = [self mockedProtocol];
70         BOOL isRequiredMethod = YES;    // no idea what to pass here
71         struct objc_method_description description = protocol_getMethodDescription(protocol, aSelector, isRequiredMethod, YES);
72     
73         // TODO: not sure how to test for missing method signatures... protocol_getMethodDescription() is not documented
74         if (description.name == NULL)
75         [NSException raise:NSInternalInconsistencyException format:@"No method signature for selector %@ in %@ protocol",
76                  NSStringFromSelector(aSelector), WOStringFromProtocol(protocol)];      
77     return [NSMethodSignature WOTest_signatureBasedOnObjCTypes:description.types];
78 }    
79
80 #pragma mark -
81 #pragma mark NSObject protocol
82
83 - (BOOL)isEqual:(id)anObject
84 {
85     if (anObject == self) return YES;
86     @try
87     {
88         if ([NSObject WOTest_object:anObject isKindOfClass:[self class]])
89         {
90             BOOL            invocationsAreEqual     = NO;
91             BOOL            returnValuesAreEqual    = NO;
92             BOOL            exceptionsAreEqual      = NO;
93             NSInvocation    *thisInvocation         = [self invocation];
94             NSInvocation    *otherInvocation        = [anObject invocation];
95             NSValue         *thisValue              = [self returnValue];
96             NSValue         *otherValue             = [anObject returnValue];
97             id              thisException           = [self exception];
98             id              otherException          = [anObject exception];
99             
100             if ([self mockedProtocol] != [anObject mockedProtocol])
101                 return NO;
102             
103             if ([self acceptsAnyArguments] != [anObject acceptsAnyArguments])
104                 return NO;
105             
106             if (!thisInvocation && !otherInvocation) // both nil
107                 invocationsAreEqual = YES;
108             else if (thisInvocation && otherInvocation) // both non-nil
109             {
110                 if ([self acceptsAnyArguments])
111                     invocationsAreEqual = [thisInvocation WOTest_isEqualToInvocationIgnoringArguments:otherInvocation];
112                 else
113                     invocationsAreEqual = [thisInvocation WOTest_isEqualToInvocation:otherInvocation];
114             }
115             
116             if (!thisValue && !otherValue) // both nil
117                 returnValuesAreEqual = YES;
118             else if (thisValue && otherValue) // both non-nil
119                 returnValuesAreEqual = [thisValue isEqual:otherValue];
120             
121             if (!thisException && !otherException) // both nil
122                 exceptionsAreEqual = YES;
123             else if (thisException && otherException) // both non-nil
124                 exceptionsAreEqual = [thisException isEqual:otherException];
125             
126             if (invocationsAreEqual && returnValuesAreEqual && 
127                 exceptionsAreEqual)
128                 return YES;
129         }
130     }
131     @catch (id e) {}
132     return NO;
133 }
134
135 - (unsigned)hash
136 {
137     // hash must not rely on the object's internal state information (see docs)
138     // hash must not change while in a collection
139     return (unsigned)mockedProtocol;
140 }
141
142 #pragma mark -
143 #pragma mark Accessors
144
145 - (Protocol *)mockedProtocol
146 {
147     return mockedProtocol; 
148 }
149
150 @end