Unverified Commit e3c51a2f authored by stuartmorgan's avatar stuartmorgan Committed by GitHub

Add Windows unit tests to plugin template (#118638)

* 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 https://github.com/flutter/flutter/issues/82458

* Minor fix

* Add test owner

* Fix typo

* Fix test feature flag
parent 4d250302
......@@ -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
......
......@@ -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
......
// 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,
]));
}
......@@ -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');
}
}
}
......
......@@ -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)
......
......@@ -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()
......@@ -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,
......
#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}}
......@@ -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",
......
......@@ -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();
......@@ -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();
......@@ -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 = '../..';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment