未验证 提交 d6ead18f 编写于 作者: C Chinmay Garde 提交者: GitHub

Make it easy to write embedder unit tests by creating a fixture and config builder. (#8276)

All embedder unit-tests have to setup the Flutter project arguments from scratch
before launching the engine. The boilerplate and having to deal with the low
level C API during each engine launch is a hinderance to writing tests.

This patch introduces an EmbedderTest fixture that sets up all the embedder side snapshots before allowing the unit test to create a FlutterConfigBuilder` that
the test can use to incrementally build and edit the Flutter project
configuration. From the given state state of a configuration, multiple engines
can be launched with their lifecylces managed by appropriate RAII wrappers.

This allows the a fully configured Flutter engine to be launched using 4 lines
of code in a fixture.

```
EmbedderConfigBuilder builder;
builder.SetSoftwareRendererConfig();
builder.SetAssetsPathFromFixture(this);
builder.SetSnapshotsFromFixture(this);
auto engine = builder.LaunchEngine();
```
上级 3a445cec
......@@ -68,6 +68,10 @@ executable("embedder_unittests") {
include_dirs = [ "." ]
sources = [
"tests/embedder_config_builder.cc",
"tests/embedder_config_builder.h",
"tests/embedder_test.cc",
"tests/embedder_test.h",
"tests/embedder_unittests.cc",
]
......
// 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_config_builder.h"
namespace shell {
namespace testing {
EmbedderConfigBuilder::EmbedderConfigBuilder() {
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; };
}
EmbedderConfigBuilder::~EmbedderConfigBuilder() = default;
void EmbedderConfigBuilder::SetSoftwareRendererConfig() {
renderer_config_.type = FlutterRendererType::kSoftware;
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::SetSnapshotsFromFixture(
const EmbedderTest* fixture) {
if (auto mapping = fixture->GetVMSnapshotData()) {
project_args_.vm_snapshot_data = mapping->GetMapping();
project_args_.vm_snapshot_data_size = mapping->GetSize();
}
if (auto mapping = fixture->GetVMSnapshotInstructions()) {
project_args_.vm_snapshot_instructions = mapping->GetMapping();
project_args_.vm_snapshot_instructions_size = mapping->GetSize();
}
if (auto mapping = fixture->GetIsolateSnapshotData()) {
project_args_.isolate_snapshot_data = mapping->GetMapping();
project_args_.isolate_snapshot_data_size = mapping->GetSize();
}
if (auto mapping = fixture->GetIsolateSnapshotInstructions()) {
project_args_.isolate_snapshot_instructions = mapping->GetMapping();
project_args_.isolate_snapshot_instructions_size = mapping->GetSize();
}
}
UniqueEngine EmbedderConfigBuilder::LaunchEngine(void* user_data) const {
FlutterEngine engine = nullptr;
auto result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &renderer_config_,
&project_args_, user_data, &engine);
if (result != kSuccess) {
return {};
}
return UniqueEngine{engine};
}
} // namespace testing
} // namespace shell
// 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_CONFIG_BUILDER_H_
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONFIG_BUILDER_H_
#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_test.h"
namespace shell {
namespace testing {
struct UniqueEngineTraits {
static FlutterEngine InvalidValue() { return nullptr; }
static bool IsValid(const FlutterEngine& value) { return value != nullptr; }
static void Free(FlutterEngine engine) {
auto result = FlutterEngineShutdown(engine);
FML_CHECK(result == kSuccess);
}
};
using UniqueEngine = fml::UniqueObject<FlutterEngine, UniqueEngineTraits>;
class EmbedderConfigBuilder {
public:
EmbedderConfigBuilder();
~EmbedderConfigBuilder();
void SetSoftwareRendererConfig();
void SetAssetsPathFromFixture(const EmbedderTest* fixture);
void SetSnapshotsFromFixture(const EmbedderTest* fixture);
UniqueEngine LaunchEngine(void* user_data = nullptr) const;
private:
FlutterProjectArgs project_args_ = {};
FlutterRendererConfig renderer_config_ = {};
FlutterSoftwareRendererConfig software_renderer_config_ = {};
std::string assets_path_;
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderConfigBuilder);
};
} // namespace testing
} // namespace shell
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONFIG_BUILDER_H_
// 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_test.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;
}
EmbedderTest::EmbedderTest() = default;
EmbedderTest::~EmbedderTest() = default;
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();
}
// |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);
}
// |testing::Test|
void EmbedderTest::TearDown() {
vm_snapshot_data_.reset();
vm_snapshot_instructions_.reset();
isolate_snapshot_data_.reset();
isolate_snapshot_instructions_.reset();
}
} // namespace testing
} // namespace shell
// 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_TEST_H_
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_H_
#include <memory>
#include "flutter/fml/file.h"
#include "flutter/fml/macros.h"
#include "flutter/fml/mapping.h"
#include "flutter/testing/testing.h"
namespace shell {
namespace testing {
class EmbedderTest : public ::testing::Test {
public:
EmbedderTest();
~EmbedderTest() override;
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;
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_;
// |testing::Test|
void SetUp() override;
// |testing::Test|
void TearDown() override;
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTest);
};
} // namespace testing
} // namespace shell
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_H_
......@@ -6,16 +6,20 @@
#include "embedder.h"
#include "flutter/fml/file.h"
#include "flutter/fml/mapping.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"
namespace {
namespace shell {
namespace testing {
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) {
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;
......@@ -34,8 +38,6 @@ void MapAOTAsset(std::vector<std::unique_ptr<fml::FileMapping>>& aot_mappings,
aot_mappings.emplace_back(std::move(mapping));
}
} // anonymous namespace
TEST(EmbedderTest, MustNotRunWithInvalidArgs) {
FlutterEngine engine = nullptr;
FlutterRendererConfig config = {};
......@@ -58,14 +60,14 @@ TEST(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) {
FlutterProjectArgs args = {};
args.struct_size = sizeof(FlutterProjectArgs);
args.assets_path = testing::GetFixturesPath();
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);
::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,
......@@ -90,3 +92,32 @@ TEST(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) {
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);
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
}
TEST_F(EmbedderFixture, CanLaunchAndShutdownWithFixtureMultipleTimes) {
EmbedderConfigBuilder builder;
builder.SetSoftwareRendererConfig();
builder.SetAssetsPathFromFixture(this);
builder.SetSnapshotsFromFixture(this);
for (size_t i = 0; i < 100; ++i) {
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
FML_LOG(INFO) << "Engine launch count: " << i + 1;
}
}
} // namespace testing
} // namespace shell
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册