diff --git a/packages/flutter_tools/lib/src/base/terminal.dart b/packages/flutter_tools/lib/src/base/terminal.dart index b782441b599..73c20f9fe67 100644 --- a/packages/flutter_tools/lib/src/base/terminal.dart +++ b/packages/flutter_tools/lib/src/base/terminal.dart @@ -314,13 +314,19 @@ class AnsiTerminal implements Terminal { return; } final io.Stdin stdin = _stdio.stdin as io.Stdin; - // The order of setting lineMode and echoMode is important on Windows. - if (value) { - stdin.echoMode = false; - stdin.lineMode = false; - } else { - stdin.lineMode = true; - stdin.echoMode = true; + + try { + // The order of setting lineMode and echoMode is important on Windows. + if (value) { + stdin.echoMode = false; + stdin.lineMode = false; + } else { + stdin.lineMode = true; + stdin.echoMode = true; + } + } on io.StdinException { + // If the pipe to STDIN has been closed it's probably because the + // terminal has been closed, and there is nothing actionable to do here. } } diff --git a/packages/flutter_tools/test/general.shard/base/terminal_test.dart b/packages/flutter_tools/test/general.shard/base/terminal_test.dart index 3d335bbe254..5810a582907 100644 --- a/packages/flutter_tools/test/general.shard/base/terminal_test.dart +++ b/packages/flutter_tools/test/general.shard/base/terminal_test.dart @@ -9,6 +9,7 @@ import 'package:flutter_tools/src/base/terminal.dart'; import 'package:test/fake.dart'; import '../../src/common.dart'; +import '../../src/fakes.dart'; void main() { group('output preferences', () { @@ -253,6 +254,16 @@ void main() { expect(AnsiTerminal(stdio: stdio, platform: const LocalPlatform(), now: DateTime(2018, 1, 10, 23)).preferredStyle, 2); expect(AnsiTerminal(stdio: stdio, platform: const LocalPlatform(), now: DateTime(2018, 1, 11, 23)).preferredStyle, 3); }); + + testWithoutContext('set singleCharMode resilient to StdinException', () async { + final FakeStdio stdio = FakeStdio(); + final AnsiTerminal terminal = AnsiTerminal(stdio: stdio, platform: const LocalPlatform()); + stdio.stdinHasTerminal = true; + stdio._stdin = FakeStdin()..echoModeCallback = (bool _) => throw const StdinException( + 'Error setting terminal echo mode, OS Error: The handle is invalid.', + ); + terminal.singleCharMode = true; + }); } late Stream mockStdInStream; @@ -269,14 +280,36 @@ class TestTerminal extends AnsiTerminal { return mockStdInStream; } + bool _singleCharMode = false; + @override - bool singleCharMode = false; + bool get singleCharMode => _singleCharMode; + + void Function(bool newMode)? _singleCharModeCallback; + + @override + set singleCharMode(bool newMode) { + _singleCharMode = newMode; + if (_singleCharModeCallback != null) { + _singleCharModeCallback!(newMode); + } + } @override int get preferredStyle => 0; } class FakeStdio extends Fake implements Stdio { + Stream>? _stdin; + + @override + Stream> get stdin { + if (_stdin != null) { + return _stdin!; + } + throw UnimplementedError('stdin'); + } + @override bool stdinHasTerminal = false; } diff --git a/packages/flutter_tools/test/src/fakes.dart b/packages/flutter_tools/test/src/fakes.dart index e8dec45f6f2..bca86131dc4 100644 --- a/packages/flutter_tools/test/src/fakes.dart +++ b/packages/flutter_tools/test/src/fakes.dart @@ -253,8 +253,20 @@ class FakeStdio extends Stdio { class FakeStdin extends Fake implements Stdin { final StreamController> controller = StreamController>(); + void Function(bool mode)? echoModeCallback; + + bool _echoMode = true; + @override - bool echoMode = true; + bool get echoMode => _echoMode; + + @override + set echoMode(bool mode) { + _echoMode = mode; + if (echoModeCallback != null) { + echoModeCallback!(mode); + } + } @override bool lineMode = true;