[vm_service] Get tester example functional and clean up
Makes adjustments so `example/vm_service_tester.dart` runs successfully, and complete some clean up to make the code and comments in the file more consistent. Closes https://github.com/dart-lang/sdk/issues/55753 TEST=N/A Change-Id: Ib91985792a25a27008c2ef96786a40bb7a26ad57 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/374242 Reviewed-by: Derek Xu <derekx@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com> Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
committed by
Commit Queue
parent
951fbd9c49
commit
d60221ed47
@@ -2,7 +2,8 @@
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library service_tester;
|
||||
/// An example of using the the libraries provided by `package:vm_service`.
|
||||
library;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
@@ -14,8 +15,8 @@ import 'package:test/test.dart';
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
import 'package:vm_service/vm_service_io.dart';
|
||||
|
||||
final String host = 'localhost';
|
||||
final int port = 7575;
|
||||
const String host = 'localhost';
|
||||
const int port = 7575;
|
||||
|
||||
late VmService serviceClient;
|
||||
|
||||
@@ -27,35 +28,40 @@ void main() {
|
||||
});
|
||||
|
||||
test('integration', () async {
|
||||
String sdk = path.dirname(path.dirname(Platform.resolvedExecutable));
|
||||
|
||||
print('Using sdk at $sdk.');
|
||||
final sdkPath = path.dirname(path.dirname(Platform.resolvedExecutable));
|
||||
print('Using sdk at $sdkPath.');
|
||||
|
||||
// pause_isolates_on_start, pause_isolates_on_exit
|
||||
process = await Process.start('$sdk/bin/dart', [
|
||||
'--pause_isolates_on_start',
|
||||
'--enable-vm-service=$port',
|
||||
'--disable-service-auth-codes',
|
||||
'example/sample_main.dart'
|
||||
]);
|
||||
final sampleProcess = process = await Process.start(
|
||||
Platform.resolvedExecutable,
|
||||
[
|
||||
'--pause-isolates-on-start',
|
||||
'--enable-vm-service=$port',
|
||||
'--disable-service-auth-codes',
|
||||
'example/sample_main.dart',
|
||||
],
|
||||
);
|
||||
|
||||
print('dart process started');
|
||||
print('Dart process started.');
|
||||
|
||||
unawaited(process!.exitCode.then((code) => print('vm exited: $code')));
|
||||
process!.stdout.transform(utf8.decoder).listen(print);
|
||||
process!.stderr.transform(utf8.decoder).listen(print);
|
||||
unawaited(sampleProcess.exitCode.then((code) => print('vm exited: $code')));
|
||||
sampleProcess.stdout.transform(utf8.decoder).listen(print);
|
||||
sampleProcess.stderr.transform(utf8.decoder).listen(print);
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 500));
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
final wsUri = 'ws://$host$port/ws';
|
||||
serviceClient = await vmServiceConnectUri(wsUri, log: StdoutLog());
|
||||
final wsUri = Uri(scheme: 'ws', host: host, port: port, path: 'ws');
|
||||
serviceClient = await vmServiceConnectUri(
|
||||
wsUri.toString(),
|
||||
log: StdoutLog(),
|
||||
);
|
||||
|
||||
print('socket connected');
|
||||
print('VM service web socket connected.');
|
||||
|
||||
serviceClient.onSend.listen((str) => print('--> $str'));
|
||||
|
||||
// The next listener will bail out if you toggle this to false, which we need
|
||||
// to do for some things like the custom service registration tests.
|
||||
// The next listener will bail out if you toggle this to false, which is
|
||||
// needed for some things like the custom service registration tests.
|
||||
var checkResponseJsonCompatibility = true;
|
||||
serviceClient.onReceive.listen((str) {
|
||||
print('<-- $str');
|
||||
@@ -65,25 +71,41 @@ void main() {
|
||||
// For each received event, check that we can deserialize it and
|
||||
// reserialize it back to the same exact representation (minus private
|
||||
// fields).
|
||||
var json = jsonDecode(str);
|
||||
final json = jsonDecode(str);
|
||||
var originalJson = json['result'] as Map<String, dynamic>?;
|
||||
if (originalJson == null && json['method'] == 'streamNotify') {
|
||||
originalJson = json['params']['event'];
|
||||
}
|
||||
expect(originalJson, isNotNull, reason: 'Unrecognized event type! $json');
|
||||
|
||||
var instance =
|
||||
createServiceObject(originalJson, const ['Event', 'Success']);
|
||||
final instance =
|
||||
createServiceObject(originalJson!, const ['Event', 'Success']);
|
||||
expect(instance, isNotNull,
|
||||
reason: 'failed to deserialize object $originalJson!');
|
||||
reason: 'Failed to deserialize object $originalJson!');
|
||||
|
||||
var reserializedJson = (instance as dynamic).toJson();
|
||||
final reserializedJson = (instance as dynamic).toJson();
|
||||
|
||||
forEachNestedMap(originalJson!, (obj) {
|
||||
// Private fields that we don't reproduce
|
||||
forEachNestedMap(originalJson, (obj) {
|
||||
// Remove private fields that we don't reproduce.
|
||||
obj.removeWhere((k, v) => k.startsWith('_'));
|
||||
// Extra fields that aren't specified and we don't reproduce
|
||||
|
||||
// Remove extra fields that aren't specified and we don't reproduce.
|
||||
obj.remove('isExport');
|
||||
obj.remove('isolate_group');
|
||||
obj.remove('parameterizedClass');
|
||||
|
||||
// Convert `Null` instances in the original JSON to
|
||||
// just `null` as `createServiceObject` will use `null`
|
||||
// to represent the reference.
|
||||
obj.updateAll((key, value) {
|
||||
if (value is Map &&
|
||||
value['type'] == '@Instance' &&
|
||||
value['kind'] == 'Null') {
|
||||
return null;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
forEachNestedMap(reserializedJson, (obj) {
|
||||
@@ -104,14 +126,14 @@ void main() {
|
||||
unawaited(serviceClient.streamListen(EventStreams.kDebug));
|
||||
unawaited(serviceClient.streamListen(EventStreams.kStdout));
|
||||
|
||||
VM vm = await serviceClient.getVM();
|
||||
final vm = await serviceClient.getVM();
|
||||
print('hostCPU=${vm.hostCPU}');
|
||||
print(await serviceClient.getVersion());
|
||||
List<IsolateRef> isolates = vm.isolates!;
|
||||
final isolates = vm.isolates!;
|
||||
print(isolates);
|
||||
|
||||
// Disable the json reserialization checks since custom services are not
|
||||
// supported.
|
||||
// Disable the json reserialization checks since custom services are
|
||||
// not supported.
|
||||
checkResponseJsonCompatibility = false;
|
||||
await testServiceRegistration();
|
||||
checkResponseJsonCompatibility = true;
|
||||
@@ -119,23 +141,23 @@ void main() {
|
||||
await testScriptParse(vm.isolates!.first);
|
||||
await testSourceReport(vm.isolates!.first);
|
||||
|
||||
IsolateRef isolateRef = isolates.first;
|
||||
final isolateRef = isolates.first;
|
||||
print(await serviceClient.resume(isolateRef.id!));
|
||||
|
||||
print('waiting for client to shut down...');
|
||||
print('Waiting for service client to shut down...');
|
||||
await serviceClient.dispose();
|
||||
|
||||
await serviceClient.onDone;
|
||||
print('service client shut down');
|
||||
print('Service client shut down.');
|
||||
});
|
||||
}
|
||||
|
||||
// Deeply traverses a map and calls [cb] with each nested map and the
|
||||
// parent map.
|
||||
void forEachNestedMap(Map input, Function(Map) cb) {
|
||||
var queue = Queue.from([input]);
|
||||
/// Deeply traverses the [input] map and calls [cb] with
|
||||
/// each nested map and the parent map.
|
||||
void forEachNestedMap(Map input, void Function(Map) cb) {
|
||||
final queue = Queue.from([input]);
|
||||
while (queue.isNotEmpty) {
|
||||
var next = queue.removeFirst();
|
||||
final next = queue.removeFirst();
|
||||
if (next is Map) {
|
||||
cb(next);
|
||||
queue.addAll(next.values);
|
||||
@@ -145,7 +167,7 @@ void forEachNestedMap(Map input, Function(Map) cb) {
|
||||
}
|
||||
}
|
||||
|
||||
Future testServiceRegistration() async {
|
||||
Future<void> testServiceRegistration() async {
|
||||
const String serviceName = 'serviceName';
|
||||
const String serviceAlias = 'serviceAlias';
|
||||
const String movedValue = 'movedValue';
|
||||
@@ -157,15 +179,18 @@ Future testServiceRegistration() async {
|
||||
};
|
||||
});
|
||||
await serviceClient.registerService(serviceName, serviceAlias);
|
||||
final wsUri = 'ws://$host$port/ws';
|
||||
VmService otherClient = await vmServiceConnectUri(wsUri, log: StdoutLog());
|
||||
Completer completer = Completer();
|
||||
final wsUri = Uri(scheme: 'ws', host: host, port: port, path: 'ws');
|
||||
final otherClient = await vmServiceConnectUri(
|
||||
wsUri.toString(),
|
||||
log: StdoutLog(),
|
||||
);
|
||||
final completer = Completer();
|
||||
otherClient.onEvent('Service').listen((e) async {
|
||||
if (e.service == serviceName && e.kind == EventKind.kServiceRegistered) {
|
||||
assert(e.alias == serviceAlias);
|
||||
Response? response = await serviceClient.callMethod(
|
||||
final response = await serviceClient.callMethod(
|
||||
e.method!,
|
||||
args: <String, dynamic>{'input': movedValue},
|
||||
args: {'input': movedValue},
|
||||
);
|
||||
assert(response.json!['output'] == movedValue);
|
||||
completer.complete();
|
||||
@@ -176,14 +201,14 @@ Future testServiceRegistration() async {
|
||||
await otherClient.dispose();
|
||||
}
|
||||
|
||||
Future testScriptParse(IsolateRef isolateRef) async {
|
||||
Future<void> testScriptParse(IsolateRef isolateRef) async {
|
||||
final isolateId = isolateRef.id!;
|
||||
final Isolate isolate = await serviceClient.getIsolate(isolateId);
|
||||
final Library rootLibrary =
|
||||
final isolate = await serviceClient.getIsolate(isolateId);
|
||||
final rootLibrary =
|
||||
await serviceClient.getObject(isolateId, isolate.rootLib!.id!) as Library;
|
||||
final ScriptRef scriptRef = rootLibrary.scripts!.first;
|
||||
final scriptRef = rootLibrary.scripts!.first;
|
||||
|
||||
final Script script =
|
||||
final script =
|
||||
await serviceClient.getObject(isolateId, scriptRef.id!) as Script;
|
||||
print(script);
|
||||
print(script.uri);
|
||||
@@ -192,21 +217,23 @@ Future testScriptParse(IsolateRef isolateRef) async {
|
||||
print(script.tokenPosTable!.length);
|
||||
}
|
||||
|
||||
Future testSourceReport(IsolateRef isolateRef) async {
|
||||
Future<void> testSourceReport(IsolateRef isolateRef) async {
|
||||
final isolateId = isolateRef.id!;
|
||||
final Isolate isolate = await serviceClient.getIsolate(isolateId);
|
||||
final Library rootLibrary =
|
||||
final isolate = await serviceClient.getIsolate(isolateId);
|
||||
final rootLibrary =
|
||||
await serviceClient.getObject(isolateId, isolate.rootLib!.id!) as Library;
|
||||
final ScriptRef scriptRef = rootLibrary.scripts!.first;
|
||||
final scriptRef = rootLibrary.scripts!.first;
|
||||
|
||||
// make sure some code has run
|
||||
// Make sure that some code has run.
|
||||
await serviceClient.resume(isolateId);
|
||||
await Future.delayed(const Duration(milliseconds: 25));
|
||||
|
||||
final SourceReport sourceReport = await serviceClient.getSourceReport(
|
||||
isolateId, [SourceReportKind.kCoverage],
|
||||
scriptId: scriptRef.id);
|
||||
for (SourceReportRange range in sourceReport.ranges!) {
|
||||
final sourceReport = await serviceClient.getSourceReport(
|
||||
isolateId,
|
||||
[SourceReportKind.kCoverage],
|
||||
scriptId: scriptRef.id,
|
||||
);
|
||||
for (final range in sourceReport.ranges!) {
|
||||
print(' $range');
|
||||
if (range.coverage != null) {
|
||||
print(' ${range.coverage}');
|
||||
|
||||
@@ -2512,7 +2512,6 @@ class AllocationProfile extends Response {
|
||||
///
|
||||
/// If the field is uninitialized, the `value` will be the `NotInitialized`
|
||||
/// [Sentinel].
|
||||
///
|
||||
class BoundField {
|
||||
static BoundField? parse(Map<String, dynamic>? json) =>
|
||||
json == null ? null : BoundField._fromJson(json);
|
||||
@@ -3073,10 +3072,15 @@ class CodeRef extends ObjRef {
|
||||
/// What kind of code object is this?
|
||||
/*CodeKind*/ String? kind;
|
||||
|
||||
/// This code object's corresponding function.
|
||||
@optional
|
||||
FuncRef? function;
|
||||
|
||||
CodeRef({
|
||||
this.name,
|
||||
this.kind,
|
||||
required String id,
|
||||
this.function,
|
||||
}) : super(
|
||||
id: id,
|
||||
);
|
||||
@@ -3084,6 +3088,8 @@ class CodeRef extends ObjRef {
|
||||
CodeRef._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
|
||||
name = json['name'] ?? '';
|
||||
kind = json['kind'] ?? '';
|
||||
function =
|
||||
createServiceObject(json['function'], const ['FuncRef']) as FuncRef?;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -3097,6 +3103,7 @@ class CodeRef extends ObjRef {
|
||||
'name': name ?? '',
|
||||
'kind': kind ?? '',
|
||||
});
|
||||
_setIfNotNull(json, 'function', function?.toJson());
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -3123,10 +3130,16 @@ class Code extends Obj implements CodeRef {
|
||||
@override
|
||||
/*CodeKind*/ String? kind;
|
||||
|
||||
/// This code object's corresponding function.
|
||||
@optional
|
||||
@override
|
||||
FuncRef? function;
|
||||
|
||||
Code({
|
||||
this.name,
|
||||
this.kind,
|
||||
required String id,
|
||||
this.function,
|
||||
}) : super(
|
||||
id: id,
|
||||
);
|
||||
@@ -3134,6 +3147,8 @@ class Code extends Obj implements CodeRef {
|
||||
Code._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
|
||||
name = json['name'] ?? '';
|
||||
kind = json['kind'] ?? '';
|
||||
function =
|
||||
createServiceObject(json['function'], const ['FuncRef']) as FuncRef?;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -3147,6 +3162,7 @@ class Code extends Obj implements CodeRef {
|
||||
'name': name ?? '',
|
||||
'kind': kind ?? '',
|
||||
});
|
||||
_setIfNotNull(json, 'function', function?.toJson());
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
@@ -2085,6 +2085,9 @@ class @Code extends @Object {
|
||||
|
||||
// What kind of code object is this?
|
||||
CodeKind kind;
|
||||
|
||||
// This code object's corresponding function.
|
||||
@Function function [optional];
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2097,6 +2100,9 @@ class Code extends Object {
|
||||
|
||||
// What kind of code object is this?
|
||||
CodeKind kind;
|
||||
|
||||
// This code object's corresponding function.
|
||||
@Function function [optional];
|
||||
}
|
||||
```
|
||||
|
||||
@@ -3188,7 +3194,7 @@ class Instance extends Object {
|
||||
// RegExp
|
||||
@Instance pattern [optional];
|
||||
|
||||
// The function associated with a Closure instance.
|
||||
// The function associated with a Closure instance.
|
||||
//
|
||||
// Provided for instance kinds:
|
||||
// Closure
|
||||
|
||||
Reference in New Issue
Block a user