[dart_test_runner] Support stdout/stderr logging to current execution window by passing handles to the dart process and the test_manager. (flutter/engine#33858)

This commit is contained in:
Naud Ghebre 2022-07-07 17:07:12 -04:00 committed by GitHub
parent e36b8d6d99
commit cf5f9d4f95
2 changed files with 59 additions and 27 deletions

View File

@ -14,6 +14,7 @@
#include <lib/fdio/fd.h>
#include <lib/fdio/namespace.h>
#include <lib/fidl/cpp/string.h>
#include <lib/fpromise/promise.h>
#include <lib/sys/cpp/service_directory.h>
#include <lib/syslog/global.h>
#include <lib/zx/clock.h>
@ -53,6 +54,8 @@ constexpr zx::duration kIdleWaitDuration = zx::sec(2);
constexpr zx::duration kIdleNotifyDuration = zx::msec(500);
constexpr zx::duration kIdleSlack = zx::sec(1);
constexpr zx::duration kTestTimeout = zx::sec(60);
void AfterTask(async_loop_t*, void*) {
tonic::DartMicrotaskQueue* queue =
tonic::DartMicrotaskQueue::GetForCurrentThread();
@ -105,6 +108,7 @@ DartTestComponentControllerV2::DartTestComponentControllerV2(
controller,
DoneCallback done_callback)
: loop_(new async::Loop(&kLoopConfig)),
executor_(loop_->dispatcher()),
label_(GetLabelFromUrl(start_info.resolved_url())),
url_(std::move(start_info.resolved_url())),
runner_incoming_services_(runner_incoming_services),
@ -427,8 +431,6 @@ void DartTestComponentControllerV2::Run(
std::vector<fuchsia::test::Invocation> tests,
fuchsia::test::RunOptions options,
fidl::InterfaceHandle<fuchsia::test::RunListener> listener) {
RunDartMain();
std::vector<std::string> args;
if (options.has_arguments()) {
args = std::move(*options.mutable_arguments());
@ -436,6 +438,10 @@ void DartTestComponentControllerV2::Run(
auto listener_proxy = listener.Bind();
// We expect 1 test that will run all other tests in the test suite (i.e. a
// single main.dart that calls out to all of the dart tests). If the dart
// implementation in-tree changes, iterating over the invocations like so
// will be able to handle that.
for (auto it = tests.begin(); it != tests.end(); it++) {
auto invocation = std::move(*it);
std::string test_case_name;
@ -443,28 +449,54 @@ void DartTestComponentControllerV2::Run(
test_case_name = invocation.name();
}
zx::socket out, err, out_client, err_client;
auto status = zx::socket::create(0, &out, &out_client);
// Create and out/err sockets and pass file descriptor for each to the
// dart process so that logging can be forwarded to the test_manager.
auto status = zx::socket::create(0, &out_, &out_client_);
if (status != ZX_OK) {
FML_LOG(FATAL) << "cannot create out socket: "
<< zx_status_get_string(status);
}
status = zx::socket::create(0, &err, &err_client);
status = fdio_fd_create(out_.release(), &stdout_fd_);
if (status != ZX_OK) {
FML_LOG(FATAL) << "failed to extract output fd: "
<< zx_status_get_string(status);
}
status = zx::socket::create(0, &err_, &err_client_);
if (status != ZX_OK) {
FML_LOG(FATAL) << "cannot create error socket: "
<< zx_status_get_string(status);
}
status = fdio_fd_create(err_.release(), &stderr_fd_);
if (status != ZX_OK) {
FML_LOG(FATAL) << "failed to extract error fd: "
<< zx_status_get_string(status);
}
// Pass client end of out and err socket for test_manager to stream logs to
// current terminal, via |fuchsia::test::StdHandles|.
fuchsia::test::StdHandles std_handles;
std_handles.set_err(std::move(err_client));
std_handles.set_out(std::move(out_client));
std_handles.set_out(std::move(out_client_));
std_handles.set_err(std::move(err_client_));
listener_proxy->OnTestCaseStarted(std::move(invocation),
std::move(std_handles),
case_listener_.NewRequest());
}
listener_proxy->OnFinished();
// Run dart main and wait until exit code has been set before notifying of
// test completion.
auto dart_main_promise = fpromise::make_promise([&] { RunDartMain(); });
auto dart_state = tonic::DartState::Current();
executor_.schedule_task(std::move(dart_main_promise));
while (!dart_state->has_set_return_code()) {
loop_->Run(zx::deadline_after(kTestTimeout), true);
}
// Notify the test_manager that the test has completed.
listener_proxy->OnFinished();
}
if (binding_.is_bound()) {
// From the documentation for ComponentController, ZX_OK should be sent when
@ -480,22 +512,17 @@ void DartTestComponentControllerV2::Run(
binding_.Close(zx_status_t(fuchsia::component::Error::INTERNAL));
}
}
// Stop and kill the test component.
Stop();
}
bool DartTestComponentControllerV2::RunDartMain() {
fpromise::promise<> DartTestComponentControllerV2::RunDartMain() {
FML_CHECK(namespace_ != nullptr);
Dart_EnterScope();
tonic::DartMicrotaskQueue::StartForCurrentThread();
// TODO(fxb/88384): Create a file descriptor for each component that is
// launched and listen for anything that is written to the component. When
// something is written to the component, forward that message along to the
// Fuchsia logger and decorate it with the tag that it came from the
// component.
stdout_fd_ = fileno(stdout);
stderr_fd_ = fileno(stderr);
fidl::InterfaceRequest<fuchsia::io::Directory> outgoing_dir =
std::move(*start_info_.mutable_outgoing_dir());
InitBuiltinLibrariesForIsolate(
@ -510,7 +537,7 @@ bool DartTestComponentControllerV2::RunDartMain() {
Dart_ShutdownIsolate();
FX_LOGF(ERROR, LOG_TAG, "Unable to make isolate runnable: %s", error);
free(error);
return false;
return fpromise::make_error_promise();
}
Dart_EnterIsolate(isolate_);
Dart_EnterScope();
@ -526,7 +553,7 @@ bool DartTestComponentControllerV2::RunDartMain() {
FX_LOGF(ERROR, LOG_TAG, "Failed to allocate Dart arguments list: %s",
Dart_GetError(dart_arguments));
Dart_ExitScope();
return false;
return fpromise::make_error_promise();
}
Dart_Handle user_main = Dart_GetField(Dart_RootLibrary(), ToDart("main"));
@ -536,7 +563,7 @@ bool DartTestComponentControllerV2::RunDartMain() {
"Failed to locate user_main in the root library: %s",
Dart_GetError(user_main));
Dart_ExitScope();
return false;
return fpromise::make_error_promise();
}
Dart_Handle fuchsia_lib = Dart_LookupLibrary(tonic::ToDart("dart:fuchsia"));
@ -545,7 +572,7 @@ bool DartTestComponentControllerV2::RunDartMain() {
FX_LOGF(ERROR, LOG_TAG, "Failed to locate dart:fuchsia: %s",
Dart_GetError(fuchsia_lib));
Dart_ExitScope();
return false;
return fpromise::make_error_promise();
}
Dart_Handle main_result = tonic::DartInvokeField(
@ -562,14 +589,17 @@ bool DartTestComponentControllerV2::RunDartMain() {
main_result);
}
Dart_ExitScope();
return false;
return fpromise::make_error_promise();
}
Dart_ExitScope();
return true;
return fpromise::make_ok_promise();
}
void DartTestComponentControllerV2::Kill() {
done_callback_(this);
close(stdout_fd_);
close(stderr_fd_);
suite_bindings_.CloseAll();
if (Dart_CurrentIsolate()) {
tonic::DartMicrotaskQueue* queue =

View File

@ -11,6 +11,7 @@
#include <fuchsia/sys/cpp/fidl.h>
#include <fuchsia/test/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async/cpp/executor.h>
#include <lib/async/cpp/wait.h>
#include <lib/fdio/namespace.h>
#include <lib/sys/cpp/component_context.h>
@ -66,9 +67,8 @@ class DartTestComponentControllerV2
}
private:
/// Helper for actually running the Dart main. Returns true if successful,
/// false otherwise.
bool RunDartMain();
/// Helper for actually running the Dart main. Returns Returns a promise.
fpromise::promise<> RunDartMain();
/// Creates and binds the namespace for this component. Returns true if
/// successful, false otherwise.
@ -126,6 +126,7 @@ class DartTestComponentControllerV2
// The loop must be the first declared member so that it gets destroyed after
// binding_ which expects the existence of a loop.
std::unique_ptr<async::Loop> loop_;
async::Executor executor_;
std::string label_;
std::string url_;
@ -137,6 +138,7 @@ class DartTestComponentControllerV2
fidl::Binding<fuchsia::component::runner::ComponentController> binding_;
DoneCallback done_callback_;
zx::socket out_, err_, out_client_, err_client_;
fdio_ns_t* namespace_ = nullptr;
int stdout_fd_ = -1;
int stderr_fd_ = -1;