mirror of
https://github.com/flutter/packages.git
synced 2025-06-05 19:17:51 +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
|
## 0.6.0+1
|
||||||
|
|
||||||
- Fixed `build-examples` to work for non-plugin packages.
|
- Fixed `build-examples` to work for non-plugin packages.
|
||||||
|
@ -21,7 +21,9 @@ const String _iosDestinationFlag = 'ios-destination';
|
|||||||
const int _exitNoIosSimulators = 3;
|
const int _exitNoIosSimulators = 3;
|
||||||
|
|
||||||
/// The command to run native tests for plugins:
|
/// 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 {
|
class NativeTestCommand extends PackageLoopingCommand {
|
||||||
/// Creates an instance of the test command.
|
/// Creates an instance of the test command.
|
||||||
NativeTestCommand(
|
NativeTestCommand(
|
||||||
@ -39,6 +41,7 @@ class NativeTestCommand extends PackageLoopingCommand {
|
|||||||
);
|
);
|
||||||
argParser.addFlag(kPlatformAndroid, help: 'Runs Android tests');
|
argParser.addFlag(kPlatformAndroid, help: 'Runs Android tests');
|
||||||
argParser.addFlag(kPlatformIos, help: 'Runs iOS tests');
|
argParser.addFlag(kPlatformIos, help: 'Runs iOS tests');
|
||||||
|
argParser.addFlag(kPlatformLinux, help: 'Runs Linux tests');
|
||||||
argParser.addFlag(kPlatformMacos, help: 'Runs macOS tests');
|
argParser.addFlag(kPlatformMacos, help: 'Runs macOS tests');
|
||||||
argParser.addFlag(kPlatformWindows, help: 'Runs Windows tests');
|
argParser.addFlag(kPlatformWindows, help: 'Runs Windows tests');
|
||||||
|
|
||||||
@ -63,9 +66,11 @@ class NativeTestCommand extends PackageLoopingCommand {
|
|||||||
Runs native unit tests and native integration tests.
|
Runs native unit tests and native integration tests.
|
||||||
|
|
||||||
Currently supported platforms:
|
Currently supported platforms:
|
||||||
- Android (unit tests only)
|
- Android
|
||||||
- iOS: requires 'xcrun' to be in your path.
|
- iOS: requires 'xcrun' to be in your path.
|
||||||
|
- Linux (unit tests only)
|
||||||
- macOS: requires 'xcrun' to be in your path.
|
- 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
|
The example app(s) must be built for all targeted platforms before running
|
||||||
this command.
|
this command.
|
||||||
@ -80,6 +85,7 @@ this command.
|
|||||||
_platforms = <String, _PlatformDetails>{
|
_platforms = <String, _PlatformDetails>{
|
||||||
kPlatformAndroid: _PlatformDetails('Android', _testAndroid),
|
kPlatformAndroid: _PlatformDetails('Android', _testAndroid),
|
||||||
kPlatformIos: _PlatformDetails('iOS', _testIos),
|
kPlatformIos: _PlatformDetails('iOS', _testIos),
|
||||||
|
kPlatformLinux: _PlatformDetails('Linux', _testLinux),
|
||||||
kPlatformMacos: _PlatformDetails('macOS', _testMacOS),
|
kPlatformMacos: _PlatformDetails('macOS', _testMacOS),
|
||||||
kPlatformWindows: _PlatformDetails('Windows', _testWindows),
|
kPlatformWindows: _PlatformDetails('Windows', _testWindows),
|
||||||
};
|
};
|
||||||
@ -103,6 +109,11 @@ this command.
|
|||||||
'See https://github.com/flutter/flutter/issues/70233.');
|
'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.
|
// iOS-specific run-level state.
|
||||||
if (_requestedPlatforms.contains('ios')) {
|
if (_requestedPlatforms.contains('ios')) {
|
||||||
String destination = getStringArg(_iosDestinationFlag);
|
String destination = getStringArg(_iosDestinationFlag);
|
||||||
@ -418,6 +429,21 @@ this command.
|
|||||||
buildDirectoryName: 'windows', isTestBinary: isTestBinary);
|
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
|
/// Finds every file in the [buildDirectoryName] subdirectory of [plugin]'s
|
||||||
/// build directory for which [isTestBinary] is true, and runs all of them,
|
/// build directory for which [isTestBinary] is true, and runs all of them,
|
||||||
/// returning the overall result.
|
/// returning the overall result.
|
||||||
@ -442,10 +468,11 @@ this command.
|
|||||||
.whereType<File>()
|
.whereType<File>()
|
||||||
.where(isTestBinary)
|
.where(isTestBinary)
|
||||||
.where((File file) {
|
.where((File file) {
|
||||||
// Only run the debug build of the unit tests, to avoid running the
|
// Only run the release build of the unit tests, to avoid running the
|
||||||
// same tests multiple times.
|
// same tests multiple times. Release is used rather than debug since
|
||||||
|
// `build-examples` builds release versions.
|
||||||
final List<String> components = path.split(file.path);
|
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.
|
// Tests behaviors of implementation that is shared between iOS and macOS.
|
||||||
group('iOS/macOS', () {
|
group('iOS/macOS', () {
|
||||||
test('fails if xcrun fails', () async {
|
test('fails if xcrun fails', () async {
|
||||||
@ -1352,7 +1493,7 @@ void main() {
|
|||||||
group('Windows', () {
|
group('Windows', () {
|
||||||
test('runs unit tests', () async {
|
test('runs unit tests', () async {
|
||||||
const String testBinaryRelativePath =
|
const String testBinaryRelativePath =
|
||||||
'build/windows/foo/Debug/bar/plugin_test.exe';
|
'build/windows/foo/Release/bar/plugin_test.exe';
|
||||||
final Directory pluginDirectory =
|
final Directory pluginDirectory =
|
||||||
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
||||||
'example/$testBinaryRelativePath'
|
'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 =
|
const String debugTestBinaryRelativePath =
|
||||||
'build/windows/foo/Debug/bar/plugin_test.exe';
|
'build/windows/foo/Debug/bar/plugin_test.exe';
|
||||||
const String releaseTestBinaryRelativePath =
|
const String releaseTestBinaryRelativePath =
|
||||||
@ -1397,8 +1538,9 @@ void main() {
|
|||||||
kPlatformWindows: const PlatformDetails(PlatformSupport.inline),
|
kPlatformWindows: const PlatformDetails(PlatformSupport.inline),
|
||||||
});
|
});
|
||||||
|
|
||||||
final File debugTestBinary = childFileWithSubcomponents(pluginDirectory,
|
final File releaseTestBinary = childFileWithSubcomponents(
|
||||||
<String>['example', ...debugTestBinaryRelativePath.split('/')]);
|
pluginDirectory,
|
||||||
|
<String>['example', ...releaseTestBinaryRelativePath.split('/')]);
|
||||||
|
|
||||||
final List<String> output = await runCapturingPrint(runner, <String>[
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
||||||
'native-test',
|
'native-test',
|
||||||
@ -1414,11 +1556,11 @@ void main() {
|
|||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Only the debug version should be run.
|
// Only the release version should be run.
|
||||||
expect(
|
expect(
|
||||||
processRunner.recordedCalls,
|
processRunner.recordedCalls,
|
||||||
orderedEquals(<ProcessCall>[
|
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 {
|
test('fails if a unit test fails', () async {
|
||||||
const String testBinaryRelativePath =
|
const String testBinaryRelativePath =
|
||||||
'build/windows/foo/Debug/bar/plugin_test.exe';
|
'build/windows/foo/Release/bar/plugin_test.exe';
|
||||||
final Directory pluginDirectory =
|
final Directory pluginDirectory =
|
||||||
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
||||||
'example/$testBinaryRelativePath'
|
'example/$testBinaryRelativePath'
|
||||||
|
Reference in New Issue
Block a user