// Copyright 2014 The Flutter 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 'dart:async'; import 'dart:convert'; import 'package:flutter_tools/src/application_package.dart'; import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/desktop_device.dart'; import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/globals.dart' as globals; 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'; import '../src/mocks.dart'; /// A trivial subclass of DesktopDevice for testing the shared functionality. class FakeDesktopDevice extends DesktopDevice { FakeDesktopDevice() : super( 'dummy', platformType: PlatformType.linux, ephemeral: false, ); /// The [mainPath] last passed to [buildForDevice]. String lastBuiltMainPath; /// The [buildInfo] last passed to [buildForDevice]. BuildInfo lastBuildInfo; @override String get name => 'dummy'; @override Future<TargetPlatform> get targetPlatform async => TargetPlatform.tester; @override bool isSupported() => true; @override bool isSupportedForProject(FlutterProject flutterProject) => true; @override Future<void> buildForDevice( ApplicationPackage package, { String mainPath, BuildInfo buildInfo, }) async { lastBuiltMainPath = mainPath; lastBuildInfo = buildInfo; } // Dummy implementation that just returns the build mode name. @override String executablePathForDevice(ApplicationPackage package, BuildMode buildMode) { return buildMode == null ? 'null' : getNameForBuildMode(buildMode); } } /// A desktop device that returns a null executable path, for failure testing. class NullExecutableDesktopDevice extends FakeDesktopDevice { @override String executablePathForDevice(ApplicationPackage package, BuildMode buildMode) { return null; } } class MockAppplicationPackage extends Mock implements ApplicationPackage {} class MockFileSystem extends Mock implements FileSystem {} class MockFile extends Mock implements File {} class MockProcessManager extends Mock implements ProcessManager {} void main() { group('Basic info', () { test('Category is desktop', () async { final FakeDesktopDevice device = FakeDesktopDevice(); expect(device.category, Category.desktop); }); test('Not an emulator', () async { final FakeDesktopDevice device = FakeDesktopDevice(); expect(await device.isLocalEmulator, false); expect(await device.emulatorId, null); }); testUsingContext('Uses OS name as SDK name', () async { final FakeDesktopDevice device = FakeDesktopDevice(); expect(await device.sdkNameAndVersion, globals.os.name); }); }); group('Install', () { test('Install checks always return true', () async { final FakeDesktopDevice device = FakeDesktopDevice(); expect(await device.isAppInstalled(null), true); expect(await device.isLatestBuildInstalled(null), true); expect(device.category, Category.desktop); }); test('Install and uninstall are no-ops that report success', () async { final FakeDesktopDevice device = FakeDesktopDevice(); final MockAppplicationPackage package = MockAppplicationPackage(); expect(await device.uninstallApp(package), true); expect(await device.isAppInstalled(package), true); expect(await device.isLatestBuildInstalled(package), true); expect(await device.installApp(package), true); expect(await device.isAppInstalled(package), true); expect(await device.isLatestBuildInstalled(package), true); expect(device.category, Category.desktop); }); }); group('Starting and stopping application', () { final MockFileSystem mockFileSystem = MockFileSystem(); final MockProcessManager mockProcessManager = MockProcessManager(); // Configures mock environment so that startApp will be able to find and // run an FakeDesktopDevice exectuable with for the given mode. void setUpMockExecutable(FakeDesktopDevice device, BuildMode mode, {Future<int> exitFuture}) { final String executableName = device.executablePathForDevice(null, mode); final MockFile mockFile = MockFile(); when(mockFileSystem.file(executableName)).thenReturn(mockFile); when(mockFile.existsSync()).thenReturn(true); when(mockProcessManager.start(<String>[executableName])).thenAnswer((Invocation invocation) async { return FakeProcess( exitCode: Completer<int>().future, stdout: Stream<List<int>>.fromIterable(<List<int>>[ utf8.encode('Observatory listening on http://127.0.0.1/0\n'), ]), stderr: const Stream<List<int>>.empty(), ); }); when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async { return ProcessResult(0, 1, '', ''); }); } test('Stop without start is a successful no-op', () async { final FakeDesktopDevice device = FakeDesktopDevice(); final MockAppplicationPackage package = MockAppplicationPackage(); expect(await device.stopApp(package), true); }); testUsingContext('Can run from prebuilt application', () async { final FakeDesktopDevice device = FakeDesktopDevice(); final MockAppplicationPackage package = MockAppplicationPackage(); setUpMockExecutable(device, null); final LaunchResult result = await device.startApp(package, prebuiltApplication: true); expect(result.started, true); expect(result.observatoryUri, Uri.parse('http://127.0.0.1/0')); }, overrides: <Type, Generator>{ FileSystem: () => mockFileSystem, ProcessManager: () => mockProcessManager, }); testUsingContext('Null executable path fails gracefully', () async { final NullExecutableDesktopDevice device = NullExecutableDesktopDevice(); final MockAppplicationPackage package = MockAppplicationPackage(); final LaunchResult result = await device.startApp(package, prebuiltApplication: true); expect(result.started, false); expect(testLogger.errorText, contains('Unable to find executable to run')); }); testUsingContext('stopApp kills process started by startApp', () async { final FakeDesktopDevice device = FakeDesktopDevice(); final MockAppplicationPackage package = MockAppplicationPackage(); setUpMockExecutable(device, null); final LaunchResult result = await device.startApp(package, prebuiltApplication: true); expect(result.started, true); expect(await device.stopApp(package), true); }, overrides: <Type, Generator>{ FileSystem: () => mockFileSystem, ProcessManager: () => mockProcessManager, }); }); test('Port forwarder is a no-op', () async { final FakeDesktopDevice device = FakeDesktopDevice(); final DevicePortForwarder portForwarder = device.portForwarder; final int result = await portForwarder.forward(2); expect(result, 2); expect(portForwarder.forwardedPorts.isEmpty, true); }); }