Skip to content

Commit

Permalink
Add Windows unit tests to plugin template (#118638)
Browse files Browse the repository at this point in the history
* Add Windows unit tests to plugin template

Adds an example native unit test to the plugin template for Windows,
matching the format we use for our 1P plugin example app unit tests.
Once these have been added for all platforms+languages, they will be
documented on a new plugin development page to explain their use.

Since we don't appear to be running our current plugin e2e tests for
Windows, this adds a new configuration to run them. I haven't
`led`-tested this, so it may not work, but this will give a starting
point for getting them running.

Part of #82458

* Minor fix

* Add test owner

* Fix typo

* Fix test feature flag
  • Loading branch information
stuartmorgan committed Jan 24, 2023
1 parent 4d25030 commit e3c51a2
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 3 deletions.
18 changes: 18 additions & 0 deletions .ci.yaml
Expand Up @@ -4364,6 +4364,24 @@ targets:
- bin/**
- .ci.yaml

- name: Windows plugin_test_windows
bringup: true # New task
recipe: devicelab/devicelab_drone
timeout: 60
properties:
dependencies: >-
[
{"dependency": "vs_build", "version": "version:vs2019"}
]
tags: >
["devicelab", "hostonly", "windows"]
task_name: plugin_test_windows
runIf:
- dev/**
- packages/flutter_tools/**
- bin/**
- .ci.yaml

- name: Windows run_debug_test_windows
recipe: devicelab/devicelab_drone
presubmit: false
Expand Down
1 change: 1 addition & 0 deletions TESTOWNERS
Expand Up @@ -251,6 +251,7 @@
/dev/devicelab/bin/tasks/plugin_lint_mac.dart @stuartmorgan @flutter/plugin
/dev/devicelab/bin/tasks/plugin_test_ios.dart @jmagman @flutter/ios
/dev/devicelab/bin/tasks/plugin_test_macos.dart @jmagman @flutter/desktop
/dev/devicelab/bin/tasks/plugin_test_windows.dart @stuartmorgan @flutter/desktop
/dev/devicelab/bin/tasks/plugin_test.dart @stuartmorgan @flutter/plugin
/dev/devicelab/bin/tasks/run_debug_test_android.dart @zanderso @flutter/tool
/dev/devicelab/bin/tasks/run_debug_test_linux.dart @loic-sharma @flutter/tool
Expand Down
16 changes: 16 additions & 0 deletions dev/devicelab/bin/tasks/plugin_test_windows.dart
@@ -0,0 +1,16 @@
// Copyright 2014 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.

import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/tasks/plugin_tests.dart';

Future<void> main() async {
await task(combine(<TaskFunction>[
PluginTest('windows', <String>['--platforms=windows']).call,
// Test that Dart-only plugins are supported.
PluginTest('windows', <String>['--platforms=windows'], dartOnlyPlugin: true).call,
// Test that FFI plugins are supported.
PluginTest('windows', <String>['--platforms=windows'], template: 'plugin_ffi').call,
]));
}
8 changes: 8 additions & 0 deletions dev/devicelab/lib/tasks/plugin_tests.dart
Expand Up @@ -274,6 +274,14 @@ public class $pluginClass: NSObject, FlutterPlugin {
)) {
throw TaskResult.failure('Platform unit tests failed');
}
} else if (buildTarget == 'windows') {
if (await exec(
path.join(rootPath, 'build', 'windows', 'plugins', 'plugintest', 'Release', 'plugintest_plugin_test'),
<String>[],
canFail: true,
) != 0) {
throw TaskResult.failure('Platform unit tests failed');
}
}
}

Expand Down
Expand Up @@ -52,6 +52,11 @@ add_subdirectory(${FLUTTER_MANAGED_DIR})
# Application build; see runner/CMakeLists.txt.
add_subdirectory("runner")

{{#withPlatformChannelPluginHook}}
# Enable the test target.
set(include_{{pluginProjectName}}_tests TRUE)
{{/withPlatformChannelPluginHook}}

# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
Expand Down
Expand Up @@ -51,3 +51,46 @@ set({{projectName}}_bundled_libraries
""
PARENT_SCOPE
)

# === Tests ===
# These unit tests can be run from a terminal after building the example, or
# from Visual Studio after opening the generated solution file.

# Only enable test builds when building the example (which sets this variable)
# so that plugin clients aren't building the tests.
if (${include_${PROJECT_NAME}_tests})
set(TEST_RUNNER "${PROJECT_NAME}_test")
enable_testing()

# Add the Google Test dependency.
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/release-1.11.0.zip
)
# Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Disable install commands for gtest so it doesn't end up in the bundle.
set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE)
FetchContent_MakeAvailable(googletest)

# The plugin's C API is not very useful for unit testing, so build the sources
# directly into the test binary rather than using the DLL.
add_executable(${TEST_RUNNER}
test/{{pluginClassSnakeCase}}_test.cpp
${PLUGIN_SOURCES}
)
apply_standard_settings(${TEST_RUNNER})
target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin)
target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock)
# flutter_wrapper_plugin has link dependencies on the Flutter DLL.
add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${FLUTTER_LIBRARY}" $<TARGET_FILE_DIR:${TEST_RUNNER}>
)

# Enable automatic test discovery.
include(GoogleTest)
gtest_discover_tests(${TEST_RUNNER})
endif()
Expand Up @@ -20,7 +20,6 @@ class {{pluginClass}} : public flutter::Plugin {
{{pluginClass}}(const {{pluginClass}}&) = delete;
{{pluginClass}}& operator=(const {{pluginClass}}&) = delete;

private:
// Called when a method is called on this plugin's channel from Dart.
void HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue> &method_call,
Expand Down
@@ -0,0 +1,43 @@
#include <flutter/method_call.h>
#include <flutter/method_result_functions.h>
#include <flutter/standard_method_codec.h>
#include <gtest/gtest.h>
#include <windows.h>

#include <memory>
#include <string>
#include <variant>

#include "{{pluginClassSnakeCase}}.h"

namespace {{projectName}} {
namespace test {

namespace {

using flutter::EncodableMap;
using flutter::EncodableValue;
using flutter::MethodCall;
using flutter::MethodResultFunctions;

} // namespace

TEST({{pluginClass}}, GetPlatformVersion) {
{{pluginClass}} plugin;
// Save the reply value from the success callback.
std::string result_string;
plugin.HandleMethodCall(
MethodCall("getPlatformVersion", std::make_unique<EncodableValue>()),
std::make_unique<MethodResultFunctions<>>(
[&result_string](const EncodableValue* result) {
result_string = std::get<std::string>(*result);
},
nullptr, nullptr));

// Since the exact string varies by host, just ensure that it's a string
// with the expected format.
EXPECT_TRUE(result_string.rfind("Windows ", 0) == 0);
}

} // namespace test
} // namespace {{projectName}}
1 change: 1 addition & 0 deletions packages/flutter_tools/templates/template_manifest.json
Expand Up @@ -278,6 +278,7 @@
"templates/plugin/test/projectName_method_channel_test.dart.tmpl",
"templates/plugin/windows.tmpl/CMakeLists.txt.tmpl",
"templates/plugin/windows.tmpl/include/projectName.tmpl/pluginClassSnakeCase_c_api.h.tmpl",
"templates/plugin/windows.tmpl/test/pluginClassSnakeCase_test.cpp.tmpl",
"templates/plugin/windows.tmpl/pluginClassSnakeCase.cpp.tmpl",
"templates/plugin/windows.tmpl/pluginClassSnakeCase.h.tmpl",
"templates/plugin/windows.tmpl/pluginClassSnakeCase_c_api.cpp.tmpl",
Expand Down
Expand Up @@ -2429,7 +2429,7 @@ void main() {
androidIdentifier: 'com.example.flutter_project');
});

testUsingContext('plugin includes native unit tests', () async {
testUsingContext('plugin includes native Swift unit tests', () async {
Cache.flutterRoot = '../..';

final CreateCommand command = CreateCommand();
Expand All @@ -2452,7 +2452,7 @@ void main() {
Logger: () => logger,
});

testUsingContext('plugin includes native Ojb-C unit tests', () async {
testUsingContext('plugin includes native Objective-C unit tests', () async {
Cache.flutterRoot = '../..';

final CreateCommand command = CreateCommand();
Expand All @@ -2476,6 +2476,28 @@ void main() {
Logger: () => logger,
});

testUsingContext('plugin includes native Windows unit tests', () async {
Cache.flutterRoot = '../..';

final CreateCommand command = CreateCommand();
final CommandRunner<void> runner = createTestCommandRunner(command);

await runner.run(<String>[
'create',
'--no-pub',
'--template=plugin',
'--platforms=windows',
projectDir.path]);

expect(projectDir
.childDirectory('windows')
.childDirectory('test')
.childFile('flutter_project_plugin_test.cpp'), exists);
}, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
Logger: () => logger,
});

testUsingContext('create a module with --platforms throws error.', () async {
Cache.flutterRoot = '../..';

Expand Down

0 comments on commit e3c51a2

Please sign in to comment.