flutter_driver_test.dart 24.5 KB
Newer Older
1 2 3 4 5
// Copyright 2016 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 'dart:async';
6

7 8
import 'package:flutter_driver/src/common/error.dart';
import 'package:flutter_driver/src/common/health.dart';
9
import 'package:flutter_driver/src/common/wait.dart';
10 11
import 'package:flutter_driver/src/driver/driver.dart';
import 'package:flutter_driver/src/driver/timeline.dart';
12
import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
13
import 'package:mockito/mockito.dart';
14
import 'package:vm_service_client/vm_service_client.dart';
15
import 'package:quiver/testing/async.dart';
16

17 18
import 'common.dart';

19
/// Magical timeout value that's different from the default.
20
const Duration _kTestTimeout = Duration(milliseconds: 1234);
21 22
const String _kSerializedTestTimeout = '1234';

23
void main() {
24 25
  group('FlutterDriver.connect', () {
    List<LogRecord> log;
Ian Hickson's avatar
Ian Hickson committed
26
    StreamSubscription<LogRecord> logSub;
27 28 29
    MockVMServiceClient mockClient;
    MockVM mockVM;
    MockIsolate mockIsolate;
30
    MockPeer mockPeer;
31

32
    void expectLogContains(String message) {
33
      expect(log.map<String>((LogRecord r) => '$r'), anyElement(contains(message)));
34
    }
35

36 37 38
    setUp(() {
      log = <LogRecord>[];
      logSub = flutterDriverLog.listen(log.add);
39 40 41 42 43
      mockClient = MockVMServiceClient();
      mockVM = MockVM();
      mockIsolate = MockIsolate();
      mockPeer = MockPeer();
      when(mockClient.getVM()).thenAnswer((_) => Future<MockVM>.value(mockVM));
pq's avatar
pq committed
44
      when(mockVM.isolates).thenReturn(<VMRunnableIsolate>[mockIsolate]);
45
      when(mockIsolate.loadRunnable()).thenAnswer((_) => Future<MockIsolate>.value(mockIsolate));
46
      when(mockIsolate.invokeExtension(any, any)).thenAnswer(
47
          (Invocation invocation) => makeMockResponse(<String, dynamic>{'status': 'ok'}));
48
      vmServiceConnectFunction = (String url) {
49 50
        return Future<VMServiceClientConnection>.value(
          VMServiceClientConnection(mockClient, mockPeer)
51 52
        );
      };
53 54 55 56
    });

    tearDown(() async {
      await logSub.cancel();
yjbanov's avatar
yjbanov committed
57
      restoreVmServiceConnectFunction();
58 59 60
    });

    test('connects to isolate paused at start', () async {
61
      final List<String> connectionLog = <String>[];
62
      when(mockPeer.sendRequest('streamListen', any)).thenAnswer((Invocation invocation) {
63 64 65
        connectionLog.add('streamListen');
        return null;
      });
66
      when(mockIsolate.pauseEvent).thenReturn(MockVMPauseStartEvent());
67
      when(mockIsolate.resume()).thenAnswer((Invocation invocation) {
68
        connectionLog.add('resume');
69
        return Future<dynamic>.value(null);
70
      });
71
      when(mockIsolate.onExtensionAdded).thenAnswer((Invocation invocation) {
72
        connectionLog.add('onExtensionAdded');
73
        return Stream<String>.fromIterable(<String>['ext.flutter.driver']);
74
      });
75

76
      final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '');
77 78
      expect(driver, isNotNull);
      expectLogContains('Isolate is paused at start');
79
      expect(connectionLog, <String>['streamListen', 'onExtensionAdded', 'resume']);
80 81 82
    });

    test('connects to isolate paused mid-flight', () async {
83
      when(mockIsolate.pauseEvent).thenReturn(MockVMPauseBreakpointEvent());
84
      when(mockIsolate.resume()).thenAnswer((Invocation invocation) => Future<dynamic>.value(null));
85

86
      final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '');
87 88 89 90 91 92 93 94 95
      expect(driver, isNotNull);
      expectLogContains('Isolate is paused mid-flight');
    });

    // This test simulates a situation when we believe that the isolate is
    // currently paused, but something else (e.g. a debugger) resumes it before
    // we do. There's no need to fail as we should be able to drive the app
    // just fine.
    test('connects despite losing the race to resume isolate', () async {
96
      when(mockIsolate.pauseEvent).thenReturn(MockVMPauseBreakpointEvent());
97
      when(mockIsolate.resume()).thenAnswer((Invocation invocation) {
98 99
        // This needs to be wrapped in a closure to not be considered uncaught
        // by package:test
100
        return Future<dynamic>.error(rpc.RpcException(101, ''));
101 102
      });

103
      final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '');
104 105 106 107 108
      expect(driver, isNotNull);
      expectLogContains('Attempted to resume an already resumed isolate');
    });

    test('connects to unpaused isolate', () async {
109
      when(mockIsolate.pauseEvent).thenReturn(MockVMResumeEvent());
110
      final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '');
111 112 113 114 115 116 117
      expect(driver, isNotNull);
      expectLogContains('Isolate is not paused. Assuming application is ready.');
    });
  });

  group('FlutterDriver', () {
    MockVMServiceClient mockClient;
118
    MockPeer mockPeer;
119 120 121 122
    MockIsolate mockIsolate;
    FlutterDriver driver;

    setUp(() {
123 124 125 126
      mockClient = MockVMServiceClient();
      mockPeer = MockPeer();
      mockIsolate = MockIsolate();
      driver = FlutterDriver.connectedTo(mockClient, mockPeer, mockIsolate);
127 128 129
    });

    test('checks the health of the driver extension', () async {
130
      when(mockIsolate.invokeExtension(any, any)).thenAnswer(
131
          (Invocation invocation) => makeMockResponse(<String, dynamic>{'status': 'ok'}));
132
      final Health result = await driver.checkHealth();
133 134 135 136
      expect(result.status, HealthStatus.ok);
    });

    test('closes connection', () async {
137
      when(mockClient.close()).thenAnswer((Invocation invocation) => Future<dynamic>.value(null));
138 139 140
      await driver.close();
    });

141
    group('ByValueKey', () {
142
      test('restricts value types', () async {
143
        expect(() => find.byValueKey(null),
144
            throwsA(isInstanceOf<DriverError>()));
145 146 147
      });

      test('finds by ValueKey', () async {
148
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
pq's avatar
pq committed
149
          expect(i.positionalArguments[1], <String, String>{
150
            'command': 'tap',
151
            'timeout': _kSerializedTestTimeout,
152
            'finderType': 'ByValueKey',
153
            'keyValueString': 'foo',
154
            'keyValueType': 'String',
155
          });
156
          return makeMockResponse(<String, dynamic>{});
157
        });
158
        await driver.tap(find.byValueKey('foo'), timeout: _kTestTimeout);
159 160 161
      });
    });

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
    group('BySemanticsLabel', () {
      test('finds by Semantic label using String', () async {
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
          expect(i.positionalArguments[1], <String, String>{
            'command': 'tap',
            'timeout': _kSerializedTestTimeout,
            'finderType': 'BySemanticsLabel',
            'label': 'foo',
          });
          return makeMockResponse(<String, dynamic>{});
        });
        await driver.tap(find.bySemanticsLabel('foo'), timeout: _kTestTimeout);
      });

      test('finds by Semantic label using RegExp', () async {
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
          expect(i.positionalArguments[1], <String, String>{
            'command': 'tap',
            'timeout': _kSerializedTestTimeout,
            'finderType': 'BySemanticsLabel',
            'label': '^foo',
            'isRegExp': 'true',
          });
          return makeMockResponse(<String, dynamic>{});
        });
        await driver.tap(find.bySemanticsLabel(RegExp('^foo')), timeout: _kTestTimeout);
      });
    });

