mirror of
https://github.com/flutter/flutter
synced 2024-09-21 01:01:58 +00:00
parent
48f4ff6dda
commit
d1cc8b6de8
77
dev/devicelab/bin/tasks/run_release_test.dart
Normal file
77
dev/devicelab/bin/tasks/run_release_test.dart
Normal file
|
@ -0,0 +1,77 @@
|
|||
// 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) {
|
|||
});
|
||||
}
|
||||
|
||||
/// 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(
|
||||
String executable,
|
||||
List<String> arguments, {
|
||||
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,
|
||||
}) async {
|
||||
assert(isBot != null);
|
||||
final String command = '$executable ${arguments?.join(" ") ?? ""}';
|
||||
print('\nExecuting: $command');
|
||||
environment ??= <String, String>{};
|
||||
environment['BOT'] = 'true';
|
||||
environment['BOT'] = isBot ? 'true' : 'false';
|
||||
final Process process = await _processManager.start(
|
||||
<String>[executable]..addAll(arguments),
|
||||
environment: environment,
|
||||
|
|
|
@ -124,6 +124,13 @@ tasks:
|
|||
stage: devicelab
|
||||
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:
|
||||
description: >
|
||||
Checks platform interaction on Android.
|
||||
|
|
|
@ -53,7 +53,6 @@ class GenSnapshot {
|
|||
'--causal_async_stacks',
|
||||
'--packages=$packagesPath',
|
||||
'--dependencies=$depfilePath',
|
||||
'--print_snapshot_sizes',
|
||||
]..addAll(additionalArgs);
|
||||
|
||||
final String snapshotterPath = getSnapshotterPath(snapshotType);
|
||||
|
|
|
@ -21,26 +21,26 @@ class BotDetector {
|
|||
const BotDetector();
|
||||
|
||||
bool get isRunningOnBot {
|
||||
return
|
||||
platform.environment['BOT'] == 'true' ||
|
||||
return platform.environment['BOT'] != 'false'
|
||||
&& (platform.environment['BOT'] == 'true'
|
||||
|
||||
// https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
|
||||
platform.environment['TRAVIS'] == 'true' ||
|
||||
platform.environment['CONTINUOUS_INTEGRATION'] == 'true' ||
|
||||
platform.environment.containsKey('CI') || // Travis and AppVeyor
|
||||
// https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
|
||||
|| platform.environment['TRAVIS'] == 'true'
|
||||
|| platform.environment['CONTINUOUS_INTEGRATION'] == 'true'
|
||||
|| platform.environment.containsKey('CI') // Travis and AppVeyor
|
||||
|
||||
// https://www.appveyor.com/docs/environment-variables/
|
||||
platform.environment.containsKey('APPVEYOR') ||
|
||||
// https://www.appveyor.com/docs/environment-variables/
|
||||
|| platform.environment.containsKey('APPVEYOR')
|
||||
|
||||
// https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html
|
||||
(platform.environment.containsKey('AWS_REGION') && platform.environment.containsKey('CODEBUILD_INITIATOR')) ||
|
||||
// https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html
|
||||
|| (platform.environment.containsKey('AWS_REGION') && platform.environment.containsKey('CODEBUILD_INITIATOR'))
|
||||
|
||||
// https://wiki.jenkins.io/display/JENKINS/Building+a+software+project#Buildingasoftwareproject-belowJenkinsSetEnvironmentVariables
|
||||
platform.environment.containsKey('JENKINS_URL') ||
|
||||
// https://wiki.jenkins.io/display/JENKINS/Building+a+software+project#Buildingasoftwareproject-belowJenkinsSetEnvironmentVariables
|
||||
|| platform.environment.containsKey('JENKINS_URL')
|
||||
|
||||
// Properties on Flutter's Chrome Infra bots.
|
||||
platform.environment['CHROME_HEADLESS'] == '1' ||
|
||||
platform.environment.containsKey('BUILDBOT_BUILDERNAME');
|
||||
// Properties on Flutter's Chrome Infra bots.
|
||||
|| platform.environment['CHROME_HEADLESS'] == '1'
|
||||
|| platform.environment.containsKey('BUILDBOT_BUILDERNAME'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -845,8 +845,9 @@ abstract class ResidentRunner {
|
|||
}
|
||||
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".');
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when a signal has requested we exit.
|
||||
|
|
|
@ -125,22 +125,29 @@ class ColdRunner extends ResidentRunner {
|
|||
@override
|
||||
void printHelp({ @required bool details }) {
|
||||
bool haveDetails = false;
|
||||
bool haveAnything = false;
|
||||
for (FlutterDevice device in flutterDevices) {
|
||||
final String dname = device.device.name;
|
||||
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');
|
||||
haveAnything = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (supportsServiceProtocol) {
|
||||
haveDetails = true;
|
||||
if (details)
|
||||
if (details) {
|
||||
printHelpDetails();
|
||||
haveAnything = true;
|
||||
}
|
||||
}
|
||||
if (haveDetails && !details) {
|
||||
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".');
|
||||
} else {
|
||||
printStatus('To quit, press "q".');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,8 @@ class FlutterCommandRunner extends CommandRunner<Null> {
|
|||
argParser.addFlag('verbose',
|
||||
abbr: 'v',
|
||||
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',
|
||||
negatable: false,
|
||||
hide: !verboseHelp,
|
||||
|
@ -66,11 +67,12 @@ class FlutterCommandRunner extends CommandRunner<Null> {
|
|||
help: 'Reports the version of this tool.');
|
||||
argParser.addFlag('machine',
|
||||
negatable: false,
|
||||
hide: true);
|
||||
hide: !verboseHelp,
|
||||
help: 'When used with the --version flag, outputs the information using JSON.');
|
||||
argParser.addFlag('color',
|
||||
negatable: true,
|
||||
hide: !verboseHelp,
|
||||
help: 'Whether to use terminal colors.');
|
||||
help: 'Whether to use terminal colors (requires support for ANSI escape sequences).');
|
||||
argParser.addFlag('version-check',
|
||||
negatable: true,
|
||||
defaultsTo: true,
|
||||
|
@ -78,61 +80,67 @@ class FlutterCommandRunner extends CommandRunner<Null> {
|
|||
help: 'Allow Flutter to check for updates when this command runs.');
|
||||
argParser.addFlag('suppress-analytics',
|
||||
negatable: false,
|
||||
hide: !verboseHelp,
|
||||
help: 'Suppress analytics reporting when this command runs.');
|
||||
argParser.addFlag('bug-report',
|
||||
negatable: false,
|
||||
help:
|
||||
'Captures a bug report file to submit to the Flutter team '
|
||||
'(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.');
|
||||
help: 'Captures a bug report file to submit to the Flutter team.\n'
|
||||
'Contains local paths, device identifiers, and log snippets.');
|
||||
|
||||
String packagesHelp;
|
||||
if (fs.isFileSync(kPackagesFileName))
|
||||
packagesHelp = '\n(defaults to "$kPackagesFileName")';
|
||||
else
|
||||
packagesHelp = '\n(required, since the current directory does not contain a "$kPackagesFileName" file)';
|
||||
bool showPackagesCommand;
|
||||
if (fs.isFileSync(kPackagesFileName)) {
|
||||
packagesHelp = '(defaults to "$kPackagesFileName")';
|
||||
showPackagesCommand = verboseHelp;
|
||||
} else {
|
||||
packagesHelp = '(required, since the current directory does not contain a "$kPackagesFileName" file)';
|
||||
showPackagesCommand = true;
|
||||
}
|
||||
argParser.addOption('packages',
|
||||
hide: !verboseHelp,
|
||||
help: 'Path to your ".packages" file.$packagesHelp');
|
||||
hide: !showPackagesCommand,
|
||||
help: 'Path to your ".packages" file.\n$packagesHelp');
|
||||
|
||||
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)
|
||||
argParser.addSeparator('Local build selection options (not normally required):');
|
||||
|
||||
argParser.addOption('local-engine-src-path',
|
||||
hide: !verboseHelp,
|
||||
help:
|
||||
'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'
|
||||
'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.');
|
||||
help: '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'
|
||||
'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.');
|
||||
|
||||
argParser.addOption('local-engine',
|
||||
hide: !verboseHelp,
|
||||
help:
|
||||
'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'
|
||||
'This path is relative to --local-engine-src-path/out.');
|
||||
help: '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'
|
||||
'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',
|
||||
hide: true,
|
||||
help:
|
||||
'Enables recording of process invocations (including stdout and stderr of all such invocations),\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'
|
||||
'directory does not already exist, it will be created.');
|
||||
hide: !verboseHelp,
|
||||
help: 'Enables recording of process invocations (including stdout and stderr of all such invocations),\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'
|
||||
'directory does not already exist, it will be created.');
|
||||
argParser.addOption('replay-from',
|
||||
hide: true,
|
||||
help:
|
||||
'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'
|
||||
'to a directory that holds serialized process invocations structured according to the output of\n'
|
||||
'--record-to.');
|
||||
hide: !verboseHelp,
|
||||
help: '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'
|
||||
'to a directory that holds serialized process invocations structured according to the output of\n'
|
||||
'--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
|
||||
|
|
Loading…
Reference in a new issue