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
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:process/process.dart';
|
import 'package:process/process.dart';
|
||||||
|
|
||||||
@ -204,13 +206,29 @@ class Doctor {
|
|||||||
// Future returned by the asyncGuard() is not awaited, we pass an
|
// Future returned by the asyncGuard() is not awaited, we pass an
|
||||||
// onError callback to it and translate errors into ValidationResults.
|
// onError callback to it and translate errors into ValidationResults.
|
||||||
asyncGuard<ValidationResult>(
|
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) {
|
onError: (Object exception, StackTrace stackTrace) {
|
||||||
return ValidationResult.crash(exception, stackTrace);
|
return ValidationResult.crash(exception, stackTrace);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
List<Workflow> get workflows {
|
List<Workflow> get workflows {
|
||||||
return DoctorValidatorsProvider._instance.workflows;
|
return DoctorValidatorsProvider._instance.workflows;
|
||||||
@ -290,6 +308,11 @@ class Doctor {
|
|||||||
return globals.cache.areRemoteArtifactsAvailable(engineVersion: engineRevision);
|
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.
|
/// Print information about the state of installed tooling.
|
||||||
///
|
///
|
||||||
/// To exclude personally identifiable information like device names and
|
/// To exclude personally identifiable information like device names and
|
||||||
@ -316,7 +339,7 @@ class Doctor {
|
|||||||
for (final ValidatorTask validatorTask in startedValidatorTasks ?? startValidatorTasks()) {
|
for (final ValidatorTask validatorTask in startedValidatorTasks ?? startValidatorTasks()) {
|
||||||
final DoctorValidator validator = validatorTask.validator;
|
final DoctorValidator validator = validatorTask.validator;
|
||||||
final Status status = _logger.startSpinner(
|
final Status status = _logger.startSpinner(
|
||||||
timeout: const Duration(seconds: 2),
|
timeout: validator.slowWarningDuration,
|
||||||
slowWarningCallback: () => validator.slowWarning,
|
slowWarningCallback: () => validator.slowWarning,
|
||||||
);
|
);
|
||||||
ValidationResult result;
|
ValidationResult result;
|
||||||
|
@ -53,6 +53,11 @@ abstract class DoctorValidator {
|
|||||||
|
|
||||||
String get slowWarning => 'This is taking an unexpectedly long time...';
|
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();
|
Future<ValidationResult> validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,6 +359,16 @@ void main() {
|
|||||||
expect(logger.statusText, contains('#0 CrashingValidator.validate'));
|
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 {
|
testUsingContext('validate non-verbose output format for run with an async crash', () async {
|
||||||
final Completer<void> completer = Completer<void>();
|
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 {
|
class PartialValidatorWithErrors extends DoctorValidator {
|
||||||
PartialValidatorWithErrors() : super('Partial Validator with Errors');
|
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.
|
/// A doctor with a validator that throws an exception.
|
||||||
class FakeAsyncCrashingDoctor extends Doctor {
|
class FakeAsyncCrashingDoctor extends Doctor {
|
||||||
FakeAsyncCrashingDoctor(this._time, Logger logger) : super(logger: logger);
|
FakeAsyncCrashingDoctor(this._time, Logger logger) : super(logger: logger);
|
||||||
|
Loading…
Reference in New Issue
Block a user