191 192
    group('tap', () {
      test('requires a target reference', () async {
193
        expect(driver.tap(null), throwsA(isInstanceOf<DriverError>()));
194 195 196
      });

      test('sends the tap command', () async {
197
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
Ian Hickson's avatar
Ian Hickson committed
198
          expect(i.positionalArguments[1], <String, dynamic>{
199
            'command': 'tap',
200
            'timeout': _kSerializedTestTimeout,
201 202
            'finderType': 'ByText',
            'text': 'foo',
203
          });
204
          return makeMockResponse(<String, dynamic>{});
205
        });
206
        await driver.tap(find.text('foo'), timeout: _kTestTimeout);
207 208 209 210 211
      });
    });

    group('getText', () {
      test('requires a target reference', () async {
212
        expect(driver.getText(null), throwsA(isInstanceOf<DriverError>()));
213 214 215
      });

      test('sends the getText command', () async {
216
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
Ian Hickson's avatar
Ian Hickson committed
217
          expect(i.positionalArguments[1], <String, dynamic>{
218
            'command': 'get_text',
219
            'timeout': _kSerializedTestTimeout,
220 221
            'finderType': 'ByValueKey',
            'keyValueString': '123',
222
            'keyValueType': 'int',
223
          });
224
          return makeMockResponse(<String, String>{
225
            'text': 'hello',
226 227
          });
        });
228
        final String result = await driver.getText(find.byValueKey(123), timeout: _kTestTimeout);
229 230 231
        expect(result, 'hello');
      });
    });
