Skip to content

Commit 86f3686

Browse files
committed
test(storage, ios): fix react-native Blob issue, re-enable storage tests
- teased out race condition between Blob create and usage, patched the race
1 parent c1c5df1 commit 86f3686

File tree

3 files changed

+139
-22
lines changed

3 files changed

+139
-22
lines changed

packages/storage/e2e/StorageTask.e2e.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,15 @@ describe('storage() -> StorageTask', function () {
194194
});
195195

196196
describe('put()', function () {
197-
// TODO flakey test, RN JSTimer exception sometimes
198-
xit('uploads a Blob', async function () {
197+
it('uploads a Blob', async function () {
199198
const jsonDerulo = JSON.stringify({ foo: 'bar' });
200199
const bob = new Blob([jsonDerulo], {
201200
type: 'application/json',
202201
});
202+
203+
// works every time if you sleep - iOS code is racy around Blob creation/usage
204+
await Utils.sleep(100);
205+
203206
const uploadTaskSnapshot = await firebase
204207
.storage()
205208
.ref(`${PATH}/putStringBlob.json`)
@@ -250,6 +253,10 @@ describe('storage() -> StorageTask', function () {
250253
const bob = new Blob([jsonDerulo], {
251254
type: 'application/json',
252255
});
256+
257+
// works every time if you sleep - iOS code is racy around Blob creation/usage
258+
await Utils.sleep(100);
259+
253260
const uploadTaskSnapshot = firebase.storage().ref(`${PATH}/putStringBlob.json`).put(bob);
254261
await uploadTaskSnapshot;
255262
const snapshot = uploadTaskSnapshot.snapshot;
@@ -973,15 +980,16 @@ describe('storage() -> StorageTask', function () {
973980
});
974981

975982
describe('put()', function () {
976-
// TODO flakey test, RN JSTimer exception sometimes
977-
xit('uploads a Blob', async function () {
983+
it('uploads a Blob', async function () {
978984
const { getStorage, ref, uploadBytesResumable, TaskState } = storageModular;
979985
const jsonDerulo = JSON.stringify({ foo: 'bar' });
980-
981986
const bob = new Blob([jsonDerulo], {
982987
type: 'application/json',
983988
});
984989

990+
// works every time if you sleep - iOS code is racy around Blob creation/usage
991+
await Utils.sleep(100);
992+
985993
const uploadTaskSnapshot = await uploadBytesResumable(
986994
ref(getStorage(), `${PATH}/putStringBlob.json`),
987995
bob,
@@ -1047,6 +1055,9 @@ describe('storage() -> StorageTask', function () {
10471055
type: 'application/json',
10481056
});
10491057

1058+
// works every time if you sleep - iOS code is racy around Blob creation/usage
1059+
await Utils.sleep(100);
1060+
10501061
const uploadTaskSnapshot = uploadBytesResumable(
10511062
ref(getStorage(), `${PATH}/putStringBlob.json`),
10521063
bob,

tests/app.js

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -234,22 +234,7 @@ function loadTests(_) {
234234
}
235235
if (platformSupportedModules.includes('storage')) {
236236
const storageTests = require.context('../packages/storage/e2e', true, /\.e2e\.js$/);
237-
238-
if (Platform.other && global.isCI) {
239-
// Skip StorageReference & StorageTask tests on CI for "other" platforms
240-
// uploadBytesResumable is pretty flaky. Crashes macOS app.
241-
// See: https://github.com/facebook/react-native/issues/32784
242-
// and: https://github.com/firebase/firebase-js-sdk/issues/5848
243-
const filteredTests = storageTests.keys().filter(test => {
244-
if (test.includes('StorageReference.e2e') || test.includes('StorageTask.e2e')) {
245-
return false;
246-
}
247-
return true;
248-
});
249-
filteredTests.forEach(storageTests);
250-
} else {
251-
storageTests.keys().forEach(storageTests);
252-
}
237+
storageTests.keys().forEach(storageTests);
253238
}
254239
});
255240
}

tests/patches/react-native-macos+0.78.3.patch

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
diff --git a/node_modules/react-native-macos/Libraries/Blob/BlobManager.js b/node_modules/react-native-macos/Libraries/Blob/BlobManager.js
2-
index f4c5ed3..7628f81 100644
2+
index f4c5ed3..f7afaf4 100644
33
--- a/node_modules/react-native-macos/Libraries/Blob/BlobManager.js
44
+++ b/node_modules/react-native-macos/Libraries/Blob/BlobManager.js
55
@@ -9,7 +9,7 @@
@@ -26,3 +26,124 @@ index f4c5ed3..7628f81 100644
2626
}
2727
if (part instanceof Blob) {
2828
return {
29+
@@ -92,6 +94,7 @@ class BlobManager {
30+
}, 0);
31+
32+
NativeBlobModule.createFromParts(items, blobId);
33+
+ // console.error('BlobManager thinks blob id ' + blobId + ' already exists (but it may not yet on iOS!).');
34+
35+
return BlobManager.createFromOptions({
36+
blobId,
37+
diff --git a/node_modules/react-native-macos/Libraries/Blob/RCTBlobManager.mm b/node_modules/react-native-macos/Libraries/Blob/RCTBlobManager.mm
38+
index 9d4cde7..c9e6a81 100755
39+
--- a/node_modules/react-native-macos/Libraries/Blob/RCTBlobManager.mm
40+
+++ b/node_modules/react-native-macos/Libraries/Blob/RCTBlobManager.mm
41+
@@ -95,6 +95,7 @@ RCT_EXPORT_MODULE(BlobModule)
42+
- (void)store:(NSData *)data withId:(NSString *)blobId
43+
{
44+
std::lock_guard<std::mutex> lock(_blobsMutex);
45+
+ //RCTLogError(@"RCTBlobManager store inside mutex storing blob id %@", blobId);
46+
_blobs[blobId] = data;
47+
}
48+
49+
@@ -117,9 +118,13 @@ RCT_EXPORT_MODULE(BlobModule)
50+
NSData *data;
51+
{
52+
std::lock_guard<std::mutex> lock(_blobsMutex);
53+
+ // FIXME Right here it seems the blob does not exist for some reason?
54+
+ //RCTLogError(@"RCTBlobManager resolve inside mutex resolving blob id %@", blobId);
55+
data = _blobs[blobId];
56+
}
57+
if (!data) {
58+
+ // FIXME and we return nil to handleNetworkingRequest which then crashes
59+
+ //RCTLogError(@"RCTBlobManager resolve no data for blob id %@", blobId);
60+
return nil;
61+
}
62+
if (offset != 0 || (size != -1 && size != data.length)) {
63+
@@ -156,6 +161,7 @@ RCT_EXPORT_MODULE(BlobModule)
64+
- (void)remove:(NSString *)blobId
65+
{
66+
std::lock_guard<std::mutex> lock(_blobsMutex);
67+
+ //RCTLogError(@"RCTBlobManager remove inside mutex removing blob id %@", blobId);
68+
[_blobs removeObjectForKey:blobId];
69+
}
70+
71+
@@ -217,15 +223,19 @@ RCT_EXPORT_METHOD(createFromParts : (NSArray<NSDictionary<NSString *, id> *> *)p
72+
}
73+
}
74+
75+
+ //RCTLogError(@"RCTBlobManager createFromParts requesting store of blob id %@", blobId);
76+
dispatch_async([self executionQueue], ^{
77+
[self store:data withId:blobId];
78+
+ //RCTLogError(@"RCTBlobManager createFromParts done storing blob id %@", blobId);
79+
});
80+
}
81+
82+
RCT_EXPORT_METHOD(release : (NSString *)blobId)
83+
{
84+
+ //RCTLogError(@"RCTBlobManager enqueueing release of blob id %@", blobId);
85+
dispatch_async([self executionQueue], ^{
86+
[self remove:blobId];
87+
+ //RCTLogError(@"RCTBlobManager done releasing blob id %@", blobId);
88+
});
89+
}
90+
91+
@@ -297,13 +307,30 @@ RCT_EXPORT_METHOD(release : (NSString *)blobId)
92+
// @lint-ignore FBOBJCUNTYPEDCOLLECTION1
93+
NSDictionary *blob = [RCTConvert NSDictionary:data[@"blob"]];
94+
95+
+ // FIXME so here - we checked that data[@"blob"] was non-nil in canHandleHandleNetworkingRequest
96+
+
97+
NSString *contentType = @"application/octet-stream";
98+
NSString *blobType = [RCTConvert NSString:RCTNilIfNull(blob[@"type"])];
99+
if (blobType != nil && blobType.length > 0) {
100+
contentType = blob[@"type"];
101+
}
102+
103+
- return @{@"body" : [self resolve:blob], @"contentType" : contentType};
104+
+ // FIXME but here in resolve:blob the blob isn't found somehow? comes back nil?
105+
+ NSData *blobBody = nil;
106+
+ NSString *blobId = nil;
107+
+ for (int retries = 0; blobBody == nil && retries < 100; retries++) {
108+
+ blobBody = [self resolve:blob];
109+
+ if (blobBody == nil) {
110+
+ if (blobId == nil) {
111+
+ blobId = [RCTConvert NSString:blob[@"blobId"]];
112+
+ }
113+
+ //RCTLogError(@"RCTBlobManager handleNetworkingRequest nil blobBody for blob id %@", blobId);
114+
+ //RCTLogError(@"RCTBlobManager handleNetworkingRequest, sleeping for blob id %@", blobId);
115+
+ [NSThread sleepForTimeInterval:0.1f];
116+
+ }
117+
+ }
118+
+
119+
+ return @{@"body" : blobBody, @"contentType" : contentType};
120+
}
121+
122+
- (BOOL)canHandleNetworkingResponse:(NSString *)responseType
123+
diff --git a/node_modules/react-native-macos/Libraries/Network/RCTNetworking.mm b/node_modules/react-native-macos/Libraries/Network/RCTNetworking.mm
124+
index 80d8c30..b68a67e 100644
125+
--- a/node_modules/react-native-macos/Libraries/Network/RCTNetworking.mm
126+
+++ b/node_modules/react-native-macos/Libraries/Network/RCTNetworking.mm
127+
@@ -417,9 +417,19 @@ RCT_EXPORT_MODULE()
128+
for (id<RCTNetworkingRequestHandler> handler in _requestHandlers) {
129+
if ([handler canHandleNetworkingRequest:query]) {
130+
// @lint-ignore FBOBJCUNTYPEDCOLLECTION1
131+
- NSDictionary *body = [handler handleNetworkingRequest:query];
132+
- if (body) {
133+
- return callback(nil, body);
134+
+ NSDictionary *blob = [RCTConvert NSDictionary:query[@"blob"]];
135+
+ NSString *blobId = [RCTConvert NSString:blob[@"blobId"]];
136+
+ try {
137+
+ //RCTLogError(@"RCTNetworking process handling blob id %@", blobId);
138+
+ NSDictionary *body = [handler handleNetworkingRequest:query];
139+
+ if (body) {
140+
+ return callback(nil, body);
141+
+ }
142+
+ } catch (NSException *exception) {
143+
+ //RCTLogError(@"RCTNetworking process blob id %@ caught %@ because %@", blobId, exception.name, exception.reason);
144+
+ NSError *error = [[NSError alloc] initWithDomain:NSURLErrorDomain code:NSURLErrorUnknown userInfo:nil];
145+
+ //RCTLogError(@"RCTNetworking process blob id %@ returning error to callback.", blobId);
146+
+ return callback(error, nil);
147+
}
148+
}
149+
}

0 commit comments

Comments
 (0)