[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.
diff --git a/packages/flutter_driver/lib/src/driver.dart b/packages/flutter_driver/lib/src/driver.dart index 74a2386..4069f2e 100644 --- a/packages/flutter_driver/lib/src/driver.dart +++ b/packages/flutter_driver/lib/src/driver.dart
@@ -148,9 +148,9 @@ 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',
diff --git a/packages/flutter_driver/lib/src/extension.dart b/packages/flutter_driver/lib/src/extension.dart index 9b57ab2..e99e50f 100644 --- a/packages/flutter_driver/lib/src/extension.dart +++ b/packages/flutter_driver/lib/src/extension.dart
@@ -60,11 +60,11 @@ }; _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, }; }
diff --git a/packages/flutter_driver/lib/src/find.dart b/packages/flutter_driver/lib/src/find.dart index 1e78815..11715eb 100644 --- a/packages/flutter_driver/lib/src/find.dart +++ b/packages/flutter_driver/lib/src/find.dart
@@ -15,10 +15,10 @@ 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 @@ } /// 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 @@ /// 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 @@ 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 @@ /// 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 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 {
diff --git a/packages/flutter_driver/lib/src/gesture.dart b/packages/flutter_driver/lib/src/gesture.dart index b4927ec..4cf0339 100644 --- a/packages/flutter_driver/lib/src/gesture.dart +++ b/packages/flutter_driver/lib/src/gesture.dart
@@ -9,11 +9,11 @@ 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 @@ 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 @@ /// 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', }); }
diff --git a/packages/flutter_driver/lib/src/health.dart b/packages/flutter_driver/lib/src/health.dart index f9d4f59..d46a302 100644 --- a/packages/flutter_driver/lib/src/health.dart +++ b/packages/flutter_driver/lib/src/health.dart
@@ -9,9 +9,9 @@ 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.
diff --git a/packages/flutter_driver/lib/src/message.dart b/packages/flutter_driver/lib/src/message.dart index 9497ef8..7290eb7 100644 --- a/packages/flutter_driver/lib/src/message.dart +++ b/packages/flutter_driver/lib/src/message.dart
@@ -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 @@ /// 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 @@ /// /// 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, }; }