232 233

    group('waitFor', () {
234
      test('requires a target reference', () async {
235
        expect(driver.waitFor(null), throwsA(isInstanceOf<DriverError>()));
236 237
      });

238
      test('sends the waitFor command', () async {
239
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
240 241 242 243
          expect(i.positionalArguments[1], <String, dynamic>{
            'command': 'waitFor',
            'finderType': 'ByTooltipMessage',
            'text': 'foo',
244
            'timeout': _kSerializedTestTimeout,
245
          });
246
          return makeMockResponse(<String, dynamic>{});
247
        });
248
        await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout);
249 250
      });
    });
251

252 253 254 255 256 257 258 259 260 261 262 263 264
    group('waitForCondition', () {
      test('sends the wait for NoPendingFrameCondition command', () async {
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
          expect(i.positionalArguments[1], <String, dynamic>{
            'command': 'waitForCondition',
            'timeout': _kSerializedTestTimeout,
            'conditionName': 'NoPendingFrameCondition',
          });
          return makeMockResponse(<String, dynamic>{});
        });
        await driver.waitForCondition(const NoPendingFrame(), timeout: _kTestTimeout);
      });

265 266 267 268 269 270 271 272 273 274 275 276
      test('sends the wait for NoPendingPlatformMessages command', () async {
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
          expect(i.positionalArguments[1], <String, dynamic>{
            'command': 'waitForCondition',
            'timeout': _kSerializedTestTimeout,
            'conditionName': 'NoPendingPlatformMessagesCondition',
          });
          return makeMockResponse(<String, dynamic>{});
        });
        await driver.waitForCondition(const NoPendingPlatformMessages(), timeout: _kTestTimeout);
      });

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
      test('sends the waitForCondition of combined conditions command', () async {
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
          expect(i.positionalArguments[1], <String, dynamic>{
            'command': 'waitForCondition',
            'timeout': _kSerializedTestTimeout,
            'conditionName': 'CombinedCondition',
            'conditions': '[{"conditionName":"NoPendingFrameCondition"},{"conditionName":"NoTransientCallbacksCondition"}]',
          });
          return makeMockResponse(<String, dynamic>{});
        });
        const SerializableWaitCondition combinedCondition =
            CombinedCondition(<SerializableWaitCondition>[NoPendingFrame(), NoTransientCallbacks()]);
        await driver.waitForCondition(combinedCondition, timeout: _kTestTimeout);
      });
    });

293 294
    group('waitUntilNoTransientCallbacks', () {
      test('sends the waitUntilNoTransientCallbacks command', () async {
295
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
296
          expect(i.positionalArguments[1], <String, dynamic>{
297
            'command': 'waitForCondition',
298
            'timeout': _kSerializedTestTimeout,
299
            'conditionName': 'NoTransientCallbacksCondition',
300 301 302
          });
          return makeMockResponse(<String, dynamic>{});
        });
303
        await driver.waitUntilNoTransientCallbacks(timeout: _kTestTimeout);
304 305
      });
    });
