mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add communication logging to Flutter Driver (#7485)
This is handy for debugging a test. Communication is logged to:
* `flutter_driver_commands_{x}.log`, where {x} is an integer, and
* (if requested by user) to stdout
fixes #7473
This commit is contained in:
parent
41251d2450
commit
ecc4972676
@ -4,20 +4,23 @@
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:file/file.dart' as f;
|
||||
import 'dart:io';
|
||||
import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:vm_service_client/vm_service_client.dart';
|
||||
import 'package:web_socket_channel/io.dart';
|
||||
|
||||
import 'common.dart';
|
||||
import 'error.dart';
|
||||
import 'find.dart';
|
||||
import 'frame_sync.dart';
|
||||
import 'gesture.dart';
|
||||
import 'health.dart';
|
||||
import 'input.dart';
|
||||
import 'message.dart';
|
||||
import 'render_tree.dart';
|
||||
import 'frame_sync.dart';
|
||||
import 'timeline.dart';
|
||||
|
||||
/// Timeline stream identifier.
|
||||
@ -94,13 +97,19 @@ class FlutterDriver {
|
||||
/// Creates a driver that uses a connection provided by the given
|
||||
/// [_serviceClient], [_peer] and [_appIsolate].
|
||||
@visibleForTesting
|
||||
FlutterDriver.connectedTo(this._serviceClient, this._peer, this._appIsolate);
|
||||
FlutterDriver.connectedTo(this._serviceClient, this._peer, this._appIsolate,
|
||||
{ bool printCommunication: false, bool logCommunicationToFile: true })
|
||||
: _printCommunication = printCommunication,
|
||||
_logCommunicationToFile = logCommunicationToFile,
|
||||
_driverId = _nextDriverId++;
|
||||
|
||||
static const String _kFlutterExtensionMethod = 'ext.flutter.driver';
|
||||
static const String _kSetVMTimelineFlagsMethod = '_setVMTimelineFlags';
|
||||
static const String _kGetVMTimelineMethod = '_getVMTimeline';
|
||||
static const Duration _kRpcGraceTime = const Duration(seconds: 2);
|
||||
|
||||
static int _nextDriverId = 0;
|
||||
|
||||
/// Connects to a Flutter application.
|
||||
///
|
||||
/// Resumes the application if it is currently paused (e.g. at a breakpoint).
|
||||
@ -108,7 +117,15 @@ class FlutterDriver {
|
||||
/// [dartVmServiceUrl] is the URL to Dart observatory (a.k.a. VM service). If
|
||||
/// not specified, the URL specified by the `VM_SERVICE_URL` environment
|
||||
/// variable is used, or 'http://localhost:8183'.
|
||||
static Future<FlutterDriver> connect({String dartVmServiceUrl}) async {
|
||||
///
|
||||
/// [printCommunication] determines whether the command communication between
|
||||
/// the test and the app should be printed to stdout.
|
||||
///
|
||||
/// [logCommunicationToFile] determines whether the command communication
|
||||
/// between the test and the app should be logged to `flutter_driver_commands.log`.
|
||||
static Future<FlutterDriver> connect({ String dartVmServiceUrl,
|
||||
bool printCommunication: false,
|
||||
bool logCommunicationToFile: true }) async {
|
||||
dartVmServiceUrl ??= Platform.environment['VM_SERVICE_URL'];
|
||||
dartVmServiceUrl ??= 'http://localhost:8183';
|
||||
|
||||
@ -137,7 +154,11 @@ class FlutterDriver {
|
||||
isolate = await vm.isolates.first.loadRunnable();
|
||||
}
|
||||
|
||||
FlutterDriver driver = new FlutterDriver.connectedTo(client, connection.peer, isolate);
|
||||
FlutterDriver driver = new FlutterDriver.connectedTo(
|
||||
client, connection.peer, isolate,
|
||||
printCommunication: printCommunication,
|
||||
logCommunicationToFile: logCommunicationToFile
|
||||
);
|
||||
|
||||
// Attempts to resume the isolate, but does not crash if it fails because
|
||||
// the isolate is already resumed. There could be a race with other tools,
|
||||
@ -220,19 +241,28 @@ class FlutterDriver {
|
||||
return driver;
|
||||
}
|
||||
|
||||
/// The unique ID of this driver instance.
|
||||
final int _driverId;
|
||||
/// Client connected to the Dart VM running the Flutter application
|
||||
final VMServiceClient _serviceClient;
|
||||
/// JSON-RPC client useful for sending raw JSON requests.
|
||||
final rpc.Peer _peer;
|
||||
/// The main isolate hosting the Flutter application
|
||||
final VMIsolateRef _appIsolate;
|
||||
/// Whether to print communication between host and app to `stdout`.
|
||||
final bool _printCommunication;
|
||||
/// Whether to log communication between host and app to `flutter_driver_commands.log`.
|
||||
final bool _logCommunicationToFile;
|
||||
|
||||
Future<Map<String, dynamic>> _sendCommand(Command command) async {
|
||||
Map<String, dynamic> response;
|
||||
try {
|
||||
response = await _appIsolate
|
||||
.invokeExtension(_kFlutterExtensionMethod, command.serialize())
|
||||
Map<String, String> serialized = command.serialize();
|
||||
_logCommunication('>>> $serialized');
|
||||
response = await _appIsolate
|
||||
.invokeExtension(_kFlutterExtensionMethod, serialized)
|
||||
.timeout(command.timeout + _kRpcGraceTime);
|
||||
_logCommunication('<<< $response');
|
||||
} on TimeoutException catch (error, stackTrace) {
|
||||
throw new DriverError(
|
||||
'Failed to fulfill ${command.runtimeType}: Flutter application not responding',
|
||||
@ -251,6 +281,16 @@ class FlutterDriver {
|
||||
return response['response'];
|
||||
}
|
||||
|
||||
void _logCommunication(String message) {
|
||||
if (_printCommunication)
|
||||
_log.info(message);
|
||||
if (_logCommunicationToFile) {
|
||||
f.File file = fs.file(p.join(testOutputsDirectory, 'flutter_driver_commands_${_driverId}.log'));
|
||||
file.createSync(recursive: true); // no-op if file exists
|
||||
file.writeAsStringSync('${new DateTime.now()} $message\n', mode: f.FileMode.APPEND, flush: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks the status of the Flutter Driver extension.
|
||||
Future<Health> checkHealth() async {
|
||||
return Health.fromJson(await _sendCommand(new GetHealth()));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user