mirror of
https://github.com/flutter/packages.git
synced 2025-05-31 13:38:45 +08:00
[flutter_plugin_tools] Add Linux support to native-test (#4294)
- Adds a minimal unit test to url_launcher_linux as a proof of concept. This uses almost exactly the same CMake structure as the Windows version that was added recently. - Adds Linux support for unit tests to `native-test`, sharing almost all of the existing Windows codepath. - Fixes the fact that it it was running the debug version of the unit tests, but `build-examples` only builds release. (On other platforms we run debug unit tests, but on those platforms the test command internally builds the requested unit tests, so the mismatch doesn't matter.) - Enables the new test in CI. Also opportunistically fixes some documentation in `native_test_command.dart` that wasn't updated as more platform support was added. Linux portion of https://github.com/flutter/flutter/issues/82445
This commit is contained in:
@ -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.
|
||||
|
@ -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(
|
||||
@ -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');
|
||||
|
||||
@ -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.
|
||||
@ -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),
|
||||
};
|
||||
@ -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);
|
||||
@ -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.
|
||||
@ -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.
|
||||
final List<String> components = path.split(file.path);
|
||||
return components.contains('debug') || components.contains('Debug');
|
||||
return components.contains('release') || components.contains('Release');
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -736,6 +736,147 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('Linux', () {
|
||||
test('runs unit tests', () async {
|
||||
const String testBinaryRelativePath =
|
||||
'build/linux/foo/release/bar/plugin_test';
|
||||
final Directory pluginDirectory =
|
||||
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
||||
'example/$testBinaryRelativePath'
|
||||
], platformSupport: <String, PlatformDetails>{
|
||||
kPlatformLinux: const PlatformDetails(PlatformSupport.inline),
|
||||
});
|
||||
|
||||
final File testBinary = childFileWithSubcomponents(pluginDirectory,
|
||||
<String>['example', ...testBinaryRelativePath.split('/')]);
|
||||
|
||||
final List<String> output = await runCapturingPrint(runner, <String>[
|
||||
'native-test',
|
||||
'--linux',
|
||||
'--no-integration',
|
||||
]);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
contains('Running plugin_test...'),
|
||||
contains('No issues found!'),
|
||||
]),
|
||||
);
|
||||
|
||||
expect(
|
||||
processRunner.recordedCalls,
|
||||
orderedEquals(<ProcessCall>[
|
||||
ProcessCall(testBinary.path, const <String>[], null),
|
||||
]));
|
||||
});
|
||||
|
||||
test('only runs release unit tests', () async {
|
||||
const String debugTestBinaryRelativePath =
|
||||
'build/linux/foo/debug/bar/plugin_test';
|
||||
const String releaseTestBinaryRelativePath =
|
||||
'build/linux/foo/release/bar/plugin_test';
|
||||
final Directory pluginDirectory =
|
||||
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
||||
'example/$debugTestBinaryRelativePath',
|
||||
'example/$releaseTestBinaryRelativePath'
|
||||
], platformSupport: <String, PlatformDetails>{
|
||||
kPlatformLinux: const PlatformDetails(PlatformSupport.inline),
|
||||
});
|
||||
|
||||
final File releaseTestBinary = childFileWithSubcomponents(
|
||||
pluginDirectory,
|
||||
<String>['example', ...releaseTestBinaryRelativePath.split('/')]);
|
||||
|
||||
final List<String> output = await runCapturingPrint(runner, <String>[
|
||||
'native-test',
|
||||
'--linux',
|
||||
'--no-integration',
|
||||
]);
|
||||
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
contains('Running plugin_test...'),
|
||||
contains('No issues found!'),
|
||||
]),
|
||||
);
|
||||
|
||||
// Only the release version should be run.
|
||||
expect(
|
||||
processRunner.recordedCalls,
|
||||
orderedEquals(<ProcessCall>[
|
||||
ProcessCall(releaseTestBinary.path, const <String>[], null),
|
||||
]));
|
||||
});
|
||||
|
||||
test('fails if there are no unit tests', () async {
|
||||
createFakePlugin('plugin', packagesDir,
|
||||
platformSupport: <String, PlatformDetails>{
|
||||
kPlatformLinux: const PlatformDetails(PlatformSupport.inline),
|
||||
});
|
||||
|
||||
Error? commandError;
|
||||
final List<String> output = await runCapturingPrint(runner, <String>[
|
||||
'native-test',
|
||||
'--linux',
|
||||
'--no-integration',
|
||||
], errorHandler: (Error e) {
|
||||
commandError = e;
|
||||
});
|
||||
|
||||
expect(commandError, isA<ToolExit>());
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
contains('No test binaries found.'),
|
||||
]),
|
||||
);
|
||||
|
||||
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
|
||||
});
|
||||
|
||||
test('fails if a unit test fails', () async {
|
||||
const String testBinaryRelativePath =
|
||||
'build/linux/foo/release/bar/plugin_test';
|
||||
final Directory pluginDirectory =
|
||||
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
||||
'example/$testBinaryRelativePath'
|
||||
], platformSupport: <String, PlatformDetails>{
|
||||
kPlatformLinux: const PlatformDetails(PlatformSupport.inline),
|
||||
});
|
||||
|
||||
final File testBinary = childFileWithSubcomponents(pluginDirectory,
|
||||
<String>['example', ...testBinaryRelativePath.split('/')]);
|
||||
|
||||
processRunner.mockProcessesForExecutable[testBinary.path] =
|
||||
<io.Process>[MockProcess(exitCode: 1)];
|
||||
|
||||
Error? commandError;
|
||||
final List<String> output = await runCapturingPrint(runner, <String>[
|
||||
'native-test',
|
||||
'--linux',
|
||||
'--no-integration',
|
||||
], errorHandler: (Error e) {
|
||||
commandError = e;
|
||||
});
|
||||
|
||||
expect(commandError, isA<ToolExit>());
|
||||
expect(
|
||||
output,
|
||||
containsAllInOrder(<Matcher>[
|
||||
contains('Running plugin_test...'),
|
||||
]),
|
||||
);
|
||||
|
||||
expect(
|
||||
processRunner.recordedCalls,
|
||||
orderedEquals(<ProcessCall>[
|
||||
ProcessCall(testBinary.path, const <String>[], null),
|
||||
]));
|
||||
});
|
||||
});
|
||||
|
||||
// Tests behaviors of implementation that is shared between iOS and macOS.
|
||||
group('iOS/macOS', () {
|
||||
test('fails if xcrun fails', () async {
|
||||
@ -1352,7 +1493,7 @@ void main() {
|
||||
group('Windows', () {
|
||||
test('runs unit tests', () async {
|
||||
const String testBinaryRelativePath =
|
||||
'build/windows/foo/Debug/bar/plugin_test.exe';
|
||||
'build/windows/foo/Release/bar/plugin_test.exe';
|
||||
final Directory pluginDirectory =
|
||||
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
||||
'example/$testBinaryRelativePath'
|
||||
@ -1384,7 +1525,7 @@ void main() {
|
||||
]));
|
||||
});
|
||||
|
||||
test('only runs debug unit tests', () async {
|
||||
test('only runs release unit tests', () async {
|
||||
const String debugTestBinaryRelativePath =
|
||||
'build/windows/foo/Debug/bar/plugin_test.exe';
|
||||
const String releaseTestBinaryRelativePath =
|
||||
@ -1397,8 +1538,9 @@ void main() {
|
||||
kPlatformWindows: const PlatformDetails(PlatformSupport.inline),
|
||||
});
|
||||
|
||||
final File debugTestBinary = childFileWithSubcomponents(pluginDirectory,
|
||||
<String>['example', ...debugTestBinaryRelativePath.split('/')]);
|
||||
final File releaseTestBinary = childFileWithSubcomponents(
|
||||
pluginDirectory,
|
||||
<String>['example', ...releaseTestBinaryRelativePath.split('/')]);
|
||||
|
||||
final List<String> output = await runCapturingPrint(runner, <String>[
|
||||
'native-test',
|
||||
@ -1414,11 +1556,11 @@ void main() {
|
||||
]),
|
||||
);
|
||||
|
||||
// Only the debug version should be run.
|
||||
// Only the release version should be run.
|
||||
expect(
|
||||
processRunner.recordedCalls,
|
||||
orderedEquals(<ProcessCall>[
|
||||
ProcessCall(debugTestBinary.path, const <String>[], null),
|
||||
ProcessCall(releaseTestBinary.path, const <String>[], null),
|
||||
]));
|
||||
});
|
||||
|
||||
@ -1450,7 +1592,7 @@ void main() {
|
||||
|
||||
test('fails if a unit test fails', () async {
|
||||
const String testBinaryRelativePath =
|
||||
'build/windows/foo/Debug/bar/plugin_test.exe';
|
||||
'build/windows/foo/Release/bar/plugin_test.exe';
|
||||
final Directory pluginDirectory =
|
||||
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
||||
'example/$testBinaryRelativePath'
|
||||
|
Reference in New Issue
Block a user