| // Copyright 2014 The Flutter 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'; |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| import 'package:fake_async/fake_async.dart'; |
| import 'package:flutter_driver/src/common/error.dart'; |
| import 'package:flutter_driver/src/common/health.dart'; |
| import 'package:flutter_driver/src/common/layer_tree.dart'; |
| import 'package:flutter_driver/src/common/text_input_action.dart'; |
| import 'package:flutter_driver/src/common/wait.dart'; |
| import 'package:flutter_driver/src/driver/driver.dart'; |
| import 'package:flutter_driver/src/driver/timeline.dart'; |
| import 'package:vm_service/vm_service.dart' as vms; |
| |
| import '../../common.dart'; |
| |
| /// Magical timeout value that's different from the default. |
| const Duration _kTestTimeout = Duration(milliseconds: 1234); |
| const String _kSerializedTestTimeout = '1234'; |
| const String _kWebScriptPrefix = r"window.$flutterDriver('"; |
| const String _kWebScriptSuffix = "')"; |
| |
| void main() { |
| final List<String> log = <String>[]; |
| |
| driverLog = (String source, String message) { |
| log.add('$source: $message'); |
| }; |
| |
| group('VMServiceFlutterDriver with logCommunicationToFile', () { |
| late FakeVmService fakeClient; |
| late FakeVM fakeVM; |
| late vms.Isolate fakeIsolate; |
| late VMServiceFlutterDriver driver; |
| late File logFile; |
| |
| setUp(() { |
| fakeIsolate = createFakeIsolate(); |
| fakeVM = FakeVM(fakeIsolate); |
| fakeClient = FakeVmService(fakeVM); |
| fakeClient.responses['waitFor'] = makeFakeResponse(<String, dynamic>{'status':'ok'}); |
| }); |
| |
| tearDown(() { |
| if (logFile.existsSync()) { |
| logFile.deleteSync(); |
| } |
| }); |
| |
| group('logCommunicationToFile', () { |
| test('logCommunicationToFile = true', () async { |
| driver = VMServiceFlutterDriver.connectedTo(fakeClient, fakeIsolate); |
| logFile = File(driver.logFilePathName); |
| |
| await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout); |
| |
| final bool exists = logFile.existsSync(); |
| expect(exists, true, reason: 'Not found ${logFile.path}'); |
| |
| final String commandLog = await logFile.readAsString(); |
| const String waitForCommandLog = '>>> {command: waitFor, timeout: $_kSerializedTestTimeout, finderType: ByTooltipMessage, text: foo}'; |
| const String responseLog = '<<< {isError: false, response: {status: ok}}'; |
| |
| expect(commandLog.contains(waitForCommandLog), true, reason: '$commandLog not contains $waitForCommandLog'); |
| expect(commandLog.contains(responseLog), true, reason: '$commandLog not contains $responseLog'); |
| }); |
| |
| test('logCommunicationToFile = false', () async { |
| driver = VMServiceFlutterDriver.connectedTo(fakeClient, fakeIsolate, logCommunicationToFile: false); |
| logFile = File(driver.logFilePathName); |
| // clear log file if left in filetree from previous run |
| if (logFile.existsSync()) { |
| logFile.deleteSync(); |
| } |
| await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout); |
| |
| final bool exists = logFile.existsSync(); |
| expect(exists, false, reason: 'because ${logFile.path} exists'); |
| }); |
| |
| test('logFilePathName was set when a new driver was created', () { |
| driver = VMServiceFlutterDriver.connectedTo(fakeClient, fakeIsolate); |
| logFile = File(driver.logFilePathName); |
| expect(logFile.path, endsWith('.log')); |
| }); |
| }); |
| }); |
| |
| group('VMServiceFlutterDriver with printCommunication', () { |
| late FakeVmService fakeClient; |
| late FakeVM fakeVM; |
| late vms.Isolate fakeIsolate; |
| late VMServiceFlutterDriver driver; |
| |
| setUp(() async { |
| log.clear(); |
| fakeIsolate = createFakeIsolate(); |
| fakeVM = FakeVM(fakeIsolate); |
| fakeClient = FakeVmService(fakeVM); |
| fakeClient.responses['waitFor'] = makeFakeResponse(<String, dynamic>{'status':'ok'}); |
| }); |
| |
| test('printCommunication = true', () async { |
| driver = VMServiceFlutterDriver.connectedTo(fakeClient, fakeIsolate, printCommunication: true); |
| await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout); |
| expect(log, <String>[ |
| 'VMServiceFlutterDriver: >>> {command: waitFor, timeout: $_kSerializedTestTimeout, finderType: ByTooltipMessage, text: foo}', |
| 'VMServiceFlutterDriver: <<< {isError: false, response: {status: ok}}', |
| ]); |
| }); |
| |
| test('printCommunication = false', () async { |
| driver = VMServiceFlutterDriver.connectedTo(fakeClient, fakeIsolate); |
| await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout); |
| expect(log, <String>[]); |
| }); |
| }); |
| |
| group('VMServiceFlutterDriver.connect', () { |
| late FakeVmService fakeClient; |
| late FakeVM fakeVM; |
| late vms.Isolate fakeIsolate; |
| |
| void expectLogContains(String message) { |
| expect(log, anyElement(contains(message))); |
| } |
| |
| setUp(() { |
| log.clear(); |
| fakeIsolate = createFakeIsolate(); |
| fakeVM = FakeVM(fakeIsolate); |
| fakeClient = FakeVmService(fakeVM); |
| vmServiceConnectFunction = (String url, Map<String, dynamic>? headers) async { |
| return fakeClient; |
| }; |
| fakeClient.responses['get_health'] = makeFakeResponse(<String, dynamic>{'status': 'ok'}); |
| }); |
| |
| tearDown(() async { |
| restoreVmServiceConnectFunction(); |
| }); |
| |
| test('Retries while Dart VM service is not available', () async { |
| // This test case will test the real implementation of `_waitAndConnect`. |
| restoreVmServiceConnectFunction(); |
| |
| // The actual behavior is to retry indefinitely until the Dart VM service |
| // becomes available. `.timeout` is used here to exit the infinite loop, |
| // expecting that no other types of error are thrown during the process. |
| expect( |
| vmServiceConnectFunction('http://foo.bar', <String, dynamic>{}) |
| .timeout(const Duration(seconds: 1)), |
| throwsA(isA<TimeoutException>()), |
| ); |
| }); |
| |
| test('throws after retries if no isolate', () async { |
| fakeVM.numberOfTriesBeforeResolvingIsolate = 10000; |
| FakeAsync().run((FakeAsync time) { |
| FlutterDriver.connect(dartVmServiceUrl: ''); |
| time.elapse(kUnusuallyLongTimeout); |
| }); |
| expect(log, <String>[ |
| 'VMServiceFlutterDriver: Connecting to Flutter application at ', |
| 'VMServiceFlutterDriver: The root isolate is taking an unusually long time to start.', |
| ]); |
| }); |
| |
| test('Retries connections if isolate is not available', () async { |
| fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kPauseStart, timestamp: 0); |
| fakeVM.numberOfTriesBeforeResolvingIsolate = 5; |
| final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: ''); |
| expect(driver, isNotNull); |
| expect( |
| fakeClient.connectionLog, |
| <String>[ |
| 'getIsolate', |
| 'setFlag pause_isolates_on_start false', |
| 'resume', |
| 'streamListen Isolate', |
| 'getIsolate', |
| 'onIsolateEvent', |
| 'streamCancel Isolate', |
| ], |
| ); |
| }); |
| |
| test('Connects to isolate number', () async { |
| fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kPauseStart, timestamp: 0); |
| final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '', isolateNumber: int.parse(fakeIsolate.number!)); |
| expect(driver, isNotNull); |
| expect( |
| fakeClient.connectionLog, |
| <String>[ |
| 'getIsolate', |
| 'setFlag pause_isolates_on_start false', |
| 'resume', |
| 'streamListen Isolate', |
| 'getIsolate', |
| 'onIsolateEvent', |
| 'streamCancel Isolate', |
| ], |
| ); |
| }); |
| |
| test('connects to isolate paused at start', () async { |
| fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kPauseStart, timestamp: 0); |
| |
| final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: ''); |
| expect(driver, isNotNull); |
| expectLogContains('Isolate is paused at start'); |
| expect( |
| fakeClient.connectionLog, |
| <String>[ |
| 'getIsolate', |
| 'setFlag pause_isolates_on_start false', |
| 'resume', |
| 'streamListen Isolate', |
| 'getIsolate', |
| 'onIsolateEvent', |
| 'streamCancel Isolate', |
| ], |
| ); |
| }); |
| |
| test('ignores setFlag failure', () async { |
| fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kPauseStart, timestamp: 0); |
| fakeClient.failOnSetFlag = true; |
| |
| final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: ''); |
| expectLogContains('Failed to set pause_isolates_on_start=false, proceeding. ' |
| 'Error: Exception: setFlag failed'); |
| expect(driver, isNotNull); |
| }); |
| |
| |
| test('connects to isolate paused mid-flight', () async { |
| fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kPauseBreakpoint, timestamp: 0); |
| |
| final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: ''); |
| 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 { |
| fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kPauseBreakpoint, timestamp: 0); |
| fakeClient.failOnResumeWith101 = true; |
| |
| final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: ''); |
| expect(driver, isNotNull); |
| expectLogContains('Attempted to resume an already resumed isolate'); |
| }); |
| |
| test('connects to unpaused isolate', () async { |
| fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kResume, timestamp: 0); |
| |
| final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: ''); |
| expect(driver, isNotNull); |
| expectLogContains('Isolate is not paused. Assuming application is ready.'); |
| }); |
| |
| test('connects to unpaused when onExtensionAdded does not contain the ' |
| 'driver extension', () async { |
| fakeIsolate.pauseEvent = vms.Event(kind: vms.EventKind.kResume, timestamp: 0); |
| fakeIsolate.extensionRPCs!.add('ext.flutter.driver'); |
| |
| final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: ''); |
| expect(driver, isNotNull); |
| expectLogContains('Isolate is not paused. Assuming application is ready.'); |
| }); |
| }); |
| |
| group('VMServiceFlutterDriver', () { |
| late FakeVmService fakeClient; |
| late FakeVM fakeVM; |
| late vms.Isolate fakeIsolate; |
| late VMServiceFlutterDriver driver; |
| |
| setUp(() { |
| fakeIsolate = createFakeIsolate(); |
| fakeVM = FakeVM(fakeIsolate); |
| fakeClient = FakeVmService(fakeVM); |
| driver = VMServiceFlutterDriver.connectedTo(fakeClient, fakeIsolate); |
| fakeClient.responses['tap'] = makeFakeResponse(<String, dynamic>{}); |
| }); |
| |
| test('checks the health of the driver extension', () async { |
| fakeClient.responses['get_health'] = makeFakeResponse(<String, dynamic>{'status': 'ok'}); |
| final Health result = await driver.checkHealth(); |
| expect(result.status, HealthStatus.ok); |
| }); |
| |
| test('closes connection', () async { |
| await driver.close(); |
| expect(fakeClient.connectionLog.last, 'dispose'); |
| }); |
| |
| group('ByValueKey', () { |
| test('restricts value types', () async { |
| expect(() => find.byValueKey(null), throwsDriverError); |
| }); |
| |
| test('finds by ValueKey', () async { |
| await driver.tap(find.byValueKey('foo'), timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: tap, timeout: $_kSerializedTestTimeout, finderType: ByValueKey, keyValueString: foo, keyValueType: String}', |
| ]); |
| }); |
| }); |
| |
| group('BySemanticsLabel', () { |
| test('finds by Semantic label using String', () async { |
| await driver.tap(find.bySemanticsLabel('foo'), timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: tap, timeout: $_kSerializedTestTimeout, finderType: BySemanticsLabel, label: foo}', |
| ]); |
| }); |
| |
| test('finds by Semantic label using RegExp', () async { |
| await driver.tap(find.bySemanticsLabel(RegExp('^foo')), timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: tap, timeout: $_kSerializedTestTimeout, finderType: BySemanticsLabel, label: ^foo, isRegExp: true}', |
| ]); |
| }); |
| }); |
| |
| group('tap', () { |
| test('sends the tap command', () async { |
| await driver.tap(find.text('foo'), timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: tap, timeout: $_kSerializedTestTimeout, finderType: ByText, text: foo}', |
| ]); |
| }); |
| }); |
| |
| group('getText', () { |
| test('sends the getText command', () async { |
| fakeClient.responses['get_text'] = makeFakeResponse(<String, dynamic>{'text': 'hello'}); |
| final String result = await driver.getText(find.byValueKey(123), timeout: _kTestTimeout); |
| expect(result, 'hello'); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: get_text, timeout: $_kSerializedTestTimeout, finderType: ByValueKey, keyValueString: 123, keyValueType: int}', |
| ]); |
| }); |
| }); |
| |
| group('sendTextInputAction', () { |
| test('sends the SendTextInputAction command with action done', () async { |
| fakeClient.responses['send_text_input_action'] = makeFakeResponse(<String, dynamic>{}); |
| await driver.sendTextInputAction(TextInputAction.done, timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: send_text_input_action, timeout: $_kSerializedTestTimeout, action: done}', |
| ]); |
| }); |
| }); |
| |
| group('getLayerTree', () { |
| test('sends the getLayerTree command', () async { |
| fakeClient.responses['get_layer_tree'] = makeFakeResponse(<String, String>{ |
| 'tree': 'hello', |
| }); |
| final LayerTree result = await driver.getLayerTree(timeout: _kTestTimeout); |
| final LayerTree referenceTree = LayerTree.fromJson(<String, String>{ |
| 'tree': 'hello', |
| }); |
| expect(result.tree, referenceTree.tree); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: get_layer_tree, timeout: $_kSerializedTestTimeout}', |
| ]); |
| }); |
| }); |
| |
| group('waitFor', () { |
| test('sends the waitFor command', () async { |
| fakeClient.responses['waitFor'] = makeFakeResponse(<String, dynamic>{}); |
| await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: waitFor, timeout: $_kSerializedTestTimeout, finderType: ByTooltipMessage, text: foo}', |
| ]); |
| }); |
| }); |
| |
| group('getWidgetDiagnostics', () { |
| test('sends the getWidgetDiagnostics command', () async { |
| fakeClient.responses['get_diagnostics_tree'] = makeFakeResponse(<String, dynamic>{}); |
| await driver.getWidgetDiagnostics(find.byTooltip('foo'), timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: get_diagnostics_tree, timeout: $_kSerializedTestTimeout, finderType: ByTooltipMessage, text: foo, subtreeDepth: 0, includeProperties: true, diagnosticsType: widget}', |
| ]); |
| }); |
| }); |
| |
| group('getRenderObjectDiagnostics', () { |
| test('sends the getRenderObjectDiagnostics command', () async { |
| fakeClient.responses['get_diagnostics_tree'] = makeFakeResponse(<String, dynamic>{}); |
| await driver.getRenderObjectDiagnostics(find.byTooltip('foo'), timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: get_diagnostics_tree, timeout: $_kSerializedTestTimeout, finderType: ByTooltipMessage, text: foo, subtreeDepth: 0, includeProperties: true, diagnosticsType: renderObject}', |
| ]); |
| }); |
| }); |
| |
| group('waitForCondition', () { |
| test('sends the wait for NoPendingFrameCondition command', () async { |
| fakeClient.responses['waitForCondition'] = makeFakeResponse(<String, dynamic>{}); |
| await driver.waitForCondition(const NoPendingFrame(), timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: waitForCondition, timeout: $_kSerializedTestTimeout, conditionName: NoPendingFrameCondition}', |
| ]); |
| }); |
| |
| test('sends the wait for NoPendingPlatformMessages command', () async { |
| fakeClient.responses['waitForCondition'] = makeFakeResponse(<String, dynamic>{}); |
| await driver.waitForCondition(const NoPendingPlatformMessages(), timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: waitForCondition, timeout: $_kSerializedTestTimeout, conditionName: NoPendingPlatformMessagesCondition}', |
| ]); |
| }); |
| |
| test('sends the waitForCondition of combined conditions command', () async { |
| fakeClient.responses['waitForCondition'] = makeFakeResponse(<String, dynamic>{}); |
| const SerializableWaitCondition combinedCondition = |
| CombinedCondition(<SerializableWaitCondition>[NoPendingFrame(), NoTransientCallbacks()]); |
| await driver.waitForCondition(combinedCondition, timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: waitForCondition, timeout: $_kSerializedTestTimeout, conditionName: CombinedCondition, conditions: [{"conditionName":"NoPendingFrameCondition"},{"conditionName":"NoTransientCallbacksCondition"}]}', |
| ]); |
| }); |
| }); |
| |
| group('waitUntilNoTransientCallbacks', () { |
| test('sends the waitUntilNoTransientCallbacks command', () async { |
| fakeClient.responses['waitForCondition'] = makeFakeResponse(<String, dynamic>{}); |
| await driver.waitUntilNoTransientCallbacks(timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: waitForCondition, timeout: $_kSerializedTestTimeout, conditionName: NoTransientCallbacksCondition}', |
| ]); |
| }); |
| }); |
| |
| group('waitUntilFirstFrameRasterized', () { |
| test('sends the waitUntilFirstFrameRasterized command', () async { |
| fakeClient.responses['waitForCondition'] = makeFakeResponse(<String, dynamic>{}); |
| await driver.waitUntilFirstFrameRasterized(); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: waitForCondition, conditionName: FirstFrameRasterizedCondition}', |
| ]); |
| }); |
| }); |
| |
| group('getOffset', () { |
| setUp(() { |
| fakeClient.responses['get_offset'] = makeFakeResponse(<String, double>{ |
| 'dx': 11, |
| 'dy': 12, |
| }); |
| }); |
| |
| test('sends the getCenter command', () async { |
| final DriverOffset result = await driver.getCenter(find.byValueKey(123), timeout: _kTestTimeout); |
| expect(result, const DriverOffset(11, 12)); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: get_offset, timeout: 1234, finderType: ByValueKey, keyValueString: 123, keyValueType: int, offsetType: center}', |
| ]); |
| }); |
| |
| test('sends the getTopLeft command', () async { |
| final DriverOffset result = await driver.getTopLeft(find.byValueKey(123), timeout: _kTestTimeout); |
| expect(result, const DriverOffset(11, 12)); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: get_offset, timeout: 1234, finderType: ByValueKey, keyValueString: 123, keyValueType: int, offsetType: topLeft}', |
| ]); |
| }); |
| |
| test('sends the getTopRight command', () async { |
| final DriverOffset result = await driver.getTopRight(find.byValueKey(123), timeout: _kTestTimeout); |
| expect(result, const DriverOffset(11, 12)); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: get_offset, timeout: 1234, finderType: ByValueKey, keyValueString: 123, keyValueType: int, offsetType: topRight}', |
| ]); |
| }); |
| |
| test('sends the getBottomLeft command', () async { |
| final DriverOffset result = await driver.getBottomLeft(find.byValueKey(123), timeout: _kTestTimeout); |
| expect(result, const DriverOffset(11, 12)); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: get_offset, timeout: 1234, finderType: ByValueKey, keyValueString: 123, keyValueType: int, offsetType: bottomLeft}', |
| ]); |
| }); |
| |
| test('sends the getBottomRight command', () async { |
| final DriverOffset result = await driver.getBottomRight(find.byValueKey(123), timeout: _kTestTimeout); |
| expect(result, const DriverOffset(11, 12)); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: get_offset, timeout: 1234, finderType: ByValueKey, keyValueString: 123, keyValueType: int, offsetType: bottomRight}', |
| ]); |
| }); |
| }); |
| |
| group('clearTimeline', () { |
| test('clears timeline', () async { |
| await driver.clearTimeline(); |
| expect(fakeClient.connectionLog, contains('clearVMTimeline')); |
| }); |
| }); |
| |
| group('traceAction', () { |
| test('without clearing timeline', () async { |
| final Timeline timeline = await driver.traceAction(() async { |
| fakeClient.connectionLog.add('action'); |
| }, retainPriorEvents: true); |
| |
| expect(fakeClient.connectionLog, const <String>[ |
| 'setVMTimelineFlags [all]', |
| 'action', |
| 'getFlagList', |
| 'setVMTimelineFlags []', |
| 'getVMTimeline null null', |
| ]); |
| expect(timeline.events!.single.name, 'test event'); |
| }); |
| |
| test('with clearing timeline', () async { |
| final Timeline timeline = await driver.traceAction(() async { |
| fakeClient.connectionLog.add('action'); |
| }); |
| |
| expect(fakeClient.connectionLog, const <String>[ |
| 'clearVMTimeline', |
| 'getVMTimelineMicros', |
| 'setVMTimelineFlags [all]', |
| 'action', |
| 'getVMTimelineMicros', |
| 'getFlagList', |
| 'setVMTimelineFlags []', |
| 'getVMTimeline 1 999999', |
| ]); |
| expect(timeline.events!.single.name, 'test event'); |
| }); |
| |
| test('with time interval', () async { |
| fakeClient.incrementMicros = true; |
| fakeClient.timelineResponses[1000001] = vms.Timeline.parse(<String, dynamic>{ |
| 'traceEvents': <dynamic>[ |
| <String, dynamic>{ |
| 'name': 'test event 2', |
| }, |
| ], |
| 'timeOriginMicros': 1000000, |
| 'timeExtentMicros': 999999, |
| }); |
| final Timeline timeline = await driver.traceAction(() async { |
| fakeClient.connectionLog.add('action'); |
| }); |
| |
| expect(fakeClient.connectionLog, const <String>[ |
| 'clearVMTimeline', |
| 'getVMTimelineMicros', |
| 'setVMTimelineFlags [all]', |
| 'action', |
| 'getVMTimelineMicros', |
| 'getFlagList', |
| 'setVMTimelineFlags []', |
| 'getVMTimeline 1 999999', |
| 'getVMTimeline 1000001 999999', |
| ]); |
| expect(timeline.events!.map((TimelineEvent event) => event.name), <String>[ |
| 'test event', |
| 'test event 2', |
| ]); |
| }); |
| }); |
| |
| group('traceAction with timeline streams', () { |
| test('specify non-default timeline streams', () async { |
| bool actionCalled = false; |
| |
| final Timeline timeline = await driver.traceAction(() async { |
| actionCalled = true; |
| }, |
| streams: const <TimelineStream>[ |
| TimelineStream.dart, |
| TimelineStream.gc, |
| TimelineStream.compiler, |
| ], |
| retainPriorEvents: true); |
| |
| expect(actionCalled, isTrue); |
| expect(fakeClient.connectionLog, <String>[ |
| 'setVMTimelineFlags [Dart, GC, Compiler]', |
| 'getFlagList', |
| 'setVMTimelineFlags []', |
| 'getVMTimeline null null', |
| ]); |
| |
| expect(timeline.events!.single.name, 'test event'); |
| }); |
| }); |
| |
| group('sendCommand error conditions', () { |
| test('local default timeout', () async { |
| log.clear(); |
| fakeClient.artificialExtensionDelay = Completer<void>().future; |
| FakeAsync().run((FakeAsync time) { |
| driver.waitFor(find.byTooltip('foo')); |
| expect(log, <String>[]); |
| time.elapse(kUnusuallyLongTimeout); |
| }); |
| expect(log, <String>['VMServiceFlutterDriver: waitFor message is taking a long time to complete...']); |
| }); |
| |
| test('local custom timeout', () async { |
| log.clear(); |
| fakeClient.artificialExtensionDelay = Completer<void>().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); |
| }); |
| expect(log, <String>['VMServiceFlutterDriver: waitFor message is taking a long time to complete...']); |
| }); |
| |
| test('remote error', () async { |
| fakeClient.responses['waitFor'] = makeFakeResponse(<String, dynamic>{ |
| 'message': 'This is a failure', |
| }, isError: true); |
| await expectLater( |
| () => driver.waitFor(find.byTooltip('foo')), |
| throwsA(isA<DriverError>().having( |
| (DriverError error) => error.message, |
| 'message', |
| 'Error in Flutter application: {message: This is a failure}', |
| )), |
| ); |
| }); |
| |
| test('uncaught remote error', () async { |
| fakeClient.artificialExtensionDelay = Future<void>.error( |
| vms.RPCError('callServiceExtension', 9999, 'test error'), |
| ); |
| |
| expect(driver.waitFor(find.byTooltip('foo')), throwsDriverError); |
| }); |
| }); |
| |
| group('setSemantics', () { |
| test('can be enabled', () async { |
| fakeClient.responses['set_semantics'] = makeFakeResponse(<String, Object>{ |
| 'changedState': true, |
| }); |
| await driver.setSemantics(true, timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: set_semantics, timeout: $_kSerializedTestTimeout, enabled: true}', |
| ]); |
| }); |
| |
| test('can be disabled', () async { |
| fakeClient.responses['set_semantics'] = makeFakeResponse(<String, Object>{ |
| 'changedState': false, |
| }); |
| await driver.setSemantics(false, timeout: _kTestTimeout); |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: set_semantics, timeout: $_kSerializedTestTimeout, enabled: false}', |
| ]); |
| }); |
| }); |
| |
| test('VMServiceFlutterDriver does not support webDriver', () async { |
| expect(() => driver.webDriver, throwsUnsupportedError); |
| }); |
| |
| group('runUnsynchronized', () { |
| test('wrap waitFor with runUnsynchronized', () async { |
| fakeClient.responses['waitFor'] = makeFakeResponse(<String, dynamic>{}); |
| fakeClient.responses['set_frame_sync'] = makeFakeResponse(<String, dynamic>{}); |
| |
| await driver.runUnsynchronized(() async { |
| await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout); |
| }); |
| |
| expect(fakeClient.commandLog, <String>[ |
| 'ext.flutter.driver {command: set_frame_sync, enabled: false}', |
| 'ext.flutter.driver {command: waitFor, timeout: $_kSerializedTestTimeout, finderType: ByTooltipMessage, text: foo}', |
| 'ext.flutter.driver {command: set_frame_sync, enabled: true}', |
| ]); |
| }); |
| }); |
| }); |
| |
| group('VMServiceFlutterDriver with custom timeout', () { |
| late FakeVmService fakeClient; |
| late FakeVM fakeVM; |
| late vms.Isolate fakeIsolate; |
| late VMServiceFlutterDriver driver; |
| |
| setUp(() { |
| fakeIsolate = createFakeIsolate(); |
| fakeVM = FakeVM(fakeIsolate); |
| fakeClient = FakeVmService(fakeVM); |
| driver = VMServiceFlutterDriver.connectedTo(fakeClient, fakeIsolate); |
| fakeClient.responses['get_health'] = makeFakeResponse(<String, dynamic>{'status': 'ok'}); |
| }); |
| |
| test('GetHealth has no default timeout', () async { |
| await driver.checkHealth(); |
| expect( |
| fakeClient.commandLog, |
| <String>['ext.flutter.driver {command: get_health}'], |
| ); |
| }); |
| |
| test('does not interfere with explicit timeouts', () async { |
| await driver.checkHealth(timeout: _kTestTimeout); |
| expect( |
| fakeClient.commandLog, |
| <String>['ext.flutter.driver {command: get_health, timeout: $_kSerializedTestTimeout}'], |
| ); |
| }); |
| }); |
| |
| group('WebFlutterDriver with logCommunicationToFile', () { |
| late FakeFlutterWebConnection fakeConnection; |
| late WebFlutterDriver driver; |
| late File logFile; |
| |
| setUp(() { |
| fakeConnection = FakeFlutterWebConnection(); |
| fakeConnection.supportsTimelineAction = true; |
| fakeConnection.responses['waitFor'] = jsonEncode(makeFakeResponse(<String, dynamic>{'status': 'ok'})); |
| }); |
| |
| tearDown(() { |
| if (logFile.existsSync()) { |
| logFile.deleteSync(); |
| } |
| }); |
| |
| test('logCommunicationToFile = true', () async { |
| driver = WebFlutterDriver.connectedTo(fakeConnection); |
| logFile = File(driver.logFilePathName); |
| await driver.waitFor(find.byTooltip('logCommunicationToFile test'), timeout: _kTestTimeout); |
| |
| final bool exists = logFile.existsSync(); |
| expect(exists, true, reason: 'Not found ${logFile.path}'); |
| |
| final String commandLog = await logFile.readAsString(); |
| const String waitForCommandLog = '>>> {command: waitFor, timeout: 1234, finderType: ByTooltipMessage, text: logCommunicationToFile test}'; |
| const String responseLog = '<<< {isError: false, response: {status: ok}, type: Response}'; |
| |
| expect(commandLog, contains(waitForCommandLog), reason: '$commandLog not contains $waitForCommandLog'); |
| expect(commandLog, contains(responseLog), reason: '$commandLog not contains $responseLog'); |
| }); |
| |
| test('logCommunicationToFile = false', () async { |
| driver = WebFlutterDriver.connectedTo(fakeConnection, logCommunicationToFile: false); |
| logFile = File(driver.logFilePathName); |
| // clear log file if left in filetree from previous run |
| if (logFile.existsSync()) { |
| logFile.deleteSync(); |
| } |
| await driver.waitFor(find.byTooltip('logCommunicationToFile test'), timeout: _kTestTimeout); |
| final bool exists = logFile.existsSync(); |
| expect(exists, false, reason: 'because ${logFile.path} exists'); |
| }); |
| }); |
| |
| group('WebFlutterDriver with printCommunication', () { |
| late FakeFlutterWebConnection fakeConnection; |
| late WebFlutterDriver driver; |
| |
| setUp(() { |
| log.clear(); |
| fakeConnection = FakeFlutterWebConnection(); |
| fakeConnection.supportsTimelineAction = true; |
| fakeConnection.responses['waitFor'] = jsonEncode(makeFakeResponse(<String, dynamic>{'status': 'ok'})); |
| }); |
| |
| test('printCommunication = true', () async { |
| driver = WebFlutterDriver.connectedTo(fakeConnection, printCommunication: true); |
| await driver.waitFor(find.byTooltip('printCommunication test'), timeout: _kTestTimeout); |
| expect(log, <String>[ |
| 'WebFlutterDriver: >>> {command: waitFor, timeout: 1234, finderType: ByTooltipMessage, text: printCommunication test}', |
| 'WebFlutterDriver: <<< {isError: false, response: {status: ok}, type: Response}', |
| ]); |
| }); |
| |
| test('printCommunication = false', () async { |
| driver = WebFlutterDriver.connectedTo(fakeConnection); |
| await driver.waitFor(find.byTooltip('printCommunication test'), timeout: _kTestTimeout); |
| expect(log, <String>[]); |
| }); |
| }); |
| |
| group('WebFlutterDriver', () { |
| late FakeFlutterWebConnection fakeConnection; |
| late WebFlutterDriver driver; |
| |
| setUp(() { |
| fakeConnection = FakeFlutterWebConnection(); |
| fakeConnection.supportsTimelineAction = true; |
| driver = WebFlutterDriver.connectedTo(fakeConnection); |
| }); |
| |
| test('closes connection', () async { |
| await driver.close(); |
| }); |
| |
| group('ByValueKey', () { |
| test('restricts value types', () async { |
| expect(() => find.byValueKey(null), |
| throwsDriverError); |
| }); |
| |
| test('finds by ValueKey', () async { |
| fakeConnection.responses['tap'] = jsonEncode(makeFakeResponse(<String, dynamic>{})); |
| await driver.tap(find.byValueKey('foo'), timeout: _kTestTimeout); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"tap","timeout":"1234","finderType":"ByValueKey","keyValueString":"foo","keyValueType":"String"}') 0:00:01.234000''', |
| ]); |
| }); |
| }); |
| |
| group('BySemanticsLabel', () { |
| test('finds by Semantic label using String', () async { |
| fakeConnection.responses['tap'] = jsonEncode(makeFakeResponse(<String, dynamic>{})); |
| await driver.tap(find.bySemanticsLabel('foo'), timeout: _kTestTimeout); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"tap","timeout":"1234","finderType":"BySemanticsLabel","label":"foo"}') 0:00:01.234000''', |
| ]); |
| }); |
| |
| test('finds by Semantic label using RegExp', () async { |
| fakeConnection.responses['tap'] = jsonEncode(makeFakeResponse(<String, dynamic>{})); |
| await driver.tap(find.bySemanticsLabel(RegExp('^foo')), timeout: _kTestTimeout); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"tap","timeout":"1234","finderType":"BySemanticsLabel","label":"^foo","isRegExp":"true"}') 0:00:01.234000''', |
| ]); |
| }); |
| }); |
| |
| group('tap', () { |
| test('sends the tap command', () async { |
| fakeConnection.responses['tap'] = jsonEncode(makeFakeResponse(<String, dynamic>{})); |
| await driver.tap(find.text('foo'), timeout: _kTestTimeout); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"tap","timeout":"1234","finderType":"ByText","text":"foo"}') 0:00:01.234000''', |
| ]); |
| }); |
| }); |
| |
| group('getText', () { |
| test('sends the getText command', () async { |
| fakeConnection.responses['get_text'] = jsonEncode(makeFakeResponse(<String, dynamic>{'text': 'hello'})); |
| final String result = await driver.getText(find.byValueKey(123), timeout: _kTestTimeout); |
| expect(result, 'hello'); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"get_text","timeout":"1234","finderType":"ByValueKey","keyValueString":"123","keyValueType":"int"}') 0:00:01.234000''', |
| ]); |
| }); |
| }); |
| |
| group('waitFor', () { |
| test('sends the waitFor command', () async { |
| fakeConnection.responses['waitFor'] = jsonEncode(makeFakeResponse(<String, dynamic>{'text': 'hello'})); |
| await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"waitFor","timeout":"1234","finderType":"ByTooltipMessage","text":"foo"}') 0:00:01.234000''', |
| ]); |
| }); |
| }); |
| |
| group('waitForCondition', () { |
| setUp(() { |
| fakeConnection.responses['waitForCondition'] = jsonEncode(makeFakeResponse(<String, dynamic>{'text': 'hello'})); |
| }); |
| |
| test('sends the wait for NoPendingFrameCondition command', () async { |
| await driver.waitForCondition(const NoPendingFrame(), timeout: _kTestTimeout); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"waitForCondition","timeout":"1234","conditionName":"NoPendingFrameCondition"}') 0:00:01.234000''', |
| ]); |
| }); |
| |
| test('sends the wait for NoPendingPlatformMessages command', () async { |
| await driver.waitForCondition(const NoPendingPlatformMessages(), timeout: _kTestTimeout); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"waitForCondition","timeout":"1234","conditionName":"NoPendingPlatformMessagesCondition"}') 0:00:01.234000''', |
| ]); |
| }); |
| |
| test('sends the waitForCondition of combined conditions command', () async { |
| const SerializableWaitCondition combinedCondition = CombinedCondition( |
| <SerializableWaitCondition>[NoPendingFrame(), NoTransientCallbacks()], |
| ); |
| await driver.waitForCondition(combinedCondition, timeout: _kTestTimeout); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"waitForCondition","timeout":"1234","conditionName":"CombinedCondition","conditions":"[{\"conditionName\":\"NoPendingFrameCondition\"},{\"conditionName\":\"NoTransientCallbacksCondition\"}]"}') 0:00:01.234000''', |
| ]); |
| }); |
| }); |
| |
| group('waitUntilNoTransientCallbacks', () { |
| test('sends the waitUntilNoTransientCallbacks command', () async { |
| fakeConnection.responses['waitForCondition'] = jsonEncode(makeFakeResponse(<String, dynamic>{})); |
| await driver.waitUntilNoTransientCallbacks(timeout: _kTestTimeout); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"waitForCondition","timeout":"1234","conditionName":"NoTransientCallbacksCondition"}') 0:00:01.234000''', |
| ]); |
| }); |
| }); |
| |
| group('getOffset', () { |
| setUp(() { |
| fakeConnection.responses['get_offset'] = jsonEncode(makeFakeResponse(<String, double>{ |
| 'dx': 11, |
| 'dy': 12, |
| })); |
| }); |
| |
| test('sends the getCenter command', () async { |
| final DriverOffset result = await driver.getCenter(find.byValueKey(123), timeout: _kTestTimeout); |
| expect(result, const DriverOffset(11, 12)); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"get_offset","timeout":"1234","finderType":"ByValueKey","keyValueString":"123","keyValueType":"int","offsetType":"center"}') 0:00:01.234000''', |
| ]); |
| }); |
| |
| test('sends the getTopLeft command', () async { |
| final DriverOffset result = await driver.getTopLeft(find.byValueKey(123), timeout: _kTestTimeout); |
| expect(result, const DriverOffset(11, 12)); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"get_offset","timeout":"1234","finderType":"ByValueKey","keyValueString":"123","keyValueType":"int","offsetType":"topLeft"}') 0:00:01.234000''', |
| ]); |
| }); |
| |
| test('sends the getTopRight command', () async { |
| final DriverOffset result = await driver.getTopRight(find.byValueKey(123), timeout: _kTestTimeout); |
| expect(result, const DriverOffset(11, 12)); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"get_offset","timeout":"1234","finderType":"ByValueKey","keyValueString":"123","keyValueType":"int","offsetType":"topRight"}') 0:00:01.234000''', |
| ]); |
| }); |
| |
| test('sends the getBottomLeft command', () async { |
| final DriverOffset result = await driver.getBottomLeft(find.byValueKey(123), timeout: _kTestTimeout); |
| expect(result, const DriverOffset(11, 12)); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"get_offset","timeout":"1234","finderType":"ByValueKey","keyValueString":"123","keyValueType":"int","offsetType":"bottomLeft"}') 0:00:01.234000''', |
| ]); |
| }); |
| |
| test('sends the getBottomRight command', () async { |
| final DriverOffset result = await driver.getBottomRight(find.byValueKey(123), timeout: _kTestTimeout); |
| expect(result, const DriverOffset(11, 12)); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"get_offset","timeout":"1234","finderType":"ByValueKey","keyValueString":"123","keyValueType":"int","offsetType":"bottomRight"}') 0:00:01.234000''', |
| ]); |
| }); |
| }); |
| |
| test('checks the health of the driver extension', () async { |
| fakeConnection.responses['get_health'] = jsonEncode(makeFakeResponse(<String, dynamic>{'status': 'ok'})); |
| await driver.checkHealth(); |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"get_health"}') null''', |
| ]); |
| }); |
| |
| group('WebFlutterDriver Unimplemented/Unsupported error', () { |
| test('forceGC', () async { |
| expect(driver.forceGC(), throwsUnimplementedError); |
| }); |
| |
| test('getVmFlags', () async { |
| expect(driver.getVmFlags(), throwsUnimplementedError); |
| }); |
| |
| test('waitUntilFirstFrameRasterized', () async { |
| expect(driver.waitUntilFirstFrameRasterized(), throwsUnimplementedError); |
| }); |
| |
| test('appIsolate', () async { |
| expect(() => driver.appIsolate.extensionRPCs, throwsUnsupportedError); |
| }); |
| |
| test('serviceClient', () async { |
| expect(() => driver.serviceClient.getVM(), throwsUnsupportedError); |
| }); |
| }); |
| |
| group('runUnsynchronized', () { |
| test('wrap waitFor with runUnsynchronized', () async { |
| fakeConnection.responses['waitFor'] = jsonEncode(makeFakeResponse(<String, dynamic>{'text': 'hello'})); |
| fakeConnection.responses['set_frame_sync'] = jsonEncode(makeFakeResponse(<String, dynamic>{})); |
| |
| await driver.runUnsynchronized(() async { |
| await driver.waitFor(find.byTooltip('foo'), timeout: _kTestTimeout); |
| }); |
| |
| expect(fakeConnection.commandLog, <String>[ |
| r'''window.$flutterDriver('{"command":"set_frame_sync","enabled":"false"}') null''', |
| r'''window.$flutterDriver('{"command":"waitFor","timeout":"1234","finderType":"ByTooltipMessage","text":"foo"}') 0:00:01.234000''', |
| r'''window.$flutterDriver('{"command":"set_frame_sync","enabled":"true"}') null''', |
| ]); |
| }); |
| }); |
| }); |
| |
| group('WebFlutterDriver with non-chrome browser', () { |
| FakeFlutterWebConnection fakeConnection; |
| late WebFlutterDriver driver; |
| |
| setUp(() { |
| fakeConnection = FakeFlutterWebConnection(); |
| driver = WebFlutterDriver.connectedTo(fakeConnection); |
| }); |
| |
| test('tracing', () async { |
| expect(driver.traceAction(() async { return Future<dynamic>.value(); }), throwsUnsupportedError); |
| expect(driver.startTracing(), throwsUnsupportedError); |
| expect(driver.stopTracingAndDownloadTimeline(), throwsUnsupportedError); |
| expect(driver.clearTimeline(), throwsUnsupportedError); |
| }); |
| }); |
| } |
| |
| // This function will verify the format of the script and return the actual |
| // script. The script will be in the following format: |
| // window.flutterDriver('[actual script]') |
| String _checkAndEncode(dynamic script) { |
| expect(script, isA<String>()); |
| final String scriptString = script as String; |
| expect(scriptString.startsWith(_kWebScriptPrefix), isTrue); |
| expect(scriptString.endsWith(_kWebScriptSuffix), isTrue); |
| // Strip prefix and suffix |
| return scriptString.substring(_kWebScriptPrefix.length, script.length - 2); |
| } |
| |
| vms.Response? makeFakeResponse( |
| Map<String, dynamic> response, { |
| bool isError = false, |
| }) { |
| return vms.Response.parse(<String, dynamic>{ |
| 'isError': isError, |
| 'response': response, |
| }); |
| } |
| |
| class FakeFlutterWebConnection extends Fake implements FlutterWebConnection { |
| @override |
| bool supportsTimelineAction = false; |
| |
| Map<String, dynamic> responses = <String, dynamic>{}; |
| List<String> commandLog = <String>[]; |
| @override |
| Future<dynamic> sendCommand(String script, Duration? duration) async { |
| commandLog.add('$script $duration'); |
| final Map<String, dynamic> decoded = jsonDecode(_checkAndEncode(script)) as Map<String, dynamic>; |
| final dynamic response = responses[decoded['command']]; |
| assert(response != null, 'Missing ${decoded['command']} in responses.'); |
| return response; |
| } |
| |
| @override |
| Future<void> close() async { |
| return; |
| } |
| } |
| |
| class FakeVmService extends Fake implements vms.VmService { |
| FakeVmService(this.vm); |
| |
| FakeVM? vm; |
| bool failOnSetFlag = false; |
| bool failOnResumeWith101 = false; |
| |
| final List<String> connectionLog = <String>[]; |
| |
| @override |
| Future<vms.VM> getVM() async => vm!; |
| |
| @override |
| Future<vms.Isolate> getIsolate(String isolateId) async { |
| connectionLog.add('getIsolate'); |
| if (isolateId == vm!.isolate!.id) { |
| return vm!.isolate!; |
| } |
| throw UnimplementedError('getIsolate called with unrecognized $isolateId'); |
| } |
| |
| @override |
| Future<vms.Success> resume(String isolateId, {String? step, int? frameIndex}) async { |
| assert(isolateId == vm!.isolate!.id); |
| connectionLog.add('resume'); |
| if (failOnResumeWith101) { |
| throw vms.RPCError('resume', 101, ''); |
| } |
| return vms.Success(); |
| } |
| |
| @override |
| Future<vms.Success> streamListen(String streamId) async { |
| connectionLog.add('streamListen $streamId'); |
| return vms.Success(); |
| } |
| |
| @override |
| Future<vms.Success> streamCancel(String streamId) async { |
| connectionLog.add('streamCancel $streamId'); |
| return vms.Success(); |
| } |
| |
| @override |
| Future<vms.Response> setFlag(String name, String value) async { |
| connectionLog.add('setFlag $name $value'); |
| if (failOnSetFlag) { |
| throw Exception('setFlag failed'); |
| } |
| return vms.Success(); |
| } |
| |
| @override |
| Stream<vms.Event> get onIsolateEvent async* { |
| connectionLog.add('onIsolateEvent'); |
| yield vms.Event( |
| kind: vms.EventKind.kServiceExtensionAdded, |
| extensionRPC: 'ext.flutter.driver', |
| timestamp: 0, |
| ); |
| } |
| |
| List<String> commandLog = <String>[]; |
| Map<String, vms.Response?> responses = <String, vms.Response?>{}; |
| Future<void>? artificialExtensionDelay; |
| |
| @override |
| Future<vms.Response> callServiceExtension(String method, {Map<dynamic, dynamic>? args, String? isolateId}) async { |
| commandLog.add('$method $args'); |
| await artificialExtensionDelay; |
| |
| final vms.Response response = responses[args!['command']]!; |
| assert(response != null, 'Failed to create a response for ${args['command']}'); |
| return response; |
| } |
| |
| @override |
| Future<vms.Success> clearVMTimeline() async { |
| connectionLog.add('clearVMTimeline'); |
| return vms.Success(); |
| } |
| |
| @override |
| Future<vms.FlagList> getFlagList() async { |
| connectionLog.add('getFlagList'); |
| return vms.FlagList(flags: <vms.Flag>[]); |
| } |
| |
| int vmTimelineMicros = -1000000; |
| bool incrementMicros = false; |
| |
| @override |
| Future<vms.Timestamp> getVMTimelineMicros() async { |
| connectionLog.add('getVMTimelineMicros'); |
| if (incrementMicros || vmTimelineMicros < 0) { |
| vmTimelineMicros = vmTimelineMicros + 1000001; |
| } |
| return vms.Timestamp(timestamp: vmTimelineMicros); |
| } |
| |
| @override |
| Future<vms.Success> setVMTimelineFlags(List<String> recordedStreams) async { |
| connectionLog.add('setVMTimelineFlags $recordedStreams'); |
| return vms.Success(); |
| } |
| |
| final Map<int, vms.Timeline?> timelineResponses = <int, vms.Timeline?>{ |
| 1: vms.Timeline.parse(<String, dynamic>{ |
| 'traceEvents': <dynamic>[ |
| <String, dynamic>{ |
| 'name': 'test event', |
| }, |
| ], |
| 'timeOriginMicros': 0, |
| 'timeExtentMicros': 999999, |
| }), |
| }; |
| |
| @override |
| Future<vms.Timeline> getVMTimeline({int? timeOriginMicros, int? timeExtentMicros}) async { |
| connectionLog.add('getVMTimeline $timeOriginMicros $timeExtentMicros'); |
| final vms.Timeline timeline = timelineResponses[timeOriginMicros ?? 1]!; |
| assert(timeline != null, 'Missing entry in timelineResponses[$timeOriginMicros]'); |
| return timeline; |
| } |
| |
| @override |
| Future<void> dispose() async { |
| connectionLog.add('dispose'); |
| } |
| |
| @override |
| Future<void> get onDone async {} |
| } |
| |
| class FakeVM extends Fake implements vms.VM { |
| FakeVM(this.isolate); |
| |
| vms.Isolate? isolate; |
| |
| int numberOfTriesBeforeResolvingIsolate = 0; |
| |
| @override |
| List<vms.IsolateRef> get isolates { |
| numberOfTriesBeforeResolvingIsolate -= 1; |
| return <vms.Isolate>[ |
| if (numberOfTriesBeforeResolvingIsolate <= 0) |
| isolate!, |
| ]; |
| } |
| } |
| |
| vms.Isolate createFakeIsolate() => vms.Isolate( |
| id: '123', |
| number: '123', |
| extensionRPCs: <String>[], |
| ); |