ec04c1f335
Change-Id: Iafa4b9c8aa5dc696c5746248a8701b4f6e875979 TEST=Existing tests. Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/191980 Commit-Queue: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Nicholas Shahan <nshahan@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com>
226 lines
7.4 KiB
Dart
226 lines
7.4 KiB
Dart
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
// 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;
|
|
|
|
import 'dart:async';
|
|
import 'dart:collection';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:pedantic/pedantic.dart';
|
|
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;
|
|
|
|
late VmService serviceClient;
|
|
|
|
void main() {
|
|
Process? process;
|
|
|
|
tearDown(() {
|
|
process?.kill();
|
|
});
|
|
|
|
test('integration', () async {
|
|
String sdk = path.dirname(path.dirname(Platform.resolvedExecutable));
|
|
|
|
print('Using sdk at ${sdk}.');
|
|
|
|
// 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'
|
|
]);
|
|
|
|
print('dart process started');
|
|
|
|
// ignore: unawaited_futures
|
|
process!.exitCode.then((code) => print('vm exited: ${code}'));
|
|
process!.stdout.transform(utf8.decoder).listen(print);
|
|
process!.stderr.transform(utf8.decoder).listen(print);
|
|
|
|
await Future.delayed(Duration(milliseconds: 500));
|
|
|
|
// ignore: deprecated_member_use_from_same_package
|
|
serviceClient = await vmServiceConnect(host, port, log: StdoutLog());
|
|
|
|
print('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.
|
|
var checkResponseJsonCompatibility = true;
|
|
serviceClient.onReceive.listen((str) {
|
|
print('<-- ${str}');
|
|
|
|
if (!checkResponseJsonCompatibility) return;
|
|
|
|
// 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);
|
|
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']);
|
|
expect(instance, isNotNull,
|
|
reason: 'failed to deserialize object $originalJson!');
|
|
|
|
var reserializedJson = (instance as dynamic).toJson();
|
|
|
|
forEachNestedMap(originalJson!, (obj) {
|
|
// Private fields that we don't reproduce
|
|
obj.removeWhere((k, v) => k.startsWith('_'));
|
|
// Extra fields that aren't specified and we don't reproduce
|
|
obj.remove('isExport');
|
|
});
|
|
|
|
forEachNestedMap(reserializedJson, (obj) {
|
|
// We provide explicit defaults for these, need to remove them.
|
|
obj.remove('valueAsStringIsTruncated');
|
|
});
|
|
|
|
expect(reserializedJson, equals(originalJson));
|
|
});
|
|
|
|
serviceClient.onIsolateEvent.listen((e) => print('onIsolateEvent: ${e}'));
|
|
serviceClient.onDebugEvent.listen((e) => print('onDebugEvent: ${e}'));
|
|
serviceClient.onGCEvent.listen((e) => print('onGCEvent: ${e}'));
|
|
serviceClient.onStdoutEvent.listen((e) => print('onStdoutEvent: ${e}'));
|
|
serviceClient.onStderrEvent.listen((e) => print('onStderrEvent: ${e}'));
|
|
|
|
unawaited(serviceClient.streamListen(EventStreams.kIsolate));
|
|
unawaited(serviceClient.streamListen(EventStreams.kDebug));
|
|
unawaited(serviceClient.streamListen(EventStreams.kStdout));
|
|
|
|
VM vm = await serviceClient.getVM();
|
|
print('hostCPU=${vm.hostCPU}');
|
|
print(await serviceClient.getVersion());
|
|
List<IsolateRef> isolates = await vm.isolates!;
|
|
print(isolates);
|
|
|
|
// Disable the json reserialization checks since custom services are not
|
|
// supported.
|
|
checkResponseJsonCompatibility = false;
|
|
await testServiceRegistration();
|
|
checkResponseJsonCompatibility = true;
|
|
|
|
await testScriptParse(vm.isolates!.first);
|
|
await testSourceReport(vm.isolates!.first);
|
|
|
|
IsolateRef isolateRef = isolates.first;
|
|
print(await serviceClient.resume(isolateRef.id!));
|
|
|
|
print('waiting for client to shut down...');
|
|
await serviceClient.dispose();
|
|
|
|
await serviceClient.onDone;
|
|
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]);
|
|
while (queue.isNotEmpty) {
|
|
var next = queue.removeFirst();
|
|
if (next is Map) {
|
|
cb(next);
|
|
queue.addAll(next.values);
|
|
} else if (next is List) {
|
|
queue.addAll(next);
|
|
}
|
|
}
|
|
}
|
|
|
|
Future testServiceRegistration() async {
|
|
const String serviceName = 'serviceName';
|
|
const String serviceAlias = 'serviceAlias';
|
|
const String movedValue = 'movedValue';
|
|
serviceClient.registerServiceCallback(serviceName,
|
|
(Map<String, dynamic> params) async {
|
|
assert(params['input'] == movedValue);
|
|
return <String, dynamic>{
|
|
'result': {'output': params['input']}
|
|
};
|
|
});
|
|
await serviceClient.registerService(serviceName, serviceAlias);
|
|
VmService otherClient =
|
|
// ignore: deprecated_member_use_from_same_package
|
|
await vmServiceConnect(host, port, log: StdoutLog());
|
|
Completer 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(
|
|
e.method!,
|
|
args: <String, dynamic>{'input': movedValue},
|
|
);
|
|
assert(response.json!['output'] == movedValue);
|
|
completer.complete();
|
|
}
|
|
});
|
|
await otherClient.streamListen('Service');
|
|
await completer.future;
|
|
await otherClient.dispose();
|
|
}
|
|
|
|
Future testScriptParse(IsolateRef isolateRef) async {
|
|
final isolateId = isolateRef.id!;
|
|
final Isolate isolate = await serviceClient.getIsolate(isolateId);
|
|
final Library rootLibrary =
|
|
await serviceClient.getObject(isolateId, isolate.rootLib!.id!) as Library;
|
|
final ScriptRef scriptRef = rootLibrary.scripts!.first;
|
|
|
|
final Script script =
|
|
await serviceClient.getObject(isolateId, scriptRef.id!) as Script;
|
|
print(script);
|
|
print(script.uri);
|
|
print(script.library);
|
|
print(script.source!.length);
|
|
print(script.tokenPosTable!.length);
|
|
}
|
|
|
|
Future testSourceReport(IsolateRef isolateRef) async {
|
|
final isolateId = isolateRef.id!;
|
|
final Isolate isolate = await serviceClient.getIsolate(isolateId);
|
|
final Library rootLibrary =
|
|
await serviceClient.getObject(isolateId, isolate.rootLib!.id!) as Library;
|
|
final ScriptRef scriptRef = rootLibrary.scripts!.first;
|
|
|
|
// make sure 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!) {
|
|
print(' $range');
|
|
if (range.coverage != null) {
|
|
print(' ${range.coverage}');
|
|
}
|
|
}
|
|
print(sourceReport);
|
|
}
|
|
|
|
class StdoutLog extends Log {
|
|
void warning(String message) => print(message);
|
|
|
|
void severe(String message) => print(message);
|
|
}
|