Skip to content

Commit f7f5f73

Browse files
Avoid leaks false positive in CoreBluetooth mock
1 parent 7412708 commit f7f5f73

File tree

1 file changed

+21
-10
lines changed

1 file changed

+21
-10
lines changed

src/darwin/Framework/CHIPTests/TestHelpers/MTRMockCB.m

+21-10
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616

1717
#import "MTRMockCB.h"
1818

19-
#import <CoreBluetooth/CoreBluetooth.h>
19+
#import "MTRDefines_Internal.h"
20+
2021
#import <XCTest/XCTest.h>
2122
#import <objc/runtime.h>
2223
#import <os/log.h>
24+
#import <stdatomic.h>
2325

2426
NS_ASSUME_NONNULL_BEGIN
2527

@@ -147,11 +149,6 @@ static void InterceptClassMethod(__strong os_block_t * inOutCleanup, Class cls,
147149
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:reason userInfo:nil];
148150
}
149151
IMP originalImp = method_getImplementation(method);
150-
if (imp_getBlock(originalImp)) {
151-
NSString * reason = [NSString stringWithFormat:@"+[%@ %@] was already intercepted",
152-
NSStringFromClass(cls), NSStringFromSelector(sel)];
153-
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:reason userInfo:nil];
154-
}
155152

156153
// Try to add the method first, in case it came from a super class.
157154
// Note we need to pass the meta-class to class_addMethod().
@@ -169,16 +166,29 @@ static void InterceptClassMethod(__strong os_block_t * inOutCleanup, Class cls,
169166
// inherited implementation; this is good enough for our purposes here.
170167
method_setImplementation(method, originalImp);
171168
imp_removeBlock(newImp); // otherwise the block might leak
172-
if (nextCleanup) {
173-
nextCleanup();
174-
}
169+
(void) block; // keep an obvious reference to avoid `leaks` false positives before cleanup
170+
nextCleanup();
175171
};
176172
}
177173

178174
- (instancetype)init
179175
{
180176
self = [super init];
181177
_log = os_log_create("com.csa.matter", "mock");
178+
179+
static atomic_flag sInitialized = ATOMIC_FLAG_INIT;
180+
if (atomic_flag_test_and_set(&sInitialized)) {
181+
os_log_error(_log, "CoreBluetooth mocking is already enabled");
182+
return nil;
183+
}
184+
185+
mtr_weakify(self);
186+
_invalidate = ^{
187+
mtr_strongify(self);
188+
self->_invalidate = nil;
189+
atomic_flag_clear(&sInitialized);
190+
};
191+
182192
_queue = dispatch_queue_create("mock.cb", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
183193
dispatch_queue_set_specific(_queue, (__bridge void *) self, @YES, nil); // mark our queue
184194

@@ -191,7 +201,8 @@ - (instancetype)init
191201
// Replace implementations of class methods we need to mock. We don't need to intercept
192202
// any instance methods directly, because we're returning a mock object from `alloc`.
193203
InterceptClassMethod(&_invalidate, CBCentralManager.class, @selector(alloc), ^id NS_RETURNS_RETAINED(void) {
194-
return [[MTRMockCBCentralManager alloc] _initWithMock:self];
204+
mtr_strongify(self);
205+
return self ? [[MTRMockCBCentralManager alloc] _initWithMock:self] : nil;
195206
});
196207
InterceptClassMethod(&_invalidate, CBCentralManager.class, @selector(supportsFeatures:), ^BOOL(CBCentralManagerFeature features) {
197208
return [MTRMockCBCentralManager supportsFeatures:features];

0 commit comments

Comments
 (0)