Adds tool warning log level and command line options to fail on warning/error output (#92031)
This commit is contained in:
parent
02cfbc320e
commit
52ae102f18
@ -170,6 +170,7 @@ Future<void> main(List<String> args) async {
|
||||
try {
|
||||
flutterTestArgs.addAll(args);
|
||||
final Set<String> removeArgs = <String>{};
|
||||
bool runSmokeTests = true;
|
||||
for (final String arg in args) {
|
||||
if (arg.startsWith('--local-engine=')) {
|
||||
localEngineEnv['FLUTTER_LOCAL_ENGINE'] = arg.substring('--local-engine='.length);
|
||||
@ -181,12 +182,18 @@ Future<void> main(List<String> args) async {
|
||||
_shuffleSeed = arg.substring('--test-randomize-ordering-seed='.length);
|
||||
removeArgs.add(arg);
|
||||
}
|
||||
if (arg == '--no-smoke-tests') {
|
||||
runSmokeTests = false;
|
||||
removeArgs.add(arg);
|
||||
}
|
||||
}
|
||||
flutterTestArgs.removeWhere((String arg) => removeArgs.contains(arg));
|
||||
if (Platform.environment.containsKey(CIRRUS_TASK_NAME))
|
||||
print('Running task: ${Platform.environment[CIRRUS_TASK_NAME]}');
|
||||
print('═' * 80);
|
||||
await _runSmokeTests();
|
||||
if (runSmokeTests) {
|
||||
await _runSmokeTests();
|
||||
}
|
||||
print('═' * 80);
|
||||
await selectShard(<String, ShardRunner>{
|
||||
'add_to_app_life_cycle_tests': _runAddToAppLifeCycleTests,
|
||||
@ -474,7 +481,7 @@ Future<void> _runBuildTests() async {
|
||||
Future<void> _runExampleProjectBuildTests(Directory exampleDirectory, [File? mainFile]) async {
|
||||
// Only verify caching with flutter gallery.
|
||||
final bool verifyCaching = exampleDirectory.path.contains('flutter_gallery');
|
||||
final String examplePath = exampleDirectory.path;
|
||||
final String examplePath = path.relative(exampleDirectory.path, from: Directory.current.path);
|
||||
final bool hasNullSafety = File(path.join(examplePath, 'null_safety')).existsSync();
|
||||
final List<String> additionalArgs = <String>[
|
||||
if (hasNullSafety) '--no-sound-null-safety',
|
||||
@ -786,7 +793,8 @@ Future<void> _runFrameworkTests() async {
|
||||
await _pubRunTest(path.join(flutterRoot, 'dev', 'bots'));
|
||||
await _pubRunTest(path.join(flutterRoot, 'dev', 'devicelab'), ensurePrecompiledTool: false); // See https://github.com/flutter/flutter/issues/86209
|
||||
await _pubRunTest(path.join(flutterRoot, 'dev', 'conductor', 'core'), forceSingleCore: true);
|
||||
await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'android_semantics_testing'));
|
||||
// TODO(gspencergoog): Remove the exception for fatalWarnings once https://github.com/flutter/flutter/pull/91127 has landed.
|
||||
await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'android_semantics_testing'), fatalWarnings: false);
|
||||
await _runFlutterTest(path.join(flutterRoot, 'dev', 'manual_tests'));
|
||||
await _runFlutterTest(path.join(flutterRoot, 'dev', 'tools', 'vitool'));
|
||||
await _runFlutterTest(path.join(flutterRoot, 'dev', 'tools', 'gen_keycodes'));
|
||||
@ -1621,6 +1629,7 @@ Future<void> _runFlutterTest(String workingDirectory, {
|
||||
Map<String, String>? environment,
|
||||
List<String> tests = const <String>[],
|
||||
bool shuffleTests = true,
|
||||
bool fatalWarnings = true,
|
||||
}) async {
|
||||
assert(!printOutput || outputChecker == null, 'Output either can be printed or checked but not both');
|
||||
|
||||
@ -1634,6 +1643,7 @@ Future<void> _runFlutterTest(String workingDirectory, {
|
||||
final List<String> args = <String>[
|
||||
'test',
|
||||
if (shuffleTests) '--test-randomize-ordering-seed=$shuffleSeed',
|
||||
if (fatalWarnings) '--fatal-warnings',
|
||||
...options,
|
||||
...tags,
|
||||
...flutterTestArgs,
|
||||
|
@ -43,15 +43,13 @@ class CodesignCommand extends Command<void> {
|
||||
}
|
||||
argParser.addFlag(
|
||||
kVerify,
|
||||
help:
|
||||
'Only verify expected binaries exist and are codesigned with entitlements.',
|
||||
help: 'Only verify expected binaries exist and are codesigned with entitlements.',
|
||||
);
|
||||
argParser.addFlag(
|
||||
kSignatures,
|
||||
defaultsTo: true,
|
||||
help:
|
||||
'When off, this command will only verify the existence of binaries, and not their\n'
|
||||
'signatures or entitlements. Must be used with --verify flag.',
|
||||
help: 'When off, this command will only verify the existence of binaries, and not their\n'
|
||||
'signatures or entitlements. Must be used with --verify flag.',
|
||||
);
|
||||
argParser.addOption(
|
||||
kUpstream,
|
||||
@ -92,24 +90,25 @@ class CodesignCommand extends Command<void> {
|
||||
Future<void> run() async {
|
||||
if (!platform.isMacOS) {
|
||||
throw ConductorException(
|
||||
'Error! Expected operating system "macos", actual operating system is: '
|
||||
'"${platform.operatingSystem}"');
|
||||
'Error! Expected operating system "macos", actual operating system is: '
|
||||
'"${platform.operatingSystem}"',
|
||||
);
|
||||
}
|
||||
|
||||
if (argResults!['verify'] as bool != true) {
|
||||
throw ConductorException(
|
||||
'Sorry, but codesigning is not implemented yet. Please pass the '
|
||||
'--$kVerify flag to verify signatures.');
|
||||
'Sorry, but codesigning is not implemented yet. Please pass the '
|
||||
'--$kVerify flag to verify signatures.',
|
||||
);
|
||||
}
|
||||
|
||||
String revision;
|
||||
if (argResults!.wasParsed(kRevision)) {
|
||||
stdio.printError(
|
||||
'Warning! When providing an arbitrary revision, the contents of the cache may not');
|
||||
stdio.printError(
|
||||
'match the expected binaries in the conductor tool. It is preferred to check out');
|
||||
stdio.printError(
|
||||
'the desired revision and run that version of the conductor.\n');
|
||||
stdio.printWarning(
|
||||
'Warning! When providing an arbitrary revision, the contents of the cache may not '
|
||||
'match the expected binaries in the conductor tool. It is preferred to check out '
|
||||
'the desired revision and run that version of the conductor.\n',
|
||||
);
|
||||
revision = argResults![kRevision] as String;
|
||||
} else {
|
||||
revision = ((await processManager.run(
|
||||
@ -225,13 +224,11 @@ class CodesignCommand extends Command<void> {
|
||||
)
|
||||
.toList();
|
||||
stdio.printError(
|
||||
'Expected binaries not found in cache:\n\n${unfoundFiles.join('\n')}\n');
|
||||
stdio.printError(
|
||||
'If this commit is removing binaries from the cache, this test should be fixed by');
|
||||
stdio.printError(
|
||||
'removing the relevant entry from either the `binariesWithEntitlements` or');
|
||||
stdio.printError(
|
||||
'`binariesWithoutEntitlements` getters in dev/tools/lib/codesign.dart.');
|
||||
'Expected binaries not found in cache:\n\n${unfoundFiles.join('\n')}\n\n'
|
||||
'If this commit is removing binaries from the cache, this test should be fixed by\n'
|
||||
'removing the relevant entry from either the "binariesWithEntitlements" or\n'
|
||||
'"binariesWithoutEntitlements" getters in dev/tools/lib/codesign.dart.',
|
||||
);
|
||||
throw ConductorException('Did not find all expected binaries!');
|
||||
}
|
||||
|
||||
@ -271,9 +268,10 @@ class CodesignCommand extends Command<void> {
|
||||
if (codeSignResult.exitCode != 0) {
|
||||
unsignedBinaries.add(binaryPath);
|
||||
stdio.printError(
|
||||
'File "$binaryPath" does not appear to be codesigned.\n'
|
||||
'The `codesign` command failed with exit code ${codeSignResult.exitCode}:\n'
|
||||
'${codeSignResult.stderr}\n');
|
||||
'File "$binaryPath" does not appear to be codesigned.\n'
|
||||
'The `codesign` command failed with exit code ${codeSignResult.exitCode}:\n'
|
||||
'${codeSignResult.stderr}\n',
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (verifyEntitlements) {
|
||||
@ -291,42 +289,39 @@ class CodesignCommand extends Command<void> {
|
||||
}
|
||||
|
||||
if (wrongEntitlementBinaries.isNotEmpty) {
|
||||
stdio.printError(
|
||||
'Found ${wrongEntitlementBinaries.length} binaries with unexpected entitlements:');
|
||||
stdio.printError('Found ${wrongEntitlementBinaries.length} binaries with unexpected entitlements:');
|
||||
wrongEntitlementBinaries.forEach(stdio.printError);
|
||||
}
|
||||
|
||||
if (unexpectedBinaries.isNotEmpty) {
|
||||
stdio.printError(
|
||||
'Found ${unexpectedBinaries.length} unexpected binaries in the cache:');
|
||||
stdio.printError('Found ${unexpectedBinaries.length} unexpected binaries in the cache:');
|
||||
unexpectedBinaries.forEach(print);
|
||||
}
|
||||
|
||||
// Finally, exit on any invalid state
|
||||
if (unsignedBinaries.isNotEmpty) {
|
||||
throw ConductorException(
|
||||
'Test failed because unsigned binaries detected.');
|
||||
throw ConductorException('Test failed because unsigned binaries detected.');
|
||||
}
|
||||
|
||||
if (wrongEntitlementBinaries.isNotEmpty) {
|
||||
throw ConductorException(
|
||||
'Test failed because files found with the wrong entitlements:\n'
|
||||
'${wrongEntitlementBinaries.join('\n')}');
|
||||
'Test failed because files found with the wrong entitlements:\n'
|
||||
'${wrongEntitlementBinaries.join('\n')}',
|
||||
);
|
||||
}
|
||||
|
||||
if (unexpectedBinaries.isNotEmpty) {
|
||||
throw ConductorException(
|
||||
'Test failed because unexpected binaries found in the cache.');
|
||||
throw ConductorException('Test failed because unexpected binaries found in the cache.');
|
||||
}
|
||||
|
||||
final String? desiredRevision = argResults![kRevision] as String?;
|
||||
if (desiredRevision == null) {
|
||||
stdio.printStatus(
|
||||
'Verified that binaries are codesigned and have expected entitlements.');
|
||||
stdio.printStatus('Verified that binaries are codesigned and have expected entitlements.');
|
||||
} else {
|
||||
stdio.printStatus(
|
||||
'Verified that binaries for commit $desiredRevision are codesigned and have '
|
||||
'expected entitlements.');
|
||||
'Verified that binaries for commit $desiredRevision are codesigned and have '
|
||||
'expected entitlements.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,8 +382,9 @@ class CodesignCommand extends Command<void> {
|
||||
|
||||
if (entitlementResult.exitCode != 0) {
|
||||
stdio.printError(
|
||||
'The `codesign --entitlements` command failed with exit code ${entitlementResult.exitCode}:\n'
|
||||
'${entitlementResult.stderr}\n');
|
||||
'The `codesign --entitlements` command failed with exit code ${entitlementResult.exitCode}:\n'
|
||||
'${entitlementResult.stderr}\n',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -399,8 +395,9 @@ class CodesignCommand extends Command<void> {
|
||||
(await binariesWithEntitlements).contains(binaryPath);
|
||||
if (output.contains(entitlement) != entitlementExpected) {
|
||||
stdio.printError(
|
||||
'File "$binaryPath" ${entitlementExpected ? 'does not have expected' : 'has unexpected'} '
|
||||
'entitlement $entitlement.');
|
||||
'File "$binaryPath" ${entitlementExpected ? 'does not have expected' : 'has unexpected'} '
|
||||
'entitlement $entitlement.',
|
||||
);
|
||||
passes = false;
|
||||
}
|
||||
}
|
||||
|
@ -9,25 +9,44 @@ import 'package:meta/meta.dart';
|
||||
abstract class Stdio {
|
||||
final List<String> logs = <String>[];
|
||||
|
||||
/// Error/warning messages printed to STDERR.
|
||||
/// Error messages printed to STDERR.
|
||||
///
|
||||
/// Display an error `message` to the user on stderr. Print errors if the code
|
||||
/// fails in some way. Errors are typically followed shortly by exiting the
|
||||
/// app with a non-zero exit status.
|
||||
@mustCallSuper
|
||||
void printError(String message) {
|
||||
logs.add('[error] $message');
|
||||
}
|
||||
|
||||
/// Warning messages printed to STDERR.
|
||||
///
|
||||
/// Display a warning `message` to the user on stderr. Print warnings if there
|
||||
/// is important information to convey to the user that is not fatal.
|
||||
@mustCallSuper
|
||||
void printWarning(String message) {
|
||||
logs.add('[warning] $message');
|
||||
}
|
||||
|
||||
/// Ordinary STDOUT messages.
|
||||
///
|
||||
/// Displays normal output on stdout. This should be used for things like
|
||||
/// progress messages, success messages, or just normal command output.
|
||||
@mustCallSuper
|
||||
void printStatus(String message) {
|
||||
logs.add('[status] $message');
|
||||
}
|
||||
|
||||
/// Debug messages that are only printed in verbose mode.
|
||||
///
|
||||
/// Use this for verbose tracing output. Users can turn this output on in order
|
||||
/// to help diagnose issues.
|
||||
@mustCallSuper
|
||||
void printTrace(String message) {
|
||||
logs.add('[trace] $message');
|
||||
}
|
||||
|
||||
/// Write string to STDOUT without trailing newline.
|
||||
/// Write the `message` string to STDOUT without a trailing newline.
|
||||
@mustCallSuper
|
||||
void write(String message) {
|
||||
logs.add('[write] $message');
|
||||
|
@ -49,6 +49,7 @@ class TestCommand extends Command<void> {
|
||||
'task, will write test results to the file.');
|
||||
argParser.addFlag(
|
||||
'silent',
|
||||
help: 'Suppresses standard output and only print standard error output.',
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:flutter_tools/src/asset.dart' hide defaultManifestPath;
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
import 'package:flutter_tools/src/base/context.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart' as libfs;
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
@ -70,8 +71,7 @@ Future<void> run(List<String> args) async {
|
||||
);
|
||||
|
||||
if (assets == null) {
|
||||
print('Unable to find assets.');
|
||||
exit(1);
|
||||
throwToolExit('Unable to find assets.', exitCode: 1);
|
||||
}
|
||||
|
||||
final List<Future<void>> calls = <Future<void>>[];
|
||||
|
@ -39,7 +39,7 @@ Future<int> run(
|
||||
// Remove the verbose option; for help and doctor, users don't need to see
|
||||
// verbose logs.
|
||||
args = List<String>.of(args);
|
||||
args.removeWhere((String option) => option == '-v' || option == '--verbose');
|
||||
args.removeWhere((String option) => option == '-vv' || option == '-v' || option == '--verbose');
|
||||
}
|
||||
|
||||
return runInContext<int>(() async {
|
||||
|
@ -7,6 +7,7 @@ import 'dart:async';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../convert.dart';
|
||||
import 'common.dart';
|
||||
import 'io.dart';
|
||||
import 'terminal.dart' show Terminal, TerminalColor, OutputPreferences;
|
||||
import 'utils.dart';
|
||||
@ -28,22 +29,41 @@ class StopwatchFactory {
|
||||
typedef VoidCallback = void Function();
|
||||
|
||||
abstract class Logger {
|
||||
/// Whether or not this logger should print [printTrace] messages.
|
||||
bool get isVerbose => false;
|
||||
|
||||
/// If true, silences the logger output.
|
||||
bool quiet = false;
|
||||
|
||||
/// If true, this logger supports color output.
|
||||
bool get supportsColor;
|
||||
|
||||
/// If true, this logger is connected to a terminal.
|
||||
bool get hasTerminal;
|
||||
|
||||
/// If true, then [printError] has been called at least once for this logger
|
||||
/// since the last time it was set to false.
|
||||
bool hadErrorOutput = false;
|
||||
|
||||
/// If true, then [printWarning] has been called at least once for this logger
|
||||
/// since the last time it was reset to false.
|
||||
bool hadWarningOutput = false;
|
||||
|
||||
/// Causes [checkForFatalLogs] to call [throwToolExit] when it is called if
|
||||
/// [hadWarningOutput] is true.
|
||||
bool fatalWarnings = false;
|
||||
|
||||
/// Returns the terminal attached to this logger.
|
||||
Terminal get terminal;
|
||||
|
||||
OutputPreferences get _outputPreferences;
|
||||
|
||||
/// Display an error `message` to the user. Commands should use this if they
|
||||
/// fail in some way.
|
||||
/// fail in some way. Errors are typically followed shortly by a call to
|
||||
/// [throwToolExit] to terminate the run.
|
||||
///
|
||||
/// The `message` argument is printed to the stderr in red by default.
|
||||
/// The `message` argument is printed to the stderr in [TerminalColor.red] by
|
||||
/// default.
|
||||
///
|
||||
/// The `stackTrace` argument is the stack trace that will be printed if
|
||||
/// supplied.
|
||||
@ -74,10 +94,41 @@ abstract class Logger {
|
||||
bool? wrap,
|
||||
});
|
||||
|
||||
/// Display a warning `message` to the user. Commands should use this if they
|
||||
/// important information to convey to the user that is not fatal.
|
||||
///
|
||||
/// The `message` argument is printed to the stderr in [TerminalColor.cyan] by
|
||||
/// default.
|
||||
///
|
||||
/// The `emphasis` argument will cause the output message be printed in bold text.
|
||||
///
|
||||
/// The `color` argument will print the message in the supplied color instead
|
||||
/// of the default of cyan. Colors will not be printed if the output terminal
|
||||
/// doesn't support them.
|
||||
///
|
||||
/// The `indent` argument specifies the number of spaces to indent the overall
|
||||
/// message. If wrapping is enabled in [outputPreferences], then the wrapped
|
||||
/// lines will be indented as well.
|
||||
///
|
||||
/// If `hangingIndent` is specified, then any wrapped lines will be indented
|
||||
/// by this much more than the first line, if wrapping is enabled in
|
||||
/// [outputPreferences].
|
||||
///
|
||||
/// If `wrap` is specified, then it overrides the
|
||||
/// `outputPreferences.wrapText` setting.
|
||||
void printWarning(
|
||||
String message, {
|
||||
bool? emphasis,
|
||||
TerminalColor? color,
|
||||
int? indent,
|
||||
int? hangingIndent,
|
||||
bool? wrap,
|
||||
});
|
||||
|
||||
/// Display normal output of the command. This should be used for things like
|
||||
/// progress messages, success messages, or just normal command output.
|
||||
///
|
||||
/// The `message` argument is printed to the stderr in red by default.
|
||||
/// The `message` argument is printed to the stdout.
|
||||
///
|
||||
/// The `stackTrace` argument is the stack trace that will be printed if
|
||||
/// supplied.
|
||||
@ -144,9 +195,21 @@ abstract class Logger {
|
||||
|
||||
/// Clears all output.
|
||||
void clear();
|
||||
|
||||
/// If [fatalWarnings] is set, causes the logger to check if
|
||||
/// [hadWarningOutput] is true, and then to call [throwToolExit] if so.
|
||||
///
|
||||
/// The [fatalWarnings] flag can be set from the command line with the
|
||||
/// "--fatal-warnings" option on commands that support it.
|
||||
void checkForFatalLogs() {
|
||||
if (fatalWarnings && (hadWarningOutput || hadErrorOutput)) {
|
||||
throwToolExit('Logger received ${hadErrorOutput ? 'error' : 'warning'} output '
|
||||
'during the run, and "--fatal-warnings" is enabled.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A [Logger] that forwards all methods to another one.
|
||||
/// A [Logger] that forwards all methods to another logger.
|
||||
///
|
||||
/// Classes can derive from this to add functionality to an existing [Logger].
|
||||
class DelegatingLogger implements Logger {
|
||||
@ -174,6 +237,24 @@ class DelegatingLogger implements Logger {
|
||||
@override
|
||||
bool get isVerbose => _delegate.isVerbose;
|
||||
|
||||
@override
|
||||
bool get hadErrorOutput => _delegate.hadErrorOutput;
|
||||
|
||||
@override
|
||||
set hadErrorOutput(bool value) => _delegate.hadErrorOutput = value;
|
||||
|
||||
@override
|
||||
bool get hadWarningOutput => _delegate.hadWarningOutput;
|
||||
|
||||
@override
|
||||
set hadWarningOutput(bool value) => _delegate.hadWarningOutput = value;
|
||||
|
||||
@override
|
||||
bool get fatalWarnings => _delegate.fatalWarnings;
|
||||
|
||||
@override
|
||||
set fatalWarnings(bool value) => _delegate.fatalWarnings = value;
|
||||
|
||||
@override
|
||||
void printError(String message, {
|
||||
StackTrace? stackTrace,
|
||||
@ -194,6 +275,24 @@ class DelegatingLogger implements Logger {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void printWarning(String message, {
|
||||
bool? emphasis,
|
||||
TerminalColor? color,
|
||||
int? indent,
|
||||
int? hangingIndent,
|
||||
bool? wrap,
|
||||
}) {
|
||||
_delegate.printWarning(
|
||||
message,
|
||||
emphasis: emphasis,
|
||||
color: color,
|
||||
indent: indent,
|
||||
hangingIndent: hangingIndent,
|
||||
wrap: wrap,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void printStatus(String message, {
|
||||
bool? emphasis,
|
||||
@ -244,6 +343,9 @@ class DelegatingLogger implements Logger {
|
||||
|
||||
@override
|
||||
void clear() => _delegate.clear();
|
||||
|
||||
@override
|
||||
void checkForFatalLogs() => _delegate.checkForFatalLogs();
|
||||
}
|
||||
|
||||
/// If [logger] is a [DelegatingLogger], walks the delegate chain and returns
|
||||
@ -303,6 +405,7 @@ class StdoutLogger extends Logger {
|
||||
int? hangingIndent,
|
||||
bool? wrap,
|
||||
}) {
|
||||
hadErrorOutput = true;
|
||||
_status?.pause();
|
||||
message = wrapText(message,
|
||||
indent: indent,
|
||||
@ -321,6 +424,31 @@ class StdoutLogger extends Logger {
|
||||
_status?.resume();
|
||||
}
|
||||
|
||||
@override
|
||||
void printWarning(
|
||||
String message, {
|
||||
bool? emphasis,
|
||||
TerminalColor? color,
|
||||
int? indent,
|
||||
int? hangingIndent,
|
||||
bool? wrap,
|
||||
}) {
|
||||
hadWarningOutput = true;
|
||||
_status?.pause();
|
||||
message = wrapText(message,
|
||||
indent: indent,
|
||||
hangingIndent: hangingIndent,
|
||||
shouldWrap: wrap ?? _outputPreferences.wrapText,
|
||||
columnWidth: _outputPreferences.wrapColumn,
|
||||
);
|
||||
if (emphasis == true) {
|
||||
message = terminal.bolden(message);
|
||||
}
|
||||
message = terminal.color(message, color ?? TerminalColor.cyan);
|
||||
writeToStdErr('$message\n');
|
||||
_status?.resume();
|
||||
}
|
||||
|
||||
@override
|
||||
void printStatus(
|
||||
String message, {
|
||||
@ -482,7 +610,6 @@ class BufferLogger extends Logger {
|
||||
_outputPreferences = outputPreferences ?? OutputPreferences.test(),
|
||||
_stopwatchFactory = const StopwatchFactory();
|
||||
|
||||
|
||||
@override
|
||||
final OutputPreferences _outputPreferences;
|
||||
|
||||
@ -498,11 +625,13 @@ class BufferLogger extends Logger {
|
||||
bool get supportsColor => terminal.supportsColor;
|
||||
|
||||
final StringBuffer _error = StringBuffer();
|
||||
final StringBuffer _warning = StringBuffer();
|
||||
final StringBuffer _status = StringBuffer();
|
||||
final StringBuffer _trace = StringBuffer();
|
||||
final StringBuffer _events = StringBuffer();
|
||||
|
||||
String get errorText => _error.toString();
|
||||
String get warningText => _warning.toString();
|
||||
String get statusText => _status.toString();
|
||||
String get traceText => _trace.toString();
|
||||
String get eventText => _events.toString();
|
||||
@ -520,6 +649,7 @@ class BufferLogger extends Logger {
|
||||
int? hangingIndent,
|
||||
bool? wrap,
|
||||
}) {
|
||||
hadErrorOutput = true;
|
||||
_error.writeln(terminal.color(
|
||||
wrapText(message,
|
||||
indent: indent,
|
||||
@ -531,6 +661,27 @@ class BufferLogger extends Logger {
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
void printWarning(
|
||||
String message, {
|
||||
bool? emphasis,
|
||||
TerminalColor? color,
|
||||
int? indent,
|
||||
int? hangingIndent,
|
||||
bool? wrap,
|
||||
}) {
|
||||
hadWarningOutput = true;
|
||||
_warning.writeln(terminal.color(
|
||||
wrapText(message,
|
||||
indent: indent,
|
||||
hangingIndent: hangingIndent,
|
||||
shouldWrap: wrap ?? _outputPreferences.wrapText,
|
||||
columnWidth: _outputPreferences.wrapColumn,
|
||||
),
|
||||
color ?? TerminalColor.cyan,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
void printStatus(
|
||||
String message, {
|
||||
@ -625,6 +776,7 @@ class VerboseLogger extends DelegatingLogger {
|
||||
int? hangingIndent,
|
||||
bool? wrap,
|
||||
}) {
|
||||
hadErrorOutput = true;
|
||||
_emit(
|
||||
_LogType.error,
|
||||
wrapText(message,
|
||||
@ -637,6 +789,29 @@ class VerboseLogger extends DelegatingLogger {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void printWarning(
|
||||
String message, {
|
||||
StackTrace? stackTrace,
|
||||
bool? emphasis,
|
||||
TerminalColor? color,
|
||||
int? indent,
|
||||
int? hangingIndent,
|
||||
bool? wrap,
|
||||
}) {
|
||||
hadWarningOutput = true;
|
||||
_emit(
|
||||
_LogType.warning,
|
||||
wrapText(message,
|
||||
indent: indent,
|
||||
hangingIndent: hangingIndent,
|
||||
shouldWrap: wrap ?? _outputPreferences.wrapText,
|
||||
columnWidth: _outputPreferences.wrapColumn,
|
||||
),
|
||||
stackTrace,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void printStatus(
|
||||
String message, {
|
||||
@ -707,15 +882,25 @@ class VerboseLogger extends DelegatingLogger {
|
||||
final String indent = ''.padLeft(prefix.length);
|
||||
final String indentMessage = message.replaceAll('\n', '\n$indent');
|
||||
|
||||
if (type == _LogType.error) {
|
||||
super.printError(prefix + terminal.bolden(indentMessage));
|
||||
if (stackTrace != null) {
|
||||
super.printError(indent + stackTrace.toString().replaceAll('\n', '\n$indent'));
|
||||
}
|
||||
} else if (type == _LogType.status) {
|
||||
super.printStatus(prefix + terminal.bolden(indentMessage));
|
||||
} else {
|
||||
super.printStatus(prefix + indentMessage);
|
||||
switch (type) {
|
||||
case _LogType.error:
|
||||
super.printError(prefix + terminal.bolden(indentMessage));
|
||||
if (stackTrace != null) {
|
||||
super.printError(indent + stackTrace.toString().replaceAll('\n', '\n$indent'));
|
||||
}
|
||||
break;
|
||||
case _LogType.warning:
|
||||
super.printWarning(prefix + terminal.bolden(indentMessage));
|
||||
break;
|
||||
case _LogType.status:
|
||||
super.printStatus(prefix + terminal.bolden(indentMessage));
|
||||
break;
|
||||
case _LogType.trace:
|
||||
// This seems wrong, since there is a 'printTrace' to call on the
|
||||
// superclass, but it's actually the entire point of this logger: to
|
||||
// make things more verbose than they normally would be.
|
||||
super.printStatus(prefix + indentMessage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -736,6 +921,7 @@ class PrefixedErrorLogger extends DelegatingLogger {
|
||||
int? hangingIndent,
|
||||
bool? wrap,
|
||||
}) {
|
||||
hadErrorOutput = true;
|
||||
if (message.trim().isNotEmpty == true) {
|
||||
message = 'ERROR: $message';
|
||||
}
|
||||
@ -751,7 +937,7 @@ class PrefixedErrorLogger extends DelegatingLogger {
|
||||
}
|
||||
}
|
||||
|
||||
enum _LogType { error, status, trace }
|
||||
enum _LogType { error, warning, status, trace }
|
||||
|
||||
typedef SlowWarningCallback = String Function();
|
||||
|
||||
@ -820,7 +1006,7 @@ abstract class Status {
|
||||
}
|
||||
}
|
||||
|
||||
/// A [SilentStatus] shows nothing.
|
||||
/// A [Status] that shows nothing.
|
||||
class SilentStatus extends Status {
|
||||
SilentStatus({
|
||||
required Stopwatch stopwatch,
|
||||
|
@ -751,7 +751,7 @@ HostPlatform getCurrentHostPlatform() {
|
||||
return HostPlatform.windows_x64;
|
||||
}
|
||||
|
||||
globals.printError('Unsupported host platform, defaulting to Linux');
|
||||
globals.printWarning('Unsupported host platform, defaulting to Linux');
|
||||
|
||||
return HostPlatform.linux_x64;
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ class Dart2JSTarget extends Target {
|
||||
final File dart2jsDeps = environment.buildDir
|
||||
.childFile('app.dill.deps');
|
||||
if (!dart2jsDeps.existsSync()) {
|
||||
globals.printError('Warning: dart2js did not produced expected deps list at '
|
||||
globals.printWarning('Warning: dart2js did not produced expected deps list at '
|
||||
'${dart2jsDeps.path}');
|
||||
return;
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ Future<void> writeBundle(
|
||||
try {
|
||||
bundleDir.deleteSync(recursive: true);
|
||||
} on FileSystemException catch (err) {
|
||||
loggerOverride.printError(
|
||||
loggerOverride.printWarning(
|
||||
'Failed to clean up asset directory ${bundleDir.path}: $err\n'
|
||||
'To clean build artifacts, use the command "flutter clean".'
|
||||
);
|
||||
|
@ -164,6 +164,7 @@ class Cache {
|
||||
|
||||
late final ArtifactUpdater _artifactUpdater = _createUpdater();
|
||||
|
||||
@visibleForTesting
|
||||
@protected
|
||||
void registerArtifact(ArtifactSet artifactSet) {
|
||||
_artifacts.add(artifactSet);
|
||||
@ -320,13 +321,17 @@ class Cache {
|
||||
} on FileSystemException {
|
||||
if (!printed) {
|
||||
_logger.printTrace('Waiting to be able to obtain lock of Flutter binary artifacts directory: ${_lock!.path}');
|
||||
// This needs to go to stderr to avoid cluttering up stdout if a parent
|
||||
// process is collecting stdout. It's not really an "error" though,
|
||||
// so print it in grey.
|
||||
_logger.printError(
|
||||
// This needs to go to stderr to avoid cluttering up stdout if a
|
||||
// parent process is collecting stdout (e.g. when calling "flutter
|
||||
// version --machine"). It's not really a "warning" though, so print it
|
||||
// in grey. Also, make sure that it isn't counted as a warning for
|
||||
// Logger.warningsAreFatal.
|
||||
final bool oldWarnings = _logger.hadWarningOutput;
|
||||
_logger.printWarning(
|
||||
'Waiting for another flutter command to release the startup lock...',
|
||||
color: TerminalColor.grey,
|
||||
);
|
||||
_logger.hadWarningOutput = oldWarnings;
|
||||
printed = true;
|
||||
}
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
@ -509,7 +514,7 @@ class Cache {
|
||||
ErrorHandlingFileSystem.deleteIfExists(file);
|
||||
}
|
||||
} on FileSystemException catch (err) {
|
||||
_logger.printError('Failed to delete some stamp files: $err');
|
||||
_logger.printWarning('Failed to delete some stamp files: $err');
|
||||
}
|
||||
}
|
||||
|
||||
@ -703,7 +708,7 @@ abstract class CachedArtifact extends ArtifactSet {
|
||||
await updateInner(artifactUpdater, fileSystem, operatingSystemUtils);
|
||||
try {
|
||||
if (version == null) {
|
||||
logger.printError(
|
||||
logger.printWarning(
|
||||
'No known version for the artifact name "$name". '
|
||||
'Flutter can continue, but the artifact may be re-downloaded on '
|
||||
'subsequent invocations until the problem is resolved.',
|
||||
@ -712,7 +717,7 @@ abstract class CachedArtifact extends ArtifactSet {
|
||||
cache.setStampFor(stampName, version!);
|
||||
}
|
||||
} on FileSystemException catch (err) {
|
||||
logger.printError(
|
||||
logger.printWarning(
|
||||
'The new artifact "$name" was downloaded, but Flutter failed to update '
|
||||
'its stamp file, receiving the error "$err". '
|
||||
'Flutter can continue, but the artifact may be re-downloaded on '
|
||||
@ -1103,7 +1108,7 @@ class ArtifactUpdater {
|
||||
try {
|
||||
file.deleteSync();
|
||||
} on FileSystemException catch (e) {
|
||||
_logger.printError('Failed to delete "${file.path}". Please delete manually. $e');
|
||||
_logger.printWarning('Failed to delete "${file.path}". Please delete manually. $e');
|
||||
continue;
|
||||
}
|
||||
for (Directory directory = file.parent; directory.absolute.path != _tempStorage.absolute.path; directory = directory.parent) {
|
||||
|
@ -65,8 +65,9 @@ class BuildCommand extends FlutterCommand {
|
||||
}
|
||||
|
||||
abstract class BuildSubCommand extends FlutterCommand {
|
||||
BuildSubCommand() {
|
||||
BuildSubCommand({@required bool verboseHelp}) {
|
||||
requiresPubspecYaml();
|
||||
usesFatalWarningsOption(verboseHelp: verboseHelp);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -20,7 +20,7 @@ import '../runner/flutter_command.dart' show FlutterCommandResult;
|
||||
import 'build.dart';
|
||||
|
||||
class BuildAarCommand extends BuildSubCommand {
|
||||
BuildAarCommand({ @required bool verboseHelp }) {
|
||||
BuildAarCommand({ @required bool verboseHelp }) : super(verboseHelp: verboseHelp) {
|
||||
argParser
|
||||
..addFlag(
|
||||
'debug',
|
||||
|
@ -16,7 +16,7 @@ import '../runner/flutter_command.dart' show FlutterCommandResult;
|
||||
import 'build.dart';
|
||||
|
||||
class BuildApkCommand extends BuildSubCommand {
|
||||
BuildApkCommand({bool verboseHelp = false}) {
|
||||
BuildApkCommand({bool verboseHelp = false}) : super(verboseHelp: verboseHelp) {
|
||||
addTreeShakeIconsFlag();
|
||||
usesTargetOption();
|
||||
addBuildModeFlags(verboseHelp: verboseHelp);
|
||||
|
@ -19,7 +19,9 @@ import '../runner/flutter_command.dart' show FlutterCommandResult;
|
||||
import 'build.dart';
|
||||
|
||||
class BuildAppBundleCommand extends BuildSubCommand {
|
||||
BuildAppBundleCommand({bool verboseHelp = false}) {
|
||||
BuildAppBundleCommand({
|
||||
bool verboseHelp = false,
|
||||
}) : super(verboseHelp: verboseHelp) {
|
||||
addTreeShakeIconsFlag();
|
||||
usesTargetOption();
|
||||
addBuildModeFlags(verboseHelp: verboseHelp);
|
||||
|
@ -16,7 +16,10 @@ import '../runner/flutter_command.dart';
|
||||
import 'build.dart';
|
||||
|
||||
class BuildBundleCommand extends BuildSubCommand {
|
||||
BuildBundleCommand({bool verboseHelp = false, this.bundleBuilder}) {
|
||||
BuildBundleCommand({
|
||||
bool verboseHelp = false,
|
||||
this.bundleBuilder,
|
||||
}) : super(verboseHelp: verboseHelp) {
|
||||
usesTargetOption();
|
||||
usesFilesystemOptions(hide: !verboseHelp);
|
||||
usesBuildNumberOption();
|
||||
|
@ -19,7 +19,9 @@ import 'build.dart';
|
||||
|
||||
/// A command to build a Fuchsia target.
|
||||
class BuildFuchsiaCommand extends BuildSubCommand {
|
||||
BuildFuchsiaCommand({ @required bool verboseHelp }) {
|
||||
BuildFuchsiaCommand({
|
||||
@required bool verboseHelp,
|
||||
}) : super(verboseHelp: verboseHelp) {
|
||||
addTreeShakeIconsFlag();
|
||||
usesTargetOption();
|
||||
usesDartDefineOption();
|
||||
|
@ -129,7 +129,7 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
|
||||
|
||||
// xcarchive failed or not at expected location.
|
||||
if (xcarchiveResult.exitStatus != ExitStatus.success) {
|
||||
globals.logger.printStatus('Skipping IPA');
|
||||
globals.printStatus('Skipping IPA');
|
||||
return xcarchiveResult;
|
||||
}
|
||||
|
||||
@ -176,14 +176,16 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
|
||||
throwToolExit('Encountered error while building IPA:\n$errorMessage');
|
||||
}
|
||||
|
||||
globals.logger.printStatus('Built IPA to $outputPath.');
|
||||
globals.printStatus('Built IPA to $outputPath.');
|
||||
|
||||
return FlutterCommandResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _BuildIOSSubCommand extends BuildSubCommand {
|
||||
_BuildIOSSubCommand({ @required bool verboseHelp }) {
|
||||
_BuildIOSSubCommand({
|
||||
@required bool verboseHelp
|
||||
}) : super(verboseHelp: verboseHelp) {
|
||||
addTreeShakeIconsFlag();
|
||||
addSplitDebugInfoOption();
|
||||
addBuildModeFlags(verboseHelp: verboseHelp);
|
||||
|
@ -39,7 +39,8 @@ class BuildIOSFrameworkCommand extends BuildSubCommand {
|
||||
}) : _flutterVersion = flutterVersion,
|
||||
_buildSystem = buildSystem,
|
||||
_injectedCache = cache,
|
||||
_injectedPlatform = platform {
|
||||
_injectedPlatform = platform,
|
||||
super(verboseHelp: verboseHelp) {
|
||||
addTreeShakeIconsFlag();
|
||||
usesTargetOption();
|
||||
usesFlavorOption();
|
||||
|
@ -23,7 +23,8 @@ class BuildLinuxCommand extends BuildSubCommand {
|
||||
BuildLinuxCommand({
|
||||
@required OperatingSystemUtils operatingSystemUtils,
|
||||
bool verboseHelp = false,
|
||||
}) : _operatingSystemUtils = operatingSystemUtils {
|
||||
}) : _operatingSystemUtils = operatingSystemUtils,
|
||||
super(verboseHelp: verboseHelp) {
|
||||
addCommonDesktopBuildOptions(verboseHelp: verboseHelp);
|
||||
final String defaultTargetPlatform =
|
||||
(_operatingSystemUtils.hostPlatform == HostPlatform.linux_arm64) ?
|
||||
|
@ -19,7 +19,9 @@ import 'build.dart';
|
||||
|
||||
/// A command to build a macOS desktop target through a build shell script.
|
||||
class BuildMacosCommand extends BuildSubCommand {
|
||||
BuildMacosCommand({ @required bool verboseHelp }) {
|
||||
BuildMacosCommand({
|
||||
@required bool verboseHelp,
|
||||
}) : super(verboseHelp: verboseHelp) {
|
||||
addCommonDesktopBuildOptions(verboseHelp: verboseHelp);
|
||||
usesBuildNumberOption();
|
||||
usesBuildNameOption();
|
||||
|
@ -20,7 +20,7 @@ import 'build.dart';
|
||||
class BuildWebCommand extends BuildSubCommand {
|
||||
BuildWebCommand({
|
||||
@required bool verboseHelp,
|
||||
}) {
|
||||
}) : super(verboseHelp: verboseHelp) {
|
||||
addTreeShakeIconsFlag(enabledByDefault: false);
|
||||
usesTargetOption();
|
||||
usesPubOption();
|
||||
|
@ -20,7 +20,9 @@ import 'build.dart';
|
||||
|
||||
/// A command to build a windows desktop target through a build shell script.
|
||||
class BuildWindowsCommand extends BuildSubCommand {
|
||||
BuildWindowsCommand({ bool verboseHelp = false }) {
|
||||
BuildWindowsCommand({
|
||||
bool verboseHelp = false,
|
||||
}) : super(verboseHelp: verboseHelp) {
|
||||
addCommonDesktopBuildOptions(verboseHelp: verboseHelp);
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,9 @@ import 'build.dart';
|
||||
|
||||
/// A command to build a Windows UWP desktop target.
|
||||
class BuildWindowsUwpCommand extends BuildSubCommand {
|
||||
BuildWindowsUwpCommand({ bool verboseHelp = false }) {
|
||||
BuildWindowsUwpCommand({
|
||||
bool verboseHelp = false,
|
||||
}) : super(verboseHelp: verboseHelp) {
|
||||
addCommonDesktopBuildOptions(verboseHelp: verboseHelp);
|
||||
}
|
||||
|
||||
|
@ -228,7 +228,7 @@ class CreateCommand extends CreateBase {
|
||||
validateProjectDir(overwrite: overwrite);
|
||||
|
||||
if (boolArg('with-driver-test')) {
|
||||
globals.printError(
|
||||
globals.printWarning(
|
||||
'The "--with-driver-test" argument has been deprecated and will no longer add a flutter '
|
||||
'driver template. Instead, learn how to use package:integration_test by '
|
||||
'visiting https://pub.dev/packages/integration_test .'
|
||||
|
@ -309,7 +309,7 @@ class DaemonDomain extends Domain {
|
||||
// capture the print output for testing.
|
||||
// ignore: avoid_print
|
||||
print(message.message);
|
||||
} else if (message.level == 'error') {
|
||||
} else if (message.level == 'error' || message.level == 'warning') {
|
||||
globals.stdio.stderrWrite('${message.message}\n');
|
||||
if (message.stackTrace != null) {
|
||||
globals.stdio.stderrWrite(
|
||||
@ -1011,6 +1011,18 @@ class NotifyingLogger extends DelegatingLogger {
|
||||
_sendMessage(LogMessage('error', message, stackTrace));
|
||||
}
|
||||
|
||||
@override
|
||||
void printWarning(
|
||||
String message, {
|
||||
bool emphasis = false,
|
||||
TerminalColor color,
|
||||
int indent,
|
||||
int hangingIndent,
|
||||
bool wrap,
|
||||
}) {
|
||||
_sendMessage(LogMessage('warning', message));
|
||||
}
|
||||
|
||||
@override
|
||||
void printStatus(
|
||||
String message, {
|
||||
|
@ -48,7 +48,7 @@ class DevicesCommand extends FlutterCommand {
|
||||
@override
|
||||
Future<void> validateCommand() {
|
||||
if (argResults?['timeout'] != null) {
|
||||
globals.printError('${globals.logger.terminal.warningMark} The "--timeout" argument is deprecated; use "--${FlutterOptions.kDeviceTimeout}" instead.');
|
||||
globals.printWarning('${globals.logger.terminal.warningMark} The "--timeout" argument is deprecated; use "--${FlutterOptions.kDeviceTimeout}" instead.');
|
||||
}
|
||||
return super.validateCommand();
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ Future<bool> installApp(
|
||||
if (uninstall && await device.isAppInstalled(package, userIdentifier: userIdentifier)) {
|
||||
globals.printStatus('Uninstalling old version...');
|
||||
if (!await device.uninstallApp(package, userIdentifier: userIdentifier)) {
|
||||
globals.printError('Warning: uninstalling old version failed');
|
||||
globals.printWarning('Warning: uninstalling old version failed');
|
||||
}
|
||||
}
|
||||
} on ProcessException catch (e) {
|
||||
|
@ -153,6 +153,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
|
||||
addDdsOptions(verboseHelp: verboseHelp);
|
||||
addDevToolsOptions(verboseHelp: verboseHelp);
|
||||
addAndroidSpecificBuildOptions(hide: !verboseHelp);
|
||||
usesFatalWarningsOption(verboseHelp: verboseHelp);
|
||||
}
|
||||
|
||||
bool get traceStartup => boolArg('trace-startup');
|
||||
|
@ -216,7 +216,8 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
|
||||
'or as the string "none" to disable the timeout entirely.',
|
||||
defaultsTo: '30s',
|
||||
);
|
||||
addDdsOptions(verboseHelp: verboseHelp);
|
||||
addDdsOptions(verboseHelp: verboseHelp);
|
||||
usesFatalWarningsOption(verboseHelp: verboseHelp);
|
||||
}
|
||||
|
||||
/// The interface for starting and configuring the tester.
|
||||
@ -283,7 +284,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
|
||||
// correct [requiredArtifacts] can be identified before [run] takes place.
|
||||
_isIntegrationTest = _shouldRunAsIntegrationTests(globals.fs.currentDirectory.absolute.path, _testFiles);
|
||||
|
||||
globals.logger.printTrace(
|
||||
globals.printTrace(
|
||||
'Found ${_testFiles.length} files which will be executed as '
|
||||
'${_isIntegrationTest ? 'Integration' : 'Widget'} Tests.',
|
||||
);
|
||||
@ -338,7 +339,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
|
||||
}
|
||||
if (_isIntegrationTest) {
|
||||
if (argResults.wasParsed('concurrency')) {
|
||||
globals.logger.printStatus(
|
||||
globals.printStatus(
|
||||
'-j/--concurrency was parsed but will be ignored, this option is not '
|
||||
'supported when running Integration Tests.',
|
||||
);
|
||||
|
@ -190,7 +190,7 @@ class UpdatePackagesCommand extends FlutterCommand {
|
||||
if (pubspec.checksum.value == null) {
|
||||
// If the checksum is invalid or missing, we can just ask them run to run
|
||||
// upgrade again to compute it.
|
||||
globals.printError(
|
||||
globals.printWarning(
|
||||
'Warning: pubspec in ${directory.path} has out of date dependencies. '
|
||||
'Please run "flutter update-packages --force-upgrade" to update them correctly.'
|
||||
);
|
||||
@ -207,7 +207,7 @@ class UpdatePackagesCommand extends FlutterCommand {
|
||||
if (checksum != pubspec.checksum.value) {
|
||||
// If the checksum doesn't match, they may have added or removed some dependencies.
|
||||
// we need to run update-packages to recapture the transitive deps.
|
||||
globals.printError(
|
||||
globals.printWarning(
|
||||
'Warning: pubspec in ${directory.path} has updated or new dependencies. '
|
||||
'Please run "flutter update-packages --force-upgrade" to update them correctly '
|
||||
'(checksum ${pubspec.checksum.value} != $checksum).'
|
||||
@ -1501,7 +1501,7 @@ Directory createTemporaryFlutterSdk(
|
||||
..createSync(recursive: true);
|
||||
final PubspecYaml pubspecYaml = pubspecsByName[flutterPackage];
|
||||
if (pubspecYaml == null) {
|
||||
logger.printError(
|
||||
logger.printWarning(
|
||||
"Unexpected package '$flutterPackage' found in packages directory",
|
||||
);
|
||||
continue;
|
||||
|
@ -339,11 +339,11 @@ class FlutterManifest {
|
||||
final YamlList? fontFiles = fontFamily['fonts'] as YamlList?;
|
||||
final String? familyName = fontFamily['family'] as String?;
|
||||
if (familyName == null) {
|
||||
_logger.printError('Warning: Missing family name for font.', emphasis: true);
|
||||
_logger.printWarning('Warning: Missing family name for font.', emphasis: true);
|
||||
continue;
|
||||
}
|
||||
if (fontFiles == null) {
|
||||
_logger.printError('Warning: No fonts specified for font $familyName', emphasis: true);
|
||||
_logger.printWarning('Warning: No fonts specified for font $familyName', emphasis: true);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -351,7 +351,7 @@ class FlutterManifest {
|
||||
for (final Map<Object?, Object?> fontFile in fontFiles.cast<Map<Object?, Object?>>()) {
|
||||
final String? asset = fontFile['asset'] as String?;
|
||||
if (asset == null) {
|
||||
_logger.printError('Warning: Missing asset in fonts for $familyName', emphasis: true);
|
||||
_logger.printWarning('Warning: Missing asset in fonts for $familyName', emphasis: true);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -393,7 +393,7 @@ Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin>
|
||||
}
|
||||
}
|
||||
if (pluginsUsingV1.length > 1) {
|
||||
globals.printError(
|
||||
globals.printWarning(
|
||||
'The plugins `${pluginsUsingV1.join(', ')}` use a deprecated version of the Android embedding.\n'
|
||||
'To avoid unexpected runtime failures, or future build failures, try to see if these plugins '
|
||||
'support the Android V2 embedding. Otherwise, consider removing them since a future release '
|
||||
@ -402,7 +402,7 @@ Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin>
|
||||
'https://flutter.dev/go/android-plugin-migration.'
|
||||
);
|
||||
} else if (pluginsUsingV1.isNotEmpty) {
|
||||
globals.printError(
|
||||
globals.printWarning(
|
||||
'The plugin `${pluginsUsingV1.first}` uses a deprecated version of the Android embedding.\n'
|
||||
'To avoid unexpected runtime failures, or future build failures, try to see if this plugin '
|
||||
'supports the Android V2 embedding. Otherwise, consider removing it since a future release '
|
||||
@ -414,7 +414,7 @@ Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin>
|
||||
templateContent = _androidPluginRegistryTemplateNewEmbedding;
|
||||
break;
|
||||
case AndroidEmbeddingVersion.v1:
|
||||
globals.printError(
|
||||
globals.printWarning(
|
||||
'This app is using a deprecated version of the Android embedding.\n'
|
||||
'To avoid unexpected runtime failures, or future build failures, try to migrate this '
|
||||
'app to the V2 embedding.\n'
|
||||
@ -1301,7 +1301,7 @@ Future<void> generateMainDartWithPluginRegistrant(
|
||||
newMainDart.deleteSync();
|
||||
}
|
||||
} on FileSystemException catch (error) {
|
||||
globals.printError(
|
||||
globals.printWarning(
|
||||
'Unable to remove ${newMainDart.path}, received error: $error.\n'
|
||||
'You might need to run flutter clean.'
|
||||
);
|
||||
|
@ -124,6 +124,10 @@ Future<void> _buildAssets(
|
||||
assetDirPath: assetDir,
|
||||
);
|
||||
|
||||
if (assets == null) {
|
||||
throwToolExit('Unable to find assets.', exitCode: 1);
|
||||
}
|
||||
|
||||
final Map<String, DevFSContent> assetEntries =
|
||||
Map<String, DevFSContent>.of(assets.entries);
|
||||
await writeBundle(globals.fs.directory(assetDir), assetEntries);
|
||||
|
@ -150,6 +150,30 @@ void printError(
|
||||
);
|
||||
}
|
||||
|
||||
/// Display a warning level message to the user. Commands should use this if they
|
||||
/// have important warnings to convey that aren't fatal.
|
||||
///
|
||||
/// Set [emphasis] to true to make the output bold if it's supported.
|
||||
/// Set [color] to a [TerminalColor] to color the output, if the logger
|
||||
/// supports it. The [color] defaults to [TerminalColor.cyan].
|
||||
void printWarning(
|
||||
String message, {
|
||||
bool? emphasis,
|
||||
TerminalColor? color,
|
||||
int? indent,
|
||||
int? hangingIndent,
|
||||
bool? wrap,
|
||||
}) {
|
||||
logger.printWarning(
|
||||
message,
|
||||
emphasis: emphasis ?? false,
|
||||
color: color,
|
||||
indent: indent,
|
||||
hangingIndent: hangingIndent,
|
||||
wrap: wrap,
|
||||
);
|
||||
}
|
||||
|
||||
/// Display normal output of the command. This should be used for things like
|
||||
/// progress messages, success messages, or just normal command output.
|
||||
///
|
||||
|
@ -176,7 +176,7 @@ class CocoaPods {
|
||||
final CocoaPodsStatus installation = await evaluateCocoaPodsInstallation;
|
||||
switch (installation) {
|
||||
case CocoaPodsStatus.notInstalled:
|
||||
_logger.printError(
|
||||
_logger.printWarning(
|
||||
'Warning: CocoaPods not installed. Skipping pod install.\n'
|
||||
'$noCocoaPodsConsequence\n'
|
||||
'To install $cocoaPodsInstallInstructions\n',
|
||||
@ -184,7 +184,7 @@ class CocoaPods {
|
||||
);
|
||||
return false;
|
||||
case CocoaPodsStatus.brokenInstall:
|
||||
_logger.printError(
|
||||
_logger.printWarning(
|
||||
'Warning: CocoaPods is installed but broken. Skipping pod install.\n'
|
||||
'$brokenCocoaPodsConsequence\n'
|
||||
'To re-install $cocoaPodsInstallInstructions\n',
|
||||
@ -192,7 +192,7 @@ class CocoaPods {
|
||||
);
|
||||
return false;
|
||||
case CocoaPodsStatus.unknownVersion:
|
||||
_logger.printError(
|
||||
_logger.printWarning(
|
||||
'Warning: Unknown CocoaPods version installed.\n'
|
||||
'$unknownCocoaPodsConsequence\n'
|
||||
'To upgrade $cocoaPodsInstallInstructions\n',
|
||||
@ -200,7 +200,7 @@ class CocoaPods {
|
||||
);
|
||||
break;
|
||||
case CocoaPodsStatus.belowMinimumVersion:
|
||||
_logger.printError(
|
||||
_logger.printWarning(
|
||||
'Warning: CocoaPods minimum required version $cocoaPodsMinimumVersion or greater not installed. Skipping pod install.\n'
|
||||
'$noCocoaPodsConsequence\n'
|
||||
'To upgrade $cocoaPodsInstallInstructions\n',
|
||||
@ -208,7 +208,7 @@ class CocoaPods {
|
||||
);
|
||||
return false;
|
||||
case CocoaPodsStatus.belowRecommendedVersion:
|
||||
_logger.printError(
|
||||
_logger.printWarning(
|
||||
'Warning: CocoaPods recommended version $cocoaPodsRecommendedVersion or greater not installed.\n'
|
||||
'Pods handling may fail on some projects involving plugins.\n'
|
||||
'To upgrade $cocoaPodsInstallInstructions\n',
|
||||
@ -406,15 +406,15 @@ class CocoaPods {
|
||||
// plugin_pods = parse_KV_file('../.flutter-plugins')
|
||||
if (xcodeProject.podfile.existsSync() &&
|
||||
xcodeProject.podfile.readAsStringSync().contains(".flutter-plugins'")) {
|
||||
const String error = 'Warning: Podfile is out of date\n'
|
||||
const String warning = 'Warning: Podfile is out of date\n'
|
||||
'$outOfDatePluginsPodfileConsequence\n'
|
||||
'To regenerate the Podfile, run:\n';
|
||||
if (isIos) {
|
||||
throwToolExit('$error\n$podfileIosMigrationInstructions\n');
|
||||
throwToolExit('$warning\n$podfileIosMigrationInstructions\n');
|
||||
} else {
|
||||
// The old macOS Podfile will work until `.flutter-plugins` is removed.
|
||||
// Warn instead of exit.
|
||||
_logger.printError('$error\n$podfileMacOSMigrationInstructions\n', emphasis: true);
|
||||
_logger.printWarning('$warning\n$podfileMacOSMigrationInstructions\n', emphasis: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -415,7 +415,7 @@ class XCDevice {
|
||||
} else {
|
||||
cpuArchitecture = DarwinArch.arm64;
|
||||
}
|
||||
_logger.printError(
|
||||
_logger.printWarning(
|
||||
'Unknown architecture $architecture, defaulting to '
|
||||
'${getNameForDarwinArch(cpuArchitecture)}',
|
||||
);
|
||||
|
@ -110,7 +110,7 @@ class MDnsObservatoryDiscovery {
|
||||
return null;
|
||||
}
|
||||
if (srv.length > 1) {
|
||||
_logger.printError('Unexpectedly found more than one observatory report for $domainName '
|
||||
_logger.printWarning('Unexpectedly found more than one observatory report for $domainName '
|
||||
'- using first one (${srv.first.port}).');
|
||||
}
|
||||
_logger.printTrace('Checking for authentication code for $domainName');
|
||||
|
@ -479,23 +479,6 @@ class AndroidProject extends FlutterProjectPlatform {
|
||||
}
|
||||
|
||||
Future<void> ensureReadyForPlatformSpecificTooling() async {
|
||||
if (getEmbeddingVersion() == AndroidEmbeddingVersion.v1) {
|
||||
globals.printStatus(
|
||||
"""
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Warning
|
||||
──────────────────────────────────────────────────────────────────────────────
|
||||
Your Flutter application is created using an older version of the Android
|
||||
embedding. It's being deprecated in favor of Android embedding v2. Follow the
|
||||
steps at
|
||||
|
||||
https://flutter.dev/go/android-project-migration
|
||||
|
||||
to migrate your project.
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
"""
|
||||
);
|
||||
}
|
||||
if (isModule && _shouldRegenerateFromTemplate()) {
|
||||
await _regenerateLibrary();
|
||||
// Add ephemeral host app, if an editable host app does not already exist.
|
||||
|
@ -1178,7 +1178,7 @@ abstract class ResidentRunner extends ResidentHandlers {
|
||||
);
|
||||
if (!_lastBuild.success) {
|
||||
for (final ExceptionMeasurement exceptionMeasurement in _lastBuild.exceptions.values) {
|
||||
globals.logger.printError(
|
||||
globals.printError(
|
||||
exceptionMeasurement.exception.toString(),
|
||||
stackTrace: globals.logger.isVerbose
|
||||
? exceptionMeasurement.stackTrace
|
||||
@ -1186,7 +1186,7 @@ abstract class ResidentRunner extends ResidentHandlers {
|
||||
);
|
||||
}
|
||||
}
|
||||
globals.logger.printTrace('complete');
|
||||
globals.printTrace('complete');
|
||||
}
|
||||
|
||||
@protected
|
||||
@ -1241,7 +1241,7 @@ abstract class ResidentRunner extends ResidentHandlers {
|
||||
if (_dillOutputPath != null) {
|
||||
return;
|
||||
}
|
||||
globals.logger.printTrace('Caching compiled dill');
|
||||
globals.printTrace('Caching compiled dill');
|
||||
final File outputDill = globals.fs.file(dillOutputPath);
|
||||
if (outputDill.existsSync()) {
|
||||
final String copyPath = getDefaultCachedKernelPath(
|
||||
@ -1561,7 +1561,7 @@ class TerminalHandler {
|
||||
_logger.printTrace('Deleting pid file (${_actualPidFile.path}).');
|
||||
_actualPidFile.deleteSync();
|
||||
} on FileSystemException catch (error) {
|
||||
_logger.printError('Failed to delete pid file (${_actualPidFile.path}): ${error.message}');
|
||||
_logger.printWarning('Failed to delete pid file (${_actualPidFile.path}): ${error.message}');
|
||||
}
|
||||
_actualPidFile = null;
|
||||
}
|
||||
|
@ -77,8 +77,8 @@ class ColdRunner extends ResidentRunner {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} on Exception catch (err) {
|
||||
globals.printError(err.toString());
|
||||
} on Exception catch (err, stack) {
|
||||
globals.printError('$err\n$stack');
|
||||
appFailedToStart();
|
||||
return 1;
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ class FlutterOptions {
|
||||
static const String kDeferredComponents = 'deferred-components';
|
||||
static const String kAndroidProjectArgs = 'android-project-arg';
|
||||
static const String kInitializeFromDill = 'initialize-from-dill';
|
||||
static const String kFatalWarnings = 'fatal-warnings';
|
||||
}
|
||||
|
||||
/// flutter command categories for usage.
|
||||
@ -178,6 +179,8 @@ abstract class FlutterCommand extends Command<void> {
|
||||
|
||||
bool _usesIpv6Flag = false;
|
||||
|
||||
bool _usesFatalWarnings = false;
|
||||
|
||||
bool get shouldRunPub => _usesPubOption && boolArg('pub');
|
||||
|
||||
bool get shouldUpdateCache => true;
|
||||
@ -271,6 +274,15 @@ abstract class FlutterCommand extends Command<void> {
|
||||
_usesTargetOption = true;
|
||||
}
|
||||
|
||||
void usesFatalWarningsOption({ required bool verboseHelp }) {
|
||||
argParser.addFlag(FlutterOptions.kFatalWarnings,
|
||||
hide: !verboseHelp,
|
||||
help: 'Causes the command to fail if warnings are sent to the console '
|
||||
'during its execution.'
|
||||
);
|
||||
_usesFatalWarnings = true;
|
||||
}
|
||||
|
||||
String get targetFile {
|
||||
if (argResults?.wasParsed('target') == true) {
|
||||
return stringArg('target')!;
|
||||
@ -413,10 +425,10 @@ abstract class FlutterCommand extends Command<void> {
|
||||
// TODO(ianh): enable the following code once google3 is migrated away from --disable-dds (and add test to flutter_command_test.dart)
|
||||
if (false) { // ignore: dead_code
|
||||
if (ddsEnabled) {
|
||||
globals.printError('${globals.logger.terminal
|
||||
globals.printWarning('${globals.logger.terminal
|
||||
.warningMark} The "--no-disable-dds" argument is deprecated and redundant, and should be omitted.');
|
||||
} else {
|
||||
globals.printError('${globals.logger.terminal
|
||||
globals.printWarning('${globals.logger.terminal
|
||||
.warningMark} The "--disable-dds" argument is deprecated. Use "--no-dds" instead.');
|
||||
}
|
||||
}
|
||||
@ -1123,6 +1135,9 @@ abstract class FlutterCommand extends Command<void> {
|
||||
name: 'command',
|
||||
overrides: <Type, Generator>{FlutterCommand: () => this},
|
||||
body: () async {
|
||||
if (_usesFatalWarnings) {
|
||||
globals.logger.fatalWarnings = boolArg(FlutterOptions.kFatalWarnings);
|
||||
}
|
||||
// Prints the welcome message if needed.
|
||||
globals.flutterUsage.printWelcome();
|
||||
_printDeprecationWarning();
|
||||
@ -1139,6 +1154,9 @@ abstract class FlutterCommand extends Command<void> {
|
||||
if (commandPath != null) {
|
||||
_sendPostUsage(commandPath, commandResult, startTime, endTime);
|
||||
}
|
||||
if (_usesFatalWarnings) {
|
||||
globals.logger.checkForFatalLogs();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
@ -1146,13 +1164,12 @@ abstract class FlutterCommand extends Command<void> {
|
||||
|
||||
void _printDeprecationWarning() {
|
||||
if (deprecated) {
|
||||
globals.printError(
|
||||
globals.printWarning(
|
||||
'${globals.logger.terminal.warningMark} The "$name" command is deprecated and '
|
||||
'will be removed in a future version of Flutter. '
|
||||
'See https://flutter.dev/docs/development/tools/sdk/releases '
|
||||
'for previous releases of Flutter.',
|
||||
'for previous releases of Flutter.\n',
|
||||
);
|
||||
globals.printError('');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,10 +277,10 @@ class FlutterVersion {
|
||||
);
|
||||
} on VersionCheckError catch (error) {
|
||||
if (globals.platform.environment.containsKey('FLUTTER_GIT_URL')) {
|
||||
globals.logger.printError('Warning: the Flutter git upstream was overridden '
|
||||
globals.printWarning('Warning: the Flutter git upstream was overridden '
|
||||
'by the environment variable FLUTTER_GIT_URL = ${globals.flutterGit}');
|
||||
}
|
||||
globals.logger.printError(error.toString());
|
||||
globals.printError(error.toString());
|
||||
rethrow;
|
||||
} finally {
|
||||
await _removeVersionCheckRemoteIfExists();
|
||||
|
@ -568,6 +568,20 @@ class StreamLogger extends Logger {
|
||||
int hangingIndent,
|
||||
bool wrap,
|
||||
}) {
|
||||
hadErrorOutput = true;
|
||||
_log('[stderr] $message');
|
||||
}
|
||||
|
||||
@override
|
||||
void printWarning(
|
||||
String message, {
|
||||
bool emphasis,
|
||||
TerminalColor color,
|
||||
int indent,
|
||||
int hangingIndent,
|
||||
bool wrap,
|
||||
}) {
|
||||
hadWarningOutput = true;
|
||||
_log('[stderr] $message');
|
||||
}
|
||||
|
||||
|
@ -227,6 +227,8 @@ void main() {
|
||||
const <String>['build', 'linux', '--debug', '--no-pub']
|
||||
);
|
||||
expect(testLogger.statusText, isNot(contains('STDOUT STUFF')));
|
||||
expect(testLogger.warningText, isNot(contains('STDOUT STUFF')));
|
||||
expect(testLogger.errorText, isNot(contains('STDOUT STUFF')));
|
||||
expect(testLogger.traceText, contains('STDOUT STUFF'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
@ -308,6 +310,8 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg
|
||||
);
|
||||
expect(testLogger.statusText, contains('STDOUT STUFF'));
|
||||
expect(testLogger.traceText, isNot(contains('STDOUT STUFF')));
|
||||
expect(testLogger.warningText, isNot(contains('STDOUT STUFF')));
|
||||
expect(testLogger.errorText, isNot(contains('STDOUT STUFF')));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
|
@ -3,9 +3,13 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/build.dart';
|
||||
import 'package:flutter_tools/src/runner/flutter_command.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
@ -13,27 +17,97 @@ import '../../src/test_flutter_command_runner.dart';
|
||||
|
||||
void main() {
|
||||
testUsingContext('obfuscate requires split-debug-info', () {
|
||||
final FakeBuildCommand command = FakeBuildCommand();
|
||||
final FakeBuildInfoCommand command = FakeBuildInfoCommand();
|
||||
final CommandRunner<void> commandRunner = createTestCommandRunner(command);
|
||||
|
||||
expect(() => commandRunner.run(<String>[
|
||||
'build',
|
||||
'fake',
|
||||
'--obfuscate',
|
||||
]), throwsToolExit());
|
||||
]), throwsToolExit(message: '"--${FlutterOptions.kDartObfuscationOption}" can only be used in '
|
||||
'combination with "--${FlutterOptions.kSplitDebugInfoOption}"'));
|
||||
});
|
||||
group('Fatal Logs', () {
|
||||
FakeBuildCommand command;
|
||||
MemoryFileSystem fs;
|
||||
|
||||
setUp(() {
|
||||
fs = MemoryFileSystem.test();
|
||||
fs.file('/package/pubspec.yaml').createSync(recursive: true);
|
||||
fs.currentDirectory = '/package';
|
||||
Cache.disableLocking();
|
||||
});
|
||||
|
||||
testUsingContext("doesn't fail if --fatal-warnings specified and no warnings occur", () async {
|
||||
command = FakeBuildCommand();
|
||||
try {
|
||||
await createTestCommandRunner(command).run(<String>[
|
||||
'build',
|
||||
'test',
|
||||
'--${FlutterOptions.kFatalWarnings}',
|
||||
]);
|
||||
} on Exception {
|
||||
fail('Unexpected exception thrown');
|
||||
}
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext("doesn't fail if --fatal-warnings not specified", () async {
|
||||
command = FakeBuildCommand();
|
||||
testLogger.printWarning('Warning: Mild annoyance Will Robinson!');
|
||||
try {
|
||||
await createTestCommandRunner(command).run(<String>[
|
||||
'build',
|
||||
'test',
|
||||
]);
|
||||
} on Exception {
|
||||
fail('Unexpected exception thrown');
|
||||
}
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('fails if --fatal-warnings specified and warnings emitted', () async {
|
||||
command = FakeBuildCommand();
|
||||
testLogger.printWarning('Warning: Mild annoyance Will Robinson!');
|
||||
await expectLater(createTestCommandRunner(command).run(<String>[
|
||||
'build',
|
||||
'test',
|
||||
'--${FlutterOptions.kFatalWarnings}',
|
||||
]), throwsToolExit(message: 'Logger received warning output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('fails if --fatal-warnings specified and errors emitted', () async {
|
||||
command = FakeBuildCommand();
|
||||
testLogger.printError('Error: Danger Will Robinson!');
|
||||
await expectLater(createTestCommandRunner(command).run(<String>[
|
||||
'build',
|
||||
'test',
|
||||
'--${FlutterOptions.kFatalWarnings}',
|
||||
]), throwsToolExit(message: 'Logger received error output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class FakeBuildCommand extends FlutterCommand {
|
||||
FakeBuildCommand() {
|
||||
class FakeBuildInfoCommand extends FlutterCommand {
|
||||
FakeBuildInfoCommand() : super() {
|
||||
addSplitDebugInfoOption();
|
||||
addDartObfuscationOption();
|
||||
}
|
||||
|
||||
@override
|
||||
String get description => throw UnimplementedError();
|
||||
String get description => '';
|
||||
|
||||
@override
|
||||
String get name => 'build';
|
||||
String get name => 'fake';
|
||||
|
||||
@override
|
||||
Future<FlutterCommandResult> runCommand() async {
|
||||
@ -41,3 +115,35 @@ class FakeBuildCommand extends FlutterCommand {
|
||||
return FlutterCommandResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
class FakeBuildCommand extends BuildCommand {
|
||||
FakeBuildCommand({bool verboseHelp = false}) : super(verboseHelp: verboseHelp) {
|
||||
addSubcommand(FakeBuildSubcommand(verboseHelp: verboseHelp));
|
||||
}
|
||||
|
||||
@override
|
||||
String get description => '';
|
||||
|
||||
@override
|
||||
String get name => 'build';
|
||||
|
||||
@override
|
||||
Future<FlutterCommandResult> runCommand() async {
|
||||
return FlutterCommandResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
class FakeBuildSubcommand extends BuildSubCommand {
|
||||
FakeBuildSubcommand({@required bool verboseHelp}) : super(verboseHelp: verboseHelp);
|
||||
|
||||
@override
|
||||
String get description => '';
|
||||
|
||||
@override
|
||||
String get name => 'test';
|
||||
|
||||
@override
|
||||
Future<FlutterCommandResult> runCommand() async {
|
||||
return FlutterCommandResult.success();
|
||||
}
|
||||
}
|
||||
|
@ -130,6 +130,29 @@ void main() {
|
||||
Logger: () => notifyingLogger,
|
||||
});
|
||||
|
||||
testUsingContext('printWarning should send daemon.logMessage event', () async {
|
||||
final StreamController<Map<String, dynamic>> commands = StreamController<Map<String, dynamic>>();
|
||||
final StreamController<Map<String, dynamic>> responses = StreamController<Map<String, dynamic>>();
|
||||
daemon = Daemon(
|
||||
commands.stream,
|
||||
responses.add,
|
||||
notifyingLogger: notifyingLogger,
|
||||
);
|
||||
globals.printWarning('daemon.logMessage test');
|
||||
final Map<String, dynamic> response = await responses.stream.firstWhere((Map<String, dynamic> map) {
|
||||
return map['event'] == 'daemon.logMessage' && (map['params'] as Map<String, dynamic>)['level'] == 'warning';
|
||||
});
|
||||
expect(response['id'], isNull);
|
||||
expect(response['event'], 'daemon.logMessage');
|
||||
final Map<String, String> logMessage = castStringKeyedMap(response['params']).cast<String, String>();
|
||||
expect(logMessage['level'], 'warning');
|
||||
expect(logMessage['message'], 'daemon.logMessage test');
|
||||
await responses.close();
|
||||
await commands.close();
|
||||
}, overrides: <Type, Generator>{
|
||||
Logger: () => notifyingLogger,
|
||||
});
|
||||
|
||||
testUsingContext('printStatus should log to stdout when logToStdout is enabled', () async {
|
||||
final StringBuffer buffer = await capturedConsolePrint(() {
|
||||
final StreamController<Map<String, dynamic>> commands = StreamController<Map<String, dynamic>>();
|
||||
|
@ -294,6 +294,75 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('Fatal Logs', () {
|
||||
TestRunCommandWithFakeResidentRunner command;
|
||||
MemoryFileSystem fs;
|
||||
|
||||
setUp(() {
|
||||
command = TestRunCommandWithFakeResidentRunner()
|
||||
..fakeResidentRunner = FakeResidentRunner();
|
||||
fs = MemoryFileSystem.test();
|
||||
});
|
||||
|
||||
testUsingContext("doesn't fail if --fatal-warnings specified and no warnings occur", () async {
|
||||
try {
|
||||
await createTestCommandRunner(command).run(<String>[
|
||||
'run',
|
||||
'--no-pub',
|
||||
'--no-hot',
|
||||
'--${FlutterOptions.kFatalWarnings}',
|
||||
]);
|
||||
} on Exception {
|
||||
fail('Unexpected exception thrown');
|
||||
}
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext("doesn't fail if --fatal-warnings not specified", () async {
|
||||
testLogger.printWarning('Warning: Mild annoyance Will Robinson!');
|
||||
try {
|
||||
await createTestCommandRunner(command).run(<String>[
|
||||
'run',
|
||||
'--no-pub',
|
||||
'--no-hot',
|
||||
]);
|
||||
} on Exception {
|
||||
fail('Unexpected exception thrown');
|
||||
}
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('fails if --fatal-warnings specified and warnings emitted', () async {
|
||||
testLogger.printWarning('Warning: Mild annoyance Will Robinson!');
|
||||
await expectLater(createTestCommandRunner(command).run(<String>[
|
||||
'run',
|
||||
'--no-pub',
|
||||
'--no-hot',
|
||||
'--${FlutterOptions.kFatalWarnings}',
|
||||
]), throwsToolExit(message: 'Logger received warning output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('fails if --fatal-warnings specified and errors emitted', () async {
|
||||
testLogger.printError('Error: Danger Will Robinson!');
|
||||
await expectLater(createTestCommandRunner(command).run(<String>[
|
||||
'run',
|
||||
'--no-pub',
|
||||
'--no-hot',
|
||||
'--${FlutterOptions.kFatalWarnings}',
|
||||
]), throwsToolExit(message: 'Logger received error output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
});
|
||||
|
||||
testUsingContext('should only request artifacts corresponding to connected devices', () async {
|
||||
mockDeviceManager.devices = <Device>[FakeDevice(targetPlatform: TargetPlatform.android_arm)];
|
||||
|
||||
@ -496,7 +565,7 @@ class FakeDevice extends Fake implements Device {
|
||||
@override
|
||||
String get id => 'fake_device';
|
||||
|
||||
void _throwToolExit(int code) => throwToolExit(null, exitCode: code);
|
||||
void _throwToolExit(int code) => throwToolExit('FakeDevice tool exit', exitCode: code);
|
||||
|
||||
@override
|
||||
Future<bool> get isLocalEmulator => Future<bool>.value(_isLocalEmulator);
|
||||
@ -504,6 +573,9 @@ class FakeDevice extends Fake implements Device {
|
||||
@override
|
||||
bool supportsRuntimeMode(BuildMode mode) => true;
|
||||
|
||||
@override
|
||||
Future<bool> get supportsHardwareRendering async => true;
|
||||
|
||||
@override
|
||||
bool supportsHotReload = false;
|
||||
|
||||
@ -542,7 +614,7 @@ class FakeDevice extends Fake implements Device {
|
||||
@override
|
||||
final PlatformType platformType = PlatformType.ios;
|
||||
|
||||
bool startAppSuccess = true;
|
||||
bool startAppSuccess;
|
||||
|
||||
@override
|
||||
DevFSWriter createDevFSWriter(
|
||||
@ -564,9 +636,12 @@ class FakeDevice extends Fake implements Device {
|
||||
bool ipv6 = false,
|
||||
String userIdentifier,
|
||||
}) async {
|
||||
if (!startAppSuccess) {
|
||||
if (startAppSuccess == false) {
|
||||
return LaunchResult.failed();
|
||||
}
|
||||
if (startAppSuccess == true) {
|
||||
return LaunchResult.succeeded();
|
||||
}
|
||||
final String dartFlags = debuggingOptions.dartFlags;
|
||||
// In release mode, --dart-flags should be set to the empty string and
|
||||
// provided flags should be dropped. In debug and profile modes,
|
||||
@ -587,18 +662,20 @@ class FakeDevice extends Fake implements Device {
|
||||
}
|
||||
|
||||
class FakeApplicationPackageFactory extends Fake implements ApplicationPackageFactory {
|
||||
ApplicationPackage package;
|
||||
FakeApplicationPackageFactory(this.applicationPackage);
|
||||
|
||||
ApplicationPackage applicationPackage;
|
||||
|
||||
@override
|
||||
Future<ApplicationPackage> getPackageForPlatform(
|
||||
TargetPlatform platform, {
|
||||
BuildInfo buildInfo,
|
||||
File applicationBinary,
|
||||
}) async {
|
||||
return package;
|
||||
}
|
||||
TargetPlatform platform, {
|
||||
BuildInfo buildInfo,
|
||||
File applicationBinary,
|
||||
}) async => applicationPackage;
|
||||
}
|
||||
|
||||
class FakeApplicationPackage extends Fake implements ApplicationPackage { }
|
||||
|
||||
class TestRunCommandWithFakeResidentRunner extends RunCommand {
|
||||
FakeResidentRunner fakeResidentRunner;
|
||||
|
||||
|
@ -16,6 +16,7 @@ import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/test.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/runner/flutter_command.dart';
|
||||
import 'package:flutter_tools/src/test/runner.dart';
|
||||
import 'package:flutter_tools/src/test/test_wrapper.dart';
|
||||
import 'package:flutter_tools/src/test/watcher.dart';
|
||||
@ -644,6 +645,60 @@ dev_dependencies:
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
DeviceManager: () => _FakeDeviceManager(<Device>[]),
|
||||
});
|
||||
|
||||
group('Fatal Logs', () {
|
||||
testUsingContext("doesn't fail when --fatal-warnings is set and no warning output", () async {
|
||||
final FakeFlutterTestRunner testRunner = FakeFlutterTestRunner(0);
|
||||
|
||||
final TestCommand testCommand = TestCommand(testRunner: testRunner);
|
||||
final CommandRunner<void> commandRunner = createTestCommandRunner(testCommand);
|
||||
|
||||
try {
|
||||
await commandRunner.run(const <String>[
|
||||
'test',
|
||||
'--no-pub',
|
||||
'--${FlutterOptions.kFatalWarnings}',
|
||||
]);
|
||||
} on Exception {
|
||||
fail('Unexpected exception thrown');
|
||||
}
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
testUsingContext('fails if --fatal-warnings specified and warnings emitted', () async {
|
||||
final FakeFlutterTestRunner testRunner = FakeFlutterTestRunner(0);
|
||||
|
||||
final TestCommand testCommand = TestCommand(testRunner: testRunner);
|
||||
final CommandRunner<void> commandRunner = createTestCommandRunner(testCommand);
|
||||
|
||||
testLogger.printWarning('Warning: Mild annoyance, Will Robinson!');
|
||||
expect(commandRunner.run(const <String>[
|
||||
'test',
|
||||
'--no-pub',
|
||||
'--${FlutterOptions.kFatalWarnings}',
|
||||
]), throwsToolExit(message: 'Logger received warning output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
testUsingContext('fails when --fatal-warnings is set and only errors emitted', () async {
|
||||
final FakeFlutterTestRunner testRunner = FakeFlutterTestRunner(0);
|
||||
|
||||
final TestCommand testCommand = TestCommand(testRunner: testRunner);
|
||||
final CommandRunner<void> commandRunner = createTestCommandRunner(testCommand);
|
||||
|
||||
testLogger.printError('Error: Danger Will Robinson!');
|
||||
expect(commandRunner.run(const <String>[
|
||||
'test',
|
||||
'--no-pub',
|
||||
'--${FlutterOptions.kFatalWarnings}',
|
||||
]), throwsToolExit(message: 'Logger received error output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class FakeFlutterTestRunner implements FlutterTestRunner {
|
||||
|
@ -311,7 +311,7 @@ flutter:
|
||||
|
||||
await writeBundle(directory, <String, DevFSContent>{}, loggerOverride: testLogger);
|
||||
|
||||
expect(testLogger.errorText, contains('Expected Error Text'));
|
||||
expect(testLogger.warningText, contains('Expected Error Text'));
|
||||
});
|
||||
|
||||
testUsingContext('does not unnecessarily recreate asset manifest, font manifest, license', () async {
|
||||
|
@ -137,7 +137,7 @@ void main() {
|
||||
final FakeSimpleArtifact artifact = FakeSimpleArtifact(cache);
|
||||
await artifact.update(FakeArtifactUpdater(), logger, fileSystem, FakeOperatingSystemUtils());
|
||||
|
||||
expect(logger.errorText, contains('stamp write failed'));
|
||||
expect(logger.warningText, contains('stamp write failed'));
|
||||
});
|
||||
|
||||
testWithoutContext('Continues on missing version file', () async {
|
||||
@ -153,7 +153,7 @@ void main() {
|
||||
final FakeSimpleArtifact artifact = FakeSimpleArtifact(cache);
|
||||
await artifact.update(FakeArtifactUpdater(), logger, fileSystem, FakeOperatingSystemUtils());
|
||||
|
||||
expect(logger.errorText, contains('No known version for the artifact name "fake"'));
|
||||
expect(logger.warningText, contains('No known version for the artifact name "fake"'));
|
||||
});
|
||||
|
||||
testWithoutContext('Gradle wrapper should not be up to date, if some cached artifact is not available', () {
|
||||
@ -725,7 +725,7 @@ void main() {
|
||||
|
||||
cache.clearStampFiles();
|
||||
|
||||
expect(logger.errorText, contains('Failed to delete some stamp files'));
|
||||
expect(logger.warningText, contains('Failed to delete some stamp files'));
|
||||
});
|
||||
|
||||
testWithoutContext('FlutterWebSdk fetches web artifacts and deletes previous directory contents', () async {
|
||||
|
@ -83,6 +83,8 @@ void main() {
|
||||
}
|
||||
|
||||
class FakeBuildSubCommand extends BuildSubCommand {
|
||||
FakeBuildSubCommand() : super(verboseHelp: false);
|
||||
|
||||
@override
|
||||
String get description => throw UnimplementedError();
|
||||
|
||||
|
@ -432,8 +432,8 @@ void main() {
|
||||
buildMode: BuildMode.debug,
|
||||
);
|
||||
|
||||
expect(logger.errorText, contains('Warning: Podfile is out of date'));
|
||||
expect(logger.errorText, contains('rm macos/Podfile'));
|
||||
expect(logger.warningText, contains('Warning: Podfile is out of date'));
|
||||
expect(logger.warningText, contains('rm macos/Podfile'));
|
||||
expect(fakeProcessManager, hasNoRemainingExpectations);
|
||||
});
|
||||
|
||||
|
@ -747,7 +747,7 @@ dependencies:
|
||||
.childFile('GeneratedPluginRegistrant.java');
|
||||
expect(registrant.readAsStringSync(),
|
||||
contains('plugin3.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin3.UseOldEmbedding"));'));
|
||||
expect(testLogger.errorText, equals(
|
||||
expect(testLogger.warningText, equals(
|
||||
'The plugin `plugin3` uses a deprecated version of the Android embedding.\n'
|
||||
'To avoid unexpected runtime failures, or future build failures, try to see if this plugin supports the Android V2 embedding. '
|
||||
'Otherwise, consider removing it since a future release of Flutter will remove these deprecated APIs.\n'
|
||||
@ -827,7 +827,7 @@ dependencies:
|
||||
|
||||
await injectPlugins(flutterProject, androidPlatform: true);
|
||||
|
||||
expect(testLogger.errorText, equals(
|
||||
expect(testLogger.warningText, equals(
|
||||
'This app is using a deprecated version of the Android embedding.\n'
|
||||
'To avoid unexpected runtime failures, or future build failures, try to migrate this app to the V2 embedding.\n'
|
||||
'Take a look at the docs for migrating an app: https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects\n'
|
||||
@ -854,7 +854,7 @@ dependencies:
|
||||
contains('plugin3.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin3.UseOldEmbedding"));'));
|
||||
expect(registrant.readAsStringSync(),
|
||||
contains('plugin4.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin4.UseOldEmbedding"));'));
|
||||
expect(testLogger.errorText, equals(
|
||||
expect(testLogger.warningText, equals(
|
||||
'The plugins `plugin3, plugin4` use a deprecated version of the Android embedding.\n'
|
||||
'To avoid unexpected runtime failures, or future build failures, try to see if these plugins support the Android V2 embedding. '
|
||||
'Otherwise, consider removing them since a future release of Flutter will remove these deprecated APIs.\n'
|
||||
@ -882,7 +882,7 @@ dependencies:
|
||||
contains('plugin3.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin3.UseOldEmbedding"));'));
|
||||
expect(registrant.readAsStringSync(),
|
||||
contains('plugin4.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin4.UseOldEmbedding"));'));
|
||||
expect(testLogger.errorText, equals(
|
||||
expect(testLogger.warningText, equals(
|
||||
'The plugins `plugin3, plugin4` use a deprecated version of the Android embedding.\n'
|
||||
'To avoid unexpected runtime failures, or future build failures, try to see if these plugins support the Android V2 embedding. '
|
||||
'Otherwise, consider removing them since a future release of Flutter will remove these deprecated APIs.\n'
|
||||
|
@ -183,7 +183,7 @@ void main() {
|
||||
// android:name="flutterEmbedding" android:value="2" />.
|
||||
|
||||
await project.regeneratePlatformSpecificTooling();
|
||||
expect(testLogger.statusText, contains('https://flutter.dev/go/android-project-migration'));
|
||||
expect(testLogger.warningText, contains('https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects'));
|
||||
});
|
||||
_testInMemory('Android plugin without example app does not show a warning', () async {
|
||||
final FlutterProject project = await aPluginProject();
|
||||
|
@ -87,7 +87,7 @@ void main() {
|
||||
final CommandRunner<void> runner = createTestCommandRunner(flutterCommand);
|
||||
await runner.run(<String>['deprecated']);
|
||||
|
||||
expect(testLogger.errorText,
|
||||
expect(testLogger.warningText,
|
||||
contains('The "deprecated" command is deprecated and will be removed in '
|
||||
'a future version of Flutter.'));
|
||||
expect(flutterCommand.usage,
|
||||
|
@ -129,7 +129,7 @@ void main() {
|
||||
|
||||
// We get a warning about the unexpected package.
|
||||
expect(
|
||||
bufferLogger.errorText,
|
||||
bufferLogger.warningText,
|
||||
contains("Unexpected package 'extra' found in packages directory"),
|
||||
);
|
||||
|
||||
|
@ -10,12 +10,15 @@ import 'package:file/file.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/os.dart';
|
||||
import 'package:flutter_tools/src/base/terminal.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:process/process.dart';
|
||||
import 'package:test/fake.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
import '../src/context.dart';
|
||||
import '../src/fakes.dart';
|
||||
import 'test_utils.dart';
|
||||
|
||||
final String dart = fileSystem.path
|
||||
@ -24,53 +27,122 @@ final String dart = fileSystem.path
|
||||
void main() {
|
||||
group('Cache.lock', () {
|
||||
// Windows locking is too flaky for this to work reliably.
|
||||
if (!platform.isWindows) {
|
||||
testWithoutContext(
|
||||
'should log a message to stderr when lock is not acquired', () async {
|
||||
final String oldRoot = Cache.flutterRoot;
|
||||
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('cache_test.');
|
||||
final BufferLogger logger = BufferLogger(
|
||||
terminal: Terminal.test(supportsColor: false, supportsEmoji: false),
|
||||
outputPreferences: OutputPreferences(),
|
||||
if (platform.isWindows) {
|
||||
return;
|
||||
}
|
||||
testWithoutContext(
|
||||
'should log a message to stderr when lock is not acquired', () async {
|
||||
final String oldRoot = Cache.flutterRoot;
|
||||
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('cache_test.');
|
||||
final BufferLogger logger = BufferLogger(
|
||||
terminal: Terminal.test(supportsColor: false, supportsEmoji: false),
|
||||
outputPreferences: OutputPreferences(),
|
||||
);
|
||||
logger.fatalWarnings = true;
|
||||
try {
|
||||
Cache.flutterRoot = tempDir.absolute.path;
|
||||
final Cache cache = Cache.test(
|
||||
fileSystem: fileSystem,
|
||||
processManager: FakeProcessManager.any(),
|
||||
logger: logger,
|
||||
);
|
||||
try {
|
||||
Cache.flutterRoot = tempDir.absolute.path;
|
||||
final Cache cache = Cache.test(
|
||||
fileSystem: fileSystem,
|
||||
processManager: FakeProcessManager.any(),
|
||||
logger: logger,
|
||||
);
|
||||
final File cacheFile = fileSystem.file(fileSystem.path
|
||||
.join(Cache.flutterRoot, 'bin', 'cache', 'lockfile'))
|
||||
..createSync(recursive: true);
|
||||
final File script = fileSystem.file(fileSystem.path
|
||||
.join(Cache.flutterRoot, 'bin', 'cache', 'test_lock.dart'));
|
||||
script.writeAsStringSync(r'''
|
||||
final File cacheFile = fileSystem.file(fileSystem.path
|
||||
.join(Cache.flutterRoot, 'bin', 'cache', 'lockfile'))
|
||||
..createSync(recursive: true);
|
||||
final File script = fileSystem.file(fileSystem.path
|
||||
.join(Cache.flutterRoot, 'bin', 'cache', 'test_lock.dart'));
|
||||
script.writeAsStringSync(r'''
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
Future<void> main(List<String> args) async {
|
||||
File file = File(args[0]);
|
||||
RandomAccessFile lock = file.openSync(mode: FileMode.write);
|
||||
lock.lockSync();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 1000));
|
||||
exit(0);
|
||||
File file = File(args[0]);
|
||||
RandomAccessFile lock = file.openSync(mode: FileMode.write);
|
||||
lock.lockSync();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 1000));
|
||||
exit(0);
|
||||
}
|
||||
''');
|
||||
final Process process = await const LocalProcessManager().start(
|
||||
<String>[dart, script.absolute.path, cacheFile.absolute.path],
|
||||
);
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
await cache.lock();
|
||||
process.kill(io.ProcessSignal.sigkill);
|
||||
} finally {
|
||||
tryToDelete(tempDir);
|
||||
Cache.flutterRoot = oldRoot;
|
||||
}
|
||||
expect(logger.statusText, isEmpty);
|
||||
expect(logger.errorText,
|
||||
equals('Waiting for another flutter command to release the startup lock...\n'));
|
||||
});
|
||||
}
|
||||
final Process process = await const LocalProcessManager().start(
|
||||
<String>[dart, script.absolute.path, cacheFile.absolute.path],
|
||||
);
|
||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
||||
await cache.lock();
|
||||
process.kill(io.ProcessSignal.sigkill);
|
||||
} finally {
|
||||
tryToDelete(tempDir);
|
||||
Cache.flutterRoot = oldRoot;
|
||||
}
|
||||
expect(logger.statusText, isEmpty);
|
||||
expect(logger.errorText, isEmpty);
|
||||
expect(logger.warningText,
|
||||
equals('Waiting for another flutter command to release the startup lock...\n'));
|
||||
expect(logger.hadErrorOutput, isFalse);
|
||||
// Should still be false, since the particular "Waiting..." message above aims to
|
||||
// avoid triggering failure as a fatal warning.
|
||||
expect(logger.hadWarningOutput, isFalse);
|
||||
});
|
||||
testWithoutContext(
|
||||
'should log a warning message for unknown version ', () async {
|
||||
final String oldRoot = Cache.flutterRoot;
|
||||
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('cache_test.');
|
||||
final BufferLogger logger = BufferLogger(
|
||||
terminal: Terminal.test(supportsColor: false, supportsEmoji: false),
|
||||
outputPreferences: OutputPreferences(),
|
||||
);
|
||||
logger.fatalWarnings = true;
|
||||
try {
|
||||
Cache.flutterRoot = tempDir.absolute.path;
|
||||
final Cache cache = Cache.test(
|
||||
fileSystem: fileSystem,
|
||||
processManager: FakeProcessManager.any(),
|
||||
logger: logger,
|
||||
);
|
||||
final FakeVersionlessArtifact artifact = FakeVersionlessArtifact(cache);
|
||||
cache.registerArtifact(artifact);
|
||||
await artifact.update(FakeArtifactUpdater(), logger, fileSystem, FakeOperatingSystemUtils());
|
||||
} finally {
|
||||
tryToDelete(tempDir);
|
||||
Cache.flutterRoot = oldRoot;
|
||||
}
|
||||
expect(logger.statusText, isEmpty);
|
||||
expect(logger.warningText, equals('No known version for the artifact name "fake". '
|
||||
'Flutter can continue, but the artifact may be re-downloaded on '
|
||||
'subsequent invocations until the problem is resolved.\n'));
|
||||
expect(logger.hadErrorOutput, isFalse);
|
||||
expect(logger.hadWarningOutput, isTrue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class FakeArtifactUpdater extends Fake implements ArtifactUpdater {
|
||||
void Function(String, Uri, Directory) onDownloadZipArchive;
|
||||
void Function(String, Uri, Directory) onDownloadZipTarball;
|
||||
|
||||
@override
|
||||
Future<void> downloadZippedTarball(String message, Uri url, Directory location) async {
|
||||
onDownloadZipTarball?.call(message, url, location);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> downloadZipArchive(String message, Uri url, Directory location) async {
|
||||
onDownloadZipArchive?.call(message, url, location);
|
||||
}
|
||||
|
||||
@override
|
||||
void removeDownloadedFiles() { }
|
||||
}
|
||||
|
||||
class FakeVersionlessArtifact extends CachedArtifact {
|
||||
FakeVersionlessArtifact(Cache cache) : super(
|
||||
'fake',
|
||||
cache,
|
||||
DevelopmentArtifact.universal,
|
||||
);
|
||||
|
||||
@override
|
||||
String get version => null;
|
||||
|
||||
@override
|
||||
Future<void> updateInner(ArtifactUpdater artifactUpdater, FileSystem fileSystem, OperatingSystemUtils operatingSystemUtils) async { }
|
||||
}
|
||||
|
@ -115,84 +115,66 @@ void main() {
|
||||
testWithoutContext('flutter test should run a test when its name matches a regexp', () async {
|
||||
final ProcessResult result = await _runFlutterTest('filtering', automatedTestsDirectory, flutterTestDirectory,
|
||||
extraArguments: const <String>['--name', 'inc.*de']);
|
||||
if (!(result.stdout as String).contains('+1: All tests passed')) {
|
||||
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
|
||||
}
|
||||
expect(result.stdout, contains(RegExp(r'\+\d+: All tests passed!')));
|
||||
expect(result.exitCode, 0);
|
||||
});
|
||||
|
||||
testWithoutContext('flutter test should run a test when its name contains a string', () async {
|
||||
final ProcessResult result = await _runFlutterTest('filtering', automatedTestsDirectory, flutterTestDirectory,
|
||||
extraArguments: const <String>['--plain-name', 'include']);
|
||||
if (!(result.stdout as String).contains('+1: All tests passed')) {
|
||||
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
|
||||
}
|
||||
expect(result.stdout, contains(RegExp(r'\+\d+: All tests passed!')));
|
||||
expect(result.exitCode, 0);
|
||||
});
|
||||
|
||||
testWithoutContext('flutter test should run a test with a given tag', () async {
|
||||
final ProcessResult result = await _runFlutterTest('filtering_tag', automatedTestsDirectory, flutterTestDirectory,
|
||||
extraArguments: const <String>['--tags', 'include-tag']);
|
||||
if (!(result.stdout as String).contains('+1: All tests passed')) {
|
||||
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
|
||||
}
|
||||
expect(result.stdout, contains(RegExp(r'\+\d+: All tests passed!')));
|
||||
expect(result.exitCode, 0);
|
||||
});
|
||||
|
||||
testWithoutContext('flutter test should not run a test with excluded tag', () async {
|
||||
final ProcessResult result = await _runFlutterTest('filtering_tag', automatedTestsDirectory, flutterTestDirectory,
|
||||
extraArguments: const <String>['--exclude-tags', 'exclude-tag']);
|
||||
if (!(result.stdout as String).contains('+1: All tests passed')) {
|
||||
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
|
||||
}
|
||||
expect(result.stdout, contains(RegExp(r'\+\d+: All tests passed!')));
|
||||
expect(result.exitCode, 0);
|
||||
});
|
||||
|
||||
testWithoutContext('flutter test should run all tests when tags are unspecified', () async {
|
||||
final ProcessResult result = await _runFlutterTest('filtering_tag', automatedTestsDirectory, flutterTestDirectory);
|
||||
if (!(result.stdout as String).contains('+1 -1: Some tests failed')) {
|
||||
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
|
||||
}
|
||||
expect(result.stdout, contains(RegExp(r'\+\d+ -1: Some tests failed\.')));
|
||||
expect(result.exitCode, 1);
|
||||
});
|
||||
|
||||
testWithoutContext('flutter test should run a widgetTest with a given tag', () async {
|
||||
final ProcessResult result = await _runFlutterTest('filtering_tag_widget', automatedTestsDirectory, flutterTestDirectory,
|
||||
extraArguments: const <String>['--tags', 'include-tag']);
|
||||
if (!(result.stdout as String).contains('+1: All tests passed')) {
|
||||
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
|
||||
}
|
||||
expect(result.stdout, contains(RegExp(r'\+\d+: All tests passed!')));
|
||||
expect(result.exitCode, 0);
|
||||
});
|
||||
|
||||
testWithoutContext('flutter test should not run a widgetTest with excluded tag', () async {
|
||||
final ProcessResult result = await _runFlutterTest('filtering_tag_widget', automatedTestsDirectory, flutterTestDirectory,
|
||||
extraArguments: const <String>['--exclude-tags', 'exclude-tag']);
|
||||
if (!(result.stdout as String).contains('+1: All tests passed')) {
|
||||
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
|
||||
}
|
||||
expect(result.stdout, contains(RegExp(r'\+\d+: All tests passed!')));
|
||||
expect(result.exitCode, 0);
|
||||
});
|
||||
|
||||
testWithoutContext('flutter test should run all widgetTest when tags are unspecified', () async {
|
||||
final ProcessResult result = await _runFlutterTest('filtering_tag_widget', automatedTestsDirectory, flutterTestDirectory);
|
||||
if (!(result.stdout as String).contains('+1 -1: Some tests failed')) {
|
||||
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
|
||||
}
|
||||
expect(result.stdout, contains(RegExp(r'\+\d+ -1: Some tests failed\.')));
|
||||
expect(result.exitCode, 1);
|
||||
});
|
||||
|
||||
testWithoutContext('flutter test should test runs to completion', () async {
|
||||
final ProcessResult result = await _runFlutterTest('trivial', automatedTestsDirectory, flutterTestDirectory,
|
||||
extraArguments: const <String>['--verbose']);
|
||||
final String stdout = result.stdout as String;
|
||||
if ((!stdout.contains('+1: All tests passed')) ||
|
||||
(!stdout.contains('test 0: Starting flutter_tester process with command')) ||
|
||||
(!stdout.contains('test 0: deleting temporary directory')) ||
|
||||
(!stdout.contains('test 0: finished')) ||
|
||||
(!stdout.contains('test package returned with exit code 0'))) {
|
||||
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
|
||||
}
|
||||
final String stdout = (result.stdout as String).replaceAll('\r', '\n');
|
||||
expect(stdout, contains(RegExp(r'\+\d+: All tests passed\!')));
|
||||
expect(stdout, contains('test 0: Starting flutter_tester process with command'));
|
||||
expect(stdout, contains('test 0: deleting temporary directory'));
|
||||
expect(stdout, contains('test 0: finished'));
|
||||
expect(stdout, contains('test package returned with exit code 0'));
|
||||
if ((result.stderr as String).isNotEmpty) {
|
||||
fail('unexpected error output from test:\n\n${result.stderr}\n-- end stderr --\n\n');
|
||||
}
|
||||
@ -202,14 +184,12 @@ void main() {
|
||||
testWithoutContext('flutter test should run all tests inside of a directory with no trailing slash', () async {
|
||||
final ProcessResult result = await _runFlutterTest(null, automatedTestsDirectory, '$flutterTestDirectory/child_directory',
|
||||
extraArguments: const <String>['--verbose']);
|
||||
final String stdout = result.stdout as String;
|
||||
if ((!stdout.contains('+2: All tests passed')) ||
|
||||
(!stdout.contains('test 0: Starting flutter_tester process with command')) ||
|
||||
(!stdout.contains('test 0: deleting temporary directory')) ||
|
||||
(!stdout.contains('test 0: finished')) ||
|
||||
(!stdout.contains('test package returned with exit code 0'))) {
|
||||
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
|
||||
}
|
||||
final String stdout = (result.stdout as String).replaceAll('\r', '\n');
|
||||
expect(result.stdout, contains(RegExp(r'\+\d+: All tests passed\!')));
|
||||
expect(stdout, contains('test 0: Starting flutter_tester process with command'));
|
||||
expect(stdout, contains('test 0: deleting temporary directory'));
|
||||
expect(stdout, contains('test 0: finished'));
|
||||
expect(stdout, contains('test package returned with exit code 0'));
|
||||
if ((result.stderr as String).isNotEmpty) {
|
||||
fail('unexpected error output from test:\n\n${result.stderr}\n-- end stderr --\n\n');
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user