| // 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 'package:fuchsia_remote_debug_protocol/src/dart/dart_vm.dart'; |
| import 'package:test/fake.dart'; |
| import 'package:test/test.dart'; |
| import 'package:vm_service/vm_service.dart' as vms; |
| |
| void main() { |
| group('DartVm.connect', () { |
| tearDown(() { |
| restoreVmServiceConnectionFunction(); |
| }); |
| |
| test('disconnect closes peer', () async { |
| final FakeVmService service = FakeVmService(); |
| Future<vms.VmService> fakeServiceFunction( |
| Uri uri, { |
| Duration? timeout, |
| }) { |
| return Future<vms.VmService>(() => service); |
| } |
| |
| fuchsiaVmServiceConnectionFunction = fakeServiceFunction; |
| final DartVm vm = |
| await DartVm.connect(Uri.parse('http://this.whatever/ws')); |
| expect(vm, isNot(null)); |
| await vm.stop(); |
| expect(service.disposed, true); |
| }); |
| }); |
| |
| group('DartVm.getAllFlutterViews', () { |
| late FakeVmService fakeService; |
| |
| setUp(() { |
| fakeService = FakeVmService(); |
| }); |
| |
| tearDown(() { |
| restoreVmServiceConnectionFunction(); |
| }); |
| |
| test('basic flutter view parsing', () async { |
| final Map<String, dynamic> flutterViewCannedResponses = <String, dynamic>{ |
| 'views': <Map<String, dynamic>>[ |
| <String, dynamic>{ |
| 'type': 'FlutterView', |
| 'id': 'flutterView0', |
| }, |
| <String, dynamic>{ |
| 'type': 'FlutterView', |
| 'id': 'flutterView1', |
| 'isolate': <String, dynamic>{ |
| 'type': '@Isolate', |
| 'fixedId': 'true', |
| 'id': 'isolates/1', |
| 'name': 'file://flutterBinary1', |
| 'number': '1', |
| }, |
| }, |
| <String, dynamic>{ |
| 'type': 'FlutterView', |
| 'id': 'flutterView2', |
| 'isolate': <String, dynamic>{ |
| 'type': '@Isolate', |
| 'fixedId': 'true', |
| 'id': 'isolates/2', |
| 'name': 'file://flutterBinary2', |
| 'number': '2', |
| }, |
| }, |
| ], |
| }; |
| |
| Future<vms.VmService> fakeVmConnectionFunction( |
| Uri uri, { |
| Duration? timeout, |
| }) { |
| fakeService.flutterListViews = |
| vms.Response.parse(flutterViewCannedResponses); |
| return Future<vms.VmService>(() => fakeService); |
| } |
| |
| fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction; |
| final DartVm vm = |
| await DartVm.connect(Uri.parse('http://whatever.com/ws')); |
| expect(vm, isNot(null)); |
| final List<FlutterView> views = await vm.getAllFlutterViews(); |
| expect(views.length, 3); |
| // Check ID's as they cannot be null. |
| expect(views[0].id, 'flutterView0'); |
| expect(views[1].id, 'flutterView1'); |
| expect(views[2].id, 'flutterView2'); |
| |
| // Verify names. |
| expect(views[0].name, equals(null)); |
| expect(views[1].name, 'file://flutterBinary1'); |
| expect(views[2].name, 'file://flutterBinary2'); |
| }); |
| |
| test('basic flutter view parsing with casting checks', () async { |
| final Map<String, dynamic> flutterViewCannedResponses = <String, dynamic>{ |
| 'views': <dynamic>[ |
| <String, dynamic>{ |
| 'type': 'FlutterView', |
| 'id': 'flutterView0', |
| }, |
| <String, dynamic>{ |
| 'type': 'FlutterView', |
| 'id': 'flutterView1', |
| 'isolate': <String, dynamic>{ |
| 'type': '@Isolate', |
| 'fixedId': 'true', |
| 'id': 'isolates/1', |
| 'name': 'file://flutterBinary1', |
| 'number': '1', |
| }, |
| }, |
| <String, dynamic>{ |
| 'type': 'FlutterView', |
| 'id': 'flutterView2', |
| 'isolate': <String, dynamic>{ |
| 'type': '@Isolate', |
| 'fixedId': 'true', |
| 'id': 'isolates/2', |
| 'name': 'file://flutterBinary2', |
| 'number': '2', |
| }, |
| }, |
| ], |
| }; |
| |
| Future<vms.VmService> fakeVmConnectionFunction( |
| Uri uri, { |
| Duration? timeout, |
| }) { |
| fakeService.flutterListViews = |
| vms.Response.parse(flutterViewCannedResponses); |
| return Future<vms.VmService>(() => fakeService); |
| } |
| |
| fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction; |
| final DartVm vm = |
| await DartVm.connect(Uri.parse('http://whatever.com/ws')); |
| expect(vm, isNot(null)); |
| final List<FlutterView> views = await vm.getAllFlutterViews(); |
| expect(views.length, 3); |
| // Check ID's as they cannot be null. |
| expect(views[0].id, 'flutterView0'); |
| expect(views[1].id, 'flutterView1'); |
| expect(views[2].id, 'flutterView2'); |
| |
| // Verify names. |
| expect(views[0].name, equals(null)); |
| expect(views[1].name, 'file://flutterBinary1'); |
| expect(views[2].name, 'file://flutterBinary2'); |
| }); |
| |
| test('invalid flutter view missing ID', () async { |
| final Map<String, dynamic> flutterViewCannedResponseMissingId = |
| <String, dynamic>{ |
| 'views': <Map<String, dynamic>>[ |
| // Valid flutter view. |
| <String, dynamic>{ |
| 'type': 'FlutterView', |
| 'id': 'flutterView1', |
| 'isolate': <String, dynamic>{ |
| 'type': '@Isolate', |
| 'name': 'IsolateThing', |
| 'fixedId': 'true', |
| 'id': 'isolates/1', |
| 'number': '1', |
| }, |
| }, |
| |
| // Missing ID. |
| <String, dynamic>{ |
| 'type': 'FlutterView', |
| }, |
| ], |
| }; |
| |
| Future<vms.VmService> fakeVmConnectionFunction( |
| Uri uri, { |
| Duration? timeout, |
| }) { |
| fakeService.flutterListViews = |
| vms.Response.parse(flutterViewCannedResponseMissingId); |
| return Future<vms.VmService>(() => fakeService); |
| } |
| |
| fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction; |
| final DartVm vm = |
| await DartVm.connect(Uri.parse('http://whatever.com/ws')); |
| expect(vm, isNot(null)); |
| Future<void> failingFunction() async { |
| await vm.getAllFlutterViews(); |
| } |
| |
| // Both views should be invalid as they were missing required fields. |
| expect(failingFunction, throwsA(isA<RpcFormatError>())); |
| }); |
| |
| test('get isolates by pattern', () async { |
| final List<vms.IsolateRef> isolates = <vms.IsolateRef>[ |
| vms.IsolateRef.parse(<String, dynamic>{ |
| 'type': '@Isolate', |
| 'fixedId': 'true', |
| 'id': 'isolates/1', |
| 'name': 'file://thingThatWillNotMatch:main()', |
| 'number': '1', |
| })!, |
| vms.IsolateRef.parse(<String, dynamic>{ |
| 'type': '@Isolate', |
| 'fixedId': 'true', |
| 'id': 'isolates/2', |
| 'name': '0:dart_name_pattern()', |
| 'number': '2', |
| })!, |
| vms.IsolateRef.parse(<String, dynamic>{ |
| 'type': '@Isolate', |
| 'fixedId': 'true', |
| 'id': 'isolates/3', |
| 'name': 'flutterBinary.cm', |
| 'number': '3', |
| })!, |
| vms.IsolateRef.parse(<String, dynamic>{ |
| 'type': '@Isolate', |
| 'fixedId': 'true', |
| 'id': 'isolates/4', |
| 'name': '0:some_other_dart_name_pattern()', |
| 'number': '4', |
| })!, |
| ]; |
| |
| Future<vms.VmService> fakeVmConnectionFunction( |
| Uri uri, { |
| Duration? timeout, |
| }) { |
| fakeService.vm = FakeVM(isolates: isolates); |
| return Future<vms.VmService>(() => fakeService); |
| } |
| |
| fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction; |
| final DartVm vm = |
| await DartVm.connect(Uri.parse('http://whatever.com/ws')); |
| expect(vm, isNot(null)); |
| final List<IsolateRef> matchingFlutterIsolates = |
| await vm.getMainIsolatesByPattern('flutterBinary.cm'); |
| expect(matchingFlutterIsolates.length, 1); |
| final List<IsolateRef> allIsolates = |
| await vm.getMainIsolatesByPattern(''); |
| expect(allIsolates.length, 4); |
| }); |
| |
| test('invalid flutter view missing ID', () async { |
| final Map<String, dynamic> flutterViewCannedResponseMissingIsolateName = |
| <String, dynamic>{ |
| 'views': <Map<String, dynamic>>[ |
| // Missing isolate name. |
| <String, dynamic>{ |
| 'type': 'FlutterView', |
| 'id': 'flutterView1', |
| 'isolate': <String, dynamic>{ |
| 'type': '@Isolate', |
| 'fixedId': 'true', |
| 'id': 'isolates/1', |
| 'number': '1', |
| }, |
| }, |
| ], |
| }; |
| |
| Future<vms.VmService> fakeVmConnectionFunction( |
| Uri uri, { |
| Duration? timeout, |
| }) { |
| fakeService.flutterListViews = |
| vms.Response.parse(flutterViewCannedResponseMissingIsolateName); |
| return Future<vms.VmService>(() => fakeService); |
| } |
| |
| fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction; |
| final DartVm vm = |
| await DartVm.connect(Uri.parse('http://whatever.com/ws')); |
| expect(vm, isNot(null)); |
| Future<void> failingFunction() async { |
| await vm.getAllFlutterViews(); |
| } |
| |
| // Both views should be invalid as they were missing required fields. |
| expect(failingFunction, throwsA(isA<RpcFormatError>())); |
| }); |
| }); |
| } |
| |
| class FakeVmService extends Fake implements vms.VmService { |
| bool disposed = false; |
| vms.Response? flutterListViews; |
| vms.VM? vm; |
| |
| @override |
| Future<vms.VM> getVM() async => vm!; |
| |
| @override |
| Future<void> dispose() async { |
| disposed = true; |
| } |
| |
| @override |
| Future<vms.Response> callMethod(String method, |
| {String? isolateId, Map<String, dynamic>? args}) async { |
| if (method == '_flutter.listViews') { |
| return flutterListViews!; |
| } |
| throw UnimplementedError(method); |
| } |
| |
| @override |
| Future<void> onDone = Future<void>.value(); |
| } |
| |
| class FakeVM extends Fake implements vms.VM { |
| FakeVM({ |
| this.isolates = const <vms.IsolateRef>[], |
| }); |
| |
| @override |
| List<vms.IsolateRef>? isolates; |
| } |