Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support fixture tests for Windows embedder #35273

Merged
merged 1 commit into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions shell/platform/windows/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ shared_library("flutter_windows") {
}

test_fixtures("flutter_windows_fixtures") {
dart_main = "fixtures/main.dart"
fixtures = []
}

Expand Down Expand Up @@ -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",
Expand Down
7 changes: 7 additions & 0 deletions shell/platform/windows/fixtures/main.dart
Original file line number Diff line number Diff line change
@@ -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!');
}
20 changes: 19 additions & 1 deletion shell/platform/windows/flutter_windows_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
#include "flutter/shell/platform/windows/public/flutter_windows.h"

#include <cstring>
#include <thread>

#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"";
Expand All @@ -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
26 changes: 26 additions & 0 deletions shell/platform/windows/testing/windows_test.cc
Original file line number Diff line number Diff line change
@@ -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 <string>

#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
47 changes: 47 additions & 0 deletions shell/platform/windows/testing/windows_test.h
Original file line number Diff line number Diff line change
@@ -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 <string>

#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_
87 changes: 87 additions & 0 deletions shell/platform/windows/testing/windows_test_config_builder.cc
Original file line number Diff line number Diff line change
@@ -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 <combaseapi.h>

#include <string>
#include <string_view>
#include <vector>

#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<const char*> 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)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

@cbracken cbracken Aug 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the test, this is intentionally COINIT_MULTITHREADED to match what flutter_tester does:

#if defined(FML_OS_WIN)
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
#endif // defined(FML_OS_WIN)

Honestly, this is primarily to allow me to be a little bit lazy with COM initialization in the tests, rather than trigger COM initialisation on each thread in the test context we create that uses COM.

}

EnginePtr WindowsConfigBuilder::InitializeEngine() const {
FlutterDesktopEngineProperties engine_properties = GetEngineProperties();
return EnginePtr(FlutterDesktopEngineCreate(&engine_properties));
}

} // namespace testing
} // namespace flutter
79 changes: 79 additions & 0 deletions shell/platform/windows/testing/windows_test_config_builder.h
Original file line number Diff line number Diff line change
@@ -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 <string>
#include <string_view>
#include <vector>

#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<FlutterDesktopEngine, EngineDeleter>;

// Deleter for FlutterViewControllerRef objects.
struct ViewControllerDeleter {
typedef FlutterDesktopViewControllerRef pointer;
void operator()(FlutterDesktopViewControllerRef engine) {
FlutterDesktopViewControllerDestroy(engine);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this doesn't return a boolean which is why there's no FML_CHECK here.

}
};

// Unique pointer wrapper for FlutterDesktopViewControllerRef.
using ViewControllerPtr =
std::unique_ptr<FlutterDesktopViewControllerState, ViewControllerDeleter>;

// 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<String>.
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<std::string> dart_entrypoint_arguments_;

FML_DISALLOW_COPY_AND_ASSIGN(WindowsConfigBuilder);
};

} // namespace testing
} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_WINDOWS_TEST_CONFIG_BUILDER_H_
26 changes: 26 additions & 0 deletions shell/platform/windows/testing/windows_test_context.cc
Original file line number Diff line number Diff line change
@@ -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
40 changes: 40 additions & 0 deletions shell/platform/windows/testing/windows_test_context.h
Original file line number Diff line number Diff line change
@@ -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 <string>
#include <string_view>

#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";
Copy link
Member Author

@cbracken cbracken Aug 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From an API point of view, it would have been nice to use std::string, but since we're passing these to the engine (via the Windows C API), they need to have a lifetime that meets or exceeds that of the engine, so converting to local std::wstrings in, e.g. WindowsTestConfigBuilder::GetEngineProperties, would mean the engine would hold pointers to garbage memory.


FML_DISALLOW_COPY_AND_ASSIGN(WindowsTestContext);
};

} // namespace testing
} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_WINDOWS_TEST_CONTEXT_H_