Unverified Commit b698c9d7 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tool] Prevent accidental calls to io.exit when asserts are active in unit tests (#46210)

parent 05862ff9
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
/// increase the API surface that we have to test in Flutter tools, and the APIs /// increase the API surface that we have to test in Flutter tools, and the APIs
/// in `dart:io` can sometimes be hard to use in tests. /// in `dart:io` can sometimes be hard to use in tests.
import 'dart:async'; import 'dart:async';
import 'dart:io' as io show exit, IOSink, Process, ProcessInfo, ProcessSignal, import 'dart:io' as io show exit, IOSink, Platform, Process, ProcessInfo, ProcessSignal,
stderr, stdin, Stdin, StdinException, Stdout, stdout; stderr, stdin, Stdin, StdinException, Stdout, stdout;
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
...@@ -92,12 +92,26 @@ ExitFunction _exitFunction = _defaultExitFunction; ...@@ -92,12 +92,26 @@ ExitFunction _exitFunction = _defaultExitFunction;
/// Exits the process. /// Exits the process.
/// ///
/// Throws [StateError] if assertions are enabled and the dart:io exit
/// is still active when called. This may indicate exit was called in
/// a test without being configured correctly. This behavior can be
/// removed by setting the `FLUTTER_TEST` environment
/// variable to a non-null string.
///
/// This is analogous to the `exit` function in `dart:io`, except that this /// This is analogous to the `exit` function in `dart:io`, except that this
/// function may be set to a testing-friendly value by calling /// function may be set to a testing-friendly value by calling
/// [setExitFunctionForTests] (and then restored to its default implementation /// [setExitFunctionForTests] (and then restored to its default implementation
/// with [restoreExitFunction]). The default implementation delegates to /// with [restoreExitFunction]). The default implementation delegates to
/// `dart:io`. /// `dart:io`.
ExitFunction get exit => _exitFunction; ExitFunction get exit {
assert(
_exitFunction != io.exit || io.Platform.environment['FLUTTER_TEST'] != null,
'io.exit was called with assertions active. If this is an integration test, '
'ensure that the environment variable FLUTTER_TEST is set '
'to a non-null String.',
);
return _exitFunction;
}
/// Sets the [exit] function to a function that throws an exception rather /// Sets the [exit] function to a function that throws an exception rather
/// than exiting the process; this is intended for testing purposes. /// than exiting the process; this is intended for testing purposes.
......
...@@ -68,6 +68,16 @@ void main() { ...@@ -68,6 +68,16 @@ void main() {
testUsingContext('ProcessSignal toString() works', () async { testUsingContext('ProcessSignal toString() works', () async {
expect(io.ProcessSignal.sigint.toString(), ProcessSignal.SIGINT.toString()); expect(io.ProcessSignal.sigint.toString(), ProcessSignal.SIGINT.toString());
}); });
test('exit throws a StateError if called without being overriden', () {
expect(() => exit(0), throwsA(isInstanceOf<AssertionError>()));
});
test('exit does not throw a StateError if overriden', () {
setExitFunctionForTests((int value) {});
expect(() => exit(0), returnsNormally);
});
} }
class MockIoProcessSignal extends Mock implements io.ProcessSignal {} class MockIoProcessSignal extends Mock implements io.ProcessSignal {}
...@@ -29,6 +29,9 @@ void main() { ...@@ -29,6 +29,9 @@ void main() {
final Process process = await processManager.start( final Process process = await processManager.start(
<String>[flutterBin, '--show-test-device', 'daemon'], <String>[flutterBin, '--show-test-device', 'daemon'],
workingDirectory: tempDir.path, workingDirectory: tempDir.path,
environment: <String, String>{
'FLUTTER_TEST': 'true',
}
); );
final StreamController<String> stdout = StreamController<String>.broadcast(); final StreamController<String> stdout = StreamController<String>.broadcast();
......
...@@ -46,7 +46,12 @@ Future<void> getPackages(String folder) async { ...@@ -46,7 +46,12 @@ Future<void> getPackages(String folder) async {
'pub', 'pub',
'get', 'get',
]; ];
final ProcessResult result = await processManager.run(command, workingDirectory: folder); final ProcessResult result = await processManager.run(command,
workingDirectory: folder,
environment: <String, String>{
'FLUTTER_TEST': 'true'
},
);
if (result.exitCode != 0) { if (result.exitCode != 0) {
throw Exception('flutter pub get failed: ${result.stderr}\n${result.stdout}'); throw Exception('flutter pub get failed: ${result.stderr}\n${result.stdout}');
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment