Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit e3c51a2

Browse files
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 flutter/flutter#82458 * Minor fix * Add test owner * Fix typo * Fix test feature flag
1 parent 4d25030 commit e3c51a2

File tree

10 files changed

+159
-3
lines changed

10 files changed

+159
-3
lines changed

.ci.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4364,6 +4364,24 @@ targets:
43644364
- bin/**
43654365
- .ci.yaml
43664366

4367+
- name: Windows plugin_test_windows
4368+
bringup: true # New task
4369+
recipe: devicelab/devicelab_drone
4370+
timeout: 60
4371+
properties:
4372+
dependencies: >-
4373+
[
4374+
{"dependency": "vs_build", "version": "version:vs2019"}
4375+
]
4376+
tags: >
4377+
["devicelab", "hostonly", "windows"]
4378+
task_name: plugin_test_windows
4379+
runIf:
4380+
- dev/**
4381+
- packages/flutter_tools/**
4382+
- bin/**
4383+
- .ci.yaml
4384+
43674385
- name: Windows run_debug_test_windows
43684386
recipe: devicelab/devicelab_drone
43694387
presubmit: false

TESTOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@
251251
/dev/devicelab/bin/tasks/plugin_lint_mac.dart @stuartmorgan @flutter/plugin
252252
/dev/devicelab/bin/tasks/plugin_test_ios.dart @jmagman @flutter/ios
253253
/dev/devicelab/bin/tasks/plugin_test_macos.dart @jmagman @flutter/desktop
254+
/dev/devicelab/bin/tasks/plugin_test_windows.dart @stuartmorgan @flutter/desktop
254255
/dev/devicelab/bin/tasks/plugin_test.dart @stuartmorgan @flutter/plugin
255256
/dev/devicelab/bin/tasks/run_debug_test_android.dart @zanderso @flutter/tool
256257
/dev/devicelab/bin/tasks/run_debug_test_linux.dart @loic-sharma @flutter/tool
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter_devicelab/framework/framework.dart';
6+
import 'package:flutter_devicelab/tasks/plugin_tests.dart';
7+
8+
Future<void> main() async {
9+
await task(combine(<TaskFunction>[
10+
PluginTest('windows', <String>['--platforms=windows']).call,
11+
// Test that Dart-only plugins are supported.
12+
PluginTest('windows', <String>['--platforms=windows'], dartOnlyPlugin: true).call,
13+
// Test that FFI plugins are supported.
14+
PluginTest('windows', <String>['--platforms=windows'], template: 'plugin_ffi').call,
15+
]));
16+
}

dev/devicelab/lib/tasks/plugin_tests.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,14 @@ public class $pluginClass: NSObject, FlutterPlugin {
274274
)) {
275275
throw TaskResult.failure('Platform unit tests failed');
276276
}
277+
} else if (buildTarget == 'windows') {
278+
if (await exec(
279+
path.join(rootPath, 'build', 'windows', 'plugins', 'plugintest', 'Release', 'plugintest_plugin_test'),
280+
<String>[],
281+
canFail: true,
282+
) != 0) {
283+
throw TaskResult.failure('Platform unit tests failed');
284+
}
277285
}
278286
}
279287

packages/flutter_tools/templates/app_shared/windows.tmpl/CMakeLists.txt.tmpl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ add_subdirectory(${FLUTTER_MANAGED_DIR})
5252
# Application build; see runner/CMakeLists.txt.
5353
add_subdirectory("runner")
5454

55+
{{#withPlatformChannelPluginHook}}
56+
# Enable the test target.
57+
set(include_{{pluginProjectName}}_tests TRUE)
58+
{{/withPlatformChannelPluginHook}}
59+
5560
# Generated plugin build rules, which manage building the plugins and adding
5661
# them to the application.
5762
include(flutter/generated_plugins.cmake)

packages/flutter_tools/templates/plugin/windows.tmpl/CMakeLists.txt.tmpl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,46 @@ set({{projectName}}_bundled_libraries
5151
""
5252
PARENT_SCOPE
5353
)
54+
55+
# === Tests ===
56+
# These unit tests can be run from a terminal after building the example, or
57+
# from Visual Studio after opening the generated solution file.
58+
59+
# Only enable test builds when building the example (which sets this variable)
60+
# so that plugin clients aren't building the tests.
61+
if (${include_${PROJECT_NAME}_tests})
62+
set(TEST_RUNNER "${PROJECT_NAME}_test")
63+
enable_testing()
64+
65+
# Add the Google Test dependency.
66+
include(FetchContent)
67+
FetchContent_Declare(
68+
googletest
69+
URL https://github.com/google/googletest/archive/release-1.11.0.zip
70+
)
71+
# Prevent overriding the parent project's compiler/linker settings
72+
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
73+
# Disable install commands for gtest so it doesn't end up in the bundle.
74+
set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE)
75+
FetchContent_MakeAvailable(googletest)
76+
77+
# The plugin's C API is not very useful for unit testing, so build the sources
78+
# directly into the test binary rather than using the DLL.
79+
add_executable(${TEST_RUNNER}
80+
test/{{pluginClassSnakeCase}}_test.cpp
81+
${PLUGIN_SOURCES}
82+
)
83+
apply_standard_settings(${TEST_RUNNER})
84+
target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
85+
target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin)
86+
target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock)
87+
# flutter_wrapper_plugin has link dependencies on the Flutter DLL.
88+
add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD
89+
COMMAND ${CMAKE_COMMAND} -E copy_if_different
90+
"${FLUTTER_LIBRARY}" $<TARGET_FILE_DIR:${TEST_RUNNER}>
91+
)
92+
93+
# Enable automatic test discovery.
94+
include(GoogleTest)
95+
gtest_discover_tests(${TEST_RUNNER})
96+
endif()

packages/flutter_tools/templates/plugin/windows.tmpl/pluginClassSnakeCase.h.tmpl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ class {{pluginClass}} : public flutter::Plugin {
2020
{{pluginClass}}(const {{pluginClass}}&) = delete;
2121
{{pluginClass}}& operator=(const {{pluginClass}}&) = delete;
2222

23-
private:
2423
// Called when a method is called on this plugin's channel from Dart.
2524
void HandleMethodCall(
2625
const flutter::MethodCall<flutter::EncodableValue> &method_call,
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include <flutter/method_call.h>
2+
#include <flutter/method_result_functions.h>
3+
#include <flutter/standard_method_codec.h>
4+
#include <gtest/gtest.h>
5+
#include <windows.h>
6+
7+
#include <memory>
8+
#include <string>
9+
#include <variant>
10+
11+
#include "{{pluginClassSnakeCase}}.h"
12+
13+
namespace {{projectName}} {
14+
namespace test {
15+
16+
namespace {
17+
18+
using flutter::EncodableMap;
19+
using flutter::EncodableValue;
20+
using flutter::MethodCall;
21+
using flutter::MethodResultFunctions;
22+
23+
} // namespace
24+
25+
TEST({{pluginClass}}, GetPlatformVersion) {
26+
{{pluginClass}} plugin;
27+
// Save the reply value from the success callback.
28+
std::string result_string;
29+
plugin.HandleMethodCall(
30+
MethodCall("getPlatformVersion", std::make_unique<EncodableValue>()),
31+
std::make_unique<MethodResultFunctions<>>(
32+
[&result_string](const EncodableValue* result) {
33+
result_string = std::get<std::string>(*result);
34+
},
35+
nullptr, nullptr));
36+
37+
// Since the exact string varies by host, just ensure that it's a string
38+
// with the expected format.
39+
EXPECT_TRUE(result_string.rfind("Windows ", 0) == 0);
40+
}
41+
42+
} // namespace test
43+
} // namespace {{projectName}}

packages/flutter_tools/templates/template_manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@
278278
"templates/plugin/test/projectName_method_channel_test.dart.tmpl",
279279
"templates/plugin/windows.tmpl/CMakeLists.txt.tmpl",
280280
"templates/plugin/windows.tmpl/include/projectName.tmpl/pluginClassSnakeCase_c_api.h.tmpl",
281+
"templates/plugin/windows.tmpl/test/pluginClassSnakeCase_test.cpp.tmpl",
281282
"templates/plugin/windows.tmpl/pluginClassSnakeCase.cpp.tmpl",
282283
"templates/plugin/windows.tmpl/pluginClassSnakeCase.h.tmpl",
283284
"templates/plugin/windows.tmpl/pluginClassSnakeCase_c_api.cpp.tmpl",

packages/flutter_tools/test/commands.shard/permeable/create_test.dart

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2429,7 +2429,7 @@ void main() {
24292429
androidIdentifier: 'com.example.flutter_project');
24302430
});
24312431

2432-
testUsingContext('plugin includes native unit tests', () async {
2432+
testUsingContext('plugin includes native Swift unit tests', () async {
24332433
Cache.flutterRoot = '../..';
24342434

24352435
final CreateCommand command = CreateCommand();
@@ -2452,7 +2452,7 @@ void main() {
24522452
Logger: () => logger,
24532453
});
24542454

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

24582458
final CreateCommand command = CreateCommand();
@@ -2476,6 +2476,28 @@ void main() {
24762476
Logger: () => logger,
24772477
});
24782478

2479+
testUsingContext('plugin includes native Windows unit tests', () async {
2480+
Cache.flutterRoot = '../..';
2481+
2482+
final CreateCommand command = CreateCommand();
2483+
final CommandRunner<void> runner = createTestCommandRunner(command);
2484+
2485+
await runner.run(<String>[
2486+
'create',
2487+
'--no-pub',
2488+
'--template=plugin',
2489+
'--platforms=windows',
2490+
projectDir.path]);
2491+
2492+
expect(projectDir
2493+
.childDirectory('windows')
2494+
.childDirectory('test')
2495+
.childFile('flutter_project_plugin_test.cpp'), exists);
2496+
}, overrides: <Type, Generator>{
2497+
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
2498+
Logger: () => logger,
2499+
});
2500+
24792501
testUsingContext('create a module with --platforms throws error.', () async {
24802502
Cache.flutterRoot = '../..';
24812503

0 commit comments

Comments
 (0)