DDSMocking.m 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // Software License Agreement (BSD License)
  2. //
  3. // Copyright (c) 2010-2025, Deusty, LLC
  4. // All rights reserved.
  5. //
  6. // Redistribution and use of this software in source and binary forms,
  7. // with or without modification, are permitted provided that the following conditions are met:
  8. //
  9. // * Redistributions of source code must retain the above copyright notice,
  10. // this list of conditions and the following disclaimer.
  11. //
  12. // * Neither the name of Deusty nor the names of its contributors may be used
  13. // to endorse or promote products derived from this software without specific
  14. // prior written permission of Deusty, LLC.
  15. #import "DDSMocking.h"
  16. @implementation DDSMocking
  17. @end
  18. @implementation DDBasicMockArgument
  19. - (instancetype)initWithBlock:(void(^)(id object))block {
  20. if (self = [super init]) {
  21. self.block = block;
  22. }
  23. return self;
  24. }
  25. + (instancetype)alongsideWithBlock:(void(^)(id object))block {
  26. return [[self alloc] initWithBlock:block];
  27. }
  28. - (id)copyWithZone:(NSZone *)zone {
  29. return [self.class alongsideWithBlock:self.block];
  30. }
  31. @end
  32. @implementation DDBasicMockArgumentPosition
  33. - (instancetype)initWithSelector:(NSString *)selector position:(NSNumber *)position {
  34. if (self = [super init]) {
  35. self.selector = selector;
  36. self.position = position;
  37. }
  38. return self;
  39. }
  40. - (id)copyWithZone:(NSZone *)zone {
  41. return [[self.class alloc] initWithSelector:self.selector position:self.position];
  42. }
  43. - (BOOL)isEqual:(id)object {
  44. if (object == self) {
  45. return YES;
  46. }
  47. if (![object isKindOfClass:self.class]) {
  48. return NO;
  49. }
  50. __auto_type position = (DDBasicMockArgumentPosition *)object;
  51. return [position.selector isEqualToString:self.selector] && [position.position isEqualToNumber:self.position];
  52. }
  53. - (NSUInteger)hash {
  54. return [self.selector hash] + [self.position hash];
  55. }
  56. - (NSString *)debugDescription {
  57. return [NSString stringWithFormat:@"%@ selector: %@ position: %@", [super debugDescription], self.selector, self.position];
  58. }
  59. - (NSString *)description {
  60. return [NSString stringWithFormat:@"%@ selector: %@ position: %@", [super description], self.selector, self.position];
  61. }
  62. @end
  63. @interface DDBasicMock ()
  64. @property (strong, nonatomic, readwrite) id object;
  65. @property (assign, nonatomic, readwrite) BOOL stubEnabled;
  66. @property (copy, nonatomic, readwrite) NSDictionary <DDBasicMockArgumentPosition *, DDBasicMockArgument *>*positionsAndArguments; // extend later to NSArray if needed.
  67. @end
  68. @implementation DDBasicMock
  69. - (instancetype)initWithInstance:(id)object {
  70. self.object = object;
  71. self.positionsAndArguments = [NSDictionary new];
  72. return self;
  73. }
  74. + (instancetype)decoratedInstance:(id)object {
  75. return [[self alloc] initWithInstance:object];
  76. }
  77. - (instancetype)enableStub {
  78. self.stubEnabled = YES;
  79. return self;
  80. }
  81. - (instancetype)disableStub {
  82. self.stubEnabled = NO;
  83. return self;
  84. }
  85. - (void)addArgument:(DDBasicMockArgument *)argument forSelector:(SEL)selector atIndex:(NSInteger)index {
  86. NSMutableDictionary *dictionary = [self.positionsAndArguments mutableCopy];
  87. __auto_type thePosition = [[DDBasicMockArgumentPosition alloc] initWithSelector:NSStringFromSelector(selector)
  88. position:@(index)];
  89. dictionary[thePosition] = [argument copy];
  90. self.positionsAndArguments = dictionary;
  91. }
  92. - (void)forwardInvocation:(NSInvocation *)invocation {
  93. __auto_type numberOfArguments = [[invocation methodSignature] numberOfArguments];
  94. BOOL found = NO;
  95. for (NSUInteger i = 2; i < numberOfArguments; ++i) {
  96. void *abc = nil;
  97. [invocation getArgument:&abc atIndex:i];
  98. id argument = (__bridge id)(abc);
  99. __auto_type thePosition = [[DDBasicMockArgumentPosition alloc] initWithSelector:NSStringFromSelector(invocation.selector) position:@(i)];
  100. __auto_type argListener = self.positionsAndArguments[thePosition];
  101. if (argListener) {
  102. found = YES;
  103. argListener.block(argument);
  104. }
  105. }
  106. if (!found) {
  107. [invocation invokeWithTarget:self.object];
  108. }
  109. }
  110. - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
  111. return [self.object methodSignatureForSelector:sel];
  112. }
  113. - (BOOL)respondsToSelector:(SEL)aSelector {
  114. return [self.object respondsToSelector:aSelector];
  115. }
  116. @end