blob: 8159e807dfd843c3ea229ec0b4d371789a02acb7 [file] [log] [blame]
// Copyright (c) 2013, 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 vmservice_test_helper;
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:expect/expect.dart';
abstract class ServiceWebSocketRequestHelper {
final String url;
final Completer _completer = new Completer();
WebSocket _socket;
ServiceWebSocketRequestHelper(this.url);
// Returns [this] when connected.
Future connect() {
return WebSocket.connect(url).then((ws) {
_socket = ws;
_socket.listen((message) {
var map = JSON.decode(message);
var response = JSON.decode(map['response']);
onResponse(map['seq'], response);
});
return this;
});
}
void complete() {
_completer.complete(this);
}
Future get completed => _completer.future;
// Must call complete.
void onResponse(var seq, Map response);
void runTest();
Future sendMessage(var seq, List<String> path) {
var map = {
'seq': seq,
'path': path
};
var message = JSON.encode(map);
_socket.add(message);
return _completer.future;
}
}
abstract class VmServiceRequestHelper {
final Uri uri;
final HttpClient client;
VmServiceRequestHelper(String url) :
uri = Uri.parse(url),
client = new HttpClient();
Future makeRequest() {
print('** GET: $uri');
return client.getUrl(uri)
.then((HttpClientRequest request) => request.close())
.then((HttpClientResponse response) {
return response
.fold(new BytesBuilder(), (b, d) => b..add(d))
.then((builder) {
return _requestCompleted(builder.takeBytes(), response);
});
}).catchError((error) {
onRequestFailed(error);
});
}
Future _requestCompleted(List<int> data, HttpClientResponse response) {
Expect.equals(200, response.statusCode, 'Invalid HTTP Status Code');
var replyAsString;
try {
replyAsString = UTF8.decode(data);
} catch (e) {
onRequestFailed(e);
return null;
}
print('** Response: $replyAsString');
var reply;
try {
reply = JSON.decode(replyAsString);
} catch (e) {
onRequestFailed(e);
return null;
}
if (reply is! Map) {
onRequestFailed('Reply was not a map: $reply');
return null;
}
if (reply['type'] == null) {
onRequestFailed('Reply does not contain a type key: $reply');
return null;
}
var r;
try {
r = onRequestCompleted(reply);
} catch (e) {
r = onRequestFailed('Test callback failed: $e');
}
return r;
}
Future onRequestFailed(dynamic error) {
Expect.fail('Failed to make request: $error');
}
Future onRequestCompleted(Map response);
}
class TestLauncher {
final String script;
Process process;
TestLauncher(this.script);
String get scriptPath {
var dartScript = Platform.script.toFilePath();
var splitPoint = dartScript.lastIndexOf(Platform.pathSeparator);
var scriptDirectory = dartScript.substring(0, splitPoint);
return scriptDirectory + Platform.pathSeparator + script;
}
Future<int> launch() {
String dartExecutable = Platform.executable;
print('** Launching $scriptPath');
return Process.start(dartExecutable,
['--enable-vm-service:0', scriptPath]).then((p) {
Completer completer = new Completer();
process = p;
var portNumber;
var blank;
var first = true;
process.stdout.transform(UTF8.decoder)
.transform(new LineSplitter()).listen((line) {
if (line.startsWith('VMService listening on port ')) {
RegExp portExp = new RegExp(r"\d+");
var port = portExp.stringMatch(line);
portNumber = int.parse(port);
}
if (line == '') {
// Received blank line.
blank = true;
}
if (portNumber != null && blank == true && first == true) {
completer.complete(portNumber);
// Stop repeat completions.
first = false;
print('** Signaled to run test queries on $portNumber');
}
print(line);
});
process.stderr.transform(UTF8.decoder)
.transform(new LineSplitter()).listen((line) {
print(line);
});
process.exitCode.then((code) {
Expect.equals(0, code, 'Launched dart executable exited with error.');
});
return completer.future;
});
}
void requestExit() {
print('** Requesting script to exit.');
process.stdin.add([32, 13, 10]);
}
}
class IsolateListTester {
final Map isolateList;
IsolateListTester(this.isolateList) {
// The reply is an IsolateList.
Expect.equals('IsolateList', isolateList['type'], 'Not an IsolateList.');
}
void checkIsolateCount(int n) {
Expect.equals(n, isolateList['members'].length, 'Isolate count not $n');
}
void checkIsolateIdExists(String id) {
var exists = false;
isolateList['members'].forEach((isolate) {
if (isolate['id'] == id) {
exists = true;
}
});
Expect.isTrue(exists, 'No isolate with id: $id');
}
String checkIsolateNameContains(String name) {
var exists = false;
String id;
isolateList['members'].forEach((isolate) {
if (isolate['name'].contains(name)) {
exists = true;
id = isolate['id'];
}
});
Expect.isTrue(exists, 'No isolate with name: $name');
return id;
}
void checkIsolateNamePrefix(String id, String name) {
var exists = false;
isolateList['members'].forEach((isolate) {
if (isolate['id'] == id) {
exists = true;
Expect.isTrue(isolate['name'].startsWith(name),
'Isolate $id does not have name prefix: $name'
' (was ${isolate['name']})');
}
});
Expect.isTrue(exists, 'No isolate with id: $id');
}
}
class ClassTableHelper {
final Map classTable;
ClassTableHelper(this.classTable) {
Expect.equals('ClassList', classTable['type'], 'Not a ClassTable.');
}
bool classExists(String user_name) {
List members = classTable['members'];
for (var i = 0; i < members.length; i++) {
Map klass = members[i];
if (klass['user_name'] == user_name) {
return true;
}
}
return false;
}
String classId(String user_name) {
List members = classTable['members'];
for (var i = 0; i < members.length; i++) {
Map klass = members[i];
if (klass['user_name'] == user_name) {
return klass['id'];
}
}
return null;
}
}
class FieldRequestHelper extends VmServiceRequestHelper {
FieldRequestHelper(port, isolate_id, field_id) :
super('http://127.0.0.1:$port/$isolate_id/$field_id');
Map field;
onRequestCompleted(Map reply) {
Expect.equals('Field', reply['type']);
field = reply;
return new Future.value(this);
}
}
class ClassFieldRequestHelper extends VmServiceRequestHelper {
final List<String> fieldNames;
int port_;
String isolate_id_;
ClassFieldRequestHelper(port, isolate_id, class_id, this.fieldNames) :
super('http://127.0.0.1:$port/$isolate_id/$class_id') {
port_ = port;
isolate_id_ = isolate_id;
}
final Map<String, Map> fields = new Map<String, Map>();
onRequestCompleted(Map reply) {
Expect.equals('Class', reply['type']);
List<Map> class_fields = reply['fields'];
List<Future> requests = new List<Future>();
fieldNames.forEach((fn) {
class_fields.forEach((f) {
if (f['user_name'] == fn) {
var request = new FieldRequestHelper(port_, isolate_id_, f['id']);
requests.add(request.makeRequest());
}
});
});
return Future.wait(requests).then((a) {
a.forEach((FieldRequestHelper field) {
fields[field.field['user_name']] = field.field;
});
return this;
});
}
}