Let flutter attach find the service port by looking through old logs again (#53153)
This commit is contained in:
parent
6563b0de34
commit
0c5ffdc9ef
@ -57,10 +57,14 @@ Future<void> testReload(Process process, { Future<void> Function() onListening }
|
|||||||
}
|
}
|
||||||
|
|
||||||
await eventOrExit(listening.future);
|
await eventOrExit(listening.future);
|
||||||
await eventOrExit(ready.future);
|
await eventOrExit(ready.future).timeout(const Duration(seconds: 5), onTimeout: () {
|
||||||
|
// If it can't attach in 5 seconds, it's not capable of finding the
|
||||||
|
// observatory URL in the logs.
|
||||||
|
throw TaskResult.failure('Failed to attach to running Flutter process');
|
||||||
|
});
|
||||||
|
|
||||||
if (exitCode != null)
|
if (exitCode != null)
|
||||||
throw 'Failed to attach to test app; command unexpected exited, with exit code $exitCode.';
|
throw TaskResult.failure('Failed to attach to test app; command unexpected exited, with exit code $exitCode.');
|
||||||
|
|
||||||
process.stdin.write('r');
|
process.stdin.write('r');
|
||||||
process.stdin.flush();
|
process.stdin.flush();
|
||||||
@ -75,10 +79,10 @@ Future<void> testReload(Process process, { Future<void> Function() onListening }
|
|||||||
await process.exitCode;
|
await process.exitCode;
|
||||||
|
|
||||||
if (stderr.isNotEmpty)
|
if (stderr.isNotEmpty)
|
||||||
throw 'flutter attach had output on standard error.';
|
throw TaskResult.failure('flutter attach had output on standard error.');
|
||||||
|
|
||||||
if (exitCode != 0)
|
if (exitCode != 0)
|
||||||
throw 'exit code was not 0';
|
throw TaskResult.failure('exit code was not 0');
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@ -120,7 +124,7 @@ void main() {
|
|||||||
// After the delay, force-stopping it shouldn't do anything, but doesn't hurt.
|
// After the delay, force-stopping it shouldn't do anything, but doesn't hurt.
|
||||||
await device.shellExec('am', <String>['force-stop', kAppId]);
|
await device.shellExec('am', <String>['force-stop', kAppId]);
|
||||||
|
|
||||||
final String currentTime = (await device.shellEval('date', <String>['"+%F %R:%S.000"'])).trim();
|
String currentTime = (await device.shellEval('date', <String>['"+%F %R:%S.000"'])).trim();
|
||||||
print('Start time on device: $currentTime');
|
print('Start time on device: $currentTime');
|
||||||
section('Relaunching application');
|
section('Relaunching application');
|
||||||
await device.shellExec('am', <String>['start', '-n', kActivityId]);
|
await device.shellExec('am', <String>['start', '-n', kActivityId]);
|
||||||
@ -140,6 +144,26 @@ void main() {
|
|||||||
);
|
);
|
||||||
await testReload(attachProcess);
|
await testReload(attachProcess);
|
||||||
|
|
||||||
|
// Give the device the time to really shut down the app.
|
||||||
|
await Future<void>.delayed(const Duration(milliseconds: 200));
|
||||||
|
// After the delay, force-stopping it shouldn't do anything, but doesn't hurt.
|
||||||
|
await device.shellExec('am', <String>['force-stop', kAppId]);
|
||||||
|
|
||||||
|
section('Attaching after relaunching application');
|
||||||
|
await device.shellExec('am', <String>['start', '-n', kActivityId]);
|
||||||
|
|
||||||
|
// Let the application launch. Sync to the next time an observatory is ready.
|
||||||
|
currentTime = (await device.shellEval('date', <String>['"+%F %R:%S.000"'])).trim();
|
||||||
|
await device.adb(<String>['logcat', '-e', 'Observatory listening on http:', '-m', '1', '-T', currentTime]);
|
||||||
|
|
||||||
|
// Attach again now that the VM is already running.
|
||||||
|
attachProcess = await startProcess(
|
||||||
|
path.join(flutterDirectory.path, 'bin', 'flutter'),
|
||||||
|
<String>['--suppress-analytics', 'attach', '-d', device.deviceId],
|
||||||
|
isBot: false, // we just want to test the output, not have any debugging info
|
||||||
|
);
|
||||||
|
// Verify that it can discover the observatory port from past logs.
|
||||||
|
await testReload(attachProcess);
|
||||||
} finally {
|
} finally {
|
||||||
section('Uninstalling');
|
section('Uninstalling');
|
||||||
await device.adb(<String>['uninstall', kAppId]);
|
await device.adb(<String>['uninstall', kAppId]);
|
||||||
|
@ -9,19 +9,17 @@ without `flutter run` and provides a HotRunner (enabling hot reload/restart).
|
|||||||
|
|
||||||
There are four ways for the attach command to discover a running app:
|
There are four ways for the attach command to discover a running app:
|
||||||
|
|
||||||
1. If the app is already running and the observatory port is known, it can be
|
|
||||||
explicitly provided to attach via the command-line, e.g. `$ flutter attach
|
|
||||||
--debug-port 12345`
|
|
||||||
1. If the app is already running and the platform is iOS, attach can use mDNS
|
|
||||||
to lookup the observatory port via the application ID, with just `$ flutter
|
|
||||||
attach`
|
|
||||||
1. If the platform is Fuchsia the module name must be provided, e.g. `$
|
1. If the platform is Fuchsia the module name must be provided, e.g. `$
|
||||||
flutter attach --module=mod_name`. This can be called either before or after
|
flutter attach --module=mod_name`. This can be called either before or after
|
||||||
the application is started, attach will poll the device if it cannot
|
the application is started, attach will poll the device if it cannot
|
||||||
immediately discover the port
|
immediately discover the port
|
||||||
1. On other platforms (i.e. Android), if the app is not yet running attach
|
1. On Android and iOS, just running `flutter attach` suffices. Flutter tools
|
||||||
will listen and wait for the app to be (manually) started with the default
|
will search for an already running Flutter app or module if available.
|
||||||
command: `$ flutter attach`
|
Otherwise, the tool will wait for the next Flutter app or module to launch
|
||||||
|
before attaching.
|
||||||
|
1. If the app or module is already running and the specific observatory port is
|
||||||
|
known, it can be explicitly provided to attach via the command-line, e.g.
|
||||||
|
`$ flutter attach --debug-port 12345`
|
||||||
|
|
||||||
## Source
|
## Source
|
||||||
|
|
||||||
|
@ -213,6 +213,7 @@ class AndroidDevice extends Device {
|
|||||||
Future<String> get apiVersion => _getProperty('ro.build.version.sdk');
|
Future<String> get apiVersion => _getProperty('ro.build.version.sdk');
|
||||||
|
|
||||||
AdbLogReader _logReader;
|
AdbLogReader _logReader;
|
||||||
|
AdbLogReader _pastLogReader;
|
||||||
_AndroidDevicePortForwarder _portForwarder;
|
_AndroidDevicePortForwarder _portForwarder;
|
||||||
|
|
||||||
List<String> adbCommandForDevice(List<String> args) {
|
List<String> adbCommandForDevice(List<String> args) {
|
||||||
@ -673,9 +674,23 @@ class AndroidDevice extends Device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<DeviceLogReader> getLogReader({ AndroidApk app }) async {
|
FutureOr<DeviceLogReader> getLogReader({
|
||||||
// The Android log reader isn't app-specific.
|
AndroidApk app,
|
||||||
return _logReader ??= await AdbLogReader.createLogReader(this, globals.processManager);
|
bool includePastLogs = false,
|
||||||
|
}) async {
|
||||||
|
// The Android log reader isn't app-specific. The `app` parameter isn't used.
|
||||||
|
if (includePastLogs) {
|
||||||
|
return _pastLogReader ??= await AdbLogReader.createLogReader(
|
||||||
|
this,
|
||||||
|
globals.processManager,
|
||||||
|
includePastLogs: true,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return _logReader ??= await AdbLogReader.createLogReader(
|
||||||
|
this,
|
||||||
|
globals.processManager,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -724,6 +739,7 @@ class AndroidDevice extends Device {
|
|||||||
@override
|
@override
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
_logReader?._stop();
|
_logReader?._stop();
|
||||||
|
_pastLogReader?._stop();
|
||||||
await _portForwarder?.dispose();
|
await _portForwarder?.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -901,6 +917,9 @@ class AdbLogReader extends DeviceLogReader {
|
|||||||
static Future<AdbLogReader> createLogReader(
|
static Future<AdbLogReader> createLogReader(
|
||||||
AndroidDevice device,
|
AndroidDevice device,
|
||||||
ProcessManager processManager,
|
ProcessManager processManager,
|
||||||
|
{
|
||||||
|
bool includePastLogs = false,
|
||||||
|
}
|
||||||
) async {
|
) async {
|
||||||
// logcat -T is not supported on Android releases before Lollipop.
|
// logcat -T is not supported on Android releases before Lollipop.
|
||||||
const int kLollipopVersionCode = 21;
|
const int kLollipopVersionCode = 21;
|
||||||
@ -915,7 +934,12 @@ class AdbLogReader extends DeviceLogReader {
|
|||||||
'logcat',
|
'logcat',
|
||||||
'-v',
|
'-v',
|
||||||
'time',
|
'time',
|
||||||
if (apiVersion != null && apiVersion >= kLollipopVersionCode) ...<String>[
|
// If we include logs from the past, filter for 'flutter' logs only.
|
||||||
|
if (includePastLogs) ...<String>[
|
||||||
|
'-s',
|
||||||
|
'flutter',
|
||||||
|
] else if (apiVersion != null && apiVersion >= kLollipopVersionCode) ...<String>[
|
||||||
|
// Otherwise, filter for logs appearing past the present.
|
||||||
// Empty `-T` means the timestamp of the logcat command invocation.
|
// Empty `-T` means the timestamp of the logcat command invocation.
|
||||||
'-T',
|
'-T',
|
||||||
device.lastLogcatTimestamp ?? '',
|
device.lastLogcatTimestamp ?? '',
|
||||||
|
@ -6,6 +6,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
import '../android/android_device.dart';
|
||||||
import '../artifacts.dart';
|
import '../artifacts.dart';
|
||||||
import '../base/common.dart';
|
import '../base/common.dart';
|
||||||
import '../base/context.dart';
|
import '../base/context.dart';
|
||||||
@ -245,7 +246,9 @@ class AttachCommand extends FlutterCommand {
|
|||||||
if (observatoryUri == null) {
|
if (observatoryUri == null) {
|
||||||
final ProtocolDiscovery observatoryDiscovery =
|
final ProtocolDiscovery observatoryDiscovery =
|
||||||
ProtocolDiscovery.observatory(
|
ProtocolDiscovery.observatory(
|
||||||
await device.getLogReader(),
|
// If it's an Android device, attaching relies on past log searching
|
||||||
|
// to find the service protocol.
|
||||||
|
await device.getLogReader(includePastLogs: device is AndroidDevice),
|
||||||
portForwarder: device.portForwarder,
|
portForwarder: device.portForwarder,
|
||||||
ipv6: ipv6,
|
ipv6: ipv6,
|
||||||
devicePort: deviceVmservicePort,
|
devicePort: deviceVmservicePort,
|
||||||
|
@ -63,7 +63,11 @@ abstract class DesktopDevice extends Device {
|
|||||||
Future<String> get sdkNameAndVersion async => globals.os.name;
|
Future<String> get sdkNameAndVersion async => globals.os.name;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DeviceLogReader getLogReader({ ApplicationPackage app }) {
|
DeviceLogReader getLogReader({
|
||||||
|
ApplicationPackage app,
|
||||||
|
bool includePastLogs = false,
|
||||||
|
}) {
|
||||||
|
assert(!includePastLogs, 'Past log reading not supported on desktop.');
|
||||||
return _deviceLogReader;
|
return _deviceLogReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,9 +415,17 @@ abstract class Device {
|
|||||||
Future<String> get sdkNameAndVersion;
|
Future<String> get sdkNameAndVersion;
|
||||||
|
|
||||||
/// Get a log reader for this device.
|
/// Get a log reader for this device.
|
||||||
/// If [app] is specified, this will return a log reader specific to that
|
///
|
||||||
|
/// If `app` is specified, this will return a log reader specific to that
|
||||||
/// application. Otherwise, a global log reader will be returned.
|
/// application. Otherwise, a global log reader will be returned.
|
||||||
FutureOr<DeviceLogReader> getLogReader({ covariant ApplicationPackage app });
|
///
|
||||||
|
/// If `includePastLogs` is true and the device type supports it, the log
|
||||||
|
/// reader will also include log messages from before the invocation time.
|
||||||
|
/// Defaults to false.
|
||||||
|
FutureOr<DeviceLogReader> getLogReader({
|
||||||
|
covariant ApplicationPackage app,
|
||||||
|
bool includePastLogs = false,
|
||||||
|
});
|
||||||
|
|
||||||
/// Get the port forwarder for this device.
|
/// Get the port forwarder for this device.
|
||||||
DevicePortForwarder get portForwarder;
|
DevicePortForwarder get portForwarder;
|
||||||
|
@ -508,8 +508,13 @@ class FuchsiaDevice extends Device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DeviceLogReader getLogReader({ApplicationPackage app}) =>
|
DeviceLogReader getLogReader({
|
||||||
_logReader ??= _FuchsiaLogReader(this, app);
|
ApplicationPackage app,
|
||||||
|
bool includePastLogs = false,
|
||||||
|
}) {
|
||||||
|
assert(!includePastLogs, 'Past log reading not supported on Fuchsia.');
|
||||||
|
return _logReader ??= _FuchsiaLogReader(this, app);
|
||||||
|
}
|
||||||
_FuchsiaLogReader _logReader;
|
_FuchsiaLogReader _logReader;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -372,7 +372,11 @@ class IOSDevice extends Device {
|
|||||||
Future<String> get sdkNameAndVersion async => 'iOS $_sdkVersion';
|
Future<String> get sdkNameAndVersion async => 'iOS $_sdkVersion';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DeviceLogReader getLogReader({ IOSApp app }) {
|
DeviceLogReader getLogReader({
|
||||||
|
IOSApp app,
|
||||||
|
bool includePastLogs = false,
|
||||||
|
}) {
|
||||||
|
assert(!includePastLogs, 'Past log reading not supported on iOS devices.');
|
||||||
_logReaders ??= <IOSApp, DeviceLogReader>{};
|
_logReaders ??= <IOSApp, DeviceLogReader>{};
|
||||||
return _logReaders.putIfAbsent(app, () => IOSDeviceLogReader.create(
|
return _logReaders.putIfAbsent(app, () => IOSDeviceLogReader.create(
|
||||||
device: this,
|
device: this,
|
||||||
|
@ -519,8 +519,12 @@ class IOSSimulator extends Device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DeviceLogReader getLogReader({ covariant IOSApp app }) {
|
DeviceLogReader getLogReader({
|
||||||
|
covariant IOSApp app,
|
||||||
|
bool includePastLogs = false,
|
||||||
|
}) {
|
||||||
assert(app is IOSApp);
|
assert(app is IOSApp);
|
||||||
|
assert(!includePastLogs, 'Past log reading not supported on iOS simulators.');
|
||||||
_logReaders ??= <ApplicationPackage, _IOSSimulatorLogReader>{};
|
_logReaders ??= <ApplicationPackage, _IOSSimulatorLogReader>{};
|
||||||
return _logReaders.putIfAbsent(app, () => _IOSSimulatorLogReader(this, app));
|
return _logReaders.putIfAbsent(app, () => _IOSSimulatorLogReader(this, app));
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,12 @@ class FlutterTesterDevice extends Device {
|
|||||||
_FlutterTesterDeviceLogReader();
|
_FlutterTesterDeviceLogReader();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DeviceLogReader getLogReader({ ApplicationPackage app }) => _logReader;
|
DeviceLogReader getLogReader({
|
||||||
|
ApplicationPackage app,
|
||||||
|
bool includePastLogs = false,
|
||||||
|
}) {
|
||||||
|
return _logReader;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> installApp(ApplicationPackage app) async => true;
|
Future<bool> installApp(ApplicationPackage app) async => true;
|
||||||
|
@ -60,7 +60,10 @@ class ChromeDevice extends Device {
|
|||||||
DeviceLogReader _logReader;
|
DeviceLogReader _logReader;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DeviceLogReader getLogReader({ApplicationPackage app}) {
|
DeviceLogReader getLogReader({
|
||||||
|
ApplicationPackage app,
|
||||||
|
bool includePastLogs = false,
|
||||||
|
}) {
|
||||||
return _logReader ??= NoOpDeviceLogReader(app?.name);
|
return _logReader ??= NoOpDeviceLogReader(app?.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +224,10 @@ class WebServerDevice extends Device {
|
|||||||
DeviceLogReader _logReader;
|
DeviceLogReader _logReader;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DeviceLogReader getLogReader({ApplicationPackage app}) {
|
DeviceLogReader getLogReader({
|
||||||
|
ApplicationPackage app,
|
||||||
|
bool includePastLogs = false,
|
||||||
|
}) {
|
||||||
return _logReader ??= NoOpDeviceLogReader(app?.name);
|
return _logReader ??= NoOpDeviceLogReader(app?.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,12 +97,13 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('finds observatory port and forwards', () async {
|
testUsingContext('finds observatory port and forwards', () async {
|
||||||
when(device.getLogReader()).thenAnswer((_) {
|
when(device.getLogReader(includePastLogs: anyNamed('includePastLogs')))
|
||||||
// Now that the reader is used, start writing messages to it.
|
.thenAnswer((_) {
|
||||||
mockLogReader.addLine('Foo');
|
// Now that the reader is used, start writing messages to it.
|
||||||
mockLogReader.addLine('Observatory listening on http://127.0.0.1:$devicePort');
|
mockLogReader.addLine('Foo');
|
||||||
return mockLogReader;
|
mockLogReader.addLine('Observatory listening on http://127.0.0.1:$devicePort');
|
||||||
});
|
return mockLogReader;
|
||||||
|
});
|
||||||
testDeviceManager.addDevice(device);
|
testDeviceManager.addDevice(device);
|
||||||
final Completer<void> completer = Completer<void>();
|
final Completer<void> completer = Completer<void>();
|
||||||
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
|
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
|
||||||
@ -134,12 +135,14 @@ void main() {
|
|||||||
..writeAsStringSync('{}');
|
..writeAsStringSync('{}');
|
||||||
|
|
||||||
when(device.name).thenReturn('MockAndroidDevice');
|
when(device.name).thenReturn('MockAndroidDevice');
|
||||||
when(device.getLogReader()).thenReturn(mockLogReader);
|
when(device.getLogReader(includePastLogs: anyNamed('includePastLogs')))
|
||||||
|
|
||||||
final Process dartProcess = MockProcess();
|
.thenReturn(mockLogReader);
|
||||||
final StreamController<List<int>> compilerStdoutController = StreamController<List<int>>();
|
|
||||||
|
|
||||||
when(dartProcess.stdout).thenAnswer((_) => compilerStdoutController.stream);
|
final Process dartProcess = MockProcess();
|
||||||
|
final StreamController<List<int>> compilerStdoutController = StreamController<List<int>>();
|
||||||
|
|
||||||
|
when(dartProcess.stdout).thenAnswer((_) => compilerStdoutController.stream);
|
||||||
when(dartProcess.stderr)
|
when(dartProcess.stderr)
|
||||||
.thenAnswer((_) => Stream<List<int>>.fromFuture(Future<List<int>>.value(const <int>[])));
|
.thenAnswer((_) => Stream<List<int>>.fromFuture(Future<List<int>>.value(const <int>[])));
|
||||||
|
|
||||||
@ -232,13 +235,14 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('Fails with tool exit on bad Observatory uri', () async {
|
testUsingContext('Fails with tool exit on bad Observatory uri', () async {
|
||||||
when(device.getLogReader()).thenAnswer((_) {
|
when(device.getLogReader(includePastLogs: anyNamed('includePastLogs')))
|
||||||
// Now that the reader is used, start writing messages to it.
|
.thenAnswer((_) {
|
||||||
mockLogReader.addLine('Foo');
|
// Now that the reader is used, start writing messages to it.
|
||||||
mockLogReader.addLine('Observatory listening on http:/:/127.0.0.1:$devicePort');
|
mockLogReader.addLine('Foo');
|
||||||
mockLogReader.dispose();
|
mockLogReader.addLine('Observatory listening on http:/:/127.0.0.1:$devicePort');
|
||||||
return mockLogReader;
|
mockLogReader.dispose();
|
||||||
});
|
return mockLogReader;
|
||||||
|
});
|
||||||
testDeviceManager.addDevice(device);
|
testDeviceManager.addDevice(device);
|
||||||
expect(createTestCommandRunner(AttachCommand()).run(<String>['attach']),
|
expect(createTestCommandRunner(AttachCommand()).run(<String>['attach']),
|
||||||
throwsToolExit());
|
throwsToolExit());
|
||||||
@ -249,12 +253,13 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('accepts filesystem parameters', () async {
|
testUsingContext('accepts filesystem parameters', () async {
|
||||||
when(device.getLogReader()).thenAnswer((_) {
|
when(device.getLogReader(includePastLogs: anyNamed('includePastLogs')))
|
||||||
// Now that the reader is used, start writing messages to it.
|
.thenAnswer((_) {
|
||||||
mockLogReader.addLine('Foo');
|
// Now that the reader is used, start writing messages to it.
|
||||||
mockLogReader.addLine('Observatory listening on http://127.0.0.1:$devicePort');
|
mockLogReader.addLine('Foo');
|
||||||
return mockLogReader;
|
mockLogReader.addLine('Observatory listening on http://127.0.0.1:$devicePort');
|
||||||
});
|
return mockLogReader;
|
||||||
|
});
|
||||||
testDeviceManager.addDevice(device);
|
testDeviceManager.addDevice(device);
|
||||||
|
|
||||||
const String filesystemScheme = 'foo';
|
const String filesystemScheme = 'foo';
|
||||||
@ -328,12 +333,13 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('exits when ipv6 is specified and debug-port is not', () async {
|
testUsingContext('exits when ipv6 is specified and debug-port is not', () async {
|
||||||
when(device.getLogReader()).thenAnswer((_) {
|
when(device.getLogReader(includePastLogs: anyNamed('includePastLogs')))
|
||||||
// Now that the reader is used, start writing messages to it.
|
.thenAnswer((_) {
|
||||||
mockLogReader.addLine('Foo');
|
// Now that the reader is used, start writing messages to it.
|
||||||
mockLogReader.addLine('Observatory listening on http://127.0.0.1:$devicePort');
|
mockLogReader.addLine('Foo');
|
||||||
return mockLogReader;
|
mockLogReader.addLine('Observatory listening on http://127.0.0.1:$devicePort');
|
||||||
});
|
return mockLogReader;
|
||||||
|
});
|
||||||
testDeviceManager.addDevice(device);
|
testDeviceManager.addDevice(device);
|
||||||
|
|
||||||
final AttachCommand command = AttachCommand();
|
final AttachCommand command = AttachCommand();
|
||||||
@ -350,12 +356,13 @@ void main() {
|
|||||||
},);
|
},);
|
||||||
|
|
||||||
testUsingContext('exits when observatory-port is specified and debug-port is not', () async {
|
testUsingContext('exits when observatory-port is specified and debug-port is not', () async {
|
||||||
when(device.getLogReader()).thenAnswer((_) {
|
when(device.getLogReader(includePastLogs: anyNamed('includePastLogs')))
|
||||||
// Now that the reader is used, start writing messages to it.
|
.thenAnswer((_) {
|
||||||
mockLogReader.addLine('Foo');
|
// Now that the reader is used, start writing messages to it.
|
||||||
mockLogReader.addLine('Observatory listening on http://127.0.0.1:$devicePort');
|
mockLogReader.addLine('Foo');
|
||||||
return mockLogReader;
|
mockLogReader.addLine('Observatory listening on http://127.0.0.1:$devicePort');
|
||||||
});
|
return mockLogReader;
|
||||||
|
});
|
||||||
testDeviceManager.addDevice(device);
|
testDeviceManager.addDevice(device);
|
||||||
|
|
||||||
final AttachCommand command = AttachCommand();
|
final AttachCommand command = AttachCommand();
|
||||||
@ -402,7 +409,7 @@ void main() {
|
|||||||
when(mockHotRunner.isWaitingForObservatory).thenReturn(false);
|
when(mockHotRunner.isWaitingForObservatory).thenReturn(false);
|
||||||
|
|
||||||
testDeviceManager.addDevice(device);
|
testDeviceManager.addDevice(device);
|
||||||
when(device.getLogReader())
|
when(device.getLogReader(includePastLogs: anyNamed('includePastLogs')))
|
||||||
.thenAnswer((_) {
|
.thenAnswer((_) {
|
||||||
// Now that the reader is used, start writing messages to it.
|
// Now that the reader is used, start writing messages to it.
|
||||||
mockLogReader.addLine('Foo');
|
mockLogReader.addLine('Foo');
|
||||||
@ -441,7 +448,8 @@ void main() {
|
|||||||
final MockHotRunner mockHotRunner = MockHotRunner();
|
final MockHotRunner mockHotRunner = MockHotRunner();
|
||||||
final MockHotRunnerFactory mockHotRunnerFactory = MockHotRunnerFactory();
|
final MockHotRunnerFactory mockHotRunnerFactory = MockHotRunnerFactory();
|
||||||
when(device.portForwarder).thenReturn(portForwarder);
|
when(device.portForwarder).thenReturn(portForwarder);
|
||||||
when(device.getLogReader()).thenAnswer((_) => mockLogReader);
|
when(device.getLogReader(includePastLogs: anyNamed('includePastLogs')))
|
||||||
|
.thenAnswer((_) => mockLogReader);
|
||||||
when(portForwarder.forward(devicePort, hostPort: anyNamed('hostPort')))
|
when(portForwarder.forward(devicePort, hostPort: anyNamed('hostPort')))
|
||||||
.thenAnswer((_) async => hostPort);
|
.thenAnswer((_) async => hostPort);
|
||||||
when(portForwarder.forwardedPorts)
|
when(portForwarder.forwardedPorts)
|
||||||
|
@ -633,7 +633,10 @@ class FakeDevice extends Fake implements Device {
|
|||||||
Future<String> get sdkNameAndVersion => Future<String>.value('');
|
Future<String> get sdkNameAndVersion => Future<String>.value('');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DeviceLogReader getLogReader({ ApplicationPackage app }) {
|
DeviceLogReader getLogReader({
|
||||||
|
ApplicationPackage app,
|
||||||
|
bool includePastLogs = false,
|
||||||
|
}) {
|
||||||
return FakeDeviceLogReader();
|
return FakeDeviceLogReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +79,30 @@ void main() {
|
|||||||
expect(processManager.hasRemainingExpectations, false);
|
expect(processManager.hasRemainingExpectations, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWithoutContext('AdbLogReader calls adb logcat with expected flags when requesting past logs', () async {
|
||||||
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'adb',
|
||||||
|
'-s',
|
||||||
|
'1234',
|
||||||
|
'logcat',
|
||||||
|
'-v',
|
||||||
|
'time',
|
||||||
|
'-s',
|
||||||
|
'flutter',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
await AdbLogReader.createLogReader(
|
||||||
|
createMockDevice(null),
|
||||||
|
processManager,
|
||||||
|
includePastLogs: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(processManager.hasRemainingExpectations, false);
|
||||||
|
});
|
||||||
|
|
||||||
testWithoutContext('AdbLogReader handles process early exit', () async {
|
testWithoutContext('AdbLogReader handles process early exit', () async {
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
FakeCommand(
|
FakeCommand(
|
||||||
|
@ -468,17 +468,48 @@ flutter:
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('lastLogcatTimestamp', () {
|
group('logcat', () {
|
||||||
final ProcessManager mockProcessManager = MockProcessManager();
|
final ProcessManager mockProcessManager = MockProcessManager();
|
||||||
final AndroidDevice device = AndroidDevice('1234');
|
final AndroidDevice device = AndroidDevice('1234');
|
||||||
|
|
||||||
testUsingContext('returns null if shell command failed', () async {
|
testUsingContext('lastLogcatTimestamp returns null if shell command failed', () async {
|
||||||
when(mockProcessManager.runSync(argThat(contains('logcat'))))
|
when(mockProcessManager.runSync(argThat(contains('logcat'))))
|
||||||
.thenReturn(ProcessResult(0, 1, '', ''));
|
.thenReturn(ProcessResult(0, 1, '', ''));
|
||||||
expect(device.lastLogcatTimestamp, isNull);
|
expect(device.lastLogcatTimestamp, isNull);
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
ProcessManager: () => mockProcessManager,
|
ProcessManager: () => mockProcessManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testUsingContext('AdbLogReaders for past+future and future logs are not the same', () async {
|
||||||
|
when(mockProcessManager.run(
|
||||||
|
argThat(contains('getprop')),
|
||||||
|
stderrEncoding: anyNamed('stderrEncoding'),
|
||||||
|
stdoutEncoding: anyNamed('stdoutEncoding'),
|
||||||
|
)).thenAnswer((_) {
|
||||||
|
final StringBuffer buf = StringBuffer()
|
||||||
|
..writeln('[ro.build.version.sdk]: [23]');
|
||||||
|
final ProcessResult result = ProcessResult(1, exitCode, buf.toString(), '');
|
||||||
|
return Future<ProcessResult>.value(result);
|
||||||
|
});
|
||||||
|
when(mockProcessManager.run(
|
||||||
|
argThat(contains('shell')),
|
||||||
|
stderrEncoding: anyNamed('stderrEncoding'),
|
||||||
|
stdoutEncoding: anyNamed('stdoutEncoding'),
|
||||||
|
)).thenAnswer((_) {
|
||||||
|
final StringBuffer buf = StringBuffer()
|
||||||
|
..writeln('11-27 15:39:04.506');
|
||||||
|
final ProcessResult result = ProcessResult(1, exitCode, buf.toString(), '');
|
||||||
|
return Future<ProcessResult>.value(result);
|
||||||
|
});
|
||||||
|
final DeviceLogReader pastLogReader = await device.getLogReader(includePastLogs: true);
|
||||||
|
final DeviceLogReader defaultLogReader = await device.getLogReader();
|
||||||
|
expect(pastLogReader, isNot(equals(defaultLogReader)));
|
||||||
|
// Getting again is cached.
|
||||||
|
expect(pastLogReader, equals(await device.getLogReader(includePastLogs: true)));
|
||||||
|
expect(defaultLogReader, equals(await device.getLogReader()));
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can parse adb shell dumpsys info', () {
|
test('Can parse adb shell dumpsys info', () {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user