未验证 提交 131cc625 编写于 作者: C Chinmay Garde 提交者: GitHub

Allow native entrypoint registration for runtime unittests. (#8379)

上级 5e124ffc
......@@ -26,12 +26,12 @@ source_set("test_font") {
# Picks the libdart implementation based on the Flutter runtime mode.
group("libdart") {
deps = []
public_deps = []
if (flutter_runtime_mode == "profile" || flutter_runtime_mode == "release") {
deps += [ "//third_party/dart/runtime:libdart_precompiled_runtime" ]
public_deps += [ "//third_party/dart/runtime:libdart_precompiled_runtime" ]
} else {
deps += [
public_deps += [
"$flutter_root/lib/snapshot",
"//third_party/dart/runtime:libdart_jit",
]
......@@ -115,7 +115,7 @@ executable("runtime_unittests") {
"$flutter_root/common",
"$flutter_root/fml",
"$flutter_root/lib/snapshot",
"$flutter_root/testing",
"$flutter_root/testing:dart",
"//third_party/skia",
"//third_party/tonic",
]
......
......@@ -419,7 +419,7 @@ static bool InvokeMainEntrypoint(Dart_Handle user_entrypoint_function) {
}
FML_WARN_UNUSED_RESULT
bool DartIsolate::Run(const std::string& entrypoint_name) {
bool DartIsolate::Run(const std::string& entrypoint_name, fml::closure on_run) {
TRACE_EVENT0("flutter", "DartIsolate::Run");
if (phase_ != Phase::Ready) {
return false;
......@@ -436,12 +436,17 @@ bool DartIsolate::Run(const std::string& entrypoint_name) {
phase_ = Phase::Running;
FML_DLOG(INFO) << "New isolate is in the running state.";
if (on_run) {
on_run();
}
return true;
}
FML_WARN_UNUSED_RESULT
bool DartIsolate::RunFromLibrary(const std::string& library_name,
const std::string& entrypoint_name) {
const std::string& entrypoint_name,
fml::closure on_run) {
TRACE_EVENT0("flutter", "DartIsolate::RunFromLibrary");
if (phase_ != Phase::Ready) {
return false;
......@@ -459,6 +464,10 @@ bool DartIsolate::RunFromLibrary(const std::string& library_name,
phase_ = Phase::Running;
FML_DLOG(INFO) << "New isolate is in the running state.";
if (on_run) {
on_run();
}
return true;
}
......
......@@ -74,11 +74,12 @@ class DartIsolate : public UIDartState {
bool last_piece = true);
FML_WARN_UNUSED_RESULT
bool Run(const std::string& entrypoint);
bool Run(const std::string& entrypoint, fml::closure on_run = nullptr);
FML_WARN_UNUSED_RESULT
bool RunFromLibrary(const std::string& library_name,
const std::string& entrypoint);
const std::string& entrypoint,
fml::closure on_run = nullptr);
FML_WARN_UNUSED_RESULT
bool Shutdown();
......
......@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/mapping.h"
#include "flutter/fml/paths.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/fml/thread.h"
#include "flutter/runtime/dart_isolate.h"
#include "flutter/runtime/dart_vm.h"
......@@ -23,10 +25,7 @@ namespace testing {
using DartIsolateTest = RuntimeTest;
TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) {
Settings settings = {};
SetSnapshotsAndAssets(settings);
settings.task_observer_add = [](intptr_t, fml::closure) {};
settings.task_observer_remove = [](intptr_t) {};
auto settings = CreateSettingsForFixture();
auto vm = DartVM::ForProcess(settings);
ASSERT_TRUE(vm);
TaskRunners task_runners(CURRENT_TEST_NAME, //
......@@ -53,9 +52,7 @@ TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) {
}
TEST_F(DartIsolateTest, IsolateShutdownCallbackIsInIsolateScope) {
Settings settings = {};
settings.task_observer_add = [](intptr_t, fml::closure) {};
settings.task_observer_remove = [](intptr_t) {};
auto settings = CreateSettingsForFixture();
auto vm = DartVM::ForProcess(settings);
ASSERT_TRUE(vm);
TaskRunners task_runners(CURRENT_TEST_NAME, //
......@@ -89,30 +86,48 @@ TEST_F(DartIsolateTest, IsolateShutdownCallbackIsInIsolateScope) {
class AutoIsolateShutdown {
public:
AutoIsolateShutdown(std::shared_ptr<blink::DartIsolate> isolate)
: isolate_(std::move(isolate)) {}
AutoIsolateShutdown() = default;
AutoIsolateShutdown(std::shared_ptr<blink::DartIsolate> isolate,
fml::RefPtr<fml::TaskRunner> runner)
: isolate_(std::move(isolate)), runner_(std::move(runner)) {}
~AutoIsolateShutdown() {
if (isolate_) {
if (!IsValid()) {
return;
}
fml::AutoResetWaitableEvent latch;
fml::TaskRunner::RunNowOrPostTask(runner_, [isolate = isolate_, &latch]() {
FML_LOG(INFO) << "Shutting down isolate.";
if (!isolate_->Shutdown()) {
if (!isolate->Shutdown()) {
FML_LOG(ERROR) << "Could not shutdown isolate.";
FML_CHECK(false);
}
}
latch.Signal();
});
latch.Wait();
}
bool IsValid() const { return isolate_ != nullptr; }
bool IsValid() const { return isolate_ != nullptr && runner_; }
FML_WARN_UNUSED_RESULT
bool RunInIsolateScope(std::function<bool(void)> closure) {
if (!isolate_) {
if (!IsValid()) {
return false;
}
tonic::DartIsolateScope scope(isolate_->isolate());
tonic::DartApiScope api_scope;
if (closure) {
return closure();
}
bool result = false;
fml::AutoResetWaitableEvent latch;
fml::TaskRunner::RunNowOrPostTask(
runner_, [this, &result, &latch, closure]() {
tonic::DartIsolateScope scope(isolate_->isolate());
tonic::DartApiScope api_scope;
if (closure) {
result = closure();
}
latch.Signal();
});
latch.Wait();
return true;
}
......@@ -123,21 +138,20 @@ class AutoIsolateShutdown {
private:
std::shared_ptr<blink::DartIsolate> isolate_;
fml::RefPtr<fml::TaskRunner> runner_;
FML_DISALLOW_COPY_AND_ASSIGN(AutoIsolateShutdown);
};
static std::unique_ptr<AutoIsolateShutdown> RunDartCodeInIsolate(
fml::RefPtr<fml::TaskRunner> task_runner,
std::string entrypoint) {
Settings settings = {};
settings.task_observer_add = [](intptr_t, fml::closure) {};
settings.task_observer_remove = [](intptr_t) {};
static void RunDartCodeInIsolate(std::unique_ptr<AutoIsolateShutdown>& result,
const Settings& settings,
fml::RefPtr<fml::TaskRunner> task_runner,
std::string entrypoint) {
FML_CHECK(task_runner->RunsTasksOnCurrentThread());
auto vm = DartVM::ForProcess(settings);
if (!vm) {
return {};
return;
}
TaskRunners task_runners(CURRENT_TEST_NAME, //
task_runner, //
......@@ -159,16 +173,16 @@ static std::unique_ptr<AutoIsolateShutdown> RunDartCodeInIsolate(
);
auto root_isolate =
std::make_unique<AutoIsolateShutdown>(weak_isolate.lock());
std::make_unique<AutoIsolateShutdown>(weak_isolate.lock(), task_runner);
if (!root_isolate->IsValid()) {
FML_LOG(ERROR) << "Could not create isolate.";
return {};
return;
}
if (root_isolate->get()->GetPhase() != DartIsolate::Phase::LibrariesSetup) {
FML_LOG(ERROR) << "Created isolate is in unexpected phase.";
return {};
return;
}
if (!DartVM::IsRunningPrecompiledCode()) {
......@@ -177,7 +191,7 @@ static std::unique_ptr<AutoIsolateShutdown> RunDartCodeInIsolate(
if (!fml::IsFile(kernel_file_path)) {
FML_LOG(ERROR) << "Could not locate kernel file.";
return {};
return;
}
auto kernel_file = fml::OpenFile(kernel_file_path.c_str(), false,
......@@ -185,61 +199,82 @@ static std::unique_ptr<AutoIsolateShutdown> RunDartCodeInIsolate(
if (!kernel_file.is_valid()) {
FML_LOG(ERROR) << "Kernel file descriptor was invalid.";
return {};
return;
}
auto kernel_mapping = std::make_unique<fml::FileMapping>(kernel_file);
if (kernel_mapping->GetMapping() == nullptr) {
FML_LOG(ERROR) << "Could not setup kernel mapping.";
return {};
return;
}
if (!root_isolate->get()->PrepareForRunningFromKernel(
std::move(kernel_mapping))) {
FML_LOG(ERROR)
<< "Could not prepare to run the isolate from the kernel file.";
return {};
return;
}
} else {
if (!root_isolate->get()->PrepareForRunningFromPrecompiledCode()) {
FML_LOG(ERROR)
<< "Could not prepare to run the isolate from precompiled code.";
return {};
return;
}
}
if (root_isolate->get()->GetPhase() != DartIsolate::Phase::Ready) {
FML_LOG(ERROR) << "Isolate is in unexpected phase.";
return {};
return;
}
if (!root_isolate->get()->Run(entrypoint)) {
if (!root_isolate->get()->Run(entrypoint,
settings.root_isolate_create_callback)) {
FML_LOG(ERROR) << "Could not run the method \"" << entrypoint
<< "\" in the isolate.";
return {};
return;
}
return root_isolate;
root_isolate->get()->AddIsolateShutdownCallback(
settings.root_isolate_shutdown_callback);
result = std::move(root_isolate);
}
static std::unique_ptr<AutoIsolateShutdown> RunDartCodeInIsolate(
const Settings& settings,
fml::RefPtr<fml::TaskRunner> task_runner,
std::string entrypoint) {
std::unique_ptr<AutoIsolateShutdown> result;
fml::AutoResetWaitableEvent latch;
fml::TaskRunner::RunNowOrPostTask(
task_runner, fml::MakeCopyable([&]() mutable {
RunDartCodeInIsolate(result, settings, task_runner, entrypoint);
latch.Signal();
}));
latch.Wait();
return result;
}
TEST_F(DartIsolateTest, IsolateCanLoadAndRunDartCode) {
auto isolate = RunDartCodeInIsolate(GetCurrentTaskRunner(), "main");
auto isolate = RunDartCodeInIsolate(CreateSettingsForFixture(),
GetCurrentTaskRunner(), "main");
ASSERT_TRUE(isolate);
ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running);
}
TEST_F(DartIsolateTest, IsolateCannotLoadAndRunUnknownDartEntrypoint) {
auto isolate =
RunDartCodeInIsolate(GetCurrentTaskRunner(), "thisShouldNotExist");
auto isolate = RunDartCodeInIsolate(
CreateSettingsForFixture(), GetCurrentTaskRunner(), "thisShouldNotExist");
ASSERT_FALSE(isolate);
}
TEST_F(DartIsolateTest, CanRunDartCodeCodeSynchronously) {
auto isolate = RunDartCodeInIsolate(GetCurrentTaskRunner(), "main");
auto isolate = RunDartCodeInIsolate(CreateSettingsForFixture(),
GetCurrentTaskRunner(), "main");
ASSERT_TRUE(isolate);
ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running);
ASSERT_TRUE(isolate->RunInIsolateScope([]() -> bool {
if (tonic::LogIfError(::Dart_Invoke(Dart_RootLibrary(),
tonic::ToDart("sayHi"), 0, nullptr))) {
......@@ -249,5 +284,20 @@ TEST_F(DartIsolateTest, CanRunDartCodeCodeSynchronously) {
}));
}
TEST_F(DartIsolateTest, CanRegisterNativeCallback) {
fml::AutoResetWaitableEvent latch;
AddNativeCallback("NotifyNative",
CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments args) {
FML_LOG(ERROR) << "Hello from Dart!";
latch.Signal();
})));
auto isolate =
RunDartCodeInIsolate(CreateSettingsForFixture(), GetThreadTaskRunner(),
"canRegisterNativeCallback");
ASSERT_TRUE(isolate);
ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running);
latch.Wait();
}
} // namespace testing
} // namespace blink
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:isolate';
void main() {
}
......@@ -14,3 +16,12 @@ void sayHi() {
void throwExceptionNow() {
throw("Hello");
}
@pragma('vm:entry-point')
void canRegisterNativeCallback() async {
print("In function canRegisterNativeCallback");
NotifyNative();
print("Called native method from canRegisterNativeCallback");
}
void NotifyNative() native "NotifyNative";
......@@ -10,7 +10,8 @@
namespace blink {
namespace testing {
RuntimeTest::RuntimeTest() = default;
RuntimeTest::RuntimeTest()
: native_resolver_(std::make_shared<::testing::TestDartNativeResolver>()) {}
RuntimeTest::~RuntimeTest() = default;
......@@ -70,6 +71,17 @@ void RuntimeTest::SetSnapshotsAndAssets(Settings& settings) {
}
}
Settings RuntimeTest::CreateSettingsForFixture() {
Settings settings;
settings.task_observer_add = [](intptr_t, fml::closure) {};
settings.task_observer_remove = [](intptr_t) {};
settings.root_isolate_create_callback = [this]() {
native_resolver_->SetNativeResolverForIsolate();
};
SetSnapshotsAndAssets(settings);
return settings;
}
// |testing::ThreadTest|
void RuntimeTest::SetUp() {
assets_dir_ = fml::OpenDirectory(::testing::GetFixturesPath(), false,
......@@ -83,5 +95,10 @@ void RuntimeTest::TearDown() {
assets_dir_.reset();
}
void RuntimeTest::AddNativeCallback(std::string name,
Dart_NativeFunction callback) {
native_resolver_->AddNativeCallback(std::move(name), callback);
}
} // namespace testing
} // namespace blink
......@@ -5,8 +5,11 @@
#ifndef FLUTTER_RUNTIME_RUNTIME_TEST_H_
#define FLUTTER_RUNTIME_RUNTIME_TEST_H_
#include <memory>
#include "flutter/common/settings.h"
#include "flutter/fml/macros.h"
#include "flutter/testing/test_dart_native_resolver.h"
#include "flutter/testing/thread_test.h"
namespace blink {
......@@ -18,7 +21,9 @@ class RuntimeTest : public ::testing::ThreadTest {
~RuntimeTest();
void SetSnapshotsAndAssets(Settings& settings);
Settings CreateSettingsForFixture();
void AddNativeCallback(std::string name, Dart_NativeFunction callback);
protected:
// |testing::ThreadTest|
......@@ -29,6 +34,9 @@ class RuntimeTest : public ::testing::ThreadTest {
private:
fml::UniqueFD assets_dir_;
std::shared_ptr<::testing::TestDartNativeResolver> native_resolver_;
void SetSnapshotsAndAssets(Settings& settings);
};
} // namespace testing
......
......@@ -72,8 +72,6 @@ executable("embedder_unittests") {
"tests/embedder_context.h",
"tests/embedder_test.cc",
"tests/embedder_test.h",
"tests/embedder_test_resolver.cc",
"tests/embedder_test_resolver.h",
"tests/embedder_unittests.cc",
]
......@@ -82,7 +80,7 @@ executable("embedder_unittests") {
":fixtures",
"$flutter_root/lib/ui",
"$flutter_root/runtime",
"$flutter_root/testing",
"$flutter_root/testing:dart",
"//third_party/skia",
"//third_party/tonic",
]
......
......@@ -34,7 +34,7 @@ TEST_F(Embedder11yTest, A11yTreeIsConsistent) {
})));
// Called by test fixture on UI thread to pass data back to this test.
NativeEntry callback;
::testing::NativeEntry callback;
context.AddNativeCallback(
"NotifyTestData",
CREATE_NATIVE_ENTRY(([&callback](Dart_NativeArguments args) {
......
......@@ -37,7 +37,7 @@ static std::unique_ptr<fml::Mapping> GetMapping(const fml::UniqueFD& directory,
EmbedderContext::EmbedderContext(std::string assets_path)
: assets_path_(std::move(assets_path)),
native_resolver_(std::make_shared<EmbedderTestResolver>()) {
native_resolver_(std::make_shared<::testing::TestDartNativeResolver>()) {
auto assets_dir = fml::OpenDirectory(assets_path_.c_str(), false,
fml::FilePermission::kRead);
vm_snapshot_data_ = GetMapping(assets_dir, "vm_snapshot_data", false);
......@@ -52,8 +52,8 @@ EmbedderContext::EmbedderContext(std::string assets_path)
}
isolate_create_callbacks_.push_back(
[weak_resolver =
std::weak_ptr<EmbedderTestResolver>{native_resolver_}]() {
[weak_resolver = std::weak_ptr<::testing::TestDartNativeResolver>{
native_resolver_}]() {
if (auto resolver = weak_resolver.lock()) {
resolver->SetNativeResolverForIsolate();
}
......
......@@ -14,22 +14,11 @@
#include "flutter/fml/macros.h"
#include "flutter/fml/mapping.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/embedder/tests/embedder_test_resolver.h"
#define CREATE_NATIVE_ENTRY(native_entry) \
([&]() { \
static ::shell::testing::NativeEntry closure; \
static Dart_NativeFunction entrypoint = [](Dart_NativeArguments args) { \
closure(args); \
}; \
closure = (native_entry); \
return entrypoint; \
})()
#include "flutter/testing/test_dart_native_resolver.h"
namespace shell {
namespace testing {
using NativeEntry = std::function<void(Dart_NativeArguments)>;
using SemanticsNodeCallback = std::function<void(const FlutterSemanticsNode*)>;
using SemanticsActionCallback =
std::function<void(const FlutterSemanticsCustomAction*)>;
......@@ -69,7 +58,7 @@ class EmbedderContext {
std::unique_ptr<fml::Mapping> isolate_snapshot_data_;
std::unique_ptr<fml::Mapping> isolate_snapshot_instructions_;
std::vector<fml::closure> isolate_create_callbacks_;
std::shared_ptr<EmbedderTestResolver> native_resolver_;
std::shared_ptr<::testing::TestDartNativeResolver> native_resolver_;
SemanticsNodeCallback update_semantics_node_callback_;
SemanticsActionCallback update_semantics_custom_action_callback_;
......
......@@ -20,3 +20,18 @@ source_set("testing") {
public_configs = [ "$flutter_root:config" ]
}
source_set("dart") {
testonly = true
sources = [
"$flutter_root/testing/test_dart_native_resolver.cc",
"$flutter_root/testing/test_dart_native_resolver.h",
]
public_deps = [
":testing",
"$flutter_root/runtime:libdart",
"//third_party/tonic",
]
}
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/embedder/tests/embedder_test_resolver.h"
#include "flutter/testing/test_dart_native_resolver.h"
#include <mutex>
#include <vector>
......@@ -10,19 +10,18 @@
#include "flutter/fml/synchronization/thread_annotations.h"
#include "tonic/converter/dart_converter.h"
namespace shell {
namespace testing {
EmbedderTestResolver::EmbedderTestResolver() = default;
TestDartNativeResolver::TestDartNativeResolver() = default;
EmbedderTestResolver::~EmbedderTestResolver() = default;
TestDartNativeResolver::~TestDartNativeResolver() = default;
void EmbedderTestResolver::AddNativeCallback(std::string name,
Dart_NativeFunction callback) {
void TestDartNativeResolver::AddNativeCallback(std::string name,
Dart_NativeFunction callback) {
native_callbacks_[name] = callback;
}
Dart_NativeFunction EmbedderTestResolver::ResolveCallback(
Dart_NativeFunction TestDartNativeResolver::ResolveCallback(
std::string name) const {
auto found = native_callbacks_.find(name);
if (found == native_callbacks_.end()) {
......@@ -33,10 +32,10 @@ Dart_NativeFunction EmbedderTestResolver::ResolveCallback(
}
static std::mutex gIsolateResolversMutex;
static std::map<Dart_Isolate, std::weak_ptr<EmbedderTestResolver>>
static std::map<Dart_Isolate, std::weak_ptr<TestDartNativeResolver>>
gIsolateResolvers FML_GUARDED_BY(gIsolateResolversMutex);
Dart_NativeFunction EmbedderTestResolver::DartNativeEntryResolverCallback(
Dart_NativeFunction TestDartNativeResolver::DartNativeEntryResolverCallback(
Dart_Handle dart_name,
int num_of_arguments,
bool* auto_setup_scope) {
......@@ -62,7 +61,7 @@ static const uint8_t* DartNativeEntrySymbolCallback(
return reinterpret_cast<const uint8_t*>(\\_(ツ)_/¯");
}
void EmbedderTestResolver::SetNativeResolverForIsolate() {
void TestDartNativeResolver::SetNativeResolverForIsolate() {
auto result = Dart_SetNativeResolver(Dart_RootLibrary(),
DartNativeEntryResolverCallback,
DartNativeEntrySymbolCallback);
......@@ -87,4 +86,3 @@ void EmbedderTestResolver::SetNativeResolverForIsolate() {
}
} // namespace testing
} // namespace shell
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_RESOLVER_H_
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_RESOLVER_H_
#ifndef FLUTTER_TESTING_TEST_DART_NATIVE_RESOLVER_H_
#define FLUTTER_TESTING_TEST_DART_NATIVE_RESOLVER_H_
#include <map>
#include <memory>
......@@ -11,26 +11,34 @@
#include "flutter/fml/macros.h"
#include "third_party/dart/runtime/include/dart_api.h"
namespace shell {
#define CREATE_NATIVE_ENTRY(native_entry) \
([&]() { \
static ::testing::NativeEntry closure; \
static Dart_NativeFunction entrypoint = [](Dart_NativeArguments args) { \
closure(args); \
}; \
closure = (native_entry); \
return entrypoint; \
})()
namespace testing {
class EmbedderTestResolver
: public std::enable_shared_from_this<EmbedderTestResolver> {
using NativeEntry = std::function<void(Dart_NativeArguments)>;
class TestDartNativeResolver
: public std::enable_shared_from_this<TestDartNativeResolver> {
public:
EmbedderTestResolver();
TestDartNativeResolver();
~EmbedderTestResolver();
~TestDartNativeResolver();
void AddNativeCallback(std::string name, Dart_NativeFunction callback);
private:
// Friend so that the context can set the native resolver.
friend class EmbedderContext;
void SetNativeResolverForIsolate();
private:
std::map<std::string, Dart_NativeFunction> native_callbacks_;
void SetNativeResolverForIsolate();
Dart_NativeFunction ResolveCallback(std::string name) const;
static Dart_NativeFunction DartNativeEntryResolverCallback(
......@@ -38,10 +46,9 @@ class EmbedderTestResolver
int num_of_arguments,
bool* auto_setup_scope);
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTestResolver);
FML_DISALLOW_COPY_AND_ASSIGN(TestDartNativeResolver);
};
} // namespace testing
} // namespace shell
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_RESOLVER_H_
#endif // FLUTTER_TESTING_TEST_DART_NATIVE_RESOLVER_H_
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册