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

[flutter_plugin_tools] Add Linux support to native-test #4294

Merged
merged 11 commits into from
Sep 1, 2021
Merged
2 changes: 2 additions & 0 deletions .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ task:
build_script:
- flutter config --enable-linux-desktop
- ./script/tool_runner.sh build-examples --linux
native_test_script:
- ./script/tool_runner.sh native-test --linux --no-integration
drive_script:
- xvfb-run ./script/tool_runner.sh drive-examples --linux

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
add_dependencies(${BINARY_NAME} flutter_assemble)

# Enable the test target.
set(include_url_launcher_linux_tests TRUE)

# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
linux-x64 ${CMAKE_BUILD_TYPE}
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
Expand Down
47 changes: 46 additions & 1 deletion packages/url_launcher/url_launcher_linux/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ project(${PROJECT_NAME} LANGUAGES CXX)

set(PLUGIN_NAME "${PROJECT_NAME}_plugin")

add_library(${PLUGIN_NAME} SHARED
list(APPEND PLUGIN_SOURCES
"url_launcher_plugin.cc"
)

add_library(${PLUGIN_NAME} SHARED
${PLUGIN_SOURCES}
)
apply_standard_settings(${PLUGIN_NAME})
set_target_properties(${PLUGIN_NAME} PROPERTIES
CXX_VISIBILITY_PRESET hidden)
Expand All @@ -15,3 +19,44 @@ target_include_directories(${PLUGIN_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter)
target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)


# === 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()
# TODO(stuartmorgan): Consider using a single shared, pre-checked-in googletest
# instance rather than downloading for each plugin. This approach makes sense
# for a template, but not for a monorepo with many plugins.
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/url_launcher_linux_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)

include(GoogleTest)
gtest_discover_tests(${TEST_RUNNER})
endif() # CMake version check
endif() # include_${PROJECT_NAME}_tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2013 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.
#include <flutter_linux/flutter_linux.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <memory>
#include <string>

#include "include/url_launcher_linux/url_launcher_plugin.h"
#include "url_launcher_plugin_private.h"

namespace url_launcher_plugin {
namespace test {

TEST(UrlLauncherPlugin, CanLaunchSuccess) {
g_autoptr(FlValue) args = fl_value_new_map();
fl_value_set_string_take(args, "url",
fl_value_new_string("https://flutter.dev"));
FlMethodResponse* response = can_launch(nullptr, args);
ASSERT_NE(response, nullptr);
ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
g_autoptr(FlValue) expected = fl_value_new_bool(true);
EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result(
FL_METHOD_SUCCESS_RESPONSE(response)),
expected));
}

TEST(UrlLauncherPlugin, CanLaunchFailureUnhandled) {
g_autoptr(FlValue) args = fl_value_new_map();
fl_value_set_string_take(args, "url", fl_value_new_string("madeup:scheme"));
FlMethodResponse* response = can_launch(nullptr, args);
ASSERT_NE(response, nullptr);
ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
g_autoptr(FlValue) expected = fl_value_new_bool(false);
EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result(
FL_METHOD_SUCCESS_RESPONSE(response)),
expected));
}

// For consistency with the established mobile implementations,
// an invalid URL should return false, not an error.
TEST(UrlLauncherPlugin, CanLaunchFailureInvalidUrl) {
g_autoptr(FlValue) args = fl_value_new_map();
fl_value_set_string_take(args, "url", fl_value_new_string(""));
FlMethodResponse* response = can_launch(nullptr, args);
ASSERT_NE(response, nullptr);
ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
g_autoptr(FlValue) expected = fl_value_new_bool(false);
EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result(
FL_METHOD_SUCCESS_RESPONSE(response)),
expected));
}

} // namespace test
} // namespace url_launcher_plugin
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include <cstring>

#include "url_launcher_plugin_private.h"

// See url_launcher_channel.dart for documentation.
const char kChannelName[] = "plugins.flutter.io/url_launcher";
const char kBadArgumentsError[] = "Bad Arguments";
Expand Down Expand Up @@ -44,7 +46,7 @@ static gchar* get_url(FlValue* args, GError** error) {
}

