From 7093a45b1ccb3ca5cabd0cb030d804ffc2b264ff Mon Sep 17 00:00:00 2001 From: Phillip Pan Date: Wed, 25 Oct 2023 09:13:06 -0700 Subject: [PATCH] setup test to use custom queue for RCTBlobManager operations instead of module queue (#41182) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/41182 Changelog: [Internal] in my quest to get rid of all synthesized methodQueues, we have RCTBlobManager which exposes its underlying execution queue. in this diff, i add a config that replaces that queue with one that is managed by the module itself instead of the one generated by the infra. Reviewed By: cipolleschi Differential Revision: D50587693 fbshipit-source-id: 993a13c617afe48c3989d8cd5ad5fbda050603f4 --- .../Libraries/Blob/RCTBlobCollector.mm | 2 +- .../Libraries/Blob/RCTBlobManager.h | 4 ++ .../Libraries/Blob/RCTBlobManager.mm | 30 ++++++++++- .../Libraries/Blob/RCTFileReaderModule.mm | 4 +- .../RNTesterUnitTests/RCTBlobManagerTests.m | 53 +++++++++++++++++++ 5 files changed, 88 insertions(+), 5 deletions(-) diff --git a/packages/react-native/Libraries/Blob/RCTBlobCollector.mm b/packages/react-native/Libraries/Blob/RCTBlobCollector.mm index c9afc78057c..9b2ca8cb3a0 100644 --- a/packages/react-native/Libraries/Blob/RCTBlobCollector.mm +++ b/packages/react-native/Libraries/Blob/RCTBlobCollector.mm @@ -21,7 +21,7 @@ RCTBlobCollector::~RCTBlobCollector() { RCTBlobManager *blobManager = blobManager_; NSString *blobId = [NSString stringWithUTF8String:blobId_.c_str()]; - dispatch_async([blobManager_ methodQueue], ^{ + dispatch_async([blobManager_ executionQueue], ^{ [blobManager remove:blobId]; }); } diff --git a/packages/react-native/Libraries/Blob/RCTBlobManager.h b/packages/react-native/Libraries/Blob/RCTBlobManager.h index 317b202911d..bceb8eb5d81 100755 --- a/packages/react-native/Libraries/Blob/RCTBlobManager.h +++ b/packages/react-native/Libraries/Blob/RCTBlobManager.h @@ -10,6 +10,8 @@ #import #import +RCT_EXTERN void RCTEnableBlobManagerProcessingQueue(BOOL enabled); + @interface RCTBlobManager : NSObject - (NSString *)store:(NSData *)data; @@ -26,4 +28,6 @@ - (void)createFromParts:(NSArray *> *)parts withId:(NSString *)blobId; +- (dispatch_queue_t)executionQueue; + @end diff --git a/packages/react-native/Libraries/Blob/RCTBlobManager.mm b/packages/react-native/Libraries/Blob/RCTBlobManager.mm index 14496f7e491..82c08978904 100755 --- a/packages/react-native/Libraries/Blob/RCTBlobManager.mm +++ b/packages/react-native/Libraries/Blob/RCTBlobManager.mm @@ -11,6 +11,7 @@ #import #import +#import #import #import #import @@ -18,6 +19,16 @@ #import "RCTBlobCollector.h" #import "RCTBlobPlugins.h" +RCT_MOCK_DEF(RCTBlobManager, dispatch_async); +#define dispatch_async RCT_MOCK_USE(RCTBlobManager, dispatch_async) + +static BOOL gBlobManagerProcessingQueueEnabled = NO; + +RCT_EXTERN void RCTEnableBlobManagerProcessingQueue(BOOL enabled) +{ + gBlobManagerProcessingQueueEnabled = enabled; +} + static NSString *const kBlobURIScheme = @"blob"; @interface RCTBlobManager () < @@ -35,6 +46,7 @@ static NSString *const kBlobURIScheme = @"blob"; std::mutex _blobsMutex; NSOperationQueue *_queue; + dispatch_queue_t _processingQueue; } RCT_EXPORT_MODULE(BlobModule) @@ -48,6 +60,10 @@ RCT_EXPORT_MODULE(BlobModule) std::lock_guard lock(_blobsMutex); _blobs = [NSMutableDictionary new]; + if (gBlobManagerProcessingQueueEnabled) { + _processingQueue = dispatch_queue_create("com.facebook.react.blobmanager.processing", DISPATCH_QUEUE_SERIAL); + } + facebook::react::RCTBlobCollector::install(self); } @@ -194,12 +210,22 @@ RCT_EXPORT_METHOD(createFromParts : (NSArray *> *)p [NSException raise:@"Invalid type for blob" format:@"%@ is invalid", type]; } } - [self store:data withId:blobId]; + + dispatch_async([self executionQueue], ^{ + [self store:data withId:blobId]; + }); } RCT_EXPORT_METHOD(release : (NSString *)blobId) { - [self remove:blobId]; + dispatch_async([self executionQueue], ^{ + [self remove:blobId]; + }); +} + +- (dispatch_queue_t)executionQueue +{ + return gBlobManagerProcessingQueueEnabled ? _processingQueue : _methodQueue; } #pragma mark - RCTURLRequestHandler methods diff --git a/packages/react-native/Libraries/Blob/RCTFileReaderModule.mm b/packages/react-native/Libraries/Blob/RCTFileReaderModule.mm index 685d86b48d8..caa554029f7 100644 --- a/packages/react-native/Libraries/Blob/RCTFileReaderModule.mm +++ b/packages/react-native/Libraries/Blob/RCTFileReaderModule.mm @@ -31,7 +31,7 @@ RCT_EXPORT_METHOD(readAsText : (RCTPromiseRejectBlock)reject) { RCTBlobManager *blobManager = [_moduleRegistry moduleForName:"BlobModule"]; - dispatch_async(blobManager.methodQueue, ^{ + dispatch_async([blobManager executionQueue], ^{ NSData *data = [blobManager resolve:blob]; if (data == nil) { @@ -62,7 +62,7 @@ RCT_EXPORT_METHOD(readAsDataURL : (RCTPromiseRejectBlock)reject) { RCTBlobManager *blobManager = [_moduleRegistry moduleForName:"BlobModule"]; - dispatch_async(blobManager.methodQueue, ^{ + dispatch_async([blobManager executionQueue], ^{ NSData *data = [blobManager resolve:blob]; if (data == nil) { diff --git a/packages/rn-tester/RNTesterUnitTests/RCTBlobManagerTests.m b/packages/rn-tester/RNTesterUnitTests/RCTBlobManagerTests.m index 2bbf23e657d..94ca812875e 100644 --- a/packages/rn-tester/RNTesterUnitTests/RCTBlobManagerTests.m +++ b/packages/rn-tester/RNTesterUnitTests/RCTBlobManagerTests.m @@ -8,6 +8,15 @@ #import #import +#import + +RCT_MOCK_REF(RCTBlobManager, dispatch_async); + +static void _mock_dispatch_async(dispatch_queue_t queue, dispatch_block_t block) +{ + XCTAssertNotNil(queue); + block(); +} @interface RCTBlobManagerTests : XCTestCase @@ -23,8 +32,12 @@ { [super setUp]; + RCT_MOCK_SET(RCTBlobManager, dispatch_async, _mock_dispatch_async); + _module = [RCTBlobManager new]; + dispatch_queue_t methodQueue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL); [_module setValue:nil forKey:@"bridge"]; + [_module setValue:methodQueue forKey:@"methodQueue"]; [_module initialize]; NSInteger size = 120; _data = [NSMutableData dataWithCapacity:size]; @@ -36,6 +49,13 @@ [_module store:_data withId:_blobId]; } +- (void)tearDown +{ + [super tearDown]; + + RCT_MOCK_RESET(RCTBlobManager, dispatch_async); +} + - (void)testResolve { XCTAssertTrue([_data isEqualToData:[_module resolve:_blobId offset:0 size:_data.length]]); @@ -98,4 +118,37 @@ XCTAssertTrue([expectedData isEqualToData:result]); } +- (void)testCreateFromPartsProcessingQueue +{ + RCTEnableBlobManagerProcessingQueue(YES); + [self setUp]; + + NSDictionary *blobData = @{ + @"blobId" : _blobId, + @"offset" : @0, + @"size" : @(_data.length), + }; + NSDictionary *blob = @{ + @"data" : blobData, + @"type" : @"blob", + }; + NSString *stringData = @"i \u2665 dogs"; + NSDictionary *string = @{ + @"data" : stringData, + @"type" : @"string", + }; + NSString *resultId = [NSUUID UUID].UUIDString; + NSArray *parts = @[ blob, string ]; + + [_module createFromParts:parts withId:resultId]; + + NSMutableData *expectedData = [NSMutableData new]; + [expectedData appendData:_data]; + [expectedData appendData:[stringData dataUsingEncoding:NSUTF8StringEncoding]]; + + NSData *result = [_module resolve:resultId offset:0 size:expectedData.length]; + + XCTAssertTrue([expectedData isEqualToData:result]); +} + @end