306 307 308 309 310 311 312 313 314 315 316 317 318

    group('waitUntilFirstFrameRasterized', () {
      test('sends the waitUntilFirstFrameRasterized command', () async {
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
          expect(i.positionalArguments[1], <String, dynamic>{
            'command': 'waitForCondition',
            'conditionName': 'FirstFrameRasterizedCondition',
          });
          return makeMockResponse(<String, dynamic>{});
        });
        await driver.waitUntilFirstFrameRasterized();
      });
    });
319

320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
    group('getOffset', () {
      test('requires a target reference', () async {
        expect(driver.getCenter(null), throwsA(isInstanceOf<DriverError>()));
        expect(driver.getTopLeft(null), throwsA(isInstanceOf<DriverError>()));
        expect(driver.getTopRight(null), throwsA(isInstanceOf<DriverError>()));
        expect(driver.getBottomLeft(null), throwsA(isInstanceOf<DriverError>()));
        expect(driver.getBottomRight(null), throwsA(isInstanceOf<DriverError>()));
      });

      test('sends the getCenter command', () async {
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
          expect(i.positionalArguments[1], <String, dynamic>{
            'command': 'get_offset',
            'offsetType': 'center',
            'timeout': _kSerializedTestTimeout,
            'finderType': 'ByValueKey',
            'keyValueString': '123',
            'keyValueType': 'int',
          });
          return makeMockResponse(<String, double>{
            'dx': 11,
            'dy': 12,
          });
        });
        final DriverOffset result = await driver.getCenter(find.byValueKey(123), timeout: _kTestTimeout);
        expect(result, const DriverOffset(11, 12));
      });

      test('sends the getTopLeft command', () async {
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
          expect(i.positionalArguments[1], <String, dynamic>{
            'command': 'get_offset',
            'offsetType': 'topLeft',
            'timeout': _kSerializedTestTimeout,
            'finderType': 'ByValueKey',
            'keyValueString': '123',
            'keyValueType': 'int',
          });
          return makeMockResponse(<String, double>{
            'dx': 11,
            'dy': 12,
          });
        });
        final DriverOffset result = await driver.getTopLeft(find.byValueKey(123), timeout: _kTestTimeout);
        expect(result, const DriverOffset(11, 12));
      });

      test('sends the getTopRight command', () async {
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
          expect(i.positionalArguments[1], <String, dynamic>{
            'command': 'get_offset',
            'offsetType': 'topRight',
            'timeout': _kSerializedTestTimeout,
            'finderType': 'ByValueKey',
            'keyValueString': '123',
            'keyValueType': 'int',
          });
          return makeMockResponse(<String, double>{
            'dx': 11,
            'dy': 12,
          });
        });
        final DriverOffset result = await driver.getTopRight(find.byValueKey(123), timeout: _kTestTimeout);
        expect(result, const DriverOffset(11, 12));
      });

      test('sends the getBottomLeft command', () async {
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
          expect(i.positionalArguments[1], <String, dynamic>{
            'command': 'get_offset',
            'offsetType': 'bottomLeft',
            'timeout': _kSerializedTestTimeout,
            'finderType': 'ByValueKey',
            'keyValueString': '123',
            'keyValueType': 'int',
          });
          return makeMockResponse(<String, double>{
            'dx': 11,
            'dy': 12,
          });
        });
        final DriverOffset result = await driver.getBottomLeft(find.byValueKey(123), timeout: _kTestTimeout);
        expect(result, const DriverOffset(11, 12));
      });

      test('sends the getBottomRight command', () async {
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
          expect(i.positionalArguments[1], <String, dynamic>{
            'command': 'get_offset',
            'offsetType': 'bottomRight',
            'timeout': _kSerializedTestTimeout,
            'finderType': 'ByValueKey',
            'keyValueString': '123',
            'keyValueType': 'int',
          });
          return makeMockResponse(<String, double>{
            'dx': 11,
            'dy': 12,
          });
        });
        final DriverOffset result = await driver.getBottomRight(find.byValueKey(123), timeout: _kTestTimeout);
        expect(result, const DriverOffset(11, 12));
      });
    });

