Migrate existing embedder unit tests to use the fixture. (#8296)

Also allows tests to specify their own embedder contexts.
This commit is contained in:
Chinmay Garde 2019-03-25 15:10:24 -07:00 committed by GitHub
parent 345ae7d373
commit 22ee8ee08f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 237 additions and 187 deletions

View File

@ -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) {

View File

@ -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) {

View File

@ -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 {};

View File

@ -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<FlutterEngine, UniqueEngineTraits>;
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);
};

View File

@ -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<fml::Mapping> 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<fml::FileMapping> mapping;
if (executable) {
mapping = std::make_unique<fml::FileMapping>(
file, std::initializer_list<Prot>{Prot::kRead, Prot::kExecute});
} else {
mapping = std::make_unique<fml::FileMapping>(
file, std::initializer_list<Prot>{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<EmbedderContext*>(user_data)->FireIsolateCreateCallbacks();
};
}
void EmbedderContext::FireIsolateCreateCallbacks() {
for (auto closure : isolate_create_callbacks_) {
closure();
}
}
} // namespace testing
} // namespace shell

View File

@ -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 <memory>
#include <string>
#include <vector>
#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<fml::Mapping> vm_snapshot_data_;
std::unique_ptr<fml::Mapping> vm_snapshot_instructions_;
std::unique_ptr<fml::Mapping> isolate_snapshot_data_;
std::unique_ptr<fml::Mapping> isolate_snapshot_instructions_;
std::vector<fml::closure> 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_

View File

@ -7,32 +7,6 @@
namespace shell {
namespace testing {
static std::unique_ptr<fml::Mapping> 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<fml::FileMapping> mapping;
if (executable) {
mapping = std::make_unique<fml::FileMapping>(
file, std::initializer_list<Prot>{Prot::kRead, Prot::kExecute});
} else {
mapping = std::make_unique<fml::FileMapping>(
file, std::initializer_list<Prot>{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<EmbedderContext>(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

View File

@ -7,9 +7,8 @@
#include <memory>
#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<fml::Mapping> vm_snapshot_data_;
std::unique_ptr<fml::Mapping> vm_snapshot_instructions_;
std::unique_ptr<fml::Mapping> isolate_snapshot_data_;
std::unique_ptr<fml::Mapping> isolate_snapshot_instructions_;
std::unique_ptr<EmbedderContext> embedder_context_;
// |testing::Test|
void SetUp() override;

View File

@ -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<std::unique_ptr<fml::FileMapping>>& 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<fml::FileMapping> mapping;
if (executable) {
mapping = std::make_unique<fml::FileMapping>(
file, std::initializer_list<fml::FileMapping::Protection>{
fml::FileMapping::Protection::kRead,
fml::FileMapping::Protection::kExecute});
} else {
mapping = std::make_unique<fml::FileMapping>(
file, std::initializer_list<fml::FileMapping::Protection>{
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<char*>(data);
ASSERT_EQ(str_data, "Data");
};
fml::UniqueFD fixtures_dir = fml::OpenDirectory(
::testing::GetFixturesPath(), false, fml::FilePermission::kRead);
std::vector<std::unique_ptr<fml::FileMapping>> 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<char*>(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());