blob: 21bbb0513089125a840e62744a25a6952f10e730 [file] [log] [blame]
// 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;
}