mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[flutter_tool] Have long-running validators fail (#100936)
This commit is contained in:
parent
61c30eed4f
commit
b4325b68a2
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:process/process.dart';
|
||||
|
||||
@ -204,13 +206,29 @@ class Doctor {
|
||||
// Future returned by the asyncGuard() is not awaited, we pass an
|
||||
// onError callback to it and translate errors into ValidationResults.
|
||||
asyncGuard<ValidationResult>(
|
||||
validator.validate,
|
||||
() {
|
||||
final Completer<ValidationResult> timeoutCompleter = Completer<ValidationResult>();
|
||||
final Timer timer = Timer(doctorDuration, () {
|
||||
timeoutCompleter.completeError(
|
||||
Exception('${validator.title} exceeded maximum allowed duration of $doctorDuration'),
|
||||
);
|
||||
});
|
||||
final Future<ValidationResult> validatorFuture = validator.validate();
|
||||
return Future.any<ValidationResult>(<Future<ValidationResult>>[
|
||||
validatorFuture,
|
||||
// This future can only complete with an error
|
||||
timeoutCompleter.future,
|
||||
]).then((ValidationResult result) async {
|
||||
timer.cancel();
|
||||
return result;
|
||||
});
|
||||
},
|
||||
onError: (Object exception, StackTrace stackTrace) {
|
||||
return ValidationResult.crash(exception, stackTrace);
|
||||
},
|
||||
),
|
||||
),
|
||||
];
|
||||
];
|
||||
|
||||
List<Workflow> get workflows {
|
||||
return DoctorValidatorsProvider._instance.workflows;
|
||||
@ -290,6 +308,11 @@ class Doctor {
|
||||
return globals.cache.areRemoteArtifactsAvailable(engineVersion: engineRevision);
|
||||
}
|
||||
|
||||
/// Maximum allowed duration for an entire validator to take.
|
||||
///
|
||||
/// This should only ever be reached if a process is stuck.
|
||||
static const Duration doctorDuration = Duration(minutes: 10);
|
||||
|
||||
/// Print information about the state of installed tooling.
|
||||
///
|
||||
/// To exclude personally identifiable information like device names and
|
||||
@ -316,7 +339,7 @@ class Doctor {
|
||||
for (final ValidatorTask validatorTask in startedValidatorTasks ?? startValidatorTasks()) {
|
||||
final DoctorValidator validator = validatorTask.validator;
|
||||
final Status status = _logger.startSpinner(
|
||||
timeout: const Duration(seconds: 2),
|
||||
timeout: validator.slowWarningDuration,
|
||||
slowWarningCallback: () => validator.slowWarning,
|
||||
);
|
||||
ValidationResult result;
|
||||
|
@ -53,6 +53,11 @@ abstract class DoctorValidator {
|
||||
|
||||
String get slowWarning => 'This is taking an unexpectedly long time...';
|
||||
|
||||
static const Duration _slowWarningDuration = Duration(seconds: 10);
|
||||
|
||||
/// Duration before the spinner should display [slowWarning].
|
||||
Duration get slowWarningDuration => _slowWarningDuration;
|
||||
|
||||
Future<ValidationResult> validate();
|
||||
}
|
||||
|
||||
|
@ -359,6 +359,16 @@ void main() {
|
||||
expect(logger.statusText, contains('#0 CrashingValidator.validate'));
|
||||
});
|
||||
|
||||
testUsingContext('validate tool exit when exceeding timeout', () async {
|
||||
FakeAsync().run<void>((FakeAsync time) {
|
||||
final Doctor doctor = FakeAsyncStuckDoctor(logger);
|
||||
doctor.diagnose(verbose: false);
|
||||
time.elapse(Doctor.doctorDuration + const Duration(seconds: 1));
|
||||
time.flushMicrotasks();
|
||||
});
|
||||
|
||||
expect(logger.statusText, contains('Stuck validator that never completes exceeded maximum allowed duration of '));
|
||||
});
|
||||
|
||||
testUsingContext('validate non-verbose output format for run with an async crash', () async {
|
||||
final Completer<void> completer = Completer<void>();
|
||||
@ -816,6 +826,18 @@ class NotAvailableValidator extends DoctorValidator {
|
||||
}
|
||||
}
|
||||
|
||||
class StuckValidator extends DoctorValidator {
|
||||
StuckValidator() : super('Stuck validator that never completes');
|
||||
|
||||
@override
|
||||
Future<ValidationResult> validate() {
|
||||
final Completer<ValidationResult> completer = Completer<ValidationResult>();
|
||||
|
||||
// This future will never complete
|
||||
return completer.future;
|
||||
}
|
||||
}
|
||||
|
||||
class PartialValidatorWithErrors extends DoctorValidator {
|
||||
PartialValidatorWithErrors() : super('Partial Validator with Errors');
|
||||
|
||||
@ -966,6 +988,25 @@ class FakeCrashingDoctor extends Doctor {
|
||||
}
|
||||
}
|
||||
|
||||
/// A doctor with a validator that will never finish.
|
||||
class FakeAsyncStuckDoctor extends Doctor {
|
||||
FakeAsyncStuckDoctor(Logger logger) : super(logger: logger);
|
||||
|
||||
List<DoctorValidator> _validators;
|
||||
@override
|
||||
List<DoctorValidator> get validators {
|
||||
if (_validators == null) {
|
||||
_validators = <DoctorValidator>[];
|
||||
_validators.add(PassingValidator('Passing Validator'));
|
||||
_validators.add(PassingValidator('Another Passing Validator'));
|
||||
_validators.add(StuckValidator());
|
||||
_validators.add(PassingValidator('Validators are fun'));
|
||||
_validators.add(PassingValidator('Four score and seven validators ago'));
|
||||
}
|
||||
return _validators;
|
||||
}
|
||||
}
|
||||
|
||||
/// A doctor with a validator that throws an exception.
|
||||
class FakeAsyncCrashingDoctor extends Doctor {
|
||||
FakeAsyncCrashingDoctor(this._time, Logger logger) : super(logger: logger);
|
||||
|
Loading…
Reference in New Issue
Block a user