From 2bb1d10ee6fc574a8e7f04313886bbadcec86d34 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Wed, 3 Aug 2022 18:41:44 -0700 Subject: [PATCH] Support fixture tests for Windows embedder This adds support for running end-to-end tests that use a live engine to run Dart test fixtures. This enables testing the public Windows C API in //flutter/shell/platform/windows/public/flutter_windows.h This only adds support for a single test entrypoint (main). A followup patch will add support for this. See: https://github.com/flutter/flutter/issues/93537 Issue: https://github.com/flutter/flutter/issues/87299 --- shell/platform/windows/BUILD.gn | 7 ++ shell/platform/windows/fixtures/main.dart | 7 ++ .../windows/flutter_windows_unittests.cc | 20 ++++- .../platform/windows/testing/windows_test.cc | 26 ++++++ shell/platform/windows/testing/windows_test.h | 47 ++++++++++ .../testing/windows_test_config_builder.cc | 87 +++++++++++++++++++ .../testing/windows_test_config_builder.h | 79 +++++++++++++++++ .../windows/testing/windows_test_context.cc | 26 ++++++ .../windows/testing/windows_test_context.h | 40 +++++++++ 9 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 shell/platform/windows/fixtures/main.dart create mode 100644 shell/platform/windows/testing/windows_test.cc create mode 100644 shell/platform/windows/testing/windows_test.h create mode 100644 shell/platform/windows/testing/windows_test_config_builder.cc create mode 100644 shell/platform/windows/testing/windows_test_config_builder.h create mode 100644 shell/platform/windows/testing/windows_test_context.cc create mode 100644 shell/platform/windows/testing/windows_test_context.h diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 09597e321851f..d2df59b2cf740 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -158,6 +158,7 @@ shared_library("flutter_windows") { } test_fixtures("flutter_windows_fixtures") { + dart_main = "fixtures/main.dart" fixtures = [] } @@ -198,6 +199,12 @@ executable("flutter_windows_unittests") { "testing/test_keyboard.cc", "testing/test_keyboard.h", "testing/test_keyboard_unittests.cc", + "testing/windows_test.cc", + "testing/windows_test.h", + "testing/windows_test_config_builder.cc", + "testing/windows_test_config_builder.h", + "testing/windows_test_context.cc", + "testing/windows_test_context.h", "testing/wm_builders.cc", "testing/wm_builders.h", "text_input_plugin_unittest.cc", diff --git a/shell/platform/windows/fixtures/main.dart b/shell/platform/windows/fixtures/main.dart new file mode 100644 index 0000000000000..6d207279d54ee --- /dev/null +++ b/shell/platform/windows/fixtures/main.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +void main() { + print('Hello windows engine test!'); +} diff --git a/shell/platform/windows/flutter_windows_unittests.cc b/shell/platform/windows/flutter_windows_unittests.cc index 52676d3fd7fd2..7b21046eac897 100644 --- a/shell/platform/windows/flutter_windows_unittests.cc +++ b/shell/platform/windows/flutter_windows_unittests.cc @@ -5,13 +5,17 @@ #include "flutter/shell/platform/windows/public/flutter_windows.h" #include +#include +#include "flutter/shell/platform/windows/testing/windows_test.h" +#include "flutter/shell/platform/windows/testing/windows_test_config_builder.h" +#include "flutter/shell/platform/windows/testing/windows_test_context.h" #include "gtest/gtest.h" namespace flutter { namespace testing { -TEST(FlutterWindowsTest, GetTextureRegistrar) { +TEST(WindowsNoFixtureTest, GetTextureRegistrar) { FlutterDesktopEngineProperties properties; memset(&properties, 0, sizeof(FlutterDesktopEngineProperties)); properties.assets_path = L""; @@ -23,5 +27,19 @@ TEST(FlutterWindowsTest, GetTextureRegistrar) { FlutterDesktopEngineDestroy(engine); } +TEST_F(WindowsTest, LaunchMain) { + auto& context = GetContext(); + WindowsConfigBuilder builder(context); + ViewControllerPtr controller{builder.LaunchEngine()}; + ASSERT_NE(controller, nullptr); + + // Run for 1 second, then shut down. + // + // TODO(cbracken): Support registring a native function we can use to + // determine that execution has made it to a specific point in the Dart + // code. https://github.com/flutter/flutter/issues/109242 + std::this_thread::sleep_for(std::chrono::seconds(1)); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/windows/testing/windows_test.cc b/shell/platform/windows/testing/windows_test.cc new file mode 100644 index 0000000000000..bd5bbb1c0012a --- /dev/null +++ b/shell/platform/windows/testing/windows_test.cc @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter 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 "flutter/shell/platform/windows/testing/windows_test.h" + +#include + +#include "flutter/shell/platform/windows/testing/windows_test_context.h" +#include "flutter/testing/testing.h" + +namespace flutter { +namespace testing { + +WindowsTest::WindowsTest() : context_(GetFixturesDirectory()) {} + +std::string WindowsTest::GetFixturesDirectory() const { + return GetFixturesPath(); +} + +WindowsTestContext& WindowsTest::GetContext() { + return context_; +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/windows/testing/windows_test.h b/shell/platform/windows/testing/windows_test.h new file mode 100644 index 0000000000000..21744d0ea8634 --- /dev/null +++ b/shell/platform/windows/testing/windows_test.h @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter 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 FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_WINDOWS_TEST_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_WINDOWS_TEST_H_ + +#include + +#include "flutter/fml/macros.h" +#include "flutter/shell/platform/windows/testing/windows_test_context.h" +#include "flutter/testing/thread_test.h" + +namespace flutter { +namespace testing { + +/// A GoogleTest test fixture for Windows tests. +/// +/// Supports looking up the test fixture data defined in the GN `test_fixtures` +/// associated with the unit test executable target. This typically includes +/// the kernel bytecode `kernel_blob.bin` compiled from the Dart file specified +/// in the test fixture's `dart_main` property, as well as any other data files +/// used in tests, such as image files used in a screenshot golden test. +/// +/// This test class can be used in GoogleTest tests using the standard +/// `TEST_F(WindowsTest, TestName)` macro. +class WindowsTest : public ThreadTest { + public: + WindowsTest(); + + // Returns the path to test fixture data such as kernel bytecode or images + // used by the C++ side of the test. + std::string GetFixturesDirectory() const; + + // Returns the test context associated with this fixture. + WindowsTestContext& GetContext(); + + private: + WindowsTestContext context_; + + FML_DISALLOW_COPY_AND_ASSIGN(WindowsTest); +}; + +} // namespace testing +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_WINDOWS_TEST_H_ diff --git a/shell/platform/windows/testing/windows_test_config_builder.cc b/shell/platform/windows/testing/windows_test_config_builder.cc new file mode 100644 index 0000000000000..69843ea5bb061 --- /dev/null +++ b/shell/platform/windows/testing/windows_test_config_builder.cc @@ -0,0 +1,87 @@ +// Copyright 2013 The Flutter 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 "flutter/shell/platform/windows/testing/windows_test_config_builder.h" + +#include + +#include +#include +#include + +#include "flutter/fml/logging.h" +#include "flutter/shell/platform/windows/public/flutter_windows.h" +#include "flutter/shell/platform/windows/testing/windows_test_context.h" + +namespace flutter { +namespace testing { + +WindowsConfigBuilder::WindowsConfigBuilder(WindowsTestContext& context) + : context_(context) {} + +WindowsConfigBuilder::~WindowsConfigBuilder() = default; + +void WindowsConfigBuilder::AddDartEntrypointArgument(std::string_view arg) { + if (arg.empty()) { + return; + } + + dart_entrypoint_arguments_.emplace_back(std::move(arg)); +} + +FlutterDesktopEngineProperties WindowsConfigBuilder::GetEngineProperties() + const { + FlutterDesktopEngineProperties engine_properties = {}; + engine_properties.assets_path = context_.GetAssetsPath().c_str(); + engine_properties.icu_data_path = context_.GetIcuDataPath().c_str(); + + // Set Dart entrypoint argc, argv. + std::vector dart_args; + dart_args.reserve(dart_entrypoint_arguments_.size()); + for (const auto& arg : dart_entrypoint_arguments_) { + dart_args.push_back(arg.c_str()); + } + if (!dart_args.empty()) { + engine_properties.dart_entrypoint_argv = dart_args.data(); + engine_properties.dart_entrypoint_argc = dart_args.size(); + } else { + // Clear this out in case this is not the first engine launch from the + // embedder config builder. + engine_properties.dart_entrypoint_argv = nullptr; + engine_properties.dart_entrypoint_argc = 0; + } + + return engine_properties; +} + +ViewControllerPtr WindowsConfigBuilder::LaunchEngine() const { + InitializeCOM(); + + EnginePtr engine = InitializeEngine(); + if (!engine) { + return {}; + } + + int width = 600; + int height = 400; + ViewControllerPtr controller( + FlutterDesktopViewControllerCreate(width, height, engine.release())); + if (!controller) { + return {}; + } + + return controller; +} + +void WindowsConfigBuilder::InitializeCOM() const { + FML_CHECK(SUCCEEDED(::CoInitializeEx(nullptr, COINIT_MULTITHREADED))); +} + +EnginePtr WindowsConfigBuilder::InitializeEngine() const { + FlutterDesktopEngineProperties engine_properties = GetEngineProperties(); + return EnginePtr(FlutterDesktopEngineCreate(&engine_properties)); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/windows/testing/windows_test_config_builder.h b/shell/platform/windows/testing/windows_test_config_builder.h new file mode 100644 index 0000000000000..0a1bfb400002e --- /dev/null +++ b/shell/platform/windows/testing/windows_test_config_builder.h @@ -0,0 +1,79 @@ +// Copyright 2013 The Flutter 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 FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_WINDOWS_TEST_CONFIG_BUILDER_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_WINDOWS_TEST_CONFIG_BUILDER_H_ + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/unique_object.h" +#include "flutter/shell/platform/windows/public/flutter_windows.h" +#include "flutter/shell/platform/windows/testing/windows_test_context.h" + +namespace flutter { +namespace testing { + +// Deleter for FlutterDesktopEngineRef objects. +struct EngineDeleter { + typedef FlutterDesktopEngineRef pointer; + void operator()(FlutterDesktopEngineRef engine) { + FML_CHECK(FlutterDesktopEngineDestroy(engine)); + } +}; + +// Unique pointer wrapper for FlutterDesktopEngineRef. +using EnginePtr = std::unique_ptr; + +// Deleter for FlutterViewControllerRef objects. +struct ViewControllerDeleter { + typedef FlutterDesktopViewControllerRef pointer; + void operator()(FlutterDesktopViewControllerRef engine) { + FlutterDesktopViewControllerDestroy(engine); + } +}; + +// Unique pointer wrapper for FlutterDesktopViewControllerRef. +using ViewControllerPtr = + std::unique_ptr; + +// Test configuration builder for WindowsTests. +// +// Utility class for configuring engine and view controller launch arguments, +// and launching the engine to run a test fixture. +class WindowsConfigBuilder { + public: + explicit WindowsConfigBuilder(WindowsTestContext& context); + ~WindowsConfigBuilder(); + + // Returns the desktop engine properties configured for this test. + FlutterDesktopEngineProperties GetEngineProperties() const; + + // Adds an argument to the Dart entrypoint arguments List. + void AddDartEntrypointArgument(std::string_view arg); + + // Returns a configured and initialized view controller running the default + // Dart entrypoint. + ViewControllerPtr LaunchEngine() const; + + private: + // Initialize COM, so that it is available for use in the library and/or + // plugins. + void InitializeCOM() const; + + // Returns a configured and initialized engine. + EnginePtr InitializeEngine() const; + + WindowsTestContext& context_; + std::vector dart_entrypoint_arguments_; + + FML_DISALLOW_COPY_AND_ASSIGN(WindowsConfigBuilder); +}; + +} // namespace testing +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_WINDOWS_TEST_CONFIG_BUILDER_H_ diff --git a/shell/platform/windows/testing/windows_test_context.cc b/shell/platform/windows/testing/windows_test_context.cc new file mode 100644 index 0000000000000..42da7c1651341 --- /dev/null +++ b/shell/platform/windows/testing/windows_test_context.cc @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter 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 "flutter/shell/platform/windows/testing/windows_test_context.h" + +#include "flutter/fml/platform/win/wstring_conversion.h" + +namespace flutter { +namespace testing { + +WindowsTestContext::WindowsTestContext(std::string_view assets_path) + : assets_path_(fml::Utf8ToWideString(assets_path)) {} + +WindowsTestContext::~WindowsTestContext() = default; + +const std::wstring& WindowsTestContext::GetAssetsPath() const { + return assets_path_; +} + +const std::wstring& WindowsTestContext::GetIcuDataPath() const { + return icu_data_path_; +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/windows/testing/windows_test_context.h b/shell/platform/windows/testing/windows_test_context.h new file mode 100644 index 0000000000000..bdc6fcc22a2de --- /dev/null +++ b/shell/platform/windows/testing/windows_test_context.h @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter 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 FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_WINDOWS_TEST_CONTEXT_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_WINDOWS_TEST_CONTEXT_H_ + +#include +#include + +#include "flutter/fml/macros.h" + +namespace flutter { +namespace testing { + +// Context associated with the current Windows test fixture. +// +// Context data includes global Flutter and Dart runtime context such as the +// path of Flutter's asset directory, ICU path, and resolvers for any +// registered native functions. +class WindowsTestContext { + public: + explicit WindowsTestContext(std::string_view assets_path = ""); + virtual ~WindowsTestContext(); + + const std::wstring& GetAssetsPath() const; + + const std::wstring& GetIcuDataPath() const; + + private: + std::wstring assets_path_; + std::wstring icu_data_path_ = L"icudtl.dat"; + + FML_DISALLOW_COPY_AND_ASSIGN(WindowsTestContext); +}; + +} // namespace testing +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_WINDOWS_TEST_CONTEXT_H_