OCMInvocationMatcher.m 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /*
  2. * Copyright (c) 2014-2015 Erik Doernenburg and contributors
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. * not use these files except in compliance with the License. You may obtain
  6. * a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations
  14. * under the License.
  15. */
  16. #import <objc/runtime.h>
  17. #import <OCMock/OCMArg.h>
  18. #import <OCMock/OCMConstraint.h>
  19. #import "OCMPassByRefSetter.h"
  20. #import "NSInvocation+OCMAdditions.h"
  21. #import "OCMInvocationMatcher.h"
  22. #import "OCClassMockObject.h"
  23. #import "OCMFunctions.h"
  24. @interface NSObject(HCMatcherDummy)
  25. - (BOOL)matches:(id)item;
  26. @end
  27. @implementation OCMInvocationMatcher
  28. - (void)dealloc
  29. {
  30. [recordedInvocation release];
  31. [super dealloc];
  32. }
  33. - (void)setInvocation:(NSInvocation *)anInvocation
  34. {
  35. [recordedInvocation release];
  36. // When the method has a char* argument we do not retain the arguments. This makes it possible
  37. // to match char* args literally and with anyPointer. Not retaining the argument means that
  38. // in these cases tests that use their own autorelease pools may fail unexpectedly.
  39. if(![anInvocation hasCharPointerArgument])
  40. [anInvocation retainArguments];
  41. recordedInvocation = [anInvocation retain];
  42. }
  43. - (void)setRecordedAsClassMethod:(BOOL)flag
  44. {
  45. recordedAsClassMethod = flag;
  46. }
  47. - (BOOL)recordedAsClassMethod
  48. {
  49. return recordedAsClassMethod;
  50. }
  51. - (void)setIgnoreNonObjectArgs:(BOOL)flag
  52. {
  53. ignoreNonObjectArgs = flag;
  54. }
  55. - (NSString *)description
  56. {
  57. return [recordedInvocation invocationDescription];
  58. }
  59. - (NSInvocation *)recordedInvocation
  60. {
  61. return recordedInvocation;
  62. }
  63. - (BOOL)matchesSelector:(SEL)sel
  64. {
  65. if(sel == [recordedInvocation selector])
  66. return YES;
  67. if(OCMIsAliasSelector(sel) &&
  68. OCMOriginalSelectorForAlias(sel) == [recordedInvocation selector])
  69. return YES;
  70. return NO;
  71. }
  72. - (BOOL)matchesInvocation:(NSInvocation *)anInvocation
  73. {
  74. id target = [anInvocation target];
  75. BOOL isClassMethodInvocation = (target != nil) && (target == [target class]);
  76. if(isClassMethodInvocation != recordedAsClassMethod)
  77. return NO;
  78. if(![self matchesSelector:[anInvocation selector]])
  79. return NO;
  80. NSMethodSignature *signature = [recordedInvocation methodSignature];
  81. NSUInteger n = [signature numberOfArguments];
  82. for(NSUInteger i = 2; i < n; i++)
  83. {
  84. if(ignoreNonObjectArgs && strcmp([signature getArgumentTypeAtIndex:i], @encode(id)))
  85. {
  86. continue;
  87. }
  88. id recordedArg = [recordedInvocation getArgumentAtIndexAsObject:i];
  89. id passedArg = [anInvocation getArgumentAtIndexAsObject:i];
  90. if([recordedArg isProxy])
  91. {
  92. if(![recordedArg isEqual:passedArg])
  93. return NO;
  94. continue;
  95. }
  96. if([recordedArg isKindOfClass:[NSValue class]])
  97. recordedArg = [OCMArg resolveSpecialValues:recordedArg];
  98. if([recordedArg isKindOfClass:[OCMConstraint class]])
  99. {
  100. if([recordedArg evaluate:passedArg] == NO)
  101. return NO;
  102. }
  103. else if([recordedArg isKindOfClass:[OCMPassByRefSetter class]])
  104. {
  105. id valueToSet = [(OCMPassByRefSetter *)recordedArg value];
  106. // side effect but easier to do here than in handleInvocation
  107. if(![valueToSet isKindOfClass:[NSValue class]])
  108. *(id *)[passedArg pointerValue] = valueToSet;
  109. else
  110. [(NSValue *)valueToSet getValue:[passedArg pointerValue]];
  111. }
  112. else if([recordedArg conformsToProtocol:objc_getProtocol("HCMatcher")])
  113. {
  114. if([recordedArg matches:passedArg] == NO)
  115. return NO;
  116. }
  117. else
  118. {
  119. if(([recordedArg class] == [NSNumber class]) &&
  120. ([(NSNumber*)recordedArg compare:(NSNumber*)passedArg] != NSOrderedSame))
  121. return NO;
  122. if(([recordedArg isEqual:passedArg] == NO) &&
  123. !((recordedArg == nil) && (passedArg == nil)))
  124. return NO;
  125. }
  126. }
  127. return YES;
  128. }
  129. @end