bot_detector_test.dart 6.47 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

7 8
import 'dart:async';

9
import 'package:file/memory.dart';
10
import 'package:flutter_tools/src/base/bot_detector.dart';
11
import 'package:flutter_tools/src/base/io.dart';
12
import 'package:flutter_tools/src/base/logger.dart';
13
import 'package:flutter_tools/src/base/platform.dart';
14
import 'package:flutter_tools/src/persistent_tool_state.dart';
15
import 'package:mockito/mockito.dart';
16
import 'package:fake_async/fake_async.dart';
17

18
import '../../src/common.dart';
19
import '../../src/mocks.dart';
20 21 22 23

void main() {
  group('BotDetector', () {
    FakePlatform fakePlatform;
24
    MockStdio mockStdio;
25 26 27
    MockHttpClient mockHttpClient;
    MockHttpClientRequest mockHttpClientRequest;
    MockHttpHeaders mockHttpHeaders;
28
    BotDetector botDetector;
29
    PersistentToolState persistentToolState;
30 31 32

    setUp(() {
      fakePlatform = FakePlatform()..environment = <String, String>{};
33
      mockStdio = MockStdio();
34 35 36
      mockHttpClient = MockHttpClient();
      mockHttpClientRequest = MockHttpClientRequest();
      mockHttpHeaders = MockHttpHeaders();
37 38 39 40
      persistentToolState = PersistentToolState.test(
        directory: MemoryFileSystem.test().currentDirectory,
        logger: BufferLogger.test(),
      );
41 42 43
      botDetector = BotDetector(
        platform: fakePlatform,
        httpClientFactory: () => mockHttpClient,
44
        persistentToolState: persistentToolState,
45
      );
46 47 48
    });

    group('isRunningOnBot', () {
49
      testWithoutContext('returns false unconditionally if BOT=false is set', () async {
50 51
        fakePlatform.environment['BOT'] = 'false';
        fakePlatform.environment['TRAVIS'] = 'true';
52

53
        expect(await botDetector.isRunningOnBot, isFalse);
54
        expect(persistentToolState.isRunningOnBot, isFalse);
55 56
      });

57
      testWithoutContext('returns false unconditionally if FLUTTER_HOST is set', () async {
58 59
        fakePlatform.environment['FLUTTER_HOST'] = 'foo';
        fakePlatform.environment['TRAVIS'] = 'true';
60

61
        expect(await botDetector.isRunningOnBot, isFalse);
62
        expect(persistentToolState.isRunningOnBot, isFalse);
63 64
      });

65 66 67 68 69 70 71 72 73 74 75
      testWithoutContext('returns false with and without a terminal attached', () async {
        when(mockHttpClient.getUrl(any)).thenAnswer((_) {
          throw const SocketException('HTTP connection timed out');
        });
        mockStdio.stdout.hasTerminal = true;
        expect(await botDetector.isRunningOnBot, isFalse);
        mockStdio.stdout.hasTerminal = false;
        expect(await botDetector.isRunningOnBot, isFalse);
        expect(persistentToolState.isRunningOnBot, isFalse);
      });

76
      testWithoutContext('can test analytics outputs on bots when outputting to a file', () async {
77 78
        fakePlatform.environment['TRAVIS'] = 'true';
        fakePlatform.environment['FLUTTER_ANALYTICS_LOG_FILE'] = '/some/file';
79
        expect(await botDetector.isRunningOnBot, isFalse);
80
        expect(persistentToolState.isRunningOnBot, isFalse);
81
      });
82 83 84 85 86 87

      testWithoutContext('returns true when azure metadata is reachable', () async {
        when(mockHttpClient.getUrl(any)).thenAnswer((_) {
          return Future<HttpClientRequest>.value(mockHttpClientRequest);
        });
        when(mockHttpClientRequest.headers).thenReturn(mockHttpHeaders);
88

89
        expect(await botDetector.isRunningOnBot, isTrue);
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
        expect(persistentToolState.isRunningOnBot, isTrue);
      });

      testWithoutContext('caches azure bot detection results across instances', () async {
        when(mockHttpClient.getUrl(any)).thenAnswer((_) {
          return Future<HttpClientRequest>.value(mockHttpClientRequest);
        });
        when(mockHttpClientRequest.headers).thenReturn(mockHttpHeaders);

        expect(await botDetector.isRunningOnBot, isTrue);
        expect(await BotDetector(
          platform: fakePlatform,
          httpClientFactory: () => mockHttpClient,
          persistentToolState: persistentToolState,
        ).isRunningOnBot, isTrue);
        verify(mockHttpClient.getUrl(any)).called(1);
106
      });
107 108 109

      testWithoutContext('returns true when running on borg', () async {
        fakePlatform.environment['BORG_ALLOC_DIR'] = 'true';
110

111
        expect(await botDetector.isRunningOnBot, isTrue);
112
        expect(persistentToolState.isRunningOnBot, isTrue);
113
      });
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
    });
  });

  group('AzureDetector', () {
    AzureDetector azureDetector;
    MockHttpClient mockHttpClient;
    MockHttpClientRequest mockHttpClientRequest;
    MockHttpHeaders mockHttpHeaders;

    setUp(() {
      mockHttpClient = MockHttpClient();
      mockHttpClientRequest = MockHttpClientRequest();
      mockHttpHeaders = MockHttpHeaders();
      azureDetector = AzureDetector(
        httpClientFactory: () => mockHttpClient,
      );
    });

    testWithoutContext('isRunningOnAzure returns false when connection times out', () async {
      when(mockHttpClient.getUrl(any)).thenAnswer((_) {
        throw const SocketException('HTTP connection timed out');
      });
136

137 138 139
      expect(await azureDetector.isRunningOnAzure, isFalse);
    });

140 141 142 143 144 145 146 147 148 149 150 151 152
    testWithoutContext('isRunningOnAzure returns false when the http request times out', () {
      FakeAsync().run((FakeAsync time) async {
        when(mockHttpClient.getUrl(any)).thenAnswer((_) {
          final Completer<HttpClientRequest> completer = Completer<HttpClientRequest>();
          return completer.future;  // Never completed to test timeout behavior.
        });
        final Future<bool> onBot = azureDetector.isRunningOnAzure;
        time.elapse(const Duration(seconds: 2));

        expect(await onBot, isFalse);
      });
    });

153 154 155 156 157 158 159 160
    testWithoutContext('isRunningOnAzure returns false when OsError is thrown', () async {
      when(mockHttpClient.getUrl(any)).thenAnswer((_) {
        throw const OSError('Connection Refused', 111);
      });

      expect(await azureDetector.isRunningOnAzure, isFalse);
    });

161 162 163 164 165
    testWithoutContext('isRunningOnAzure returns true when azure metadata is reachable', () async {
      when(mockHttpClient.getUrl(any)).thenAnswer((_) {
        return Future<HttpClientRequest>.value(mockHttpClientRequest);
      });
      when(mockHttpClientRequest.headers).thenReturn(mockHttpHeaders);
166

167
      expect(await azureDetector.isRunningOnAzure, isTrue);
168 169 170
    });
  });
}
171 172 173 174

class MockHttpClient extends Mock implements HttpClient {}
class MockHttpClientRequest extends Mock implements HttpClientRequest {}
class MockHttpHeaders extends Mock implements HttpHeaders {}