425 426 427
    group('clearTimeline', () {
      test('clears timeline', () async {
        bool clearWasCalled = false;
428
        when(mockPeer.sendRequest('clearVMTimeline', argThat(equals(<String, dynamic>{}))))
429 430 431 432
          .thenAnswer((Invocation invocation) async {
            clearWasCalled = true;
            return null;
          });
433 434 435 436 437
        await driver.clearTimeline();
        expect(clearWasCalled, isTrue);
      });
    });

438
    group('traceAction', () {
439 440 441 442 443
      List<String> log;

      setUp(() async {
        log = <String>[];

444
        when(mockPeer.sendRequest('clearVMTimeline', argThat(equals(<String, dynamic>{}))))
445 446 447 448
          .thenAnswer((Invocation invocation) async {
            log.add('clear');
            return null;
          });
449

450
        when(mockPeer.sendRequest('setVMTimelineFlags', argThat(equals(<String, dynamic>{'recordedStreams': '[all]'}))))
451 452 453 454
          .thenAnswer((Invocation invocation) async {
            log.add('startTracing');
            return null;
          });
455

456
        when(mockPeer.sendRequest('setVMTimelineFlags', argThat(equals(<String, dynamic>{'recordedStreams': '[]'}))))
457 458 459 460
          .thenAnswer((Invocation invocation) async {
            log.add('stopTracing');
            return null;
          });
461

462
        when(mockPeer.sendRequest('getVMTimeline')).thenAnswer((Invocation invocation) async {
463
          log.add('download');
464
          return <String, dynamic>{
pq's avatar
pq committed
465 466
            'traceEvents': <dynamic>[
              <String, String>{
467 468
                'name': 'test event',
              },
469
            ],
470 471
          };
        });
472
      });
473

474
      test('without clearing timeline', () async {
475
        final Timeline timeline = await driver.traceAction(() async {
476 477 478 479 480 481 482 483 484 485 486 487 488
          log.add('action');
        }, retainPriorEvents: true);

        expect(log, const <String>[
          'startTracing',
          'action',
          'stopTracing',
          'download',
        ]);
        expect(timeline.events.single.name, 'test event');
      });

      test('with clearing timeline', () async {
489
        final Timeline timeline = await driver.traceAction(() async {
490
          log.add('action');
491 492
        });

493 494 495 496 497 498 499
        expect(log, const <String>[
          'clear',
          'startTracing',
          'action',
          'stopTracing',
          'download',
        ]);
500
        expect(timeline.events.single.name, 'test event');
501 502
      });
    });
503

504 505
    group('traceAction with timeline streams', () {
      test('specify non-default timeline streams', () async {
506 507 508 509
        bool actionCalled = false;
        bool startTracingCalled = false;
        bool stopTracingCalled = false;

510
        when(mockPeer.sendRequest('setVMTimelineFlags', argThat(equals(<String, dynamic>{'recordedStreams': '[Dart, GC, Compiler]'}))))
511
          .thenAnswer((Invocation invocation) async {
512 513 514 515
            startTracingCalled = true;
            return null;
          });

516
        when(mockPeer.sendRequest('setVMTimelineFlags', argThat(equals(<String, dynamic>{'recordedStreams': '[]'}))))
517
          .thenAnswer((Invocation invocation) async {
518 519 520 521
            stopTracingCalled = true;
            return null;
          });

522
        when(mockPeer.sendRequest('getVMTimeline')).thenAnswer((Invocation invocation) async {
523
          return <String, dynamic>{
pq's avatar
pq committed
524 525
            'traceEvents': <dynamic>[
              <String, String>{
526 527
                'name': 'test event',
              },
528 529 530 531
            ],
          };
        });

532
        final Timeline timeline = await driver.traceAction(() async {
533 534
          actionCalled = true;
        },
535 536 537
        streams: const <TimelineStream>[
          TimelineStream.dart,
          TimelineStream.gc,
538
          TimelineStream.compiler,
539 540
        ],
        retainPriorEvents: true);
541 542 543 544 545 546 547

        expect(actionCalled, isTrue);
        expect(startTracingCalled, isTrue);
        expect(stopTracingCalled, isTrue);
        expect(timeline.events.single.name, 'test event');
      });
    });
