[driver] serialize commands to plain strings

Turns out VM service only accepts flat parameter name/value pairs. It
only worked this far because vm_service_client (mistakenly) uses JSON
encoder to encode parameters.

This change separates the Command type hierarchy from that of Result and
tightens type constraints on Command object parameters.
This commit is contained in:
yjbanov 2016-03-02 17:50:09 -08:00
parent 60b8127155
commit 008785be8f
6 changed files with 51 additions and 51 deletions

View File

@ -148,9 +148,9 @@ class FlutterDriver {
final VMIsolateRef _appIsolate;
Future<Map<String, dynamic>> _sendCommand(Command command) async {
Map<String, dynamic> json = <String, dynamic>{'command': command.kind}
..addAll(command.toJson());
return _appIsolate.invokeExtension(_kFlutterExtensionMethod, json)
Map<String, String> parameters = <String, String>{'command': command.kind}
..addAll(command.serialize());
return _appIsolate.invokeExtension(_kFlutterExtensionMethod, parameters)
.then((Map<String, dynamic> result) => result, onError: (error, stackTrace) {
throw new DriverError(
'Failed to fulfill ${command.runtimeType} due to remote error',

View File

@ -60,11 +60,11 @@ class FlutterDriverExtension {
};
_commandDeserializers = {
'get_health': GetHealth.fromJson,
'find': Find.fromJson,
'tap': Tap.fromJson,
'get_text': GetText.fromJson,
'scroll': Scroll.fromJson,
'get_health': GetHealth.deserialize,
'find': Find.deserialize,
'tap': Tap.deserialize,
'get_text': GetText.deserialize,
'scroll': Scroll.deserialize,
};
}

View File

@ -15,10 +15,10 @@ class Find extends Command {
final SearchSpecification searchSpec;
Map<String, dynamic> toJson() => searchSpec.toJson();
Map<String, String> serialize() => searchSpec.serialize();
static Find fromJson(Map<String, dynamic> json) {
return new Find(SearchSpecification.fromJson(json));
static Find deserialize(Map<String, String> json) {
return new Find(SearchSpecification.deserialize(json));
}
static _throwInvalidKeyValueType(String invalidType) {
@ -27,20 +27,20 @@ class Find extends Command {
}
/// Describes how to the driver should search for elements.
abstract class SearchSpecification extends Message {
abstract class SearchSpecification {
String get searchSpecType;
static SearchSpecification fromJson(Map<String, dynamic> json) {
static SearchSpecification deserialize(Map<String, String> json) {
String searchSpecType = json['searchSpecType'];
switch(searchSpecType) {
case 'ByValueKey': return ByValueKey.fromJson(json);
case 'ByTooltipMessage': return ByTooltipMessage.fromJson(json);
case 'ByText': return ByText.fromJson(json);
case 'ByValueKey': return ByValueKey.deserialize(json);
case 'ByTooltipMessage': return ByTooltipMessage.deserialize(json);
case 'ByText': return ByText.deserialize(json);
}
throw new DriverError('Unsupported search specification type $searchSpecType');
}
Map<String, dynamic> toJson() => {
Map<String, String> serialize() => {
'searchSpecType': searchSpecType,
};
}
@ -54,11 +54,11 @@ class ByTooltipMessage extends SearchSpecification {
/// Tooltip message text.
final String text;
Map<String, dynamic> toJson() => super.toJson()..addAll({
Map<String, String> serialize() => super.serialize()..addAll({
'text': text,
});
static ByTooltipMessage fromJson(Map<String, dynamic> json) {
static ByTooltipMessage deserialize(Map<String, String> json) {
return new ByTooltipMessage(json['text']);
}
}
@ -71,11 +71,11 @@ class ByText extends SearchSpecification {
final String text;
Map<String, dynamic> toJson() => super.toJson()..addAll({
Map<String, String> serialize() => super.serialize()..addAll({
'text': text,
});
static ByText fromJson(Map<String, dynamic> json) {
static ByText deserialize(Map<String, String> json) {
return new ByText(json['text']);
}
}
@ -103,12 +103,12 @@ class ByValueKey extends SearchSpecification {
/// May be one of "String", "int". The list of supported types may change.
final String keyValueType;
Map<String, dynamic> toJson() => super.toJson()..addAll({
Map<String, String> serialize() => super.serialize()..addAll({
'keyValueString': keyValueString,
'keyValueType': keyValueType,
});
static ByValueKey fromJson(Map<String, dynamic> json) {
static ByValueKey deserialize(Map<String, String> json) {
String keyValueString = json['keyValueString'];
String keyValueType = json['keyValueType'];
switch(keyValueType) {
@ -130,14 +130,14 @@ class ByValueKey extends SearchSpecification {
class GetText extends CommandWithTarget {
final String kind = 'get_text';
static GetText fromJson(Map<String, dynamic> json) {
static GetText deserialize(Map<String, String> json) {
return new GetText(new ObjectRef(json['targetRef']));
}
/// [targetRef] identifies an element that contains a piece of text.
GetText(ObjectRef targetRef) : super(targetRef);
Map<String, dynamic> toJson() => super.toJson();
Map<String, String> serialize() => super.serialize();
}
class GetTextResult extends Result {

View File

@ -9,11 +9,11 @@ class Tap extends CommandWithTarget {
Tap(ObjectRef targetRef) : super(targetRef);
static Tap fromJson(Map<String, dynamic> json) {
static Tap deserialize(Map<String, String> json) {
return new Tap(new ObjectRef(json['targetRef']));
}
Map<String, dynamic> toJson() => super.toJson();
Map<String, String> serialize() => super.serialize();
}
class TapResult extends Result {
@ -37,7 +37,7 @@ class Scroll extends CommandWithTarget {
this.frequency
) : super(targetRef);
static Scroll fromJson(Map<String, dynamic> json) {
static Scroll deserialize(Map<String, dynamic> json) {
return new Scroll(
new ObjectRef(json['targetRef']),
double.parse(json['dx']),
@ -59,11 +59,11 @@ class Scroll extends CommandWithTarget {
/// The frequency in Hz of the generated move events.
final int frequency;
Map<String, dynamic> toJson() => super.toJson()..addAll({
'dx': dx,
'dy': dy,
'duration': duration.inMicroseconds,
'frequency': frequency,
Map<String, String> serialize() => super.serialize()..addAll({
'dx': '$dx',
'dy': '$dy',
'duration': '${duration.inMicroseconds}',
'frequency': '$frequency',
});
}

View File

@ -9,9 +9,9 @@ import 'message.dart';
class GetHealth implements Command {
final String kind = 'get_health';
static fromJson(Map<String, dynamic> json) => new GetHealth();
static deserialize(Map<String, String> json) => new GetHealth();
Map<String, dynamic> toJson() => const {};
Map<String, String> serialize() => const {};
}
/// Application health status.

View File

@ -4,23 +4,23 @@
import 'error.dart';
/// A piece of data travelling between Flutter Driver and a Flutter application.
abstract class Message {
/// An object sent from the Flutter Driver to a Flutter application to instruct
/// the application to perform a task.
abstract class Command {
/// Identifies the type of the command object and of the handler.
String get kind;
/// Serializes this command to parameter name/value pairs.
Map<String, String> serialize();
}
/// An object sent from a Flutter application back to the Flutter Driver in
/// response to a command.
abstract class Result {
/// Serializes this message to a JSON map.
Map<String, dynamic> toJson();
}
/// A message that travels from the Flutter Driver to a Flutter application to
/// instruct the application to perform a task.
abstract class Command extends Message {
/// Identifies the type of the command object and of the handler.
String get kind;
}
/// A message sent from a Flutter application back to the Flutter Driver in
/// response to a command.
abstract class Result extends Message { }
/// A serializable reference to an object that lives in the application isolate.
class ObjectRef extends Result {
ObjectRef(this.objectReferenceKey);
@ -47,7 +47,7 @@ class ObjectRef extends Result {
/// A command aimed at an object represented by [targetRef].
///
/// Implementations must provide a concrete [kind]. If additional data is
/// required beyond the [targetRef] the implementation may override [toJson]
/// required beyond the [targetRef] the implementation may override [serialize]
/// and add more keys to the returned map.
abstract class CommandWithTarget extends Command {
CommandWithTarget(ObjectRef ref) : this.targetRef = ref?.objectReferenceKey {
@ -66,10 +66,10 @@ abstract class CommandWithTarget extends Command {
///
/// Example:
///
/// Map<String, dynamic> toJson() => super.toJson()..addAll({
/// Map<String, String> toJson() => super.toJson()..addAll({
/// 'foo': this.foo,
/// });
Map<String, dynamic> toJson() => {
Map<String, String> serialize() => <String, String>{
'targetRef': targetRef,
};
}