mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/51512 Pull Request resolved: https://github.com/facebook/react-native/pull/50585 # Changelog: [Internal] Replaces `DOMHighResTimeStamp` alias completely in `ReactCommon` with `HighResTimeStamp`. `DOMHighResTimeStamp` as a type is now expected to be used only in JavaScript. I didn't update places where we explcitly use `std::chrono::high_resolution_clock`, since it is platform-specific and there is no guarantee that `std::chrono::high_resolution_clock` == `std::chrono::steady_clock`. Also, places that are isolated and not part of the Web Performance APIs, such as Telemetry for Fabric, are not updates as part of this diff. Although these subsystems are also using `std::chrono::steady_clock` as a low-level representation, they are not sharing it with other parts of the React Native core. Reviewed By: rubennorte Differential Revision: D75185613 fbshipit-source-id: 889719368de163e6f529689df6cc16d816fde66c
799 lines
29 KiB
C++
799 lines
29 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include "BridgingTest.h"
|
|
|
|
namespace facebook::react {
|
|
|
|
using namespace std::literals;
|
|
|
|
TEST_F(BridgingTest, jsiTest) {
|
|
jsi::Value value = true;
|
|
jsi::Value string = jsi::String::createFromAscii(rt, "hello");
|
|
jsi::Value object = jsi::Object(rt);
|
|
jsi::Value array = jsi::Array::createWithElements(rt, value, object);
|
|
jsi::Value func = function("() => {}");
|
|
|
|
// The bridging mechanism needs to know how to copy and downcast values.
|
|
EXPECT_NO_THROW(bridging::fromJs<jsi::Value>(rt, value, invoker));
|
|
EXPECT_NO_THROW(bridging::fromJs<jsi::String>(rt, string, invoker));
|
|
EXPECT_NO_THROW(bridging::fromJs<jsi::Object>(rt, object, invoker));
|
|
EXPECT_NO_THROW(bridging::fromJs<jsi::Array>(rt, array, invoker));
|
|
EXPECT_NO_THROW(bridging::fromJs<jsi::Function>(rt, func, invoker));
|
|
|
|
// Should throw when attempting an invalid cast.
|
|
EXPECT_JSI_THROW(bridging::fromJs<jsi::Object>(rt, value, invoker));
|
|
EXPECT_JSI_THROW(bridging::fromJs<jsi::String>(rt, array, invoker));
|
|
EXPECT_JSI_THROW(bridging::fromJs<jsi::Array>(rt, object, invoker));
|
|
EXPECT_JSI_THROW(bridging::fromJs<jsi::Array>(rt, string, invoker));
|
|
EXPECT_JSI_THROW(bridging::fromJs<jsi::Array>(rt, func, invoker));
|
|
|
|
// Should be able to generically no-op convert JSI.
|
|
EXPECT_NO_THROW(bridging::toJs(rt, value, invoker));
|
|
EXPECT_NO_THROW(bridging::toJs(rt, string.asString(rt), invoker));
|
|
EXPECT_NO_THROW(bridging::toJs(rt, object.asObject(rt), invoker));
|
|
EXPECT_NO_THROW(bridging::toJs(rt, array.asObject(rt).asArray(rt), invoker));
|
|
EXPECT_NO_THROW(
|
|
bridging::toJs(rt, func.asObject(rt).asFunction(rt), invoker));
|
|
}
|
|
|
|
TEST_F(BridgingTest, boolTest) {
|
|
EXPECT_TRUE(bridging::fromJs<bool>(rt, jsi::Value(true), invoker));
|
|
EXPECT_FALSE(bridging::fromJs<bool>(rt, jsi::Value(false), invoker));
|
|
EXPECT_JSI_THROW(bridging::fromJs<bool>(rt, jsi::Value(1), invoker));
|
|
|
|
EXPECT_TRUE(bridging::toJs(rt, true));
|
|
EXPECT_FALSE(bridging::toJs(rt, false));
|
|
}
|
|
|
|
TEST_F(BridgingTest, numberTest) {
|
|
EXPECT_EQ(1, bridging::fromJs<int>(rt, jsi::Value(1), invoker));
|
|
EXPECT_FLOAT_EQ(1.2f, bridging::fromJs<float>(rt, jsi::Value(1.2), invoker));
|
|
EXPECT_DOUBLE_EQ(1.2, bridging::fromJs<double>(rt, jsi::Value(1.2), invoker));
|
|
EXPECT_JSI_THROW(bridging::fromJs<double>(rt, jsi::Value(true), invoker));
|
|
|
|
EXPECT_EQ(1, static_cast<int>(bridging::toJs(rt, 1)));
|
|
EXPECT_FLOAT_EQ(1.2f, static_cast<float>(bridging::toJs(rt, 1.2f)));
|
|
EXPECT_DOUBLE_EQ(1.2, bridging::toJs(rt, 1.2));
|
|
|
|
EXPECT_EQ(
|
|
42,
|
|
static_cast<uint32_t>(
|
|
bridging::toJs(rt, static_cast<uint32_t>(42)).asNumber()));
|
|
|
|
EXPECT_EQ(
|
|
-42,
|
|
static_cast<uint32_t>(
|
|
bridging::toJs(rt, static_cast<uint32_t>(-42)).asNumber()));
|
|
|
|
EXPECT_FALSE(
|
|
-42 ==
|
|
static_cast<int32_t>(
|
|
bridging::toJs(rt, static_cast<uint32_t>(-42)).asNumber()));
|
|
}
|
|
|
|
TEST_F(BridgingTest, stringTest) {
|
|
auto string = jsi::String::createFromAscii(rt, "hello");
|
|
|
|
EXPECT_EQ("hello"s, bridging::fromJs<std::string>(rt, string, invoker));
|
|
EXPECT_JSI_THROW(bridging::fromJs<std::string>(rt, jsi::Value(1), invoker));
|
|
|
|
EXPECT_TRUE(
|
|
jsi::String::strictEquals(rt, string, bridging::toJs(rt, "hello")));
|
|
EXPECT_TRUE(
|
|
jsi::String::strictEquals(rt, string, bridging::toJs(rt, "hello"s)));
|
|
EXPECT_TRUE(
|
|
jsi::String::strictEquals(rt, string, bridging::toJs(rt, "hello"sv)));
|
|
}
|
|
|
|
TEST_F(BridgingTest, objectTest) {
|
|
auto object = jsi::Object(rt);
|
|
object.setProperty(rt, "foo", "bar");
|
|
|
|
auto omap =
|
|
bridging::fromJs<std::map<std::string, std::string>>(rt, object, invoker);
|
|
auto umap = bridging::fromJs<std::unordered_map<std::string, std::string>>(
|
|
rt, object, invoker);
|
|
|
|
EXPECT_EQ(1, omap.size());
|
|
EXPECT_EQ(1, umap.size());
|
|
EXPECT_EQ("bar"s, omap["foo"]);
|
|
EXPECT_EQ("bar"s, umap["foo"]);
|
|
|
|
EXPECT_EQ(
|
|
"bar"s,
|
|
bridging::toJs(rt, omap, invoker)
|
|
.getProperty(rt, "foo")
|
|
.asString(rt)
|
|
.utf8(rt));
|
|
EXPECT_EQ(
|
|
"bar"s,
|
|
bridging::toJs(rt, umap, invoker)
|
|
.getProperty(rt, "foo")
|
|
.asString(rt)
|
|
.utf8(rt));
|
|
}
|
|
|
|
TEST_F(BridgingTest, hostObjectTest) {
|
|
struct TestHostObject : public jsi::HostObject {
|
|
jsi::Value get(jsi::Runtime& rt, const jsi::PropNameID& name) override {
|
|
if (name.utf8(rt) == "test") {
|
|
return {1};
|
|
}
|
|
return jsi::Value::undefined();
|
|
}
|
|
};
|
|
|
|
auto hostobject = std::make_shared<TestHostObject>();
|
|
auto object = bridging::toJs(rt, hostobject);
|
|
|
|
EXPECT_EQ(1, object.getProperty(rt, "test").asNumber());
|
|
EXPECT_EQ(
|
|
hostobject, bridging::fromJs<decltype(hostobject)>(rt, object, invoker));
|
|
}
|
|
|
|
TEST_F(BridgingTest, weakbjectTest) {
|
|
auto object = jsi::Object(rt);
|
|
auto weakobject = jsi::WeakObject(rt, object);
|
|
|
|
EXPECT_TRUE(jsi::Object::strictEquals(
|
|
rt,
|
|
object,
|
|
bridging::fromJs<jsi::WeakObject>(rt, object, invoker)
|
|
.lock(rt)
|
|
.asObject(rt)));
|
|
|
|
EXPECT_TRUE(jsi::Object::strictEquals(
|
|
rt, object, bridging::toJs(rt, weakobject).asObject(rt)));
|
|
}
|
|
|
|
TEST_F(BridgingTest, arrayTest) {
|
|
auto vec = std::vector({"foo"s, "bar"s});
|
|
auto array = jsi::Array::createWithElements(rt, "foo", "bar");
|
|
|
|
EXPECT_EQ(
|
|
vec, bridging::fromJs<std::vector<std::string>>(rt, array, invoker));
|
|
auto arr = bridging::fromJs<std::array<std::string, 2>>(rt, array, invoker);
|
|
EXPECT_EQ(vec[0], arr[0]);
|
|
EXPECT_EQ(vec[1], arr[1]);
|
|
auto pair =
|
|
bridging::fromJs<std::pair<std::string, std::string>>(rt, array, invoker);
|
|
EXPECT_EQ(vec[0], pair.first);
|
|
EXPECT_EQ(vec[1], pair.second);
|
|
|
|
EXPECT_EQ(vec.size(), bridging::toJs(rt, vec, invoker).size(rt));
|
|
for (size_t i = 0; i < vec.size(); i++) {
|
|
EXPECT_EQ(
|
|
vec[i],
|
|
bridging::toJs(rt, vec, invoker)
|
|
.getValueAtIndex(rt, i)
|
|
.asString(rt)
|
|
.utf8(rt));
|
|
}
|
|
|
|
EXPECT_EQ(2, bridging::toJs(rt, std::make_pair(1, "2"), invoker).size(rt));
|
|
EXPECT_EQ(2, bridging::toJs(rt, std::make_tuple(1, "2"), invoker).size(rt));
|
|
EXPECT_EQ(2, bridging::toJs(rt, std::array<int, 2>{1, 2}, invoker).size(rt));
|
|
EXPECT_EQ(
|
|
2,
|
|
bridging::toJs(rt, std::array<std::string, 2>{"1", "2"}, invoker)
|
|
.size(rt));
|
|
EXPECT_EQ(2, bridging::toJs(rt, std::deque<int>{1, 2}, invoker).size(rt));
|
|
EXPECT_EQ(2, bridging::toJs(rt, std::list<int>{1, 2}, invoker).size(rt));
|
|
EXPECT_EQ(
|
|
2,
|
|
bridging::toJs(rt, std::initializer_list<int>{1, 2}, invoker).size(rt));
|
|
|
|
std::vector<std::array<std::string, 2>> headers{
|
|
{"foo", "bar"}, {"baz", "qux"}};
|
|
auto jsiHeaders = bridging::toJs(rt, headers, invoker);
|
|
EXPECT_EQ(headers.size(), jsiHeaders.size(rt));
|
|
}
|
|
|
|
TEST_F(BridgingTest, functionTest) {
|
|
auto object = jsi::Object(rt);
|
|
object.setProperty(rt, "foo", "bar");
|
|
|
|
auto lambda = [](std::map<std::string, std::string> map, std::string key) {
|
|
return map[key];
|
|
};
|
|
|
|
auto func = bridging::toJs(rt, lambda, invoker);
|
|
|
|
EXPECT_EQ(
|
|
"bar"s,
|
|
func.call(rt, object, jsi::String::createFromAscii(rt, "foo"))
|
|
.asString(rt)
|
|
.utf8(rt));
|
|
|
|
// Should throw if not enough arguments are passed or are the wrong types.
|
|
EXPECT_JSI_THROW(func.call(rt, object));
|
|
EXPECT_JSI_THROW(func.call(rt, object, jsi::Value(1)));
|
|
|
|
// Test with non-capturing lambda converted to function pointer.
|
|
func = bridging::toJs(rt, +lambda, invoker);
|
|
|
|
EXPECT_EQ(
|
|
"bar"s,
|
|
func.call(rt, object, jsi::String::createFromAscii(rt, "foo"))
|
|
.asString(rt)
|
|
.utf8(rt));
|
|
}
|
|
|
|
TEST_F(BridgingTest, syncCallbackTest) {
|
|
auto fn = function("(a, b) => a + b");
|
|
auto cb = bridging::fromJs<SyncCallback<std::string(std::string, int)>>(
|
|
rt, fn, invoker);
|
|
auto foo = "foo"s;
|
|
|
|
EXPECT_EQ("foo1"s, cb(foo, 1)); // Tests lvalue string
|
|
EXPECT_EQ("bar2", cb("bar", 2)); // Tests rvalue C string
|
|
EXPECT_TRUE(fn.isFunction(rt)); // Ensure the function wasn't invalidated.
|
|
}
|
|
|
|
TEST_F(BridgingTest, syncCallbackImplicitBridgingTest) {
|
|
{ // Value
|
|
auto fn = function("(a, b) => a + b");
|
|
auto cb = bridging::fromJs<SyncCallback<std::string(jsi::Value, int)>>(
|
|
rt, fn, invoker);
|
|
jsi::Value foo(jsi::String::createFromAscii(rt, "foo"));
|
|
|
|
EXPECT_EQ(cb(std::move(foo), 1), "foo1");
|
|
EXPECT_EQ(cb(jsi::String::createFromAscii(rt, "bar"), 2), "bar2");
|
|
EXPECT_TRUE(fn.isFunction(rt));
|
|
}
|
|
{ // Object
|
|
auto fn = function("(a, b) => a.obj + b");
|
|
auto cb = bridging::fromJs<SyncCallback<std::string(jsi::Object, int)>>(
|
|
rt, fn, invoker);
|
|
|
|
jsi::Object foo(rt);
|
|
foo.setProperty(rt, "obj", "foo");
|
|
|
|
EXPECT_EQ(cb(std::move(foo), 1), "foo1");
|
|
EXPECT_TRUE(fn.isFunction(rt));
|
|
}
|
|
{ // String
|
|
auto fn = function("(a, b) => a + b");
|
|
auto cb = bridging::fromJs<SyncCallback<std::string(jsi::String, int)>>(
|
|
rt, fn, invoker);
|
|
jsi::String foo(jsi::String::createFromAscii(rt, "foo"));
|
|
|
|
EXPECT_EQ(cb(std::move(foo), 1), "foo1");
|
|
EXPECT_EQ(cb(jsi::String::createFromAscii(rt, "bar"), 2), "bar2");
|
|
EXPECT_TRUE(fn.isFunction(rt));
|
|
}
|
|
{ // Array
|
|
auto fn = function("(a, b) => a[0] + b");
|
|
auto cb = bridging::fromJs<SyncCallback<std::string(jsi::Array, int)>>(
|
|
rt, fn, invoker);
|
|
|
|
jsi::Array foo(rt, 1);
|
|
foo.setValueAtIndex(rt, 0, jsi::String::createFromAscii(rt, "foo"));
|
|
|
|
EXPECT_EQ(cb(std::move(foo), 1), "foo1");
|
|
EXPECT_TRUE(fn.isFunction(rt));
|
|
}
|
|
}
|
|
|
|
TEST_F(BridgingTest, asyncCallbackTest) {
|
|
std::string output;
|
|
|
|
auto func = std::function<void(std::string)>([&](auto str) { output = str; });
|
|
|
|
auto cb = bridging::fromJs<AsyncCallback<decltype(func), std::string>>(
|
|
rt, function("(func, str) => func(str)"), invoker);
|
|
|
|
cb(func, "hello");
|
|
|
|
flushQueue(); // Run scheduled async work
|
|
EXPECT_EQ("hello"s, output);
|
|
|
|
// Test with lambda invocation
|
|
cb.call([func, jsInvoker = invoker](jsi::Runtime& rt, jsi::Function& f) {
|
|
f.call(
|
|
rt,
|
|
bridging::toJs(rt, func, jsInvoker),
|
|
bridging::toJs(rt, "hello again", jsInvoker));
|
|
});
|
|
|
|
flushQueue();
|
|
EXPECT_EQ("hello again"s, output);
|
|
}
|
|
|
|
TEST_F(BridgingTest, asyncCallbackInvalidation) {
|
|
std::string output;
|
|
std::function<void(std::string)> func = [&](auto str) { output = str; };
|
|
|
|
auto jsCallback = bridging::fromJs<AsyncCallback<>>(
|
|
rt, bridging::toJs(rt, func, invoker), invoker);
|
|
jsCallback.call(
|
|
[](jsi::Runtime& rt, jsi::Function& f) { f.call(rt, "hello"); });
|
|
|
|
// LongLivedObjectCollection goes away before callback is executed
|
|
LongLivedObjectCollection::get(rt).clear();
|
|
|
|
flushQueue();
|
|
|
|
// Assert native callback is never invoked
|
|
ASSERT_EQ(""s, output);
|
|
}
|
|
|
|
TEST_F(BridgingTest, asyncCallbackImplicitBridgingTest) {
|
|
std::string output;
|
|
auto func = std::function<void(std::string)>([&](auto str) { output = str; });
|
|
{ // Value
|
|
auto cb = bridging::fromJs<AsyncCallback<decltype(func), jsi::Value, int>>(
|
|
rt, function("(func, a, b) => func(a + b)"), invoker);
|
|
jsi::Value foo(jsi::String::createFromAscii(rt, "foo"));
|
|
|
|
cb(func, std::move(foo), 1);
|
|
flushQueue();
|
|
EXPECT_EQ(output, "foo1");
|
|
|
|
cb(func, jsi::String::createFromAscii(rt, "bar"), 2);
|
|
flushQueue();
|
|
EXPECT_EQ(output, "bar2");
|
|
|
|
output.clear();
|
|
}
|
|
{ // Object
|
|
auto cb = bridging::fromJs<AsyncCallback<decltype(func), jsi::Object, int>>(
|
|
rt, function("(func, a, b) => func(a.obj + b)"), invoker);
|
|
|
|
jsi::Object foo(rt);
|
|
foo.setProperty(rt, "obj", "foo");
|
|
|
|
cb(func, std::move(foo), 1);
|
|
flushQueue();
|
|
EXPECT_EQ(output, "foo1");
|
|
|
|
output.clear();
|
|
}
|
|
{ // String
|
|
auto cb = bridging::fromJs<AsyncCallback<decltype(func), jsi::String, int>>(
|
|
rt, function("(func, a, b) => func(a + b)"), invoker);
|
|
jsi::String foo(jsi::String::createFromAscii(rt, "foo"));
|
|
|
|
cb(func, std::move(foo), 1);
|
|
flushQueue();
|
|
EXPECT_EQ(output, "foo1");
|
|
|
|
cb(func, jsi::String::createFromAscii(rt, "bar"), 2);
|
|
flushQueue();
|
|
EXPECT_EQ(output, "bar2");
|
|
|
|
output.clear();
|
|
}
|
|
{ // Array
|
|
auto fn = function("(func, a, b) => func(a[0] + b)");
|
|
auto cb = bridging::fromJs<AsyncCallback<decltype(func), jsi::Array, int>>(
|
|
rt, fn, invoker);
|
|
|
|
jsi::Array foo(rt, 1);
|
|
foo.setValueAtIndex(rt, 0, jsi::String::createFromAscii(rt, "foo"));
|
|
|
|
cb(func, std::move(foo), 1);
|
|
flushQueue();
|
|
EXPECT_EQ(output, "foo1");
|
|
|
|
output.clear();
|
|
}
|
|
}
|
|
|
|
TEST_F(BridgingTest, promiseTest) {
|
|
auto func = function(
|
|
"(promise, obj) => {"
|
|
" promise.then("
|
|
" (res) => { obj.res = res; },"
|
|
" (err) => { obj.err = err; }"
|
|
" )"
|
|
"}");
|
|
|
|
auto promise = AsyncPromise<std::vector<std::string>>(rt, invoker);
|
|
auto output = jsi::Object(rt);
|
|
|
|
func.call(rt, bridging::toJs(rt, promise, invoker), output);
|
|
promise.resolve({"foo"s, "bar"s});
|
|
flushQueue();
|
|
|
|
EXPECT_EQ(1, output.getPropertyNames(rt).size(rt));
|
|
EXPECT_EQ(2, output.getProperty(rt, "res").asObject(rt).asArray(rt).size(rt));
|
|
EXPECT_NO_THROW(promise.resolve({"ignored"}));
|
|
EXPECT_NO_THROW(promise.reject("ignored"));
|
|
|
|
promise = AsyncPromise<std::vector<std::string>>(rt, invoker);
|
|
output = jsi::Object(rt);
|
|
|
|
func.call(rt, bridging::toJs(rt, promise, invoker), output);
|
|
promise.reject("fail");
|
|
flushQueue();
|
|
|
|
EXPECT_EQ(1, output.getPropertyNames(rt).size(rt));
|
|
EXPECT_EQ(
|
|
"fail"s,
|
|
output.getProperty(rt, "err")
|
|
.asObject(rt)
|
|
.getProperty(rt, "message")
|
|
.asString(rt)
|
|
.utf8(rt));
|
|
EXPECT_NO_THROW(promise.resolve({"ignored"}));
|
|
EXPECT_NO_THROW(promise.reject("ignored"));
|
|
}
|
|
|
|
using EventType = std::vector<std::string>;
|
|
using EventSubscriptionsWithLastEvent =
|
|
std::vector<std::pair<jsi::Object, std::shared_ptr<EventType>>>;
|
|
|
|
namespace {
|
|
|
|
template <typename EventType>
|
|
void addEventSubscription(
|
|
jsi::Runtime& rt,
|
|
const AsyncEventEmitter<EventType>& eventEmitter,
|
|
EventSubscriptionsWithLastEvent& eventSubscriptionsWithListener,
|
|
const std::shared_ptr<TestCallInvoker>& invoker) {
|
|
auto eventEmitterJs = bridging::toJs(rt, eventEmitter, invoker);
|
|
auto lastEvent = std::make_shared<EventType>();
|
|
auto listenJs = bridging::toJs(
|
|
rt,
|
|
[lastEvent = lastEvent](const EventType& event) { *lastEvent = event; },
|
|
invoker);
|
|
eventSubscriptionsWithListener.emplace_back(std::make_pair(
|
|
jsi::Object(eventEmitterJs.asFunction(rt)
|
|
.callWithThis(rt, eventEmitterJs, listenJs)
|
|
.asObject(rt)),
|
|
std::move(lastEvent)));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_F(BridgingTest, eventEmitterTest) {
|
|
EventSubscriptionsWithLastEvent eventSubscriptionsWithListener;
|
|
|
|
AsyncEventEmitter<EventType> eventEmitter;
|
|
EXPECT_NO_THROW(eventEmitter.emit({"one", "two", "three"}));
|
|
EXPECT_EQ(0, eventSubscriptionsWithListener.size());
|
|
|
|
// register 3 JavaScript listeners to the event emitter
|
|
for (int i = 0; i < 3; ++i) {
|
|
addEventSubscription<EventType>(
|
|
rt, eventEmitter, eventSubscriptionsWithListener, invoker);
|
|
}
|
|
|
|
EXPECT_TRUE(eventEmitter.state_->listeners.contains(0));
|
|
EXPECT_TRUE(eventEmitter.state_->listeners.contains(1));
|
|
EXPECT_TRUE(eventEmitter.state_->listeners.contains(2));
|
|
|
|
// emit with args
|
|
EXPECT_NO_THROW(eventEmitter.emit({"four", "five", "six"}));
|
|
flushQueue();
|
|
|
|
// verify all listeners received the event
|
|
for (const auto& [_, lastEvent] : eventSubscriptionsWithListener) {
|
|
EXPECT_EQ(3, lastEvent->size());
|
|
EXPECT_EQ("four", lastEvent->at(0));
|
|
EXPECT_EQ("five", lastEvent->at(1));
|
|
EXPECT_EQ("six", lastEvent->at(2));
|
|
}
|
|
|
|
// Remove 2nd eventSubscriptions
|
|
eventSubscriptionsWithListener[1]
|
|
.first.getPropertyAsFunction(rt, "remove")
|
|
.callWithThis(rt, eventSubscriptionsWithListener[1].first);
|
|
eventSubscriptionsWithListener.erase(
|
|
eventSubscriptionsWithListener.begin() + 1);
|
|
|
|
// Add 4th and 5th eventSubscriptions
|
|
addEventSubscription<EventType>(
|
|
rt, eventEmitter, eventSubscriptionsWithListener, invoker);
|
|
addEventSubscription<EventType>(
|
|
rt, eventEmitter, eventSubscriptionsWithListener, invoker);
|
|
|
|
EXPECT_TRUE(eventEmitter.state_->listeners.contains(0));
|
|
EXPECT_FALSE(eventEmitter.state_->listeners.contains(1));
|
|
EXPECT_TRUE(eventEmitter.state_->listeners.contains(2));
|
|
EXPECT_TRUE(eventEmitter.state_->listeners.contains(3));
|
|
EXPECT_TRUE(eventEmitter.state_->listeners.contains(4));
|
|
|
|
// Emit more events
|
|
EXPECT_NO_THROW(eventEmitter.emit({"seven", "eight", "nine"}));
|
|
flushQueue();
|
|
|
|
for (const auto& [_, lastEvent] : eventSubscriptionsWithListener) {
|
|
EXPECT_EQ(3, lastEvent->size());
|
|
EXPECT_EQ("seven", lastEvent->at(0));
|
|
EXPECT_EQ("eight", lastEvent->at(1));
|
|
EXPECT_EQ("nine", lastEvent->at(2));
|
|
}
|
|
|
|
// clean-up the event subscriptions
|
|
for (const auto& [eventSubscription, _] : eventSubscriptionsWithListener) {
|
|
eventSubscription.getPropertyAsFunction(rt, "remove")
|
|
.callWithThis(rt, eventSubscription);
|
|
}
|
|
flushQueue();
|
|
|
|
// Emit with function
|
|
EXPECT_NO_THROW(eventEmitter.emit(
|
|
[jsInvoker = invoker,
|
|
value = {"ten", "eleven", "twelve"}](jsi::Runtime& rt) -> jsi::Value {
|
|
return bridging::toJs(rt, value, jsInvoker);
|
|
}));
|
|
flushQueue();
|
|
|
|
// no new data as listeners had been removed
|
|
for (const auto& [_, lastEvent] : eventSubscriptionsWithListener) {
|
|
EXPECT_EQ(3, lastEvent->size());
|
|
EXPECT_EQ("seven", lastEvent->at(0));
|
|
EXPECT_EQ("eight", lastEvent->at(1));
|
|
EXPECT_EQ("nine", lastEvent->at(2));
|
|
}
|
|
}
|
|
|
|
TEST_F(BridgingTest, optionalTest) {
|
|
EXPECT_EQ(
|
|
1, bridging::fromJs<std::optional<int>>(rt, jsi::Value(1), invoker));
|
|
EXPECT_EQ(
|
|
1,
|
|
bridging::fromJs<std::optional<int>>(
|
|
rt, std::make_optional(jsi::Value(1)), invoker));
|
|
EXPECT_EQ(
|
|
"hi"s,
|
|
bridging::fromJs<std::optional<std::string>>(
|
|
rt,
|
|
std::make_optional(jsi::String::createFromAscii(rt, "hi")),
|
|
invoker));
|
|
EXPECT_FALSE(
|
|
bridging::fromJs<std::optional<int>>(rt, jsi::Value::undefined(), invoker)
|
|
.has_value());
|
|
EXPECT_FALSE(
|
|
bridging::fromJs<std::optional<int>>(rt, jsi::Value::null(), invoker)
|
|
.has_value());
|
|
|
|
EXPECT_TRUE(bridging::toJs(rt, std::optional<int>(), invoker).isNull());
|
|
EXPECT_EQ(1, bridging::toJs(rt, std::optional<int>(1), invoker).asNumber());
|
|
}
|
|
|
|
TEST_F(BridgingTest, pointerTest) {
|
|
auto str = "hi"s;
|
|
auto unique = std::make_unique<std::string>(str);
|
|
auto shared = std::make_shared<std::string>(str);
|
|
auto weak = std::weak_ptr<std::string>(shared);
|
|
|
|
EXPECT_EQ(str, bridging::toJs(rt, unique, invoker).asString(rt).utf8(rt));
|
|
EXPECT_EQ(str, bridging::toJs(rt, shared, invoker).asString(rt).utf8(rt));
|
|
EXPECT_EQ(str, bridging::toJs(rt, weak, invoker).asString(rt).utf8(rt));
|
|
|
|
shared.reset();
|
|
|
|
EXPECT_TRUE(bridging::toJs(rt, weak, invoker).isNull());
|
|
}
|
|
|
|
TEST_F(BridgingTest, supportTest) {
|
|
// Ensure sure can convert some basic types, including primitives that can be
|
|
// trivially converted to JSI values.
|
|
EXPECT_TRUE((bridging::supportsFromJs<bool>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<bool, bool>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<bool, jsi::Value&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<int>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<int, int>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<int, jsi::Value&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<double>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<double, double>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<double, jsi::Value&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<std::string>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<std::string, jsi::String>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<std::string, jsi::String&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<std::set<int>, jsi::Array>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<std::set<int>, jsi::Array&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<std::vector<int>, jsi::Array>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<std::vector<int>, jsi::Array&>));
|
|
EXPECT_TRUE((
|
|
bridging::
|
|
supportsFromJs<std::vector<std::array<std::string, 2>>, jsi::Array>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<
|
|
std::vector<std::array<std::string, 2>>,
|
|
jsi::Array&>));
|
|
EXPECT_TRUE(
|
|
(bridging::supportsFromJs<std::map<std::string, int>, jsi::Object>));
|
|
EXPECT_TRUE(
|
|
(bridging::supportsFromJs<std::map<std::string, int>, jsi::Object&>));
|
|
|
|
// Ensure incompatible conversions will fail.
|
|
EXPECT_FALSE((bridging::supportsFromJs<bool, jsi::String>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<bool, jsi::String&>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<int, jsi::String>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<int, jsi::String&>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<double, jsi::String>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<double, jsi::String&>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<bool, jsi::Object>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<bool, jsi::Object&>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<int, jsi::Object>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<int, jsi::Object&>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<double, jsi::Object>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<double, jsi::Object&>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<std::string, jsi::Object>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<std::string, jsi::Object&>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<std::set<int>, jsi::String>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<std::set<int>, jsi::String&>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<std::vector<int>, jsi::String>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<std::vector<int>, jsi::String&>));
|
|
|
|
// Ensure copying and down casting JSI values is also supported.
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Value>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Value, jsi::Value&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::String>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::String, jsi::String>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::String, jsi::String&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object, jsi::Object>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object, jsi::Object&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object, jsi::Array>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object, jsi::Array&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object, jsi::Function>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Object, jsi::Function&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Array>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Array, jsi::Array>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Array, jsi::Array&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Array, jsi::Object>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Array, jsi::Object&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Function>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Function, jsi::Function>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Function, jsi::Function&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Function, jsi::Object>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<jsi::Function, jsi::Object&>));
|
|
|
|
// Ensure incorrect casts will fail.
|
|
EXPECT_FALSE((bridging::supportsFromJs<jsi::Array, jsi::Function>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<jsi::Array, jsi::Function&>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<jsi::Function, jsi::Array>));
|
|
EXPECT_FALSE((bridging::supportsFromJs<jsi::Function, jsi::Array&>));
|
|
|
|
// Ensure we can create HighResTimeStamp and HighResDuration from JSI
|
|
// values.
|
|
EXPECT_TRUE((bridging::supportsFromJs<HighResTimeStamp, jsi::Value>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<HighResTimeStamp, jsi::Value&>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<HighResDuration, jsi::Value>));
|
|
EXPECT_TRUE((bridging::supportsFromJs<HighResDuration, jsi::Value&>));
|
|
|
|
// Ensure we can convert some basic types to JSI values.
|
|
EXPECT_TRUE((bridging::supportsToJs<bool>));
|
|
EXPECT_TRUE((bridging::supportsToJs<int>));
|
|
EXPECT_TRUE((bridging::supportsToJs<double>));
|
|
EXPECT_TRUE((bridging::supportsToJs<std::string>));
|
|
EXPECT_TRUE((bridging::supportsToJs<std::string, jsi::String>));
|
|
EXPECT_TRUE((bridging::supportsToJs<std::set<int>>));
|
|
EXPECT_TRUE((bridging::supportsToJs<std::set<int>, jsi::Array>));
|
|
EXPECT_TRUE((bridging::supportsToJs<std::vector<int>>));
|
|
EXPECT_TRUE((bridging::supportsToJs<std::vector<int>, jsi::Array>));
|
|
EXPECT_TRUE((bridging::supportsToJs<std::map<std::string, int>>));
|
|
EXPECT_TRUE(
|
|
(bridging::supportsToJs<std::map<std::string, int>, jsi::Object>));
|
|
EXPECT_TRUE((bridging::supportsToJs<void (*)()>));
|
|
EXPECT_TRUE((bridging::supportsToJs<void (*)(), jsi::Function>));
|
|
|
|
// Ensure invalid conversions to JSI values are not supported.
|
|
EXPECT_FALSE((bridging::supportsToJs<void*>));
|
|
EXPECT_FALSE((bridging::supportsToJs<bool, jsi::Object>));
|
|
EXPECT_FALSE((bridging::supportsToJs<int, jsi::Object>));
|
|
EXPECT_FALSE((bridging::supportsToJs<double, jsi::Object>));
|
|
EXPECT_FALSE((bridging::supportsToJs<std::string, jsi::Object>));
|
|
EXPECT_FALSE((bridging::supportsToJs<std::vector<int>, jsi::Function>));
|
|
|
|
// Ensure we can convert HighResTimeStamp and HighResDuration to
|
|
// DOMHighResTimeStamp (double).
|
|
EXPECT_TRUE((bridging::supportsToJs<HighResTimeStamp, double>));
|
|
EXPECT_TRUE((bridging::supportsToJs<HighResDuration, double>));
|
|
}
|
|
|
|
TEST_F(BridgingTest, dynamicTest) {
|
|
// Null
|
|
auto nullFromJsResult =
|
|
bridging::fromJs<folly::dynamic>(rt, jsi::Value::null(), invoker);
|
|
EXPECT_TRUE(nullFromJsResult.isNull());
|
|
|
|
auto nullToJsResult = bridging::toJs<folly::dynamic>(rt, nullptr, invoker);
|
|
EXPECT_TRUE(nullToJsResult.isNull());
|
|
|
|
// Boolean
|
|
auto booleanFromJsResult =
|
|
bridging::fromJs<folly::dynamic>(rt, jsi::Value(true), invoker);
|
|
EXPECT_TRUE(booleanFromJsResult.isBool());
|
|
EXPECT_TRUE(booleanFromJsResult.asBool());
|
|
|
|
auto booleanToJsResult = bridging::toJs<folly::dynamic>(rt, true, invoker);
|
|
EXPECT_TRUE(booleanToJsResult.isBool());
|
|
EXPECT_TRUE(booleanToJsResult.asBool());
|
|
|
|
// Number
|
|
auto numberFromJsResult =
|
|
bridging::fromJs<folly::dynamic>(rt, jsi::Value(1.2), invoker);
|
|
EXPECT_TRUE(numberFromJsResult.isNumber());
|
|
EXPECT_DOUBLE_EQ(1.2, numberFromJsResult.asDouble());
|
|
|
|
auto numberToJsResult = bridging::toJs<folly::dynamic>(rt, 1.2, invoker);
|
|
EXPECT_TRUE(numberToJsResult.isNumber());
|
|
EXPECT_DOUBLE_EQ(1.2, numberToJsResult.asNumber());
|
|
|
|
// String
|
|
auto stringFromJsResult = bridging::fromJs<folly::dynamic>(
|
|
rt, jsi::Value(jsi::String::createFromAscii(rt, "hello")), invoker);
|
|
EXPECT_TRUE(stringFromJsResult.isString());
|
|
EXPECT_EQ("hello"s, stringFromJsResult.asString());
|
|
|
|
auto stringToJsResult = bridging::toJs<folly::dynamic>(rt, "hello", invoker);
|
|
EXPECT_TRUE(stringToJsResult.isString());
|
|
EXPECT_EQ("hello"s, stringToJsResult.asString(rt).utf8(rt));
|
|
|
|
// Array
|
|
auto arrayFromJsResult = bridging::fromJs<folly::dynamic>(
|
|
rt,
|
|
jsi::Value(jsi::Array::createWithElements(rt, "foo", "bar")),
|
|
invoker);
|
|
EXPECT_TRUE(arrayFromJsResult.isArray());
|
|
EXPECT_EQ(2, arrayFromJsResult.size());
|
|
EXPECT_EQ("foo"s, arrayFromJsResult[0].asString());
|
|
EXPECT_EQ("bar"s, arrayFromJsResult[1].asString());
|
|
|
|
auto arrayToJsResult = bridging::toJs<folly::dynamic>(
|
|
rt, folly::dynamic::array("foo", "bar"), invoker);
|
|
EXPECT_TRUE(arrayToJsResult.isObject());
|
|
EXPECT_TRUE(arrayToJsResult.asObject(rt).isArray(rt));
|
|
auto arrayToJsResultArray = arrayToJsResult.asObject(rt).asArray(rt);
|
|
EXPECT_EQ(2, arrayToJsResultArray.size(rt));
|
|
EXPECT_EQ(
|
|
"foo"s,
|
|
arrayToJsResultArray.getValueAtIndex(rt, 0).asString(rt).utf8(rt));
|
|
EXPECT_EQ(
|
|
"bar"s,
|
|
arrayToJsResultArray.getValueAtIndex(rt, 1).asString(rt).utf8(rt));
|
|
|
|
// Object
|
|
auto jsiObject = jsi::Object(rt);
|
|
jsiObject.setProperty(rt, "foo", "bar");
|
|
auto objectFromJsResult = bridging::fromJs<folly::dynamic>(
|
|
rt, jsi::Value(std::move(jsiObject)), invoker);
|
|
|
|
EXPECT_TRUE(objectFromJsResult.isObject());
|
|
EXPECT_EQ(1, objectFromJsResult.size());
|
|
EXPECT_EQ("bar"s, objectFromJsResult["foo"].asString());
|
|
|
|
auto objectToJsResult = bridging::toJs<folly::dynamic>(
|
|
rt, folly::dynamic::object("foo", "bar"), invoker);
|
|
EXPECT_TRUE(objectToJsResult.isObject());
|
|
auto objectToJsResultObject = objectToJsResult.asObject(rt);
|
|
EXPECT_EQ(
|
|
"bar"s,
|
|
objectToJsResultObject.getProperty(rt, "foo").asString(rt).utf8(rt));
|
|
|
|
// Undefined
|
|
auto undefinedFromJsResult =
|
|
bridging::fromJs<folly::dynamic>(rt, jsi::Value::undefined(), invoker);
|
|
EXPECT_TRUE(undefinedFromJsResult.isNull());
|
|
}
|
|
|
|
TEST_F(BridgingTest, highResTimeStampTest) {
|
|
HighResTimeStamp timestamp = HighResTimeStamp::now();
|
|
EXPECT_EQ(
|
|
timestamp,
|
|
bridging::fromJs<HighResTimeStamp>(
|
|
rt, bridging::toJs(rt, timestamp), invoker));
|
|
|
|
auto duration = HighResDuration::fromNanoseconds(1);
|
|
EXPECT_EQ(
|
|
duration,
|
|
bridging::fromJs<HighResDuration>(
|
|
rt, bridging::toJs(rt, duration), invoker));
|
|
|
|
EXPECT_EQ(1.0, bridging::toJs(rt, HighResDuration::fromNanoseconds(1e6)));
|
|
EXPECT_EQ(
|
|
1.000001, bridging::toJs(rt, HighResDuration::fromNanoseconds(1e6 + 1)));
|
|
}
|
|
|
|
} // namespace facebook::react
|