From 22ee8ee08f6a25a32270813e28442e3a3ec00127 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 25 Mar 2019 15:10:24 -0700 Subject: [PATCH] Migrate existing embedder unit tests to use the fixture. (#8296) Also allows tests to specify their own embedder contexts. --- shell/platform/embedder/BUILD.gn | 5 + shell/platform/embedder/embedder.cc | 9 +- .../embedder/tests/embedder_config_builder.cc | 39 +++--- .../embedder/tests/embedder_config_builder.h | 20 +++- .../embedder/tests/embedder_context.cc | 95 +++++++++++++++ .../embedder/tests/embedder_context.h | 59 +++++++++ .../platform/embedder/tests/embedder_test.cc | 67 ++--------- shell/platform/embedder/tests/embedder_test.h | 18 +-- .../embedder/tests/embedder_unittests.cc | 112 +++--------------- 9 files changed, 237 insertions(+), 187 deletions(-) create mode 100644 shell/platform/embedder/tests/embedder_context.cc create mode 100644 shell/platform/embedder/tests/embedder_context.h diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index 1b7a81b8445..5df39518c8b 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -70,6 +70,8 @@ executable("embedder_unittests") { sources = [ "tests/embedder_config_builder.cc", "tests/embedder_config_builder.h", + "tests/embedder_context.cc", + "tests/embedder_context.h", "tests/embedder_test.cc", "tests/embedder_test.h", "tests/embedder_unittests.cc", @@ -78,7 +80,10 @@ executable("embedder_unittests") { deps = [ ":embedder", ":fixtures", + "$flutter_root/runtime", "$flutter_root/testing", + "//third_party/skia", + "//third_party/tonic", ] if (is_linux) { diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 65aa88808f8..003f40113c4 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -52,14 +52,17 @@ extern const intptr_t kPlatformStrongDillSize; static FlutterEngineResult LogEmbedderError(FlutterEngineResult code, const char* name, - const char* function) { + const char* function, + const char* file, + int line) { FML_LOG(ERROR) << "Returning error '" << name << "' (" << code << ") from Flutter Embedder API call to '" << __FUNCTION__ - << "'."; + << "'. Origin: " << file << ":" << line; return code; } -#define LOG_EMBEDDER_ERROR(code) LogEmbedderError(code, #code, __FUNCTION__) +#define LOG_EMBEDDER_ERROR(code) \ + LogEmbedderError(code, #code, __FUNCTION__, __FILE__, __LINE__) static bool IsOpenGLRendererConfigValid(const FlutterRendererConfig* config) { if (config->type != kOpenGL) { diff --git a/shell/platform/embedder/tests/embedder_config_builder.cc b/shell/platform/embedder/tests/embedder_config_builder.cc index 108535ea8b6..c93886b1133 100644 --- a/shell/platform/embedder/tests/embedder_config_builder.cc +++ b/shell/platform/embedder/tests/embedder_config_builder.cc @@ -7,12 +7,21 @@ namespace shell { namespace testing { -EmbedderConfigBuilder::EmbedderConfigBuilder() { +EmbedderConfigBuilder::EmbedderConfigBuilder( + EmbedderContext& context, + InitializationPreference preference) + : context_(context) { project_args_.struct_size = sizeof(project_args_); - software_renderer_config_.struct_size = sizeof(FlutterSoftwareRendererConfig); software_renderer_config_.surface_present_callback = [](void*, const void*, size_t, size_t) { return true; }; + + if (preference == InitializationPreference::kInitialize) { + SetSoftwareRendererConfig(); + SetAssetsPath(); + SetSnapshots(); + SetIsolateCreateCallbackHook(); + } } EmbedderConfigBuilder::~EmbedderConfigBuilder() = default; @@ -22,39 +31,41 @@ void EmbedderConfigBuilder::SetSoftwareRendererConfig() { renderer_config_.software = software_renderer_config_; } -void EmbedderConfigBuilder::SetAssetsPathFromFixture( - const EmbedderTest* fixture) { - assets_path_ = fixture->GetAssetsPath(); - project_args_.assets_path = assets_path_.c_str(); +void EmbedderConfigBuilder::SetAssetsPath() { + project_args_.assets_path = context_.GetAssetsPath().c_str(); } -void EmbedderConfigBuilder::SetSnapshotsFromFixture( - const EmbedderTest* fixture) { - if (auto mapping = fixture->GetVMSnapshotData()) { +void EmbedderConfigBuilder::SetSnapshots() { + if (auto mapping = context_.GetVMSnapshotData()) { project_args_.vm_snapshot_data = mapping->GetMapping(); project_args_.vm_snapshot_data_size = mapping->GetSize(); } - if (auto mapping = fixture->GetVMSnapshotInstructions()) { + if (auto mapping = context_.GetVMSnapshotInstructions()) { project_args_.vm_snapshot_instructions = mapping->GetMapping(); project_args_.vm_snapshot_instructions_size = mapping->GetSize(); } - if (auto mapping = fixture->GetIsolateSnapshotData()) { + if (auto mapping = context_.GetIsolateSnapshotData()) { project_args_.isolate_snapshot_data = mapping->GetMapping(); project_args_.isolate_snapshot_data_size = mapping->GetSize(); } - if (auto mapping = fixture->GetIsolateSnapshotInstructions()) { + if (auto mapping = context_.GetIsolateSnapshotInstructions()) { project_args_.isolate_snapshot_instructions = mapping->GetMapping(); project_args_.isolate_snapshot_instructions_size = mapping->GetSize(); } } -UniqueEngine EmbedderConfigBuilder::LaunchEngine(void* user_data) const { +void EmbedderConfigBuilder::SetIsolateCreateCallbackHook() { + project_args_.root_isolate_create_callback = + EmbedderContext::GetIsolateCreateCallbackHook(); +} + +UniqueEngine EmbedderConfigBuilder::LaunchEngine() const { FlutterEngine engine = nullptr; auto result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &renderer_config_, - &project_args_, user_data, &engine); + &project_args_, &context_, &engine); if (result != kSuccess) { return {}; diff --git a/shell/platform/embedder/tests/embedder_config_builder.h b/shell/platform/embedder/tests/embedder_config_builder.h index a474f3e3bc8..34dca1aed9f 100644 --- a/shell/platform/embedder/tests/embedder_config_builder.h +++ b/shell/platform/embedder/tests/embedder_config_builder.h @@ -8,6 +8,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/unique_object.h" #include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/tests/embedder_context.h" #include "flutter/shell/platform/embedder/tests/embedder_test.h" namespace shell { @@ -28,23 +29,32 @@ using UniqueEngine = fml::UniqueObject; class EmbedderConfigBuilder { public: - EmbedderConfigBuilder(); + enum class InitializationPreference { + kInitialize, + kNoInitialize, + }; + + EmbedderConfigBuilder(EmbedderContext& context, + InitializationPreference preference = + InitializationPreference::kInitialize); ~EmbedderConfigBuilder(); void SetSoftwareRendererConfig(); - void SetAssetsPathFromFixture(const EmbedderTest* fixture); + void SetAssetsPath(); - void SetSnapshotsFromFixture(const EmbedderTest* fixture); + void SetSnapshots(); - UniqueEngine LaunchEngine(void* user_data = nullptr) const; + void SetIsolateCreateCallbackHook(); + + UniqueEngine LaunchEngine() const; private: + EmbedderContext& context_; FlutterProjectArgs project_args_ = {}; FlutterRendererConfig renderer_config_ = {}; FlutterSoftwareRendererConfig software_renderer_config_ = {}; - std::string assets_path_; FML_DISALLOW_COPY_AND_ASSIGN(EmbedderConfigBuilder); }; diff --git a/shell/platform/embedder/tests/embedder_context.cc b/shell/platform/embedder/tests/embedder_context.cc new file mode 100644 index 00000000000..57b4335e661 --- /dev/null +++ b/shell/platform/embedder/tests/embedder_context.cc @@ -0,0 +1,95 @@ +// 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/embedder/tests/embedder_context.h" + +#include "flutter/runtime/dart_vm.h" + +namespace shell { +namespace testing { + +static std::unique_ptr GetMapping(const fml::UniqueFD& directory, + const char* path, + bool executable) { + fml::UniqueFD file = fml::OpenFile(directory, path, false /* create */, + fml::FilePermission::kRead); + if (!file.is_valid()) { + return nullptr; + } + + using Prot = fml::FileMapping::Protection; + std::unique_ptr mapping; + if (executable) { + mapping = std::make_unique( + file, std::initializer_list{Prot::kRead, Prot::kExecute}); + } else { + mapping = std::make_unique( + file, std::initializer_list{Prot::kRead}); + } + + if (mapping->GetSize() == 0 || mapping->GetMapping() == nullptr) { + return nullptr; + } + + return mapping; +} + +EmbedderContext::EmbedderContext(std::string assets_path) + : assets_path_(std::move(assets_path)) { + auto assets_dir = fml::OpenDirectory(assets_path_.c_str(), false, + fml::FilePermission::kRead); + vm_snapshot_data_ = GetMapping(assets_dir, "vm_snapshot_data", false); + isolate_snapshot_data_ = + GetMapping(assets_dir, "isolate_snapshot_data", false); + + if (blink::DartVM::IsRunningPrecompiledCode()) { + vm_snapshot_instructions_ = + GetMapping(assets_dir, "vm_snapshot_instr", true); + isolate_snapshot_instructions_ = + GetMapping(assets_dir, "isolate_snapshot_instr", true); + } +} + +EmbedderContext::~EmbedderContext() = default; + +const std::string& EmbedderContext::GetAssetsPath() const { + return assets_path_; +} + +const fml::Mapping* EmbedderContext::GetVMSnapshotData() const { + return vm_snapshot_data_.get(); +} + +const fml::Mapping* EmbedderContext::GetVMSnapshotInstructions() const { + return vm_snapshot_instructions_.get(); +} + +const fml::Mapping* EmbedderContext::GetIsolateSnapshotData() const { + return isolate_snapshot_data_.get(); +} + +const fml::Mapping* EmbedderContext::GetIsolateSnapshotInstructions() const { + return isolate_snapshot_instructions_.get(); +} + +void EmbedderContext::AddIsolateCreateCallback(fml::closure closure) { + if (closure) { + isolate_create_callbacks_.push_back(closure); + } +} + +VoidCallback EmbedderContext::GetIsolateCreateCallbackHook() { + return [](void* user_data) { + reinterpret_cast(user_data)->FireIsolateCreateCallbacks(); + }; +} + +void EmbedderContext::FireIsolateCreateCallbacks() { + for (auto closure : isolate_create_callbacks_) { + closure(); + } +} + +} // namespace testing +} // namespace shell diff --git a/shell/platform/embedder/tests/embedder_context.h b/shell/platform/embedder/tests/embedder_context.h new file mode 100644 index 00000000000..63aec332b2a --- /dev/null +++ b/shell/platform/embedder/tests/embedder_context.h @@ -0,0 +1,59 @@ +// 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_EMBEDDER_TESTS_EMBEDDER_CONTEXT_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONTEXT_H_ + +#include +#include +#include + +#include "flutter/fml/closure.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "flutter/shell/platform/embedder/embedder.h" + +namespace shell { +namespace testing { + +class EmbedderContext { + public: + EmbedderContext(std::string assets_path = ""); + + ~EmbedderContext(); + + const std::string& GetAssetsPath() const; + + const fml::Mapping* GetVMSnapshotData() const; + + const fml::Mapping* GetVMSnapshotInstructions() const; + + const fml::Mapping* GetIsolateSnapshotData() const; + + const fml::Mapping* GetIsolateSnapshotInstructions() const; + + void AddIsolateCreateCallback(fml::closure closure); + + private: + // This allows the builder to access the hooks. + friend class EmbedderConfigBuilder; + + std::string assets_path_; + std::unique_ptr vm_snapshot_data_; + std::unique_ptr vm_snapshot_instructions_; + std::unique_ptr isolate_snapshot_data_; + std::unique_ptr isolate_snapshot_instructions_; + std::vector isolate_create_callbacks_; + + static VoidCallback GetIsolateCreateCallbackHook(); + + void FireIsolateCreateCallbacks(); + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderContext); +}; + +} // namespace testing +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONTEXT_H_ diff --git a/shell/platform/embedder/tests/embedder_test.cc b/shell/platform/embedder/tests/embedder_test.cc index 044aacc4dfc..b23e7959674 100644 --- a/shell/platform/embedder/tests/embedder_test.cc +++ b/shell/platform/embedder/tests/embedder_test.cc @@ -7,32 +7,6 @@ namespace shell { namespace testing { -static std::unique_ptr GetMapping(const fml::UniqueFD& directory, - const char* path, - bool executable) { - fml::UniqueFD file = fml::OpenFile(directory, path, false /* create */, - fml::FilePermission::kRead); - if (!file.is_valid()) { - return nullptr; - } - - using Prot = fml::FileMapping::Protection; - std::unique_ptr mapping; - if (executable) { - mapping = std::make_unique( - file, std::initializer_list{Prot::kRead, Prot::kExecute}); - } else { - mapping = std::make_unique( - file, std::initializer_list{Prot::kRead}); - } - - if (mapping->GetSize() == 0 || mapping->GetMapping() == nullptr) { - return nullptr; - } - - return mapping; -} - EmbedderTest::EmbedderTest() = default; EmbedderTest::~EmbedderTest() = default; @@ -41,45 +15,24 @@ std::string EmbedderTest::GetFixturesDirectory() const { return ::testing::GetFixturesPath(); } -std::string EmbedderTest::GetAssetsPath() const { - return GetFixturesDirectory(); -} - -const fml::Mapping* EmbedderTest::GetVMSnapshotData() const { - return vm_snapshot_data_.get(); -} - -const fml::Mapping* EmbedderTest::GetVMSnapshotInstructions() const { - return vm_snapshot_instructions_.get(); -} - -const fml::Mapping* EmbedderTest::GetIsolateSnapshotData() const { - return isolate_snapshot_data_.get(); -} - -const fml::Mapping* EmbedderTest::GetIsolateSnapshotInstructions() const { - return isolate_snapshot_instructions_.get(); +EmbedderContext& EmbedderTest::GetEmbedderContext() { + // Setup the embedder context lazily instead of in the SetUp method because we + // don't to do all the work if the test won't end up using context. + if (!embedder_context_) { + embedder_context_ = + std::make_unique(GetFixturesDirectory()); + } + return *embedder_context_; } // |testing::Test| void EmbedderTest::SetUp() { - auto fixures_dir = fml::OpenDirectory(GetFixturesDirectory().c_str(), false, - fml::FilePermission::kRead); - vm_snapshot_data_ = GetMapping(fixures_dir, "vm_snapshot_data", false); - vm_snapshot_instructions_ = - GetMapping(fixures_dir, "vm_snapshot_instr", true); - isolate_snapshot_data_ = - GetMapping(fixures_dir, "isolate_snapshot_data", false); - isolate_snapshot_instructions_ = - GetMapping(fixures_dir, "isolate_snapshot_instr", true); + // Nothing to do here since we will lazily setup the context when asked. } // |testing::Test| void EmbedderTest::TearDown() { - vm_snapshot_data_.reset(); - vm_snapshot_instructions_.reset(); - isolate_snapshot_data_.reset(); - isolate_snapshot_instructions_.reset(); + embedder_context_.reset(); } } // namespace testing diff --git a/shell/platform/embedder/tests/embedder_test.h b/shell/platform/embedder/tests/embedder_test.h index f861e3b7267..f0850bf6f8a 100644 --- a/shell/platform/embedder/tests/embedder_test.h +++ b/shell/platform/embedder/tests/embedder_test.h @@ -7,9 +7,8 @@ #include -#include "flutter/fml/file.h" #include "flutter/fml/macros.h" -#include "flutter/fml/mapping.h" +#include "flutter/shell/platform/embedder/tests/embedder_context.h" #include "flutter/testing/testing.h" namespace shell { @@ -23,21 +22,10 @@ class EmbedderTest : public ::testing::Test { std::string GetFixturesDirectory() const; - std::string GetAssetsPath() const; - - const fml::Mapping* GetVMSnapshotData() const; - - const fml::Mapping* GetVMSnapshotInstructions() const; - - const fml::Mapping* GetIsolateSnapshotData() const; - - const fml::Mapping* GetIsolateSnapshotInstructions() const; + EmbedderContext& GetEmbedderContext(); private: - std::unique_ptr vm_snapshot_data_; - std::unique_ptr vm_snapshot_instructions_; - std::unique_ptr isolate_snapshot_data_; - std::unique_ptr isolate_snapshot_instructions_; + std::unique_ptr embedder_context_; // |testing::Test| void SetUp() override; diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 7415183eb67..eda64316a01 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -6,6 +6,7 @@ #include "embedder.h" #include "flutter/fml/file.h" #include "flutter/fml/mapping.h" +#include "flutter/fml/synchronization/waitable_event.h" #include "flutter/shell/platform/embedder/tests/embedder_config_builder.h" #include "flutter/shell/platform/embedder/tests/embedder_test.h" #include "flutter/testing/testing.h" @@ -13,105 +14,30 @@ namespace shell { namespace testing { -static void MapAOTAsset( - std::vector>& aot_mappings, - const fml::UniqueFD& fixtures_dir, - const char* path, - bool executable, - const uint8_t** data, - size_t* size) { - fml::UniqueFD file = - fml::OpenFile(fixtures_dir, path, false, fml::FilePermission::kRead); - std::unique_ptr mapping; - if (executable) { - mapping = std::make_unique( - file, std::initializer_list{ - fml::FileMapping::Protection::kRead, - fml::FileMapping::Protection::kExecute}); - } else { - mapping = std::make_unique( - file, std::initializer_list{ - fml::FileMapping::Protection::kRead}); - } - *data = mapping->GetMapping(); - *size = mapping->GetSize(); - aot_mappings.emplace_back(std::move(mapping)); +using EmbedderTest = testing::EmbedderTest; + +TEST(EmbedderTestNoFixture, MustNotRunWithInvalidArgs) { + EmbedderContext context; + EmbedderConfigBuilder builder( + context, EmbedderConfigBuilder::InitializationPreference::kNoInitialize); + auto engine = builder.LaunchEngine(); + ASSERT_FALSE(engine.is_valid()); } -TEST(EmbedderTest, MustNotRunWithInvalidArgs) { - FlutterEngine engine = nullptr; - FlutterRendererConfig config = {}; - FlutterProjectArgs args = {}; - FlutterEngineResult result = FlutterEngineRun(FLUTTER_ENGINE_VERSION + 1, - &config, &args, NULL, &engine); - ASSERT_NE(result, FlutterEngineResult::kSuccess); -} - -TEST(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) { - FlutterSoftwareRendererConfig renderer; - renderer.struct_size = sizeof(FlutterSoftwareRendererConfig); - renderer.surface_present_callback = [](void*, const void*, size_t, size_t) { - return false; - }; - - FlutterRendererConfig config = {}; - config.type = FlutterRendererType::kSoftware; - config.software = renderer; - - FlutterProjectArgs args = {}; - args.struct_size = sizeof(FlutterProjectArgs); - args.assets_path = ::testing::GetFixturesPath(); - args.root_isolate_create_callback = [](void* data) { - std::string str_data = reinterpret_cast(data); - ASSERT_EQ(str_data, "Data"); - }; - - fml::UniqueFD fixtures_dir = fml::OpenDirectory( - ::testing::GetFixturesPath(), false, fml::FilePermission::kRead); - std::vector> aot_mappings; - if (fml::FileExists(fixtures_dir, "vm_snapshot_data")) { - MapAOTAsset(aot_mappings, fixtures_dir, "vm_snapshot_data", false, - &args.vm_snapshot_data, &args.vm_snapshot_data_size); - MapAOTAsset(aot_mappings, fixtures_dir, "vm_snapshot_instr", true, - &args.vm_snapshot_instructions, - &args.vm_snapshot_instructions_size); - MapAOTAsset(aot_mappings, fixtures_dir, "isolate_snapshot_data", false, - &args.isolate_snapshot_data, &args.isolate_snapshot_data_size); - MapAOTAsset(aot_mappings, fixtures_dir, "isolate_snapshot_instr", true, - &args.isolate_snapshot_instructions, - &args.isolate_snapshot_instructions_size); - } - - std::string str_data = "Data"; - void* user_data = const_cast(str_data.c_str()); - FlutterEngine engine = nullptr; - FlutterEngineResult result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, - &args, user_data, &engine); - ASSERT_EQ(result, FlutterEngineResult::kSuccess); - - result = FlutterEngineShutdown(engine); - ASSERT_EQ(result, FlutterEngineResult::kSuccess); -} - -using EmbedderFixture = testing::EmbedderTest; - -TEST_F(EmbedderFixture, CanLaunchAndShutdownWithFixture) { - EmbedderConfigBuilder builder; - - builder.SetSoftwareRendererConfig(); - builder.SetAssetsPathFromFixture(this); - builder.SetSnapshotsFromFixture(this); - +TEST_F(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) { + auto& context = GetEmbedderContext(); + fml::AutoResetWaitableEvent latch; + context.AddIsolateCreateCallback([&latch]() { latch.Signal(); }); + EmbedderConfigBuilder builder(context); auto engine = builder.LaunchEngine(); ASSERT_TRUE(engine.is_valid()); + // Wait for the root isolate to launch. + latch.Wait(); + engine.reset(); } -TEST_F(EmbedderFixture, CanLaunchAndShutdownWithFixtureMultipleTimes) { - EmbedderConfigBuilder builder; - - builder.SetSoftwareRendererConfig(); - builder.SetAssetsPathFromFixture(this); - builder.SetSnapshotsFromFixture(this); +TEST_F(EmbedderTest, CanLaunchAndShutdownMultipleTimes) { + EmbedderConfigBuilder builder(GetEmbedderContext()); for (size_t i = 0; i < 100; ++i) { auto engine = builder.LaunchEngine(); ASSERT_TRUE(engine.is_valid());