Begin migrating tools to NNBD (#3891)

- Updates dependencies to null-safe versions
- Migrates common.dart (which doesn't depend on anything)
- Migrates common_tests.dart and its one dependency, utils.dart
- Adds build_runner for Mockito mock generation
- Adds a new utility methods for getting arguments that handle both the casting and the removal of nullability to address a common problematic pattern while migrating code.
  - Converts all files, not just the migrated ones, to those new helpers.

Migrating common.dart and utils.dart should unblock a command-by-command migration to null safety.

Reverts the separate of podspect lints into a step that doesn't do a Flutter upgrade
(https://github.com/flutter/plugins/pull/3700) because without that step we had a
version of Dart too old to run null-safe tooling.

First step of https://github.com/flutter/flutter/issues/81912
This commit is contained in:
stuartmorgan
2021-05-14 20:04:26 -04:00
committed by GitHub
parent e46aa5583f
commit bd0081258a
37 changed files with 397 additions and 179 deletions

View File

@ -2,4 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
export 'package:flutter_plugin_tools/src/main.dart';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'package:file/file.dart';
@ -42,8 +44,8 @@ class AnalyzeCommand extends PluginCommand {
continue;
}
final bool allowed = (argResults[_customAnalysisFlag] as List<String>)
.any((String directory) =>
final bool allowed = (getStringListArg(_customAnalysisFlag)).any(
(String directory) =>
directory != null &&
directory.isNotEmpty &&
p.isWithin(p.join(packagesDir.path, directory), file.path));

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:io' as io;
@ -52,7 +54,7 @@ class BuildExamplesCommand extends PluginCommand {
];
final Map<String, bool> platforms = <String, bool>{
for (final String platform in platformSwitches)
platform: argResults[platform] as bool
platform: getBoolArg(platform)
};
if (!platforms.values.any((bool enabled) => enabled)) {
print(
@ -63,7 +65,7 @@ class BuildExamplesCommand extends PluginCommand {
final String flutterCommand =
const LocalPlatform().isWindows ? 'flutter.bat' : 'flutter';
final String enableExperiment = argResults[kEnableExperiment] as String;
final String enableExperiment = getStringArg(kEnableExperiment);
final List<String> failingPackages = <String>[];
await for (final Directory plugin in getPlugins()) {

View File

@ -12,14 +12,13 @@ import 'package:colorize/colorize.dart';
import 'package:file/file.dart';
import 'package:git/git.dart';
import 'package:http/http.dart' as http;
import 'package:meta/meta.dart';
import 'package:path/path.dart' as p;
import 'package:pub_semver/pub_semver.dart';
import 'package:yaml/yaml.dart';
/// The signature for a print handler for commands that allow overriding the
/// print destination.
typedef Print = void Function(Object object);
typedef Print = void Function(Object? object);
/// Key for windows platform.
const String kWindows = 'windows';
@ -50,7 +49,7 @@ const String kEnableExperiment = 'enable-experiment';
/// Returns whether the given directory contains a Flutter package.
bool isFlutterPackage(FileSystemEntity entity, FileSystem fileSystem) {
if (entity == null || entity is! Directory) {
if (entity is! Directory) {
return false;
}
@ -59,7 +58,7 @@ bool isFlutterPackage(FileSystemEntity entity, FileSystem fileSystem) {
fileSystem.file(p.join(entity.path, 'pubspec.yaml'));
final YamlMap pubspecYaml =
loadYaml(pubspecFile.readAsStringSync()) as YamlMap;
final YamlMap dependencies = pubspecYaml['dependencies'] as YamlMap;
final YamlMap? dependencies = pubspecYaml['dependencies'] as YamlMap?;
if (dependencies == null) {
return false;
}
@ -87,7 +86,7 @@ bool pluginSupportsPlatform(
platform == kMacos ||
platform == kWindows ||
platform == kLinux);
if (entity == null || entity is! Directory) {
if (entity is! Directory) {
return false;
}
@ -96,15 +95,15 @@ bool pluginSupportsPlatform(
fileSystem.file(p.join(entity.path, 'pubspec.yaml'));
final YamlMap pubspecYaml =
loadYaml(pubspecFile.readAsStringSync()) as YamlMap;
final YamlMap flutterSection = pubspecYaml['flutter'] as YamlMap;
final YamlMap? flutterSection = pubspecYaml['flutter'] as YamlMap?;
if (flutterSection == null) {
return false;
}
final YamlMap pluginSection = flutterSection['plugin'] as YamlMap;
final YamlMap? pluginSection = flutterSection['plugin'] as YamlMap?;
if (pluginSection == null) {
return false;
}
final YamlMap platforms = pluginSection['platforms'] as YamlMap;
final YamlMap? platforms = pluginSection['platforms'] as YamlMap?;
if (platforms == null) {
// Legacy plugin specs are assumed to support iOS and Android.
if (!pluginSection.containsKey('platforms')) {
@ -151,7 +150,7 @@ bool isLinuxPlugin(FileSystemEntity entity, FileSystem fileSystem) {
}
/// Throws a [ToolExit] with `exitCode` and log the `errorMessage` in red.
void printErrorAndExit({@required String errorMessage, int exitCode = 1}) {
void printErrorAndExit({required String errorMessage, int exitCode = 1}) {
final Colorize redError = Colorize(errorMessage)..red();
print(redError);
throw ToolExit(exitCode);
@ -236,17 +235,17 @@ abstract class PluginCommand extends Command<void> {
/// The git directory to use. By default it uses the parent directory.
///
/// This can be mocked for testing.
final GitDir gitDir;
final GitDir? gitDir;
int _shardIndex;
int _shardCount;
int? _shardIndex;
int? _shardCount;
/// The shard of the overall command execution that this instance should run.
int get shardIndex {
if (_shardIndex == null) {
_checkSharding();
}
return _shardIndex;
return _shardIndex!;
}
/// The number of shards this command is divided into.
@ -254,12 +253,27 @@ abstract class PluginCommand extends Command<void> {
if (_shardCount == null) {
_checkSharding();
}
return _shardCount;
return _shardCount!;
}
/// Convenience accessor for boolean arguments.
bool getBoolArg(String key) {
return (argResults![key] as bool?) ?? false;
}
/// Convenience accessor for String arguments.
String getStringArg(String key) {
return (argResults![key] as String?) ?? '';
}
/// Convenience accessor for List<String> arguments.
List<String> getStringListArg(String key) {
return (argResults![key] as List<String>?) ?? <String>[];
}
void _checkSharding() {
final int shardIndex = int.tryParse(argResults[_shardIndexArg] as String);
final int shardCount = int.tryParse(argResults[_shardCountArg] as String);
final int? shardIndex = int.tryParse(getStringArg(_shardIndexArg));
final int? shardCount = int.tryParse(getStringArg(_shardCountArg));
if (shardIndex == null) {
usageException('$_shardIndexArg must be an integer');
}
@ -317,12 +331,10 @@ abstract class PluginCommand extends Command<void> {
/// is a sibling of the packages directory. This is used for a small number
/// of packages in the flutter/packages repository.
Stream<Directory> _getAllPlugins() async* {
Set<String> plugins =
Set<String>.from(argResults[_pluginsArg] as List<String>);
Set<String> plugins = Set<String>.from(getStringListArg(_pluginsArg));
final Set<String> excludedPlugins =
Set<String>.from(argResults[_excludeArg] as List<String>);
final bool runOnChangedPackages =
argResults[_runOnChangedPackagesArg] as bool;
Set<String>.from(getStringListArg(_excludeArg));
final bool runOnChangedPackages = getBoolArg(_runOnChangedPackagesArg);
if (plugins.isEmpty && runOnChangedPackages) {
plugins = await _getChangedPackages();
}
@ -429,9 +441,9 @@ abstract class PluginCommand extends Command<void> {
/// Throws tool exit if [gitDir] nor root directory is a git directory.
Future<GitVersionFinder> retrieveVersionFinder() async {
final String rootDir = packagesDir.parent.absolute.path;
final String baseSha = argResults[_kBaseSha] as String;
final String baseSha = getStringArg(_kBaseSha);
GitDir baseGitDir = gitDir;
GitDir? baseGitDir = gitDir;
if (baseGitDir == null) {
if (!await GitDir.isGitDir(rootDir)) {
printErrorAndExit(
@ -490,7 +502,7 @@ class ProcessRunner {
Future<int> runAndStream(
String executable,
List<String> args, {
Directory workingDir,
Directory? workingDir,
bool exitOnError = false,
}) async {
print(
@ -522,7 +534,7 @@ class ProcessRunner {
///
/// Returns the [io.ProcessResult] of the [executable].
Future<io.ProcessResult> run(String executable, List<String> args,
{Directory workingDir,
{Directory? workingDir,
bool exitOnError = false,
bool logOnError = false,
Encoding stdoutEncoding = io.systemEncoding,
@ -550,15 +562,15 @@ class ProcessRunner {
/// passing [workingDir].
///
/// Returns the started [io.Process].
Future<io.Process> start(String executable, List<String> args,
{Directory workingDirectory}) async {
Future<io.Process?> start(String executable, List<String> args,
{Directory? workingDirectory}) async {
final io.Process process = await io.Process.start(executable, args,
workingDirectory: workingDirectory?.path);
return process;
}
String _getErrorString(String executable, List<String> args,
{Directory workingDir}) {
{Directory? workingDir}) {
final String workdir = workingDir == null ? '' : ' in ${workingDir.path}';
return 'ERROR: Unable to execute "$executable ${args.join(' ')}"$workdir.';
}
@ -569,7 +581,7 @@ class PubVersionFinder {
/// Constructor.
///
/// Note: you should manually close the [httpClient] when done using the finder.
PubVersionFinder({this.pubHost = defaultPubHost, @required this.httpClient});
PubVersionFinder({this.pubHost = defaultPubHost, required this.httpClient});
/// The default pub host to use.
static const String defaultPubHost = 'https://pub.dev';
@ -584,8 +596,8 @@ class PubVersionFinder {
/// Get the package version on pub.
Future<PubVersionFinderResponse> getPackageVersion(
{@required String package}) async {
assert(package != null && package.isNotEmpty);
{required String package}) async {
assert(package.isNotEmpty);
final Uri pubHostUri = Uri.parse(pubHost);
final Uri url = pubHostUri.replace(path: '/packages/$package.json');
final http.Response response = await httpClient.get(url);
@ -618,8 +630,8 @@ class PubVersionFinder {
class PubVersionFinderResponse {
/// Constructor.
PubVersionFinderResponse({this.versions, this.result, this.httpResponse}) {
if (versions != null && versions.isNotEmpty) {
versions.sort((Version a, Version b) {
if (versions != null && versions!.isNotEmpty) {
versions!.sort((Version a, Version b) {
// TODO(cyanglaz): Think about how to handle pre-release version with [Version.prioritize].
// https://github.com/flutter/flutter/issues/82222
return b.compareTo(a);
@ -631,13 +643,13 @@ class PubVersionFinderResponse {
///
/// This is sorted by largest to smallest, so the first element in the list is the largest version.
/// Might be `null` if the [result] is not [PubVersionFinderResult.success].
final List<Version> versions;
final List<Version>? versions;
/// The result of the version finder.
final PubVersionFinderResult result;
final PubVersionFinderResult? result;
/// The response object of the http request.
final http.Response httpResponse;
final http.Response? httpResponse;
}
/// An enum representing the result of [PubVersionFinder].
@ -667,7 +679,7 @@ class GitVersionFinder {
final GitDir baseGitDir;
/// The base sha used to get diff.
final String baseSha;
final String? baseSha;
static bool _isPubspec(String file) {
return file.trim().endsWith('pubspec.yaml');
@ -684,8 +696,7 @@ class GitVersionFinder {
final io.ProcessResult changedFilesCommand = await baseGitDir
.runCommand(<String>['diff', '--name-only', baseSha, 'HEAD']);
print('Determine diff with base sha: $baseSha');
final String changedFilesStdout =
changedFilesCommand.stdout.toString() ?? '';
final String changedFilesStdout = changedFilesCommand.stdout.toString();
if (changedFilesStdout.isEmpty) {
return <String>[];
}
@ -696,7 +707,8 @@ class GitVersionFinder {
/// Get the package version specified in the pubspec file in `pubspecPath` and
/// at the revision of `gitRef` (defaulting to the base if not provided).
Future<Version> getPackageVersion(String pubspecPath, {String gitRef}) async {
Future<Version?> getPackageVersion(String pubspecPath,
{String? gitRef}) async {
final String ref = gitRef ?? (await _getBaseSha());
io.ProcessResult gitShow;
@ -707,20 +719,19 @@ class GitVersionFinder {
return null;
}
final String fileContent = gitShow.stdout as String;
final String versionString = loadYaml(fileContent)['version'] as String;
final String? versionString = loadYaml(fileContent)['version'] as String?;
return versionString == null ? null : Version.parse(versionString);
}
Future<String> _getBaseSha() async {
if (baseSha != null && baseSha.isNotEmpty) {
return baseSha;
if (baseSha != null && baseSha!.isNotEmpty) {
return baseSha!;
}
io.ProcessResult baseShaFromMergeBase = await baseGitDir.runCommand(
<String>['merge-base', '--fork-point', 'FETCH_HEAD', 'HEAD'],
throwOnError: false);
if (baseShaFromMergeBase == null ||
baseShaFromMergeBase.stderr != null ||
if (baseShaFromMergeBase.stderr != null ||
baseShaFromMergeBase.stdout == null) {
baseShaFromMergeBase = await baseGitDir
.runCommand(<String>['merge-base', 'FETCH_HEAD', 'HEAD']);

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:io' as io;

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'package:file/file.dart';
import 'package:path/path.dart' as p;
@ -53,10 +55,10 @@ class DriveExamplesCommand extends PluginCommand {
Future<void> run() async {
final List<String> failingTests = <String>[];
final List<String> pluginsWithoutTests = <String>[];
final bool isLinux = argResults[kLinux] == true;
final bool isMacos = argResults[kMacos] == true;
final bool isWeb = argResults[kWeb] == true;
final bool isWindows = argResults[kWindows] == true;
final bool isLinux = getBoolArg(kLinux);
final bool isMacos = getBoolArg(kMacos);
final bool isWeb = getBoolArg(kWeb);
final bool isWindows = getBoolArg(kWindows);
await for (final Directory plugin in getPlugins()) {
final String pluginName = plugin.basename;
if (pluginName.endsWith('_platform_interface') &&
@ -140,8 +142,7 @@ Tried searching for the following:
final List<String> driveArgs = <String>['drive'];
final String enableExperiment =
argResults[kEnableExperiment] as String;
final String enableExperiment = getStringArg(kEnableExperiment);
if (enableExperiment.isNotEmpty) {
driveArgs.add('--enable-experiment=$enableExperiment');
}
@ -222,12 +223,12 @@ Tried searching for the following:
Future<bool> _pluginSupportedOnCurrentPlatform(
FileSystemEntity plugin, FileSystem fileSystem) async {
final bool isAndroid = argResults[kAndroid] == true;
final bool isIOS = argResults[kIos] == true;
final bool isLinux = argResults[kLinux] == true;
final bool isMacos = argResults[kMacos] == true;
final bool isWeb = argResults[kWeb] == true;
final bool isWindows = argResults[kWindows] == true;
final bool isAndroid = getBoolArg(kAndroid);
final bool isIOS = getBoolArg(kIos);
final bool isLinux = getBoolArg(kLinux);
final bool isMacos = getBoolArg(kMacos);
final bool isWeb = getBoolArg(kWeb);
final bool isWindows = getBoolArg(kWindows);
if (isAndroid) {
return isAndroidPlugin(plugin, fileSystem);
}

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:io' as io;
@ -30,7 +32,7 @@ class FirebaseTestLabCommand extends PluginCommand {
defaultsTo:
p.join(io.Platform.environment['HOME'], 'gcloud-service-key.json'));
argParser.addOption('test-run-id',
defaultsTo: Uuid().v4(),
defaultsTo: const Uuid().v4(),
help:
'Optional string to append to the results path, to avoid conflicts. '
'Randomly chosen on each invocation if none is provided. '
@ -78,7 +80,7 @@ class FirebaseTestLabCommand extends PluginCommand {
<String>[
'auth',
'activate-service-account',
'--key-file=${argResults['service-key']}',
'--key-file=${getStringArg('service-key')}',
],
exitOnError: true,
logOnError: true,
@ -87,7 +89,7 @@ class FirebaseTestLabCommand extends PluginCommand {
'config',
'set',
'project',
argResults['project'] as String,
getStringArg('project'),
]);
if (exitCode == 0) {
_print('\nFirebase project configured.');
@ -125,7 +127,7 @@ class FirebaseTestLabCommand extends PluginCommand {
final Directory androidDirectory =
fileSystem.directory(p.join(exampleDirectory.path, 'android'));
final String enableExperiment = argResults[kEnableExperiment] as String;
final String enableExperiment = getStringArg(kEnableExperiment);
final String encodedEnableExperiment =
Uri.encodeComponent('--enable-experiment=$enableExperiment');
@ -213,7 +215,7 @@ class FirebaseTestLabCommand extends PluginCommand {
continue;
}
final String buildId = io.Platform.environment['CIRRUS_BUILD_ID'];
final String testRunId = argResults['test-run-id'] as String;
final String testRunId = getStringArg('test-run-id');
final String resultsDir =
'plugins_android_test/$packageName/$buildId/$testRunId/${resultsCounter++}/';
final List<String> args = <String>[
@ -229,10 +231,10 @@ class FirebaseTestLabCommand extends PluginCommand {
'build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk',
'--timeout',
'5m',
'--results-bucket=${argResults['results-bucket']}',
'--results-bucket=${getStringArg('results-bucket')}',
'--results-dir=$resultsDir',
];
for (final String device in argResults['device'] as List<String>) {
for (final String device in getStringListArg('device')) {
args.addAll(<String>['--device', device]);
}
exitCode = await processRunner.runAndStream('gcloud', args,

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
@ -13,8 +15,8 @@ import 'package:quiver/iterables.dart';
import 'common.dart';
const String _googleFormatterUrl =
'https://github.com/google/google-java-format/releases/download/google-java-format-1.3/google-java-format-1.3-all-deps.jar';
final Uri _googleFormatterUrl = Uri.https('github.com',
'/google/google-java-format/releases/download/google-java-format-1.3/google-java-format-1.3-all-deps.jar');
/// A command to format all package code.
class FormatCommand extends PluginCommand {
@ -47,7 +49,7 @@ class FormatCommand extends PluginCommand {
await _formatJava(googleFormatterPath);
await _formatCppAndObjectiveC();
if (argResults['fail-on-change'] == true) {
if (getBoolArg('fail-on-change')) {
final bool modified = await _didModifyAnything();
if (modified) {
throw ToolExit(1);
@ -105,7 +107,7 @@ class FormatCommand extends PluginCommand {
// 'ProcessException: Argument list too long'.
final Iterable<List<String>> batches = partition(allFiles, 100);
for (final List<String> batch in batches) {
await processRunner.runAndStream(argResults['clang-format'] as String,
await processRunner.runAndStream(getStringArg('clang-format'),
<String>['-i', '--style=Google', ...batch],
workingDir: packagesDir, exitOnError: true);
}

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'package:file/file.dart';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'package:file/file.dart';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:convert';
import 'dart:io';
@ -89,7 +91,7 @@ class LintPodspecsCommand extends PluginCommand {
final List<File> podspecs = await getFiles().where((File entity) {
final String filePath = entity.path;
return p.extension(filePath) == '.podspec' &&
!(argResults['skip'] as List<String>)
!getStringListArg('skip')
.contains(p.basenameWithoutExtension(filePath));
}).toList();
@ -122,7 +124,7 @@ class LintPodspecsCommand extends PluginCommand {
Future<ProcessResult> _runPodLint(String podspecPath,
{bool libraryLint}) async {
final bool allowWarnings = (argResults['ignore-warnings'] as List<String>)
final bool allowWarnings = (getStringListArg('ignore-warnings'))
.contains(p.basenameWithoutExtension(podspecPath));
final List<String> arguments = <String>[
'lib',

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'package:file/file.dart';
@ -36,7 +38,7 @@ class ListCommand extends PluginCommand {
@override
Future<void> run() async {
switch (argResults[_type] as String) {
switch (getStringArg(_type)) {
case _plugin:
await for (final Directory package in getPlugins()) {
print(package.path);

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:io' as io;
import 'package:args/command_runner.dart';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
@ -78,7 +80,7 @@ class PublishCheckCommand extends PluginCommand {
Future<void> run() async {
final ZoneSpecification logSwitchSpecification = ZoneSpecification(
print: (Zone self, ZoneDelegate parent, Zone zone, String message) {
final bool logMachineMessage = argResults[_machineFlag] as bool;
final bool logMachineMessage = getBoolArg(_machineFlag);
if (logMachineMessage && message != _prettyJson(_machineOutput)) {
_humanMessages.add(message);
} else {
@ -123,7 +125,7 @@ class PublishCheckCommand extends PluginCommand {
isError: false);
}
if (argResults[_machineFlag] as bool) {
if (getBoolArg(_machineFlag)) {
_setStatus(status);
_machineOutput[_humanMessageKey] = _humanMessages;
print(_prettyJson(_machineOutput));
@ -184,7 +186,7 @@ class PublishCheckCommand extends PluginCommand {
return true;
}
if (!(argResults[_allowPrereleaseFlag] as bool)) {
if (!getBoolArg(_allowPrereleaseFlag)) {
return false;
}
@ -270,7 +272,7 @@ HTTP response: ${pubVersionFinderResponse.httpResponse.body}
void _printImportantStatusMessage(String message, {@required bool isError}) {
final String statusMessage = '${isError ? 'ERROR' : 'SUCCESS'}: $message';
if (argResults[_machineFlag] as bool) {
if (getBoolArg(_machineFlag)) {
print(statusMessage);
} else {
final Colorize colorizedMessage = Colorize(statusMessage);

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
@ -122,9 +124,9 @@ class PublishPluginCommand extends PluginCommand {
@override
Future<void> run() async {
final String package = argResults[_packageOption] as String;
final bool publishAllChanged = argResults[_allChangedFlag] as bool;
if (package == null && !publishAllChanged) {
final String package = getStringArg(_packageOption);
final bool publishAllChanged = getBoolArg(_allChangedFlag);
if (package.isEmpty && !publishAllChanged) {
_print(
'Must specify a package to publish. See `plugin_tools help publish-plugin`.');
throw ToolExit(1);
@ -138,14 +140,14 @@ class PublishPluginCommand extends PluginCommand {
final GitDir baseGitDir =
await GitDir.fromExisting(packagesDir.path, allowSubdirectory: true);
final bool shouldPushTag = argResults[_pushTagsOption] == true;
final String remote = argResults[_remoteOption] as String;
final bool shouldPushTag = getBoolArg(_pushTagsOption);
final String remote = getStringArg(_remoteOption);
String remoteUrl;
if (shouldPushTag) {
remoteUrl = await _verifyRemote(remote);
}
_print('Local repo is ready!');
if (argResults[_dryRunFlag] as bool) {
if (getBoolArg(_dryRunFlag)) {
_print('=============== DRY RUN ===============');
}
@ -244,7 +246,7 @@ class PublishPluginCommand extends PluginCommand {
if (!await _publishPlugin(packageDir: packageDir)) {
return false;
}
if (argResults[_tagReleaseOption] as bool) {
if (getBoolArg(_tagReleaseOption)) {
if (!await _tagRelease(
packageDir: packageDir,
remote: remote,
@ -333,7 +335,7 @@ Safe to ignore if the package is deleted in this commit.
}) async {
final String tag = _getTag(packageDir);
_print('Tagging release $tag...');
if (!(argResults[_dryRunFlag] as bool)) {
if (!getBoolArg(_dryRunFlag)) {
final io.ProcessResult result = await processRunner.run(
'git',
<String>['tag', tag],
@ -416,15 +418,14 @@ Safe to ignore if the package is deleted in this commit.
}
Future<bool> _publish(Directory packageDir) async {
final List<String> publishFlags =
argResults[_pubFlagsOption] as List<String>;
final List<String> publishFlags = getStringListArg(_pubFlagsOption);
_print(
'Running `pub publish ${publishFlags.join(' ')}` in ${packageDir.absolute.path}...\n');
if (argResults[_dryRunFlag] as bool) {
if (getBoolArg(_dryRunFlag)) {
return true;
}
if (argResults[_skipConfirmationFlag] as bool) {
if (getBoolArg(_skipConfirmationFlag)) {
publishFlags.add('--force');
}
if (publishFlags.contains('--force')) {
@ -474,7 +475,7 @@ Safe to ignore if the package is deleted in this commit.
@required String remoteUrl,
}) async {
assert(remote != null && tag != null && remoteUrl != null);
if (!(argResults[_skipConfirmationFlag] as bool)) {
if (!getBoolArg(_skipConfirmationFlag)) {
_print('Ready to push $tag to $remoteUrl (y/n)?');
final String input = _stdin.readLineSync();
if (input.toLowerCase() != 'y') {
@ -482,7 +483,7 @@ Safe to ignore if the package is deleted in this commit.
return false;
}
}
if (!(argResults[_dryRunFlag] as bool)) {
if (!getBoolArg(_dryRunFlag)) {
final io.ProcessResult result = await processRunner.run(
'git',
<String>['push', remote, tag],

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'package:file/file.dart';
@ -44,7 +46,7 @@ class TestCommand extends PluginCommand {
print('RUNNING $packageName tests...');
final String enableExperiment = argResults[kEnableExperiment] as String;
final String enableExperiment = getStringArg(kEnableExperiment);
// `flutter test` automatically gets packages. `pub run test` does not. :(
int exitCode = 0;

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'package:file/file.dart';
@ -135,7 +137,7 @@ class VersionCheckCommand extends PluginCommand {
'"publish_to: none".');
}
Version sourceVersion;
if (argResults[_againstPubFlag] as bool) {
if (getBoolArg(_againstPubFlag)) {
final String packageName = pubspecFile.parent.basename;
final PubVersionFinderResponse pubVersionFinderResponse =
await _pubVersionFinder.getPackageVersion(package: packageName);
@ -161,7 +163,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
}
if (sourceVersion == null) {
String safeToIgnoreMessage;
if (argResults[_againstPubFlag] as bool) {
if (getBoolArg(_againstPubFlag)) {
safeToIgnoreMessage =
'${indentation}Unable to find package on pub server.';
} else {
@ -181,8 +183,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
getAllowedNextVersions(sourceVersion, headVersion);
if (!allowedNextVersions.containsKey(headVersion)) {
final String source =
(argResults[_againstPubFlag] as bool) ? 'pub' : 'master';
final String source = (getBoolArg(_againstPubFlag)) ? 'pub' : 'master';
final String error = '${indentation}Incorrectly updated version.\n'
'${indentation}HEAD: $headVersion, $source: $sourceVersion.\n'
'${indentation}Allowed versions: $allowedNextVersions';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
@ -48,8 +50,8 @@ class XCTestCommand extends PluginCommand {
@override
Future<void> run() async {
String destination = argResults[_kiOSDestination] as String;
if (destination == null) {
String destination = getStringArg(_kiOSDestination);
if (destination.isEmpty) {
final String simulatorId = await _findAvailableIphoneSimulator();
if (simulatorId == null) {
print(_kFoundNoSimulatorsMessage);
@ -58,7 +60,7 @@ class XCTestCommand extends PluginCommand {
destination = 'id=$simulatorId';
}
final List<String> skipped = argResults[_kSkip] as List<String>;
final List<String> skipped = getStringListArg(_kSkip);
final List<String> failingPackages = <String>[];
await for (final Directory plugin in getPlugins()) {

View File

@ -4,28 +4,29 @@ repository: https://github.com/flutter/plugins/tree/master/script/tool
version: 0.1.1
dependencies:
args: "^1.4.3"
path: "^1.6.1"
http: "^0.12.1"
async: "^2.0.7"
yaml: "^2.1.15"
quiver: "^2.0.2"
pub_semver: ^1.4.2
colorize: ^2.0.0
git: ^1.0.0
platform: ^2.2.0
pubspec_parse: "^0.1.4"
test: ^1.6.4
meta: ^1.1.7
file: ^5.0.10
uuid: ^2.0.4
http_multi_server: ^2.2.0
collection: ^1.14.13
args: ^2.1.0
async: ^2.6.1
collection: ^1.15.0
colorize: ^3.0.0
file: ^6.1.0
git: ^2.0.0
http: ^0.13.3
http_multi_server: ^3.0.1
meta: ^1.3.0
path: ^1.8.0
platform: ^3.0.0
pub_semver: ^2.0.0
pubspec_parse: ^1.0.0
quiver: ^3.0.1
test: ^1.17.3
uuid: ^3.0.4
yaml: ^3.1.0
dev_dependencies:
matcher: ^0.12.6
mockito: ^4.1.1
pedantic: ^1.8.0
build_runner: ^2.0.3
matcher: ^0.12.10
mockito: ^5.0.7
pedantic: ^1.11.0
environment:
sdk: ">=2.3.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:flutter_plugin_tools/src/analyze_command.dart';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:flutter_plugin_tools/src/build_examples_command.dart';

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
@ -12,21 +13,24 @@ import 'package:flutter_plugin_tools/src/common.dart';
import 'package:git/git.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:test/test.dart';
import 'common_test.mocks.dart';
import 'util.dart';
@GenerateMocks(<Type>[GitDir])
void main() {
RecordingProcessRunner processRunner;
CommandRunner<void> runner;
FileSystem fileSystem;
Directory packagesDir;
Directory thirdPartyPackagesDir;
List<String> plugins;
List<List<String>> gitDirCommands;
String gitDiffResponse;
late RecordingProcessRunner processRunner;
late CommandRunner<void> runner;
late FileSystem fileSystem;
late Directory packagesDir;
late Directory thirdPartyPackagesDir;
late List<String> plugins;
late List<List<String>?> gitDirCommands;
late String gitDiffResponse;
setUp(() {
fileSystem = MemoryFileSystem();
@ -35,14 +39,15 @@ void main() {
.childDirectory('third_party')
.childDirectory('packages');
gitDirCommands = <List<String>>[];
gitDirCommands = <List<String>?>[];
gitDiffResponse = '';
final MockGitDir gitDir = MockGitDir();
when(gitDir.runCommand(any)).thenAnswer((Invocation invocation) {
gitDirCommands.add(invocation.positionalArguments[0] as List<String>);
when(gitDir.runCommand(any, throwOnError: anyNamed('throwOnError')))
.thenAnswer((Invocation invocation) {
gitDirCommands.add(invocation.positionalArguments[0] as List<String>?);
final MockProcessResult mockProcessResult = MockProcessResult();
if (invocation.positionalArguments[0][0] == 'diff') {
when<String>(mockProcessResult.stdout as String)
when<String?>(mockProcessResult.stdout as String?)
.thenReturn(gitDiffResponse);
}
return Future<ProcessResult>.value(mockProcessResult);
@ -255,23 +260,24 @@ packages/plugin3/plugin3.dart
});
group('$GitVersionFinder', () {
List<List<String>> gitDirCommands;
String gitDiffResponse;
String mergeBaseResponse;
MockGitDir gitDir;
late List<List<String>?> gitDirCommands;
late String gitDiffResponse;
String? mergeBaseResponse;
late MockGitDir gitDir;
setUp(() {
gitDirCommands = <List<String>>[];
gitDirCommands = <List<String>?>[];
gitDiffResponse = '';
gitDir = MockGitDir();
when(gitDir.runCommand(any)).thenAnswer((Invocation invocation) {
gitDirCommands.add(invocation.positionalArguments[0] as List<String>);
when(gitDir.runCommand(any, throwOnError: anyNamed('throwOnError')))
.thenAnswer((Invocation invocation) {
gitDirCommands.add(invocation.positionalArguments[0] as List<String>?);
final MockProcessResult mockProcessResult = MockProcessResult();
if (invocation.positionalArguments[0][0] == 'diff') {
when<String>(mockProcessResult.stdout as String)
when<String?>(mockProcessResult.stdout as String?)
.thenReturn(gitDiffResponse);
} else if (invocation.positionalArguments[0][0] == 'merge-base') {
when<String>(mockProcessResult.stdout as String)
when<String?>(mockProcessResult.stdout as String?)
.thenReturn(mergeBaseResponse);
}
return Future<ProcessResult>.value(mockProcessResult);
@ -320,10 +326,11 @@ file2/file2.cc
file1/pubspec.yaml
file2/file2.cc
''';
final GitVersionFinder finder = GitVersionFinder(gitDir, null);
await finder.getChangedFiles();
verify(gitDir.runCommand(
<String>['diff', '--name-only', mergeBaseResponse, 'HEAD']));
<String>['diff', '--name-only', mergeBaseResponse!, 'HEAD']));
});
test('use correct base sha if specified', () async {
@ -350,8 +357,8 @@ file2/file2.cc
expect(response.versions, isNull);
expect(response.result, PubVersionFinderResult.noPackageFound);
expect(response.httpResponse.statusCode, 404);
expect(response.httpResponse.body, '');
expect(response.httpResponse!.statusCode, 404);
expect(response.httpResponse!.body, '');
});
test('HTTP error when getting versions from pub', () async {
@ -364,8 +371,8 @@ file2/file2.cc
expect(response.versions, isNull);
expect(response.result, PubVersionFinderResult.fail);
expect(response.httpResponse.statusCode, 400);
expect(response.httpResponse.body, '');
expect(response.httpResponse!.statusCode, 400);
expect(response.httpResponse!.body, '');
});
test('Get a correct list of versions when http response is OK.', () async {
@ -408,8 +415,8 @@ file2/file2.cc
Version.parse('0.0.1'),
]);
expect(response.result, PubVersionFinderResult.success);
expect(response.httpResponse.statusCode, 200);
expect(response.httpResponse.body, json.encode(httpResponse));
expect(response.httpResponse!.statusCode, 200);
expect(response.httpResponse!.body, json.encode(httpResponse));
});
});
}
@ -420,7 +427,7 @@ class SamplePluginCommand extends PluginCommand {
Directory packagesDir,
FileSystem fileSystem, {
ProcessRunner processRunner = const ProcessRunner(),
GitDir gitDir,
GitDir? gitDir,
}) : super(packagesDir, fileSystem,
processRunner: processRunner, gitDir: gitDir);
@ -440,6 +447,4 @@ class SamplePluginCommand extends PluginCommand {
}
}
class MockGitDir extends Mock implements GitDir {}
class MockProcessResult extends Mock implements ProcessResult {}

View File

@ -0,0 +1,143 @@
// Mocks generated by Mockito 5.0.7 from annotations
// in flutter_plugin_tools/test/common_test.dart.
// Do not manually edit this file.
import 'dart:async' as _i6;
import 'dart:io' as _i4;
import 'package:git/src/branch_reference.dart' as _i3;
import 'package:git/src/commit.dart' as _i2;
import 'package:git/src/commit_reference.dart' as _i8;
import 'package:git/src/git_dir.dart' as _i5;
import 'package:git/src/tag.dart' as _i7;
import 'package:git/src/tree_entry.dart' as _i9;
import 'package:mockito/mockito.dart' as _i1;
// ignore_for_file: comment_references
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: prefer_const_constructors
// ignore_for_file: avoid_redundant_argument_values
class _FakeCommit extends _i1.Fake implements _i2.Commit {}
class _FakeBranchReference extends _i1.Fake implements _i3.BranchReference {}
class _FakeProcessResult extends _i1.Fake implements _i4.ProcessResult {}
/// A class which mocks [GitDir].
///
/// See the documentation for Mockito's code generation for more information.
class MockGitDir extends _i1.Mock implements _i5.GitDir {
MockGitDir() {
_i1.throwOnMissingStub(this);
}
@override
String get path =>
(super.noSuchMethod(Invocation.getter(#path), returnValue: '') as String);
@override
_i6.Future<int> commitCount([String? branchName = r'HEAD']) =>
(super.noSuchMethod(Invocation.method(#commitCount, [branchName]),
returnValue: Future<int>.value(0)) as _i6.Future<int>);
@override
_i6.Future<_i2.Commit> commitFromRevision(String? revision) =>
(super.noSuchMethod(Invocation.method(#commitFromRevision, [revision]),
returnValue: Future<_i2.Commit>.value(_FakeCommit()))
as _i6.Future<_i2.Commit>);
@override
_i6.Future<Map<String, _i2.Commit>> commits([String? branchName = r'HEAD']) =>
(super.noSuchMethod(Invocation.method(#commits, [branchName]),
returnValue:
Future<Map<String, _i2.Commit>>.value(<String, _i2.Commit>{}))
as _i6.Future<Map<String, _i2.Commit>>);
@override
_i6.Future<_i3.BranchReference?> branchReference(String? branchName) =>
(super.noSuchMethod(Invocation.method(#branchReference, [branchName]),
returnValue:
Future<_i3.BranchReference?>.value(_FakeBranchReference()))
as _i6.Future<_i3.BranchReference?>);
@override
_i6.Future<List<_i3.BranchReference>> branches() => (super.noSuchMethod(
Invocation.method(#branches, []),
returnValue:
Future<List<_i3.BranchReference>>.value(<_i3.BranchReference>[]))
as _i6.Future<List<_i3.BranchReference>>);
@override
_i6.Stream<_i7.Tag> tags() =>
(super.noSuchMethod(Invocation.method(#tags, []),
returnValue: Stream<_i7.Tag>.empty()) as _i6.Stream<_i7.Tag>);
@override
_i6.Future<List<_i8.CommitReference>> showRef(
{bool? heads = false, bool? tags = false}) =>
(super.noSuchMethod(
Invocation.method(#showRef, [], {#heads: heads, #tags: tags}),
returnValue: Future<List<_i8.CommitReference>>.value(
<_i8.CommitReference>[]))
as _i6.Future<List<_i8.CommitReference>>);
@override
_i6.Future<_i3.BranchReference> currentBranch() =>
(super.noSuchMethod(Invocation.method(#currentBranch, []),
returnValue:
Future<_i3.BranchReference>.value(_FakeBranchReference()))
as _i6.Future<_i3.BranchReference>);
@override
_i6.Future<List<_i9.TreeEntry>> lsTree(String? treeish,
{bool? subTreesOnly = false, String? path}) =>
(super.noSuchMethod(
Invocation.method(#lsTree, [treeish],
{#subTreesOnly: subTreesOnly, #path: path}),
returnValue: Future<List<_i9.TreeEntry>>.value(<_i9.TreeEntry>[]))
as _i6.Future<List<_i9.TreeEntry>>);
@override
_i6.Future<String?> createOrUpdateBranch(
String? branchName, String? treeSha, String? commitMessage) =>
(super.noSuchMethod(
Invocation.method(
#createOrUpdateBranch, [branchName, treeSha, commitMessage]),
returnValue: Future<String?>.value('')) as _i6.Future<String?>);
@override
_i6.Future<String> commitTree(String? treeSha, String? commitMessage,
{List<String>? parentCommitShas}) =>
(super.noSuchMethod(
Invocation.method(#commitTree, [treeSha, commitMessage],
{#parentCommitShas: parentCommitShas}),
returnValue: Future<String>.value('')) as _i6.Future<String>);
@override
_i6.Future<Map<String, String>> writeObjects(List<String>? paths) =>
(super.noSuchMethod(Invocation.method(#writeObjects, [paths]),
returnValue:
Future<Map<String, String>>.value(<String, String>{}))
as _i6.Future<Map<String, String>>);
@override
_i6.Future<_i4.ProcessResult> runCommand(Iterable<String>? args,
{bool? throwOnError = true}) =>
(super.noSuchMethod(
Invocation.method(#runCommand, [args], {#throwOnError: throwOnError}),
returnValue:
Future<_i4.ProcessResult>.value(_FakeProcessResult())) as _i6
.Future<_i4.ProcessResult>);
@override
_i6.Future<bool> isWorkingTreeClean() =>
(super.noSuchMethod(Invocation.method(#isWorkingTreeClean, []),
returnValue: Future<bool>.value(false)) as _i6.Future<bool>);
@override
_i6.Future<_i2.Commit?> updateBranch(
String? branchName,
_i6.Future<dynamic> Function(_i4.Directory)? populater,
String? commitMessage) =>
(super.noSuchMethod(
Invocation.method(
#updateBranch, [branchName, populater, commitMessage]),
returnValue: Future<_i2.Commit?>.value(_FakeCommit()))
as _i6.Future<_i2.Commit?>);
@override
_i6.Future<_i2.Commit?> updateBranchWithDirectoryContents(String? branchName,
String? sourceDirectoryPath, String? commitMessage) =>
(super.noSuchMethod(
Invocation.method(#updateBranchWithDirectoryContents,
[branchName, sourceDirectoryPath, commitMessage]),
returnValue: Future<_i2.Commit?>.value(_FakeCommit()))
as _i6.Future<_i2.Commit?>);
}

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:flutter_plugin_tools/src/common.dart';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:io';
import 'package:args/command_runner.dart';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:flutter_plugin_tools/src/java_test_command.dart';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:flutter_plugin_tools/src/lint_podspecs_command.dart';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:flutter_plugin_tools/src/list_command.dart';

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:io' as io;

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:collection';
import 'dart:convert';
import 'dart:io' as io;

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:flutter_plugin_tools/src/test_command.dart';

View File

@ -21,13 +21,13 @@ FileSystem mockFileSystem = MemoryFileSystem(
style: const LocalPlatform().isWindows
? FileSystemStyle.windows
: FileSystemStyle.posix);
Directory mockPackagesDir;
late Directory mockPackagesDir;
/// Creates a mock packages directory in the mock file system.
///
/// If [parentDir] is set the mock packages dir will be creates as a child of
/// it. If not [mockFileSystem] will be used instead.
void initializeFakePackages({Directory parentDir}) {
void initializeFakePackages({Directory? parentDir}) {
mockPackagesDir =
(parentDir ?? mockFileSystem.currentDirectory).childDirectory('packages');
mockPackagesDir.createSync();
@ -51,7 +51,7 @@ Directory createFakePlugin(
bool includeVersion = false,
String version = '0.0.1',
String parentDirectoryName = '',
Directory packagesDirectory,
Directory? packagesDirectory,
}) {
assert(!(withSingleExample && withExamples.isNotEmpty),
'cannot pass withSingleExample and withExamples simultaneously');
@ -211,7 +211,8 @@ typedef _ErrorHandler = void Function(Error error);
/// what was printed.
/// A custom [errorHandler] can be used to handle the runner error as desired without throwing.
Future<List<String>> runCapturingPrint(
CommandRunner<void> runner, List<String> args, {_ErrorHandler errorHandler}) async {
CommandRunner<void> runner, List<String> args,
{_ErrorHandler? errorHandler}) async {
final List<String> prints = <String>[];
final ZoneSpecification spec = ZoneSpecification(
print: (_, __, ___, String message) {
@ -220,8 +221,8 @@ Future<List<String>> runCapturingPrint(
);
try {
await Zone.current
.fork(specification: spec)
.run<Future<void>>(() => runner.run(args));
.fork(specification: spec)
.run<Future<void>>(() => runner.run(args));
} on Error catch (e) {
if (errorHandler == null) {
rethrow;
@ -234,25 +235,25 @@ Future<List<String>> runCapturingPrint(
/// A mock [ProcessRunner] which records process calls.
class RecordingProcessRunner extends ProcessRunner {
io.Process processToReturn;
io.Process? processToReturn;
final List<ProcessCall> recordedCalls = <ProcessCall>[];
/// Populate for [io.ProcessResult] to use a String [stdout] instead of a [List] of [int].
String resultStdout;
String? resultStdout;
/// Populate for [io.ProcessResult] to use a String [stderr] instead of a [List] of [int].
String resultStderr;
String? resultStderr;
@override
Future<int> runAndStream(
String executable,
List<String> args, {
Directory workingDir,
Directory? workingDir,
bool exitOnError = false,
}) async {
recordedCalls.add(ProcessCall(executable, args, workingDir?.path));
return Future<int>.value(
processToReturn == null ? 0 : await processToReturn.exitCode);
processToReturn == null ? 0 : await processToReturn!.exitCode);
}
/// Returns [io.ProcessResult] created from [processToReturn], [resultStdout], and [resultStderr].
@ -260,28 +261,26 @@ class RecordingProcessRunner extends ProcessRunner {
Future<io.ProcessResult> run(
String executable,
List<String> args, {
Directory workingDir,
Directory? workingDir,
bool exitOnError = false,
bool logOnError = false,
Encoding stdoutEncoding = io.systemEncoding,
Encoding stderrEncoding = io.systemEncoding,
}) async {
recordedCalls.add(ProcessCall(executable, args, workingDir?.path));
io.ProcessResult result;
io.ProcessResult? result;
if (processToReturn != null) {
result = io.ProcessResult(
processToReturn.pid,
await processToReturn.exitCode,
resultStdout ?? processToReturn.stdout,
resultStderr ?? processToReturn.stderr);
final io.Process? process = processToReturn;
if (process != null) {
result = io.ProcessResult(process.pid, await process.exitCode,
resultStdout ?? process.stdout, resultStderr ?? process.stderr);
}
return Future<io.ProcessResult>.value(result);
}
@override
Future<io.Process> start(String executable, List<String> args,
{Directory workingDirectory}) async {
{Directory? workingDirectory}) async {
recordedCalls.add(ProcessCall(executable, args, workingDirectory?.path));
return Future<io.Process>.value(processToReturn);
}
@ -299,7 +298,7 @@ class ProcessCall {
final List<String> args;
/// The working directory this process was called from.
final String workingDir;
final String? workingDir;
@override
bool operator ==(dynamic other) {
@ -311,10 +310,7 @@ class ProcessCall {
@override
int get hashCode =>
executable?.hashCode ??
0 ^ args?.hashCode ??
0 ^ workingDir?.hashCode ??
0;
(executable.hashCode) ^ (args.hashCode) ^ (workingDir?.hashCode ?? 0);
@override
String toString() {

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
@ -65,7 +67,8 @@ void main() {
gitDiffResponse = '';
gitShowResponses = <String, String>{};
gitDir = MockGitDir();
when(gitDir.runCommand(any)).thenAnswer((Invocation invocation) {
when(gitDir.runCommand(any, throwOnError: anyNamed('throwOnError')))
.thenAnswer((Invocation invocation) {
gitDirCommands.add(invocation.positionalArguments[0] as List<String>);
final MockProcessResult mockProcessResult = MockProcessResult();
if (invocation.positionalArguments[0][0] == 'diff') {

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:convert';
import 'package:args/command_runner.dart';