Revert "Clean up startProgress logic. (#19695)" (#19842)
This reverts commit 0636c6fe6026c24763738886db94ba948e7fd5a5.
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart
index 4fa661b..d1a0cc3 100644
--- a/packages/flutter_tools/lib/src/android/gradle.dart
+++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -93,34 +93,29 @@
final FlutterProject flutterProject = new FlutterProject(fs.currentDirectory);
final String gradle = await _ensureGradle(flutterProject);
await updateLocalProperties(project: flutterProject);
- final Status status = logger.startProgress('Resolving dependencies...', expectSlowOperation: true);
- GradleProject project;
try {
+ final Status status = logger.startProgress('Resolving dependencies...', expectSlowOperation: true);
final RunResult runResult = await runCheckedAsync(
<String>[gradle, 'app:properties'],
workingDirectory: flutterProject.android.directory.path,
environment: _gradleEnv,
);
final String properties = runResult.stdout.trim();
- project = new GradleProject.fromAppProperties(properties);
- } catch (exception) {
+ final GradleProject project = new GradleProject.fromAppProperties(properties);
+ status.stop();
+ return project;
+ } catch (e) {
if (getFlutterPluginVersion(flutterProject.android) == FlutterPluginVersion.managed) {
- status.cancel();
// Handle known exceptions. This will exit if handled.
- handleKnownGradleExceptions(exception);
+ handleKnownGradleExceptions(e);
// Print a general Gradle error and exit.
- printError('* Error running Gradle:\n$exception\n');
+ printError('* Error running Gradle:\n$e\n');
throwToolExit('Please review your Gradle project setup in the android/ folder.');
}
- // Fall back to the default
- project = new GradleProject(
- <String>['debug', 'profile', 'release'],
- <String>[], flutterProject.android.gradleAppOutV1Directory,
- );
}
- status.stop();
- return project;
+ // Fall back to the default
+ return new GradleProject(<String>['debug', 'profile', 'release'], <String>[], flutterProject.android.gradleAppOutV1Directory);
}
void handleKnownGradleExceptions(String exceptionString) {
diff --git a/packages/flutter_tools/lib/src/base/logger.dart b/packages/flutter_tools/lib/src/base/logger.dart
index dcab62c..7248c77 100644
--- a/packages/flutter_tools/lib/src/base/logger.dart
+++ b/packages/flutter_tools/lib/src/base/logger.dart
@@ -13,8 +13,6 @@
const int kDefaultStatusPadding = 59;
-typedef void VoidCallback();
-
abstract class Logger {
bool get isVerbose => false;
@@ -55,6 +53,8 @@
});
}
+typedef void _FinishCallback();
+
class StdoutLogger extends Logger {
Status _status;
@@ -66,6 +66,7 @@
void printError(String message, { StackTrace stackTrace, bool emphasis = false }) {
_status?.cancel();
_status = null;
+
if (emphasis)
message = terminal.bolden(message);
stderr.writeln(message);
@@ -108,25 +109,16 @@
}) {
if (_status != null) {
// Ignore nested progresses; return a no-op status object.
- return new Status(onFinish: _clearStatus)..start();
+ return new Status()..start();
}
if (terminal.supportsColor) {
- _status = new AnsiStatus(
- message: message,
- expectSlowOperation: expectSlowOperation,
- padding: progressIndicatorPadding,
- onFinish: _clearStatus,
- )..start();
+ _status = new AnsiStatus(message, expectSlowOperation, () { _status = null; }, progressIndicatorPadding)..start();
} else {
printStatus(message);
- _status = new Status(onFinish: _clearStatus)..start();
+ _status = new Status()..start();
}
return _status;
}
-
- void _clearStatus() {
- _status = null;
- }
}
/// A [StdoutLogger] which replaces Unicode characters that cannot be printed to
@@ -188,7 +180,7 @@
int progressIndicatorPadding = kDefaultStatusPadding,
}) {
printStatus(message);
- return new Status()..start();
+ return new Status();
}
/// Clears all buffers.
@@ -238,9 +230,7 @@
int progressIndicatorPadding = kDefaultStatusPadding,
}) {
printStatus(message);
- return new Status(onFinish: () {
- printTrace('$message (completed)');
- })..start();
+ return new Status();
}
void _emit(_LogType type, String message, [StackTrace stackTrace]) {
@@ -285,91 +275,75 @@
/// A [Status] class begins when start is called, and may produce progress
/// information asynchronously.
///
-/// The [Status] class itself never has any output.
-///
-/// The [AnsiSpinner] subclass shows a spinner, and replaces it with a single
-/// space character when stopped or canceled.
-///
-/// The [AnsiStatus] subclass shows a spinner, and replaces it with timing
-/// information when stopped. When canceled, the information isn't shown. In
-/// either case, a newline is printed.
-///
-/// Generally, consider `logger.startProgress` instead of directly creating
-/// a [Status] or one of its subclasses.
+/// When stop is called, summary information supported by this class is printed.
+/// If cancel is called, no summary information is displayed.
+/// The base class displays nothing at all.
class Status {
- Status({ this.onFinish });
-
- /// A straight [Status] or an [AnsiSpinner] (depending on whether the
- /// terminal is fancy enough), already started.
- factory Status.withSpinner({ VoidCallback onFinish }) {
- if (terminal.supportsColor)
- return new AnsiSpinner(onFinish: onFinish)..start();
- return new Status(onFinish: onFinish)..start();
- }
-
- final VoidCallback onFinish;
+ Status();
bool _isStarted = false;
- /// Call to start spinning.
+ factory Status.withSpinner() {
+ if (terminal.supportsColor)
+ return new AnsiSpinner()..start();
+ return new Status()..start();
+ }
+
+ /// Display summary information for this spinner; called by [stop].
+ void summaryInformation() {}
+
+ /// Call to start spinning. Call this method via super at the beginning
+ /// of a subclass [start] method.
void start() {
- assert(!_isStarted);
_isStarted = true;
}
- /// Call to stop spinning after success.
+ /// Call to stop spinning and delete the spinner. Print summary information,
+ /// if applicable to the spinner.
void stop() {
- assert(_isStarted);
- _isStarted = false;
- if (onFinish != null)
- onFinish();
+ if (_isStarted) {
+ cancel();
+ summaryInformation();
+ }
}
- /// Call to cancel the spinner after failure or cancelation.
+ /// Call to cancel the spinner without printing any summary output. Call
+ /// this method via super at the end of a subclass [cancel] method.
void cancel() {
- assert(_isStarted);
_isStarted = false;
- if (onFinish != null)
- onFinish();
}
}
/// An [AnsiSpinner] is a simple animation that does nothing but implement an
-/// ASCII spinner. When stopped or canceled, the animation erases itself.
+/// ASCII spinner. When stopped or canceled, the animation erases itself.
class AnsiSpinner extends Status {
- AnsiSpinner({ VoidCallback onFinish }) : super(onFinish: onFinish);
-
int ticks = 0;
Timer timer;
- static final List<String> _progress = <String>[r'-', r'\', r'|', r'/'];
+ static final List<String> _progress = <String>['-', r'\', '|', r'/'];
- void _callback(Timer timer) {
+ void _callback(Timer _) {
stdout.write('\b${_progress[ticks++ % _progress.length]}');
}
@override
void start() {
super.start();
- assert(timer == null);
stdout.write(' ');
+ _callback(null);
timer = new Timer.periodic(const Duration(milliseconds: 100), _callback);
- _callback(timer);
}
@override
- void stop() {
- assert(timer.isActive);
- timer.cancel();
- stdout.write('\b \b');
- super.stop();
- }
-
- @override
+ /// Clears the spinner. After cancel, the cursor will be one space right
+ /// of where it was when [start] was called (assuming no other input).
void cancel() {
- assert(timer.isActive);
- timer.cancel();
- stdout.write('\b \b');
+ if (timer?.isActive == true) {
+ timer.cancel();
+ // Many terminals do not interpret backspace as deleting a character,
+ // but rather just moving the cursor back one.
+ stdout.write('\b \b');
+ }
super.cancel();
}
}
@@ -379,50 +353,59 @@
/// On [stop], will additionally print out summary information in
/// milliseconds if [expectSlowOperation] is false, as seconds otherwise.
class AnsiStatus extends AnsiSpinner {
- AnsiStatus({
- this.message,
- this.expectSlowOperation,
- this.padding,
- VoidCallback onFinish,
- }) : super(onFinish: onFinish);
+ AnsiStatus(this.message, this.expectSlowOperation, this.onFinish, this.padding);
final String message;
final bool expectSlowOperation;
+ final _FinishCallback onFinish;
final int padding;
Stopwatch stopwatch;
+ bool _finished = false;
@override
+ /// Writes [message] to [stdout] with padding, then begins spinning.
void start() {
stopwatch = new Stopwatch()..start();
stdout.write('${message.padRight(padding)} ');
+ assert(!_finished);
super.start();
}
@override
+ /// Calls onFinish.
void stop() {
- super.stop();
- writeSummaryInformation();
- stdout.write('\n');
+ if (!_finished) {
+ onFinish();
+ _finished = true;
+ super.cancel();
+ summaryInformation();
+ }
}
@override
- void cancel() {
- super.cancel();
- stdout.write('\n');
- }
-
/// Backs up 4 characters and prints a (minimum) 5 character padded time. If
/// [expectSlowOperation] is true, the time is in seconds; otherwise,
/// milliseconds. Only backs up 4 characters because [super.cancel] backs
/// up one.
///
/// Example: '\b\b\b\b 0.5s', '\b\b\b\b150ms', '\b\b\b\b1600ms'
- void writeSummaryInformation() {
+ void summaryInformation() {
if (expectSlowOperation) {
- stdout.write('\b\b\b\b${getElapsedAsSeconds(stopwatch.elapsed).padLeft(5)}');
+ stdout.writeln('\b\b\b\b${getElapsedAsSeconds(stopwatch.elapsed).padLeft(5)}');
} else {
- stdout.write('\b\b\b\b${getElapsedAsMilliseconds(stopwatch.elapsed).padLeft(5)}');
+ stdout.writeln('\b\b\b\b${getElapsedAsMilliseconds(stopwatch.elapsed).padLeft(5)}');
+ }
+ }
+
+ @override
+ /// Calls [onFinish].
+ void cancel() {
+ if (!_finished) {
+ onFinish();
+ _finished = true;
+ super.cancel();
+ stdout.write('\n');
}
}
}
diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart
index 716d33c..0e3cb61 100644
--- a/packages/flutter_tools/lib/src/cache.dart
+++ b/packages/flutter_tools/lib/src/cache.dart
@@ -295,15 +295,11 @@
return _withDownloadFile('${flattenNameSubdirs(url)}', (File tempFile) async {
if (!verifier(tempFile)) {
final Status status = logger.startProgress(message, expectSlowOperation: true);
- try {
- await _downloadFile(url, tempFile);
+ await _downloadFile(url, tempFile).then<Null>((_) {
status.stop();
- } catch (exception) {
- status.cancel();
- rethrow;
- }
+ }).whenComplete(status.cancel);
} else {
- logger.printTrace('$message (cached)');
+ logger.printStatus('$message(cached)');
}
_ensureExists(location);
extractor(tempFile, location);
diff --git a/packages/flutter_tools/lib/src/commands/build_aot.dart b/packages/flutter_tools/lib/src/commands/build_aot.dart
index f28a1d2..5bf9bb8 100644
--- a/packages/flutter_tools/lib/src/commands/build_aot.dart
+++ b/packages/flutter_tools/lib/src/commands/build_aot.dart
@@ -74,10 +74,8 @@
Status status;
if (!argResults['quiet']) {
final String typeName = artifacts.getEngineType(platform, buildMode);
- status = logger.startProgress(
- 'Building AOT snapshot in ${getModeName(getBuildMode())} mode ($typeName)...',
- expectSlowOperation: true,
- );
+ status = logger.startProgress('Building AOT snapshot in ${getModeName(getBuildMode())} mode ($typeName)...',
+ expectSlowOperation: true);
}
final String outputPath = argResults['output-dir'] ?? getAotBuildDirectory();
try {
@@ -122,6 +120,8 @@
buildSharedLibrary: false,
extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions],
).then((int buildExitCode) {
+ if (buildExitCode != 0)
+ printError('Snapshotting ($iosArch) exited with non-zero exit code: $buildExitCode');
return buildExitCode;
});
});
@@ -134,12 +134,6 @@
..addAll(dylibs)
..addAll(<String>['-create', '-output', fs.path.join(outputPath, 'App.framework', 'App')]),
);
- } else {
- status?.cancel();
- exitCodes.forEach((IOSArch iosArch, Future<int> exitCodeFuture) async {
- final int buildExitCode = await exitCodeFuture;
- printError('Snapshotting ($iosArch) exited with non-zero exit code: $buildExitCode');
- });
}
} else {
// Android AOT snapshot.
@@ -154,14 +148,12 @@
extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions],
);
if (snapshotExitCode != 0) {
- status?.cancel();
printError('Snapshotting exited with non-zero exit code: $snapshotExitCode');
return;
}
}
} on String catch (error) {
// Catch the String exceptions thrown from the `runCheckedSync` methods below.
- status?.cancel();
printError(error);
return;
}
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index eaf51f7..202fbf2 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -901,14 +901,7 @@
'message': message,
});
- _status = new Status(onFinish: () {
- _status = null;
- _sendProgressEvent(<String, dynamic>{
- 'id': id.toString(),
- 'progressId': progressId,
- 'finished': true
- });
- });
+ _status = new _AppLoggerStatus(this, id, progressId);
return _status;
}
@@ -931,6 +924,37 @@
}
}
+class _AppLoggerStatus extends Status {
+ _AppLoggerStatus(this.logger, this.id, this.progressId);
+
+ final _AppRunLogger logger;
+ final int id;
+ final String progressId;
+
+ @override
+ void start() {}
+
+ @override
+ void stop() {
+ logger._status = null;
+ _sendFinished();
+ }
+
+ @override
+ void cancel() {
+ logger._status = null;
+ _sendFinished();
+ }
+
+ void _sendFinished() {
+ logger._sendProgressEvent(<String, dynamic>{
+ 'id': id.toString(),
+ 'progressId': progressId,
+ 'finished': true
+ });
+ }
+}
+
class LogMessage {
final String level;
final String message;
diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart
index e3925f8..b80ba46 100644
--- a/packages/flutter_tools/lib/src/commands/update_packages.dart
+++ b/packages/flutter_tools/lib/src/commands/update_packages.dart
@@ -84,10 +84,7 @@
final bool hidden;
Future<Null> _downloadCoverageData() async {
- final Status status = logger.startProgress(
- 'Downloading lcov data for package:flutter...',
- expectSlowOperation: true,
- );
+ final Status status = logger.startProgress('Downloading lcov data for package:flutter...', expectSlowOperation: true);
final String urlBase = platform.environment['FLUTTER_STORAGE_BASE_URL'] ?? 'https://storage.googleapis.com';
final List<int> data = await fetchUrl(Uri.parse('$urlBase/flutter_infra/flutter/coverage/lcov.info'));
final String coverageDir = fs.path.join(Cache.flutterRoot, 'packages/flutter/coverage');
diff --git a/packages/flutter_tools/lib/src/dart/pub.dart b/packages/flutter_tools/lib/src/dart/pub.dart
index 90ef41b..d633374 100644
--- a/packages/flutter_tools/lib/src/dart/pub.dart
+++ b/packages/flutter_tools/lib/src/dart/pub.dart
@@ -109,10 +109,8 @@
failureMessage: 'pub $command failed',
retry: true,
);
+ } finally {
status.stop();
- } catch (exception) {
- status.cancel();
- rethrow;
}
}
diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart
index f97f2ff..9d9dc90 100644
--- a/packages/flutter_tools/lib/src/doctor.dart
+++ b/packages/flutter_tools/lib/src/doctor.dart
@@ -145,13 +145,9 @@
for (ValidatorTask validatorTask in startValidatorTasks()) {
final DoctorValidator validator = validatorTask.validator;
final Status status = new Status.withSpinner();
- try {
- await validatorTask.result;
- } catch (exception) {
- status.cancel();
- rethrow;
- }
- status.stop();
+ await (validatorTask.result).then<void>((_) {
+ status.stop();
+ }).whenComplete(status.cancel);
final ValidationResult result = await validatorTask.result;
if (result.type == ValidationType.missing) {
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index 14b1b91..3ebc8ba 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -299,6 +299,7 @@
bundlePath: bundle.path,
launchArguments: launchArguments,
);
+ installStatus.stop();
} else {
// Debugging is enabled, look for the observatory server port post launch.
printTrace('Debugging is enabled, connecting to observatory');
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index a18ea9d..ca16780 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -385,7 +385,7 @@
}) async {
final Status devFSStatus = logger.startProgress(
'Syncing files to device ${device.name}...',
- expectSlowOperation: true,
+ expectSlowOperation: true
);
int bytes = 0;
try {
@@ -554,9 +554,8 @@
for (FlutterView view in device.views)
await view.uiIsolate.flutterDebugAllowBanner(false);
} catch (error) {
- status.cancel();
+ status.stop();
printError('Error communicating with Flutter on the device: $error');
- return;
}
}
try {
@@ -567,9 +566,8 @@
for (FlutterView view in device.views)
await view.uiIsolate.flutterDebugAllowBanner(true);
} catch (error) {
- status.cancel();
+ status.stop();
printError('Error communicating with Flutter on the device: $error');
- return;
}
}
}
@@ -577,7 +575,7 @@
status.stop();
printStatus('Screenshot written to ${fs.path.relative(outputFile.path)} (${sizeKB}kB).');
} catch (error) {
- status.cancel();
+ status.stop();
printError('Error taking screenshot: $error');
}
}
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart
index 39ee197..f28cf49 100644
--- a/packages/flutter_tools/lib/src/run_hot.dart
+++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -497,20 +497,23 @@
if (fullRestart) {
final Status status = logger.startProgress(
'Performing hot restart...',
- progressId: 'hot.restart',
+ progressId: 'hot.restart'
);
try {
+ final Stopwatch timer = new Stopwatch()..start();
if (!(await hotRunnerConfig.setupHotRestart())) {
status.cancel();
return new OperationResult(1, 'setupHotRestart failed');
}
await _restartFromSources();
+ timer.stop();
+ status.cancel();
+ printStatus('Restarted app in ${getElapsedAsMilliseconds(timer.elapsed)}.');
+ return OperationResult.ok;
} catch (error) {
status.cancel();
rethrow;
}
- status.stop(); // Prints timing information.
- return OperationResult.ok;
} else {
final bool reloadOnTopOfSnapshot = _runningFromSnapshot;
final String progressPrefix = reloadOnTopOfSnapshot ? 'Initializing' : 'Performing';
@@ -518,21 +521,20 @@
'$progressPrefix hot reload...',
progressId: 'hot.reload'
);
- final Stopwatch timer = new Stopwatch()..start();
- OperationResult result;
try {
- result = await _reloadSources(pause: pauseAfterRestart);
+ final Stopwatch timer = new Stopwatch()..start();
+ final OperationResult result = await _reloadSources(pause: pauseAfterRestart);
+ timer.stop();
+ status.cancel();
+ if (result.isOk)
+ printStatus('${result.message} in ${getElapsedAsMilliseconds(timer.elapsed)}.');
+ if (result.hintMessage != null)
+ printStatus('\n${result.hintMessage}');
+ return result;
} catch (error) {
- status?.cancel();
+ status.cancel();
rethrow;
}
- timer.stop();
- status.cancel(); // Do not show summary information, since we show it in more detail below.
- if (result.isOk)
- printStatus('${result.message} in ${getElapsedAsMilliseconds(timer.elapsed)}.');
- if (result.hintMessage != null)
- printStatus('\n${result.hintMessage}');
- return result;
}
}
diff --git a/packages/flutter_tools/test/base/logger_test.dart b/packages/flutter_tools/test/base/logger_test.dart
index 24d0f0f..5fa9778 100644
--- a/packages/flutter_tools/test/base/logger_test.dart
+++ b/packages/flutter_tools/test/base/logger_test.dart
@@ -4,7 +4,6 @@
import 'dart:async';
-import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:test/test.dart';
@@ -41,27 +40,23 @@
mockStdio = new MockStdio();
ansiSpinner = new AnsiSpinner();
called = 0;
- ansiStatus = new AnsiStatus(
- message: 'Hello world',
- expectSlowOperation: true,
- padding: 20,
- onFinish: () => called++,
- );
+ ansiStatus = new AnsiStatus('Hello world', true, () => called++, 20);
});
List<String> outputLines() => mockStdio.writtenToStdout.join('').split('\n');
- Future<void> doWhileAsync(bool doThis()) async {
- return Future.doWhile(() {
- // We want to let other tasks run at the same time, so we schedule these
- // using a timer rather than a microtask.
- return Future<bool>.delayed(Duration.zero, doThis);
+ Future<void> doWhile(bool doThis()) async {
+ return Future.doWhile(() async {
+ // Future.doWhile() isn't enough by itself, because the VM never gets
+ // around to scheduling the other tasks for some reason.
+ await new Future<void>.delayed(const Duration(milliseconds: 0));
+ return doThis();
});
}
testUsingContext('AnsiSpinner works', () async {
ansiSpinner.start();
- await doWhileAsync(() => ansiSpinner.ticks < 10);
+ await doWhile(() => ansiSpinner.ticks < 10);
List<String> lines = outputLines();
expect(lines[0], startsWith(' \b-\b\\\b|\b/\b-\b\\\b|\b/'));
expect(lines[0].endsWith('\n'), isFalse);
@@ -71,37 +66,44 @@
expect(lines[0], endsWith('\b \b'));
expect(lines.length, equals(1));
- // Verify that stopping or canceling multiple times throws.
- expect(() { ansiSpinner.stop(); }, throwsA(const isInstanceOf<AssertionError>()));
- expect(() { ansiSpinner.cancel(); }, throwsA(const isInstanceOf<AssertionError>()));
+ // Verify that stopping multiple times doesn't clear multiple times.
+ ansiSpinner.stop();
+ lines = outputLines();
+ expect(lines[0].endsWith('\b \b '), isFalse);
+ expect(lines.length, equals(1));
+ ansiSpinner.cancel();
+ lines = outputLines();
+ expect(lines[0].endsWith('\b \b '), isFalse);
+ expect(lines.length, equals(1));
}, overrides: <Type, Generator>{Stdio: () => mockStdio});
testUsingContext('AnsiStatus works when cancelled', () async {
ansiStatus.start();
- await doWhileAsync(() => ansiStatus.ticks < 10);
+ await doWhile(() => ansiStatus.ticks < 10);
List<String> lines = outputLines();
expect(lines[0], startsWith('Hello world \b-\b\\\b|\b/\b-\b\\\b|\b/\b-'));
- expect(lines.length, equals(1));
expect(lines[0].endsWith('\n'), isFalse);
-
- // Verify a cancel does _not_ print the time and prints a newline.
+ expect(lines.length, equals(1));
ansiStatus.cancel();
lines = outputLines();
- final List<Match> matches = secondDigits.allMatches(lines[0]).toList();
- expect(matches, isEmpty);
expect(lines[0], endsWith('\b \b'));
- expect(called, equals(1));
expect(lines.length, equals(2));
- expect(lines[1], equals(''));
-
- // Verify that stopping or canceling multiple times throws.
- expect(() { ansiStatus.cancel(); }, throwsA(const isInstanceOf<AssertionError>()));
- expect(() { ansiStatus.stop(); }, throwsA(const isInstanceOf<AssertionError>()));
+ expect(called, equals(1));
+ ansiStatus.cancel();
+ lines = outputLines();
+ expect(lines[0].endsWith('\b \b\b \b'), isFalse);
+ expect(lines.length, equals(2));
+ expect(called, equals(1));
+ ansiStatus.stop();
+ lines = outputLines();
+ expect(lines[0].endsWith('\b \b\b \b'), isFalse);
+ expect(lines.length, equals(2));
+ expect(called, equals(1));
}, overrides: <Type, Generator>{Stdio: () => mockStdio});
testUsingContext('AnsiStatus works when stopped', () async {
ansiStatus.start();
- await doWhileAsync(() => ansiStatus.ticks < 10);
+ await doWhile(() => ansiStatus.ticks < 10);
List<String> lines = outputLines();
expect(lines[0], startsWith('Hello world \b-\b\\\b|\b/\b-\b\\\b|\b/\b-'));
expect(lines.length, equals(1));
@@ -109,55 +111,55 @@
// Verify a stop prints the time.
ansiStatus.stop();
lines = outputLines();
- final List<Match> matches = secondDigits.allMatches(lines[0]).toList();
+ List<Match> matches = secondDigits.allMatches(lines[0]).toList();
expect(matches, isNotNull);
expect(matches, hasLength(1));
- final Match match = matches.first;
+ Match match = matches.first;
expect(lines[0], endsWith(match.group(0)));
+ final String initialTime = match.group(0);
expect(called, equals(1));
expect(lines.length, equals(2));
expect(lines[1], equals(''));
- // Verify that stopping or canceling multiple times throws.
- expect(() { ansiStatus.stop(); }, throwsA(const isInstanceOf<AssertionError>()));
- expect(() { ansiStatus.cancel(); }, throwsA(const isInstanceOf<AssertionError>()));
+ // Verify stopping more than once generates no additional output.
+ ansiStatus.stop();
+ lines = outputLines();
+ matches = secondDigits.allMatches(lines[0]).toList();
+ expect(matches, hasLength(1));
+ match = matches.first;
+ expect(lines[0], endsWith(initialTime));
+ expect(called, equals(1));
+ expect(lines.length, equals(2));
+ expect(lines[1], equals(''));
}, overrides: <Type, Generator>{Stdio: () => mockStdio});
- testUsingContext('sequential startProgress calls with StdoutLogger', () async {
- context[Logger].startProgress('AAA')..stop();
- context[Logger].startProgress('BBB')..stop();
- expect(outputLines(), <String>[
- 'AAA',
- 'BBB',
- '',
- ]);
- }, overrides: <Type, Generator>{
- Stdio: () => mockStdio,
- Logger: () => new StdoutLogger(),
- });
+ testUsingContext('AnsiStatus works when cancelled', () async {
+ ansiStatus.start();
+ await doWhile(() => ansiStatus.ticks < 10);
+ List<String> lines = outputLines();
+ expect(lines[0], startsWith('Hello world \b-\b\\\b|\b/\b-\b\\\b|\b/\b-'));
+ expect(lines.length, equals(1));
- testUsingContext('sequential startProgress calls with VerboseLogger and StdoutLogger', () async {
- context[Logger].startProgress('AAA')..stop();
- context[Logger].startProgress('BBB')..stop();
- expect(outputLines(), <String>[
- '[ ] AAA',
- '[ ] AAA (completed)',
- '[ ] BBB',
- '[ ] BBB (completed)',
- ''
- ]);
- }, overrides: <Type, Generator>{
- Stdio: () => mockStdio,
- Logger: () => new VerboseLogger(new StdoutLogger()),
- });
+ // Verify a cancel does _not_ print the time and prints a newline.
+ ansiStatus.cancel();
+ lines = outputLines();
+ List<Match> matches = secondDigits.allMatches(lines[0]).toList();
+ expect(matches, isEmpty);
+ expect(lines[0], endsWith('\b \b'));
+ expect(called, equals(1));
+ // TODO(jcollins-g): Consider having status objects print the newline
+ // when canceled, or never printing a newline at all.
+ expect(lines.length, equals(2));
- testUsingContext('sequential startProgress calls with BufferLogger', () async {
- context[Logger].startProgress('AAA')..stop();
- context[Logger].startProgress('BBB')..stop();
- final BufferLogger logger = context[Logger];
- expect(logger.statusText, 'AAA\nBBB\n');
- }, overrides: <Type, Generator>{
- Logger: () => new BufferLogger(),
- });
+ // Verifying calling stop after cancel doesn't print anything weird.
+ ansiStatus.stop();
+ lines = outputLines();
+ matches = secondDigits.allMatches(lines[0]).toList();
+ expect(matches, isEmpty);
+ expect(lines[0], endsWith('\b \b'));
+ expect(called, equals(1));
+ expect(lines[0], isNot(endsWith('\b \b\b \b')));
+ expect(lines.length, equals(2));
+ }, overrides: <Type, Generator>{Stdio: () => mockStdio});
});
}