Consider all non-interactive terminals to be bots (#34179)
diff --git a/packages/flutter_tools/lib/src/base/io.dart b/packages/flutter_tools/lib/src/base/io.dart
index 7ae40ae..9f9b882 100644
--- a/packages/flutter_tools/lib/src/base/io.dart
+++ b/packages/flutter_tools/lib/src/base/io.dart
@@ -26,7 +26,7 @@
/// 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.
import 'dart:async';
-import 'dart:io' as io show exit, IOSink, ProcessSignal, stderr, stdin, stdout;
+import 'dart:io' as io show exit, IOSink, ProcessSignal, stderr, stdin, Stdout, stdout;
import 'package:meta/meta.dart';
@@ -72,6 +72,7 @@
Stdin,
StdinException,
// stdout, NO! Use `io.dart`
+ Stdout,
Socket,
SocketException,
systemEncoding,
@@ -156,7 +157,7 @@
const Stdio();
Stream<List<int>> get stdin => io.stdin;
- io.IOSink get stdout => io.stdout;
+ io.Stdout get stdout => io.stdout;
io.IOSink get stderr => io.stderr;
bool get hasTerminal => io.stdout.hasTerminal;
@@ -165,7 +166,7 @@
bool get supportsAnsiEscapes => hasTerminal ? io.stdout.supportsAnsiEscapes : false;
}
-Stdio get stdio => context.get<Stdio>();
-io.IOSink get stdout => stdio.stdout;
+Stdio get stdio => context.get<Stdio>() ?? const Stdio();
+io.Stdout get stdout => stdio.stdout;
Stream<List<int>> get stdin => stdio.stdin;
io.IOSink get stderr => stdio.stderr;
diff --git a/packages/flutter_tools/lib/src/base/utils.dart b/packages/flutter_tools/lib/src/base/utils.dart
index 1b6d2c3..9b612e2 100644
--- a/packages/flutter_tools/lib/src/base/utils.dart
+++ b/packages/flutter_tools/lib/src/base/utils.dart
@@ -21,8 +21,20 @@
const BotDetector();
bool get isRunningOnBot {
- return platform.environment['BOT'] != 'false'
- && (platform.environment['BOT'] == 'true'
+ if (
+ // Explicitly stated to not be a bot.
+ platform.environment['BOT'] == 'false'
+
+ // Set by the IDEs to the IDE name, so a strong signal that this is not a bot.
+ || platform.environment.containsKey('FLUTTER_HOST')
+ ) {
+ return false;
+ }
+
+ return platform.environment['BOT'] == 'true'
+
+ // Non-interactive terminals are assumed to be bots.
+ || !io.stdout.hasTerminal
// https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
|| platform.environment['TRAVIS'] == 'true'
@@ -43,7 +55,8 @@
// Properties on Flutter's Chrome Infra bots.
|| platform.environment['CHROME_HEADLESS'] == '1'
- || platform.environment.containsKey('BUILDBOT_BUILDERNAME'));
+ || platform.environment.containsKey('BUILDBOT_BUILDERNAME')
+ || platform.environment.containsKey('SWARMING_TASK_ID');
}
}
diff --git a/packages/flutter_tools/test/base/utils_test.dart b/packages/flutter_tools/test/base/utils_test.dart
new file mode 100644
index 0000000..8c1d885
--- /dev/null
+++ b/packages/flutter_tools/test/base/utils_test.dart
@@ -0,0 +1,56 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter_tools/src/base/io.dart';
+import 'package:flutter_tools/src/base/platform.dart';
+import 'package:flutter_tools/src/base/utils.dart';
+import 'package:platform/platform.dart';
+
+import '../src/common.dart';
+import '../src/context.dart';
+import '../src/mocks.dart';
+
+void main() {
+ group('BotDetector', () {
+ FakePlatform fakePlatform;
+ MockStdio mockStdio;
+ BotDetector botDetector;
+
+ setUp(() {
+ fakePlatform = FakePlatform()..environment = <String, String>{};
+ mockStdio = MockStdio();
+ botDetector = const BotDetector();
+ });
+
+ group('isRunningOnBot', () {
+ testUsingContext('returns false unconditionally if BOT=false is set', () async {
+ fakePlatform.environment['BOT'] = 'false';
+ fakePlatform.environment['TRAVIS'] = 'true';
+ expect(botDetector.isRunningOnBot, isFalse);
+ }, overrides: <Type, Generator>{
+ Stdio: () => mockStdio,
+ Platform: () => fakePlatform,
+ });
+
+ testUsingContext('returns false unconditionally if FLUTTER_HOST is set', () async {
+ fakePlatform.environment['FLUTTER_HOST'] = 'foo';
+ fakePlatform.environment['TRAVIS'] = 'true';
+ expect(botDetector.isRunningOnBot, isFalse);
+ }, overrides: <Type, Generator>{
+ Stdio: () => mockStdio,
+ Platform: () => fakePlatform,
+ });
+
+ testUsingContext('returns true for non-interactive terminals', () async {
+ mockStdio.stdout.hasTerminal = true;
+ expect(botDetector.isRunningOnBot, isFalse);
+ mockStdio.stdout.hasTerminal = false;
+ expect(botDetector.isRunningOnBot, isTrue);
+ }, overrides: <Type, Generator>{
+ Stdio: () => mockStdio,
+ Platform: () => fakePlatform,
+ });
+ });
+ });
+}
diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart
index 8c318bc..5e93576 100644
--- a/packages/flutter_tools/test/src/mocks.dart
+++ b/packages/flutter_tools/test/src/mocks.dart
@@ -4,7 +4,7 @@
import 'dart:async';
import 'dart:convert';
-import 'dart:io' as io show IOSink, ProcessSignal;
+import 'dart:io' as io show IOSink, ProcessSignal, Stdout, StdoutException;
import 'package:flutter_tools/src/android/android_device.dart';
import 'package:flutter_tools/src/android/android_sdk.dart' show AndroidSdk;
@@ -342,17 +342,56 @@
Future<void> flush() async { }
}
+class MemoryStdout extends MemoryIOSink implements io.Stdout {
+ @override
+ bool get hasTerminal => _hasTerminal;
+ set hasTerminal(bool value) {
+ assert(value != null);
+ _hasTerminal = value;
+ }
+ bool _hasTerminal = true;
+
+ @override
+ io.IOSink get nonBlocking => this;
+
+ @override
+ bool get supportsAnsiEscapes => _supportsAnsiEscapes;
+ set supportsAnsiEscapes(bool value) {
+ assert(value != null);
+ _supportsAnsiEscapes = value;
+ }
+ bool _supportsAnsiEscapes = true;
+
+ @override
+ int get terminalColumns {
+ if (_terminalColumns != null)
+ return _terminalColumns;
+ throw const io.StdoutException('unspecified mock value');
+ }
+ set terminalColumns(int value) => _terminalColumns = value;
+ int _terminalColumns;
+
+ @override
+ int get terminalLines {
+ if (_terminalLines != null)
+ return _terminalLines;
+ throw const io.StdoutException('unspecified mock value');
+ }
+ set terminalLines(int value) => _terminalLines = value;
+ int _terminalLines;
+}
+
/// A Stdio that collects stdout and supports simulated stdin.
class MockStdio extends Stdio {
- final MemoryIOSink _stdout = MemoryIOSink();
+ final MemoryStdout _stdout = MemoryStdout();
final MemoryIOSink _stderr = MemoryIOSink();
final StreamController<List<int>> _stdin = StreamController<List<int>>();
@override
- IOSink get stdout => _stdout;
+ MemoryStdout get stdout => _stdout;
@override
- IOSink get stderr => _stderr;
+ MemoryIOSink get stderr => _stderr;
@override
Stream<List<int>> get stdin => _stdin.stream;