[flutter_tool] Observatory connection error handling cleanup (#38353)
This commit is contained in:
parent
f5dcbdab3a
commit
a40ab895cf
@ -243,6 +243,8 @@ class AttachCommand extends FlutterCommand {
|
|||||||
// Determine ipv6 status from the scanned logs.
|
// Determine ipv6 status from the scanned logs.
|
||||||
usesIpv6 = observatoryDiscovery.ipv6;
|
usesIpv6 = observatoryDiscovery.ipv6;
|
||||||
printStatus('Done.'); // FYI, this message is used as a sentinel in tests.
|
printStatus('Done.'); // FYI, this message is used as a sentinel in tests.
|
||||||
|
} catch (error) {
|
||||||
|
throwToolExit('Failed to establish a debug connection with ${device.name}: $error');
|
||||||
} finally {
|
} finally {
|
||||||
await observatoryDiscovery?.cancel();
|
await observatoryDiscovery?.cancel();
|
||||||
}
|
}
|
||||||
|
@ -152,9 +152,9 @@ class IOSDevice extends Device {
|
|||||||
@override
|
@override
|
||||||
final String name;
|
final String name;
|
||||||
|
|
||||||
Map<ApplicationPackage, _IOSDeviceLogReader> _logReaders;
|
Map<ApplicationPackage, DeviceLogReader> _logReaders;
|
||||||
|
|
||||||
_IOSDevicePortForwarder _portForwarder;
|
DevicePortForwarder _portForwarder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> get isLocalEmulator async => false;
|
Future<bool> get isLocalEmulator async => false;
|
||||||
@ -342,56 +342,30 @@ class IOSDevice extends Device {
|
|||||||
if (platformArgs['trace-startup'] ?? false)
|
if (platformArgs['trace-startup'] ?? false)
|
||||||
launchArguments.add('--trace-startup');
|
launchArguments.add('--trace-startup');
|
||||||
|
|
||||||
int installationResult = -1;
|
final Status installStatus = logger.startProgress(
|
||||||
Uri localObservatoryUri;
|
'Installing and launching...',
|
||||||
|
timeout: timeoutConfiguration.slowOperation);
|
||||||
final Status installStatus = logger.startProgress('Installing and launching...', timeout: timeoutConfiguration.slowOperation);
|
try {
|
||||||
|
ProtocolDiscovery observatoryDiscovery;
|
||||||
if (!debuggingOptions.debuggingEnabled) {
|
if (debuggingOptions.debuggingEnabled) {
|
||||||
// If debugging is not enabled, just launch the application and continue.
|
|
||||||
printTrace('Debugging is not enabled');
|
|
||||||
installationResult = await const IOSDeploy().runApp(
|
|
||||||
deviceId: id,
|
|
||||||
bundlePath: bundle.path,
|
|
||||||
launchArguments: launchArguments,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Debugging is enabled, look for the observatory server port post launch.
|
// Debugging is enabled, look for the observatory server port post launch.
|
||||||
printTrace('Debugging is enabled, connecting to observatory');
|
printTrace('Debugging is enabled, connecting to observatory');
|
||||||
|
|
||||||
// TODO(danrubel): The Android device class does something similar to this code below.
|
// TODO(danrubel): The Android device class does something similar to this code below.
|
||||||
// The various Device subclasses should be refactored and common code moved into the superclass.
|
// The various Device subclasses should be refactored and common code moved into the superclass.
|
||||||
final ProtocolDiscovery observatoryDiscovery = ProtocolDiscovery.observatory(
|
observatoryDiscovery = ProtocolDiscovery.observatory(
|
||||||
getLogReader(app: package),
|
getLogReader(app: package),
|
||||||
portForwarder: portForwarder,
|
portForwarder: portForwarder,
|
||||||
hostPort: debuggingOptions.observatoryPort,
|
hostPort: debuggingOptions.observatoryPort,
|
||||||
ipv6: ipv6,
|
ipv6: ipv6,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final Future<Uri> forwardObservatoryUri = observatoryDiscovery.uri;
|
final int installationResult = await const IOSDeploy().runApp(
|
||||||
|
|
||||||
final Future<int> launch = const IOSDeploy().runApp(
|
|
||||||
deviceId: id,
|
deviceId: id,
|
||||||
bundlePath: bundle.path,
|
bundlePath: bundle.path,
|
||||||
launchArguments: launchArguments,
|
launchArguments: launchArguments,
|
||||||
);
|
);
|
||||||
|
|
||||||
localObservatoryUri = await launch.then<Uri>((int result) async {
|
|
||||||
installationResult = result;
|
|
||||||
|
|
||||||
if (result != 0) {
|
|
||||||
printTrace('Failed to launch the application on device.');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
printTrace('Application launched on the device. Waiting for observatory port.');
|
|
||||||
return await forwardObservatoryUri;
|
|
||||||
}).whenComplete(() {
|
|
||||||
observatoryDiscovery.cancel();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
installStatus.stop();
|
|
||||||
|
|
||||||
if (installationResult != 0) {
|
if (installationResult != 0) {
|
||||||
printError('Could not install ${bundle.path} on $id.');
|
printError('Could not install ${bundle.path} on $id.');
|
||||||
printError('Try launching Xcode and selecting "Product > Run" to fix the problem:');
|
printError('Try launching Xcode and selecting "Product > Run" to fix the problem:');
|
||||||
@ -400,7 +374,23 @@ class IOSDevice extends Device {
|
|||||||
return LaunchResult.failed();
|
return LaunchResult.failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
return LaunchResult.succeeded(observatoryUri: localObservatoryUri);
|
if (!debuggingOptions.debuggingEnabled) {
|
||||||
|
return LaunchResult.succeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
printTrace('Application launched on the device. Waiting for observatory port.');
|
||||||
|
final Uri localUri = await observatoryDiscovery.uri;
|
||||||
|
return LaunchResult.succeeded(observatoryUri: localUri);
|
||||||
|
} catch (error) {
|
||||||
|
printError('Failed to establish a debug connection with $id: $error');
|
||||||
|
return LaunchResult.failed();
|
||||||
|
} finally {
|
||||||
|
await observatoryDiscovery?.cancel();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
installStatus.stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -417,13 +407,24 @@ class IOSDevice extends Device {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
DeviceLogReader getLogReader({ ApplicationPackage app }) {
|
DeviceLogReader getLogReader({ ApplicationPackage app }) {
|
||||||
_logReaders ??= <ApplicationPackage, _IOSDeviceLogReader>{};
|
_logReaders ??= <ApplicationPackage, DeviceLogReader>{};
|
||||||
return _logReaders.putIfAbsent(app, () => _IOSDeviceLogReader(this, app));
|
return _logReaders.putIfAbsent(app, () => _IOSDeviceLogReader(this, app));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
void setLogReader(ApplicationPackage app, DeviceLogReader logReader) {
|
||||||
|
_logReaders ??= <ApplicationPackage, DeviceLogReader>{};
|
||||||
|
_logReaders[app] = logReader;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DevicePortForwarder get portForwarder => _portForwarder ??= _IOSDevicePortForwarder(this);
|
DevicePortForwarder get portForwarder => _portForwarder ??= _IOSDevicePortForwarder(this);
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
set portForwarder(DevicePortForwarder forwarder) {
|
||||||
|
_portForwarder = forwarder;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void clearLogs() { }
|
void clearLogs() { }
|
||||||
|
|
||||||
|
@ -65,9 +65,9 @@ class ProtocolDiscovery {
|
|||||||
if (match != null) {
|
if (match != null) {
|
||||||
try {
|
try {
|
||||||
uri = Uri.parse(match[1]);
|
uri = Uri.parse(match[1]);
|
||||||
} catch (error) {
|
} catch (error, stackTrace) {
|
||||||
_stopScrapingLogs();
|
_stopScrapingLogs();
|
||||||
_completer.completeError(error);
|
_completer.completeError(error, stackTrace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,15 +52,6 @@ void main() {
|
|||||||
mockLogReader = MockDeviceLogReader();
|
mockLogReader = MockDeviceLogReader();
|
||||||
portForwarder = MockPortForwarder();
|
portForwarder = MockPortForwarder();
|
||||||
device = MockAndroidDevice();
|
device = MockAndroidDevice();
|
||||||
when(device.getLogReader()).thenAnswer((_) {
|
|
||||||
// Now that the reader is used, start writing messages to it.
|
|
||||||
Timer.run(() {
|
|
||||||
mockLogReader.addLine('Foo');
|
|
||||||
mockLogReader.addLine('Observatory listening on http://127.0.0.1:$devicePort');
|
|
||||||
});
|
|
||||||
|
|
||||||
return mockLogReader;
|
|
||||||
});
|
|
||||||
when(device.portForwarder)
|
when(device.portForwarder)
|
||||||
.thenReturn(portForwarder);
|
.thenReturn(portForwarder);
|
||||||
when(portForwarder.forward(devicePort, hostPort: anyNamed('hostPort')))
|
when(portForwarder.forward(devicePort, hostPort: anyNamed('hostPort')))
|
||||||
@ -82,6 +73,14 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('finds observatory port and forwards', () async {
|
testUsingContext('finds observatory port and forwards', () async {
|
||||||
|
when(device.getLogReader()).thenAnswer((_) {
|
||||||
|
// Now that the reader is used, start writing messages to it.
|
||||||
|
Timer.run(() {
|
||||||
|
mockLogReader.addLine('Foo');
|
||||||
|
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) {
|
||||||
@ -102,7 +101,32 @@ void main() {
|
|||||||
Logger: () => logger,
|
Logger: () => logger,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testUsingContext('Fails with tool exit on bad Observatory uri', () async {
|
||||||
|
when(device.getLogReader()).thenAnswer((_) {
|
||||||
|
// Now that the reader is used, start writing messages to it.
|
||||||
|
Timer.run(() {
|
||||||
|
mockLogReader.addLine('Foo');
|
||||||
|
mockLogReader.addLine('Observatory listening on http:/:/127.0.0.1:$devicePort');
|
||||||
|
});
|
||||||
|
return mockLogReader;
|
||||||
|
});
|
||||||
|
testDeviceManager.addDevice(device);
|
||||||
|
expect(createTestCommandRunner(AttachCommand()).run(<String>['attach']),
|
||||||
|
throwsA(isA<ToolExit>()));
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => testFileSystem,
|
||||||
|
Logger: () => logger,
|
||||||
|
});
|
||||||
|
|
||||||
testUsingContext('accepts filesystem parameters', () async {
|
testUsingContext('accepts filesystem parameters', () async {
|
||||||
|
when(device.getLogReader()).thenAnswer((_) {
|
||||||
|
// Now that the reader is used, start writing messages to it.
|
||||||
|
Timer.run(() {
|
||||||
|
mockLogReader.addLine('Foo');
|
||||||
|
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';
|
||||||
@ -175,6 +199,14 @@ 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((_) {
|
||||||
|
// Now that the reader is used, start writing messages to it.
|
||||||
|
Timer.run(() {
|
||||||
|
mockLogReader.addLine('Foo');
|
||||||
|
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();
|
||||||
@ -190,6 +222,14 @@ 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((_) {
|
||||||
|
// Now that the reader is used, start writing messages to it.
|
||||||
|
Timer.run(() {
|
||||||
|
mockLogReader.addLine('Foo');
|
||||||
|
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();
|
||||||
|
@ -10,6 +10,7 @@ import 'package:flutter_tools/src/application_package.dart';
|
|||||||
import 'package:flutter_tools/src/artifacts.dart';
|
import 'package:flutter_tools/src/artifacts.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
import 'package:flutter_tools/src/base/io.dart';
|
import 'package:flutter_tools/src/base/io.dart';
|
||||||
|
import 'package:flutter_tools/src/build_info.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
import 'package:flutter_tools/src/device.dart';
|
import 'package:flutter_tools/src/device.dart';
|
||||||
import 'package:flutter_tools/src/ios/devices.dart';
|
import 'package:flutter_tools/src/ios/devices.dart';
|
||||||
@ -30,10 +31,9 @@ class MockCache extends Mock implements Cache {}
|
|||||||
class MockDirectory extends Mock implements Directory {}
|
class MockDirectory extends Mock implements Directory {}
|
||||||
class MockFileSystem extends Mock implements FileSystem {}
|
class MockFileSystem extends Mock implements FileSystem {}
|
||||||
class MockIMobileDevice extends Mock implements IMobileDevice {}
|
class MockIMobileDevice extends Mock implements IMobileDevice {}
|
||||||
class MockProcessManager extends Mock implements ProcessManager {}
|
|
||||||
class MockXcode extends Mock implements Xcode {}
|
class MockXcode extends Mock implements Xcode {}
|
||||||
class MockFile extends Mock implements File {}
|
class MockFile extends Mock implements File {}
|
||||||
class MockProcess extends Mock implements Process {}
|
class MockPortForwarder extends Mock implements DevicePortForwarder {}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
final FakePlatform macPlatform = FakePlatform.fromPlatform(const LocalPlatform());
|
final FakePlatform macPlatform = FakePlatform.fromPlatform(const LocalPlatform());
|
||||||
@ -63,6 +63,147 @@ void main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
group('startApp', () {
|
||||||
|
MockIOSApp mockApp;
|
||||||
|
MockArtifacts mockArtifacts;
|
||||||
|
MockCache mockCache;
|
||||||
|
MockFileSystem mockFileSystem;
|
||||||
|
MockProcessManager mockProcessManager;
|
||||||
|
MockDeviceLogReader mockLogReader;
|
||||||
|
MockPortForwarder mockPortForwarder;
|
||||||
|
|
||||||
|
const int devicePort = 499;
|
||||||
|
const int hostPort = 42;
|
||||||
|
const String installerPath = '/path/to/ideviceinstaller';
|
||||||
|
const String iosDeployPath = '/path/to/iosdeploy';
|
||||||
|
// const String appId = '789';
|
||||||
|
const MapEntry<String, String> libraryEntry = MapEntry<String, String>(
|
||||||
|
'DYLD_LIBRARY_PATH',
|
||||||
|
'/path/to/libraries'
|
||||||
|
);
|
||||||
|
final Map<String, String> env = Map<String, String>.fromEntries(
|
||||||
|
<MapEntry<String, String>>[libraryEntry]
|
||||||
|
);
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
mockApp = MockIOSApp();
|
||||||
|
mockArtifacts = MockArtifacts();
|
||||||
|
mockCache = MockCache();
|
||||||
|
when(mockCache.dyLdLibEntry).thenReturn(libraryEntry);
|
||||||
|
mockFileSystem = MockFileSystem();
|
||||||
|
mockProcessManager = MockProcessManager();
|
||||||
|
mockLogReader = MockDeviceLogReader();
|
||||||
|
mockPortForwarder = MockPortForwarder();
|
||||||
|
|
||||||
|
when(
|
||||||
|
mockArtifacts.getArtifactPath(
|
||||||
|
Artifact.ideviceinstaller,
|
||||||
|
platform: anyNamed('platform'),
|
||||||
|
)
|
||||||
|
).thenReturn(installerPath);
|
||||||
|
|
||||||
|
when(
|
||||||
|
mockArtifacts.getArtifactPath(
|
||||||
|
Artifact.iosDeploy,
|
||||||
|
platform: anyNamed('platform'),
|
||||||
|
)
|
||||||
|
).thenReturn(iosDeployPath);
|
||||||
|
|
||||||
|
when(mockPortForwarder.forward(devicePort, hostPort: anyNamed('hostPort')))
|
||||||
|
.thenAnswer((_) async => hostPort);
|
||||||
|
when(mockPortForwarder.forwardedPorts)
|
||||||
|
.thenReturn(<ForwardedPort>[ForwardedPort(hostPort, devicePort)]);
|
||||||
|
when(mockPortForwarder.unforward(any))
|
||||||
|
.thenAnswer((_) async => null);
|
||||||
|
|
||||||
|
const String bundlePath = '/path/to/bundle';
|
||||||
|
final List<String> installArgs = <String>[installerPath, '-i', bundlePath];
|
||||||
|
when(mockApp.deviceBundlePath).thenReturn(bundlePath);
|
||||||
|
final MockDirectory directory = MockDirectory();
|
||||||
|
when(mockFileSystem.directory(bundlePath)).thenReturn(directory);
|
||||||
|
when(directory.existsSync()).thenReturn(true);
|
||||||
|
when(mockProcessManager.run(installArgs, environment: env))
|
||||||
|
.thenAnswer(
|
||||||
|
(_) => Future<ProcessResult>.value(ProcessResult(1, 0, '', ''))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
mockLogReader.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext(' succeeds in debug mode', () async {
|
||||||
|
final IOSDevice device = IOSDevice('123');
|
||||||
|
device.portForwarder = mockPortForwarder;
|
||||||
|
device.setLogReader(mockApp, mockLogReader);
|
||||||
|
|
||||||
|
// Now that the reader is used, start writing messages to it.
|
||||||
|
Timer.run(() {
|
||||||
|
mockLogReader.addLine('Foo');
|
||||||
|
mockLogReader.addLine('Observatory listening on http://127.0.0.1:$devicePort');
|
||||||
|
});
|
||||||
|
|
||||||
|
final LaunchResult launchResult = await device.startApp(mockApp,
|
||||||
|
prebuiltApplication: true,
|
||||||
|
debuggingOptions: DebuggingOptions.enabled(const BuildInfo(BuildMode.debug, null)),
|
||||||
|
platformArgs: <String, dynamic>{},
|
||||||
|
);
|
||||||
|
expect(launchResult.started, isTrue);
|
||||||
|
expect(launchResult.hasObservatory, isTrue);
|
||||||
|
expect(await device.stopApp(mockApp), isFalse);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
Artifacts: () => mockArtifacts,
|
||||||
|
Cache: () => mockCache,
|
||||||
|
FileSystem: () => mockFileSystem,
|
||||||
|
Platform: () => macPlatform,
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext(' succeeds in release mode', () async {
|
||||||
|
final IOSDevice device = IOSDevice('123');
|
||||||
|
final LaunchResult launchResult = await device.startApp(mockApp,
|
||||||
|
prebuiltApplication: true,
|
||||||
|
debuggingOptions: DebuggingOptions.disabled(const BuildInfo(BuildMode.release, null)),
|
||||||
|
platformArgs: <String, dynamic>{},
|
||||||
|
);
|
||||||
|
expect(launchResult.started, isTrue);
|
||||||
|
expect(launchResult.hasObservatory, isFalse);
|
||||||
|
expect(await device.stopApp(mockApp), isFalse);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
Artifacts: () => mockArtifacts,
|
||||||
|
Cache: () => mockCache,
|
||||||
|
FileSystem: () => mockFileSystem,
|
||||||
|
Platform: () => macPlatform,
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext(' fails in debug mode when Observatory URI is malformed', () async {
|
||||||
|
final IOSDevice device = IOSDevice('123');
|
||||||
|
device.portForwarder = mockPortForwarder;
|
||||||
|
device.setLogReader(mockApp, mockLogReader);
|
||||||
|
|
||||||
|
// Now that the reader is used, start writing messages to it.
|
||||||
|
Timer.run(() {
|
||||||
|
mockLogReader.addLine('Foo');
|
||||||
|
mockLogReader.addLine('Observatory listening on http:/:/127.0.0.1:$devicePort');
|
||||||
|
});
|
||||||
|
|
||||||
|
final LaunchResult launchResult = await device.startApp(mockApp,
|
||||||
|
prebuiltApplication: true,
|
||||||
|
debuggingOptions: DebuggingOptions.enabled(const BuildInfo(BuildMode.debug, null)),
|
||||||
|
platformArgs: <String, dynamic>{},
|
||||||
|
);
|
||||||
|
expect(launchResult.started, isFalse);
|
||||||
|
expect(launchResult.hasObservatory, isFalse);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
Artifacts: () => mockArtifacts,
|
||||||
|
Cache: () => mockCache,
|
||||||
|
FileSystem: () => mockFileSystem,
|
||||||
|
Platform: () => macPlatform,
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
group('Process calls', () {
|
group('Process calls', () {
|
||||||
MockIOSApp mockApp;
|
MockIOSApp mockApp;
|
||||||
MockArtifacts mockArtifacts;
|
MockArtifacts mockArtifacts;
|
||||||
@ -263,20 +404,15 @@ f577a7903cc54959be2e34bc4f7f80b7009efcf4
|
|||||||
|
|
||||||
testUsingContext('suppresses non-Flutter lines from output', () async {
|
testUsingContext('suppresses non-Flutter lines from output', () async {
|
||||||
when(mockIMobileDevice.startLogger('123456')).thenAnswer((Invocation invocation) {
|
when(mockIMobileDevice.startLogger('123456')).thenAnswer((Invocation invocation) {
|
||||||
final Process mockProcess = MockProcess();
|
final Process mockProcess = MockProcess(
|
||||||
when(mockProcess.stdout).thenAnswer((Invocation invocation) =>
|
stdout: Stream<List<int>>.fromIterable(<List<int>>['''
|
||||||
Stream<List<int>>.fromIterable(<List<int>>['''
|
|
||||||
Runner(Flutter)[297] <Notice>: A is for ari
|
Runner(Flutter)[297] <Notice>: A is for ari
|
||||||
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt MobileGestaltSupport.m:153: pid 123 (Runner) does not have sandbox access for frZQaeyWLUvLjeuEK43hmg and IS NOT appropriately entitled
|
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt MobileGestaltSupport.m:153: pid 123 (Runner) does not have sandbox access for frZQaeyWLUvLjeuEK43hmg and IS NOT appropriately entitled
|
||||||
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt MobileGestalt.c:550: no access to InverseDeviceID (see <rdar://problem/11744455>)
|
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt MobileGestalt.c:550: no access to InverseDeviceID (see <rdar://problem/11744455>)
|
||||||
Runner(Flutter)[297] <Notice>: I is for ichigo
|
Runner(Flutter)[297] <Notice>: I is for ichigo
|
||||||
Runner(UIKit)[297] <Notice>: E is for enpitsu"
|
Runner(UIKit)[297] <Notice>: E is for enpitsu"
|
||||||
'''.codeUnits]));
|
'''.codeUnits])
|
||||||
when(mockProcess.stderr)
|
);
|
||||||
.thenAnswer((Invocation invocation) => const Stream<List<int>>.empty());
|
|
||||||
// Delay return of exitCode until after stdout stream data, since it terminates the logger.
|
|
||||||
when(mockProcess.exitCode)
|
|
||||||
.thenAnswer((Invocation invocation) => Future<int>.delayed(Duration.zero, () => 0));
|
|
||||||
return Future<Process>.value(mockProcess);
|
return Future<Process>.value(mockProcess);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -293,20 +429,15 @@ f577a7903cc54959be2e34bc4f7f80b7009efcf4
|
|||||||
});
|
});
|
||||||
testUsingContext('includes multi-line Flutter logs in the output', () async {
|
testUsingContext('includes multi-line Flutter logs in the output', () async {
|
||||||
when(mockIMobileDevice.startLogger('123456')).thenAnswer((Invocation invocation) {
|
when(mockIMobileDevice.startLogger('123456')).thenAnswer((Invocation invocation) {
|
||||||
final Process mockProcess = MockProcess();
|
final Process mockProcess = MockProcess(
|
||||||
when(mockProcess.stdout).thenAnswer((Invocation invocation) =>
|
stdout: Stream<List<int>>.fromIterable(<List<int>>['''
|
||||||
Stream<List<int>>.fromIterable(<List<int>>['''
|
|
||||||
Runner(Flutter)[297] <Notice>: This is a multi-line message,
|
Runner(Flutter)[297] <Notice>: This is a multi-line message,
|
||||||
with another Flutter message following it.
|
with another Flutter message following it.
|
||||||
Runner(Flutter)[297] <Notice>: This is a multi-line message,
|
Runner(Flutter)[297] <Notice>: This is a multi-line message,
|
||||||
with a non-Flutter log message following it.
|
with a non-Flutter log message following it.
|
||||||
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt
|
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt
|
||||||
'''.codeUnits]));
|
'''.codeUnits]),
|
||||||
when(mockProcess.stderr)
|
);
|
||||||
.thenAnswer((Invocation invocation) => const Stream<List<int>>.empty());
|
|
||||||
// Delay return of exitCode until after stdout stream data, since it terminates the logger.
|
|
||||||
when(mockProcess.exitCode)
|
|
||||||
.thenAnswer((Invocation invocation) => Future<int>.delayed(Duration.zero, () => 0));
|
|
||||||
return Future<Process>.value(mockProcess);
|
return Future<Process>.value(mockProcess);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ ro.build.version.codename=REL
|
|||||||
typedef ProcessFactory = Process Function(List<String> command);
|
typedef ProcessFactory = Process Function(List<String> command);
|
||||||
|
|
||||||
/// A ProcessManager that starts Processes by delegating to a ProcessFactory.
|
/// A ProcessManager that starts Processes by delegating to a ProcessFactory.
|
||||||
class MockProcessManager implements ProcessManager {
|
class MockProcessManager extends Mock implements ProcessManager {
|
||||||
ProcessFactory processFactory = (List<String> commands) => MockProcess();
|
ProcessFactory processFactory = (List<String> commands) => MockProcess();
|
||||||
bool canRunSucceeds = true;
|
bool canRunSucceeds = true;
|
||||||
bool runSucceeds = true;
|
bool runSucceeds = true;
|
||||||
@ -180,9 +180,6 @@ class MockProcessManager implements ProcessManager {
|
|||||||
commands = command;
|
commands = command;
|
||||||
return Future<Process>.value(processFactory(command));
|
return Future<Process>.value(processFactory(command));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
dynamic noSuchMethod(Invocation invocation) => null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A process that exits successfully with no output and ignores all input.
|
/// A process that exits successfully with no output and ignores all input.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user