protocol_discovery_test.dart 16.4 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
import 'package:fake_async/fake_async.dart';
8
import 'package:flutter_tools/src/base/logger.dart';
9
import 'package:flutter_tools/src/device_port_forwarder.dart';
10
import 'package:flutter_tools/src/protocol_discovery.dart';
11

12
import '../src/common.dart';
13
import '../src/fake_devices.dart';
14

15
void main() {
16
  group('service_protocol discovery', () {
17
    FakeDeviceLogReader logReader;
18 19
    ProtocolDiscovery discoverer;

20
    setUp(() {
21
      logReader = FakeDeviceLogReader();
22 23 24 25
      discoverer = ProtocolDiscovery.observatory(
        logReader,
        ipv6: false,
        hostPort: null,
26
        devicePort: null,
27
        throttleDuration: const Duration(milliseconds: 5),
28
        logger: BufferLogger.test(),
29
      );
30
    });
31

32
    testWithoutContext('returns non-null uri future', () async {
33 34
      expect(discoverer.uri, isNotNull);
    });
35

36
    group('no port forwarding', () {
37 38 39 40 41
      tearDown(() {
        discoverer.cancel();
        logReader.dispose();
      });

42
      testWithoutContext('discovers uri if logs already produced output', () async {
43 44 45 46 47
        logReader.addLine('HELLO WORLD');
        logReader.addLine('Observatory listening on http://127.0.0.1:9999');
        final Uri uri = await discoverer.uri;
        expect(uri.port, 9999);
        expect('$uri', 'http://127.0.0.1:9999');
48 49
      });

50 51 52 53 54 55 56 57 58 59 60 61 62 63
      testWithoutContext('does not discover uri with no host', () async {
        final Future<Uri> pendingUri = discoverer.uri;
        logReader.addLine('Observatory listening on http12asdasdsd9999');
        await Future<void>.delayed(const Duration(milliseconds: 10));
        logReader.addLine('Observatory listening on http://127.0.0.1:9999');

        await Future<void>.delayed(Duration.zero);

        final Uri uri = await pendingUri;
        expect(uri, isNotNull);
        expect(uri.port, 9999);
        expect('$uri', 'http://127.0.0.1:9999');
      });

64
      testWithoutContext('discovers uri if logs already produced output and no listener is attached', () async {
65 66
        logReader.addLine('HELLO WORLD');
        logReader.addLine('Observatory listening on http://127.0.0.1:9999');
67 68 69

        await Future<void>.delayed(Duration.zero);

70
        final Uri uri = await discoverer.uri;
71
        expect(uri, isNotNull);
72 73 74 75
        expect(uri.port, 9999);
        expect('$uri', 'http://127.0.0.1:9999');
      });

76
      testWithoutContext('uri throws if logs produce bad line and no listener is attached', () async {
77 78 79 80 81 82 83
        logReader.addLine('Observatory listening on http://127.0.0.1:apple');

        await Future<void>.delayed(Duration.zero);

        expect(discoverer.uri, throwsA(isFormatException));
      });

84
      testWithoutContext('discovers uri if logs not yet produced output', () async {
85 86 87 88 89 90 91
        final Future<Uri> uriFuture = discoverer.uri;
        logReader.addLine('Observatory listening on http://127.0.0.1:3333');
        final Uri uri = await uriFuture;
        expect(uri.port, 3333);
        expect('$uri', 'http://127.0.0.1:3333');
      });

92
      testWithoutContext('discovers uri with Ascii Esc code', () async {
93
        logReader.addLine('Observatory listening on http://127.0.0.1:3333\x1b[');
94 95 96 97 98
        final Uri uri = await discoverer.uri;
        expect(uri.port, 3333);
        expect('$uri', 'http://127.0.0.1:3333');
      });

99
      testWithoutContext('uri throws if logs produce bad line', () async {
100
        logReader.addLine('Observatory listening on http://127.0.0.1:apple');
101 102 103
        expect(discoverer.uri, throwsA(isFormatException));
      });

104
      testWithoutContext('uri is null when the log reader closes early', () async {
105 106 107 108 109 110
        final Future<Uri> uriFuture = discoverer.uri;
        await logReader.dispose();

        expect(await uriFuture, isNull);
      });

111
      testWithoutContext('uri waits for correct log line', () async {
112 113 114 115
        final Future<Uri> uriFuture = discoverer.uri;
        logReader.addLine('Observatory not listening...');
        final Uri timeoutUri = Uri.parse('http://timeout');
        final Uri actualUri = await uriFuture.timeout(
116 117 118
          const Duration(milliseconds: 100),
          onTimeout: () => timeoutUri,
        );
119 120 121
        expect(actualUri, timeoutUri);
      });

122
      testWithoutContext('discovers uri if log line contains Android prefix', () async {
123 124 125 126 127 128
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:52584');
        final Uri uri = await discoverer.uri;
        expect(uri.port, 52584);
        expect('$uri', 'http://127.0.0.1:52584');
      });

129
      testWithoutContext('discovers uri if log line contains auth key', () async {
130 131 132 133 134 135 136
        final Future<Uri> uriFuture = discoverer.uri;
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
        final Uri uri = await uriFuture;
        expect(uri.port, 54804);
        expect('$uri', 'http://127.0.0.1:54804/PTwjm8Ii8qg=/');
      });

137
      testWithoutContext('discovers uri if log line contains non-localhost', () async {
138
        final Future<Uri> uriFuture = discoverer.uri;
139
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
140 141
        final Uri uri = await uriFuture;
        expect(uri.port, 54804);
142
        expect('$uri', 'http://127.0.0.1:54804/PTwjm8Ii8qg=/');
143
      });
144

145 146 147 148 149 150 151 152 153
      testWithoutContext('skips uri if port does not match the requested vmservice - requested last', () async {
        discoverer = ProtocolDiscovery.observatory(
          logReader,
          ipv6: false,
          hostPort: null,
          devicePort: 12346,
          throttleDuration: const Duration(milliseconds: 200),
          logger: BufferLogger.test(),
        );
154 155 156 157 158 159 160 161
        final Future<Uri> uriFuture = discoverer.uri;
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/');
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/');
        final Uri uri = await uriFuture;
        expect(uri.port, 12346);
        expect('$uri', 'http://127.0.0.1:12346/PTwjm8Ii8qg=/');
      });

162 163 164 165 166 167 168 169 170
      testWithoutContext('skips uri if port does not match the requested vmservice - requested first', () async {
        discoverer = ProtocolDiscovery.observatory(
          logReader,
          ipv6: false,
          hostPort: null,
          devicePort: 12346,
          throttleDuration: const Duration(milliseconds: 200),
          logger: BufferLogger.test(),
        );
171 172 173 174 175 176 177
        final Future<Uri> uriFuture = discoverer.uri;
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/');
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/');
        final Uri uri = await uriFuture;
        expect(uri.port, 12346);
        expect('$uri', 'http://127.0.0.1:12346/PTwjm8Ii8qg=/');
      });
178

179
      testWithoutContext('first uri in the stream is the last one from the log', () async {
180 181 182 183 184 185 186
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/');
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/');
        final Uri uri = await discoverer.uris.first;
        expect(uri.port, 12345);
        expect('$uri', 'http://127.0.0.1:12345/PTwjm8Ii8qg=/');
      });

187 188 189 190 191 192 193 194 195
      testWithoutContext('first uri in the stream is the last one from the log that matches the port', () async {
        discoverer = ProtocolDiscovery.observatory(
          logReader,
          ipv6: false,
          hostPort: null,
          devicePort: 12345,
          throttleDuration: const Duration(milliseconds: 200),
          logger: BufferLogger.test(),
        );
196 197 198 199 200 201 202 203
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/');
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/');
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12344/PTwjm8Ii8qg=/');
        final Uri uri = await discoverer.uris.first;
        expect(uri.port, 12345);
        expect('$uri', 'http://127.0.0.1:12345/PTwjm8Ii8qg=/');
      });

204 205 206 207 208 209 210 211 212
      testWithoutContext('protocol discovery does not crash if the log reader is closed while delaying', () async {
        discoverer = ProtocolDiscovery.observatory(
          logReader,
          ipv6: false,
          hostPort: null,
          devicePort: 12346,
          throttleDuration: const Duration(milliseconds: 10),
          logger: BufferLogger.test(),
        );
213 214 215 216 217 218 219 220 221 222
        final Future<List<Uri>> results = discoverer.uris.toList();
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/');
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/');
        await logReader.dispose();

        // Give time for throttle to finish.
        await Future<void>.delayed(const Duration(milliseconds: 11));
        expect(await results, isEmpty);
      });

223
      testWithoutContext('uris in the stream are throttled', () async {
224 225 226
        const Duration kThrottleDuration = Duration(milliseconds: 10);

        FakeAsync().run((FakeAsync time) {
227 228 229 230 231 232 233 234
          discoverer = ProtocolDiscovery.observatory(
            logReader,
            ipv6: false,
            hostPort: null,
            devicePort: null,
            throttleDuration: kThrottleDuration,
            logger: BufferLogger.test(),
          );
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258

          final List<Uri> discoveredUris = <Uri>[];
          discoverer.uris.listen((Uri uri) {
            discoveredUris.add(uri);
          });

          logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/');
          logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/');

          time.elapse(kThrottleDuration);

          logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12344/PTwjm8Ii8qg=/');
          logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12343/PTwjm8Ii8qg=/');

          time.elapse(kThrottleDuration);

          expect(discoveredUris.length, 2);
          expect(discoveredUris[0].port, 12345);
          expect('${discoveredUris[0]}', 'http://127.0.0.1:12345/PTwjm8Ii8qg=/');
          expect(discoveredUris[1].port, 12343);
          expect('${discoveredUris[1]}', 'http://127.0.0.1:12343/PTwjm8Ii8qg=/');
        });
      });

259
      testWithoutContext('uris in the stream are throttled when they match the port', () async {
260 261 262
        const Duration kThrottleTimeInMilliseconds = Duration(milliseconds: 10);

        FakeAsync().run((FakeAsync time) {
263 264 265 266
          discoverer = ProtocolDiscovery.observatory(
            logReader,
            ipv6: false,
            hostPort: null,
267 268
            devicePort: 12345,
            throttleDuration: kThrottleTimeInMilliseconds,
269
            logger: BufferLogger.test(),
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
          );

          final List<Uri> discoveredUris = <Uri>[];
          discoverer.uris.listen((Uri uri) {
            discoveredUris.add(uri);
          });

          logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12346/PTwjm8Ii8qg=/');
          logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qg=/');

          time.elapse(kThrottleTimeInMilliseconds);

          logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12345/PTwjm8Ii8qc=/');
          logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:12344/PTwjm8Ii8qf=/');

          time.elapse(kThrottleTimeInMilliseconds);

          expect(discoveredUris.length, 2);
          expect(discoveredUris[0].port, 12345);
          expect('${discoveredUris[0]}', 'http://127.0.0.1:12345/PTwjm8Ii8qg=/');
          expect(discoveredUris[1].port, 12345);
          expect('${discoveredUris[1]}', 'http://127.0.0.1:12345/PTwjm8Ii8qc=/');
        });
      });
294
    });
295

296
    group('port forwarding', () {
297
      testWithoutContext('default port', () async {
298
        final FakeDeviceLogReader logReader = FakeDeviceLogReader();
299
        final ProtocolDiscovery discoverer = ProtocolDiscovery.observatory(
300
          logReader,
301
          portForwarder: MockPortForwarder(99),
302 303 304
          hostPort: null,
          devicePort: null,
          ipv6: false,
305
          logger: BufferLogger.test(),
306
        );
307 308 309 310 311

        // Get next port future.
        final Future<Uri> nextUri = discoverer.uri;
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
        final Uri uri = await nextUri;
312 313
        expect(uri.port, 99);
        expect('$uri', 'http://127.0.0.1:99/PTwjm8Ii8qg=/');
314

315
        await discoverer.cancel();
316
        await logReader.dispose();
317 318
      });

319
      testWithoutContext('specified port', () async {
320
        final FakeDeviceLogReader logReader = FakeDeviceLogReader();
321
        final ProtocolDiscovery discoverer = ProtocolDiscovery.observatory(
322
          logReader,
323
          portForwarder: MockPortForwarder(99),
324
          hostPort: 1243,
325 326
          devicePort: null,
          ipv6: false,
327
          logger: BufferLogger.test(),
328
        );
329 330 331 332 333 334 335 336

        // Get next port future.
        final Future<Uri> nextUri = discoverer.uri;
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
        final Uri uri = await nextUri;
        expect(uri.port, 1243);
        expect('$uri', 'http://127.0.0.1:1243/PTwjm8Ii8qg=/');

337
        await discoverer.cancel();
338
        await logReader.dispose();
339 340
      });

341
      testWithoutContext('specified port zero', () async {
342
        final FakeDeviceLogReader logReader = FakeDeviceLogReader();
343
        final ProtocolDiscovery discoverer = ProtocolDiscovery.observatory(
344
          logReader,
345
          portForwarder: MockPortForwarder(99),
346
          hostPort: 0,
347 348
          devicePort: null,
          ipv6: false,
349
          logger: BufferLogger.test(),
350 351 352 353 354 355 356 357 358
        );

        // Get next port future.
        final Future<Uri> nextUri = discoverer.uri;
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
        final Uri uri = await nextUri;
        expect(uri.port, 99);
        expect('$uri', 'http://127.0.0.1:99/PTwjm8Ii8qg=/');

359
        await discoverer.cancel();
360
        await logReader.dispose();
361 362
      });

363
      testWithoutContext('ipv6', () async {
364
        final FakeDeviceLogReader logReader = FakeDeviceLogReader();
365
        final ProtocolDiscovery discoverer = ProtocolDiscovery.observatory(
366
          logReader,
367
          portForwarder: MockPortForwarder(99),
368 369
          hostPort: 54777,
          ipv6: true,
370
          devicePort: null,
371
          logger: BufferLogger.test(),
372
        );
373

374 375 376 377 378 379 380
        // Get next port future.
        final Future<Uri> nextUri = discoverer.uri;
        logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
        final Uri uri = await nextUri;
        expect(uri.port, 54777);
        expect('$uri', 'http://[::1]:54777/PTwjm8Ii8qg=/');

381
        await discoverer.cancel();
382
        await logReader.dispose();
383
      });
384

385
      testWithoutContext('ipv6 with Ascii Escape code', () async {
386
        final FakeDeviceLogReader logReader = FakeDeviceLogReader();
387
        final ProtocolDiscovery discoverer = ProtocolDiscovery.observatory(
388
          logReader,
389
          portForwarder: MockPortForwarder(99),
390 391
          hostPort: 54777,
          ipv6: true,
392
          devicePort: null,
393
          logger: BufferLogger.test(),
394 395 396 397 398 399 400 401 402
        );

        // Get next port future.
        final Future<Uri> nextUri = discoverer.uri;
        logReader.addLine('I/flutter : Observatory listening on http://[::1]:54777/PTwjm8Ii8qg=/\x1b[');
        final Uri uri = await nextUri;
        expect(uri.port, 54777);
        expect('$uri', 'http://[::1]:54777/PTwjm8Ii8qg=/');

403
        await discoverer.cancel();
404
        await logReader.dispose();
405
      });
406
    });
407 408
  });
}
409 410 411 412

class MockPortForwarder extends DevicePortForwarder {
  MockPortForwarder([this.availablePort]);

413 414
  final int availablePort;

415
  @override
416
  Future<int> forward(int devicePort, { int hostPort }) async {
417
    hostPort ??= 0;
418
    if (hostPort == 0) {
419
      return availablePort;
420
    }
421 422
    return hostPort;
  }
423 424 425 426 427

  @override
  List<ForwardedPort> get forwardedPorts => throw 'not implemented';

  @override
428
  Future<void> unforward(ForwardedPort forwardedPort) {
429 430
    throw 'not implemented';
  }
431 432 433

  @override
  Future<void> dispose() async {}
434
}