// Called to check if a URL can be launched.
static FlMethodResponse* can_launch(FlUrlLauncherPlugin* self, FlValue* args) {
FlMethodResponse* can_launch(FlUrlLauncherPlugin* self, FlValue* args) {
g_autoptr(GError) error = nullptr;
g_autofree gchar* url = get_url(args, &error);
if (url == nullptr) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2013 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.

#include <flutter_linux/flutter_linux.h>

#include "include/url_launcher_linux/url_launcher_plugin.h"

// TODO(stuartmorgan): Remove this private header and change the below back to
// a static function once https://github.com/flutter/flutter/issues/88724
// is fixed, and test through the public API instead.

// Handles the canLaunch method call.
FlMethodResponse* can_launch(FlUrlLauncherPlugin* self, FlValue* args);
4 changes: 4 additions & 0 deletions script/tool/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## NEXT

- `native-test` now supports `--linux` for unit tests.

## 0.6.0+1

- Fixed `build-examples` to work for non-plugin packages.
Expand Down
37 changes: 32 additions & 5 deletions script/tool/lib/src/native_test_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ const String _iosDestinationFlag = 'ios-destination';
const int _exitNoIosSimulators = 3;

/// The command to run native tests for plugins:
/// - iOS and macOS: XCTests (XCUnitTest and XCUITest) in plugins.
/// - iOS and macOS: XCTests (XCUnitTest and XCUITest)
/// - Android: JUnit tests
/// - Windows and Linux: GoogleTest tests
class NativeTestCommand extends PackageLoopingCommand {
/// Creates an instance of the test command.
NativeTestCommand(
Expand All @@ -39,6 +41,7 @@ class NativeTestCommand extends PackageLoopingCommand {
);
argParser.addFlag(kPlatformAndroid, help: 'Runs Android tests');
argParser.addFlag(kPlatformIos, help: 'Runs iOS tests');
argParser.addFlag(kPlatformLinux, help: 'Runs Linux tests');
argParser.addFlag(kPlatformMacos, help: 'Runs macOS tests');
argParser.addFlag(kPlatformWindows, help: 'Runs Windows tests');

Expand All @@ -63,9 +66,11 @@ class NativeTestCommand extends PackageLoopingCommand {
Runs native unit tests and native integration tests.

Currently supported platforms:
- Android (unit tests only)
- Android
- iOS: requires 'xcrun' to be in your path.
- Linux (unit tests only)
- macOS: requires 'xcrun' to be in your path.
- Windows (unit tests only)

The example app(s) must be built for all targeted platforms before running
this command.
Expand All @@ -80,6 +85,7 @@ this command.
_platforms = <String, _PlatformDetails>{
kPlatformAndroid: _PlatformDetails('Android', _testAndroid),
kPlatformIos: _PlatformDetails('iOS', _testIos),
kPlatformLinux: _PlatformDetails('Linux', _testLinux),
kPlatformMacos: _PlatformDetails('macOS', _testMacOS),
kPlatformWindows: _PlatformDetails('Windows', _testWindows),
};
Expand All @@ -103,6 +109,11 @@ this command.
'See https://github.com/flutter/flutter/issues/70233.');
}

if (getBoolArg(kPlatformLinux) && getBoolArg(_integrationTestFlag)) {
logWarning('This command currently only supports unit tests for Linux. '
'See https://github.com/flutter/flutter/issues/70235.');
}

// iOS-specific run-level state.
if (_requestedPlatforms.contains('ios')) {
String destination = getStringArg(_iosDestinationFlag);
Expand Down Expand Up @@ -418,6 +429,21 @@ this command.
buildDirectoryName: 'windows', isTestBinary: isTestBinary);
}

Future<_PlatformResult> _testLinux(
RepositoryPackage plugin, _TestMode mode) async {
if (mode.integrationOnly) {
return _PlatformResult(RunState.skipped);
}

bool isTestBinary(File file) {
return file.basename.endsWith('_test') ||
file.basename.endsWith('_tests');
}

return _runGoogleTestTests(plugin,
buildDirectoryName: 'linux', isTestBinary: isTestBinary);
}

/// Finds every file in the [buildDirectoryName] subdirectory of [plugin]'s
/// build directory for which [isTestBinary] is true, and runs all of them,
/// returning the overall result.
Expand All @@ -442,10 +468,11 @@ this command.
.whereType<File>()
.where(isTestBinary)
.where((File file) {
// Only run the debug build of the unit tests, to avoid running the
// same tests multiple times.
// Only run the release build of the unit tests, to avoid running the
// same tests multiple times. Release is used rather than debug since
// `build-examples` builds release versions.
Copy link
Member

@cbracken cbracken Sep 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was going to say perhaps we should be changing to generate a debug build of build-examples given that it's typically more convenient to debug debug builds. That said, in the end you want to verify the release build of the library code in the plugin, and people shouldn't be relying on #ifdef'ed code in their tests, so this seems fine.

If this ever gets problematic, we could add a buildmode flag.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized that there's actually a more annoying problem, which is that the current behavior isn't going to be good for local use. Filed flutter/flutter#89303.

I'm going to do that as a follow-up since it applies to the already-landed Windows flow in the same way, but I'll do it shortly.

final List<String> components = path.split(file.path);
return components.contains('debug') || components.contains('Debug');
return components.contains('release') || components.contains('Release');
}));
}

Expand Down
Loading