From f8294ebb1cb39d1425dea514c7384324561977a4 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 15 Feb 2023 15:51:13 +0000 Subject: [PATCH 1/6] 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 --- .../app_shared/linux.tmpl/CMakeLists.txt.tmpl | 5 ++ .../plugin/linux.tmpl/CMakeLists.txt.tmpl | 53 +++++++++++++++++-- .../linux.tmpl/pluginClassSnakeCase.cc.tmpl | 16 ++++-- .../pluginClassSnakeCase_private.h.tmpl | 10 ++++ .../test/pluginClassSnakeCase_test.cc.tmpl | 31 +++++++++++ .../templates/template_manifest.json | 2 + 6 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 packages/flutter_tools/templates/plugin/linux.tmpl/pluginClassSnakeCase_private.h.tmpl create mode 100644 packages/flutter_tools/templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl diff --git a/packages/flutter_tools/templates/app_shared/linux.tmpl/CMakeLists.txt.tmpl b/packages/flutter_tools/templates/app_shared/linux.tmpl/CMakeLists.txt.tmpl index f2269110c9998..a4fc907bec376 100644 --- a/packages/flutter_tools/templates/app_shared/linux.tmpl/CMakeLists.txt.tmpl +++ b/packages/flutter_tools/templates/app_shared/linux.tmpl/CMakeLists.txt.tmpl @@ -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) diff --git a/packages/flutter_tools/templates/plugin/linux.tmpl/CMakeLists.txt.tmpl b/packages/flutter_tools/templates/plugin/linux.tmpl/CMakeLists.txt.tmpl index 40a885d92f208..2d8110fe75c7b 100644 --- a/packages/flutter_tools/templates/plugin/linux.tmpl/CMakeLists.txt.tmpl +++ b/packages/flutter_tools/templates/plugin/linux.tmpl/CMakeLists.txt.tmpl @@ -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 diff --git a/packages/flutter_tools/templates/plugin/linux.tmpl/pluginClassSnakeCase.cc.tmpl b/packages/flutter_tools/templates/plugin/linux.tmpl/pluginClassSnakeCase.cc.tmpl index 4fa617a9ca4f9..4eeb03c7578b8 100644 --- a/packages/flutter_tools/templates/plugin/linux.tmpl/pluginClassSnakeCase.cc.tmpl +++ b/packages/flutter_tools/templates/plugin/linux.tmpl/pluginClassSnakeCase.cc.tmpl @@ -6,6 +6,8 @@ #include +#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); } diff --git a/packages/flutter_tools/templates/plugin/linux.tmpl/pluginClassSnakeCase_private.h.tmpl b/packages/flutter_tools/templates/plugin/linux.tmpl/pluginClassSnakeCase_private.h.tmpl new file mode 100644 index 0000000000000..3b0a95d3aa4aa --- /dev/null +++ b/packages/flutter_tools/templates/plugin/linux.tmpl/pluginClassSnakeCase_private.h.tmpl @@ -0,0 +1,10 @@ +#include + +#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(); \ No newline at end of file diff --git a/packages/flutter_tools/templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl b/packages/flutter_tools/templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl new file mode 100644 index 0000000000000..4de26e77c2f4e --- /dev/null +++ b/packages/flutter_tools/templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl @@ -0,0 +1,31 @@ +#include +#include +#include + +#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 +// build 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}} \ No newline at end of file diff --git a/packages/flutter_tools/templates/template_manifest.json b/packages/flutter_tools/templates/template_manifest.json index d4b45206c2d88..97d596433b051 100644 --- a/packages/flutter_tools/templates/template_manifest.json +++ b/packages/flutter_tools/templates/template_manifest.json @@ -272,6 +272,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", From 4395fe8c30ae31ec58af515b17bf19c9da318c2a Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 15 Feb 2023 16:06:19 +0000 Subject: [PATCH 2/6] Add creation test --- .../commands.shard/permeable/create_test.dart | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart index 0674b56e62302..98a7f3409b8a5 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart @@ -2498,6 +2498,28 @@ void main() { Logger: () => logger, }); + testUsingContext('plugin includes native Linux unit tests', () async { + Cache.flutterRoot = '../..'; + + final CreateCommand command = CreateCommand(); + final CommandRunner runner = createTestCommandRunner(command); + + await runner.run([ + 'create', + '--no-pub', + '--template=plugin', + '--platforms=linux', + projectDir.path]); + + expect(projectDir + .childDirectory('linux') + .childDirectory('test') + .childFile('flutter_project_plugin_test.cc'), exists); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + Logger: () => logger, + }); + testUsingContext('create a module with --platforms throws error.', () async { Cache.flutterRoot = '../..'; From 1bc1376bfd08cbd315021b14014fdeb8c306eed2 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 15 Feb 2023 16:13:16 +0000 Subject: [PATCH 3/6] Add integration tests --- .ci.yaml | 20 +++++++++++++++++++ .../bin/tasks/plugin_test_linux.dart | 16 +++++++++++++++ dev/devicelab/lib/tasks/plugin_tests.dart | 8 ++++++++ 3 files changed, 44 insertions(+) create mode 100644 dev/devicelab/bin/tasks/plugin_test_linux.dart diff --git a/.ci.yaml b/.ci.yaml index 480dd8116cee6..1aa5993ddd9a9 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -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 diff --git a/dev/devicelab/bin/tasks/plugin_test_linux.dart b/dev/devicelab/bin/tasks/plugin_test_linux.dart new file mode 100644 index 0000000000000..8e2a9e2909c7e --- /dev/null +++ b/dev/devicelab/bin/tasks/plugin_test_linux.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 main() async { + await task(combine([ + PluginTest('linux', ['--platforms=linux']).call, + // Test that Dart-only plugins are supported. + PluginTest('linux', ['--platforms=linux'], dartOnlyPlugin: true).call, + // Test that FFI plugins are supported. + PluginTest('linux', ['--platforms=linux'], template: 'plugin_ffi').call, + ])); +} diff --git a/dev/devicelab/lib/tasks/plugin_tests.dart b/dev/devicelab/lib/tasks/plugin_tests.dart index 21cbd824c5d05..21de398dd2bbc 100644 --- a/dev/devicelab/lib/tasks/plugin_tests.dart +++ b/dev/devicelab/lib/tasks/plugin_tests.dart @@ -282,6 +282,14 @@ public class $pluginClass: NSObject, FlutterPlugin { ) != 0) { throw TaskResult.failure('Platform unit tests failed'); } + } else if (buildTarget == 'linux') { + if (await exec( + path.join(rootPath, 'build', 'linux', 'x64', 'release', 'plugins', 'plugintest', 'plugintest_plugin_test'), + [], + canFail: true, + ) != 0) { + throw TaskResult.failure('Platform unit tests failed'); + } } } From 227a532ea0bd9a204b9d4401ced9758073e7ecd9 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 15 Feb 2023 16:20:51 +0000 Subject: [PATCH 4/6] Missing newlines --- .../plugin/linux.tmpl/pluginClassSnakeCase_private.h.tmpl | 2 +- .../plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/templates/plugin/linux.tmpl/pluginClassSnakeCase_private.h.tmpl b/packages/flutter_tools/templates/plugin/linux.tmpl/pluginClassSnakeCase_private.h.tmpl index 3b0a95d3aa4aa..604a14e30ec3c 100644 --- a/packages/flutter_tools/templates/plugin/linux.tmpl/pluginClassSnakeCase_private.h.tmpl +++ b/packages/flutter_tools/templates/plugin/linux.tmpl/pluginClassSnakeCase_private.h.tmpl @@ -7,4 +7,4 @@ // in the unit-testable API. // Handles the getPlatformVersion method call. -FlMethodResponse *get_platform_version(); \ No newline at end of file +FlMethodResponse *get_platform_version(); diff --git a/packages/flutter_tools/templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl b/packages/flutter_tools/templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl index 4de26e77c2f4e..e9db2c59ab232 100644 --- a/packages/flutter_tools/templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl +++ b/packages/flutter_tools/templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl @@ -28,4 +28,4 @@ TEST({{pluginClass}}, GetPlatformVersion) { } } // namespace test -} // namespace {{projectName}} \ No newline at end of file +} // namespace {{projectName}} From af72b920a8e99ee820ba8e06c204bf2e15a33a2a Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 15 Feb 2023 16:49:59 +0000 Subject: [PATCH 5/6] test owner --- TESTOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/TESTOWNERS b/TESTOWNERS index 32627ce013efd..dac94ada50c73 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -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 From 9aaff9f6ae50959b75b1420cd1c242fc1fea3ba8 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 15 Feb 2023 12:46:09 -0500 Subject: [PATCH 6/6] Typo --- .../plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl b/packages/flutter_tools/templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl index e9db2c59ab232..c5bfdde8201fc 100644 --- a/packages/flutter_tools/templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl +++ b/packages/flutter_tools/templates/plugin/linux.tmpl/test/pluginClassSnakeCase_test.cc.tmpl @@ -10,7 +10,7 @@ // // 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 -// build for x64 debug, run: +// built for x64 debug, run: // $ build/linux/x64/debug/plugins/my_plugin/my_plugin_test namespace {{projectName}} {