Unverified Commit ddb81770 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] remove globals from desktop configuration (#67146)

Refactors the desktop devices and workflow to remove unnecessary usage of global variables. This should make it easier to test and continue enhancing the desktop functionality of the tooling

#47161
parent ddb01a0c
......@@ -615,14 +615,13 @@ class AndroidDevice extends Device {
ProtocolDiscovery observatoryDiscovery;
if (debuggingOptions.debuggingEnabled) {
// TODO(devoncarew): Remember the forwarding information (so we can later remove the
// port forwarding or set it up again when adb fails on us).
observatoryDiscovery = ProtocolDiscovery.observatory(
await getLogReader(),
portForwarder: portForwarder,
hostPort: debuggingOptions.hostVmServicePort,
devicePort: debuggingOptions.deviceVmServicePort,
ipv6: ipv6,
logger: _logger,
);
}
......
......@@ -144,10 +144,12 @@ Future<T> runInContext<T>(
config: globals.config,
fuchsiaWorkflow: fuchsiaWorkflow,
xcDevice: globals.xcdevice,
windowsWorkflow: windowsWorkflow,
macOSWorkflow: MacOSWorkflow(
platform: globals.platform,
featureFlags: featureFlags,
),
operatingSystemUtils: globals.os,
),
Doctor: () => Doctor(logger: globals.logger),
DoctorValidatorsProvider: () => DoctorValidatorsProvider.defaultInstance,
......@@ -249,7 +251,10 @@ Future<T> runInContext<T>(
featureFlags: featureFlags,
platform: globals.platform,
),
WindowsWorkflow: () => const WindowsWorkflow(),
WindowsWorkflow: () => WindowsWorkflow(
featureFlags: featureFlags,
platform: globals.platform,
),
Xcode: () => Xcode(
logger: globals.logger,
processManager: globals.processManager,
......
......@@ -12,11 +12,11 @@ import 'base/common.dart';
import 'base/file_system.dart';
import 'base/io.dart';
import 'base/logger.dart';
import 'base/os.dart';
import 'build_info.dart';
import 'convert.dart';
import 'devfs.dart';
import 'device.dart';
import 'globals.dart' as globals;
import 'protocol_discovery.dart';
/// A partial implementation of Device for desktop-class devices to inherit
......@@ -25,12 +25,14 @@ abstract class DesktopDevice extends Device {
DesktopDevice(String identifier, {
@required PlatformType platformType,
@required bool ephemeral,
Logger logger,
ProcessManager processManager,
FileSystem fileSystem,
}) : _logger = logger ?? globals.logger, // TODO(jonahwilliams): remove after updating google3
_processManager = processManager ?? globals.processManager,
_fileSystem = fileSystem ?? globals.fs,
@required Logger logger,
@required ProcessManager processManager,
@required FileSystem fileSystem,
@required OperatingSystemUtils operatingSystemUtils,
}) : _logger = logger,
_processManager = processManager,
_fileSystem = fileSystem,
_operatingSystemUtils = operatingSystemUtils,
super(
identifier,
category: Category.desktop,
......@@ -41,6 +43,7 @@ abstract class DesktopDevice extends Device {
final Logger _logger;
final ProcessManager _processManager;
final FileSystem _fileSystem;
final OperatingSystemUtils _operatingSystemUtils;
final Set<Process> _runningProcesses = <Process>{};
final DesktopLogReader _deviceLogReader = DesktopLogReader();
......@@ -86,7 +89,7 @@ abstract class DesktopDevice extends Device {
DevicePortForwarder get portForwarder => const NoOpDevicePortForwarder();
@override
Future<String> get sdkNameAndVersion async => globals.os.name;
Future<String> get sdkNameAndVersion async => _operatingSystemUtils.name;
@override
bool supportsRuntimeMode(BuildMode buildMode) => buildMode != BuildMode.jitRelease;
......@@ -130,9 +133,11 @@ abstract class DesktopDevice extends Device {
return LaunchResult.failed();
}
final Process process = await _processManager.start(<String>[
executable,
]);
final Process process = await _processManager.start(
<String>[
executable,
],
);
_runningProcesses.add(process);
unawaited(process.exitCode.then((_) => _runningProcesses.remove(process)));
......@@ -144,6 +149,7 @@ abstract class DesktopDevice extends Device {
devicePort: debuggingOptions?.deviceVmServicePort,
hostPort: debuggingOptions?.hostVmServicePort,
ipv6: ipv6,
logger: _logger,
);
try {
final Uri observatoryUri = await observatoryDiscovery.uri;
......@@ -172,7 +178,7 @@ abstract class DesktopDevice extends Device {
// Walk a copy of _runningProcesses, since the exit handler removes from the
// set.
for (final Process process in Set<Process>.of(_runningProcesses)) {
succeeded &= process.kill();
succeeded &= _processManager.killPid(process.pid);
}
return succeeded;
}
......@@ -198,9 +204,12 @@ abstract class DesktopDevice extends Device {
void onAttached(ApplicationPackage package, BuildMode buildMode, Process process) {}
}
/// A log reader for desktop applications that delegates to a [Process] stdout
/// and stderr streams.
class DesktopLogReader extends DeviceLogReader {
final StreamController<List<int>> _inputController = StreamController<List<int>>.broadcast();
/// Begin listening to the stdout and stderr streams of the provided [process].
void initializeProcess(Process process) {
process.stdout.listen(_inputController.add);
process.stderr.listen(_inputController.add);
......
......@@ -21,6 +21,7 @@ import 'base/dds.dart';
import 'base/file_system.dart';
import 'base/io.dart';
import 'base/logger.dart';
import 'base/os.dart';
import 'base/platform.dart';
import 'base/user_messages.dart';
import 'base/utils.dart';
......@@ -42,6 +43,7 @@ import 'tester/flutter_tester.dart';
import 'version.dart';
import 'web/web_device.dart';
import 'windows/windows_device.dart';
import 'windows/windows_workflow.dart';
DeviceManager get deviceManager => context.get<DeviceManager>();
......@@ -340,6 +342,8 @@ class FlutterDeviceManager extends DeviceManager {
@required Config config,
@required Artifacts artifacts,
@required MacOSWorkflow macOSWorkflow,
@required OperatingSystemUtils operatingSystemUtils,
@required WindowsWorkflow windowsWorkflow,
}) : deviceDiscoverers = <DeviceDiscovery>[
AndroidDevices(
logger: logger,
......@@ -376,6 +380,7 @@ class FlutterDeviceManager extends DeviceManager {
logger: logger,
platform: platform,
fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils,
),
LinuxDevices(
platform: platform,
......@@ -383,8 +388,15 @@ class FlutterDeviceManager extends DeviceManager {
processManager: processManager,
logger: logger,
fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils,
),
WindowsDevices(
processManager: processManager,
operatingSystemUtils: operatingSystemUtils,
logger: logger,
fileSystem: fileSystem,
windowsWorkflow: windowsWorkflow,
),
WindowsDevices(),
WebDevices(
featureFlags: featureFlags,
fileSystem: fileSystem,
......
......@@ -7,12 +7,12 @@ import 'package:process/process.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/os.dart';
import '../base/platform.dart';
import '../build_info.dart';
import '../desktop_device.dart';
import '../device.dart';
import '../features.dart';
import '../globals.dart' as globals;
import '../project.dart';
import 'application_package.dart';
import 'build_linux.dart';
......@@ -24,6 +24,7 @@ class LinuxDevice extends DesktopDevice {
@required ProcessManager processManager,
@required Logger logger,
@required FileSystem fileSystem,
@required OperatingSystemUtils operatingSystemUtils,
}) : super(
'linux',
platformType: PlatformType.linux,
......@@ -31,6 +32,7 @@ class LinuxDevice extends DesktopDevice {
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils,
);
@override
......@@ -70,17 +72,19 @@ class LinuxDevices extends PollingDeviceDiscovery {
LinuxDevices({
@required Platform platform,
@required FeatureFlags featureFlags,
FileSystem fileSystem,
ProcessManager processManager,
Logger logger,
}) : _platform = platform ?? globals.platform, // TODO(jonahwilliams): remove after google3 roll
@required OperatingSystemUtils operatingSystemUtils,
@required FileSystem fileSystem,
@required ProcessManager processManager,
@required Logger logger,
}) : _platform = platform,
_linuxWorkflow = LinuxWorkflow(
platform: platform,
featureFlags: featureFlags,
),
_fileSystem = fileSystem ?? globals.fs,
_fileSystem = fileSystem,
_logger = logger,
_processManager = processManager ?? globals.processManager,
_processManager = processManager,
_operatingSystemUtils = operatingSystemUtils,
super('linux devices');
final Platform _platform;
......@@ -88,6 +92,7 @@ class LinuxDevices extends PollingDeviceDiscovery {
final ProcessManager _processManager;
final Logger _logger;
final FileSystem _fileSystem;
final OperatingSystemUtils _operatingSystemUtils;
@override
bool get supportsPlatform => _platform.isLinux;
......@@ -105,6 +110,7 @@ class LinuxDevices extends PollingDeviceDiscovery {
logger: _logger,
processManager: _processManager,
fileSystem: _fileSystem,
operatingSystemUtils: _operatingSystemUtils,
),
];
}
......
......@@ -8,11 +8,11 @@ import 'package:process/process.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/os.dart';
import '../base/platform.dart';
import '../build_info.dart';
import '../desktop_device.dart';
import '../device.dart';
import '../globals.dart' as globals;
import '../macos/application_package.dart';
import '../project.dart';
import 'build_macos.dart';
......@@ -23,7 +23,8 @@ class MacOSDevice extends DesktopDevice {
MacOSDevice({
@required ProcessManager processManager,
@required Logger logger,
FileSystem fileSystem,
@required FileSystem fileSystem,
@required OperatingSystemUtils operatingSystemUtils,
}) : _processManager = processManager,
_logger = logger,
super(
......@@ -32,7 +33,8 @@ class MacOSDevice extends DesktopDevice {
ephemeral: false,
processManager: processManager,
logger: logger,
fileSystem: fileSystem ?? globals.fs,
fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils,
);
final ProcessManager _processManager;
......@@ -93,12 +95,14 @@ class MacOSDevices extends PollingDeviceDiscovery {
@required MacOSWorkflow macOSWorkflow,
@required ProcessManager processManager,
@required Logger logger,
FileSystem fileSystem,
@required FileSystem fileSystem,
@required OperatingSystemUtils operatingSystemUtils,
}) : _logger = logger,
_platform = platform,
_macOSWorkflow = macOSWorkflow,
_processManager = processManager,
_fileSystem = fileSystem ?? globals.fs,
_fileSystem = fileSystem,
_operatingSystemUtils = operatingSystemUtils,
super('macOS devices');
final MacOSWorkflow _macOSWorkflow;
......@@ -106,6 +110,7 @@ class MacOSDevices extends PollingDeviceDiscovery {
final ProcessManager _processManager;
final Logger _logger;
final FileSystem _fileSystem;
final OperatingSystemUtils _operatingSystemUtils;
@override
bool get supportsPlatform => _platform.isMacOS;
......@@ -123,6 +128,7 @@ class MacOSDevices extends PollingDeviceDiscovery {
processManager: _processManager,
logger: _logger,
fileSystem: _fileSystem,
operatingSystemUtils: _operatingSystemUtils,
),
];
}
......
......@@ -7,6 +7,7 @@ import 'dart:async';
import 'package:meta/meta.dart';
import 'base/io.dart';
import 'base/logger.dart';
import 'device.dart';
import 'globals.dart' as globals;
......@@ -22,8 +23,9 @@ class ProtocolDiscovery {
this.hostPort,
this.devicePort,
this.ipv6,
}) : assert(logReader != null)
{
Logger logger,
}) : _logger = logger,
assert(logReader != null) {
_deviceLogSubscription = logReader.logLines.listen(
_handleLine,
onDone: _stopScrapingLogs,
......@@ -39,6 +41,7 @@ class ProtocolDiscovery {
@required int hostPort,
@required int devicePort,
@required bool ipv6,
Logger logger, // TODO(jonahwilliams): make required.
}) {
const String kObservatoryService = 'Observatory';
return ProtocolDiscovery._(
......@@ -50,6 +53,7 @@ class ProtocolDiscovery {
hostPort: hostPort,
devicePort: devicePort,
ipv6: ipv6,
logger: logger ?? globals.logger,
);
}
......@@ -59,6 +63,7 @@ class ProtocolDiscovery {
final int hostPort;
final int devicePort;
final bool ipv6;
final Logger _logger;
/// The time to wait before forwarding a new observatory URIs from [logReader].
final Duration throttleDuration;
......@@ -138,20 +143,20 @@ class ProtocolDiscovery {
return;
}
if (devicePort != null && uri.port != devicePort) {
globals.printTrace('skipping potential observatory $uri due to device port mismatch');
_logger.printTrace('skipping potential observatory $uri due to device port mismatch');
return;
}
_uriStreamController.add(uri);
}
Future<Uri> _forwardPort(Uri deviceUri) async {
globals.printTrace('$serviceName URL on device: $deviceUri');
_logger.printTrace('$serviceName URL on device: $deviceUri');
Uri hostUri = deviceUri;
if (portForwarder != null) {
final int actualDevicePort = deviceUri.port;
final int actualHostPort = await portForwarder.forward(actualDevicePort, hostPort: hostPort);
globals.printTrace('Forwarded host port $actualHostPort to device port $actualDevicePort for $serviceName');
_logger.printTrace('Forwarded host port $actualHostPort to device port $actualDevicePort for $serviceName');
hostUri = deviceUri.replace(port: actualHostPort);
}
......
......@@ -3,13 +3,16 @@
// found in the LICENSE file.
import 'package:meta/meta.dart';
import 'package:process/process.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/os.dart';
import '../base/process.dart';
import '../build_info.dart';
import '../desktop_device.dart';
import '../device.dart';
import '../globals.dart' as globals;
import '../project.dart';
import 'application_package.dart';
import 'build_windows.dart';
......@@ -17,10 +20,19 @@ import 'windows_workflow.dart';
/// A device that represents a desktop Windows target.
class WindowsDevice extends DesktopDevice {
WindowsDevice() : super(
WindowsDevice({
@required ProcessManager processManager,
@required Logger logger,
@required FileSystem fileSystem,
@required OperatingSystemUtils operatingSystemUtils,
}) : super(
'windows',
platformType: PlatformType.windows,
ephemeral: false,
processManager: processManager,
logger: logger,
fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils,
);
@override
......@@ -57,13 +69,30 @@ class WindowsDevice extends DesktopDevice {
}
class WindowsDevices extends PollingDeviceDiscovery {
WindowsDevices() : super('windows devices');
WindowsDevices({
@required ProcessManager processManager,
@required Logger logger,
@required FileSystem fileSystem,
@required OperatingSystemUtils operatingSystemUtils,
@required WindowsWorkflow windowsWorkflow,
}) : _fileSystem = fileSystem,
_logger = logger,
_processManager = processManager,
_operatingSystemUtils = operatingSystemUtils,
_windowsWorkflow = windowsWorkflow,
super('windows devices');
final FileSystem _fileSystem;
final Logger _logger;
final ProcessManager _processManager;
final OperatingSystemUtils _operatingSystemUtils;
final WindowsWorkflow _windowsWorkflow;
@override
bool get supportsPlatform => globals.platform.isWindows;
bool get supportsPlatform => _windowsWorkflow.appliesToHostPlatform;
@override
bool get canListAnything => windowsWorkflow.canListDevices;
bool get canListAnything => _windowsWorkflow.canListDevices;
@override
Future<List<Device>> pollingGetDevices({ Duration timeout }) async {
......@@ -71,7 +100,12 @@ class WindowsDevices extends PollingDeviceDiscovery {
return const <Device>[];
}
return <Device>[
WindowsDevice(),
WindowsDevice(
fileSystem: _fileSystem,
logger: _logger,
processManager: _processManager,
operatingSystemUtils: _operatingSystemUtils,
),
];
}
......
......@@ -2,29 +2,38 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:meta/meta.dart';
import '../base/context.dart';
import '../base/platform.dart';
import '../doctor.dart';
import '../features.dart';
import '../globals.dart' as globals;
/// The [WindowsWorkflow] instance.
WindowsWorkflow get windowsWorkflow => context.get<WindowsWorkflow>();
/// The windows-specific implementation of a [Workflow].
/// The Windows-specific implementation of a [Workflow].
///
/// This workflow requires the flutter-desktop-embedding as a sibling
/// repository to the flutter repo.
/// This workflow requires the host machine to be Windows, and the Windows
/// desktop configuration setting to be enabled.
class WindowsWorkflow implements Workflow {
const WindowsWorkflow();
const WindowsWorkflow({
@required Platform platform,
@required FeatureFlags featureFlags,
}) : _platform = platform,
_featureFlags = featureFlags;
final Platform _platform;
final FeatureFlags _featureFlags;
@override
bool get appliesToHostPlatform => globals.platform.isWindows && featureFlags.isWindowsEnabled;
bool get appliesToHostPlatform => _platform.isWindows && _featureFlags.isWindowsEnabled;
@override
bool get canLaunchDevices => globals.platform.isWindows && featureFlags.isWindowsEnabled;
bool get canLaunchDevices => _platform.isWindows && _featureFlags.isWindowsEnabled;
@override
bool get canListDevices => globals.platform.isWindows && featureFlags.isWindowsEnabled;
bool get canListDevices => _platform.isWindows && _featureFlags.isWindowsEnabled;
@override
bool get canListEmulators => false;
......
......@@ -5,10 +5,10 @@
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/linux/application_package.dart';
import 'package:flutter_tools/src/linux/linux_device.dart';
import 'package:flutter_tools/src/project.dart';
......@@ -32,6 +32,7 @@ void main() {
processManager: FakeProcessManager.any(),
logger: BufferLogger.test(),
fileSystem: MemoryFileSystem.test(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
final PrebuiltLinuxApp linuxApp = PrebuiltLinuxApp(executable: 'foo');
......@@ -57,6 +58,7 @@ void main() {
featureFlags: TestFeatureFlags(isLinuxEnabled: true),
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(),
).devices, <Device>[]);
});
......@@ -67,6 +69,7 @@ void main() {
featureFlags: TestFeatureFlags(isLinuxEnabled: false),
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(),
).devices, <Device>[]);
});
......@@ -77,6 +80,7 @@ void main() {
featureFlags: TestFeatureFlags(isLinuxEnabled: true),
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(),
).devices, hasLength(1));
});
......@@ -88,47 +92,47 @@ void main() {
featureFlags: TestFeatureFlags(isLinuxEnabled: true),
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(),
).discoverDevices(timeout: const Duration(seconds: 10));
expect(devices, hasLength(1));
});
testUsingContext('LinuxDevice.isSupportedForProject is true with editable host app', () async {
globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages').createSync();
globals.fs.directory('linux').createSync();
final FlutterProject flutterProject = FlutterProject.current();
testWithoutContext('LinuxDevice.isSupportedForProject is true with editable host app', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file('.packages').createSync();
fileSystem.directory('linux').createSync();
final FlutterProject flutterProject = setUpFlutterProject(fileSystem.currentDirectory);
expect(LinuxDevice(
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
fileSystem: MemoryFileSystem.test(),
fileSystem: fileSystem,
operatingSystemUtils: FakeOperatingSystemUtils(),
).isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('LinuxDevice.isSupportedForProject is false with no host app', () async {
globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages').createSync();
final FlutterProject flutterProject = FlutterProject.current();
testWithoutContext('LinuxDevice.isSupportedForProject is false with no host app', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file('.packages').createSync();
final FlutterProject flutterProject = setUpFlutterProject(fileSystem.currentDirectory);
expect(LinuxDevice(
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
fileSystem: MemoryFileSystem.test(),
fileSystem: fileSystem,
operatingSystemUtils: FakeOperatingSystemUtils(),
).isSupportedForProject(flutterProject), false);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('LinuxDevice.executablePathForDevice uses the correct package executable', () async {
testWithoutContext('LinuxDevice.executablePathForDevice uses the correct package executable', () async {
final MockLinuxApp mockApp = MockLinuxApp();
final LinuxDevice device = LinuxDevice(
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
fileSystem: MemoryFileSystem.test(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
const String debugPath = 'debug/executable';
const String profilePath = 'profile/executable';
......@@ -140,10 +144,19 @@ void main() {
expect(device.executablePathForDevice(mockApp, BuildMode.debug), debugPath);
expect(device.executablePathForDevice(mockApp, BuildMode.profile), profilePath);
expect(device.executablePathForDevice(mockApp, BuildMode.release), releasePath);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
}
FlutterProject setUpFlutterProject(Directory directory) {
final FlutterProjectFactory flutterProjectFactory = FlutterProjectFactory(
fileSystem: directory.fileSystem,
logger: BufferLogger.test(),
);
return flutterProjectFactory.fromDirectory(directory);
}
class MockLinuxApp extends Mock implements LinuxApp {}
class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils {
@override
String get name => 'Linux';
}
......@@ -5,19 +5,16 @@
import 'dart:async';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/macos/application_package.dart';
import 'package:flutter_tools/src/macos/macos_device.dart';
import 'package:flutter_tools/src/macos/macos_workflow.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import '../../src/common.dart';
import '../../src/context.dart';
......@@ -37,6 +34,7 @@ void main() {
processManager: FakeProcessManager.any(),
logger: BufferLogger.test(),
fileSystem: MemoryFileSystem.test(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
final MockMacOSApp mockMacOSApp = MockMacOSApp();
......@@ -54,7 +52,7 @@ void main() {
expect(device.supportsRuntimeMode(BuildMode.jitRelease), false);
});
testUsingContext('Attaches to log reader when running in release mode', () async {
testWithoutContext('Attaches to log reader when running in release mode', () async {
final Completer<void> completer = Completer<void>();
final MacOSDevice device = MacOSDevice(
fileSystem: MemoryFileSystem.test(),
......@@ -67,6 +65,7 @@ void main() {
)
]),
logger: BufferLogger.test(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
final MockMacOSApp mockMacOSApp = MockMacOSApp();
when(mockMacOSApp.executable(BuildMode.release)).thenReturn('Example.app');
......@@ -83,9 +82,6 @@ void main() {
expect(logReader.logLines, emits('Hello WorldGoodnight, Moon'));
completer.complete();
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
testWithoutContext('No devices listed if platform is unsupported', () async {
......@@ -94,6 +90,7 @@ void main() {
processManager: FakeProcessManager.any(),
logger: BufferLogger.test(),
platform: linux,
operatingSystemUtils: FakeOperatingSystemUtils(),
macOSWorkflow: MacOSWorkflow(
featureFlags: TestFeatureFlags(isMacOSEnabled: true),
platform: linux,
......@@ -107,6 +104,7 @@ void main() {
processManager: FakeProcessManager.any(),
logger: BufferLogger.test(),
platform: macOS,
operatingSystemUtils: FakeOperatingSystemUtils(),
macOSWorkflow: MacOSWorkflow(
featureFlags: TestFeatureFlags(isMacOSEnabled: false),
platform: macOS,
......@@ -122,6 +120,7 @@ void main() {
processManager: FakeProcessManager.any(),
logger: BufferLogger.test(),
platform: macOS,
operatingSystemUtils: FakeOperatingSystemUtils(),
macOSWorkflow: MacOSWorkflow(
featureFlags: TestFeatureFlags(isMacOSEnabled: true),
platform: macOS,
......@@ -137,6 +136,7 @@ void main() {
processManager: FakeProcessManager.any(),
logger: BufferLogger.test(),
platform: macOS,
operatingSystemUtils: FakeOperatingSystemUtils(),
macOSWorkflow: MacOSWorkflow(
featureFlags: TestFeatureFlags(isMacOSEnabled: true),
platform: macOS,
......@@ -149,38 +149,36 @@ void main() {
expect(devices, hasLength(1));
});
testUsingContext('isSupportedForProject is true with editable host app', () async {
testWithoutContext('isSupportedForProject is true with editable host app', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final MacOSDevice device = MacOSDevice(
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages').createSync();
globals.fs.directory('macos').createSync();
final FlutterProject flutterProject = FlutterProject.current();
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file('.packages').createSync();
fileSystem.directory('macos').createSync();
final FlutterProject flutterProject = setUpFlutterProject(fileSystem.currentDirectory);
expect(device.isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('isSupportedForProject is false with no host app', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final MacOSDevice device = MacOSDevice(
fileSystem: MemoryFileSystem.test(),
fileSystem: fileSystem,
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages').createSync();
final FlutterProject flutterProject = FlutterProject.current();
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file('.packages').createSync();
final FlutterProject flutterProject = setUpFlutterProject(fileSystem.currentDirectory);
expect(device.isSupportedForProject(flutterProject), false);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('executablePathForDevice uses the correct package executable', () async {
......@@ -189,6 +187,7 @@ void main() {
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(),
);
const String debugPath = 'debug/executable';
const String profilePath = 'profile/executable';
......@@ -200,10 +199,15 @@ void main() {
expect(device.executablePathForDevice(mockApp, BuildMode.debug), debugPath);
expect(device.executablePathForDevice(mockApp, BuildMode.profile), profilePath);
expect(device.executablePathForDevice(mockApp, BuildMode.release), releasePath);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
}
FlutterProject setUpFlutterProject(Directory directory) {
final FlutterProjectFactory flutterProjectFactory = FlutterProjectFactory(
fileSystem: directory.fileSystem,
logger: BufferLogger.test(),
);
return flutterProjectFactory.fromDirectory(directory);
}
class MockMacOSApp extends Mock implements MacOSApp {}
......@@ -3,51 +3,60 @@
// found in the LICENSE file.
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/windows/windows_workflow.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/testbed.dart';
void main() {
Testbed testbed;
FakePlatform windows;
FakePlatform notWindows;
setUp(() {
windows = FakePlatform(operatingSystem: 'windows');
notWindows = FakePlatform(operatingSystem: 'linux');
testbed = Testbed(
overrides: <Type, Generator>{
Platform: () => windows,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
},
final FakePlatform windows = FakePlatform(operatingSystem: 'windows');
final FakePlatform notWindows = FakePlatform(operatingSystem: 'linux');
testWithoutContext('Windows workflow configuration when feature is enabled on Windows host machine', () {
final WindowsWorkflow windowsWorkflow = WindowsWorkflow(
platform: windows,
featureFlags: TestFeatureFlags(isWindowsEnabled: true),
);
});
test('Windows default workflow values', () => testbed.run(() {
expect(windowsWorkflow.appliesToHostPlatform, true);
expect(windowsWorkflow.canListDevices, true);
expect(windowsWorkflow.canLaunchDevices, true);
expect(windowsWorkflow.canListEmulators, false);
}));
});
testWithoutContext('Windows workflow configuration when feature is disabled on Windows host machine', () {
final WindowsWorkflow windowsWorkflow = WindowsWorkflow(
platform: windows,
featureFlags: TestFeatureFlags(isWindowsEnabled: false),
);
test('Windows defaults on non-windows platform', () => testbed.run(() {
expect(windowsWorkflow.appliesToHostPlatform, false);
expect(windowsWorkflow.canListDevices, false);
expect(windowsWorkflow.canLaunchDevices, false);
expect(windowsWorkflow.canListEmulators, false);
}, overrides: <Type, Generator>{
Platform: () => notWindows,
}));
});
testWithoutContext('Windows workflow configuration when feature is enabled on non-Windows host machine', () {
final WindowsWorkflow windowsWorkflow = WindowsWorkflow(
platform: notWindows,
featureFlags: TestFeatureFlags(isWindowsEnabled: true),
);
test('Windows defaults on non-windows platform', () => testbed.run(() {
expect(windowsWorkflow.appliesToHostPlatform, false);
expect(windowsWorkflow.canListDevices, false);
expect(windowsWorkflow.canLaunchDevices, false);
expect(windowsWorkflow.canListEmulators, false);
}, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: false),
}));
});
testWithoutContext('Windows workflow configuration when feature is disabled on non-Windows host machine', () {
final WindowsWorkflow windowsWorkflow = WindowsWorkflow(
platform: notWindows,
featureFlags: TestFeatureFlags(isWindowsEnabled: false),
);
expect(windowsWorkflow.appliesToHostPlatform, false);
expect(windowsWorkflow.canListDevices, false);
expect(windowsWorkflow.canLaunchDevices, false);
expect(windowsWorkflow.canListEmulators, false);
});
}
......@@ -123,13 +123,13 @@ class _FakeProcess implements Process {
this._stderr,
this.stdin,
this._stdout,
Completer<void> completer,
this._completer,
) : exitCode = Future<void>.delayed(duration).then((void value) {
if (onRun != null) {
onRun();
}
if (completer != null) {
return completer.future.then((void _) => _exitCode);
if (_completer != null) {
return _completer.future.then((void _) => _exitCode);
}
return _exitCode;
}),
......@@ -141,6 +141,7 @@ class _FakeProcess implements Process {
: Stream<List<int>>.value(utf8.encode(_stdout));
final int _exitCode;
final Completer<void> _completer;
@override
final Future<int> exitCode;
......@@ -205,6 +206,8 @@ abstract class FakeProcessManager implements ProcessManager {
commands.forEach(addCommand);
}
final Map<int, _FakeProcess> _fakeRunningProcesses = <int, _FakeProcess>{};
/// Whether this fake has more [FakeCommand]s that are expected to run.
///
/// This is always `true` for [FakeProcessManager.any].
......@@ -248,7 +251,16 @@ abstract class FakeProcessManager implements ProcessManager {
bool includeParentEnvironment = true, // ignored
bool runInShell = false, // ignored
ProcessStartMode mode = ProcessStartMode.normal, // ignored
}) async => _runCommand(command.cast<String>(), workingDirectory, environment, systemEncoding);
}) {
final _FakeProcess process = _runCommand(command.cast<String>(), workingDirectory, environment, systemEncoding);
if (process._completer != null) {
_fakeRunningProcesses[process.pid] = process;
process.exitCode.whenComplete(() {
_fakeRunningProcesses.remove(process.pid);
});
}
return Future<Process>.value(process);
}
@override
Future<ProcessResult> run(
......@@ -294,8 +306,13 @@ abstract class FakeProcessManager implements ProcessManager {
@override
bool killPid(int pid, [io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
// Killing a fake process has no effect.
return false;
// Killing a fake process has no effect unless it has an attached completer.
final _FakeProcess fakeProcess = _fakeRunningProcesses[pid];
if (fakeProcess == null) {
return false;
}
fakeProcess._completer.complete();
return true;
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment