8#include <fuchsia/test/cpp/fidl.h>
9#include <lib/async-loop/loop.h>
10#include <lib/async/cpp/task.h>
11#include <lib/async/default.h>
12#include <lib/fdio/directory.h>
13#include <lib/fdio/fd.h>
14#include <lib/fdio/namespace.h>
15#include <lib/fidl/cpp/string.h>
16#include <lib/fpromise/promise.h>
17#include <lib/sys/cpp/service_directory.h>
18#include <lib/zx/clock.h>
19#include <lib/zx/thread.h>
23#include <zircon/status.h>
28#include "flutter/fml/logging.h"
33#include "third_party/dart/runtime/include/dart_tools_api.h"
49constexpr char kTmpPath[] =
"/tmp";
52constexpr zx::duration kIdleNotifyDuration = zx::msec(500);
57void AfterTask(async_loop_t*,
void*) {
63 queue->RunMicrotasks();
67constexpr async_loop_config_t kLoopConfig = {
70 .getter = async_get_default_dispatcher,
71 .setter = async_set_default_dispatcher,
73 .make_default_for_current_thread =
true,
74 .epilogue = &AfterTask,
79std::string GetLabelFromUrl(
const std::string& url) {
80 for (
size_t i = url.length() - 1;
i > 0;
i--) {
82 return url.substr(
i + 1, url.length() - 1);
90std::string GetComponentNameFromUrl(
const std::string& url) {
91 const std::string label = GetLabelFromUrl(url);
92 for (
size_t i = 0;
i < label.length(); ++
i) {
93 if (label[
i] ==
'.') {
94 return label.substr(0,
i);
103 fuchsia::component::runner::ComponentStartInfo start_info,
104 std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
105 fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
107 DoneCallback done_callback)
108 : loop_(new async::Loop(&kLoopConfig)),
109 executor_(loop_->dispatcher()),
110 label_(GetLabelFromUrl(start_info.resolved_url())),
111 url_(
std::move(start_info.resolved_url())),
112 runner_incoming_services_(runner_incoming_services),
113 start_info_(
std::move(start_info)),
115 done_callback_(
std::move(done_callback)) {
120 test_component_name_ = GetComponentNameFromUrl(url_);
121 data_path_ =
"pkg/data/" + test_component_name_;
123 if (controller.is_valid()) {
124 binding_.Bind(std::move(controller));
125 binding_.set_error_handler([
this](zx_status_t status) { Kill(); });
127 FML_LOG(
ERROR) <<
"Fuchsia component controller endpoint is not valid.";
130 zx_status_t idle_timer_status =
132 if (idle_timer_status != ZX_OK) {
133 FML_LOG(INFO) <<
"Idle timer creation failed: "
134 << zx_status_get_string(idle_timer_status);
136 idle_wait_.set_object(idle_timer_.get());
137 idle_wait_.set_trigger(ZX_TIMER_SIGNALED);
138 idle_wait_.Begin(async_get_default_dispatcher());
143 start_info_.clear_runtime_dir();
148 fdio_ns_destroy(namespace_);
149 namespace_ =
nullptr;
157 zx::thread::self()->set_property(ZX_PROP_NAME, label_.c_str(), label_.size());
160 if (!CreateAndBindNamespace()) {
164 if (SetUpFromAppSnapshot()) {
165 FML_LOG(INFO) << url_ <<
" is running from an app snapshot";
166 }
else if (SetUpFromKernel()) {
167 FML_LOG(INFO) << url_ <<
" is running from kernel";
169 FML_LOG(
ERROR) <<
"Failed to set up component controller for " << url_;
175 suite_context_->outgoing()->AddPublicService(this->
GetHandler());
176 suite_context_->outgoing()->Serve(
177 std::move(*start_info_.mutable_outgoing_dir()), loop_->dispatcher());
182bool DartTestComponentController::CreateAndBindNamespace() {
183 if (!start_info_.has_ns()) {
184 FML_LOG(
ERROR) <<
"Component start info does not have a namespace.";
188 const zx_status_t ns_create_status = fdio_ns_create(&namespace_);
189 if (ns_create_status != ZX_OK) {
191 << zx_status_get_string(ns_create_status);
198 for (
auto& ns_entry : *start_info_.mutable_ns()) {
201 if (!ns_entry.has_path() || !ns_entry.has_directory()) {
205 if (ns_entry.path() == kTmpPath) {
212 fidl::InterfaceHandle<::fuchsia::io::Directory>
dir =
213 std::move(*ns_entry.mutable_directory());
214 const std::string
path = std::move(*ns_entry.mutable_path());
216 const zx_status_t ns_bind_status =
217 fdio_ns_bind(namespace_,
path.c_str(),
dir.TakeChannel().release());
218 if (ns_bind_status != ZX_OK) {
220 << zx_status_get_string(ns_bind_status);
228bool DartTestComponentController::SetUpFromKernel() {
231 namespace_, data_path_ +
"/app.dilplist",
manifest)) {
236 nullptr,
"/pkg/data/isolate_core_snapshot_data.bin",
237 isolate_snapshot_data_)) {
241 std::string str(
reinterpret_cast<const char*
>(
manifest.address()),
246 size_t end = str.find(
"\n", start);
247 if (end == std::string::npos) {
252 std::string
path = data_path_ +
"/" + str.substr(start, end - start);
261 kernel_peices_.emplace_back(std::move(kernel));
264 if (!CreateIsolate(isolate_snapshot_data_.
address(),
271 for (
const auto& kernel : kernel_peices_) {
292bool DartTestComponentController::SetUpFromAppSnapshot() {
293#if !defined(AOT_RUNTIME)
298 const uint8_t *isolate_data, *isolate_instructions;
299 if (elf_snapshot_.
Load(namespace_, data_path_ +
"/app_aot_snapshot.so")) {
302 if (isolate_data ==
nullptr || isolate_instructions ==
nullptr) {
307 namespace_, data_path_ +
"/isolate_snapshot_data.bin",
308 isolate_snapshot_data_)) {
311 isolate_data = isolate_snapshot_data_.
address();
312 isolate_instructions =
nullptr;
314 return CreateIsolate(isolate_data, isolate_instructions);
318bool DartTestComponentController::CreateIsolate(
322 char*
error =
nullptr;
325 intptr_t namespace_fd = -1;
343 state->get()->SetIsolate(isolate_);
346 [loop = loop_.get()](
auto callback) {
347 async::PostTask(loop->dispatcher(), std::move(
callback));
349 state->get()->message_handler().Initialize(dispatcher);
351 state->get()->SetReturnCodeCallback([
this](uint32_t return_code) {
352 return_code_ = return_code;
353 auto ret_status = return_code == 0 ? fuchsia::test::Status::PASSED
355 fuchsia::test::Result
result;
356 result.set_status(ret_status);
357 case_listener_->Finished(std::move(
result));
364DartTestComponentController::CaseIterator::CaseIterator(
365 fidl::InterfaceRequest<fuchsia::test::CaseIterator> request,
366 async_dispatcher_t* dispatcher,
367 std::string test_component_name,
370 test_component_name_(test_component_name),
371 done_callback_(
std::move(done_callback)) {}
374void DartTestComponentController::CaseIterator::GetNext(
379 fuchsia::test::Case test_case;
380 test_case.set_name(test_component_name_);
381 test_case.set_enabled(
true);
382 std::vector<fuchsia::test::Case> cases;
383 cases.push_back(std::move(test_case));
389 std::vector<fuchsia::test::Case> cases;
391 done_callback_(
this);
396std::unique_ptr<DartTestComponentController::CaseIterator>
397DartTestComponentController::RemoveCaseInterator(CaseIterator* case_iterator) {
398 auto it = case_iterators_.find(case_iterator);
399 std::unique_ptr<DartTestComponentController::CaseIterator> case_iterator_ptr;
400 if (it != case_iterators_.end()) {
401 case_iterator_ptr = std::move(it->second);
402 case_iterators_.erase(it);
404 return case_iterator_ptr;
409 fidl::InterfaceRequest<fuchsia::test::CaseIterator> iterator) {
410 auto case_iterator = std::make_unique<CaseIterator>(
411 std::move(iterator), loop_->dispatcher(), test_component_name_,
412 [
this](CaseIterator* case_iterator) {
413 RemoveCaseInterator(case_iterator);
415 case_iterators_.emplace(case_iterator.get(), std::move(case_iterator));
420 std::vector<fuchsia::test::Invocation>
tests,
421 fuchsia::test::RunOptions
options,
422 fidl::InterfaceHandle<fuchsia::test::RunListener> listener) {
423 std::vector<std::string>
args;
428 auto listener_proxy = listener.Bind();
434 for (
auto it =
tests.begin(); it !=
tests.end(); it++) {
435 auto invocation = std::move(*it);
436 std::string test_case_name;
437 if (invocation.has_name()) {
438 test_case_name = invocation.name();
444 if (status != ZX_OK) {
446 << zx_status_get_string(status);
449 status = fdio_fd_create(out_.release(), &stdout_fd_);
450 if (status != ZX_OK) {
452 << zx_status_get_string(status);
456 if (status != ZX_OK) {
458 << zx_status_get_string(status);
461 status = fdio_fd_create(err_.release(), &stderr_fd_);
462 if (status != ZX_OK) {
464 << zx_status_get_string(status);
469 fuchsia::test::StdHandles std_handles;
470 std_handles.set_out(std::move(out_client_));
471 std_handles.set_err(std::move(err_client_));
473 listener_proxy->OnTestCaseStarted(std::move(invocation),
474 std::move(std_handles),
475 case_listener_.NewRequest());
479 auto dart_main_promise = fpromise::make_promise([&] { RunDartMain(); });
481 executor_.schedule_task(std::move(dart_main_promise));
482 while (!dart_state->has_set_return_code()) {
483 loop_->Run(zx::deadline_after(kTestTimeout),
true);
487 listener_proxy->OnFinished();
490 if (binding_.is_bound()) {
498 if (return_code_ == 0) {
499 binding_.Close(ZX_OK);
501 binding_.Close(zx_status_t(fuchsia::component::Error::INTERNAL));
509fpromise::promise<> DartTestComponentController::RunDartMain() {
515 fidl::InterfaceRequest<fuchsia::io::Directory> outgoing_dir =
516 std::move(*start_info_.mutable_outgoing_dir());
518 outgoing_dir.TakeChannel(),
524 if (
error !=
nullptr) {
529 return fpromise::make_error_promise();
542 FML_LOG(
ERROR) <<
"Failed to allocate Dart arguments list: "
545 return fpromise::make_error_promise();
551 FML_LOG(
ERROR) <<
"Failed to locate user_main in the root library: "
554 return fpromise::make_error_promise();
563 return fpromise::make_error_promise();
567 fuchsia_lib,
"_runUserMainForDartRunner", {user_main, dart_arguments});
571 if (!dart_state->has_set_return_code()) {
580 return fpromise::make_error_promise();
584 return fpromise::make_ok_promise();
587void DartTestComponentController::Kill() {
588 done_callback_(
this);
591 suite_bindings_.CloseAll();
615 if (dart_state->has_set_return_code()) {
624 if (return_code_ != 0) {
629 idle_start_ = zx::clock::get_monotonic();
631 idle_timer_.set(idle_start_ + kIdleWaitDuration, kIdleSlack);
632 if (status != ZX_OK) {
633 FML_LOG(INFO) <<
"Idle timer set failed: " << zx_status_get_string(status);
637void DartTestComponentController::Stop() {
641void DartTestComponentController::OnIdleTimer(async_dispatcher_t* dispatcher,
642 async::WaitBase* wait,
644 const zx_packet_signal* signal) {
645 if ((status != ZX_OK) || !(signal->observed & ZX_TIMER_SIGNALED) ||
651 zx::time deadline = idle_start_ + kIdleWaitDuration;
652 zx::time now = zx::clock::get_monotonic();
653 if (now >= deadline) {
658 idle_timer_.cancel();
661 zx_status_t status = idle_timer_.set(deadline, kIdleSlack);
662 if (status != ZX_OK) {
663 FML_LOG(INFO) <<
"Idle timer set failed: "
664 << zx_status_get_string(status);
667 wait->Begin(dispatcher);
static sk_sp< Effect > Create()
void Run(std::vector< fuchsia::test::Invocation > tests, fuchsia::test::RunOptions options, fidl::InterfaceHandle< fuchsia::test::RunListener > listener) override
|Suite| protocol implementation.
~DartTestComponentController() override
fidl::InterfaceRequestHandler< fuchsia::test::Suite > GetHandler()
void GetTests(fidl::InterfaceRequest< fuchsia::test::CaseIterator > iterator) override
|Suite| protocol implementation.
DartTestComponentController(fuchsia::component::runner::ComponentStartInfo start_info, std::shared_ptr< sys::ServiceDirectory > runner_incoming_services, fidl::InterfaceRequest< fuchsia::component::runner::ComponentController > controller, DoneCallback done_callback)
bool Load(fdio_ns_t *namespc, const std::string &path)
const uint8_t * IsolateData() const
const uint8_t * IsolateInstrs() const
static bool LoadFromNamespace(fdio_ns_t *namespc, const std::string &path, MappedResource &resource, bool executable=false)
const uint8_t * address() const
std::function< void(std::function< void(void)>)> TaskDispatcher
static DartMicrotaskQueue * GetForCurrentThread()
static void StartForCurrentThread()
static DartState * Current()
DART_EXPORT Dart_Isolate Dart_CreateIsolateGroup(const char *script_uri, const char *name, const uint8_t *isolate_snapshot_data, const uint8_t *isolate_snapshot_instructions, Dart_IsolateFlags *flags, void *isolate_group_data, void *isolate_data, char **error)
DART_EXPORT void Dart_IsolateFlagsInitialize(Dart_IsolateFlags *flags)
DART_EXPORT Dart_Handle Dart_GetNonNullableType(Dart_Handle library, Dart_Handle class_name, intptr_t number_of_type_arguments, Dart_Handle *type_arguments)
DART_EXPORT DART_WARN_UNUSED_RESULT char * Dart_IsolateMakeRunnable(Dart_Isolate isolate)
DART_EXPORT void Dart_EnterScope(void)
DART_EXPORT void Dart_ExitScope(void)
DART_EXPORT void Dart_ShutdownIsolate(void)
DART_EXPORT Dart_Handle Dart_NewListOfTypeFilled(Dart_Handle element_type, Dart_Handle fill_object, intptr_t length)
struct _Dart_Handle * Dart_Handle
DART_EXPORT Dart_Handle Dart_EmptyString(void)
DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_LoadLibraryFromKernel(const uint8_t *kernel_buffer, intptr_t kernel_buffer_size)
DART_EXPORT Dart_Handle Dart_LookupLibrary(Dart_Handle url)
DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_FinalizeLoading(bool complete_futures)
DART_EXPORT void Dart_NotifyIdle(int64_t deadline)
DART_EXPORT Dart_Isolate Dart_CurrentIsolate(void)
DART_EXPORT Dart_Handle Dart_Null(void)
DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_GetField(Dart_Handle container, Dart_Handle name)
DART_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate)
DART_EXPORT bool Dart_IsError(Dart_Handle handle)
DART_EXPORT void Dart_ExitIsolate(void)
DART_EXPORT void Dart_SetMessageNotifyCallback(Dart_MessageNotifyCallback message_notify_callback)
DART_EXPORT const char * Dart_GetError(Dart_Handle handle)
DART_EXPORT Dart_Handle Dart_RootLibrary(void)
DART_EXPORT Dart_Handle Dart_SetRootLibrary(Dart_Handle library)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
const uint8_t uint32_t uint32_t GError ** error
#define FML_LOG(severity)
#define FML_CHECK(condition)
Dart_NativeFunction function
const uint8_t * isolate_snapshot_data
const uint8_t * isolate_snapshot_instructions
void InitBuiltinLibrariesForIsolate(const std::string &script_uri, fdio_ns_t *namespc, int stdoutfd, int stderrfd, zx::channel directory_request, bool service_isolate)
void HandleIfException(std::shared_ptr<::sys::ServiceDirectory > services, const std::string &component_url, Dart_Handle result)
void BindTemp(fdio_ns_t *ns)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
const myers::Point & get(const myers::Segment &)
Dart_Handle ToDart(const T &object)
int GetErrorExitCode(Dart_Handle handle)
Dart_Handle DartInvokeField(Dart_Handle target, const char *name, std::initializer_list< Dart_Handle > args)
static double time(int loops, Benchmark *bench, Target *target)