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

Add Linux unit tests to plugin template (#120814)

* Add Linux unit tests to plugin template

Adds an example native unit test to the plugin template for Linux,
matching the structure we use for our 1P plugin 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.

While ideally we would adjust the engine APIs first to allow for testing
the method call handler directly, it's unclear when we will have time
for that work, and for a complex plugin most of the testing wouldn't be
at that layer anyway, so having the structure in place with the
limitations documented is still a significant improvement over having
nothing in the template.

Part of https://github.com/flutter/flutter/issues/82458

* Add creation test

* Add integration tests

* Missing newlines

* test owner

* Typo
parent 2b7d709f
......@@ -697,6 +697,26 @@ targets:
- bin/**
- .ci.yaml
- name: Linux plugin_test_linux
recipe: devicelab/devicelab_drone
bringup: true # New task
timeout: 60
properties:
dependencies: >-
[
{"dependency": "clang", "version": "git_revision:5d5aba78dbbee75508f01bcaa69aedb2ab79065a"},
{"dependency": "cmake", "version": "version:3.16.1"},
{"dependency": "ninja", "version": "version:1.9.0"}
]
tags: >
["devicelab", "hostonly", "linux"]
task_name: plugin_test_linux
runIf:
- dev/**
- packages/flutter_tools/**
- bin/**
- .ci.yaml
- name: Linux run_debug_test_linux
recipe: devicelab/devicelab_drone
bringup: true
......
......@@ -252,6 +252,7 @@
/dev/devicelab/bin/tasks/platform_view_win_desktop__start_up.dart @yaakovschectman @flutter/desktop
/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_linux.dart @stuartmorgan @flutter/desktop
/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
......
// 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('linux', <String>['--platforms=linux']).call,
// Test that Dart-only plugins are supported.
PluginTest('linux', <String>['--platforms=linux'], dartOnlyPlugin: true).call,
// Test that FFI plugins are supported.
PluginTest('linux', <String>['--platforms=linux'], template: 'plugin_ffi').call,
]));
}
......@@ -276,6 +276,15 @@ public class $pluginClass: NSObject, FlutterPlugin {
}
});
break;
case 'linux':
if (await exec(
path.join(rootPath, 'build', 'linux', 'x64', 'release', 'plugins', 'plugintest', 'plugintest_plugin_test'),
<String>[],
canFail: true,
) != 0) {
throw TaskResult.failure('Platform unit tests failed');
}
break;
case 'macos':
if (!await runXcodeTests(
platformDirectory: path.join(rootPath, 'macos'),
......
......@@ -86,6 +86,11 @@ set_target_properties(${BINARY_NAME}
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
{{#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)
......
......@@ -11,12 +11,15 @@ project(${PROJECT_NAME} LANGUAGES CXX)
# not be changed.
set(PLUGIN_NAME "{{projectName}}_plugin")
# Any new source files that you add to the plugin should be added here.
list(APPEND PLUGIN_SOURCES
"{{pluginClassSnakeCase}}.cc"
)
# Define the plugin library target. Its name must not be changed (see comment
# on PLUGIN_NAME above).
#
# Any new source files that you add to the plugin should be added here.
add_library(${PLUGIN_NAME} SHARED
"{{pluginClassSnakeCase}}.cc"
${PLUGIN_SOURCES}
)
# Apply a standard set of build settings that are configured in the
......@@ -45,3 +48,47 @@ set({{projectName}}_bundled_libraries
""
PARENT_SCOPE
)
# === Tests ===
# These unit tests can be run from a terminal after building the example.
# 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})
if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
message("Unit tests require CMake 3.11.0 or later")
else()
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 exported API is not very useful for unit testing, so build the
# sources directly into the test binary rather than using the shared library.
add_executable(${TEST_RUNNER}
test/{{pluginClassSnakeCase}}_test.cc
${PLUGIN_SOURCES}
)
apply_standard_settings(${TEST_RUNNER})
target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(${TEST_RUNNER} PRIVATE flutter)
target_link_libraries(${TEST_RUNNER} PRIVATE PkgConfig::GTK)
target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock)
# Enable automatic test discovery.
include(GoogleTest)
gtest_discover_tests(${TEST_RUNNER})
endif() # CMake version check
endif() # include_${PROJECT_NAME}_tests
\ No newline at end of file
......@@ -6,6 +6,8 @@
#include <cstring>
#include "{{pluginClassSnakeCase}}_private.h"
#define {{pluginClassCapitalSnakeCase}}(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), {{pluginClassSnakeCase}}_get_type(), \
{{pluginClass}}))
......@@ -25,11 +27,7 @@ static void {{pluginClassSnakeCase}}_handle_method_call(
const gchar* method = fl_method_call_get_name(method_call);
if (strcmp(method, "getPlatformVersion") == 0) {
struct utsname uname_data = {};
uname(&uname_data);
g_autofree gchar *version = g_strdup_printf("Linux %s", uname_data.version);
g_autoptr(FlValue) result = fl_value_new_string(version);
response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
response = get_platform_version();
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}
......@@ -37,6 +35,14 @@ static void {{pluginClassSnakeCase}}_handle_method_call(
fl_method_call_respond(method_call, response, nullptr);
}
FlMethodResponse* get_platform_version() {
struct utsname uname_data = {};
uname(&uname_data);
g_autofree gchar *version = g_strdup_printf("Linux %s", uname_data.version);
g_autoptr(FlValue) result = fl_value_new_string(version);
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
static void {{pluginClassSnakeCase}}_dispose(GObject* object) {
G_OBJECT_CLASS({{pluginClassSnakeCase}}_parent_class)->dispose(object);
}
......
#include <flutter_linux/flutter_linux.h>
#include "include/{{projectName}}/{{pluginClassSnakeCase}}.h"
// This file exposes some plugin internals for unit testing. See
// https://github.com/flutter/flutter/issues/88724 for current limitations
// in the unit-testable API.
// Handles the getPlatformVersion method call.
FlMethodResponse *get_platform_version();
#include <flutter_linux/flutter_linux.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "include/{{projectName}}/{{pluginClassSnakeCase}}.h"
#include "{{pluginClassSnakeCase}}_private.h"
// This demonstrates a simple unit test of the C portion of this plugin's
// implementation.
//
// Once you have built the plugin's example app, you can run these tests
// from the command line. For instance, for a plugin called my_plugin
// built for x64 debug, run:
// $ build/linux/x64/debug/plugins/my_plugin/my_plugin_test
namespace {{projectName}} {
namespace test {
TEST({{pluginClass}}, GetPlatformVersion) {
g_autoptr(FlMethodResponse) response = get_platform_version();
ASSERT_NE(response, nullptr);
ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
FlValue* result = fl_method_success_response_get_result(
FL_METHOD_SUCCESS_RESPONSE(response));
ASSERT_EQ(fl_value_get_type(result), FL_VALUE_TYPE_STRING);
// The full string varies, so just valiate that it has the right format.
EXPECT_THAT(fl_value_get_string(result), testing::StartsWith("Linux "));
}
} // namespace test
} // namespace {{projectName}}
......@@ -274,6 +274,8 @@
"templates/plugin/linux.tmpl/CMakeLists.txt.tmpl",
"templates/plugin/linux.tmpl/include/projectName.tmpl/pluginClassSnakeCase.h.tmpl",
"templates/plugin/linux.tmpl/pluginClassSnakeCase.cc.tmpl",
"templates/plugin/linux.tmpl/pluginClassSnakeCase_private.h.tmpl",
"templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl",
"templates/plugin/macos.tmpl/Classes/pluginClass.swift.tmpl",
"templates/plugin/README.md.tmpl",
"templates/plugin/test/projectName_test.dart.tmpl",
......
......@@ -2555,6 +2555,28 @@ void main() {
Logger: () => logger,
});
testUsingContext('plugin includes native Linux 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=linux',
projectDir.path]);
expect(projectDir
.childDirectory('linux')
.childDirectory('test')
.childFile('flutter_project_plugin_test.cc'), exists);
}, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: 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