mirror of
https://github.com/flutter/packages.git
synced 2025-08-23 18:44:31 +08:00

Adds initial file-based filtering. This does not attempt to be comprehensive, just to get some low-hanging fruit, and to create a blueprint for anyone to follow in the future when adding more filtering. I expect that once this is in place, what will happen is that as we notice cases where PRs are hitting slow or flaky tests that they clearly don't need to, we can incrementally improve the filtering on demand. Fixes https://github.com/flutter/flutter/issues/136394
2399 lines
81 KiB
Dart
2399 lines
81 KiB
Dart
// 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.
|
|
|
|
import 'dart:convert';
|
|
import 'dart:ffi';
|
|
|
|
import 'package:args/command_runner.dart';
|
|
import 'package:file/file.dart';
|
|
import 'package:flutter_plugin_tools/src/common/cmake.dart';
|
|
import 'package:flutter_plugin_tools/src/common/core.dart';
|
|
import 'package:flutter_plugin_tools/src/common/file_utils.dart';
|
|
import 'package:flutter_plugin_tools/src/common/plugin_utils.dart';
|
|
import 'package:flutter_plugin_tools/src/native_test_command.dart';
|
|
import 'package:git/git.dart';
|
|
import 'package:path/path.dart' as p;
|
|
import 'package:platform/platform.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
import 'mocks.dart';
|
|
import 'util.dart';
|
|
|
|
const String _allAbiFlag =
|
|
'-Ptarget-platform=android-arm,android-arm64,android-x64,android-x86';
|
|
|
|
const String _androidIntegrationTestFilter =
|
|
'-Pandroid.testInstrumentationRunnerArguments.'
|
|
'notAnnotation=io.flutter.plugins.DartIntegrationTest';
|
|
|
|
const String _simulatorDeviceId = '1E76A0FD-38AC-4537-A989-EA639D7D012A';
|
|
|
|
final Map<String, dynamic> _kDeviceListMap = <String, dynamic>{
|
|
'runtimes': <Map<String, dynamic>>[
|
|
<String, dynamic>{
|
|
'bundlePath':
|
|
'/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 13.4.simruntime',
|
|
'buildversion': '17L255',
|
|
'runtimeRoot':
|
|
'/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 13.4.simruntime/Contents/Resources/RuntimeRoot',
|
|
'identifier': 'com.apple.CoreSimulator.SimRuntime.iOS-13-4',
|
|
'version': '13.4',
|
|
'isAvailable': true,
|
|
'name': 'iOS 13.4'
|
|
},
|
|
],
|
|
'devices': <String, dynamic>{
|
|
'com.apple.CoreSimulator.SimRuntime.iOS-13-4': <Map<String, dynamic>>[
|
|
<String, dynamic>{
|
|
'dataPath':
|
|
'/Users/xxx/Library/Developer/CoreSimulator/Devices/1E76A0FD-38AC-4537-A989-EA639D7D012A/data',
|
|
'logPath':
|
|
'/Users/xxx/Library/Logs/CoreSimulator/1E76A0FD-38AC-4537-A989-EA639D7D012A',
|
|
'udid': '1E76A0FD-38AC-4537-A989-EA639D7D012A',
|
|
'isAvailable': true,
|
|
'deviceTypeIdentifier':
|
|
'com.apple.CoreSimulator.SimDeviceType.iPhone-8-Plus',
|
|
'state': 'Shutdown',
|
|
'name': 'iPhone 8 Plus'
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
const String _fakeCmakeCommand = 'path/to/cmake';
|
|
const String _archDirX64 = 'x64';
|
|
const String _archDirArm64 = 'arm64';
|
|
|
|
void _createFakeCMakeCache(
|
|
RepositoryPackage plugin, Platform platform, String? archDir) {
|
|
final CMakeProject project = CMakeProject(getExampleDir(plugin),
|
|
platform: platform, buildMode: 'Release', arch: archDir);
|
|
final File cache = project.buildDirectory.childFile('CMakeCache.txt');
|
|
cache.createSync(recursive: true);
|
|
cache.writeAsStringSync('CMAKE_COMMAND:INTERNAL=$_fakeCmakeCommand');
|
|
}
|
|
|
|
// TODO(stuartmorgan): Rework these tests to use a mock Xcode instead of
|
|
// doing all the process mocking and validation.
|
|
void main() {
|
|
const String kDestination = '--ios-destination';
|
|
|
|
group('test native_test_command on Posix', () {
|
|
late MockPlatform mockPlatform;
|
|
late Directory packagesDir;
|
|
late CommandRunner<void> runner;
|
|
late RecordingProcessRunner processRunner;
|
|
late RecordingProcessRunner gitProcessRunner;
|
|
|
|
setUp(() {
|
|
// iOS and macOS tests expect macOS, Linux tests expect Linux; nothing
|
|
// needs to distinguish between Linux and macOS, so set both to true to
|
|
// allow them to share a setup group.
|
|
mockPlatform = MockPlatform(isMacOS: true, isLinux: true);
|
|
final GitDir gitDir;
|
|
(:packagesDir, :processRunner, :gitProcessRunner, :gitDir) =
|
|
configureBaseCommandMocks(platform: mockPlatform);
|
|
final NativeTestCommand command = NativeTestCommand(
|
|
packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: gitDir,
|
|
);
|
|
|
|
runner = CommandRunner<void>(
|
|
'native_test_command', 'Test for native_test_command');
|
|
runner.addCommand(command);
|
|
});
|
|
|
|
// Returns a FakeProcessInfo to provide for "xcrun xcodebuild -list" for a
|
|
// project that contains [targets].
|
|
FakeProcessInfo getMockXcodebuildListProcess(List<String> targets) {
|
|
final Map<String, dynamic> projects = <String, dynamic>{
|
|
'project': <String, dynamic>{
|
|
'targets': targets,
|
|
}
|
|
};
|
|
return FakeProcessInfo(MockProcess(stdout: jsonEncode(projects)),
|
|
<String>['xcodebuild', '-list']);
|
|
}
|
|
|
|
// Returns the ProcessCall to expect for checking the targets present in
|
|
// the [package]'s [platform]/Runner.xcodeproj.
|
|
ProcessCall getTargetCheckCall(Directory package, String platform) {
|
|
return ProcessCall(
|
|
'xcrun',
|
|
<String>[
|
|
'xcodebuild',
|
|
'-list',
|
|
'-json',
|
|
'-project',
|
|
package
|
|
.childDirectory(platform)
|
|
.childDirectory('Runner.xcodeproj')
|
|
.path,
|
|
],
|
|
null);
|
|
}
|
|
|
|
// Returns the ProcessCall to expect for generating the native project files
|
|
// with a --config-only build on iOS or macOS.
|
|
ProcessCall getConfigOnlyDarwinBuildCall(
|
|
Directory package, FlutterPlatform platform) {
|
|
return ProcessCall(
|
|
'flutter',
|
|
<String>[
|
|
'build',
|
|
if (platform == FlutterPlatform.ios) 'ios' else 'macos',
|
|
'--debug',
|
|
'--config-only',
|
|
],
|
|
package.path);
|
|
}
|
|
|
|
// Returns the ProcessCall to expect for running the tests in the
|
|
// workspace [platform]/Runner.xcworkspace, with the given extra flags.
|
|
ProcessCall getRunTestCall(
|
|
Directory package,
|
|
String platform, {
|
|
String? destination,
|
|
List<String> extraFlags = const <String>[],
|
|
bool treatWarningsAsErrors = true,
|
|
}) {
|
|
return ProcessCall(
|
|
'xcrun',
|
|
<String>[
|
|
'xcodebuild',
|
|
'clean',
|
|
'test',
|
|
'-workspace',
|
|
'$platform/Runner.xcworkspace',
|
|
'-scheme',
|
|
'Runner',
|
|
'-configuration',
|
|
'Debug',
|
|
if (destination != null) ...<String>['-destination', destination],
|
|
...extraFlags,
|
|
if (treatWarningsAsErrors) 'GCC_TREAT_WARNINGS_AS_ERRORS=YES',
|
|
],
|
|
package.path);
|
|
}
|
|
|
|
// Returns the ProcessCall to expect for build the Linux unit tests for the
|
|
// given plugin.
|
|
ProcessCall getLinuxBuildCall(RepositoryPackage plugin) {
|
|
return ProcessCall(
|
|
'cmake',
|
|
<String>[
|
|
'--build',
|
|
getExampleDir(plugin)
|
|
.childDirectory('build')
|
|
.childDirectory('linux')
|
|
.childDirectory('x64')
|
|
.childDirectory('release')
|
|
.path,
|
|
'--target',
|
|
'unit_tests'
|
|
],
|
|
null);
|
|
}
|
|
|
|
test('fails if no platforms are provided', () async {
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['native-test'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('At least one platform flag must be provided.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails if all test types are disabled', () async {
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--macos',
|
|
'--no-unit',
|
|
'--no-integration',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('At least one test type must be enabled.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('reports skips with no tests', () async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformMacOS: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
getMockXcodebuildListProcess(<String>['RunnerTests', 'RunnerUITests']),
|
|
// Exit code 66 from testing indicates no tests.
|
|
FakeProcessInfo(
|
|
MockProcess(exitCode: 66), <String>['xcodebuild', 'clean', 'test']),
|
|
];
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['native-test', '--macos', '--no-unit']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No tests found.'),
|
|
contains('Skipped 1 package(s)'),
|
|
]));
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getTargetCheckCall(pluginExampleDirectory, 'macos'),
|
|
getConfigOnlyDarwinBuildCall(
|
|
pluginExampleDirectory, FlutterPlatform.macos),
|
|
getRunTestCall(pluginExampleDirectory, 'macos',
|
|
extraFlags: <String>['-only-testing:RunnerUITests']),
|
|
]));
|
|
});
|
|
|
|
group('iOS', () {
|
|
test('skip if iOS is not supported', () async {
|
|
createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformMacOS: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['native-test', '--ios', kDestination, 'foo_destination']);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No implementation for iOS.'),
|
|
contains('SKIPPING: Nothing to test for target platform(s).'),
|
|
]));
|
|
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
|
|
});
|
|
|
|
test('skip if iOS is implemented in a federated package', () async {
|
|
createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformIOS: const PlatformDetails(PlatformSupport.federated)
|
|
});
|
|
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['native-test', '--ios', kDestination, 'foo_destination']);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No implementation for iOS.'),
|
|
contains('SKIPPING: Nothing to test for target platform(s).'),
|
|
]));
|
|
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
|
|
});
|
|
|
|
test('running with correct destination', () async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformIOS: const PlatformDetails(PlatformSupport.inline)
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--ios',
|
|
kDestination,
|
|
'foo_destination',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('Successfully ran iOS xctest for plugin/example')
|
|
]));
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getTargetCheckCall(pluginExampleDirectory, 'ios'),
|
|
getConfigOnlyDarwinBuildCall(
|
|
pluginExampleDirectory, FlutterPlatform.ios),
|
|
getRunTestCall(pluginExampleDirectory, 'ios',
|
|
destination: 'foo_destination'),
|
|
]));
|
|
});
|
|
|
|
test('Not specifying --ios-destination assigns an available simulator',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformIOS: const PlatformDetails(PlatformSupport.inline)
|
|
});
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: jsonEncode(_kDeviceListMap)),
|
|
<String>['simctl', 'list']),
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']),
|
|
];
|
|
|
|
await runCapturingPrint(runner, <String>['native-test', '--ios']);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
const ProcessCall(
|
|
'xcrun',
|
|
<String>[
|
|
'simctl',
|
|
'list',
|
|
'devices',
|
|
'runtimes',
|
|
'available',
|
|
'--json',
|
|
],
|
|
null),
|
|
getTargetCheckCall(pluginExampleDirectory, 'ios'),
|
|
getConfigOnlyDarwinBuildCall(
|
|
pluginExampleDirectory, FlutterPlatform.ios),
|
|
getRunTestCall(pluginExampleDirectory, 'ios',
|
|
destination: 'id=$_simulatorDeviceId'),
|
|
]));
|
|
});
|
|
|
|
group('file filtering', () {
|
|
const List<String> files = <String>[
|
|
'pubspec.yaml',
|
|
'foo.dart',
|
|
'foo.java',
|
|
'foo.kt',
|
|
'foo.m',
|
|
'foo.swift',
|
|
'foo.cc',
|
|
'foo.cpp',
|
|
'foo.h',
|
|
];
|
|
for (final String file in files) {
|
|
test('runs command for changes to $file', () async {
|
|
createFakePackage('package_a', packagesDir);
|
|
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/package_a/$file
|
|
''')),
|
|
];
|
|
|
|
// The target platform is irrelevant here; because this repo's
|
|
// packages are fully federated, there's no need to distinguish
|
|
// the ignore list by target (e.g., skipping iOS tests if only Java or
|
|
// Kotlin files change), because package-level filering will already
|
|
// accomplish the same goal.
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['native-test', '--android']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for package_a'),
|
|
]));
|
|
});
|
|
}
|
|
|
|
test('skips commands if all files should be ignored', () async {
|
|
createFakePackage('package_a', packagesDir);
|
|
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
README.md
|
|
CODEOWNERS
|
|
packages/package_a/CHANGELOG.md
|
|
''')),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['native-test', 'android']);
|
|
|
|
expect(
|
|
output,
|
|
isNot(containsAllInOrder(<Matcher>[
|
|
contains('Running for package_a'),
|
|
])));
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('SKIPPING ALL PACKAGES'),
|
|
]));
|
|
});
|
|
});
|
|
});
|
|
|
|
group('macOS', () {
|
|
test('skip if macOS is not supported', () async {
|
|
createFakePlugin('plugin', packagesDir);
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['native-test', '--macos']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No implementation for macOS.'),
|
|
contains('SKIPPING: Nothing to test for target platform(s).'),
|
|
]));
|
|
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
|
|
});
|
|
|
|
test('skip if macOS is implemented in a federated package', () async {
|
|
createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformMacOS: const PlatformDetails(PlatformSupport.federated),
|
|
});
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['native-test', '--macos']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No implementation for macOS.'),
|
|
contains('SKIPPING: Nothing to test for target platform(s).'),
|
|
]));
|
|
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
|
|
});
|
|
|
|
test('runs for macOS plugin', () async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformMacOS: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--macos',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
contains(
|
|
contains('Successfully ran macOS xctest for plugin/example')));
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getTargetCheckCall(pluginExampleDirectory, 'macos'),
|
|
getConfigOnlyDarwinBuildCall(
|
|
pluginExampleDirectory, FlutterPlatform.macos),
|
|
getRunTestCall(pluginExampleDirectory, 'macos'),
|
|
]));
|
|
});
|
|
});
|
|
|
|
group('Android', () {
|
|
test('runs Java unit tests in Android implementation folder', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
'android/src/test/example_test.java',
|
|
],
|
|
);
|
|
|
|
await runCapturingPrint(runner, <String>['native-test', '--android']);
|
|
|
|
final Directory androidFolder = plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
androidFolder.childFile('gradlew').path,
|
|
const <String>[
|
|
'app:testDebugUnitTest',
|
|
'plugin:testDebugUnitTest',
|
|
],
|
|
androidFolder.path,
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('runs Java unit tests in example folder', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
'example/android/app/src/test/example_test.java',
|
|
],
|
|
);
|
|
|
|
await runCapturingPrint(runner, <String>['native-test', '--android']);
|
|
|
|
final Directory androidFolder = plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
androidFolder.childFile('gradlew').path,
|
|
const <String>[
|
|
'app:testDebugUnitTest',
|
|
'plugin:testDebugUnitTest',
|
|
],
|
|
androidFolder.path,
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('only runs plugin-level unit tests once', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
examples: <String>['example1', 'example2'],
|
|
extraFiles: <String>[
|
|
'example/example1/android/gradlew',
|
|
'example/example1/android/app/src/test/example_test.java',
|
|
'example/example2/android/gradlew',
|
|
'example/example2/android/app/src/test/example_test.java',
|
|
],
|
|
);
|
|
|
|
await runCapturingPrint(runner, <String>['native-test', '--android']);
|
|
|
|
final List<RepositoryPackage> examples = plugin.getExamples().toList();
|
|
final Directory androidFolder1 =
|
|
examples[0].platformDirectory(FlutterPlatform.android);
|
|
final Directory androidFolder2 =
|
|
examples[1].platformDirectory(FlutterPlatform.android);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
androidFolder1.childFile('gradlew').path,
|
|
const <String>[
|
|
'app:testDebugUnitTest',
|
|
'plugin:testDebugUnitTest',
|
|
],
|
|
androidFolder1.path,
|
|
),
|
|
ProcessCall(
|
|
androidFolder2.childFile('gradlew').path,
|
|
const <String>[
|
|
'app:testDebugUnitTest',
|
|
],
|
|
androidFolder2.path,
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('runs Java integration tests', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
'example/android/app/src/androidTest/IntegrationTest.java',
|
|
],
|
|
);
|
|
|
|
await runCapturingPrint(
|
|
runner, <String>['native-test', '--android', '--no-unit']);
|
|
|
|
final Directory androidFolder = plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
androidFolder.childFile('gradlew').path,
|
|
const <String>[
|
|
'app:connectedAndroidTest',
|
|
_androidIntegrationTestFilter,
|
|
_allAbiFlag,
|
|
],
|
|
androidFolder.path,
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'ignores Java integration test files using (or defining) DartIntegrationTest',
|
|
() async {
|
|
const String dartTestDriverRelativePath =
|
|
'android/app/src/androidTest/java/io/flutter/plugins/plugin/FlutterActivityTest.java';
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
'example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java',
|
|
'example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.kt',
|
|
'example/$dartTestDriverRelativePath',
|
|
],
|
|
);
|
|
|
|
final File dartTestDriverFile = childFileWithSubcomponents(
|
|
plugin.getExamples().first.directory,
|
|
p.posix.split(dartTestDriverRelativePath));
|
|
dartTestDriverFile.writeAsStringSync('''
|
|
import io.flutter.plugins.DartIntegrationTest;
|
|
import org.junit.runner.RunWith;
|
|
|
|
@DartIntegrationTest
|
|
@RunWith(FlutterTestRunner.class)
|
|
public class FlutterActivityTest {
|
|
}
|
|
''');
|
|
|
|
await runCapturingPrint(
|
|
runner, <String>['native-test', '--android', '--no-unit']);
|
|
|
|
// Nothing should run since those files are all
|
|
// integration_test-specific.
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'fails for Java integration tests Using FlutterTestRunner without @DartIntegrationTest',
|
|
() async {
|
|
const String dartTestDriverRelativePath =
|
|
'android/app/src/androidTest/java/io/flutter/plugins/plugin/FlutterActivityTest.java';
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
'example/$dartTestDriverRelativePath',
|
|
],
|
|
);
|
|
|
|
final File dartTestDriverFile = childFileWithSubcomponents(
|
|
plugin.getExamples().first.directory,
|
|
p.posix.split(dartTestDriverRelativePath));
|
|
dartTestDriverFile.writeAsStringSync('''
|
|
import io.flutter.plugins.DartIntegrationTest;
|
|
import org.junit.runner.RunWith;
|
|
|
|
@RunWith(FlutterTestRunner.class)
|
|
public class FlutterActivityTest {
|
|
}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['native-test', '--android', '--no-unit'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
contains(
|
|
contains(misconfiguredJavaIntegrationTestErrorExplanation)));
|
|
expect(
|
|
output,
|
|
contains(contains(
|
|
'example/android/app/src/androidTest/java/io/flutter/plugins/plugin/FlutterActivityTest.java')));
|
|
});
|
|
|
|
test('runs all tests when present', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'android/src/test/example_test.java',
|
|
'example/android/gradlew',
|
|
'example/android/app/src/androidTest/IntegrationTest.java',
|
|
],
|
|
);
|
|
|
|
await runCapturingPrint(runner, <String>['native-test', '--android']);
|
|
|
|
final Directory androidFolder = plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
androidFolder.childFile('gradlew').path,
|
|
const <String>[
|
|
'app:testDebugUnitTest',
|
|
'plugin:testDebugUnitTest',
|
|
],
|
|
androidFolder.path,
|
|
),
|
|
ProcessCall(
|
|
androidFolder.childFile('gradlew').path,
|
|
const <String>[
|
|
'app:connectedAndroidTest',
|
|
_androidIntegrationTestFilter,
|
|
_allAbiFlag,
|
|
],
|
|
androidFolder.path,
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('honors --no-unit', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'android/src/test/example_test.java',
|
|
'example/android/gradlew',
|
|
'example/android/app/src/androidTest/IntegrationTest.java',
|
|
],
|
|
);
|
|
|
|
await runCapturingPrint(
|
|
runner, <String>['native-test', '--android', '--no-unit']);
|
|
|
|
final Directory androidFolder = plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
androidFolder.childFile('gradlew').path,
|
|
const <String>[
|
|
'app:connectedAndroidTest',
|
|
_androidIntegrationTestFilter,
|
|
_allAbiFlag,
|
|
],
|
|
androidFolder.path,
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('honors --no-integration', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'android/src/test/example_test.java',
|
|
'example/android/gradlew',
|
|
'example/android/app/src/androidTest/IntegrationTest.java',
|
|
],
|
|
);
|
|
|
|
await runCapturingPrint(
|
|
runner, <String>['native-test', '--android', '--no-integration']);
|
|
|
|
final Directory androidFolder = plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
androidFolder.childFile('gradlew').path,
|
|
const <String>[
|
|
'app:testDebugUnitTest',
|
|
'plugin:testDebugUnitTest',
|
|
],
|
|
androidFolder.path,
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('runs a config-only build when the app needs to be built', () async {
|
|
final RepositoryPackage package = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/app/src/test/example_test.java',
|
|
],
|
|
);
|
|
final RepositoryPackage example = package.getExamples().first;
|
|
final Directory androidFolder =
|
|
example.platformDirectory(FlutterPlatform.android);
|
|
|
|
await runCapturingPrint(runner, <String>['native-test', '--android']);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
getFlutterCommand(mockPlatform),
|
|
const <String>['build', 'apk', '--config-only'],
|
|
example.path,
|
|
),
|
|
ProcessCall(
|
|
androidFolder.childFile('gradlew').path,
|
|
const <String>[
|
|
'app:testDebugUnitTest',
|
|
'plugin:testDebugUnitTest',
|
|
],
|
|
androidFolder.path,
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when the gradlew generation fails', () async {
|
|
createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/app/src/test/example_test.java',
|
|
],
|
|
);
|
|
|
|
processRunner
|
|
.mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
|
|
<FakeProcessInfo>[FakeProcessInfo(MockProcess(exitCode: 1))];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['native-test', '--android'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to configure Gradle project'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('logs missing test types', () async {
|
|
// No unit tests.
|
|
createFakePlugin(
|
|
'plugin1',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
'example/android/app/src/androidTest/IntegrationTest.java',
|
|
],
|
|
);
|
|
// No integration tests.
|
|
createFakePlugin(
|
|
'plugin2',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'android/src/test/example_test.java',
|
|
'example/android/gradlew',
|
|
],
|
|
);
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['native-test', '--android'],
|
|
errorHandler: (Error e) {
|
|
// Having no unit tests is fatal, but that's not the point of this
|
|
// test so just ignore the failure.
|
|
});
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No Android unit tests found for plugin1/example'),
|
|
contains('Running integration tests...'),
|
|
contains(
|
|
'No Android integration tests found for plugin2/example'),
|
|
contains('Running unit tests...'),
|
|
]));
|
|
});
|
|
|
|
test('fails when a unit test fails', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
'example/android/app/src/test/example_test.java',
|
|
],
|
|
);
|
|
|
|
final String gradlewPath = plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android)
|
|
.childFile('gradlew')
|
|
.path;
|
|
processRunner.mockProcessesForExecutable[gradlewPath] =
|
|
<FakeProcessInfo>[FakeProcessInfo(MockProcess(exitCode: 1))];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['native-test', '--android'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('plugin/example unit tests failed.'),
|
|
contains('The following packages had errors:'),
|
|
contains('plugin')
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when an integration test fails', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
'example/android/app/src/test/example_test.java',
|
|
'example/android/app/src/androidTest/IntegrationTest.java',
|
|
],
|
|
);
|
|
|
|
final String gradlewPath = plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android)
|
|
.childFile('gradlew')
|
|
.path;
|
|
processRunner.mockProcessesForExecutable[gradlewPath] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(), <String>['app:testDebugUnitTest']), // unit passes
|
|
FakeProcessInfo(MockProcess(exitCode: 1),
|
|
<String>['app:connectedAndroidTest']), // integration fails
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['native-test', '--android'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('plugin/example integration tests failed.'),
|
|
contains('The following packages had errors:'),
|
|
contains('plugin')
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails if there are no unit tests', () async {
|
|
createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
'example/android/app/src/androidTest/IntegrationTest.java',
|
|
],
|
|
);
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['native-test', '--android'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No Android unit tests found for plugin/example'),
|
|
contains(
|
|
'No unit tests ran. Plugins are required to have unit tests.'),
|
|
contains('The following packages had errors:'),
|
|
contains('plugin:\n'
|
|
' No unit tests ran (use --exclude if this is intentional).')
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('skips if Android is not supported', () async {
|
|
createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
);
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['native-test', '--android']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No implementation for Android.'),
|
|
contains('SKIPPING: Nothing to test for target platform(s).'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('skips when running no tests in integration-only mode', () async {
|
|
createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline)
|
|
},
|
|
);
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['native-test', '--android', '--no-unit']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No Android integration tests found for plugin/example'),
|
|
contains('SKIPPING: No tests found.'),
|
|
]),
|
|
);
|
|
});
|
|
});
|
|
|
|
group('Linux', () {
|
|
test('builds and runs unit tests', () async {
|
|
const String testBinaryRelativePath =
|
|
'build/linux/x64/release/bar/plugin_test';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/$testBinaryRelativePath'
|
|
], platformSupport: <String, PlatformDetails>{
|
|
platformLinux: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, _archDirX64);
|
|
|
|
final File testBinary = childFileWithSubcomponents(plugin.directory,
|
|
<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>[
|
|
getLinuxBuildCall(plugin),
|
|
ProcessCall(testBinary.path, const <String>[], null),
|
|
]));
|
|
});
|
|
|
|
test('only runs release unit tests', () async {
|
|
const String debugTestBinaryRelativePath =
|
|
'build/linux/x64/debug/bar/plugin_test';
|
|
const String releaseTestBinaryRelativePath =
|
|
'build/linux/x64/release/bar/plugin_test';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/$debugTestBinaryRelativePath',
|
|
'example/$releaseTestBinaryRelativePath'
|
|
], platformSupport: <String, PlatformDetails>{
|
|
platformLinux: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, _archDirX64);
|
|
|
|
final File releaseTestBinary = childFileWithSubcomponents(
|
|
plugin.directory,
|
|
<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!'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getLinuxBuildCall(plugin),
|
|
ProcessCall(releaseTestBinary.path, const <String>[], null),
|
|
]));
|
|
});
|
|
|
|
test('fails if CMake has not been configured', () async {
|
|
createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformLinux: 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('plugin:\n'
|
|
' Examples must be built before testing.')
|
|
]),
|
|
);
|
|
|
|
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
|
|
});
|
|
|
|
test('fails if there are no unit tests', () async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformLinux: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, _archDirX64);
|
|
|
|
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>[
|
|
getLinuxBuildCall(plugin),
|
|
]));
|
|
});
|
|
|
|
test('fails if a unit test fails', () async {
|
|
const String testBinaryRelativePath =
|
|
'build/linux/x64/release/bar/plugin_test';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/$testBinaryRelativePath'
|
|
], platformSupport: <String, PlatformDetails>{
|
|
platformLinux: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, _archDirX64);
|
|
|
|
final File testBinary = childFileWithSubcomponents(plugin.directory,
|
|
<String>['example', ...testBinaryRelativePath.split('/')]);
|
|
|
|
processRunner.mockProcessesForExecutable[testBinary.path] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(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>[
|
|
getLinuxBuildCall(plugin),
|
|
ProcessCall(testBinary.path, const <String>[], null),
|
|
]));
|
|
});
|
|
});
|
|
|
|
// Tests behaviors of implementation that is shared between iOS and macOS.
|
|
group('iOS or macOS', () {
|
|
test('fails if xcrun fails', () async {
|
|
createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformMacOS: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(exitCode: 1))
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['native-test', '--macos'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('The following packages had errors:'),
|
|
contains(' plugin'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('honors unit-only', () async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformMacOS: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--macos',
|
|
'--no-integration',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
contains(
|
|
contains('Successfully ran macOS xctest for plugin/example')));
|
|
|
|
// --no-integration should translate to '-only-testing:RunnerTests'.
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getTargetCheckCall(pluginExampleDirectory, 'macos'),
|
|
getConfigOnlyDarwinBuildCall(
|
|
pluginExampleDirectory, FlutterPlatform.macos),
|
|
getRunTestCall(pluginExampleDirectory, 'macos',
|
|
extraFlags: <String>['-only-testing:RunnerTests']),
|
|
]));
|
|
});
|
|
|
|
test('honors integration-only', () async {
|
|
final RepositoryPackage plugin1 = createFakePlugin(
|
|
'plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformMacOS: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin1);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--macos',
|
|
'--no-unit',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
contains(
|
|
contains('Successfully ran macOS xctest for plugin/example')));
|
|
|
|
// --no-unit should translate to '-only-testing:RunnerUITests'.
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getTargetCheckCall(pluginExampleDirectory, 'macos'),
|
|
getConfigOnlyDarwinBuildCall(
|
|
pluginExampleDirectory, FlutterPlatform.macos),
|
|
getRunTestCall(pluginExampleDirectory, 'macos',
|
|
extraFlags: <String>['-only-testing:RunnerUITests']),
|
|
]));
|
|
});
|
|
|
|
test('skips when the requested target is not present', () async {
|
|
final RepositoryPackage plugin1 = createFakePlugin(
|
|
'plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformMacOS: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin1);
|
|
|
|
// Simulate a project with unit tests but no integration tests...
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
getMockXcodebuildListProcess(<String>['RunnerTests']),
|
|
];
|
|
|
|
// ... then try to run only integration tests.
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--macos',
|
|
'--no-unit',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'No "RunnerUITests" target in plugin/example; skipping.'),
|
|
]));
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getTargetCheckCall(pluginExampleDirectory, 'macos'),
|
|
]));
|
|
});
|
|
|
|
test('fails if there are no unit tests', () async {
|
|
final RepositoryPackage plugin1 = createFakePlugin(
|
|
'plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformMacOS: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin1);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
getMockXcodebuildListProcess(<String>['RunnerUITests']),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['native-test', '--macos'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No "RunnerTests" target in plugin/example; skipping.'),
|
|
contains(
|
|
'No unit tests ran. Plugins are required to have unit tests.'),
|
|
contains('The following packages had errors:'),
|
|
contains('plugin:\n'
|
|
' No unit tests ran (use --exclude if this is intentional).'),
|
|
]));
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getTargetCheckCall(pluginExampleDirectory, 'macos'),
|
|
]));
|
|
});
|
|
|
|
test('fails if unable to check for requested target', () async {
|
|
final RepositoryPackage plugin1 = createFakePlugin(
|
|
'plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformMacOS: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin1);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(exitCode: 1), <String>['xcodebuild', '-list']),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--macos',
|
|
'--no-integration',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to check targets for plugin/example.'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getTargetCheckCall(pluginExampleDirectory, 'macos'),
|
|
]));
|
|
});
|
|
|
|
test('Xcode warnings exceptions list', () async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformIOS: const PlatformDetails(PlatformSupport.inline)
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: jsonEncode(_kDeviceListMap)),
|
|
<String>['simctl', 'list']),
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']),
|
|
];
|
|
|
|
await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--ios',
|
|
'--xcode-warnings-exceptions=plugin'
|
|
]);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
contains(
|
|
getRunTestCall(pluginExampleDirectory, 'ios',
|
|
destination: 'id=$_simulatorDeviceId',
|
|
treatWarningsAsErrors: false),
|
|
));
|
|
});
|
|
|
|
test('Xcode warnings exceptions file', () async {
|
|
final File configFile = packagesDir.childFile('exceptions.yaml');
|
|
await configFile.writeAsString('- plugin');
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformIOS: const PlatformDetails(PlatformSupport.inline)
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: jsonEncode(_kDeviceListMap)),
|
|
<String>['simctl', 'list']),
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']),
|
|
];
|
|
|
|
await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--ios',
|
|
'--xcode-warnings-exceptions=${configFile.path}'
|
|
]);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
contains(
|
|
getRunTestCall(pluginExampleDirectory, 'ios',
|
|
destination: 'id=$_simulatorDeviceId',
|
|
treatWarningsAsErrors: false),
|
|
));
|
|
});
|
|
|
|
test('treat warnings as errors if plugin not on exceptions list',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformIOS: const PlatformDetails(PlatformSupport.inline)
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: jsonEncode(_kDeviceListMap)),
|
|
<String>['simctl', 'list']),
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']),
|
|
];
|
|
|
|
await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--ios',
|
|
'--xcode-warnings-exceptions=foo,bar'
|
|
]);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
contains(
|
|
getRunTestCall(pluginExampleDirectory, 'ios',
|
|
destination: 'id=$_simulatorDeviceId'),
|
|
));
|
|
});
|
|
});
|
|
|
|
group('multiplatform', () {
|
|
test('runs all platfroms when supported', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
'android/src/test/example_test.java',
|
|
],
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline),
|
|
platformIOS: const PlatformDetails(PlatformSupport.inline),
|
|
platformMacOS: const PlatformDetails(PlatformSupport.inline),
|
|
},
|
|
);
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin);
|
|
final Directory androidFolder =
|
|
pluginExampleDirectory.childDirectory('android');
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']), // iOS list
|
|
FakeProcessInfo(MockProcess(),
|
|
<String>['xcodebuild', 'clean', 'test']), // iOS run
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']), // macOS list
|
|
FakeProcessInfo(MockProcess(),
|
|
<String>['xcodebuild', 'clean', 'test']), // macOS run
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--android',
|
|
'--ios',
|
|
'--macos',
|
|
kDestination,
|
|
'foo_destination',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAll(<Matcher>[
|
|
contains('Running Android tests for plugin/example'),
|
|
contains('Successfully ran iOS xctest for plugin/example'),
|
|
contains('Successfully ran macOS xctest for plugin/example'),
|
|
]));
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
androidFolder.childFile('gradlew').path,
|
|
const <String>[
|
|
'app:testDebugUnitTest',
|
|
'plugin:testDebugUnitTest',
|
|
],
|
|
androidFolder.path),
|
|
getTargetCheckCall(pluginExampleDirectory, 'ios'),
|
|
getConfigOnlyDarwinBuildCall(
|
|
pluginExampleDirectory, FlutterPlatform.ios),
|
|
getRunTestCall(pluginExampleDirectory, 'ios',
|
|
destination: 'foo_destination'),
|
|
getTargetCheckCall(pluginExampleDirectory, 'macos'),
|
|
getConfigOnlyDarwinBuildCall(
|
|
pluginExampleDirectory, FlutterPlatform.macos),
|
|
getRunTestCall(pluginExampleDirectory, 'macos'),
|
|
]));
|
|
});
|
|
|
|
test('runs only macOS for a macOS plugin', () async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformMacOS: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--ios',
|
|
'--macos',
|
|
kDestination,
|
|
'foo_destination',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No implementation for iOS.'),
|
|
contains('Successfully ran macOS xctest for plugin/example'),
|
|
]));
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getTargetCheckCall(pluginExampleDirectory, 'macos'),
|
|
getConfigOnlyDarwinBuildCall(
|
|
pluginExampleDirectory, FlutterPlatform.macos),
|
|
getRunTestCall(pluginExampleDirectory, 'macos'),
|
|
]));
|
|
});
|
|
|
|
test('runs only iOS for a iOS plugin', () async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformIOS: const PlatformDetails(PlatformSupport.inline)
|
|
});
|
|
|
|
final Directory pluginExampleDirectory = getExampleDir(plugin);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--ios',
|
|
'--macos',
|
|
kDestination,
|
|
'foo_destination',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No implementation for macOS.'),
|
|
contains('Successfully ran iOS xctest for plugin/example')
|
|
]));
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getTargetCheckCall(pluginExampleDirectory, 'ios'),
|
|
getConfigOnlyDarwinBuildCall(
|
|
pluginExampleDirectory, FlutterPlatform.ios),
|
|
getRunTestCall(pluginExampleDirectory, 'ios',
|
|
destination: 'foo_destination'),
|
|
]));
|
|
});
|
|
|
|
test('skips when nothing is supported', () async {
|
|
createFakePlugin('plugin', packagesDir);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--android',
|
|
'--ios',
|
|
'--macos',
|
|
'--windows',
|
|
kDestination,
|
|
'foo_destination',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No implementation for Android.'),
|
|
contains('No implementation for iOS.'),
|
|
contains('No implementation for macOS.'),
|
|
contains('SKIPPING: Nothing to test for target platform(s).'),
|
|
]));
|
|
|
|
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
|
|
});
|
|
|
|
test('skips Dart-only plugins', () async {
|
|
createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformMacOS: const PlatformDetails(PlatformSupport.inline,
|
|
hasDartCode: true, hasNativeCode: false),
|
|
platformWindows: const PlatformDetails(PlatformSupport.inline,
|
|
hasDartCode: true, hasNativeCode: false),
|
|
},
|
|
);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--macos',
|
|
'--windows',
|
|
kDestination,
|
|
'foo_destination',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No native code for macOS.'),
|
|
contains('No native code for Windows.'),
|
|
contains('SKIPPING: Nothing to test for target platform(s).'),
|
|
]));
|
|
|
|
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
|
|
});
|
|
|
|
test('failing one platform does not stop the tests', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline),
|
|
platformIOS: const PlatformDetails(PlatformSupport.inline),
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
'example/android/app/src/test/example_test.java',
|
|
],
|
|
);
|
|
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
getMockXcodebuildListProcess(
|
|
<String>['RunnerTests', 'RunnerUITests']),
|
|
];
|
|
|
|
// Simulate failing Android, but not iOS.
|
|
final String gradlewPath = plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android)
|
|
.childFile('gradlew')
|
|
.path;
|
|
processRunner.mockProcessesForExecutable[gradlewPath] =
|
|
<FakeProcessInfo>[FakeProcessInfo(MockProcess(exitCode: 1))];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--android',
|
|
'--ios',
|
|
'--ios-destination',
|
|
'foo_destination',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running tests for Android...'),
|
|
contains('plugin/example unit tests failed.'),
|
|
contains('Running tests for iOS...'),
|
|
contains('Successfully ran iOS xctest for plugin/example'),
|
|
contains('The following packages had errors:'),
|
|
contains('plugin:\n'
|
|
' Android')
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('failing multiple platforms reports multiple failures', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformAndroid: const PlatformDetails(PlatformSupport.inline),
|
|
platformIOS: const PlatformDetails(PlatformSupport.inline),
|
|
},
|
|
extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
'example/android/app/src/test/example_test.java',
|
|
],
|
|
);
|
|
|
|
// Simulate failing Android.
|
|
final String gradlewPath = plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android)
|
|
.childFile('gradlew')
|
|
.path;
|
|
processRunner.mockProcessesForExecutable[gradlewPath] =
|
|
<FakeProcessInfo>[FakeProcessInfo(MockProcess(exitCode: 1))];
|
|
// Simulate failing iOS.
|
|
processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(exitCode: 1))
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--android',
|
|
'--ios',
|
|
'--ios-destination',
|
|
'foo_destination',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running tests for Android...'),
|
|
contains('Running tests for iOS...'),
|
|
contains('The following packages had errors:'),
|
|
contains('plugin:\n'
|
|
' Android\n'
|
|
' iOS')
|
|
]),
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
group('test native_test_command on Windows', () {
|
|
late MockPlatform mockPlatform;
|
|
late Directory packagesDir;
|
|
late CommandRunner<void> runner;
|
|
late RecordingProcessRunner processRunner;
|
|
late GitDir gitDir;
|
|
|
|
setUp(() {
|
|
mockPlatform = MockPlatform(isWindows: true);
|
|
(:packagesDir, :processRunner, gitProcessRunner: _, :gitDir) =
|
|
configureBaseCommandMocks(platform: mockPlatform);
|
|
});
|
|
|
|
// Returns the ProcessCall to expect for build the Windows unit tests for
|
|
// the given plugin.
|
|
ProcessCall getWindowsBuildCall(RepositoryPackage plugin, String? arch) {
|
|
Directory projectDir = getExampleDir(plugin)
|
|
.childDirectory('build')
|
|
.childDirectory('windows');
|
|
if (arch != null) {
|
|
projectDir = projectDir.childDirectory(arch);
|
|
}
|
|
return ProcessCall(
|
|
_fakeCmakeCommand,
|
|
<String>[
|
|
'--build',
|
|
projectDir.path,
|
|
'--target',
|
|
'unit_tests',
|
|
'--config',
|
|
'Debug'
|
|
],
|
|
null);
|
|
}
|
|
|
|
group('Windows x64', () {
|
|
setUp(() {
|
|
final NativeTestCommand command = NativeTestCommand(packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: gitDir,
|
|
abi: Abi.windowsX64);
|
|
|
|
runner = CommandRunner<void>(
|
|
'native_test_command', 'Test for native_test_command');
|
|
runner.addCommand(command);
|
|
});
|
|
|
|
test('runs unit tests', () async {
|
|
const String x64TestBinaryRelativePath =
|
|
'build/windows/x64/Debug/bar/plugin_test.exe';
|
|
const String arm64TestBinaryRelativePath =
|
|
'build/windows/arm64/Debug/bar/plugin_test.exe';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/$x64TestBinaryRelativePath',
|
|
'example/$arm64TestBinaryRelativePath',
|
|
], platformSupport: <String, PlatformDetails>{
|
|
platformWindows: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, _archDirX64);
|
|
|
|
final File testBinary = childFileWithSubcomponents(plugin.directory,
|
|
<String>['example', ...x64TestBinaryRelativePath.split('/')]);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--windows',
|
|
'--no-integration',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running plugin_test.exe...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getWindowsBuildCall(plugin, _archDirX64),
|
|
ProcessCall(testBinary.path, const <String>[], null),
|
|
]));
|
|
});
|
|
|
|
test('runs unit tests with legacy build output', () async {
|
|
const String testBinaryRelativePath =
|
|
'build/windows/Debug/bar/plugin_test.exe';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/$testBinaryRelativePath'
|
|
], platformSupport: <String, PlatformDetails>{
|
|
platformWindows: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, null);
|
|
|
|
final File testBinary = childFileWithSubcomponents(plugin.directory,
|
|
<String>['example', ...testBinaryRelativePath.split('/')]);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--windows',
|
|
'--no-integration',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running plugin_test.exe...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getWindowsBuildCall(plugin, null),
|
|
ProcessCall(testBinary.path, const <String>[], null),
|
|
]));
|
|
});
|
|
|
|
test('only runs debug unit tests', () async {
|
|
const String debugTestBinaryRelativePath =
|
|
'build/windows/x64/Debug/bar/plugin_test.exe';
|
|
const String releaseTestBinaryRelativePath =
|
|
'build/windows/x64/Release/bar/plugin_test.exe';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/$debugTestBinaryRelativePath',
|
|
'example/$releaseTestBinaryRelativePath'
|
|
], platformSupport: <String, PlatformDetails>{
|
|
platformWindows: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, _archDirX64);
|
|
|
|
final File debugTestBinary = childFileWithSubcomponents(
|
|
plugin.directory,
|
|
<String>['example', ...debugTestBinaryRelativePath.split('/')]);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--windows',
|
|
'--no-integration',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running plugin_test.exe...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getWindowsBuildCall(plugin, _archDirX64),
|
|
ProcessCall(debugTestBinary.path, const <String>[], null),
|
|
]));
|
|
});
|
|
|
|
test('only runs debug unit tests with legacy build output', () async {
|
|
const String debugTestBinaryRelativePath =
|
|
'build/windows/Debug/bar/plugin_test.exe';
|
|
const String releaseTestBinaryRelativePath =
|
|
'build/windows/Release/bar/plugin_test.exe';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/$debugTestBinaryRelativePath',
|
|
'example/$releaseTestBinaryRelativePath'
|
|
], platformSupport: <String, PlatformDetails>{
|
|
platformWindows: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, null);
|
|
|
|
final File debugTestBinary = childFileWithSubcomponents(
|
|
plugin.directory,
|
|
<String>['example', ...debugTestBinaryRelativePath.split('/')]);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--windows',
|
|
'--no-integration',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running plugin_test.exe...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getWindowsBuildCall(plugin, null),
|
|
ProcessCall(debugTestBinary.path, const <String>[], null),
|
|
]));
|
|
});
|
|
|
|
test('fails if CMake has not been configured', () async {
|
|
createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformWindows: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--windows',
|
|
'--no-integration',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('plugin:\n'
|
|
' Examples must be built before testing.')
|
|
]),
|
|
);
|
|
|
|
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
|
|
});
|
|
|
|
test('fails if there are no unit tests', () async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
platformSupport: <String, PlatformDetails>{
|
|
platformWindows: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, _archDirX64);
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--windows',
|
|
'--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>[
|
|
getWindowsBuildCall(plugin, _archDirX64),
|
|
]));
|
|
});
|
|
|
|
test('fails if a unit test fails', () async {
|
|
const String testBinaryRelativePath =
|
|
'build/windows/x64/Debug/bar/plugin_test.exe';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/$testBinaryRelativePath'
|
|
], platformSupport: <String, PlatformDetails>{
|
|
platformWindows: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, _archDirX64);
|
|
|
|
final File testBinary = childFileWithSubcomponents(plugin.directory,
|
|
<String>['example', ...testBinaryRelativePath.split('/')]);
|
|
|
|
processRunner.mockProcessesForExecutable[testBinary.path] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(exitCode: 1)),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--windows',
|
|
'--no-integration',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running plugin_test.exe...'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getWindowsBuildCall(plugin, _archDirX64),
|
|
ProcessCall(testBinary.path, const <String>[], null),
|
|
]));
|
|
});
|
|
});
|
|
|
|
group('Windows arm64', () {
|
|
setUp(() {
|
|
final NativeTestCommand command = NativeTestCommand(packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: gitDir,
|
|
abi: Abi.windowsArm64);
|
|
|
|
runner = CommandRunner<void>(
|
|
'native_test_command', 'Test for native_test_command');
|
|
runner.addCommand(command);
|
|
});
|
|
|
|
test('runs unit tests', () async {
|
|
const String x64TestBinaryRelativePath =
|
|
'build/windows/x64/Debug/bar/plugin_test.exe';
|
|
const String arm64TestBinaryRelativePath =
|
|
'build/windows/arm64/Debug/bar/plugin_test.exe';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/$x64TestBinaryRelativePath',
|
|
'example/$arm64TestBinaryRelativePath',
|
|
], platformSupport: <String, PlatformDetails>{
|
|
platformWindows: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, _archDirArm64);
|
|
|
|
final File testBinary = childFileWithSubcomponents(plugin.directory,
|
|
<String>['example', ...arm64TestBinaryRelativePath.split('/')]);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--windows',
|
|
'--no-integration',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running plugin_test.exe...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getWindowsBuildCall(plugin, _archDirArm64),
|
|
ProcessCall(testBinary.path, const <String>[], null),
|
|
]));
|
|
});
|
|
|
|
test('falls back to x64 unit tests if arm64 is not built', () async {
|
|
const String x64TestBinaryRelativePath =
|
|
'build/windows/x64/Debug/bar/plugin_test.exe';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/$x64TestBinaryRelativePath',
|
|
], platformSupport: <String, PlatformDetails>{
|
|
platformWindows: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, _archDirX64);
|
|
|
|
final File testBinary = childFileWithSubcomponents(plugin.directory,
|
|
<String>['example', ...x64TestBinaryRelativePath.split('/')]);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--windows',
|
|
'--no-integration',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running plugin_test.exe...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getWindowsBuildCall(plugin, _archDirX64),
|
|
ProcessCall(testBinary.path, const <String>[], null),
|
|
]));
|
|
});
|
|
|
|
test('only runs debug unit tests', () async {
|
|
const String debugTestBinaryRelativePath =
|
|
'build/windows/arm64/Debug/bar/plugin_test.exe';
|
|
const String releaseTestBinaryRelativePath =
|
|
'build/windows/arm64/Release/bar/plugin_test.exe';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/$debugTestBinaryRelativePath',
|
|
'example/$releaseTestBinaryRelativePath'
|
|
], platformSupport: <String, PlatformDetails>{
|
|
platformWindows: const PlatformDetails(PlatformSupport.inline),
|
|
});
|
|
_createFakeCMakeCache(plugin, mockPlatform, _archDirArm64);
|
|
|
|
final File debugTestBinary = childFileWithSubcomponents(
|
|
plugin.directory,
|
|
<String>['example', ...debugTestBinaryRelativePath.split('/')]);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'native-test',
|
|
'--windows',
|
|
'--no-integration',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running plugin_test.exe...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
getWindowsBuildCall(plugin, _archDirArm64),
|
|
ProcessCall(debugTestBinary.path, const <String>[], null),
|
|
]));
|
|
});
|
|
});
|
|
});
|
|
}
|