From f442a035b876ce58977bccb4cda627ce783b9abf Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 29 Jun 2015 10:39:44 -0700 Subject: [PATCH] Make it possible to run a test in sky_shell This CL makes it possible to run sky/tests/raw/bogus_import.dart in sky_shell on Linux. We'd eventually like to scale this up to running all the tests. R=eseidel@chromium.org Review URL: https://codereview.chromium.org/1218633003. --- engine/src/flutter/shell/BUILD.gn | 7 +- engine/src/flutter/shell/linux/main.cc | 37 ++--- .../linux/platform_service_provider_linux.cc | 25 +++- .../src/flutter/shell/testing/test_runner.cc | 128 ++++++++++++++++++ .../src/flutter/shell/testing/test_runner.h | 62 +++++++++ engine/src/flutter/tools/tester/BUILD.gn | 2 +- 6 files changed, 232 insertions(+), 29 deletions(-) create mode 100644 engine/src/flutter/shell/testing/test_runner.cc create mode 100644 engine/src/flutter/shell/testing/test_runner.h diff --git a/engine/src/flutter/shell/BUILD.gn b/engine/src/flutter/shell/BUILD.gn index 4032deb4d93..cba8fdf390c 100644 --- a/engine/src/flutter/shell/BUILD.gn +++ b/engine/src/flutter/shell/BUILD.gn @@ -181,9 +181,14 @@ if (is_android) { "linux/main.cc", "linux/platform_service_provider_linux.cc", "linux/platform_view_linux.cc", + "testing/test_runner.cc", + "testing/test_runner.h", ] - deps = common_deps + [ ":common" ] + deps = common_deps + [ + ":common", + "//sky/services/testing", + ] } } else { assert(false, "Unsupported platform") diff --git a/engine/src/flutter/shell/linux/main.cc b/engine/src/flutter/shell/linux/main.cc index f6beeabe2d7..d04d290d0b3 100644 --- a/engine/src/flutter/shell/linux/main.cc +++ b/engine/src/flutter/shell/linux/main.cc @@ -16,6 +16,7 @@ #include "sky/shell/shell.h" #include "sky/shell/shell_view.h" #include "sky/shell/switches.h" +#include "sky/shell/testing/test_runner.h" namespace sky { namespace shell { @@ -23,39 +24,26 @@ namespace { void Usage() { std::cerr << "Usage: sky_shell" - << " [MAIN_DART --" << switches::kPackageRoot << "=PACKAGE_ROOT]" - << " [--" << switches::kSnapshot << "=SNAPSHOT]" << std::endl; + << " --" << switches::kPackageRoot << "=PACKAGE_ROOT" + << " [ MAIN_DART ]" << std::endl; } void Init() { Shell::Init(make_scoped_ptr(new ServiceProviderContext( base::MessageLoop::current()->task_runner()))); - // TODO(abarth): Currently we leak the ShellView. - ShellView* shell_view = new ShellView(Shell::Shared()); - - ViewportObserverPtr viewport_observer; - shell_view->view()->ConnectToViewportObserver(GetProxy(&viewport_observer)); - base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kSnapshot)) { - std::string snapshot = - command_line.GetSwitchValueASCII(switches::kSnapshot); - viewport_observer->RunFromSnapshot(snapshot); - return; - } + std::string package_root = + command_line.GetSwitchValueASCII(switches::kPackageRoot); - if (command_line.HasSwitch(switches::kPackageRoot)) { - std::string main = command_line.GetArgs()[0]; - std::string package_root = - command_line.GetSwitchValueASCII(switches::kPackageRoot); - viewport_observer->RunFromFile(main, package_root); - return; - } + std::string main; + auto args = command_line.GetArgs(); + if (!args.empty()) + main = args[0]; - std::cerr << "One of --" << switches::kPackageRoot << " or --" - << switches::kSnapshot << " is required." << std::endl; + TestRunner::Shared().set_package_root(package_root); + TestRunner::Shared().Start(main); } } // namespace @@ -68,7 +56,8 @@ int main(int argc, const char* argv[]) { base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(sky::shell::switches::kHelp)) { + if (command_line.HasSwitch(sky::shell::switches::kHelp) || + !command_line.HasSwitch(sky::shell::switches::kPackageRoot)) { sky::shell::Usage(); return 0; } diff --git a/engine/src/flutter/shell/linux/platform_service_provider_linux.cc b/engine/src/flutter/shell/linux/platform_service_provider_linux.cc index e0199163d6b..8bc64cc3367 100644 --- a/engine/src/flutter/shell/linux/platform_service_provider_linux.cc +++ b/engine/src/flutter/shell/linux/platform_service_provider_linux.cc @@ -3,17 +3,36 @@ // found in the LICENSE file. #include "base/bind.h" -#include "base/trace_event/trace_event.h" -#include "mojo/public/cpp/bindings/interface_request.h" +#include "base/bind_helpers.h" +#include "base/lazy_instance.h" +#include "base/location.h" +#include "base/single_thread_task_runner.h" +#include "mojo/public/cpp/application/service_provider_impl.h" #include "sky/shell/service_provider.h" +#include "sky/shell/testing/test_runner.h" namespace sky { namespace shell { +namespace { + +base::LazyInstance> g_service_provider = + LAZY_INSTANCE_INITIALIZER; + +static void CreateServiceProviderImpl( + mojo::InterfaceRequest request) { + g_service_provider.Get().reset(new mojo::ServiceProviderImpl(request.Pass())); + g_service_provider.Get()->AddService(&TestRunner::Shared()); +} + +} // namespace mojo::ServiceProviderPtr CreateServiceProvider( ServiceProviderContext* context) { + DCHECK(context); mojo::MessagePipe pipe; - // TODO(abarth): Wire pipe.handle1 up to something. + auto request = mojo::MakeRequest(pipe.handle1.Pass()); + context->platform_task_runner->PostTask( + FROM_HERE, base::Bind(CreateServiceProviderImpl, base::Passed(&request))); return mojo::MakeProxy( mojo::InterfacePtrInfo(pipe.handle0.Pass(), 0u)); } diff --git a/engine/src/flutter/shell/testing/test_runner.cc b/engine/src/flutter/shell/testing/test_runner.cc new file mode 100644 index 00000000000..704e855888f --- /dev/null +++ b/engine/src/flutter/shell/testing/test_runner.cc @@ -0,0 +1,128 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sky/shell/testing/test_runner.h" + +#include + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/string_util.h" +#include "sky/shell/platform_view.h" +#include "sky/shell/shell.h" +#include "sky/shell/shell_view.h" + +namespace sky { +namespace shell { +namespace { + +struct UrlData { + std::string url; + std::string expected_pixel_hash; + bool enable_pixel_dumping = false; +}; + +void WaitForURL(UrlData& data) { + // A test name is formated like file:///path/to/test'--pixel-test'pixelhash + std::cin >> data.url; + + std::string pixel_switch; + std::string::size_type separator_position = data.url.find('\''); + if (separator_position != std::string::npos) { + pixel_switch = data.url.substr(separator_position + 1); + data.url.erase(separator_position); + } + + std::string pixel_hash; + separator_position = pixel_switch.find('\''); + if (separator_position != std::string::npos) { + pixel_hash = pixel_switch.substr(separator_position + 1); + pixel_switch.erase(separator_position); + } + + data.enable_pixel_dumping = pixel_switch == "--pixel-test"; + data.expected_pixel_hash = pixel_hash; +} + +void PrintAndFlush(const std::string& value) { + std::cout << value; + std::cout.flush(); +} + +const char kFileUrlPrefix[] = "file://"; +static TestRunner* g_test_runner = nullptr; + +} // namespace + +TestRunner::TestRunner() + : shell_view_(new ShellView(Shell::Shared())), + weak_ptr_factory_(this) { + CHECK(!g_test_runner) << "Only create one TestRunner."; + + shell_view_->view()->ConnectToViewportObserver(GetProxy(&viewport_observer_)); + viewport_observer_->OnViewportMetricsChanged(320, 640, 1.0); +} + +TestRunner::~TestRunner() { +} + +TestRunner& TestRunner::Shared() { + if (!g_test_runner) + g_test_runner = new TestRunner(); + return *g_test_runner; +} + +void TestRunner::Start(const std::string& single_test_url) { + single_test_url_ = single_test_url; + PrintAndFlush("#READY\n"); + ScheduleRun(); +} + +void TestRunner::OnTestComplete(const mojo::String& test_result, + const mojo::Array pixels) { + std::cout << "Content-Type: text/plain\n"; + std::cout << test_result << "\n"; + PrintAndFlush("#EOF\n"); // Text result complete + PrintAndFlush("#EOF\n"); // Pixel result complete + std::cerr << "#EOF\n"; + std::cerr.flush(); + bindings_.CloseAllBindings(); + + if (single_test_url_.length()) + exit(0); + ScheduleRun(); +} + +void TestRunner::DispatchInputEvent(mojo::EventPtr event) { + // TODO(abarth): Not implemented. +} + +void TestRunner::Create(mojo::ApplicationConnection* app, + mojo::InterfaceRequest request) { + bindings_.AddBinding(this, request.Pass()); +} + +void TestRunner::ScheduleRun() { + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&TestRunner::Run, weak_ptr_factory_.GetWeakPtr())); +} + +void TestRunner::Run() { + UrlData data; + if (single_test_url_.length()) { + data.url = single_test_url_; + } else { + WaitForURL(data); + } + + std::cout << "#BEGIN\n"; + std::cout.flush(); + + if (StartsWithASCII(data.url, kFileUrlPrefix, true)) + ReplaceFirstSubstringAfterOffset(&data.url, 0, kFileUrlPrefix, ""); + viewport_observer_->RunFromFile(data.url, package_root_); +} + +} // namespace shell +} // namespace sky diff --git a/engine/src/flutter/shell/testing/test_runner.h b/engine/src/flutter/shell/testing/test_runner.h new file mode 100644 index 00000000000..eb27c13d6eb --- /dev/null +++ b/engine/src/flutter/shell/testing/test_runner.h @@ -0,0 +1,62 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SKY_SHELL_TESTING_TEST_RUNNER_H_ +#define SKY_SHELL_TESTING_TEST_RUNNER_H_ + +#include + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "mojo/common/weak_binding_set.h" +#include "mojo/public/cpp/application/interface_factory_impl.h" +#include "sky/services/testing/test_harness.mojom.h" +#include "sky/services/viewport/viewport_observer.mojom.h" + +namespace sky { +namespace shell { +class ShellView; + +class TestRunner : public mojo::InterfaceFactory, + public TestHarness { + public: + static TestRunner& Shared(); + + void set_package_root(const std::string& package_root) { + package_root_ = package_root; + } + + void Start(const std::string& single_test_url); + + private: + // mojo::InterfaceFactory implementation: + void Create(mojo::ApplicationConnection* app, + mojo::InterfaceRequest request) override; + + // TestHarness implementation: + void OnTestComplete(const mojo::String& test_result, + const mojo::Array pixels) override; + void DispatchInputEvent(mojo::EventPtr event) override; + + TestRunner(); + ~TestRunner() override; + void ScheduleRun(); + void Run(); + + std::string package_root_; + scoped_ptr shell_view_; + ViewportObserverPtr viewport_observer_; + + std::string single_test_url_; + mojo::WeakBindingSet bindings_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(TestRunner); +}; + +} // namespace shell +} // namespace sky + +#endif // SKY_SHELL_TESTING_TEST_RUNNER_H_ diff --git a/engine/src/flutter/tools/tester/BUILD.gn b/engine/src/flutter/tools/tester/BUILD.gn index a84cbabf83e..d7b02630192 100644 --- a/engine/src/flutter/tools/tester/BUILD.gn +++ b/engine/src/flutter/tools/tester/BUILD.gn @@ -26,6 +26,6 @@ mojo_native_application("tester") { "//mojo/services/input_events/public/interfaces", "//mojo/services/view_manager/public/cpp", "//services/window_manager:lib", - "//sky/services/testing:bindings", + "//sky/services/testing", ] }