Unverified Commit e8d73068 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Some minor cleanup for flutter_tools (#36569)

parent 6fe45fb1
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
/// increase the API surface that we have to test in Flutter tools, and the APIs /// increase the API surface that we have to test in Flutter tools, and the APIs
/// in `dart:io` can sometimes be hard to use in tests. /// in `dart:io` can sometimes be hard to use in tests.
import 'dart:async'; import 'dart:async';
import 'dart:io' as io show exit, IOSink, ProcessSignal, stderr, stdin, Stdout, stdout; import 'dart:io' as io show exit, IOSink, Process, ProcessSignal, stderr, stdin, Stdout, stdout;
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
...@@ -116,7 +116,12 @@ void restoreExitFunction() { ...@@ -116,7 +116,12 @@ void restoreExitFunction() {
/// Listening on signals that don't exist on the current platform is just a /// Listening on signals that don't exist on the current platform is just a
/// no-op. This is in contrast to [io.ProcessSignal], where listening to /// no-op. This is in contrast to [io.ProcessSignal], where listening to
/// non-existent signals throws an exception. /// non-existent signals throws an exception.
class ProcessSignal implements io.ProcessSignal { ///
/// This class does NOT implement io.ProcessSignal, because that class uses
/// private fields. This means it cannot be used with, e.g., [Process.killPid].
/// Alternative implementations of the relevant methods that take
/// [ProcessSignal] instances are available on this class (e.g. "send").
class ProcessSignal {
@visibleForTesting @visibleForTesting
const ProcessSignal(this._delegate); const ProcessSignal(this._delegate);
...@@ -129,11 +134,23 @@ class ProcessSignal implements io.ProcessSignal { ...@@ -129,11 +134,23 @@ class ProcessSignal implements io.ProcessSignal {
final io.ProcessSignal _delegate; final io.ProcessSignal _delegate;
@override
Stream<ProcessSignal> watch() { Stream<ProcessSignal> watch() {
return _delegate.watch().map<ProcessSignal>((io.ProcessSignal signal) => this); return _delegate.watch().map<ProcessSignal>((io.ProcessSignal signal) => this);
} }
/// Sends the signal to the given process (identified by pid).
///
/// Returns true if the signal was delivered, false otherwise.
///
/// On Windows, this can only be used with [ProcessSignal.SIGTERM], which
/// terminates the process.
///
/// This is implemented by sending the signal using [Process.killPid].
bool send(int pid) {
assert(!platform.isWindows || this == ProcessSignal.SIGTERM);
return io.Process.killPid(pid, _delegate);
}
@override @override
String toString() => _delegate.toString(); String toString() => _delegate.toString();
} }
......
...@@ -89,16 +89,13 @@ class AttachCommand extends FlutterCommand { ...@@ -89,16 +89,13 @@ class AttachCommand extends FlutterCommand {
'project-root', 'project-root',
hide: !verboseHelp, hide: !verboseHelp,
help: 'Normally used only in run target', help: 'Normally used only in run target',
)..addFlag('track-widget-creation',
hide: !verboseHelp,
help: 'Track widget creation locations.',
defaultsTo: false,
)..addFlag('machine', )..addFlag('machine',
hide: !verboseHelp, hide: !verboseHelp,
negatable: false, negatable: false,
help: 'Handle machine structured JSON command input and provide output ' help: 'Handle machine structured JSON command input and provide output '
'and progress in machine friendly format.', 'and progress in machine friendly format.',
); );
usesTrackWidgetCreation(verboseHelp: verboseHelp);
hotRunnerFactory ??= HotRunnerFactory(); hotRunnerFactory ??= HotRunnerFactory();
} }
......
...@@ -23,7 +23,7 @@ class BuildCommand extends FlutterCommand { ...@@ -23,7 +23,7 @@ class BuildCommand extends FlutterCommand {
addSubcommand(BuildAarCommand(verboseHelp: verboseHelp)); addSubcommand(BuildAarCommand(verboseHelp: verboseHelp));
addSubcommand(BuildApkCommand(verboseHelp: verboseHelp)); addSubcommand(BuildApkCommand(verboseHelp: verboseHelp));
addSubcommand(BuildAppBundleCommand(verboseHelp: verboseHelp)); addSubcommand(BuildAppBundleCommand(verboseHelp: verboseHelp));
addSubcommand(BuildAotCommand()); addSubcommand(BuildAotCommand(verboseHelp: verboseHelp));
addSubcommand(BuildIOSCommand()); addSubcommand(BuildIOSCommand());
addSubcommand(BuildBundleCommand(verboseHelp: verboseHelp)); addSubcommand(BuildBundleCommand(verboseHelp: verboseHelp));
addSubcommand(BuildWebCommand()); addSubcommand(BuildWebCommand());
......
...@@ -21,7 +21,7 @@ import '../runner/flutter_command.dart'; ...@@ -21,7 +21,7 @@ import '../runner/flutter_command.dart';
import 'build.dart'; import 'build.dart';
class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmentArtifacts { class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmentArtifacts {
BuildAotCommand() { BuildAotCommand({bool verboseHelp = false}) {
usesTargetOption(); usesTargetOption();
addBuildModeFlags(); addBuildModeFlags();
usesPubOption(); usesPubOption();
...@@ -37,12 +37,6 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen ...@@ -37,12 +37,6 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen
defaultsTo: false, defaultsTo: false,
help: 'Report timing information about build steps in machine readable form,', help: 'Report timing information about build steps in machine readable form,',
) )
// track-widget-creation is exposed as a flag here but ignored to deal with build
// invalidation issues - there are no plans to support it for AOT mode.
..addFlag('track-widget-creation',
defaultsTo: false,
hide: true,
)
..addMultiOption('ios-arch', ..addMultiOption('ios-arch',
splitCommas: true, splitCommas: true,
defaultsTo: defaultIOSArchs.map<String>(getNameForIOSArch), defaultsTo: defaultIOSArchs.map<String>(getNameForIOSArch),
...@@ -62,6 +56,10 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen ...@@ -62,6 +56,10 @@ class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmen
help: 'Build the AOT bundle with bitcode. Requires a compatible bitcode engine.', help: 'Build the AOT bundle with bitcode. Requires a compatible bitcode engine.',
hide: true, hide: true,
); );
// --track-widget-creation is exposed as a flag here to deal with build
// invalidation issues, but it is ignored -- there are no plans to support
// it for AOT mode.
usesTrackWidgetCreation(hasEffect: false, verboseHelp: verboseHelp);
} }
@override @override
......
...@@ -22,7 +22,6 @@ class BuildApkCommand extends BuildSubCommand { ...@@ -22,7 +22,6 @@ class BuildApkCommand extends BuildSubCommand {
usesBuildNameOption(); usesBuildNameOption();
argParser argParser
..addFlag('track-widget-creation', negatable: false, hide: !verboseHelp)
..addFlag('split-per-abi', ..addFlag('split-per-abi',
negatable: false, negatable: false,
help: 'Whether to split the APKs per ABIs.' help: 'Whether to split the APKs per ABIs.'
...@@ -34,6 +33,7 @@ class BuildApkCommand extends BuildSubCommand { ...@@ -34,6 +33,7 @@ class BuildApkCommand extends BuildSubCommand {
allowed: <String>['android-arm', 'android-arm64', 'android-x86', 'android-x64'], allowed: <String>['android-arm', 'android-arm64', 'android-x86', 'android-x64'],
help: 'The target platform for which the app is compiled.', help: 'The target platform for which the app is compiled.',
); );
usesTrackWidgetCreation(verboseHelp: verboseHelp);
} }
@override @override
......
...@@ -41,10 +41,6 @@ class BuildBundleCommand extends BuildSubCommand { ...@@ -41,10 +41,6 @@ class BuildBundleCommand extends BuildSubCommand {
'windows-x64', 'windows-x64',
], ],
) )
..addFlag('track-widget-creation',
hide: !verboseHelp,
help: 'Track widget creation locations. Requires Dart 2.0 functionality.',
)
..addMultiOption(FlutterOptions.kExtraFrontEndOptions, ..addMultiOption(FlutterOptions.kExtraFrontEndOptions,
splitCommas: true, splitCommas: true,
hide: true, hide: true,
...@@ -59,6 +55,7 @@ class BuildBundleCommand extends BuildSubCommand { ...@@ -59,6 +55,7 @@ class BuildBundleCommand extends BuildSubCommand {
'in the application\'s LICENSE file.', 'in the application\'s LICENSE file.',
defaultsTo: false); defaultsTo: false);
usesPubOption(); usesPubOption();
usesTrackWidgetCreation(verboseHelp: verboseHelp);
bundleBuilder ??= BundleBuilder(); bundleBuilder ??= BundleBuilder();
} }
......
...@@ -49,6 +49,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment ...@@ -49,6 +49,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
usesPortOptions(); usesPortOptions();
usesIpv6Flag(); usesIpv6Flag();
usesPubOption(); usesPubOption();
usesTrackWidgetCreation(verboseHelp: verboseHelp);
usesIsolateFilterOption(hide: !verboseHelp); usesIsolateFilterOption(hide: !verboseHelp);
} }
...@@ -61,7 +62,6 @@ class RunCommand extends RunCommandBase { ...@@ -61,7 +62,6 @@ class RunCommand extends RunCommandBase {
RunCommand({ bool verboseHelp = false }) : super(verboseHelp: verboseHelp) { RunCommand({ bool verboseHelp = false }) : super(verboseHelp: verboseHelp) {
requiresPubspecYaml(); requiresPubspecYaml();
usesFilesystemOptions(hide: !verboseHelp); usesFilesystemOptions(hide: !verboseHelp);
argParser argParser
..addFlag('start-paused', ..addFlag('start-paused',
negatable: false, negatable: false,
...@@ -130,10 +130,6 @@ class RunCommand extends RunCommandBase { ...@@ -130,10 +130,6 @@ class RunCommand extends RunCommandBase {
hide: !verboseHelp, hide: !verboseHelp,
help: 'Specify a pre-built application binary to use when running.', help: 'Specify a pre-built application binary to use when running.',
) )
..addFlag('track-widget-creation',
hide: !verboseHelp,
help: 'Track widget creation locations.',
)
..addOption('project-root', ..addOption('project-root',
hide: !verboseHelp, hide: !verboseHelp,
help: 'Specify the project root directory.', help: 'Specify the project root directory.',
......
...@@ -78,12 +78,6 @@ class TestCommand extends FastFlutterCommand { ...@@ -78,12 +78,6 @@ class TestCommand extends FastFlutterCommand {
help: 'Handle machine structured JSON command input\n' help: 'Handle machine structured JSON command input\n'
'and provide output and progress in machine friendly format.', 'and provide output and progress in machine friendly format.',
) )
..addFlag('track-widget-creation',
negatable: false,
hide: !verboseHelp,
help: 'Track widget creation locations.\n'
'This enables testing of features such as the widget inspector.',
)
..addFlag('update-goldens', ..addFlag('update-goldens',
negatable: false, negatable: false,
help: 'Whether matchesGoldenFile() calls within your test methods should ' help: 'Whether matchesGoldenFile() calls within your test methods should '
...@@ -106,6 +100,7 @@ class TestCommand extends FastFlutterCommand { ...@@ -106,6 +100,7 @@ class TestCommand extends FastFlutterCommand {
defaultsTo: 'tester', defaultsTo: 'tester',
help: 'The platform to run the unit tests on. Defaults to "tester".' help: 'The platform to run the unit tests on. Defaults to "tester".'
); );
usesTrackWidgetCreation(verboseHelp: verboseHelp);
} }
@override @override
......
...@@ -119,10 +119,11 @@ Future<void> pubGet({ ...@@ -119,10 +119,11 @@ Future<void> pubGet({
} }
if (!dotPackages.existsSync()) if (!dotPackages.existsSync())
throwToolExit('$directory: pub did not create .packages file'); throwToolExit('$directory: pub did not create .packages file.');
if (dotPackages.lastModifiedSync().isBefore(pubSpecYaml.lastModifiedSync())) if (dotPackages.lastModifiedSync().isBefore(pubSpecYaml.lastModifiedSync())) {
throwToolExit('$directory: pub did not update .packages file (pubspec.yaml file has a newer timestamp)'); throwToolExit('$directory: pub did not update .packages file (pubspec.yaml timestamp: ${pubSpecYaml.lastModifiedSync()}; .packages timestamp: ${dotPackages.lastModifiedSync()}).');
}
} }
typedef MessageFilter = String Function(String message); typedef MessageFilter = String Function(String message);
......
...@@ -288,8 +288,18 @@ abstract class FlutterCommand extends Command<void> { ...@@ -288,8 +288,18 @@ abstract class FlutterCommand extends Command<void> {
argParser.addOption( argParser.addOption(
'flavor', 'flavor',
help: 'Build a custom app flavor as defined by platform-specific build setup.\n' help: 'Build a custom app flavor as defined by platform-specific build setup.\n'
'Supports the use of product flavors in Android Gradle scripts.\n' 'Supports the use of product flavors in Android Gradle scripts, and '
'Supports the use of custom Xcode schemes.', 'the use of custom Xcode schemes.',
);
}
void usesTrackWidgetCreation({ bool hasEffect = true, @required bool verboseHelp }) {
argParser.addFlag(
'track-widget-creation',
hide: !hasEffect && !verboseHelp,
defaultsTo: false, // this will soon be changed to true
help: 'Track widget creation locations. This enables features such as the widget inspector. '
'This parameter is only functional in debug mode (i.e. when compiling JIT, not AOT).',
); );
} }
...@@ -384,7 +394,6 @@ abstract class FlutterCommand extends Command<void> { ...@@ -384,7 +394,6 @@ abstract class FlutterCommand extends Command<void> {
} finally { } finally {
final DateTime endTime = systemClock.now(); final DateTime endTime = systemClock.now();
printTrace(userMessages.flutterElapsedTime(name, getElapsedAsMilliseconds(endTime.difference(startTime)))); printTrace(userMessages.flutterElapsedTime(name, getElapsedAsMilliseconds(endTime.difference(startTime))));
printTrace('"flutter $name" took ${getElapsedAsMilliseconds(endTime.difference(startTime))}.');
_sendPostUsage(commandPath, commandResult, startTime, endTime); _sendPostUsage(commandPath, commandResult, startTime, endTime);
} }
}, },
......
# Integration tests # Integration tests
These tests are not hermetic, and use actual Flutter SDK. These tests are not hermetic, and use the actual Flutter SDK. While
While they don't require actual devices, they run `flutter_tester` to test they don't require actual devices, they run `flutter_tester` to test
Dart VM and Flutter integration. Dart VM and Flutter integration.
Use this command to run: Use this command to run (from the `flutter_tools` directory):
```shell ```shell
../../bin/cache/dart-sdk/bin/pub run test ../../bin/cache/dart-sdk/bin/pub run test test/integration.shard
``` ```
These tests are expensive to run and do not give meaningful coverage
information for the flutter tool (since they are black-box tests that
run the tool as a subprocess, rather than being unit tests). For this
reason, they are in a separate shard when running on continuous
integration and are not run when calculating coverage.
...@@ -152,13 +152,13 @@ abstract class FlutterTestDriver { ...@@ -152,13 +152,13 @@ abstract class FlutterTestDriver {
if (_processPid == null) if (_processPid == null)
return -1; return -1;
_debugPrint('Sending SIGTERM to $_processPid..'); _debugPrint('Sending SIGTERM to $_processPid..');
Process.killPid(_processPid); ProcessSignal.SIGTERM.send(_processPid);
return _process.exitCode.timeout(quitTimeout, onTimeout: _killForcefully); return _process.exitCode.timeout(quitTimeout, onTimeout: _killForcefully);
} }
Future<int> _killForcefully() { Future<int> _killForcefully() {
_debugPrint('Sending SIGKILL to $_processPid..'); _debugPrint('Sending SIGKILL to $_processPid..');
Process.killPid(_processPid, ProcessSignal.SIGKILL); ProcessSignal.SIGKILL.send(_processPid);
return _process.exitCode; return _process.exitCode;
} }
...@@ -655,6 +655,7 @@ class FlutterTestTestDriver extends FlutterTestDriver { ...@@ -655,6 +655,7 @@ class FlutterTestTestDriver extends FlutterTestDriver {
Future<Map<String, dynamic>> _waitForJson({ Future<Map<String, dynamic>> _waitForJson({
Duration timeout = defaultTimeout, Duration timeout = defaultTimeout,
}) async { }) async {
assert(timeout != null);
return _timeoutWithMessages<Map<String, dynamic>>( return _timeoutWithMessages<Map<String, dynamic>>(
() => _stdout.stream.map<Map<String, dynamic>>(_parseJsonResponse) () => _stdout.stream.map<Map<String, dynamic>>(_parseJsonResponse)
.firstWhere((Map<String, dynamic> output) => output != null), .firstWhere((Map<String, dynamic> output) => output != null),
......
...@@ -51,6 +51,5 @@ Future<void> getPackages(String folder) async { ...@@ -51,6 +51,5 @@ Future<void> getPackages(String folder) async {
process.stderr.transform(utf8.decoder).listen(errorOutput.write); process.stderr.transform(utf8.decoder).listen(errorOutput.write);
final int exitCode = await process.exitCode; final int exitCode = await process.exitCode;
if (exitCode != 0) if (exitCode != 0)
throw Exception( throw Exception('flutter pub get failed: $errorOutput');
'flutter pub get failed: ${errorOutput.toString()}');
} }
...@@ -91,7 +91,6 @@ void testUsingContext( ...@@ -91,7 +91,6 @@ void testUsingContext(
}, },
body: () { body: () {
final String flutterRoot = getFlutterRoot(); final String flutterRoot = getFlutterRoot();
return runZoned<Future<dynamic>>(() { return runZoned<Future<dynamic>>(() {
try { try {
return context.run<dynamic>( return context.run<dynamic>(
...@@ -105,7 +104,6 @@ void testUsingContext( ...@@ -105,7 +104,6 @@ void testUsingContext(
// tests can override this either in the test or during setup. // tests can override this either in the test or during setup.
Cache.flutterRoot ??= flutterRoot; Cache.flutterRoot ??= flutterRoot;
} }
return await testMethod(); return await testMethod();
}, },
); );
......
...@@ -25,15 +25,11 @@ import 'context.dart'; ...@@ -25,15 +25,11 @@ import 'context.dart';
export 'package:flutter_tools/src/base/context.dart' show Generator; export 'package:flutter_tools/src/base/context.dart' show Generator;
// A default value should be provided if one of the following criteria is met: // A default value should be provided if the vast majority of tests should use
// - The vast majority of tests should use this provider. For example, // this provider. For example, [BufferLogger], [MemoryFileSystem].
// [BufferLogger], [MemoryFileSystem].
// - More TBD.
final Map<Type, Generator> _testbedDefaults = <Type, Generator>{ final Map<Type, Generator> _testbedDefaults = <Type, Generator>{
// Keeps tests fast by avoid actual file system. // Keeps tests fast by avoiding the actual file system.
FileSystem: () => MemoryFileSystem(style: platform.isWindows FileSystem: () => MemoryFileSystem(style: platform.isWindows ? FileSystemStyle.windows : FileSystemStyle.posix),
? FileSystemStyle.windows
: FileSystemStyle.posix),
Logger: () => BufferLogger(), // Allows reading logs and prevents stdout. Logger: () => BufferLogger(), // Allows reading logs and prevents stdout.
OperatingSystemUtils: () => FakeOperatingSystemUtils(), OperatingSystemUtils: () => FakeOperatingSystemUtils(),
OutputPreferences: () => OutputPreferences(showColor: false), // configures BufferLogger to avoid color codes. OutputPreferences: () => OutputPreferences(showColor: false), // configures BufferLogger to avoid color codes.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment