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

Clean up output of "flutter run --release" (#18380)

(second attempt)
parent 48f4ff6d
// Copyright (c) 2017 The Chromium 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:async';
import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';
void main() {
task(() async {
final Device device = await devices.workingDevice;
await device.unlock();
final Directory appDir = dir(path.join(flutterDirectory.path, 'dev/integration_tests/ui'));
await inDirectory(appDir, () async {
final Completer<Null> ready = new Completer<Null>();
print('run: starting...');
final Process run = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'),
<String>['--suppress-analytics', 'run', '--release', '-d', device.deviceId, 'lib/main.dart'],
isBot: false, // we just want to test the output, not have any debugging info
);
final List<String> stdout = <String>[];
final List<String> stderr = <String>[];
int runExitCode;
run.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen((String line) {
print('run:stdout: $line');
stdout.add(line);
if (line.contains('To quit, press "q".'))
ready.complete();
});
run.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen((String line) {
print('run:stderr: $line');
stdout.add(line);
});
run.exitCode.then((int exitCode) { runExitCode = exitCode; });
await Future.any<dynamic>(<Future<dynamic>>[ ready.future, run.exitCode ]);
if (runExitCode != null)
throw 'Failed to run test app; runner unexpected exited, with exit code $runExitCode.';
run.stdin.write('q');
await run.exitCode;
if (stderr.isNotEmpty)
throw 'flutter run --release had output on standard error.';
if (stdout.first == 'Building flutter tool...')
stdout.removeAt(0);
if (stdout.first == 'Running "flutter packages get" in ui...')
stdout.removeAt(0);
if (stdout.first == 'Initializing gradle...')
stdout.removeAt(0);
if (!(stdout.first.startsWith('Launching lib/main.dart on ') && stdout.first.endsWith(' in release mode...')))
throw 'flutter run --release had unexpected first line: ${stdout.first}';
stdout.removeAt(0);
if (stdout.first != 'Running \'gradlew assembleRelease\'...')
throw 'flutter run --release had unexpected second line: ${stdout.first}';
stdout.removeAt(0);
if (!(stdout.first.startsWith('Built build/app/outputs/apk/release/app-release.apk (') && stdout.first.endsWith('MB).')))
throw 'flutter run --release had unexpected third line: ${stdout.first}';
stdout.removeAt(0);
if (stdout.first == 'Installing build/app/outputs/apk/app.apk...')
stdout.removeAt(0);
if (stdout.join('\n') != '\nTo quit, press "q".\n\nApplication finished.')
throw 'flutter run --release had unexpected output after third line';
});
return new TaskResult.success(null);
});
}
...@@ -183,16 +183,42 @@ Future<DateTime> getFlutterRepoCommitTimestamp(String commit) { ...@@ -183,16 +183,42 @@ Future<DateTime> getFlutterRepoCommitTimestamp(String commit) {
}); });
} }
/// Starts a subprocess.
///
/// The first argument is the full path to the executable to run.
///
/// The second argument is the list of arguments to provide on the command line.
/// This argument can be null, indicating no arguments (same as the empty list).
///
/// The `environment` argument can be provided to configure environment variables
/// that will be made available to the subprocess. The `BOT` environment variable
/// is always set and overrides any value provided in the `environment` argument.
/// The `isBot` argument controls the value of the `BOT` variable. It will either
/// be "true", if `isBot` is true (the default), or "false" if it is false.
///
/// The `BOT` variable is in particular used by the `flutter` tool to determine
/// how verbose to be and whether to enable analytics by default.
///
/// The working directory can be provided using the `workingDirectory` argument.
/// By default it will default to the current working directory (see [cwd]).
///
/// Information regarding the execution of the subprocess is printed to the
/// console.
///
/// The actual process executes asynchronously. A handle to the subprocess is
/// returned in the form of a [Future] that completes to a [Process] object.
Future<Process> startProcess( Future<Process> startProcess(
String executable, String executable,
List<String> arguments, { List<String> arguments, {
Map<String, String> environment, Map<String, String> environment,
bool isBot = true, // set to false to pretend not to be on a bot (e.g. to test user-facing outputs)
String workingDirectory, String workingDirectory,
}) async { }) async {
assert(isBot != null);
final String command = '$executable ${arguments?.join(" ") ?? ""}'; final String command = '$executable ${arguments?.join(" ") ?? ""}';
print('\nExecuting: $command'); print('\nExecuting: $command');
environment ??= <String, String>{}; environment ??= <String, String>{};
environment['BOT'] = 'true'; environment['BOT'] = isBot ? 'true' : 'false';
final Process process = await _processManager.start( final Process process = await _processManager.start(
<String>[executable]..addAll(arguments), <String>[executable]..addAll(arguments),
environment: environment, environment: environment,
......
...@@ -124,6 +124,13 @@ tasks: ...@@ -124,6 +124,13 @@ tasks:
stage: devicelab stage: devicelab
required_agent_capabilities: ["mac/android"] required_agent_capabilities: ["mac/android"]
run_release_test:
description: >
Checks that `flutter run --release` does not crash.
stage: devicelab
required_agent_capabilities: ["mac/android"]
flaky: true
platform_interaction_test: platform_interaction_test:
description: > description: >
Checks platform interaction on Android. Checks platform interaction on Android.
......
...@@ -53,7 +53,6 @@ class GenSnapshot { ...@@ -53,7 +53,6 @@ class GenSnapshot {
'--causal_async_stacks', '--causal_async_stacks',
'--packages=$packagesPath', '--packages=$packagesPath',
'--dependencies=$depfilePath', '--dependencies=$depfilePath',
'--print_snapshot_sizes',
]..addAll(additionalArgs); ]..addAll(additionalArgs);
final String snapshotterPath = getSnapshotterPath(snapshotType); final String snapshotterPath = getSnapshotterPath(snapshotType);
......
...@@ -21,26 +21,26 @@ class BotDetector { ...@@ -21,26 +21,26 @@ class BotDetector {
const BotDetector(); const BotDetector();
bool get isRunningOnBot { bool get isRunningOnBot {
return return platform.environment['BOT'] != 'false'
platform.environment['BOT'] == 'true' || && (platform.environment['BOT'] == 'true'
// https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables // https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
platform.environment['TRAVIS'] == 'true' || || platform.environment['TRAVIS'] == 'true'
platform.environment['CONTINUOUS_INTEGRATION'] == 'true' || || platform.environment['CONTINUOUS_INTEGRATION'] == 'true'
platform.environment.containsKey('CI') || // Travis and AppVeyor || platform.environment.containsKey('CI') // Travis and AppVeyor
// https://www.appveyor.com/docs/environment-variables/ // https://www.appveyor.com/docs/environment-variables/
platform.environment.containsKey('APPVEYOR') || || platform.environment.containsKey('APPVEYOR')
// https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html // https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html
(platform.environment.containsKey('AWS_REGION') && platform.environment.containsKey('CODEBUILD_INITIATOR')) || || (platform.environment.containsKey('AWS_REGION') && platform.environment.containsKey('CODEBUILD_INITIATOR'))
// https://wiki.jenkins.io/display/JENKINS/Building+a+software+project#Buildingasoftwareproject-belowJenkinsSetEnvironmentVariables // https://wiki.jenkins.io/display/JENKINS/Building+a+software+project#Buildingasoftwareproject-belowJenkinsSetEnvironmentVariables
platform.environment.containsKey('JENKINS_URL') || || platform.environment.containsKey('JENKINS_URL')
// Properties on Flutter's Chrome Infra bots. // Properties on Flutter's Chrome Infra bots.
platform.environment['CHROME_HEADLESS'] == '1' || || platform.environment['CHROME_HEADLESS'] == '1'
platform.environment.containsKey('BUILDBOT_BUILDERNAME'); || platform.environment.containsKey('BUILDBOT_BUILDERNAME'));
} }
} }
......
...@@ -845,8 +845,9 @@ abstract class ResidentRunner { ...@@ -845,8 +845,9 @@ abstract class ResidentRunner {
} }
printStatus('To display the performance overlay (WidgetsApp.showPerformanceOverlay), press "P".'); printStatus('To display the performance overlay (WidgetsApp.showPerformanceOverlay), press "P".');
} }
if (flutterDevices.any((FlutterDevice d) => d.device.supportsScreenshot)) if (flutterDevices.any((FlutterDevice d) => d.device.supportsScreenshot)) {
printStatus('To save a screenshot to flutter.png, press "s".'); printStatus('To save a screenshot to flutter.png, press "s".');
}
} }
/// Called when a signal has requested we exit. /// Called when a signal has requested we exit.
......
...@@ -125,22 +125,29 @@ class ColdRunner extends ResidentRunner { ...@@ -125,22 +125,29 @@ class ColdRunner extends ResidentRunner {
@override @override
void printHelp({ @required bool details }) { void printHelp({ @required bool details }) {
bool haveDetails = false; bool haveDetails = false;
bool haveAnything = false;
for (FlutterDevice device in flutterDevices) { for (FlutterDevice device in flutterDevices) {
final String dname = device.device.name; final String dname = device.device.name;
if (device.observatoryUris != null) { if (device.observatoryUris != null) {
for (Uri uri in device.observatoryUris) for (Uri uri in device.observatoryUris) {
printStatus('An Observatory debugger and profiler on $dname is available at $uri'); printStatus('An Observatory debugger and profiler on $dname is available at $uri');
haveAnything = true;
}
} }
} }
if (supportsServiceProtocol) { if (supportsServiceProtocol) {
haveDetails = true; haveDetails = true;
if (details) if (details) {
printHelpDetails(); printHelpDetails();
haveAnything = true;
}
} }
if (haveDetails && !details) { if (haveDetails && !details) {
printStatus('For a more detailed help message, press "h". To quit, press "q".'); printStatus('For a more detailed help message, press "h". To quit, press "q".');
} else { } else if (haveAnything) {
printStatus('To repeat this help message, press "h". To quit, press "q".'); printStatus('To repeat this help message, press "h". To quit, press "q".');
} else {
printStatus('To quit, press "q".');
} }
} }
......
...@@ -53,7 +53,8 @@ class FlutterCommandRunner extends CommandRunner<Null> { ...@@ -53,7 +53,8 @@ class FlutterCommandRunner extends CommandRunner<Null> {
argParser.addFlag('verbose', argParser.addFlag('verbose',
abbr: 'v', abbr: 'v',
negatable: false, negatable: false,
help: 'Noisy logging, including all shell commands executed.'); help: 'Noisy logging, including all shell commands executed.\n'
'If used with --help, shows hidden options.');
argParser.addFlag('quiet', argParser.addFlag('quiet',
negatable: false, negatable: false,
hide: !verboseHelp, hide: !verboseHelp,
...@@ -66,11 +67,12 @@ class FlutterCommandRunner extends CommandRunner<Null> { ...@@ -66,11 +67,12 @@ class FlutterCommandRunner extends CommandRunner<Null> {
help: 'Reports the version of this tool.'); help: 'Reports the version of this tool.');
argParser.addFlag('machine', argParser.addFlag('machine',
negatable: false, negatable: false,
hide: true); hide: !verboseHelp,
help: 'When used with the --version flag, outputs the information using JSON.');
argParser.addFlag('color', argParser.addFlag('color',
negatable: true, negatable: true,
hide: !verboseHelp, hide: !verboseHelp,
help: 'Whether to use terminal colors.'); help: 'Whether to use terminal colors (requires support for ANSI escape sequences).');
argParser.addFlag('version-check', argParser.addFlag('version-check',
negatable: true, negatable: true,
defaultsTo: true, defaultsTo: true,
...@@ -78,61 +80,67 @@ class FlutterCommandRunner extends CommandRunner<Null> { ...@@ -78,61 +80,67 @@ class FlutterCommandRunner extends CommandRunner<Null> {
help: 'Allow Flutter to check for updates when this command runs.'); help: 'Allow Flutter to check for updates when this command runs.');
argParser.addFlag('suppress-analytics', argParser.addFlag('suppress-analytics',
negatable: false, negatable: false,
hide: !verboseHelp,
help: 'Suppress analytics reporting when this command runs.'); help: 'Suppress analytics reporting when this command runs.');
argParser.addFlag('bug-report', argParser.addFlag('bug-report',
negatable: false, negatable: false,
help: help: 'Captures a bug report file to submit to the Flutter team.\n'
'Captures a bug report file to submit to the Flutter team ' 'Contains local paths, device identifiers, and log snippets.');
'(contains local paths, device\nidentifiers, and log snippets).');
argParser.addFlag('show-test-device',
negatable: false,
hide: !verboseHelp,
help: 'List the special \'flutter-tester\' device in device listings. '
'This headless device is used to\ntest Flutter tooling.');
String packagesHelp; String packagesHelp;
if (fs.isFileSync(kPackagesFileName)) bool showPackagesCommand;
packagesHelp = '\n(defaults to "$kPackagesFileName")'; if (fs.isFileSync(kPackagesFileName)) {
else packagesHelp = '(defaults to "$kPackagesFileName")';
packagesHelp = '\n(required, since the current directory does not contain a "$kPackagesFileName" file)'; showPackagesCommand = verboseHelp;
} else {
packagesHelp = '(required, since the current directory does not contain a "$kPackagesFileName" file)';
showPackagesCommand = true;
}
argParser.addOption('packages', argParser.addOption('packages',
hide: !verboseHelp, hide: !showPackagesCommand,
help: 'Path to your ".packages" file.$packagesHelp'); help: 'Path to your ".packages" file.\n$packagesHelp');
argParser.addOption('flutter-root', argParser.addOption('flutter-root',
help: 'The root directory of the Flutter repository (uses \$$kFlutterRootEnvironmentVariableName if set).'); hide: !verboseHelp,
help: 'The root directory of the Flutter repository.\n'
'Defaults to \$$kFlutterRootEnvironmentVariableName if set, otherwise uses the parent of the\n'
'directory that the "flutter" script itself is in.');
if (verboseHelp) if (verboseHelp)
argParser.addSeparator('Local build selection options (not normally required):'); argParser.addSeparator('Local build selection options (not normally required):');
argParser.addOption('local-engine-src-path', argParser.addOption('local-engine-src-path',
hide: !verboseHelp, hide: !verboseHelp,
help: help: 'Path to your engine src directory, if you are building Flutter locally.\n'
'Path to your engine src directory, if you are building Flutter locally.\n' 'Defaults to \$$kFlutterEngineEnvironmentVariableName if set, otherwise defaults to the path given in your pubspec.yaml\n'
'Defaults to \$$kFlutterEngineEnvironmentVariableName if set, otherwise defaults to the path given in your pubspec.yaml\n' 'dependency_overrides for $kFlutterEnginePackageName, if any, or, failing that, tries to guess at the location\n'
'dependency_overrides for $kFlutterEnginePackageName, if any, or, failing that, tries to guess at the location\n' 'based on the value of the --flutter-root option.');
'based on the value of the --flutter-root option.');
argParser.addOption('local-engine', argParser.addOption('local-engine',
hide: !verboseHelp, hide: !verboseHelp,
help: help: 'Name of a build output within the engine out directory, if you are building Flutter locally.\n'
'Name of a build output within the engine out directory, if you are building Flutter locally.\n' 'Use this to select a specific version of the engine if you have built multiple engine targets.\n'
'Use this to select a specific version of the engine if you have built multiple engine targets.\n' 'This path is relative to --local-engine-src-path/out.');
'This path is relative to --local-engine-src-path/out.');
if (verboseHelp)
argParser.addSeparator('Options for testing the "flutter" tool itself:');
argParser.addOption('record-to', argParser.addOption('record-to',
hide: true, hide: !verboseHelp,
help: help: 'Enables recording of process invocations (including stdout and stderr of all such invocations),\n'
'Enables recording of process invocations (including stdout and stderr of all such invocations),\n' 'and file system access (reads and writes).\n'
'and file system access (reads and writes).\n' 'Serializes that recording to a directory with the path specified in this flag. If the\n'
'Serializes that recording to a directory with the path specified in this flag. If the\n' 'directory does not already exist, it will be created.');
'directory does not already exist, it will be created.');
argParser.addOption('replay-from', argParser.addOption('replay-from',
hide: true, hide: !verboseHelp,
help: help: 'Enables mocking of process invocations by replaying their stdout, stderr, and exit code from\n'
'Enables mocking of process invocations by replaying their stdout, stderr, and exit code from\n' 'the specified recording (obtained via --record-to). The path specified in this flag must refer\n'
'the specified recording (obtained via --record-to). The path specified in this flag must refer\n' 'to a directory that holds serialized process invocations structured according to the output of\n'
'to a directory that holds serialized process invocations structured according to the output of\n' '--record-to.');
'--record-to.'); argParser.addFlag('show-test-device',
negatable: false,
hide: !verboseHelp,
help: 'List the special \'flutter-tester\' device in device listings. '
'This headless device is used to\ntest Flutter tooling.');
} }
@override @override
......
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