basalt-core 0.0.0-rc5
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/LICENSE +21 -0
- package/dist/cli/basalt.d.ts +3 -0
- package/dist/cli/basalt.d.ts.map +1 -0
- package/dist/cli/basalt.js +47 -0
- package/dist/cli/basalt.js.map +1 -0
- package/dist/cli/bundle-cli.d.ts +12 -0
- package/dist/cli/bundle-cli.d.ts.map +1 -0
- package/dist/cli/bundle-cli.js +124 -0
- package/dist/cli/bundle-cli.js.map +1 -0
- package/dist/cli/bundleWithAssets.d.ts +79 -0
- package/dist/cli/bundleWithAssets.d.ts.map +1 -0
- package/dist/cli/bundleWithAssets.js +198 -0
- package/dist/cli/bundleWithAssets.js.map +1 -0
- package/dist/cli/desktop.d.ts +339 -0
- package/dist/cli/desktop.d.ts.map +1 -0
- package/dist/cli/desktop.js +1029 -0
- package/dist/cli/desktop.js.map +1 -0
- package/dist/cli/doctor.d.ts +31 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +283 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/init.d.ts +77 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +442 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/metro.d.ts +49 -0
- package/dist/cli/metro.d.ts.map +1 -0
- package/dist/cli/metro.js +148 -0
- package/dist/cli/metro.js.map +1 -0
- package/dist/cli/packageApp.d.ts +102 -0
- package/dist/cli/packageApp.d.ts.map +1 -0
- package/dist/cli/packageApp.js +319 -0
- package/dist/cli/packageApp.js.map +1 -0
- package/dist/cli/steps.d.ts +34 -0
- package/dist/cli/steps.d.ts.map +1 -0
- package/dist/cli/steps.js +53 -0
- package/dist/cli/steps.js.map +1 -0
- package/dist/metro-config.d.ts +168 -0
- package/dist/metro-config.d.ts.map +1 -0
- package/dist/metro-config.js +671 -0
- package/dist/metro-config.js.map +1 -0
- package/dist/react-native.config.d.ts +7 -0
- package/dist/react-native.config.d.ts.map +1 -0
- package/dist/react-native.config.js +80 -0
- package/dist/react-native.config.js.map +1 -0
- package/dist/src/DragSource.d.ts +52 -0
- package/dist/src/DragSource.d.ts.map +1 -0
- package/dist/src/DragSource.js +97 -0
- package/dist/src/DragSource.js.map +1 -0
- package/dist/src/DropTarget.d.ts +58 -0
- package/dist/src/DropTarget.d.ts.map +1 -0
- package/dist/src/DropTarget.js +131 -0
- package/dist/src/DropTarget.js.map +1 -0
- package/dist/src/KeyHandler.d.ts +57 -0
- package/dist/src/KeyHandler.d.ts.map +1 -0
- package/dist/src/KeyHandler.js +48 -0
- package/dist/src/KeyHandler.js.map +1 -0
- package/dist/src/Menu.d.ts +72 -0
- package/dist/src/Menu.d.ts.map +1 -0
- package/dist/src/Menu.js +179 -0
- package/dist/src/Menu.js.map +1 -0
- package/dist/src/TitleBar.d.ts +75 -0
- package/dist/src/TitleBar.d.ts.map +1 -0
- package/dist/src/TitleBar.js +236 -0
- package/dist/src/TitleBar.js.map +1 -0
- package/dist/src/Window.d.ts +87 -0
- package/dist/src/Window.d.ts.map +1 -0
- package/dist/src/Window.js +248 -0
- package/dist/src/Window.js.map +1 -0
- package/dist/src/closeRequest.d.ts +55 -0
- package/dist/src/closeRequest.d.ts.map +1 -0
- package/dist/src/closeRequest.js +134 -0
- package/dist/src/closeRequest.js.map +1 -0
- package/dist/src/index.d.ts +45 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +92 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/overrides/Alert.d.ts +69 -0
- package/dist/src/overrides/Alert.d.ts.map +1 -0
- package/dist/src/overrides/Alert.js +112 -0
- package/dist/src/overrides/Alert.js.map +1 -0
- package/dist/src/overrides/BaseViewConfig.d.ts +28 -0
- package/dist/src/overrides/BaseViewConfig.d.ts.map +1 -0
- package/dist/src/overrides/BaseViewConfig.js +72 -0
- package/dist/src/overrides/BaseViewConfig.js.map +1 -0
- package/dist/src/overrides/Platform.linux.d.ts +10 -0
- package/dist/src/overrides/Platform.linux.d.ts.map +1 -0
- package/dist/src/overrides/Platform.linux.js +15 -0
- package/dist/src/overrides/Platform.linux.js.map +1 -0
- package/dist/src/overrides/Platform.macos.d.ts +10 -0
- package/dist/src/overrides/Platform.macos.d.ts.map +1 -0
- package/dist/src/overrides/Platform.macos.js +15 -0
- package/dist/src/overrides/Platform.macos.js.map +1 -0
- package/dist/src/overrides/Platform.windows.d.ts +10 -0
- package/dist/src/overrides/Platform.windows.d.ts.map +1 -0
- package/dist/src/overrides/Platform.windows.js +15 -0
- package/dist/src/overrides/Platform.windows.js.map +1 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts +24 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts.map +1 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.js +30 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.js.map +1 -0
- package/dist/src/overrides/RefreshControl.d.ts +52 -0
- package/dist/src/overrides/RefreshControl.d.ts.map +1 -0
- package/dist/src/overrides/RefreshControl.js +92 -0
- package/dist/src/overrides/RefreshControl.js.map +1 -0
- package/dist/src/overrides/ScrollView.d.ts +39 -0
- package/dist/src/overrides/ScrollView.d.ts.map +1 -0
- package/dist/src/overrides/ScrollView.js +59 -0
- package/dist/src/overrides/ScrollView.js.map +1 -0
- package/dist/src/overrides/Share.d.ts +96 -0
- package/dist/src/overrides/Share.d.ts.map +1 -0
- package/dist/src/overrides/Share.js +97 -0
- package/dist/src/overrides/Share.js.map +1 -0
- package/dist/src/overrides/TextInput.d.ts +81 -0
- package/dist/src/overrides/TextInput.d.ts.map +1 -0
- package/dist/src/overrides/TextInput.js +213 -0
- package/dist/src/overrides/TextInput.js.map +1 -0
- package/dist/src/overrides/View.d.ts +36 -0
- package/dist/src/overrides/View.d.ts.map +1 -0
- package/dist/src/overrides/View.js +99 -0
- package/dist/src/overrides/View.js.map +1 -0
- package/dist/src/overrides/createPlatform.d.ts +32 -0
- package/dist/src/overrides/createPlatform.d.ts.map +1 -0
- package/dist/src/overrides/createPlatform.js +84 -0
- package/dist/src/overrides/createPlatform.js.map +1 -0
- package/dist/src/overrides/setUpDeveloperTools.d.ts +28 -0
- package/dist/src/overrides/setUpDeveloperTools.d.ts.map +1 -0
- package/dist/src/overrides/setUpDeveloperTools.js +84 -0
- package/dist/src/overrides/setUpDeveloperTools.js.map +1 -0
- package/dist/src/overrides/setUpXHR.d.ts +46 -0
- package/dist/src/overrides/setUpXHR.d.ts.map +1 -0
- package/dist/src/overrides/setUpXHR.js +236 -0
- package/dist/src/overrides/setUpXHR.js.map +1 -0
- package/dist/src/quitRequest.d.ts +55 -0
- package/dist/src/quitRequest.d.ts.map +1 -0
- package/dist/src/quitRequest.js +121 -0
- package/dist/src/quitRequest.js.map +1 -0
- package/dist/src/resourcePath.d.ts +18 -0
- package/dist/src/resourcePath.d.ts.map +1 -0
- package/dist/src/resourcePath.js +31 -0
- package/dist/src/resourcePath.js.map +1 -0
- package/dist/src/responseBody.d.ts +76 -0
- package/dist/src/responseBody.d.ts.map +1 -0
- package/dist/src/responseBody.js +94 -0
- package/dist/src/responseBody.js.map +1 -0
- package/dist/src/responseHeaders.d.ts +40 -0
- package/dist/src/responseHeaders.d.ts.map +1 -0
- package/dist/src/responseHeaders.js +75 -0
- package/dist/src/responseHeaders.js.map +1 -0
- package/dist/src/titleBarState.d.ts +43 -0
- package/dist/src/titleBarState.d.ts.map +1 -0
- package/dist/src/titleBarState.js +77 -0
- package/dist/src/titleBarState.js.map +1 -0
- package/dist/src/useContextMenu.d.ts +113 -0
- package/dist/src/useContextMenu.d.ts.map +1 -0
- package/dist/src/useContextMenu.js +109 -0
- package/dist/src/useContextMenu.js.map +1 -0
- package/dist/src/useDialog.d.ts +98 -0
- package/dist/src/useDialog.d.ts.map +1 -0
- package/dist/src/useDialog.js +112 -0
- package/dist/src/useDialog.js.map +1 -0
- package/dist/src/useDisplays.d.ts +70 -0
- package/dist/src/useDisplays.d.ts.map +1 -0
- package/dist/src/useDisplays.js +118 -0
- package/dist/src/useDisplays.js.map +1 -0
- package/dist/src/useHandledKeys.d.ts +38 -0
- package/dist/src/useHandledKeys.d.ts.map +1 -0
- package/dist/src/useHandledKeys.js +70 -0
- package/dist/src/useHandledKeys.js.map +1 -0
- package/dist/src/useWindow.d.ts +140 -0
- package/dist/src/useWindow.d.ts.map +1 -0
- package/dist/src/useWindow.js +233 -0
- package/dist/src/useWindow.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/native/CMakeLists.txt +355 -0
- package/native/bootstrap.sh +613 -0
- package/native/cmake/CompilerFlags.cmake +49 -0
- package/native/cmake/Expo.cmake +125 -0
- package/native/cmake/ReactNativeCore.cmake +612 -0
- package/native/cmake/Reanimated.cmake +108 -0
- package/native/cmake/Skia.cmake +379 -0
- package/native/cmake/ThirdParty.cmake +290 -0
- package/native/cmake/Worklets.cmake +67 -0
- package/native/cmake/codegen/CMakeLists.txt +32 -0
- package/native/core/AppIdentity.cpp +85 -0
- package/native/core/AppIdentity.h +59 -0
- package/native/core/AppPaths.cpp +96 -0
- package/native/core/AppPaths.h +53 -0
- package/native/core/AppearanceModule.cpp +60 -0
- package/native/core/AppearanceModule.h +40 -0
- package/native/core/Backface.h +59 -0
- package/native/core/BlobModule.cpp +283 -0
- package/native/core/BlobModule.h +76 -0
- package/native/core/BlobRegistry.cpp +75 -0
- package/native/core/BlobRegistry.h +44 -0
- package/native/core/ColorScheme.cpp +105 -0
- package/native/core/ColorScheme.h +59 -0
- package/native/core/ComponentRegistry.h +30 -0
- package/native/core/ControlMetrics.h +37 -0
- package/native/core/CoreModules.cpp +592 -0
- package/native/core/CoreModules.h +308 -0
- package/native/core/DebuggingOverlay.cpp +106 -0
- package/native/core/DebuggingOverlay.h +65 -0
- package/native/core/DesktopControls.cpp +232 -0
- package/native/core/DesktopControls.h +170 -0
- package/native/core/DevBundle.cpp +108 -0
- package/native/core/DevBundle.h +69 -0
- package/native/core/DevMenu.cpp +99 -0
- package/native/core/DevMenu.h +75 -0
- package/native/core/DialogModule.cpp +150 -0
- package/native/core/DialogModule.h +51 -0
- package/native/core/DragAndDrop.cpp +45 -0
- package/native/core/DragAndDrop.h +225 -0
- package/native/core/ExpoImageComponent.cpp +79 -0
- package/native/core/ExpoImageComponent.h +80 -0
- package/native/core/ExpoModules.cpp +415 -0
- package/native/core/ExpoModules.h +115 -0
- package/native/core/ExpoRuntime.cpp +366 -0
- package/native/core/ExpoRuntime.h +42 -0
- package/native/core/FocusRing.h +36 -0
- package/native/core/FontRegistry.h +51 -0
- package/native/core/GestureHandlerModule.cpp +136 -0
- package/native/core/GestureHandlerModule.h +71 -0
- package/native/core/Gestures.cpp +533 -0
- package/native/core/Gestures.h +145 -0
- package/native/core/HoverTracker.cpp +69 -0
- package/native/core/HoverTracker.h +109 -0
- package/native/core/HttpClient.cpp +282 -0
- package/native/core/ImageBytes.cpp +205 -0
- package/native/core/ImageBytes.h +29 -0
- package/native/core/ImageCache.cpp +51 -0
- package/native/core/ImageCache.h +89 -0
- package/native/core/JsRuntimeAccess.cpp +70 -0
- package/native/core/JsRuntimeAccess.h +60 -0
- package/native/core/JsiPromise.h +51 -0
- package/native/core/KeyEvents.cpp +116 -0
- package/native/core/KeyEvents.h +144 -0
- package/native/core/LogBoxSurface.cpp +46 -0
- package/native/core/LogBoxSurface.h +68 -0
- package/native/core/MenuModel.h +117 -0
- package/native/core/MenuModule.cpp +209 -0
- package/native/core/MenuModule.h +66 -0
- package/native/core/MountingWalk.h +524 -0
- package/native/core/PackageModules.cpp.in +25 -0
- package/native/core/PackageModules.h +50 -0
- package/native/core/PlatformConstantsModule.cpp +63 -0
- package/native/core/PlatformConstantsModule.h +64 -0
- package/native/core/PlatformServices.h +213 -0
- package/native/core/PointerButtons.h +63 -0
- package/native/core/PullToRefresh.cpp +57 -0
- package/native/core/PullToRefresh.h +73 -0
- package/native/core/ReanimatedCompat.h +47 -0
- package/native/core/ReanimatedModule.cpp +203 -0
- package/native/core/ReanimatedModule.h +55 -0
- package/native/core/ScrollAnimation.cpp +59 -0
- package/native/core/ScrollAnimation.h +70 -0
- package/native/core/ScrollBounds.cpp +26 -0
- package/native/core/ScrollBounds.h +68 -0
- package/native/core/ScrollIndicator.cpp +50 -0
- package/native/core/ScrollIndicator.h +69 -0
- package/native/core/ScrollMomentum.cpp +71 -0
- package/native/core/ScrollMomentum.h +73 -0
- package/native/core/ScrollSnap.cpp +123 -0
- package/native/core/ScrollSnap.h +80 -0
- package/native/core/ShareFallback.cpp +101 -0
- package/native/core/ShareFallback.h +45 -0
- package/native/core/SkiaPictureViewComponent.cpp +11 -0
- package/native/core/SkiaPictureViewComponent.h +47 -0
- package/native/core/SourceCodeModule.cpp +55 -0
- package/native/core/SourceCodeModule.h +53 -0
- package/native/core/StatusBarModule.cpp +23 -0
- package/native/core/StatusBarModule.h +42 -0
- package/native/core/TestDialog.cpp +148 -0
- package/native/core/TestDialog.h +96 -0
- package/native/core/TestKeys.cpp +78 -0
- package/native/core/TestKeys.h +50 -0
- package/native/core/TestQuitFile.cpp +33 -0
- package/native/core/TestQuitFile.h +60 -0
- package/native/core/TestSettle.h +192 -0
- package/native/core/TitleBarRegions.h +31 -0
- package/native/core/TurboModuleProxy.cpp +51 -0
- package/native/core/TurboModuleProxy.h +46 -0
- package/native/core/UIManagerAccess.cpp +43 -0
- package/native/core/UIManagerAccess.h +43 -0
- package/native/core/WindowBoundsCache.cpp +319 -0
- package/native/core/WindowControl.h +235 -0
- package/native/core/WindowHost.h +158 -0
- package/native/core/WindowMethods.cpp +167 -0
- package/native/core/WindowMethods.h +84 -0
- package/native/core/WindowsModule.cpp +448 -0
- package/native/core/WindowsModule.h +133 -0
- package/native/core/WorkletsModule.cpp +199 -0
- package/native/core/WorkletsModule.h +57 -0
- package/native/core/portability_probe.cpp +210 -0
- package/native/tests/EventRecorder.h +124 -0
- package/native/tests/TestHarness.cpp +109 -0
- package/native/tests/TestHarness.h +89 -0
- package/native/tests/TreeDump.h +49 -0
- package/native/tests/test_app_identity.cpp +114 -0
- package/native/tests/test_app_paths.cpp +48 -0
- package/native/tests/test_appearance.cpp +107 -0
- package/native/tests/test_backface.cpp +79 -0
- package/native/tests/test_blobs.cpp +157 -0
- package/native/tests/test_controls.cpp +185 -0
- package/native/tests/test_devbundle.cpp +103 -0
- package/native/tests/test_drag_and_drop.cpp +143 -0
- package/native/tests/test_gestures.cpp +331 -0
- package/native/tests/test_hover.cpp +92 -0
- package/native/tests/test_image_cache.cpp +109 -0
- package/native/tests/test_js_runtime_access.cpp +137 -0
- package/native/tests/test_key_events.cpp +235 -0
- package/native/tests/test_momentum.cpp +138 -0
- package/native/tests/test_quit_file.cpp +104 -0
- package/native/tests/test_scroll_animation.cpp +120 -0
- package/native/tests/test_scroll_bounds.cpp +66 -0
- package/native/tests/test_scroll_indicator.cpp +149 -0
- package/native/tests/test_scroll_snap.cpp +112 -0
- package/native/tests/test_settle.cpp +115 -0
- package/native/tests/test_share.cpp +75 -0
- package/native/tests/test_sourcecode.cpp +62 -0
- package/native/tests/test_window_close.cpp +119 -0
- package/native/tests/test_window_limits.cpp +123 -0
- package/package.json +43 -0
- package/react-native.config.js +86 -0
- package/supported-versions.json +66 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// Tests for the gesture recognisers.
|
|
2
|
+
//
|
|
3
|
+
// Core's, so compiled into both platforms' suites: a pan is a pan whichever
|
|
4
|
+
// toolkit reported the pointer.
|
|
5
|
+
//
|
|
6
|
+
// Driven through the registry rather than through the module, which is what
|
|
7
|
+
// makes them possible at all -- the module hops to the UI thread, and a test
|
|
8
|
+
// binary has no UI thread to hop to. The same reason keeps the long press out
|
|
9
|
+
// of here: it activates from a timer, and `postDelayed` needs a run loop. What
|
|
10
|
+
// is covered is everything decided by the pointer itself.
|
|
11
|
+
|
|
12
|
+
#include "TestHarness.h"
|
|
13
|
+
|
|
14
|
+
#include "Gestures.h"
|
|
15
|
+
|
|
16
|
+
#include <sstream>
|
|
17
|
+
#include <string>
|
|
18
|
+
#include <initializer_list>
|
|
19
|
+
#include <vector>
|
|
20
|
+
|
|
21
|
+
namespace {
|
|
22
|
+
|
|
23
|
+
struct Recorded {
|
|
24
|
+
std::string event;
|
|
25
|
+
int handlerTag{0};
|
|
26
|
+
int state{0};
|
|
27
|
+
int oldState{-1};
|
|
28
|
+
double translationX{0};
|
|
29
|
+
double translationY{0};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// A registry with nothing left over from the last test. The registry is a
|
|
33
|
+
// process-wide singleton -- there is one pointer -- so each test starts by
|
|
34
|
+
// dropping whatever the previous one attached.
|
|
35
|
+
basalt::GestureRegistry &fresh(std::vector<Recorded> &recorded) {
|
|
36
|
+
auto ®istry = basalt::gestures();
|
|
37
|
+
for (int tag = 1; tag <= 32; tag++) {
|
|
38
|
+
registry.drop(tag);
|
|
39
|
+
}
|
|
40
|
+
recorded.clear();
|
|
41
|
+
registry.setEmitter([&recorded](const std::string &event, folly::dynamic payload) {
|
|
42
|
+
Recorded entry;
|
|
43
|
+
entry.event = event;
|
|
44
|
+
entry.handlerTag = static_cast<int>(payload["handlerTag"].asDouble());
|
|
45
|
+
entry.state = static_cast<int>(payload["state"].asDouble());
|
|
46
|
+
if (const auto *old = payload.get_ptr("oldState"); old != nullptr) {
|
|
47
|
+
entry.oldState = static_cast<int>(old->asDouble());
|
|
48
|
+
}
|
|
49
|
+
if (const auto *tx = payload.get_ptr("translationX"); tx != nullptr) {
|
|
50
|
+
entry.translationX = tx->asDouble();
|
|
51
|
+
}
|
|
52
|
+
if (const auto *ty = payload.get_ptr("translationY"); ty != nullptr) {
|
|
53
|
+
entry.translationY = ty->asDouble();
|
|
54
|
+
}
|
|
55
|
+
recorded.push_back(entry);
|
|
56
|
+
});
|
|
57
|
+
return registry;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The state changes one handler went through, as "2,4,5" -- readable in a
|
|
61
|
+
// failure message, which a vector of ints is not.
|
|
62
|
+
std::string statesFor(const std::vector<Recorded> &recorded, int handlerTag) {
|
|
63
|
+
std::ostringstream states;
|
|
64
|
+
bool first = true;
|
|
65
|
+
for (const auto &entry : recorded) {
|
|
66
|
+
if (entry.handlerTag == handlerTag && entry.event == "onGestureHandlerStateChange") {
|
|
67
|
+
if (!first) {
|
|
68
|
+
states << ",";
|
|
69
|
+
}
|
|
70
|
+
states << entry.state;
|
|
71
|
+
first = false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return states.str();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
int countOf(const std::vector<Recorded> &recorded, int handlerTag, const char *event) {
|
|
78
|
+
int count = 0;
|
|
79
|
+
for (const auto &entry : recorded) {
|
|
80
|
+
if (entry.handlerTag == handlerTag && entry.event == event) {
|
|
81
|
+
count++;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return count;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
std::vector<basalt::HitView> chainOf(int tag, double originX = 0, double originY = 0) {
|
|
88
|
+
return {basalt::HitView{.tag = tag, .originX = originX, .originY = originY}};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Spelled the same way statesFor() spells them, so an expectation reads as the
|
|
92
|
+
// sequence it is.
|
|
93
|
+
std::string states(std::initializer_list<int> values) {
|
|
94
|
+
std::ostringstream out;
|
|
95
|
+
bool first = true;
|
|
96
|
+
for (int value : values) {
|
|
97
|
+
if (!first) {
|
|
98
|
+
out << ",";
|
|
99
|
+
}
|
|
100
|
+
out << value;
|
|
101
|
+
first = false;
|
|
102
|
+
}
|
|
103
|
+
return out.str();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
constexpr int kBegan = static_cast<int>(basalt::GestureState::Began);
|
|
107
|
+
constexpr int kActive = static_cast<int>(basalt::GestureState::Active);
|
|
108
|
+
constexpr int kEnd = static_cast<int>(basalt::GestureState::End);
|
|
109
|
+
constexpr int kFailed = static_cast<int>(basalt::GestureState::Failed);
|
|
110
|
+
constexpr int kCancelled = static_cast<int>(basalt::GestureState::Cancelled);
|
|
111
|
+
|
|
112
|
+
} // namespace
|
|
113
|
+
|
|
114
|
+
TEST(a_pan_begins_then_activates_once_it_has_moved) {
|
|
115
|
+
std::vector<Recorded> recorded;
|
|
116
|
+
auto ®istry = fresh(recorded);
|
|
117
|
+
registry.create("PanGestureHandler", 1, folly::dynamic::object());
|
|
118
|
+
registry.attach(1, 100);
|
|
119
|
+
|
|
120
|
+
registry.pointerDown(chainOf(100), 50, 50, 0);
|
|
121
|
+
// Under the default 10pt slop: still only BEGAN.
|
|
122
|
+
registry.pointerMove(54, 50, 16);
|
|
123
|
+
EXPECT_EQ(statesFor(recorded, 1), states({kBegan}));
|
|
124
|
+
|
|
125
|
+
registry.pointerMove(80, 50, 32);
|
|
126
|
+
registry.pointerUp(80, 50, 48);
|
|
127
|
+
EXPECT_EQ(statesFor(recorded, 1), states({kBegan, kActive, kEnd}));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
TEST(a_pans_translation_is_measured_from_where_it_started) {
|
|
131
|
+
std::vector<Recorded> recorded;
|
|
132
|
+
auto ®istry = fresh(recorded);
|
|
133
|
+
registry.create("PanGestureHandler", 1, folly::dynamic::object());
|
|
134
|
+
registry.attach(1, 100);
|
|
135
|
+
|
|
136
|
+
registry.pointerDown(chainOf(100), 50, 50, 0);
|
|
137
|
+
registry.pointerMove(90, 70, 16);
|
|
138
|
+
registry.pointerMove(120, 90, 32);
|
|
139
|
+
|
|
140
|
+
// Not from the previous update: RNGH's translation is cumulative, and an app
|
|
141
|
+
// that adds it up frame by frame moves twice as far as the pointer did.
|
|
142
|
+
const Recorded &last = recorded.back();
|
|
143
|
+
EXPECT_EQ(last.event, std::string("onGestureHandlerEvent"));
|
|
144
|
+
EXPECT_EQ(static_cast<int>(last.translationX), 70);
|
|
145
|
+
EXPECT_EQ(static_cast<int>(last.translationY), 40);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
TEST(a_tap_is_a_press_that_did_not_move) {
|
|
149
|
+
std::vector<Recorded> recorded;
|
|
150
|
+
auto ®istry = fresh(recorded);
|
|
151
|
+
registry.create("TapGestureHandler", 1, folly::dynamic::object());
|
|
152
|
+
registry.attach(1, 100);
|
|
153
|
+
|
|
154
|
+
registry.pointerDown(chainOf(100), 10, 10, 0);
|
|
155
|
+
registry.pointerUp(12, 11, 90);
|
|
156
|
+
|
|
157
|
+
EXPECT_EQ(statesFor(recorded, 1), states({kBegan, kActive, kEnd}));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
TEST(a_tap_fails_when_the_pointer_wanders) {
|
|
161
|
+
std::vector<Recorded> recorded;
|
|
162
|
+
auto ®istry = fresh(recorded);
|
|
163
|
+
registry.create("TapGestureHandler", 1, folly::dynamic::object());
|
|
164
|
+
registry.attach(1, 100);
|
|
165
|
+
|
|
166
|
+
registry.pointerDown(chainOf(100), 10, 10, 0);
|
|
167
|
+
registry.pointerMove(200, 10, 40);
|
|
168
|
+
registry.pointerUp(200, 10, 80);
|
|
169
|
+
|
|
170
|
+
EXPECT_EQ(statesFor(recorded, 1), states({kBegan, kFailed}));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
TEST(a_tap_fails_when_it_is_held_too_long) {
|
|
174
|
+
std::vector<Recorded> recorded;
|
|
175
|
+
auto ®istry = fresh(recorded);
|
|
176
|
+
registry.create("TapGestureHandler", 1, folly::dynamic::object());
|
|
177
|
+
registry.attach(1, 100);
|
|
178
|
+
|
|
179
|
+
registry.pointerDown(chainOf(100), 10, 10, 0);
|
|
180
|
+
registry.pointerUp(10, 10, 5000);
|
|
181
|
+
|
|
182
|
+
EXPECT_EQ(statesFor(recorded, 1), states({kBegan, kFailed}));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
TEST(a_gesture_that_activates_cancels_the_others) {
|
|
186
|
+
std::vector<Recorded> recorded;
|
|
187
|
+
auto ®istry = fresh(recorded);
|
|
188
|
+
registry.create("PanGestureHandler", 1, folly::dynamic::object());
|
|
189
|
+
registry.attach(1, 100);
|
|
190
|
+
registry.create("TapGestureHandler", 2, folly::dynamic::object());
|
|
191
|
+
registry.attach(2, 100);
|
|
192
|
+
|
|
193
|
+
registry.pointerDown(chainOf(100), 10, 10, 0);
|
|
194
|
+
registry.pointerMove(200, 10, 40);
|
|
195
|
+
|
|
196
|
+
EXPECT_EQ(statesFor(recorded, 1), states({kBegan, kActive}));
|
|
197
|
+
// The tap had already failed on the slop check, which is the right answer for
|
|
198
|
+
// the wrong-looking reason: either way it must not be left at BEGAN.
|
|
199
|
+
const std::string tapStates = statesFor(recorded, 2);
|
|
200
|
+
EXPECT(tapStates == states({kBegan, kFailed}) || tapStates == states({kBegan, kCancelled}));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
TEST(simultaneous_handlers_are_left_alone) {
|
|
204
|
+
std::vector<Recorded> recorded;
|
|
205
|
+
auto ®istry = fresh(recorded);
|
|
206
|
+
|
|
207
|
+
registry.create("PanGestureHandler", 1, folly::dynamic::object());
|
|
208
|
+
registry.attach(1, 100);
|
|
209
|
+
|
|
210
|
+
// A second pan on the same view, declared simultaneous with the first. Both
|
|
211
|
+
// should activate; without the declaration the second would be cancelled.
|
|
212
|
+
folly::dynamic simultaneous = folly::dynamic::object("simultaneousHandlers",
|
|
213
|
+
folly::dynamic::array(1));
|
|
214
|
+
registry.create("PanGestureHandler", 2, simultaneous);
|
|
215
|
+
registry.attach(2, 100);
|
|
216
|
+
|
|
217
|
+
registry.pointerDown(chainOf(100), 10, 10, 0);
|
|
218
|
+
registry.pointerMove(200, 10, 40);
|
|
219
|
+
registry.pointerUp(200, 10, 80);
|
|
220
|
+
|
|
221
|
+
EXPECT_EQ(statesFor(recorded, 1), states({kBegan, kActive, kEnd}));
|
|
222
|
+
EXPECT_EQ(statesFor(recorded, 2), states({kBegan, kActive, kEnd}));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
TEST(a_handler_waits_for_the_one_it_was_told_to_wait_for) {
|
|
226
|
+
std::vector<Recorded> recorded;
|
|
227
|
+
auto ®istry = fresh(recorded);
|
|
228
|
+
|
|
229
|
+
registry.create("PanGestureHandler", 1, folly::dynamic::object());
|
|
230
|
+
registry.attach(1, 100);
|
|
231
|
+
registry.create("PanGestureHandler", 2,
|
|
232
|
+
folly::dynamic::object("waitFor", folly::dynamic::array(1)));
|
|
233
|
+
registry.attach(2, 100);
|
|
234
|
+
|
|
235
|
+
registry.pointerDown(chainOf(100), 10, 10, 0);
|
|
236
|
+
registry.pointerMove(200, 10, 40);
|
|
237
|
+
|
|
238
|
+
// 1 activated, which cancelled 2 -- and 2 never activated, because it was
|
|
239
|
+
// waiting for 1 to fail and 1 did the opposite.
|
|
240
|
+
EXPECT_EQ(statesFor(recorded, 1), states({kBegan, kActive}));
|
|
241
|
+
const std::string waiting = statesFor(recorded, 2);
|
|
242
|
+
EXPECT(waiting == states({kBegan, kFailed}) || waiting == states({kBegan, kCancelled}));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
TEST(a_gesture_reaches_a_handler_on_an_ancestor) {
|
|
246
|
+
std::vector<Recorded> recorded;
|
|
247
|
+
auto ®istry = fresh(recorded);
|
|
248
|
+
registry.create("PanGestureHandler", 1, folly::dynamic::object());
|
|
249
|
+
// Attached to the outer view, while the pointer lands on the inner one.
|
|
250
|
+
registry.attach(1, 200);
|
|
251
|
+
|
|
252
|
+
const std::vector<basalt::HitView> chain = {
|
|
253
|
+
basalt::HitView{.tag = 100, .originX = 30, .originY = 30},
|
|
254
|
+
basalt::HitView{.tag = 200, .originX = 10, .originY = 10},
|
|
255
|
+
};
|
|
256
|
+
registry.pointerDown(chain, 50, 50, 0);
|
|
257
|
+
registry.pointerMove(100, 50, 16);
|
|
258
|
+
|
|
259
|
+
EXPECT_EQ(statesFor(recorded, 1), states({kBegan, kActive}));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
TEST(a_dropped_handler_stops_hearing_about_the_pointer) {
|
|
263
|
+
std::vector<Recorded> recorded;
|
|
264
|
+
auto ®istry = fresh(recorded);
|
|
265
|
+
registry.create("PanGestureHandler", 1, folly::dynamic::object());
|
|
266
|
+
registry.attach(1, 100);
|
|
267
|
+
registry.drop(1);
|
|
268
|
+
|
|
269
|
+
registry.pointerDown(chainOf(100), 10, 10, 0);
|
|
270
|
+
registry.pointerMove(200, 10, 40);
|
|
271
|
+
|
|
272
|
+
EXPECT_EQ(countOf(recorded, 1, "onGestureHandlerStateChange"), 0);
|
|
273
|
+
EXPECT(registry.empty());
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
TEST(a_handler_attached_to_nothing_is_never_offered_a_gesture) {
|
|
277
|
+
std::vector<Recorded> recorded;
|
|
278
|
+
auto ®istry = fresh(recorded);
|
|
279
|
+
registry.create("PanGestureHandler", 1, folly::dynamic::object());
|
|
280
|
+
// No attach: the view tag is 0, which is not a view.
|
|
281
|
+
|
|
282
|
+
registry.pointerDown(chainOf(0), 10, 10, 0);
|
|
283
|
+
registry.pointerMove(200, 10, 40);
|
|
284
|
+
|
|
285
|
+
EXPECT_EQ(countOf(recorded, 1, "onGestureHandlerStateChange"), 0);
|
|
286
|
+
registry.drop(1);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
TEST(a_gesture_that_cannot_happen_here_fails_rather_than_hanging) {
|
|
290
|
+
std::vector<Recorded> recorded;
|
|
291
|
+
auto ®istry = fresh(recorded);
|
|
292
|
+
// Pinch needs a second finger, which a cursor does not have.
|
|
293
|
+
registry.create("PinchGestureHandler", 1, folly::dynamic::object());
|
|
294
|
+
registry.attach(1, 100);
|
|
295
|
+
|
|
296
|
+
registry.pointerDown(chainOf(100), 10, 10, 0);
|
|
297
|
+
registry.pointerMove(200, 200, 40);
|
|
298
|
+
registry.pointerUp(200, 200, 80);
|
|
299
|
+
|
|
300
|
+
// BEGAN then FAILED, never ACTIVE: an app's onBegin runs and its onStart does
|
|
301
|
+
// not, which is what "this platform cannot do that" should look like.
|
|
302
|
+
EXPECT_EQ(statesFor(recorded, 1), states({kBegan, kFailed}));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
TEST(the_pointers_position_is_reported_relative_to_the_handlers_view) {
|
|
306
|
+
std::vector<Recorded> recorded;
|
|
307
|
+
auto ®istry = fresh(recorded);
|
|
308
|
+
registry.create("PanGestureHandler", 1, folly::dynamic::object());
|
|
309
|
+
registry.attach(1, 100);
|
|
310
|
+
|
|
311
|
+
// The view sits at (40, 20) in the root, so a pointer at (100, 60) is at
|
|
312
|
+
// (60, 40) inside it.
|
|
313
|
+
registry.pointerDown(chainOf(100, 40, 20), 100, 60, 0);
|
|
314
|
+
|
|
315
|
+
bool checked = false;
|
|
316
|
+
for (const auto &entry : recorded) {
|
|
317
|
+
(void)entry;
|
|
318
|
+
checked = true;
|
|
319
|
+
}
|
|
320
|
+
EXPECT(checked);
|
|
321
|
+
|
|
322
|
+
// Read the payload directly for the one field the recorder does not keep.
|
|
323
|
+
registry.setEmitter([&checked](const std::string &, folly::dynamic payload) {
|
|
324
|
+
EXPECT_EQ(static_cast<int>(payload["x"].asDouble()), 80);
|
|
325
|
+
EXPECT_EQ(static_cast<int>(payload["y"].asDouble()), 40);
|
|
326
|
+
EXPECT_EQ(static_cast<int>(payload["absoluteX"].asDouble()), 120);
|
|
327
|
+
checked = true;
|
|
328
|
+
});
|
|
329
|
+
registry.pointerMove(120, 60, 16);
|
|
330
|
+
registry.pointerUp(120, 60, 32);
|
|
331
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Tests for the hover gate.
|
|
2
|
+
//
|
|
3
|
+
// Core's, so compiled into every platform's suite: which views a cursor is
|
|
4
|
+
// inside is the toolkit's answer, and what to do about it is not.
|
|
5
|
+
//
|
|
6
|
+
// What is asserted here is small on purpose, because the hover *model* is
|
|
7
|
+
// React Native's own -- `PointerEventsProcessor` turns a stream of moves into
|
|
8
|
+
// enter, leave, over and out, and a host that also emits those gets every one
|
|
9
|
+
// of them twice over. See the header of HoverTracker.h. What is left to this
|
|
10
|
+
// project is deciding which moves are worth dispatching at all, and the case
|
|
11
|
+
// worth testing is the one that is easy to get wrong: the move *after* the
|
|
12
|
+
// cursor leaves everything that was listening.
|
|
13
|
+
|
|
14
|
+
#include "TestHarness.h"
|
|
15
|
+
|
|
16
|
+
#include "HoverTracker.h"
|
|
17
|
+
|
|
18
|
+
#include <cstdint>
|
|
19
|
+
#include <sstream>
|
|
20
|
+
|
|
21
|
+
namespace {
|
|
22
|
+
|
|
23
|
+
using basalt::HoverTracker;
|
|
24
|
+
|
|
25
|
+
constexpr std::uint16_t kListening = basalt::HoverListenerEnter | basalt::HoverListenerLeave;
|
|
26
|
+
constexpr std::uint16_t kSilent = basalt::HoverListenerNone;
|
|
27
|
+
|
|
28
|
+
} // namespace
|
|
29
|
+
|
|
30
|
+
TEST(hover_dispatches_a_move_over_a_view_that_listens) {
|
|
31
|
+
HoverTracker tracker;
|
|
32
|
+
EXPECT(tracker.admitMove(10, kListening));
|
|
33
|
+
// Every move while the cursor is over it, because React Native works out
|
|
34
|
+
// whether anything actually changed.
|
|
35
|
+
EXPECT(tracker.admitMove(10, kListening));
|
|
36
|
+
EXPECT(tracker.admitMove(11, kListening));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
TEST(hover_dispatches_nothing_over_views_that_do_not) {
|
|
40
|
+
HoverTracker tracker;
|
|
41
|
+
// The reason the gate exists: a cursor crossing an app that uses no hover
|
|
42
|
+
// must not cost a trip into JavaScript per motion event.
|
|
43
|
+
EXPECT(!tracker.admitMove(10, kSilent));
|
|
44
|
+
EXPECT(!tracker.admitMove(11, kSilent));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
TEST(hover_dispatches_one_last_move_after_leaving_a_listening_view) {
|
|
48
|
+
HoverTracker tracker;
|
|
49
|
+
EXPECT(tracker.admitMove(10, kListening));
|
|
50
|
+
// The cursor is now over something that listens to nothing. Suppressing this
|
|
51
|
+
// move would leave React Native holding a hover path it never hears is
|
|
52
|
+
// stale, and view 10 hovered for good -- so exactly one more goes through.
|
|
53
|
+
EXPECT(tracker.admitMove(11, kSilent));
|
|
54
|
+
EXPECT(!tracker.admitMove(12, kSilent));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
TEST(hover_leaving_the_surface_names_the_view_a_move_was_dispatched_at) {
|
|
58
|
+
HoverTracker tracker;
|
|
59
|
+
EXPECT(tracker.admitMove(10, kListening));
|
|
60
|
+
// A leave is dispatched at whatever React Native was last told about; the
|
|
61
|
+
// processor reads it as the pointer being gone and unwinds the path itself.
|
|
62
|
+
EXPECT_EQ(tracker.admitLeave(), 10);
|
|
63
|
+
// And a second leave is not a second event.
|
|
64
|
+
EXPECT_EQ(tracker.admitLeave(), 0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
TEST(hover_leaving_without_ever_dispatching_is_nothing) {
|
|
68
|
+
HoverTracker tracker;
|
|
69
|
+
tracker.admitMove(10, kSilent);
|
|
70
|
+
// React Native has no path to unwind, so there is nothing to tell it.
|
|
71
|
+
EXPECT_EQ(tracker.admitLeave(), 0);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
TEST(hover_forget_drops_the_state_without_naming_anything) {
|
|
75
|
+
HoverTracker tracker;
|
|
76
|
+
tracker.admitMove(10, kListening);
|
|
77
|
+
// For a surface teardown: the view is about to stop existing, and a leave
|
|
78
|
+
// dispatched at it would be a lookup of an emitter that is gone.
|
|
79
|
+
tracker.forget();
|
|
80
|
+
EXPECT_EQ(tracker.admitLeave(), 0);
|
|
81
|
+
EXPECT(!tracker.admitMove(11, kSilent));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
TEST(hover_listeners_come_off_the_props_react_native_parsed) {
|
|
85
|
+
facebook::react::ViewEvents events{};
|
|
86
|
+
EXPECT_EQ(basalt::hoverListenersFrom(events), (std::uint16_t)basalt::HoverListenerNone);
|
|
87
|
+
|
|
88
|
+
events[facebook::react::ViewEvents::Offset::PointerEnter] = true;
|
|
89
|
+
events[facebook::react::ViewEvents::Offset::PointerOutCapture] = true;
|
|
90
|
+
EXPECT_EQ(basalt::hoverListenersFrom(events),
|
|
91
|
+
(std::uint16_t)(basalt::HoverListenerEnter | basalt::HoverListenerOutCapture));
|
|
92
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Which decoded image gets dropped, and when.
|
|
2
|
+
//
|
|
3
|
+
// The three hosts hold three unrelated texture types and none of them can be
|
|
4
|
+
// described here, so what is tested is the decision rather than the pixels --
|
|
5
|
+
// see core/ImageCache.h.
|
|
6
|
+
|
|
7
|
+
#include "TestHarness.h"
|
|
8
|
+
|
|
9
|
+
#include "ImageCache.h"
|
|
10
|
+
|
|
11
|
+
#include <sstream>
|
|
12
|
+
|
|
13
|
+
using basalt::ImageCachePolicy;
|
|
14
|
+
|
|
15
|
+
TEST(image_cache_keeps_what_fits) {
|
|
16
|
+
ImageCachePolicy cache(1000);
|
|
17
|
+
EXPECT(cache.insert("a", 400).empty());
|
|
18
|
+
EXPECT(cache.insert("b", 400).empty());
|
|
19
|
+
EXPECT_EQ((long)cache.count(), 2L);
|
|
20
|
+
EXPECT_EQ((long)cache.bytes(), 800L);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
TEST(image_cache_evicts_the_oldest_when_it_does_not) {
|
|
24
|
+
ImageCachePolicy cache(1000);
|
|
25
|
+
cache.insert("a", 400);
|
|
26
|
+
cache.insert("b", 400);
|
|
27
|
+
|
|
28
|
+
const auto evicted = cache.insert("c", 400);
|
|
29
|
+
EXPECT_EQ((long)evicted.size(), 1L);
|
|
30
|
+
EXPECT(evicted[0] == "a");
|
|
31
|
+
EXPECT_EQ((long)cache.count(), 2L);
|
|
32
|
+
EXPECT_EQ((long)cache.bytes(), 800L);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// The whole reason for least-recently-*used* rather than least recently added:
|
|
36
|
+
// scrolling back up should not re-decode the rows about to be on screen.
|
|
37
|
+
TEST(image_cache_a_use_saves_an_entry) {
|
|
38
|
+
ImageCachePolicy cache(1000);
|
|
39
|
+
cache.insert("a", 400);
|
|
40
|
+
cache.insert("b", 400);
|
|
41
|
+
cache.noteUse("a");
|
|
42
|
+
|
|
43
|
+
const auto evicted = cache.insert("c", 400);
|
|
44
|
+
EXPECT_EQ((long)evicted.size(), 1L);
|
|
45
|
+
// `b` is now the oldest, because `a` was touched after it went in.
|
|
46
|
+
EXPECT(evicted[0] == "b");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
TEST(image_cache_evicts_as_many_as_it_takes) {
|
|
50
|
+
ImageCachePolicy cache(1000);
|
|
51
|
+
cache.insert("a", 300);
|
|
52
|
+
cache.insert("b", 300);
|
|
53
|
+
cache.insert("c", 300);
|
|
54
|
+
|
|
55
|
+
const auto evicted = cache.insert("big", 900);
|
|
56
|
+
EXPECT_EQ((long)evicted.size(), 3L);
|
|
57
|
+
EXPECT(evicted[0] == "a");
|
|
58
|
+
EXPECT(evicted[2] == "c");
|
|
59
|
+
EXPECT_EQ((long)cache.count(), 1L);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// On screen and larger than the whole budget. Keeping it is wrong in one way
|
|
63
|
+
// and evicting it is wrong in a worse one: it would be decoded again on the
|
|
64
|
+
// next frame, and every frame after that.
|
|
65
|
+
TEST(image_cache_keeps_an_image_larger_than_the_budget) {
|
|
66
|
+
ImageCachePolicy cache(1000);
|
|
67
|
+
const auto evicted = cache.insert("huge", 5000);
|
|
68
|
+
EXPECT(evicted.empty());
|
|
69
|
+
EXPECT_EQ((long)cache.count(), 1L);
|
|
70
|
+
|
|
71
|
+
// And the next insert clears it out rather than leaving both.
|
|
72
|
+
const auto next = cache.insert("small", 100);
|
|
73
|
+
EXPECT_EQ((long)next.size(), 1L);
|
|
74
|
+
EXPECT(next[0] == "huge");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
TEST(image_cache_reinserting_replaces_rather_than_doubling) {
|
|
78
|
+
ImageCachePolicy cache(1000);
|
|
79
|
+
cache.insert("a", 400);
|
|
80
|
+
cache.insert("a", 600);
|
|
81
|
+
EXPECT_EQ((long)cache.count(), 1L);
|
|
82
|
+
EXPECT_EQ((long)cache.bytes(), 600L);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
TEST(image_cache_remove_and_clear_free_their_bytes) {
|
|
86
|
+
ImageCachePolicy cache(1000);
|
|
87
|
+
cache.insert("a", 400);
|
|
88
|
+
cache.insert("b", 300);
|
|
89
|
+
|
|
90
|
+
cache.remove("a");
|
|
91
|
+
EXPECT_EQ((long)cache.count(), 1L);
|
|
92
|
+
EXPECT_EQ((long)cache.bytes(), 300L);
|
|
93
|
+
|
|
94
|
+
// Removing what is not there is not an error: a host may release an image
|
|
95
|
+
// this never saw.
|
|
96
|
+
cache.remove("nothing");
|
|
97
|
+
EXPECT_EQ((long)cache.count(), 1L);
|
|
98
|
+
|
|
99
|
+
cache.clear();
|
|
100
|
+
EXPECT_EQ((long)cache.count(), 0L);
|
|
101
|
+
EXPECT_EQ((long)cache.bytes(), 0L);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
TEST(image_cache_a_use_of_something_absent_is_harmless) {
|
|
105
|
+
ImageCachePolicy cache(1000);
|
|
106
|
+
cache.insert("a", 400);
|
|
107
|
+
cache.noteUse("never-seen");
|
|
108
|
+
EXPECT_EQ((long)cache.count(), 1L);
|
|
109
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// The door to the JavaScript thread that a host leaves open.
|
|
2
|
+
//
|
|
3
|
+
// What is testable here is the decision -- is there a runner, does work reach
|
|
4
|
+
// it, does clearing it stop the work -- and not the running, which needs a
|
|
5
|
+
// runtime and a host. So the runner in these tests records what it was handed
|
|
6
|
+
// rather than calling it: the point of the seam is that native code on some
|
|
7
|
+
// other thread can post *without* knowing whether anyone is listening, and that
|
|
8
|
+
// is exactly the part a test can pin down.
|
|
9
|
+
//
|
|
10
|
+
// The half that needs a runtime -- an event arriving at a JavaScript listener --
|
|
11
|
+
// is proven by the harness scenario, the same division test_blobs.cpp makes.
|
|
12
|
+
|
|
13
|
+
#include "TestHarness.h"
|
|
14
|
+
|
|
15
|
+
#include "ExpoModules.h"
|
|
16
|
+
#include "ExpoRuntime.h"
|
|
17
|
+
#include "JsRuntimeAccess.h"
|
|
18
|
+
|
|
19
|
+
#include <sstream>
|
|
20
|
+
#include <stdexcept>
|
|
21
|
+
#include <string>
|
|
22
|
+
#include <vector>
|
|
23
|
+
|
|
24
|
+
namespace {
|
|
25
|
+
|
|
26
|
+
// Restores the seam whatever a test does to it. Process-wide state that a later
|
|
27
|
+
// test reads, exactly like the blob registry.
|
|
28
|
+
struct RunnerFixture {
|
|
29
|
+
~RunnerFixture() { basalt::setRuntimeRunner(nullptr); }
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
} // namespace
|
|
33
|
+
|
|
34
|
+
TEST(without_a_host_there_is_nowhere_to_post) {
|
|
35
|
+
// The tests and the portability probe, and the answer has to be a reported
|
|
36
|
+
// false rather than a crash: a subprocess reader thread emitting into a
|
|
37
|
+
// process that never had a runtime is ordinary, not exceptional.
|
|
38
|
+
RunnerFixture fixture;
|
|
39
|
+
basalt::setRuntimeRunner(nullptr);
|
|
40
|
+
|
|
41
|
+
bool ran = false;
|
|
42
|
+
EXPECT(!basalt::runOnJsRuntime([&ran](facebook::jsi::Runtime &) { ran = true; }));
|
|
43
|
+
EXPECT(!ran);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
TEST(work_reaches_the_runner_a_host_left) {
|
|
47
|
+
RunnerFixture fixture;
|
|
48
|
+
int posted = 0;
|
|
49
|
+
basalt::setRuntimeRunner([&posted](std::function<void(facebook::jsi::Runtime &)>) {
|
|
50
|
+
// Held rather than called: calling it needs a runtime. What is asserted is
|
|
51
|
+
// that it arrived.
|
|
52
|
+
posted++;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
EXPECT(basalt::runOnJsRuntime([](facebook::jsi::Runtime &) {}));
|
|
56
|
+
EXPECT(basalt::runOnJsRuntime([](facebook::jsi::Runtime &) {}));
|
|
57
|
+
EXPECT_EQ(posted, 2);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
TEST(clearing_the_runner_stops_the_posting) {
|
|
61
|
+
// The shutdown path: a host clears this before it destroys its ReactHost, so
|
|
62
|
+
// that work in flight from another thread does not reach a runtime that is
|
|
63
|
+
// being joined.
|
|
64
|
+
RunnerFixture fixture;
|
|
65
|
+
int posted = 0;
|
|
66
|
+
basalt::setRuntimeRunner([&posted](std::function<void(facebook::jsi::Runtime &)>) { posted++; });
|
|
67
|
+
EXPECT(basalt::runOnJsRuntime([](facebook::jsi::Runtime &) {}));
|
|
68
|
+
|
|
69
|
+
basalt::setRuntimeRunner(nullptr);
|
|
70
|
+
EXPECT(!basalt::runOnJsRuntime([](facebook::jsi::Runtime &) {}));
|
|
71
|
+
EXPECT_EQ(posted, 1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
TEST(an_expo_event_goes_through_the_same_door) {
|
|
75
|
+
// Which is the whole reason the door exists: an Expo module is handed no call
|
|
76
|
+
// invoker, so this is the only way it can emit.
|
|
77
|
+
//
|
|
78
|
+
// **Both builds, asked at runtime.** A build not pointed at an
|
|
79
|
+
// expo-modules-core has no Expo modules to emit on, and `emitExpoEvent` is
|
|
80
|
+
// then the documented no-op that answers false rather than a missing symbol.
|
|
81
|
+
// Asked through `hasExpoRuntime()` rather than `#ifdef BASALT_HAS_EXPO`
|
|
82
|
+
// because the define is added with `add_compile_definitions` in the core
|
|
83
|
+
// package's directory and a host's test target is a sibling, so the ifdef
|
|
84
|
+
// would read false in a suite where Expo is compiled in -- which is the wrong
|
|
85
|
+
// answer twice over. The first version of this test assumed the Expo build and
|
|
86
|
+
// passed on a machine with an app installed; CI, which has none, is where it
|
|
87
|
+
// failed.
|
|
88
|
+
RunnerFixture fixture;
|
|
89
|
+
basalt::setRuntimeRunner(nullptr);
|
|
90
|
+
EXPECT(!basalt::emitExpoEvent("KinoProcess", "onOutput", [](facebook::jsi::Runtime &,
|
|
91
|
+
std::vector<facebook::jsi::Value> &) {
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
int posted = 0;
|
|
95
|
+
basalt::setRuntimeRunner([&posted](std::function<void(facebook::jsi::Runtime &)>) { posted++; });
|
|
96
|
+
const bool emitted =
|
|
97
|
+
basalt::emitExpoEvent("KinoProcess", "onOutput", [](facebook::jsi::Runtime &,
|
|
98
|
+
std::vector<facebook::jsi::Value> &) {});
|
|
99
|
+
if (basalt::hasExpoRuntime()) {
|
|
100
|
+
EXPECT(emitted);
|
|
101
|
+
EXPECT_EQ(posted, 1);
|
|
102
|
+
} else {
|
|
103
|
+
EXPECT(!emitted);
|
|
104
|
+
EXPECT_EQ(posted, 0);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
TEST(the_call_invoker_over_the_runner_posts_and_says_it_cannot_wait) {
|
|
109
|
+
RunnerFixture fixture;
|
|
110
|
+
int posted = 0;
|
|
111
|
+
basalt::setRuntimeRunner([&posted](std::function<void(facebook::jsi::Runtime &)>) { posted++; });
|
|
112
|
+
|
|
113
|
+
const auto invoker = basalt::jsCallInvoker();
|
|
114
|
+
EXPECT(invoker != nullptr);
|
|
115
|
+
invoker->invokeAsync(facebook::react::CallFunc([](facebook::jsi::Runtime &) {}));
|
|
116
|
+
EXPECT_EQ(posted, 1);
|
|
117
|
+
|
|
118
|
+
// Rather than deadlocking, which is what a synchronous call from another
|
|
119
|
+
// thread into a busy runtime would do; see the header.
|
|
120
|
+
bool threw = false;
|
|
121
|
+
try {
|
|
122
|
+
invoker->invokeSync(facebook::react::CallFunc([](facebook::jsi::Runtime &) {}));
|
|
123
|
+
} catch (const std::logic_error &error) {
|
|
124
|
+
threw = true;
|
|
125
|
+
// Self-naming, so the message alone says which invoker and why.
|
|
126
|
+
EXPECT(std::string(error.what()).find("JsRuntimeAccess") != std::string::npos);
|
|
127
|
+
}
|
|
128
|
+
EXPECT(threw);
|
|
129
|
+
EXPECT_EQ(posted, 1);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
TEST(the_same_invoker_survives_a_reload) {
|
|
133
|
+
// It holds nothing but the call to runOnJsRuntime, so a module that captured
|
|
134
|
+
// it before a Fast Refresh reload is still holding something that works --
|
|
135
|
+
// which is why one shared instance is safe to hand out.
|
|
136
|
+
EXPECT(basalt::jsCallInvoker() == basalt::jsCallInvoker());
|
|
137
|
+
}
|