Permalink
Browse files

Spy expectations handle object arguments and exceptions properly.

  • Loading branch information...
Adam Milligan
Adam Milligan committed Jan 28, 2012
1 parent 232641e commit a1ae0d83da6a8f05cca34c3b2e92b1ccbd6170c7
@@ -620,7 +620,7 @@
children = (
6628FC9B14C4DEC50016652A /* CDRSpy.mm */,
);
- name = Doubles;
+ path = Doubles;
sourceTree = "<group>";
};
66F00B4F14C4D8F600146D88 /* Doubles */ = {
@@ -66,9 +66,11 @@ - (void)asOriginalObject:(void(^)())block {
Class spyClass = object_getClass(self);
object_setClass(self, objc_getAssociatedObject(self, @"original-class"));
- block();
-
- object_setClass(self, spyClass);
+ @try {
+ block();
+ } @finally {
+ object_setClass(self, spyClass);
+ }
}
@end
@@ -1,6 +1,7 @@
#import <Foundation/Foundation.h>
#import "CompareEqual.h"
#import "CedarStringifiers.h"
+#import "CedarComparators.h"
namespace Cedar { namespace Doubles {
@@ -46,14 +47,14 @@ namespace Cedar { namespace Doubles {
/* virtual */ const char * TypedArgument<T>::value_encoding() const {
return @encode(T);
}
-
+
template<typename T>
/* virtual */ NSString * TypedArgument<T>::value_string() const {
return Matchers::Stringifiers::string_for(value_);
}
-
+
template<typename T>
/* virtual */ bool TypedArgument<T>::matches_bytes(void * expectedArgumentBytes) const {
- return value_ == *(static_cast<T *>(expectedArgumentBytes));
+ return Matchers::Comparators::compare_equal(value_, *(static_cast<T *>(expectedArgumentBytes)));
}
}}
@@ -13,8 +13,7 @@ namespace Cedar { namespace Doubles {
// Allow default copy ctor.
template<typename T>
- HaveReceived & with(const T & );
-
+ HaveReceived & with(const T &);
template<typename T>
HaveReceived & and_with(const T & argument) { return with(argument); }
@@ -24,8 +23,10 @@ namespace Cedar { namespace Doubles {
virtual NSString * failure_message_end() const;
private:
- bool matches_invocation(NSInvocation * const invocation) const;
- bool matches_arguments(NSInvocation * const invocation) const;
+ void verify_object_is_a_double(id) const;
+ bool matches_invocation(NSInvocation * const) const;
+ bool matches_arguments(NSInvocation * const) const;
+ void verify_correct_number_of_arguments(NSInvocation * const) const;
private:
const SEL expectedSelector_;
@@ -45,8 +46,8 @@ namespace Cedar { namespace Doubles {
return *this;
}
- // I'd like to have this in a separate implementation file, but doing so generates
- // an inscrutable compiler error:
+ // This belongs in a separate implementation file, but doing so generates an
+ // inscrutable linker error:
//
// ld: bad codegen, pointer diff in ___block_global_9 to global weak symbol
// __ZTVN5Cedar8Matchers4BaseINS0_18BaseMessageBuilderEEE for architecture i386
@@ -71,53 +72,73 @@ namespace Cedar { namespace Doubles {
arguments_.clear();
}
+ inline bool HaveReceived::matches(id instance) const {
+ this->verify_object_is_a_double(instance);
+
+ for (NSInvocation *invocation in [instance sent_messages]) {
+ if (this->matches_invocation(invocation)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ inline void HaveReceived::verify_object_is_a_double(id instance) const {
+ if (![instance respondsToSelector:@selector(sent_messages)]) {
+ [[CDRSpecFailure specFailureWithReason:[NSString stringWithFormat:@"Received expectation for non-double object <%@>", instance]] raise];
+ }
+ }
+
+#pragma mark Protected interface
inline /*virtual*/ NSString * HaveReceived::failure_message_end() const {
NSMutableString *message = [NSMutableString stringWithFormat:@"have received message <%@>", NSStringFromSelector(expectedSelector_)];
if (arguments_.size()) {
[message appendString:@", with arguments: <"];
- for (arguments_vector_t::const_iterator cit = arguments_.begin(); cit != arguments_.end(); ++cit) {
- [message appendString:(*cit)->value_string()];
+ arguments_vector_t::const_iterator cit = arguments_.begin();
+ [message appendString:(*cit++)->value_string()];
+ for (; cit != arguments_.end(); ++cit) {
+ [message appendString:[NSString stringWithFormat:@", %@", (*cit)->value_string()]];
}
[message appendString:@">"];
}
return message;
}
- inline bool HaveReceived::matches(id instance) const {
- for (NSInvocation *invocation in [instance sent_messages]) {
- if (this->matches_invocation(invocation)) {
- return true;
- }
- }
- return false;
- }
-
+#pragma mark Private interface
inline bool HaveReceived::matches_invocation(NSInvocation * const invocation) const {
return sel_isEqual(invocation.selector, expectedSelector_) && this->matches_arguments(invocation);
}
inline bool HaveReceived::matches_arguments(NSInvocation * const invocation) const {
- bool matches = true;
+ this->verify_correct_number_of_arguments(invocation);
+ bool matches = true;
size_t index = 2;
for (arguments_vector_t::const_iterator cit = arguments_.begin(); cit != arguments_.end() && matches; ++cit, ++index) {
const char *actualArgumentEncoding = [invocation.methodSignature getArgumentTypeAtIndex:index];
NSUInteger actualArgumentSize;
NSGetSizeAndAlignment(actualArgumentEncoding, &actualArgumentSize, nil);
- void * actualArgumentBytes;
- try {
- actualArgumentBytes = malloc(actualArgumentSize);
- [invocation getArgument:actualArgumentBytes atIndex:index];
- matches = (*cit)->matches_bytes(actualArgumentBytes);
- free(actualArgumentBytes);
- }
- catch (...) {
- free(actualArgumentBytes);
- throw;
- }
+ char actualArgumentBytes[actualArgumentSize];
+ [invocation getArgument:&actualArgumentBytes atIndex:index];
+ matches = (*cit)->matches_bytes(&actualArgumentBytes);
}
return matches;
}
+ inline void HaveReceived::verify_correct_number_of_arguments(NSInvocation * const invocation) const {
+ size_t expectedArgumentCount = arguments_.size();
+ size_t actualArgumentCount = invocation.methodSignature.numberOfArguments - 2; //
+
+ if (expectedArgumentCount) {
+ if (expectedArgumentCount < actualArgumentCount) {
+ NSString *reason = [NSString stringWithFormat:@"Too few parameters expected for message <%@>; required %d, expected %d", NSStringFromSelector(expectedSelector_), actualArgumentCount, expectedArgumentCount];
+ [[CDRSpecFailure specFailureWithReason:reason] raise];
+ } else if (expectedArgumentCount > actualArgumentCount) {
+ NSString *reason = [NSString stringWithFormat:@"Too many parameters expected for message <%@>; required %d, expected %d", NSStringFromSelector(expectedSelector_), actualArgumentCount, expectedArgumentCount];
+ [[CDRSpecFailure specFailureWithReason:reason] raise];
+ }
+ }
+ }
+
}}
Oops, something went wrong.

0 comments on commit a1ae0d8

Please sign in to comment.