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

View File

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

View File

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

View File

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

View File

@ -9,9 +9,9 @@ import 'message.dart';
class GetHealth implements Command { class GetHealth implements Command {
final String kind = 'get_health'; 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. /// Application health status.

View File

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