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

Add flutter run support for linux and windows (#31229)

parent 65f45999
......@@ -20,10 +20,12 @@ import 'build_info.dart';
import 'globals.dart';
import 'ios/ios_workflow.dart';
import 'ios/plist_utils.dart' as plist;
import 'linux/application_package.dart';
import 'macos/application_package.dart';
import 'project.dart';
import 'tester/flutter_tester.dart';
import 'web/web_device.dart';
import 'windows/application_package.dart';
class ApplicationPackageFactory {
static ApplicationPackageFactory get instance => context[ApplicationPackageFactory];
......@@ -51,12 +53,18 @@ class ApplicationPackageFactory {
return FlutterTesterApp.fromCurrentDirectory();
case TargetPlatform.darwin_x64:
return applicationBinary == null
? MacOSApp.fromMacOSProject((await FlutterProject.current()).macos)
: MacOSApp.fromPrebuiltApp(applicationBinary);
? MacOSApp.fromMacOSProject((await FlutterProject.current()).macos)
: MacOSApp.fromPrebuiltApp(applicationBinary);
case TargetPlatform.web:
return WebApplicationPackage(await FlutterProject.current());
case TargetPlatform.linux_x64:
return applicationBinary == null
? LinuxApp.fromLinuxProject((await FlutterProject.current()).linux)
: LinuxApp.fromPrebuiltApp(applicationBinary);
case TargetPlatform.windows_x64:
return applicationBinary == null
? WindowsApp.fromWindowsProject((await FlutterProject.current()).windows)
: WindowsApp.fromPrebuiltApp(applicationBinary);
case TargetPlatform.fuchsia:
return null;
}
......
......@@ -5,13 +5,10 @@
import 'dart:async';
import '../base/common.dart';
import '../base/io.dart';
import '../base/platform.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../cache.dart';
import '../convert.dart';
import '../globals.dart';
import '../linux/build_linux.dart';
import '../project.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult;
import 'build.dart';
......@@ -62,24 +59,7 @@ class BuildLinuxCommand extends BuildSubCommand {
if (!flutterProject.linux.existsSync()) {
throwToolExit('No Linux desktop project configured.');
}
final Process process = await processManager.start(<String>[
flutterProject.linux.buildScript.path,
Cache.flutterRoot,
buildInfo.isDebug ? 'debug' : 'release',
buildInfo?.trackWidgetCreation == true ? 'track-widget-creation' : 'no-track-widget-creation',
], runInShell: true);
process.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(printError);
process.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(printStatus);
final int result = await process.exitCode;
if (result != 0) {
throwToolExit('Build process failed');
}
await buildLinux(flutterProject.linux, buildInfo);
return null;
}
}
......@@ -5,15 +5,12 @@
import 'dart:async';
import '../base/common.dart';
import '../base/io.dart';
import '../base/platform.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../cache.dart';
import '../convert.dart';
import '../globals.dart';
import '../project.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult;
import '../windows/build_windows.dart';
import 'build.dart';
/// A command to build a windows desktop target through a build shell script.
......@@ -62,24 +59,7 @@ class BuildWindowsCommand extends BuildSubCommand {
if (!flutterProject.windows.existsSync()) {
throwToolExit('No Windows desktop project configured.');
}
final Process process = await processManager.start(<String>[
flutterProject.windows.buildScript.path,
Cache.flutterRoot,
buildInfo.isDebug ? 'debug' : 'release',
buildInfo?.trackWidgetCreation == true ? 'track-widget-creation' : 'no-track-widget-creation',
], runInShell: true);
process.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(printError);
process.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(printStatus);
final int result = await process.exitCode;
if (result != 0) {
throwToolExit('Build process failed');
}
await buildWindows(flutterProject.windows, buildInfo);
return null;
}
}
......@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'base/io.dart';
import 'base/platform.dart';
import 'base/process_manager.dart';
import 'version.dart';
// Only launch or display desktop embedding devices if
......@@ -12,3 +14,36 @@ bool get flutterDesktopEnabled {
return _flutterDesktopEnabled && !FlutterVersion.instance.isStable;
}
bool _flutterDesktopEnabled;
/// Kills a process on linux or macOS.
Future<bool> killProcess(String executable) async {
final RegExp whitespace = RegExp(r'\s+');
bool succeeded = true;
try {
final ProcessResult result = await processManager.run(<String>[
'ps', 'aux',
]);
if (result.exitCode != 0) {
return false;
}
final List<String> lines = result.stdout.split('\n');
for (String line in lines) {
if (!line.contains(executable)) {
continue;
}
final List<String> values = line.split(whitespace);
if (values.length < 2) {
continue;
}
final String pid = values[1];
final ProcessResult killResult = await processManager.run(<String>[
'kill', pid,
]);
succeeded &= killResult.exitCode == 0;
}
return true;
} on ArgumentError {
succeeded = false;
}
return succeeded;
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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 '../application_package.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../project.dart';
abstract class LinuxApp extends ApplicationPackage {
LinuxApp({@required String projectBundleId}) : super(id: projectBundleId);
/// Creates a new [LinuxApp] from a linux sub project.
factory LinuxApp.fromLinuxProject(LinuxProject project) {
return BuildableLinuxApp(
project: project,
);
}
/// Creates a new [LinuxApp] from an existing executable.
///
/// `applicationBinary` is the path to the executable.
factory LinuxApp.fromPrebuiltApp(FileSystemEntity applicationBinary) {
return PrebuiltLinuxApp(
executable: applicationBinary.path,
);
}
@override
String get displayName => id;
String executable(BuildMode buildMode);
}
class PrebuiltLinuxApp extends LinuxApp {
PrebuiltLinuxApp({
@required String executable,
}) : _executable = executable,
super(projectBundleId: executable);
final String _executable;
@override
String executable(BuildMode buildMode) => _executable;
@override
String get name => _executable;
}
class BuildableLinuxApp extends LinuxApp {
BuildableLinuxApp({this.project}) : super(projectBundleId: project.project.manifest.appName);
final LinuxProject project;
@override
String executable(BuildMode buildMode) {
final ProcessResult result = processManager.runSync(<String>[
project.nameScript.path,
buildMode == BuildMode.debug ? 'debug' : 'release',
]);
if (result.exitCode != 0) {
throwToolExit('Failed to find Linux project name');
}
return result.stdout.toString().trim();
}
@override
String get name => project.project.manifest.appName;
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../base/common.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../cache.dart';
import '../convert.dart';
import '../globals.dart';
import '../project.dart';
/// Builds the Linux project through the project shell script.
Future<void> buildLinux(LinuxProject linuxProject, BuildInfo buildInfo) async {
final Process process = await processManager.start(<String>[
linuxProject.buildScript.path,
Cache.flutterRoot,
buildInfo?.isDebug == true ? 'debug' : 'release',
buildInfo?.trackWidgetCreation == true ? 'track-widget-creation' : 'no-track-widget-creation',
], runInShell: true);
final Status status = logger.startProgress(
'Building Linux application...',
timeout: null,
);
int result;
try {
process.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(printError);
process.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(printTrace);
result = await process.exitCode;
} finally {
status.cancel();
}
if (result != 0) {
throwToolExit('Build process failed');
}
}
......@@ -3,10 +3,19 @@
// found in the LICENSE file.
import '../application_package.dart';
import '../base/io.dart';
import '../base/os.dart';
import '../base/platform.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../convert.dart';
import '../desktop.dart';
import '../device.dart';
import '../globals.dart';
import '../project.dart';
import '../protocol_discovery.dart';
import 'application_package.dart';
import 'build_linux.dart';
import 'linux_workflow.dart';
/// A device that represents a desktop Linux target.
......@@ -19,20 +28,20 @@ class LinuxDevice extends Device {
@override
DeviceLogReader getLogReader({ ApplicationPackage app }) => NoOpDeviceLogReader('linux');
// Since the host and target devices are the same, no work needs to be done
// to install the application.
@override
Future<bool> installApp(ApplicationPackage app) {
throw UnimplementedError();
}
Future<bool> installApp(ApplicationPackage app) async => true;
// Since the host and target devices are the same, no work needs to be done
// to install the application.
@override
Future<bool> isAppInstalled(ApplicationPackage app) {
throw UnimplementedError();
}
Future<bool> isAppInstalled(ApplicationPackage app) async => true;
// Since the host and target devices are the same, no work needs to be done
// to install the application.
@override
Future<bool> isLatestBuildInstalled(ApplicationPackage app) {
throw UnimplementedError();
}
Future<bool> isLatestBuildInstalled(ApplicationPackage app) async => true;
@override
Future<bool> get isLocalEmulator async => false;
......@@ -51,7 +60,7 @@ class LinuxDevice extends Device {
@override
Future<LaunchResult> startApp(
ApplicationPackage package, {
covariant LinuxApp package, {
String mainPath,
String route,
DebuggingOptions debuggingOptions,
......@@ -59,22 +68,43 @@ class LinuxDevice extends Device {
bool prebuiltApplication = false,
bool usesTerminalUi = true,
bool ipv6 = false,
}) {
throw UnimplementedError();
}) async {
if (!prebuiltApplication) {
await buildLinux((await FlutterProject.current()).linux, debuggingOptions.buildInfo);
}
await stopApp(package);
final Process process = await processManager.start(<String>[
package.executable(debuggingOptions?.buildInfo?.mode)
]);
if (debuggingOptions?.buildInfo?.isRelease == true) {
return LaunchResult.succeeded();
}
final LinuxLogReader logReader = LinuxLogReader(package, process);
final ProtocolDiscovery observatoryDiscovery = ProtocolDiscovery.observatory(logReader);
try {
final Uri observatoryUri = await observatoryDiscovery.uri;
return LaunchResult.succeeded(observatoryUri: observatoryUri);
} catch (error) {
printError('Error waiting for a debug connection: $error');
return LaunchResult.failed();
} finally {
await observatoryDiscovery.cancel();
}
}
@override
Future<bool> stopApp(ApplicationPackage app) {
throw UnimplementedError();
Future<bool> stopApp(covariant LinuxApp app) async {
// Assume debug for now.
return killProcess(app.executable(BuildMode.debug));
}
@override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.linux_x64;
// Since the host and target devices are the same, no work needs to be done
// to uninstall the application.
@override
Future<bool> uninstallApp(ApplicationPackage app) {
throw UnimplementedError();
}
Future<bool> uninstallApp(ApplicationPackage app) async => true;
}
class LinuxDevices extends PollingDeviceDiscovery {
......@@ -99,3 +129,18 @@ class LinuxDevices extends PollingDeviceDiscovery {
@override
Future<List<String>> getDiagnostics() async => const <String>[];
}
class LinuxLogReader extends DeviceLogReader {
LinuxLogReader(this.linuxApp, this.process);
final LinuxApp linuxApp;
final Process process;
@override
Stream<String> get logLines {
return process.stdout.transform(utf8.decoder);
}
@override
String get name => linuxApp.displayName;
}
......@@ -10,6 +10,7 @@ import '../base/process_manager.dart';
import '../build_info.dart';
import '../cache.dart';
import '../convert.dart';
import '../desktop.dart';
import '../device.dart';
import '../globals.dart';
import '../macos/application_package.dart';
......@@ -103,37 +104,8 @@ class MacOSDevice extends Device {
// currently we rely on killing the isolate taking down the application.
@override
Future<bool> stopApp(covariant MacOSApp app) async {
final RegExp whitespace = RegExp(r'\s+');
bool succeeded = true;
// assume debug for now.
final String executable = app.executable(BuildMode.debug);
try {
final ProcessResult result = await processManager.run(<String>[
'ps', 'aux',
]);
if (result.exitCode != 0) {
return false;
}
final List<String> lines = result.stdout.split('\n');
for (String line in lines) {
if (!line.contains(executable)) {
continue;
}
final List<String> values = line.split(whitespace);
if (values.length < 2) {
continue;
}
final String pid = values[1];
final ProcessResult killResult = await processManager.run(<String>[
'kill', pid,
]);
succeeded &= killResult.exitCode == 0;
}
return true;
} on ArgumentError {
succeeded = false;
}
return succeeded;
// Assume debug for now.
return killProcess(app.executable(BuildMode.debug));
}
@override
......
......@@ -555,7 +555,7 @@ class WindowsProject {
File get buildScript => project.directory.childDirectory('windows').childFile('build.bat');
// Note: The name script file exists as a temporary shim.
File get nameScript => project.directory.childDirectory('windows').childFile('name_output.sh');
File get nameScript => project.directory.childDirectory('windows').childFile('name_output.bat');
}
/// The Linux sub project.
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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 '../application_package.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../project.dart';
abstract class WindowsApp extends ApplicationPackage {
WindowsApp({@required String projectBundleId}) : super(id: projectBundleId);
/// Creates a new [WindowsApp] from a windows sub project.
factory WindowsApp.fromWindowsProject(WindowsProject project) {
return BuildableWindowsApp(
project: project,
);
}
/// Creates a new [WindowsApp] from an existing executable.
///
/// `applicationBinary` is the path to the executable.
factory WindowsApp.fromPrebuiltApp(FileSystemEntity applicationBinary) {
return PrebuiltWindowsApp(
executable: applicationBinary.path,
);
}
@override
String get displayName => id;
String executable(BuildMode buildMode);
}
class PrebuiltWindowsApp extends WindowsApp {
PrebuiltWindowsApp({
@required String executable,
}) : _executable = executable,
super(projectBundleId: executable);
final String _executable;
@override
String executable(BuildMode buildMode) => _executable;
@override
String get name => _executable;
}
class BuildableWindowsApp extends WindowsApp {
BuildableWindowsApp({
@required this.project,
}) : super(projectBundleId: project.project.manifest.appName);
final WindowsProject project;
@override
String executable(BuildMode buildMode) {
final ProcessResult result = processManager.runSync(<String>[
project.nameScript.path,
buildMode == BuildMode.debug ? 'debug' : 'release',
]);
if (result.exitCode != 0) {
throwToolExit('Failed to find Windows project name');
}
return result.stdout.toString().trim();
}
@override
String get name => project.project.manifest.appName;
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file
import '../base/common.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../cache.dart';
import '../convert.dart';
import '../globals.dart';
import '../project.dart';
/// Builds the Windows project through the project bat script.
Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo) async {
final Process process = await processManager.start(<String>[
windowsProject.buildScript.path,
Cache.flutterRoot,
buildInfo.isDebug ? 'debug' : 'release',
buildInfo?.trackWidgetCreation == true ? 'track-widget-creation' : 'no-track-widget-creation',
], runInShell: true);
final Status status = logger.startProgress(
'Building Windows application...',
timeout: null,
);
int result;
try {
process.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(printError);
process.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(printTrace);
result = await process.exitCode;
} finally {
status.cancel();
}
if (result != 0) {
throwToolExit('Build process failed');
}
}
......@@ -2,11 +2,21 @@
// 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 '../application_package.dart';
import '../base/io.dart';
import '../base/os.dart';
import '../base/platform.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../convert.dart';
import '../device.dart';
import '../globals.dart';
import '../project.dart';
import '../protocol_discovery.dart';
import 'application_package.dart';
import 'build_windows.dart';
import 'windows_workflow.dart';
/// A device that represents a desktop Windows target.
......@@ -19,20 +29,20 @@ class WindowsDevice extends Device {
@override
DeviceLogReader getLogReader({ ApplicationPackage app }) => NoOpDeviceLogReader('windows');
// Since the host and target devices are the same, no work needs to be done
// to install the application.
@override
Future<bool> installApp(ApplicationPackage app) {
throw UnimplementedError();
}
Future<bool> installApp(ApplicationPackage app) async => true;
// Since the host and target devices are the same, no work needs to be done
// to install the application.
@override
Future<bool> isAppInstalled(ApplicationPackage app) {
throw UnimplementedError();
}
Future<bool> isAppInstalled(ApplicationPackage app) async => true;
// Since the host and target devices are the same, no work needs to be done
// to install the application.
@override
Future<bool> isLatestBuildInstalled(ApplicationPackage app) {
throw UnimplementedError();
}
Future<bool> isLatestBuildInstalled(ApplicationPackage app) async => true;
@override
Future<bool> get isLocalEmulator async => false;
......@@ -51,7 +61,7 @@ class WindowsDevice extends Device {
@override
Future<LaunchResult> startApp(
ApplicationPackage package, {
covariant WindowsApp package, {
String mainPath,
String route,
DebuggingOptions debuggingOptions,
......@@ -59,22 +69,48 @@ class WindowsDevice extends Device {
bool prebuiltApplication = false,
bool usesTerminalUi = true,
bool ipv6 = false,
}) {
throw UnimplementedError();
}) async {
if (!prebuiltApplication) {
await buildWindows((await FlutterProject.current()).windows, debuggingOptions.buildInfo);
}
await stopApp(package);
final Process process = await processManager.start(<String>[
package.executable(debuggingOptions?.buildInfo?.mode)
]);
if (debuggingOptions?.buildInfo?.isRelease == true) {
return LaunchResult.succeeded();
}
final WindowsLogReader logReader = WindowsLogReader(package, process);
final ProtocolDiscovery observatoryDiscovery = ProtocolDiscovery.observatory(logReader);
try {
final Uri observatoryUri = await observatoryDiscovery.uri;
return LaunchResult.succeeded(observatoryUri: observatoryUri);
} catch (error) {
printError('Error waiting for a debug connection: $error');
return LaunchResult.failed();
} finally {
await observatoryDiscovery.cancel();
}
}
@override
Future<bool> stopApp(ApplicationPackage app) {
throw UnimplementedError();
Future<bool> stopApp(covariant WindowsApp app) async {
// Assume debug for now.
final List<String> process = runningProcess(app.executable(BuildMode.debug));
if (process == null) {
return false;
}
final ProcessResult result = await processManager.run(<String>['Taskkill', '/PID', process.first, '/F']);
return result.exitCode == 0;
}
@override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.windows_x64;
// Since the host and target devices are the same, no work needs to be done
// to uninstall the application.
@override
Future<bool> uninstallApp(ApplicationPackage app) {
throw UnimplementedError();
}
Future<bool> uninstallApp(ApplicationPackage app) async => true;
}
class WindowsDevices extends PollingDeviceDiscovery {
......@@ -99,3 +135,45 @@ class WindowsDevices extends PollingDeviceDiscovery {
@override
Future<List<String>> getDiagnostics() async => const <String>[];
}
final RegExp _whitespace = RegExp(r'\w+');
/// Returns the running process matching `process` name.
///
/// This list contains the process name and id.
@visibleForTesting
List<String> runningProcess(String processName) {
// TODO(jonahwilliams): find a way to do this without powershell.
final ProcessResult result = processManager.runSync(<String>['powershell', '-script="Get-CimInstance Win32_Process"']);
if (result.exitCode != 0) {
return null;
}
for (String rawProcess in result.stdout.split('\n')) {
final String process = rawProcess.trim();
if (!process.contains(processName)) {
continue;
}
final List<String> parts = process.split(_whitespace);
final List<String> data = <String>[
parts[0], // ID
parts[1], // Name
];
return data;
}
return null;
}
class WindowsLogReader extends DeviceLogReader {
WindowsLogReader(this.windowsApp, this.process);
final WindowsApp windowsApp;
final Process process;
@override
Stream<String> get logLines {
return process.stdout.transform(utf8.decoder);
}
@override
String get name => windowsApp.displayName;
}
......@@ -2,11 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/linux/application_package.dart';
import 'package:flutter_tools/src/linux/linux_device.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import '../src/common.dart';
import '../src/context.dart';
......@@ -15,21 +19,30 @@ void main() {
group(LinuxDevice, () {
final LinuxDevice device = LinuxDevice();
final MockPlatform notLinux = MockPlatform();
final MockProcessManager mockProcessManager = MockProcessManager();
when(notLinux.isLinux).thenReturn(false);
when(notLinux.environment).thenReturn(const <String, String>{});
when(mockProcessManager.run(<String>[
'ps', 'aux',
])).thenAnswer((Invocation invocation) async {
final MockProcessResult result = MockProcessResult();
when(result.exitCode).thenReturn(0);
when<String>(result.stdout).thenReturn('');
return result;
});
test('defaults', () async {
testUsingContext('defaults', () async {
final PrebuiltLinuxApp linuxApp = PrebuiltLinuxApp(executable: 'foo');
expect(await device.targetPlatform, TargetPlatform.linux_x64);
expect(device.name, 'Linux');
});
test('unimplemented methods', () {
expect(() => device.installApp(null), throwsA(isInstanceOf<UnimplementedError>()));
expect(() => device.uninstallApp(null), throwsA(isInstanceOf<UnimplementedError>()));
expect(() => device.isLatestBuildInstalled(null), throwsA(isInstanceOf<UnimplementedError>()));
expect(() => device.startApp(null), throwsA(isInstanceOf<UnimplementedError>()));
expect(() => device.stopApp(null), throwsA(isInstanceOf<UnimplementedError>()));
expect(() => device.isAppInstalled(null), throwsA(isInstanceOf<UnimplementedError>()));
expect(await device.installApp(linuxApp), true);
expect(await device.uninstallApp(linuxApp), true);
expect(await device.isLatestBuildInstalled(linuxApp), true);
expect(await device.isAppInstalled(linuxApp), true);
expect(await device.stopApp(linuxApp), true);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
test('noop port forwarding', () async {
......@@ -49,3 +62,13 @@ void main() {
}
class MockPlatform extends Mock implements Platform {}
class MockFileSystem extends Mock implements FileSystem {}
class MockFile extends Mock implements File {}
class MockProcessManager extends Mock implements ProcessManager {}
class MockProcess extends Mock implements Process {}
class MockProcessResult extends Mock implements ProcessResult {}
\ No newline at end of file
......@@ -2,11 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/windows/application_package.dart';
import 'package:flutter_tools/src/windows/windows_device.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import '../src/common.dart';
import '../src/context.dart';
......@@ -15,21 +19,30 @@ void main() {
group(WindowsDevice, () {
final WindowsDevice device = WindowsDevice();
final MockPlatform notWindows = MockPlatform();
final MockProcessManager mockProcessManager = MockProcessManager();
when(notWindows.isWindows).thenReturn(false);
when(notWindows.environment).thenReturn(const <String, String>{});
when(mockProcessManager.runSync(<String>[
'powershell', '-script="Get-CimInstance Win32_Process"'
])).thenAnswer((Invocation invocation) {
final MockProcessResult result = MockProcessResult();
when(result.exitCode).thenReturn(0);
when<String>(result.stdout).thenReturn('');
return result;
});
test('defaults', () async {
testUsingContext('defaults', () async {
final PrebuiltWindowsApp windowsApp = PrebuiltWindowsApp(executable: 'foo');
expect(await device.targetPlatform, TargetPlatform.windows_x64);
expect(device.name, 'Windows');
});
test('unimplemented methods', () {
expect(() => device.installApp(null), throwsA(isInstanceOf<UnimplementedError>()));
expect(() => device.uninstallApp(null), throwsA(isInstanceOf<UnimplementedError>()));
expect(() => device.isLatestBuildInstalled(null), throwsA(isInstanceOf<UnimplementedError>()));
expect(() => device.startApp(null), throwsA(isInstanceOf<UnimplementedError>()));
expect(() => device.stopApp(null), throwsA(isInstanceOf<UnimplementedError>()));
expect(() => device.isAppInstalled(null), throwsA(isInstanceOf<UnimplementedError>()));
expect(await device.installApp(windowsApp), true);
expect(await device.uninstallApp(windowsApp), true);
expect(await device.isLatestBuildInstalled(windowsApp), true);
expect(await device.isAppInstalled(windowsApp), true);
expect(await device.stopApp(windowsApp), false);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
test('noop port forwarding', () async {
......@@ -49,3 +62,13 @@ void main() {
}
class MockPlatform extends Mock implements Platform {}
class MockFileSystem extends Mock implements FileSystem {}
class MockFile extends Mock implements File {}
class MockProcessManager extends Mock implements ProcessManager {}
class MockProcess extends Mock implements Process {}
class MockProcessResult extends Mock implements ProcessResult {}
\ No newline at end of file
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