548 549

    group('sendCommand error conditions', () {
550
      test('local default timeout', () async {
551 552
        final List<String> log = <String>[];
        final StreamSubscription<LogRecord> logSub = flutterDriverLog.listen((LogRecord s) => log.add(s.toString()));
553
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
554
          // completer never completed to trigger timeout
555
          return Completer<Map<String, dynamic>>().future;
556
        });
557 558 559
        FakeAsync().run((FakeAsync time) {
          driver.waitFor(find.byTooltip('foo'));
          expect(log, <String>[]);
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
          time.elapse(kUnusuallyLongTimeout);
        });
        expect(log, <String>['[warning] FlutterDriver: waitFor message is taking a long time to complete...']);
        await logSub.cancel();
      });

      test('local custom timeout', () async {
        final List<String> log = <String>[];
        final StreamSubscription<LogRecord> logSub = flutterDriverLog.listen((LogRecord s) => log.add(s.toString()));
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
          // completer never completed to trigger timeout
          return Completer<Map<String, dynamic>>().future;
        });
        FakeAsync().run((FakeAsync time) {
          final Duration customTimeout = kUnusuallyLongTimeout - const Duration(seconds: 1);
          driver.waitFor(find.byTooltip('foo'), timeout: customTimeout);
          expect(log, <String>[]);
          time.elapse(customTimeout);
578 579 580
        });
        expect(log, <String>['[warning] FlutterDriver: waitFor message is taking a long time to complete...']);
        await logSub.cancel();
581 582 583
      });

      test('remote error', () async {
584
        when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
585
          return makeMockResponse(<String, dynamic>{
586
            'message': 'This is a failure',
587 588 589 590 591
          }, isError: true);
        });
        try {
          await driver.waitFor(find.byTooltip('foo'));
          fail('expected an exception');
592
        } catch (error) {
593 594 595 596 597 598
          expect(error is DriverError, isTrue);
          expect(error.message, 'Error in Flutter application: {message: This is a failure}');
        }
      });
    });
  });
599 600 601 602 603 604 605 606 607 608 609

  group('FlutterDriver with custom timeout', () {
    MockVMServiceClient mockClient;
    MockPeer mockPeer;
    MockIsolate mockIsolate;
    FlutterDriver driver;

    setUp(() {
      mockClient = MockVMServiceClient();
      mockPeer = MockPeer();
      mockIsolate = MockIsolate();
610
      driver = FlutterDriver.connectedTo(mockClient, mockPeer, mockIsolate);
611 612
    });

613
    test('GetHealth has no default timeout', () async {
614 615 616 617 618 619 620 621 622
      when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
        expect(i.positionalArguments[1], <String, String>{
          'command': 'get_health',
        });
        return makeMockResponse(<String, dynamic>{'status': 'ok'});
      });
      await driver.checkHealth();
    });

623
    test('does not interfere with explicit timeouts', () async {
624 625 626 627 628 629 630 631 632 633
      when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
        expect(i.positionalArguments[1], <String, String>{
          'command': 'get_health',
          'timeout': _kSerializedTestTimeout,
        });
        return makeMockResponse(<String, dynamic>{'status': 'ok'});
      });
      await driver.checkHealth(timeout: _kTestTimeout);
    });
  });
634 635 636
}

Future<Map<String, dynamic>> makeMockResponse(
637 638 639
  Map<String, dynamic> response, {
  bool isError = false,
}) {
640
  return Future<Map<String, dynamic>>.value(<String, dynamic>{
641
    'isError': isError,
642
    'response': response,
643 644 645 646 647 648 649 650 651 652 653 654 655 656
  });
}

class MockVMServiceClient extends Mock implements VMServiceClient { }

class MockVM extends Mock implements VM { }

class MockIsolate extends Mock implements VMRunnableIsolate { }

class MockVMPauseStartEvent extends Mock implements VMPauseStartEvent { }

class MockVMPauseBreakpointEvent extends Mock implements VMPauseBreakpointEvent { }

class MockVMResumeEvent extends Mock implements VMResumeEvent { }
657

658 659 660 661
class MockPeer extends Mock implements rpc.Peer {
  @override
  bool get isClosed => false;
}