@truewatchtech/react-native-mobile 0.4.1 → 0.4.3-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FTMobileReactNativeSDK.podspec +7 -5
- package/README.md +34 -0
- package/android/build.gradle +9 -2
- package/android/src/main/java/com/ft/sdk/reactnative/FTMobileImpl.java +45 -14
- package/android/src/main/java/com/ft/sdk/reactnative/FTRUMImpl.java +85 -2
- package/android/src/main/java/com/ft/sdk/reactnative/FTTraceImpl.java +5 -1
- package/android/src/main/java/com/ft/sdk/reactnative/JsLongTaskMonitor.java +281 -0
- package/android/src/main/java/com/ft/sdk/reactnative/utils/ReactNativeUtils.java +5 -12
- package/android/src/newarch/java/com/ft/sdk/reactnative/FTMobileModule.java +9 -5
- package/android/src/newarch/java/com/ft/sdk/reactnative/FTRUMModule.java +26 -1
- package/android/src/newarch/java/com/ft/sdk/reactnative/FTTraceModule.java +14 -0
- package/android/src/oldarch/java/com/ft/sdk/reactnative/FTMobileModule.java +2 -2
- package/android/src/oldarch/java/com/ft/sdk/reactnative/FTRUMModule.java +29 -1
- package/android/src/oldarch/java/com/ft/sdk/reactnative/FTTraceModule.java +12 -0
- package/android/src/rnpost70/java/com/ft/sdk/reactnative/ReactJsThreadExecutor.java +21 -0
- package/android/src/rnpre70/java/com/ft/sdk/reactnative/ReactJsThreadExecutor.java +37 -0
- package/android/src/test/java/com/ft/sdk/reactnative/FTRUMModuleTest.java +117 -0
- package/android/src/test/java/com/ft/sdk/reactnative/JsLongTaskMonitorTest.java +446 -0
- package/android/src/test/java/com/ft/sdk/reactnative/utils/ReactNativeUtilsTest.java +33 -0
- package/ios/FTJSLongTaskMonitor.h +51 -0
- package/ios/FTJSLongTaskMonitor.m +250 -0
- package/ios/FTMobileReactNative.mm +23 -16
- package/ios/FTReactNativeLog.mm +3 -4
- package/ios/FTReactNativeRUM.mm +103 -28
- package/ios/FTReactNativeResource.h +4 -0
- package/ios/FTReactNativeResource.m +58 -0
- package/ios/FTReactNativeTrace.mm +56 -13
- package/ios/FTReactNativeUtils.m +10 -15
- package/ios/FTReactNativeWebSocket.h +10 -0
- package/ios/FTReactNativeWebSocket.mm +201 -0
- package/ios/FTWebSocketMetadataStore.h +25 -0
- package/ios/FTWebSocketMetadataStore.m +622 -0
- package/ios/FTWebSocketResourceData.h +16 -0
- package/ios/FTWebSocketResourceData.m +75 -0
- package/lib/commonjs/ft_mobile_agent.js +36 -3
- package/lib/commonjs/ft_mobile_agent.js.map +1 -1
- package/lib/commonjs/ft_rum.js +46 -2
- package/lib/commonjs/ft_rum.js.map +1 -1
- package/lib/commonjs/ft_tracing.js +12 -3
- package/lib/commonjs/ft_tracing.js.map +1 -1
- package/lib/commonjs/index.js +14 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/rum/FTBabelInteractionTracking.js +72 -0
- package/lib/commonjs/rum/FTBabelInteractionTracking.js.map +1 -0
- package/lib/commonjs/rum/FTBabelUtils.js +87 -0
- package/lib/commonjs/rum/FTBabelUtils.js.map +1 -0
- package/lib/commonjs/rum/FTRumActionTracking.js +10 -39
- package/lib/commonjs/rum/FTRumActionTracking.js.map +1 -1
- package/lib/commonjs/rum/FTRumActionTrackingContext.js +50 -0
- package/lib/commonjs/rum/FTRumActionTrackingContext.js.map +1 -0
- package/lib/commonjs/rum/FTRumWebSocketNative.js +73 -0
- package/lib/commonjs/rum/FTRumWebSocketNative.js.map +1 -0
- package/lib/commonjs/rum/FTRumWebSocketTiming.js +55 -0
- package/lib/commonjs/rum/FTRumWebSocketTiming.js.map +1 -0
- package/lib/commonjs/rum/FTRumWebSocketTracking.js +510 -0
- package/lib/commonjs/rum/FTRumWebSocketTracking.js.map +1 -0
- package/lib/commonjs/rum/longTasksUtils.js +25 -0
- package/lib/commonjs/rum/longTasksUtils.js.map +1 -0
- package/lib/commonjs/specs/NativeFTMobileReactNative.js.map +1 -1
- package/lib/commonjs/specs/NativeFTReactNativeRUM.js.map +1 -1
- package/lib/commonjs/specs/NativeFTReactNativeTrace.js.map +1 -1
- package/lib/commonjs/specs/NativeFTReactNativeWebSocket.js +11 -0
- package/lib/commonjs/specs/NativeFTReactNativeWebSocket.js.map +1 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/commonjs/version.js.map +1 -1
- package/lib/module/ft_mobile_agent.js +36 -3
- package/lib/module/ft_mobile_agent.js.map +1 -1
- package/lib/module/ft_rum.js +46 -2
- package/lib/module/ft_rum.js.map +1 -1
- package/lib/module/ft_tracing.js +12 -1
- package/lib/module/ft_tracing.js.map +1 -1
- package/lib/module/index.js +3 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/rum/FTBabelInteractionTracking.js +66 -0
- package/lib/module/rum/FTBabelInteractionTracking.js.map +1 -0
- package/lib/module/rum/FTBabelUtils.js +80 -0
- package/lib/module/rum/FTBabelUtils.js.map +1 -0
- package/lib/module/rum/FTRumActionTracking.js +10 -39
- package/lib/module/rum/FTRumActionTracking.js.map +1 -1
- package/lib/module/rum/FTRumActionTrackingContext.js +43 -0
- package/lib/module/rum/FTRumActionTrackingContext.js.map +1 -0
- package/lib/module/rum/FTRumWebSocketNative.js +62 -0
- package/lib/module/rum/FTRumWebSocketNative.js.map +1 -0
- package/lib/module/rum/FTRumWebSocketTiming.js +49 -0
- package/lib/module/rum/FTRumWebSocketTiming.js.map +1 -0
- package/lib/module/rum/FTRumWebSocketTracking.js +503 -0
- package/lib/module/rum/FTRumWebSocketTracking.js.map +1 -0
- package/lib/module/rum/longTasksUtils.js +18 -0
- package/lib/module/rum/longTasksUtils.js.map +1 -0
- package/lib/module/specs/NativeFTMobileReactNative.js.map +1 -1
- package/lib/module/specs/NativeFTReactNativeRUM.js.map +1 -1
- package/lib/module/specs/NativeFTReactNativeTrace.js.map +1 -1
- package/lib/module/specs/NativeFTReactNativeWebSocket.js +8 -0
- package/lib/module/specs/NativeFTReactNativeWebSocket.js.map +1 -0
- package/lib/module/version.js +1 -1
- package/lib/module/version.js.map +1 -1
- package/lib/typescript/ft_mobile_agent.d.ts +7 -0
- package/lib/typescript/ft_rum.d.ts +9 -0
- package/lib/typescript/index.d.ts +3 -1
- package/lib/typescript/rum/FTBabelInteractionTracking.d.ts +30 -0
- package/lib/typescript/rum/FTBabelUtils.d.ts +1 -0
- package/lib/typescript/rum/FTRumActionTracking.d.ts +0 -2
- package/lib/typescript/rum/FTRumActionTrackingContext.d.ts +11 -0
- package/lib/typescript/rum/FTRumWebSocketNative.d.ts +23 -0
- package/lib/typescript/rum/FTRumWebSocketTiming.d.ts +2 -0
- package/lib/typescript/rum/FTRumWebSocketTracking.d.ts +56 -0
- package/lib/typescript/rum/longTasksUtils.d.ts +8 -0
- package/lib/typescript/specs/NativeFTMobileReactNative.d.ts +14 -0
- package/lib/typescript/specs/NativeFTReactNativeRUM.d.ts +4 -0
- package/lib/typescript/specs/NativeFTReactNativeTrace.d.ts +12 -0
- package/lib/typescript/specs/NativeFTReactNativeWebSocket.d.ts +13 -0
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +7 -6
- package/src/ft_mobile_agent.tsx +49 -6
- package/src/ft_rum.tsx +61 -2
- package/src/ft_tracing.tsx +15 -1
- package/src/index.tsx +4 -0
- package/src/rum/FTBabelInteractionTracking.ts +124 -0
- package/src/rum/FTBabelUtils.tsx +99 -0
- package/src/rum/FTRumActionTracking.tsx +14 -43
- package/src/rum/FTRumActionTrackingContext.ts +68 -0
- package/src/rum/FTRumWebSocketNative.ts +106 -0
- package/src/rum/FTRumWebSocketTiming.ts +50 -0
- package/src/rum/FTRumWebSocketTracking.ts +727 -0
- package/src/rum/longTasksUtils.ts +17 -0
- package/src/specs/NativeFTMobileReactNative.ts +17 -0
- package/src/specs/NativeFTReactNativeRUM.ts +4 -0
- package/src/specs/NativeFTReactNativeTrace.ts +12 -0
- package/src/specs/NativeFTReactNativeWebSocket.ts +33 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
package com.ft.sdk.reactnative;
|
|
2
|
+
|
|
3
|
+
import org.junit.Before;
|
|
4
|
+
import org.junit.Test;
|
|
5
|
+
|
|
6
|
+
import java.util.ArrayList;
|
|
7
|
+
import java.util.List;
|
|
8
|
+
import java.util.Map;
|
|
9
|
+
import java.util.HashMap;
|
|
10
|
+
import java.util.Collections;
|
|
11
|
+
|
|
12
|
+
import static org.junit.Assert.assertEquals;
|
|
13
|
+
import static org.junit.Assert.assertFalse;
|
|
14
|
+
import static org.junit.Assert.assertTrue;
|
|
15
|
+
|
|
16
|
+
public class JsLongTaskMonitorTest {
|
|
17
|
+
|
|
18
|
+
private FakeExecutor executor;
|
|
19
|
+
private FakeSchedulerFactory schedulerFactory;
|
|
20
|
+
private FakeReporter reporter;
|
|
21
|
+
private JsLongTaskMonitor monitor;
|
|
22
|
+
|
|
23
|
+
@Before
|
|
24
|
+
public void setUp() {
|
|
25
|
+
executor = new FakeExecutor();
|
|
26
|
+
schedulerFactory = new FakeSchedulerFactory();
|
|
27
|
+
reporter = new FakeReporter();
|
|
28
|
+
monitor = new JsLongTaskMonitor(executor, schedulerFactory, reporter);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Test
|
|
32
|
+
public void createsFrameSchedulerOnlyOnJsThread() {
|
|
33
|
+
monitor.setThresholdMilliseconds(100);
|
|
34
|
+
monitor.start();
|
|
35
|
+
|
|
36
|
+
assertEquals(0, schedulerFactory.createCount);
|
|
37
|
+
assertEquals(1, executor.runnables.size());
|
|
38
|
+
|
|
39
|
+
executor.runAll();
|
|
40
|
+
|
|
41
|
+
assertEquals(1, schedulerFactory.createCount);
|
|
42
|
+
assertEquals(1, schedulerFactory.scheduler.postCount);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Test
|
|
46
|
+
public void bridgeContextUpdatesBeforeDelayedStartAndSnapshotsEachDetection() {
|
|
47
|
+
Map<String, Object> context = new HashMap<>();
|
|
48
|
+
context.put("sdk_bridge_info", "{\"react_native\":\"test-version\"}");
|
|
49
|
+
context.put("wgt_id", "first");
|
|
50
|
+
monitor.setBridgeContext(context);
|
|
51
|
+
monitor.setThresholdMilliseconds(100);
|
|
52
|
+
monitor.start();
|
|
53
|
+
context.put("wgt_id", "second");
|
|
54
|
+
monitor.setBridgeContext(context);
|
|
55
|
+
executor.runAll();
|
|
56
|
+
schedulerFactory.scheduler.fire(1_000_000_000L);
|
|
57
|
+
schedulerFactory.scheduler.fire(1_250_000_000L);
|
|
58
|
+
Map<String, Object> firstEvent = reporter.properties.get(0);
|
|
59
|
+
Map<String, Object> expected = new HashMap<>(context);
|
|
60
|
+
context.put("wgt_id", "third");
|
|
61
|
+
monitor.setBridgeContext(context);
|
|
62
|
+
schedulerFactory.scheduler.fire(1_500_000_000L);
|
|
63
|
+
|
|
64
|
+
assertEquals(java.util.Arrays.asList(250_000_000L, 250_000_000L), reporter.durations);
|
|
65
|
+
assertEquals(expected, firstEvent);
|
|
66
|
+
assertEquals(context, reporter.properties.get(1));
|
|
67
|
+
monitor.disable(null);
|
|
68
|
+
context.put("wgt_id", "after-stop");
|
|
69
|
+
monitor.setBridgeContext(context);
|
|
70
|
+
schedulerFactory.scheduler.fire(2_000_000_000L);
|
|
71
|
+
assertEquals(2, reporter.durations.size());
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@Test
|
|
75
|
+
public void noBridgeContextReportsEmptyProperties() {
|
|
76
|
+
startWithThreshold(100);
|
|
77
|
+
schedulerFactory.scheduler.fire(1_000_000_000L);
|
|
78
|
+
schedulerFactory.scheduler.fire(1_250_000_000L);
|
|
79
|
+
assertEquals(250_000_000L, reporter.durations.get(0).longValue());
|
|
80
|
+
assertEquals(Collections.emptyMap(), reporter.properties.get(0));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@Test
|
|
84
|
+
public void firstFrameIsBaselineAndThresholdComparisonIsStrict() {
|
|
85
|
+
startWithThreshold(100);
|
|
86
|
+
|
|
87
|
+
schedulerFactory.scheduler.fire(1_000_000_000L);
|
|
88
|
+
schedulerFactory.scheduler.fire(1_100_000_000L);
|
|
89
|
+
schedulerFactory.scheduler.fire(1_200_000_001L);
|
|
90
|
+
|
|
91
|
+
assertEquals(1, reporter.durations.size());
|
|
92
|
+
assertEquals(100_000_001L, reporter.durations.get(0).longValue());
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@Test
|
|
96
|
+
public void reportsEveryQualifyingFullFrameInterval() {
|
|
97
|
+
startWithThreshold(100);
|
|
98
|
+
|
|
99
|
+
schedulerFactory.scheduler.fire(1_000_000_000L);
|
|
100
|
+
schedulerFactory.scheduler.fire(1_050_000_000L);
|
|
101
|
+
schedulerFactory.scheduler.fire(1_200_000_000L);
|
|
102
|
+
schedulerFactory.scheduler.fire(1_450_000_000L);
|
|
103
|
+
|
|
104
|
+
assertEquals(2, reporter.durations.size());
|
|
105
|
+
assertEquals(150_000_000L, reporter.durations.get(0).longValue());
|
|
106
|
+
assertEquals(250_000_000L, reporter.durations.get(1).longValue());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@Test
|
|
110
|
+
public void preservesFractionalMillisecondThresholds() {
|
|
111
|
+
startWithThreshold(200.5);
|
|
112
|
+
|
|
113
|
+
schedulerFactory.scheduler.fire(1_000_000_000L);
|
|
114
|
+
schedulerFactory.scheduler.fire(1_200_500_000L);
|
|
115
|
+
schedulerFactory.scheduler.fire(1_401_000_001L);
|
|
116
|
+
|
|
117
|
+
assertEquals(1, reporter.durations.size());
|
|
118
|
+
assertEquals(200_500_001L, reporter.durations.get(0).longValue());
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@Test
|
|
122
|
+
public void stopRemovesCallbackAndRestartResetsBaseline() {
|
|
123
|
+
startWithThreshold(100);
|
|
124
|
+
FakeScheduler firstScheduler = schedulerFactory.scheduler;
|
|
125
|
+
firstScheduler.fire(1_000_000_000L);
|
|
126
|
+
|
|
127
|
+
monitor.stop();
|
|
128
|
+
firstScheduler.fire(1_500_000_000L);
|
|
129
|
+
executor.runAll();
|
|
130
|
+
|
|
131
|
+
assertEquals(1, firstScheduler.removeCount);
|
|
132
|
+
assertTrue(reporter.durations.isEmpty());
|
|
133
|
+
|
|
134
|
+
monitor.start();
|
|
135
|
+
executor.runAll();
|
|
136
|
+
FakeScheduler secondScheduler = schedulerFactory.scheduler;
|
|
137
|
+
secondScheduler.fire(2_000_000_000L);
|
|
138
|
+
secondScheduler.fire(2_100_000_001L);
|
|
139
|
+
|
|
140
|
+
assertEquals(1, reporter.durations.size());
|
|
141
|
+
assertEquals(100_000_001L, reporter.durations.get(0).longValue());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@Test
|
|
145
|
+
public void repeatedStartKeepsOnlyLatestRequest() {
|
|
146
|
+
monitor.setThresholdMilliseconds(100);
|
|
147
|
+
monitor.start();
|
|
148
|
+
monitor.start();
|
|
149
|
+
|
|
150
|
+
executor.runAll();
|
|
151
|
+
|
|
152
|
+
assertEquals(1, schedulerFactory.createCount);
|
|
153
|
+
assertEquals(1, schedulerFactory.scheduler.postCount);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@Test
|
|
157
|
+
public void zeroThresholdDisablesMonitoring() {
|
|
158
|
+
startWithThreshold(100);
|
|
159
|
+
FakeScheduler scheduler = schedulerFactory.scheduler;
|
|
160
|
+
|
|
161
|
+
monitor.setThresholdMilliseconds(0);
|
|
162
|
+
executor.runAll();
|
|
163
|
+
scheduler.fire(1_000_000_000L);
|
|
164
|
+
scheduler.fire(2_000_000_000L);
|
|
165
|
+
|
|
166
|
+
assertEquals(1, scheduler.removeCount);
|
|
167
|
+
assertTrue(reporter.durations.isEmpty());
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@Test
|
|
171
|
+
public void schedulerFailureDoesNotCrashOrPostFrames() {
|
|
172
|
+
schedulerFactory.shouldThrow = true;
|
|
173
|
+
monitor.setThresholdMilliseconds(100);
|
|
174
|
+
monitor.start();
|
|
175
|
+
|
|
176
|
+
executor.runAll();
|
|
177
|
+
|
|
178
|
+
assertEquals(1, schedulerFactory.createCount);
|
|
179
|
+
assertFalse(schedulerFactory.scheduler.wasPosted);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@Test
|
|
183
|
+
public void javascriptQueueFailureDoesNotCrash() {
|
|
184
|
+
executor.shouldThrow = true;
|
|
185
|
+
monitor.setThresholdMilliseconds(100);
|
|
186
|
+
|
|
187
|
+
monitor.start();
|
|
188
|
+
monitor.stop();
|
|
189
|
+
|
|
190
|
+
assertEquals(0, schedulerFactory.createCount);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@Test
|
|
194
|
+
public void rejectedStartDoesNotCreateScheduler() {
|
|
195
|
+
executor.acceptsTasks = false;
|
|
196
|
+
monitor.setThresholdMilliseconds(100);
|
|
197
|
+
|
|
198
|
+
monitor.start();
|
|
199
|
+
|
|
200
|
+
assertEquals(0, schedulerFactory.createCount);
|
|
201
|
+
assertTrue(executor.runnables.isEmpty());
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@Test
|
|
205
|
+
public void rejectedStopCompletesAndSuppressesExistingFrameCallbacks() {
|
|
206
|
+
startWithThreshold(100);
|
|
207
|
+
FakeScheduler scheduler = schedulerFactory.scheduler;
|
|
208
|
+
scheduler.fire(1_000_000_000L);
|
|
209
|
+
int posts = scheduler.postCount;
|
|
210
|
+
executor.acceptsTasks = false;
|
|
211
|
+
final int[] completions = {0};
|
|
212
|
+
|
|
213
|
+
monitor.disable(() -> completions[0]++);
|
|
214
|
+
scheduler.fire(1_500_000_000L);
|
|
215
|
+
monitor.start();
|
|
216
|
+
|
|
217
|
+
assertEquals(1, completions[0]);
|
|
218
|
+
assertEquals(posts, scheduler.postCount);
|
|
219
|
+
assertEquals(1, schedulerFactory.createCount);
|
|
220
|
+
assertTrue(reporter.durations.isEmpty());
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@Test
|
|
224
|
+
public void throwingQueueCompletesStopExactlyOnce() {
|
|
225
|
+
executor.shouldThrow = true;
|
|
226
|
+
final int[] completions = {0};
|
|
227
|
+
|
|
228
|
+
monitor.stop(() -> completions[0]++);
|
|
229
|
+
|
|
230
|
+
assertEquals(1, completions[0]);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
@Test
|
|
234
|
+
public void inlineCompletionFailureIsNotInvokedTwice() {
|
|
235
|
+
JsLongTaskMonitor inlineMonitor = new JsLongTaskMonitor(
|
|
236
|
+
runnable -> {
|
|
237
|
+
runnable.run();
|
|
238
|
+
return true;
|
|
239
|
+
},
|
|
240
|
+
schedulerFactory,
|
|
241
|
+
reporter
|
|
242
|
+
);
|
|
243
|
+
final int[] completions = {0};
|
|
244
|
+
|
|
245
|
+
inlineMonitor.stop(() -> {
|
|
246
|
+
completions[0]++;
|
|
247
|
+
throw new IllegalStateException("Promise is already unavailable");
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
assertEquals(1, completions[0]);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
@Test
|
|
254
|
+
public void framePostFailureDisablesMonitoring() {
|
|
255
|
+
schedulerFactory.throwOnPost = true;
|
|
256
|
+
monitor.setThresholdMilliseconds(100);
|
|
257
|
+
|
|
258
|
+
monitor.start();
|
|
259
|
+
executor.runAll();
|
|
260
|
+
|
|
261
|
+
assertEquals(1, schedulerFactory.createCount);
|
|
262
|
+
assertFalse(schedulerFactory.scheduler.wasPosted);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@Test
|
|
266
|
+
public void stopCompletionRunsAfterCallbackRemoval() {
|
|
267
|
+
startWithThreshold(100);
|
|
268
|
+
final boolean[] completed = {false};
|
|
269
|
+
|
|
270
|
+
monitor.stop(new Runnable() {
|
|
271
|
+
@Override
|
|
272
|
+
public void run() {
|
|
273
|
+
completed[0] = true;
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
assertFalse(completed[0]);
|
|
278
|
+
assertEquals(0, schedulerFactory.scheduler.removeCount);
|
|
279
|
+
|
|
280
|
+
executor.runAll();
|
|
281
|
+
|
|
282
|
+
assertTrue(completed[0]);
|
|
283
|
+
assertEquals(1, schedulerFactory.scheduler.removeCount);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
@Test
|
|
287
|
+
public void disablePreventsRestartUntilConfiguredAgain() {
|
|
288
|
+
startWithThreshold(100);
|
|
289
|
+
FakeScheduler firstScheduler = schedulerFactory.scheduler;
|
|
290
|
+
firstScheduler.fire(1_000_000_000L);
|
|
291
|
+
final int[] completions = {0};
|
|
292
|
+
|
|
293
|
+
monitor.disable(() -> completions[0]++);
|
|
294
|
+
// A foreground event can arrive before the JS queue finishes stopping.
|
|
295
|
+
monitor.start();
|
|
296
|
+
firstScheduler.fire(1_500_000_000L);
|
|
297
|
+
assertEquals(0, completions[0]);
|
|
298
|
+
executor.runAll();
|
|
299
|
+
|
|
300
|
+
assertEquals(1, completions[0]);
|
|
301
|
+
assertEquals(1, firstScheduler.removeCount);
|
|
302
|
+
assertTrue(reporter.durations.isEmpty());
|
|
303
|
+
|
|
304
|
+
// Further background/foreground transitions must not re-enable monitoring.
|
|
305
|
+
monitor.stop();
|
|
306
|
+
monitor.start();
|
|
307
|
+
executor.runAll();
|
|
308
|
+
assertEquals(1, schedulerFactory.createCount);
|
|
309
|
+
|
|
310
|
+
monitor.setThresholdMilliseconds(200);
|
|
311
|
+
monitor.start();
|
|
312
|
+
executor.runAll();
|
|
313
|
+
assertEquals(2, schedulerFactory.createCount);
|
|
314
|
+
schedulerFactory.scheduler.fire(10_000_000_000L);
|
|
315
|
+
schedulerFactory.scheduler.fire(10_150_000_000L);
|
|
316
|
+
assertTrue(reporter.durations.isEmpty());
|
|
317
|
+
schedulerFactory.scheduler.fire(10_400_000_000L);
|
|
318
|
+
assertEquals(1, reporter.durations.size());
|
|
319
|
+
assertEquals(250_000_000L, reporter.durations.get(0).longValue());
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
@Test
|
|
323
|
+
public void repeatedDisableCancelsPendingStartAndCompletesEachRequest() {
|
|
324
|
+
monitor.setThresholdMilliseconds(100);
|
|
325
|
+
monitor.start();
|
|
326
|
+
final int[] completions = {0};
|
|
327
|
+
|
|
328
|
+
monitor.disable(() -> completions[0]++);
|
|
329
|
+
monitor.disable(() -> completions[0]++);
|
|
330
|
+
monitor.start();
|
|
331
|
+
executor.runAll();
|
|
332
|
+
|
|
333
|
+
assertEquals(2, completions[0]);
|
|
334
|
+
assertEquals(0, schedulerFactory.createCount);
|
|
335
|
+
assertTrue(reporter.durations.isEmpty());
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
@Test
|
|
339
|
+
public void reporterFailureDoesNotStopFrameCallbacks() {
|
|
340
|
+
reporter.shouldThrow = true;
|
|
341
|
+
startWithThreshold(100);
|
|
342
|
+
int initialPostCount = schedulerFactory.scheduler.postCount;
|
|
343
|
+
|
|
344
|
+
schedulerFactory.scheduler.fire(1_000_000_000L);
|
|
345
|
+
schedulerFactory.scheduler.fire(1_200_000_000L);
|
|
346
|
+
|
|
347
|
+
assertEquals(initialPostCount + 2, schedulerFactory.scheduler.postCount);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private void startWithThreshold(double thresholdMilliseconds) {
|
|
351
|
+
monitor.setThresholdMilliseconds(thresholdMilliseconds);
|
|
352
|
+
monitor.start();
|
|
353
|
+
executor.runAll();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
private static final class FakeExecutor implements JsLongTaskMonitor.Executor {
|
|
357
|
+
private final List<Runnable> runnables = new ArrayList<>();
|
|
358
|
+
private boolean shouldThrow;
|
|
359
|
+
private boolean acceptsTasks = true;
|
|
360
|
+
|
|
361
|
+
@Override
|
|
362
|
+
public boolean runOnJsThread(Runnable runnable) {
|
|
363
|
+
if (shouldThrow) {
|
|
364
|
+
throw new IllegalStateException("JavaScript queue unavailable");
|
|
365
|
+
}
|
|
366
|
+
if (!acceptsTasks) {
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
runnables.add(runnable);
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private void runAll() {
|
|
374
|
+
while (!runnables.isEmpty()) {
|
|
375
|
+
runnables.remove(0).run();
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private static final class FakeSchedulerFactory
|
|
381
|
+
implements JsLongTaskMonitor.FrameSchedulerFactory {
|
|
382
|
+
private int createCount;
|
|
383
|
+
private boolean shouldThrow;
|
|
384
|
+
private boolean throwOnPost;
|
|
385
|
+
private FakeScheduler scheduler = new FakeScheduler();
|
|
386
|
+
|
|
387
|
+
@Override
|
|
388
|
+
public JsLongTaskMonitor.FrameScheduler create(
|
|
389
|
+
JsLongTaskMonitor.FrameCallback callback
|
|
390
|
+
) {
|
|
391
|
+
createCount++;
|
|
392
|
+
if (shouldThrow) {
|
|
393
|
+
throw new IllegalStateException("No looper");
|
|
394
|
+
}
|
|
395
|
+
scheduler = new FakeScheduler();
|
|
396
|
+
scheduler.callback = callback;
|
|
397
|
+
scheduler.throwOnPost = throwOnPost;
|
|
398
|
+
return scheduler;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
private static final class FakeScheduler
|
|
403
|
+
implements JsLongTaskMonitor.FrameScheduler {
|
|
404
|
+
private JsLongTaskMonitor.FrameCallback callback;
|
|
405
|
+
private int postCount;
|
|
406
|
+
private int removeCount;
|
|
407
|
+
private boolean wasPosted;
|
|
408
|
+
private boolean throwOnPost;
|
|
409
|
+
|
|
410
|
+
@Override
|
|
411
|
+
public void postFrameCallback() {
|
|
412
|
+
if (throwOnPost) {
|
|
413
|
+
throw new IllegalStateException("Frame scheduler unavailable");
|
|
414
|
+
}
|
|
415
|
+
postCount++;
|
|
416
|
+
wasPosted = true;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
@Override
|
|
420
|
+
public void removeFrameCallback() {
|
|
421
|
+
removeCount++;
|
|
422
|
+
wasPosted = false;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private void fire(long timestampNanos) {
|
|
426
|
+
if (callback != null) {
|
|
427
|
+
callback.doFrame(timestampNanos);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
private static final class FakeReporter implements JsLongTaskMonitor.Reporter {
|
|
433
|
+
private final List<Long> durations = new ArrayList<>();
|
|
434
|
+
private final List<Map<String, Object>> properties = new ArrayList<>();
|
|
435
|
+
private boolean shouldThrow;
|
|
436
|
+
|
|
437
|
+
@Override
|
|
438
|
+
public void reportLongTask(long durationNanos, Map<String, Object> property) {
|
|
439
|
+
if (shouldThrow) {
|
|
440
|
+
throw new IllegalStateException("Reporter unavailable");
|
|
441
|
+
}
|
|
442
|
+
durations.add(durationNanos);
|
|
443
|
+
properties.add(property);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
package com.ft.sdk.reactnative.utils;
|
|
2
|
+
|
|
3
|
+
import static org.junit.Assert.assertFalse;
|
|
4
|
+
import static org.junit.Assert.assertTrue;
|
|
5
|
+
|
|
6
|
+
import org.junit.Test;
|
|
7
|
+
|
|
8
|
+
public class ReactNativeUtilsTest {
|
|
9
|
+
|
|
10
|
+
@Test
|
|
11
|
+
public void filtersAndroidEmulatorInspectorResource() {
|
|
12
|
+
assertTrue(ReactNativeUtils.isReactNativeDevUrl(
|
|
13
|
+
"http://10.0.2.2:8081/inspector/device?name=sdk_gphone64_arm64%20-%2012%20-%20API%2032"
|
|
14
|
+
+ "&app=com.ft.sdk.reactnative.example&device=a520746d2418fde7c33ceff1d852b112040a08c0"
|
|
15
|
+
));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Test
|
|
19
|
+
public void filtersReactNativeAndExpoDevelopmentResources() {
|
|
20
|
+
assertTrue(ReactNativeUtils.isReactNativeDevUrl("http://localhost:8081/hot?platform=android"));
|
|
21
|
+
assertTrue(ReactNativeUtils.isReactNativeDevUrl("ws://10.0.2.2:8081/message?device=emulator"));
|
|
22
|
+
assertTrue(ReactNativeUtils.isReactNativeDevUrl("http://192.168.1.2:8082/logs"));
|
|
23
|
+
assertTrue(ReactNativeUtils.isReactNativeDevUrl("http://[::1]:8081/debugger-proxy?role=client"));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Test
|
|
27
|
+
public void doesNotFilterApplicationResources() {
|
|
28
|
+
assertFalse(ReactNativeUtils.isReactNativeDevUrl("https://api.example.com/inspector/device"));
|
|
29
|
+
assertFalse(ReactNativeUtils.isReactNativeDevUrl("http://10.0.2.2:9529/inspector/device"));
|
|
30
|
+
assertFalse(ReactNativeUtils.isReactNativeDevUrl("http://10.0.2.2:8081/business"));
|
|
31
|
+
assertFalse(ReactNativeUtils.isReactNativeDevUrl(null));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
typedef void (^FTJSLongTaskFrameCallback)(CFTimeInterval timestamp);
|
|
6
|
+
|
|
7
|
+
@protocol FTJSLongTaskQueue <NSObject>
|
|
8
|
+
- (BOOL)dispatchBlock:(dispatch_block_t)block;
|
|
9
|
+
@end
|
|
10
|
+
|
|
11
|
+
@protocol FTJSLongTaskFrameScheduler <NSObject>
|
|
12
|
+
// The monitor serializes start/stop; start runs on the JS thread, while stop
|
|
13
|
+
// must release resources synchronously even when that thread is unavailable.
|
|
14
|
+
- (void)start;
|
|
15
|
+
- (void)stop;
|
|
16
|
+
@end
|
|
17
|
+
|
|
18
|
+
@protocol FTJSLongTaskFrameSchedulerFactory <NSObject>
|
|
19
|
+
- (id<FTJSLongTaskFrameScheduler>)createSchedulerWithCallback:(FTJSLongTaskFrameCallback)callback;
|
|
20
|
+
@end
|
|
21
|
+
|
|
22
|
+
@protocol FTJSLongTaskReporter <NSObject>
|
|
23
|
+
- (void)reportLongTaskWithDurationNanoseconds:(int64_t)durationNanoseconds property:(NSDictionary *)property;
|
|
24
|
+
@end
|
|
25
|
+
|
|
26
|
+
@interface FTJSLongTaskMonitor : NSObject
|
|
27
|
+
|
|
28
|
+
- (instancetype)initWithQueue:(id<FTJSLongTaskQueue>)queue
|
|
29
|
+
schedulerFactory:(id<FTJSLongTaskFrameSchedulerFactory>)schedulerFactory
|
|
30
|
+
reporter:(id<FTJSLongTaskReporter>)reporter NS_DESIGNATED_INITIALIZER;
|
|
31
|
+
- (instancetype)init NS_UNAVAILABLE;
|
|
32
|
+
|
|
33
|
+
- (void)setThresholdMilliseconds:(double)thresholdMilliseconds;
|
|
34
|
+
// Replaces the snapshot for future detections; does not change monitor lifecycle.
|
|
35
|
+
- (void)setBridgeContext:(NSDictionary *)context;
|
|
36
|
+
- (void)start;
|
|
37
|
+
- (void)stop;
|
|
38
|
+
// Completion runs on the calling thread after resources have been released.
|
|
39
|
+
- (void)stopWithCompletion:(nullable dispatch_block_t)completion;
|
|
40
|
+
// Prevents automatic restarts until a nonzero threshold is configured again.
|
|
41
|
+
- (void)disableWithCompletion:(nullable dispatch_block_t)completion;
|
|
42
|
+
|
|
43
|
+
@end
|
|
44
|
+
|
|
45
|
+
@class RCTBridge;
|
|
46
|
+
|
|
47
|
+
@interface FTJSLongTaskMonitor (Production)
|
|
48
|
+
+ (instancetype)monitorWithBridge:(RCTBridge *)bridge;
|
|
49
|
+
@end
|
|
50
|
+
|
|
51
|
+
NS_ASSUME_NONNULL_END
|