Unverified Commit 8a31a3a2 authored by Christopher Fujino's avatar Christopher Fujino Committed by GitHub

Flutter preview device (#135639)

Fixes https://github.com/flutter/flutter/issues/130277

This PR does two things:

1. introduce a hidden `flutter build _preview` command, that will build a debug windows desktop app and copy it into the SDK's binary cache. This command is only intended to be run during packaging.
2. introduce a new device type, called `PreviewDevice`, which relies on the prebuilt desktop debug app from step 1, copies it into the target app's assets build folder, and then hot reloads their dart code into it.
parent 492b6f7d
...@@ -173,9 +173,11 @@ List<FlutterCommand> generateCommands({ ...@@ -173,9 +173,11 @@ List<FlutterCommand> generateCommands({
fileSystem: globals.fs, fileSystem: globals.fs,
), ),
BuildCommand( BuildCommand(
artifacts: globals.artifacts!,
fileSystem: globals.fs, fileSystem: globals.fs,
buildSystem: globals.buildSystem, buildSystem: globals.buildSystem,
osUtils: globals.os, osUtils: globals.os,
processUtils: globals.processUtils,
verboseHelp: verboseHelp, verboseHelp: verboseHelp,
androidSdk: globals.androidSdk, androidSdk: globals.androidSdk,
logger: globals.logger, logger: globals.logger,
......
...@@ -69,6 +69,9 @@ enum Artifact { ...@@ -69,6 +69,9 @@ enum Artifact {
/// The location of file generators. /// The location of file generators.
flutterToolsFileGenerators, flutterToolsFileGenerators,
/// Pre-built desktop debug app.
flutterPreviewDevice,
} }
/// A subset of [Artifact]s that are platform and build mode independent /// A subset of [Artifact]s that are platform and build mode independent
...@@ -213,6 +216,8 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod ...@@ -213,6 +216,8 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod
return 'const_finder.dart.snapshot'; return 'const_finder.dart.snapshot';
case Artifact.flutterToolsFileGenerators: case Artifact.flutterToolsFileGenerators:
return ''; return '';
case Artifact.flutterPreviewDevice:
return 'flutter_preview$exe';
} }
} }
...@@ -573,6 +578,7 @@ class CachedArtifacts implements Artifacts { ...@@ -573,6 +578,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.windowsCppClientWrapper: case Artifact.windowsCppClientWrapper:
case Artifact.windowsDesktopPath: case Artifact.windowsDesktopPath:
case Artifact.flutterToolsFileGenerators: case Artifact.flutterToolsFileGenerators:
case Artifact.flutterPreviewDevice:
return _getHostArtifactPath(artifact, platform, mode); return _getHostArtifactPath(artifact, platform, mode);
} }
} }
...@@ -612,6 +618,7 @@ class CachedArtifacts implements Artifacts { ...@@ -612,6 +618,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.windowsCppClientWrapper: case Artifact.windowsCppClientWrapper:
case Artifact.windowsDesktopPath: case Artifact.windowsDesktopPath:
case Artifact.flutterToolsFileGenerators: case Artifact.flutterToolsFileGenerators:
case Artifact.flutterPreviewDevice:
return _getHostArtifactPath(artifact, platform, mode); return _getHostArtifactPath(artifact, platform, mode);
} }
} }
...@@ -663,6 +670,7 @@ class CachedArtifacts implements Artifacts { ...@@ -663,6 +670,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.windowsCppClientWrapper: case Artifact.windowsCppClientWrapper:
case Artifact.windowsDesktopPath: case Artifact.windowsDesktopPath:
case Artifact.flutterToolsFileGenerators: case Artifact.flutterToolsFileGenerators:
case Artifact.flutterPreviewDevice:
return _getHostArtifactPath(artifact, platform, mode); return _getHostArtifactPath(artifact, platform, mode);
} }
} }
...@@ -745,6 +753,9 @@ class CachedArtifacts implements Artifacts { ...@@ -745,6 +753,9 @@ class CachedArtifacts implements Artifacts {
throw StateError('Artifact $artifact not available for platform $platform.'); throw StateError('Artifact $artifact not available for platform $platform.');
case Artifact.flutterToolsFileGenerators: case Artifact.flutterToolsFileGenerators:
return _getFileGeneratorsPath(); return _getFileGeneratorsPath();
case Artifact.flutterPreviewDevice:
assert(platform == TargetPlatform.windows_x64);
return _cache.getArtifactDirectory('flutter_preview').childFile('flutter_preview.exe').path;
} }
} }
...@@ -1016,6 +1027,8 @@ class CachedLocalEngineArtifacts implements Artifacts { ...@@ -1016,6 +1027,8 @@ class CachedLocalEngineArtifacts implements Artifacts {
return _fileSystem.path.join(_getDartSdkPath(), 'bin', 'utils', artifactFileName); return _fileSystem.path.join(_getDartSdkPath(), 'bin', 'utils', artifactFileName);
case Artifact.flutterToolsFileGenerators: case Artifact.flutterToolsFileGenerators:
return _getFileGeneratorsPath(); return _getFileGeneratorsPath();
case Artifact.flutterPreviewDevice:
throw UnimplementedError('The preview device is not supported with local engine builds');
} }
} }
...@@ -1118,7 +1131,6 @@ class CachedLocalWebSdkArtifacts implements Artifacts { ...@@ -1118,7 +1131,6 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
_platform = platform, _platform = platform,
_operatingSystemUtils = operatingSystemUtils; _operatingSystemUtils = operatingSystemUtils;
final Artifacts _parent; final Artifacts _parent;
final String _webSdkPath; final String _webSdkPath;
final FileSystem _fileSystem; final FileSystem _fileSystem;
...@@ -1169,6 +1181,7 @@ class CachedLocalWebSdkArtifacts implements Artifacts { ...@@ -1169,6 +1181,7 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
case Artifact.fontSubset: case Artifact.fontSubset:
case Artifact.constFinder: case Artifact.constFinder:
case Artifact.flutterToolsFileGenerators: case Artifact.flutterToolsFileGenerators:
case Artifact.flutterPreviewDevice:
break; break;
} }
} }
......
...@@ -5,11 +5,14 @@ ...@@ -5,11 +5,14 @@
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import '../android/android_sdk.dart'; import '../android/android_sdk.dart';
import '../artifacts.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/logger.dart'; import '../base/logger.dart';
import '../base/os.dart'; import '../base/os.dart';
import '../base/process.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../build_system/build_system.dart'; import '../build_system/build_system.dart';
import '../cache.dart';
import '../commands/build_linux.dart'; import '../commands/build_linux.dart';
import '../commands/build_macos.dart'; import '../commands/build_macos.dart';
import '../commands/build_windows.dart'; import '../commands/build_windows.dart';
...@@ -21,15 +24,18 @@ import 'build_bundle.dart'; ...@@ -21,15 +24,18 @@ import 'build_bundle.dart';
import 'build_ios.dart'; import 'build_ios.dart';
import 'build_ios_framework.dart'; import 'build_ios_framework.dart';
import 'build_macos_framework.dart'; import 'build_macos_framework.dart';
import 'build_preview.dart';
import 'build_web.dart'; import 'build_web.dart';
class BuildCommand extends FlutterCommand { class BuildCommand extends FlutterCommand {
BuildCommand({ BuildCommand({
required Artifacts artifacts,
required FileSystem fileSystem, required FileSystem fileSystem,
required BuildSystem buildSystem, required BuildSystem buildSystem,
required OperatingSystemUtils osUtils, required OperatingSystemUtils osUtils,
required Logger logger, required Logger logger,
required AndroidSdk? androidSdk, required AndroidSdk? androidSdk,
required ProcessUtils processUtils,
bool verboseHelp = false, bool verboseHelp = false,
}){ }){
_addSubcommand( _addSubcommand(
...@@ -67,6 +73,14 @@ class BuildCommand extends FlutterCommand { ...@@ -67,6 +73,14 @@ class BuildCommand extends FlutterCommand {
verboseHelp: verboseHelp verboseHelp: verboseHelp
)); ));
_addSubcommand(BuildWindowsCommand(logger: logger, verboseHelp: verboseHelp)); _addSubcommand(BuildWindowsCommand(logger: logger, verboseHelp: verboseHelp));
_addSubcommand(BuildPreviewCommand(
artifacts: artifacts,
flutterRoot: Cache.flutterRoot!,
fs: fileSystem,
logger: logger,
processUtils: processUtils,
verboseHelp: verboseHelp,
));
} }
void _addSubcommand(BuildSubCommand command) { void _addSubcommand(BuildSubCommand command) {
...@@ -90,14 +104,15 @@ class BuildCommand extends FlutterCommand { ...@@ -90,14 +104,15 @@ class BuildCommand extends FlutterCommand {
abstract class BuildSubCommand extends FlutterCommand { abstract class BuildSubCommand extends FlutterCommand {
BuildSubCommand({ BuildSubCommand({
required Logger logger, required this.logger,
required bool verboseHelp required bool verboseHelp
}): _logger = logger { }) {
requiresPubspecYaml(); requiresPubspecYaml();
usesFatalWarningsOption(verboseHelp: verboseHelp); usesFatalWarningsOption(verboseHelp: verboseHelp);
} }
final Logger _logger; @protected
final Logger logger;
@override @override
bool get reportNullSafety => true; bool get reportNullSafety => true;
...@@ -111,15 +126,15 @@ abstract class BuildSubCommand extends FlutterCommand { ...@@ -111,15 +126,15 @@ abstract class BuildSubCommand extends FlutterCommand {
@protected @protected
void displayNullSafetyMode(BuildInfo buildInfo) { void displayNullSafetyMode(BuildInfo buildInfo) {
if (buildInfo.nullSafetyMode != NullSafetyMode.sound) { if (buildInfo.nullSafetyMode != NullSafetyMode.sound) {
_logger.printStatus(''); logger.printStatus('');
_logger.printStatus( logger.printStatus(
'Building without sound null safety ⚠️', 'Building without sound null safety ⚠️',
emphasis: true, emphasis: true,
); );
_logger.printStatus( logger.printStatus(
'Dart 3 will only support sound null safety, see https://dart.dev/null-safety', 'Dart 3 will only support sound null safety, see https://dart.dev/null-safety',
); );
} }
_logger.printStatus(''); logger.printStatus('');
} }
} }
// 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 'package:file/file.dart';
import '../artifacts.dart';
import '../base/common.dart';
import '../base/io.dart';
import '../base/process.dart';
import '../build_info.dart';
import '../cache.dart';
import '../globals.dart' as globals;
import '../project.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult;
import '../windows/build_windows.dart';
import 'build.dart';
class BuildPreviewCommand extends BuildSubCommand {
BuildPreviewCommand({
required super.logger,
required bool verboseHelp,
required this.fs,
required this.flutterRoot,
required this.processUtils,
required this.artifacts,
}) : super(verboseHelp: verboseHelp) {
addCommonDesktopBuildOptions(verboseHelp: verboseHelp);
}
@override
final String name = '_preview';
@override
final bool hidden = true;
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => <DevelopmentArtifact>{
DevelopmentArtifact.windows,
};
@override
final String description = 'Build Flutter preview (desktop) app.';
final FileSystem fs;
final String flutterRoot;
final ProcessUtils processUtils;
final Artifacts artifacts;
static const BuildInfo buildInfo = BuildInfo(
BuildMode.debug,
null, // no flavor
// users may add icons later
treeShakeIcons: false,
);
@override
void requiresPubspecYaml() {}
static const String appName = 'flutter_preview';
@override
Future<FlutterCommandResult> runCommand() async {
if (!globals.platform.isWindows) {
throwToolExit('"build _preview" is currently only supported on Windows hosts.');
}
final Directory targetDir = fs.systemTempDirectory.createTempSync('flutter-build-preview');
try {
final FlutterProject flutterProject = await _createProject(targetDir);
await buildWindows(
flutterProject.windows,
buildInfo,
);
final File previewDevice = targetDir
.childDirectory(getWindowsBuildDirectory(TargetPlatform.windows_x64))
.childDirectory('runner')
.childDirectory('Debug')
.childFile('$appName.exe');
if (!previewDevice.existsSync()) {
throw StateError('Preview device not found at ${previewDevice.absolute.path}');
}
final String newPath = artifacts.getArtifactPath(Artifact.flutterPreviewDevice);
fs.file(newPath).parent.createSync(recursive: true);
previewDevice.copySync(newPath);
return FlutterCommandResult.success();
} finally {
try {
targetDir.deleteSync(recursive: true);
} on FileSystemException catch (exception) {
logger.printError('Failed to delete ${targetDir.path}\n\n$exception');
}
}
}
Future<FlutterProject> _createProject(Directory targetDir) async {
final List<String> cmd = <String>[
fs.path.join(flutterRoot, 'bin', 'flutter.bat'),
'create',
'--empty',
'--project-name',
'flutter_preview',
targetDir.path,
];
final RunResult result = await processUtils.run(
cmd,
allowReentrantFlutter: true,
);
if (result.exitCode != 0) {
final StringBuffer buffer = StringBuffer('${cmd.join(' ')} exited with code ${result.exitCode}\n');
buffer.writeln('stdout:\n${result.stdout}\n');
buffer.writeln('stderr:\n${result.stderr}');
throw ProcessException(cmd.first, cmd.sublist(1), buffer.toString(), result.exitCode);
}
return FlutterProject.fromDirectory(targetDir);
}
}
...@@ -53,6 +53,9 @@ abstract class FeatureFlags { ...@@ -53,6 +53,9 @@ abstract class FeatureFlags {
/// Whether native assets compilation and bundling is enabled. /// Whether native assets compilation and bundling is enabled.
bool get isNativeAssetsEnabled => false; bool get isNativeAssetsEnabled => false;
/// Whether native assets compilation and bundling is enabled.
bool get isPreviewDeviceEnabled => true;
/// Whether a particular feature is enabled for the current channel. /// Whether a particular feature is enabled for the current channel.
/// ///
/// Prefer using one of the specific getters above instead of this API. /// Prefer using one of the specific getters above instead of this API.
...@@ -72,6 +75,7 @@ const List<Feature> allFeatures = <Feature>[ ...@@ -72,6 +75,7 @@ const List<Feature> allFeatures = <Feature>[
flutterWebWasm, flutterWebWasm,
cliAnimation, cliAnimation,
nativeAssets, nativeAssets,
previewDevice,
]; ];
/// All current Flutter feature flags that can be configured. /// All current Flutter feature flags that can be configured.
...@@ -172,6 +176,19 @@ const Feature nativeAssets = Feature( ...@@ -172,6 +176,19 @@ const Feature nativeAssets = Feature(
), ),
); );
/// Enable Flutter preview prebuilt device.
const Feature previewDevice = Feature(
name: 'Flutter preview prebuilt device',
configSetting: 'enable-flutter-preview',
environmentOverride: 'FLUTTER_PREVIEW_DEVICE',
master: FeatureChannelSetting(
available: true,
),
beta: FeatureChannelSetting(
available: true,
),
);
/// A [Feature] is a process for conditionally enabling tool features. /// A [Feature] is a process for conditionally enabling tool features.
/// ///
/// All settings are optional, and if not provided will generally default to /// All settings are optional, and if not provided will generally default to
......
...@@ -27,6 +27,7 @@ import 'macos/macos_device.dart'; ...@@ -27,6 +27,7 @@ import 'macos/macos_device.dart';
import 'macos/macos_ipad_device.dart'; import 'macos/macos_ipad_device.dart';
import 'macos/macos_workflow.dart'; import 'macos/macos_workflow.dart';
import 'macos/xcdevice.dart'; import 'macos/xcdevice.dart';
import 'preview_device.dart';
import 'tester/flutter_tester.dart'; import 'tester/flutter_tester.dart';
import 'version.dart'; import 'version.dart';
import 'web/web_device.dart'; import 'web/web_device.dart';
...@@ -104,6 +105,14 @@ class FlutterDeviceManager extends DeviceManager { ...@@ -104,6 +105,14 @@ class FlutterDeviceManager extends DeviceManager {
fileSystem: fileSystem, fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils, operatingSystemUtils: operatingSystemUtils,
), ),
PreviewDeviceDiscovery(
platform: platform,
artifacts: artifacts,
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
featureFlags: featureFlags,
),
LinuxDevices( LinuxDevices(
platform: platform, platform: platform,
featureFlags: featureFlags, featureFlags: featureFlags,
......
...@@ -58,6 +58,9 @@ class FlutterFeatureFlags implements FeatureFlags { ...@@ -58,6 +58,9 @@ class FlutterFeatureFlags implements FeatureFlags {
@override @override
bool get isNativeAssetsEnabled => isEnabled(nativeAssets); bool get isNativeAssetsEnabled => isEnabled(nativeAssets);
@override
bool get isPreviewDeviceEnabled => isEnabled(previewDevice);
@override @override
bool isEnabled(Feature feature) { bool isEnabled(Feature feature) {
final String currentChannel = _flutterVersion.channel; final String currentChannel = _flutterVersion.channel;
......
...@@ -8,17 +8,18 @@ import 'package:meta/meta.dart'; ...@@ -8,17 +8,18 @@ import 'package:meta/meta.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'application_package.dart'; import 'application_package.dart';
import 'artifacts.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'base/io.dart'; import 'base/io.dart';
import 'base/logger.dart'; import 'base/logger.dart';
import 'base/platform.dart'; import 'base/platform.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'bundle_builder.dart'; import 'bundle_builder.dart';
import 'cache.dart';
import 'desktop_device.dart'; import 'desktop_device.dart';
import 'devfs.dart'; import 'devfs.dart';
import 'device.dart'; import 'device.dart';
import 'device_port_forwarder.dart'; import 'device_port_forwarder.dart';
import 'features.dart';
import 'project.dart'; import 'project.dart';
import 'protocol_discovery.dart'; import 'protocol_discovery.dart';
...@@ -28,30 +29,99 @@ BundleBuilder _defaultBundleBuilder() { ...@@ -28,30 +29,99 @@ BundleBuilder _defaultBundleBuilder() {
return BundleBuilder(); return BundleBuilder();
} }
class PreviewDeviceDiscovery extends DeviceDiscovery {
PreviewDeviceDiscovery({
required Platform platform,
required Artifacts artifacts,
required FileSystem fileSystem,
required Logger logger,
required ProcessManager processManager,
required FeatureFlags featureFlags,
}) : _artifacts = artifacts,
_logger = logger,
_processManager = processManager,
_fileSystem = fileSystem,
_platform = platform,
_features = featureFlags;
final Platform _platform;
final Artifacts _artifacts;
final Logger _logger;
final ProcessManager _processManager;
final FileSystem _fileSystem;
final FeatureFlags _features;
@override
bool get canListAnything => _platform.isWindows;
@override
bool get supportsPlatform => _platform.isWindows;
@override
List<String> get wellKnownIds => <String>['preview'];
@override
Future<List<Device>> devices({
Duration? timeout,
DeviceDiscoveryFilter? filter,
}) async {
final File previewBinary = _fileSystem.file(_artifacts.getArtifactPath(Artifact.flutterPreviewDevice));
if (!previewBinary.existsSync()) {
return const <Device>[];
}
final PreviewDevice device = PreviewDevice(
artifacts: _artifacts,
fileSystem: _fileSystem,
logger: _logger,
processManager: _processManager,
previewBinary: previewBinary,
);
final bool matchesRequirements;
if (!_features.isPreviewDeviceEnabled) {
matchesRequirements = false;
} else if (filter == null) {
matchesRequirements = true;
} else {
matchesRequirements = await filter.matchesRequirements(device);
}
return <Device>[
if (matchesRequirements)
device,
];
}
@override
Future<List<Device>> discoverDevices({
Duration? timeout,
DeviceDiscoveryFilter? filter,
}) {
return devices();
}
}
/// A device type that runs a prebuilt desktop binary alongside a locally compiled kernel file. /// A device type that runs a prebuilt desktop binary alongside a locally compiled kernel file.
///
/// This could be used to support debug local development without plugins on machines that
/// have not completed the SDK setup. These features are not fully implemented and the
/// device is not currently discoverable.
class PreviewDevice extends Device { class PreviewDevice extends Device {
PreviewDevice({ PreviewDevice({
required Platform platform,
required ProcessManager processManager, required ProcessManager processManager,
required Logger logger, required Logger logger,
required FileSystem fileSystem, required FileSystem fileSystem,
required Artifacts artifacts,
required File previewBinary,
@visibleForTesting BundleBuilderFactory builderFactory = _defaultBundleBuilder, @visibleForTesting BundleBuilderFactory builderFactory = _defaultBundleBuilder,
}) : _platform = platform, }) : _previewBinary = previewBinary,
_processManager = processManager, _processManager = processManager,
_logger = logger, _logger = logger,
_fileSystem = fileSystem, _fileSystem = fileSystem,
_bundleBuilderFactory = builderFactory, _bundleBuilderFactory = builderFactory,
_artifacts = artifacts,
super('preview', ephemeral: false, category: Category.desktop, platformType: PlatformType.custom); super('preview', ephemeral: false, category: Category.desktop, platformType: PlatformType.custom);
final Platform _platform;
final ProcessManager _processManager; final ProcessManager _processManager;
final Logger _logger; final Logger _logger;
final FileSystem _fileSystem; final FileSystem _fileSystem;
final BundleBuilderFactory _bundleBuilderFactory; final BundleBuilderFactory _bundleBuilderFactory;
final Artifacts _artifacts;
final File _previewBinary;
@override @override
void clearLogs() { } void clearLogs() { }
...@@ -116,7 +186,7 @@ class PreviewDevice extends Device { ...@@ -116,7 +186,7 @@ class PreviewDevice extends Device {
await _bundleBuilderFactory().build( await _bundleBuilderFactory().build(
buildInfo: debuggingOptions.buildInfo, buildInfo: debuggingOptions.buildInfo,
mainPath: mainPath, mainPath: mainPath,
platform: TargetPlatform.tester, platform: TargetPlatform.windows_x64,
assetDirPath: getAssetBuildDirectory(), assetDirPath: getAssetBuildDirectory(),
); );
copyDirectory(_fileSystem.directory( copyDirectory(_fileSystem.directory(
...@@ -128,13 +198,18 @@ class PreviewDevice extends Device { ...@@ -128,13 +198,18 @@ class PreviewDevice extends Device {
} }
// Merge with precompiled executable. // Merge with precompiled executable.
final Directory precompiledDirectory = _fileSystem.directory(_fileSystem.path.join(Cache.flutterRoot!, 'artifacts_temp', 'Debug')); final String copiedPreviewBinaryPath = assetDirectory.childFile(_previewBinary.basename).path;
copyDirectory(precompiledDirectory, assetDirectory); _previewBinary.copySync(copiedPreviewBinaryPath);
final String windowsPath = _artifacts
.getArtifactPath(Artifact.windowsDesktopPath, platform: TargetPlatform.windows_x64, mode: BuildMode.debug);
final File windowsDll = _fileSystem.file(_fileSystem.path.join(windowsPath, 'flutter_windows.dll'));
final File icu = _fileSystem.file(_fileSystem.path.join(windowsPath, 'icudtl.dat'));
windowsDll.copySync(assetDirectory.childFile('flutter_windows.dll').path);
icu.copySync(assetDirectory.childDirectory('data').childFile('icudtl.dat').path);
final Process process = await _processManager.start( final Process process = await _processManager.start(
<String>[ <String>[copiedPreviewBinaryPath],
assetDirectory.childFile('splash').path,
],
); );
_process = process; _process = process;
_logReader.initializeProcess(process); _logReader.initializeProcess(process);
...@@ -169,10 +244,7 @@ class PreviewDevice extends Device { ...@@ -169,10 +244,7 @@ class PreviewDevice extends Device {
@override @override
Future<TargetPlatform> get targetPlatform async { Future<TargetPlatform> get targetPlatform async {
if (_platform.isWindows) { return TargetPlatform.windows_x64;
return TargetPlatform.windows_x64;
}
return TargetPlatform.tester;
} }
@override @override
......
...@@ -56,9 +56,10 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, { ...@@ -56,9 +56,10 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {
// TODO(pbo-linaro): Add support for windows-arm64 platform, https://github.com/flutter/flutter/issues/129807 // TODO(pbo-linaro): Add support for windows-arm64 platform, https://github.com/flutter/flutter/issues/129807
const TargetPlatform targetPlatform = TargetPlatform.windows_x64; const TargetPlatform targetPlatform = TargetPlatform.windows_x64;
final Directory buildDirectory = globals.fs.directory( final Directory buildDirectory = globals.fs.directory(globals.fs.path.join(
getWindowsBuildDirectory(targetPlatform) projectPath,
); getWindowsBuildDirectory(targetPlatform),
));
final List<ProjectMigrator> migrators = <ProjectMigrator>[ final List<ProjectMigrator> migrators = <ProjectMigrator>[
CmakeCustomCommandMigration(windowsProject, globals.logger), CmakeCustomCommandMigration(windowsProject, globals.logger),
......
...@@ -10,6 +10,7 @@ import 'package:flutter_tools/src/artifacts.dart'; ...@@ -10,6 +10,7 @@ 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/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
...@@ -65,7 +66,10 @@ void main() { ...@@ -65,7 +66,10 @@ void main() {
late FileSystem fileSystem; late FileSystem fileSystem;
late TestUsage usage; late TestUsage usage;
late FakeProcessManager fakeProcessManager; late FakeProcessManager fakeProcessManager;
late ProcessUtils processUtils;
late BufferLogger logger;
late XcodeProjectInterpreter xcodeProjectInterpreter; late XcodeProjectInterpreter xcodeProjectInterpreter;
late Artifacts artifacts;
setUpAll(() { setUpAll(() {
Cache.disableLocking(); Cache.disableLocking();
...@@ -73,8 +77,14 @@ void main() { ...@@ -73,8 +77,14 @@ void main() {
setUp(() { setUp(() {
fileSystem = MemoryFileSystem.test(); fileSystem = MemoryFileSystem.test();
artifacts = Artifacts.test(fileSystem: fileSystem);
logger = BufferLogger.test();
usage = TestUsage(); usage = TestUsage();
fakeProcessManager = FakeProcessManager.empty(); fakeProcessManager = FakeProcessManager.empty();
processUtils = ProcessUtils(
logger: logger,
processManager: fakeProcessManager,
);
xcodeProjectInterpreter = FakeXcodeProjectInterpreter(); xcodeProjectInterpreter = FakeXcodeProjectInterpreter();
}); });
...@@ -139,10 +149,12 @@ STDERR STUFF ...@@ -139,10 +149,12 @@ STDERR STUFF
testUsingContext('macOS build fails when there is no macos project', () async { testUsingContext('macOS build fails when there is no macos project', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
createCoreMockProjectFiles(); createCoreMockProjectFiles();
...@@ -161,10 +173,12 @@ STDERR STUFF ...@@ -161,10 +173,12 @@ STDERR STUFF
testUsingContext('macOS build successfully with renamed .xcodeproj/.xcworkspace files', () async { testUsingContext('macOS build successfully with renamed .xcodeproj/.xcworkspace files', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
...@@ -184,10 +198,12 @@ STDERR STUFF ...@@ -184,10 +198,12 @@ STDERR STUFF
testUsingContext('macOS build fails on non-macOS platform', () async { testUsingContext('macOS build fails on non-macOS platform', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
fileSystem.file('pubspec.yaml').createSync(); fileSystem.file('pubspec.yaml').createSync();
...@@ -206,10 +222,12 @@ STDERR STUFF ...@@ -206,10 +222,12 @@ STDERR STUFF
testUsingContext('macOS build fails when feature is disabled', () async { testUsingContext('macOS build fails when feature is disabled', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
fileSystem.file('pubspec.yaml').createSync(); fileSystem.file('pubspec.yaml').createSync();
...@@ -228,10 +246,12 @@ STDERR STUFF ...@@ -228,10 +246,12 @@ STDERR STUFF
testUsingContext('macOS build forwards error stdout to status logger error', () async { testUsingContext('macOS build forwards error stdout to status logger error', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
createMinimalMockProjectFiles(); createMinimalMockProjectFiles();
...@@ -258,10 +278,12 @@ STDERR STUFF ...@@ -258,10 +278,12 @@ STDERR STUFF
testUsingContext('macOS build invokes xcode build (debug)', () async { testUsingContext('macOS build invokes xcode build (debug)', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
createMinimalMockProjectFiles(); createMinimalMockProjectFiles();
...@@ -280,10 +302,12 @@ STDERR STUFF ...@@ -280,10 +302,12 @@ STDERR STUFF
testUsingContext('macOS build invokes xcode build (debug) with verbosity', () async { testUsingContext('macOS build invokes xcode build (debug) with verbosity', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
createMinimalMockProjectFiles(); createMinimalMockProjectFiles();
...@@ -303,10 +327,12 @@ STDERR STUFF ...@@ -303,10 +327,12 @@ STDERR STUFF
testUsingContext('macOS build invokes xcode build (profile)', () async { testUsingContext('macOS build invokes xcode build (profile)', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
createMinimalMockProjectFiles(); createMinimalMockProjectFiles();
...@@ -326,10 +352,12 @@ STDERR STUFF ...@@ -326,10 +352,12 @@ STDERR STUFF
testUsingContext('macOS build invokes xcode build (release)', () async { testUsingContext('macOS build invokes xcode build (release)', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
createMinimalMockProjectFiles(); createMinimalMockProjectFiles();
...@@ -348,10 +376,12 @@ STDERR STUFF ...@@ -348,10 +376,12 @@ STDERR STUFF
testUsingContext('macOS build supports standard desktop build options', () async { testUsingContext('macOS build supports standard desktop build options', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
createMinimalMockProjectFiles(); createMinimalMockProjectFiles();
...@@ -439,10 +469,12 @@ STDERR STUFF ...@@ -439,10 +469,12 @@ STDERR STUFF
]); ]);
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
...@@ -461,10 +493,12 @@ STDERR STUFF ...@@ -461,10 +493,12 @@ STDERR STUFF
testUsingContext('macOS build supports build-name and build-number', () async { testUsingContext('macOS build supports build-name and build-number', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
createMinimalMockProjectFiles(); createMinimalMockProjectFiles();
...@@ -496,10 +530,12 @@ STDERR STUFF ...@@ -496,10 +530,12 @@ STDERR STUFF
testUsingContext('Refuses to build for macOS when feature is disabled', () { testUsingContext('Refuses to build for macOS when feature is disabled', () {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand( final CommandRunner<void> runner = createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
)); ));
...@@ -526,10 +562,12 @@ STDERR STUFF ...@@ -526,10 +562,12 @@ STDERR STUFF
testUsingContext('Performs code size analysis and sends analytics', () async { testUsingContext('Performs code size analysis and sends analytics', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
createMinimalMockProjectFiles(); createMinimalMockProjectFiles();
......
...@@ -4,8 +4,10 @@ ...@@ -4,8 +4,10 @@
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:file/memory.dart'; import 'package:file/memory.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/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart'; import 'package:flutter_tools/src/commands/build.dart';
...@@ -28,23 +30,37 @@ void main() { ...@@ -28,23 +30,37 @@ void main() {
]), throwsToolExit(message: '"--${FlutterOptions.kDartObfuscationOption}" can only be used in ' ]), throwsToolExit(message: '"--${FlutterOptions.kDartObfuscationOption}" can only be used in '
'combination with "--${FlutterOptions.kSplitDebugInfoOption}"')); 'combination with "--${FlutterOptions.kSplitDebugInfoOption}"'));
}); });
group('Fatal Logs', () { group('Fatal Logs', () {
late FakeBuildCommand command; late FakeBuildCommand command;
late MemoryFileSystem fs; late MemoryFileSystem fs;
late BufferLogger logger;
late ProcessManager processManager;
late ProcessUtils processUtils;
late Artifacts artifacts;
setUp(() { setUp(() {
fs = MemoryFileSystem.test(); fs = MemoryFileSystem.test();
artifacts = Artifacts.test(fileSystem: fs);
fs.file('/package/pubspec.yaml').createSync(recursive: true); fs.file('/package/pubspec.yaml').createSync(recursive: true);
fs.currentDirectory = '/package'; fs.currentDirectory = '/package';
Cache.disableLocking(); Cache.disableLocking();
logger = BufferLogger.test();
processManager = FakeProcessManager.empty();
processUtils = ProcessUtils(
logger: logger,
processManager: processManager,
);
}); });
testUsingContext("doesn't fail if --fatal-warnings specified and no warnings occur", () async { testUsingContext("doesn't fail if --fatal-warnings specified and no warnings occur", () async {
command = FakeBuildCommand( command = FakeBuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fs,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
try { try {
...@@ -58,15 +74,17 @@ void main() { ...@@ -58,15 +74,17 @@ void main() {
} }
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
}); });
testUsingContext("doesn't fail if --fatal-warnings not specified", () async { testUsingContext("doesn't fail if --fatal-warnings not specified", () async {
command = FakeBuildCommand( command = FakeBuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fs,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
testLogger.printWarning('Warning: Mild annoyance Will Robinson!'); testLogger.printWarning('Warning: Mild annoyance Will Robinson!');
...@@ -80,15 +98,17 @@ void main() { ...@@ -80,15 +98,17 @@ void main() {
} }
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
}); });
testUsingContext('fails if --fatal-warnings specified and warnings emitted', () async { testUsingContext('fails if --fatal-warnings specified and warnings emitted', () async {
command = FakeBuildCommand( command = FakeBuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fs,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
testLogger.printWarning('Warning: Mild annoyance Will Robinson!'); testLogger.printWarning('Warning: Mild annoyance Will Robinson!');
...@@ -99,15 +119,17 @@ void main() { ...@@ -99,15 +119,17 @@ void main() {
]), throwsToolExit(message: 'Logger received warning output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.')); ]), throwsToolExit(message: 'Logger received warning output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
}); });
testUsingContext('fails if --fatal-warnings specified and errors emitted', () async { testUsingContext('fails if --fatal-warnings specified and errors emitted', () async {
command = FakeBuildCommand( command = FakeBuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fs,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
testLogger.printError('Error: Danger Will Robinson!'); testLogger.printError('Error: Danger Will Robinson!');
...@@ -118,7 +140,7 @@ void main() { ...@@ -118,7 +140,7 @@ void main() {
]), throwsToolExit(message: 'Logger received error output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.')); ]), throwsToolExit(message: 'Logger received error output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
}); });
}); });
} }
...@@ -149,8 +171,10 @@ class FakeBuildCommand extends BuildCommand { ...@@ -149,8 +171,10 @@ class FakeBuildCommand extends BuildCommand {
required super.osUtils, required super.osUtils,
required Logger logger, required Logger logger,
required super.androidSdk, required super.androidSdk,
required super.processUtils,
required super.artifacts,
bool verboseHelp = false, bool verboseHelp = false,
}) : super(logger: logger, verboseHelp: verboseHelp,) { }) : super(logger: logger) {
addSubcommand(FakeBuildSubcommand(logger: logger, verboseHelp: verboseHelp)); addSubcommand(FakeBuildSubcommand(logger: logger, verboseHelp: verboseHelp));
} }
......
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:file/memory.dart'; import 'package:file/memory.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/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
...@@ -28,6 +30,10 @@ void main() { ...@@ -28,6 +30,10 @@ void main() {
'FLUTTER_ROOT': '/', 'FLUTTER_ROOT': '/',
}, },
); );
late ProcessUtils processUtils;
late BufferLogger logger;
late ProcessManager processManager;
late Artifacts artifacts;
setUpAll(() { setUpAll(() {
Cache.flutterRoot = ''; Cache.flutterRoot = '';
...@@ -42,15 +48,24 @@ void main() { ...@@ -42,15 +48,24 @@ void main() {
fileSystem.file('.packages').createSync(); fileSystem.file('.packages').createSync();
fileSystem.file(fileSystem.path.join('web', 'index.html')).createSync(recursive: true); fileSystem.file(fileSystem.path.join('web', 'index.html')).createSync(recursive: true);
fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true); fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true);
artifacts = Artifacts.test(fileSystem: fileSystem);
logger = BufferLogger.test();
processManager = FakeProcessManager.empty();
processUtils = ProcessUtils(
logger: logger,
processManager: processManager,
);
}); });
testUsingContext('Refuses to build for web when missing index.html', () async { testUsingContext('Refuses to build for web when missing index.html', () async {
fileSystem.file(fileSystem.path.join('web', 'index.html')).deleteSync(); fileSystem.file(fileSystem.path.join('web', 'index.html')).deleteSync();
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand( final CommandRunner<void> runner = createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
)); ));
...@@ -62,15 +77,17 @@ void main() { ...@@ -62,15 +77,17 @@ void main() {
Platform: () => fakePlatform, Platform: () => fakePlatform,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
}); });
testUsingContext('Refuses to build a debug build for web', () async { testUsingContext('Refuses to build a debug build for web', () async {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand( final CommandRunner<void> runner = createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
)); ));
...@@ -79,15 +96,17 @@ void main() { ...@@ -79,15 +96,17 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => fakePlatform, Platform: () => fakePlatform,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
}); });
testUsingContext('Refuses to build for web when feature is disabled', () async { testUsingContext('Refuses to build for web when feature is disabled', () async {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand( final CommandRunner<void> runner = createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
)); ));
...@@ -99,15 +118,17 @@ void main() { ...@@ -99,15 +118,17 @@ void main() {
Platform: () => fakePlatform, Platform: () => fakePlatform,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(), FeatureFlags: () => TestFeatureFlags(),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
}); });
testUsingContext('Setup for a web build with default output directory', () async { testUsingContext('Setup for a web build with default output directory', () async {
final BuildCommand buildCommand = BuildCommand( final BuildCommand buildCommand = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
final CommandRunner<void> runner = createTestCommandRunner(buildCommand); final CommandRunner<void> runner = createTestCommandRunner(buildCommand);
...@@ -121,7 +142,7 @@ void main() { ...@@ -121,7 +142,7 @@ void main() {
Platform: () => fakePlatform, Platform: () => fakePlatform,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true), (Target target, Environment environment) { BuildSystem: () => TestBuildSystem.all(BuildResult(success: true), (Target target, Environment environment) {
expect(environment.defines, <String, String>{ expect(environment.defines, <String, String>{
'TargetFile': 'lib/main.dart', 'TargetFile': 'lib/main.dart',
...@@ -144,11 +165,13 @@ void main() { ...@@ -144,11 +165,13 @@ void main() {
testUsingContext('Does not allow -O0 optimization level', () async { testUsingContext('Does not allow -O0 optimization level', () async {
final BuildCommand buildCommand = BuildCommand( final BuildCommand buildCommand = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
processUtils: processUtils,
); );
final CommandRunner<void> runner = createTestCommandRunner(buildCommand); final CommandRunner<void> runner = createTestCommandRunner(buildCommand);
setupFileSystemForEndToEndTest(fileSystem); setupFileSystemForEndToEndTest(fileSystem);
...@@ -191,10 +214,12 @@ void main() { ...@@ -191,10 +214,12 @@ void main() {
testUsingContext('Setup for a web build with a user specified output directory', testUsingContext('Setup for a web build with a user specified output directory',
() async { () async {
final BuildCommand buildCommand = BuildCommand( final BuildCommand buildCommand = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
final CommandRunner<void> runner = createTestCommandRunner(buildCommand); final CommandRunner<void> runner = createTestCommandRunner(buildCommand);
...@@ -219,7 +244,7 @@ void main() { ...@@ -219,7 +244,7 @@ void main() {
Platform: () => fakePlatform, Platform: () => fakePlatform,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true), (Target target, Environment environment) { BuildSystem: () => TestBuildSystem.all(BuildResult(success: true), (Target target, Environment environment) {
expect(environment.defines, <String, String>{ expect(environment.defines, <String, String>{
'TargetFile': 'lib/main.dart', 'TargetFile': 'lib/main.dart',
...@@ -246,7 +271,7 @@ void main() { ...@@ -246,7 +271,7 @@ void main() {
Platform: () => fakePlatform, Platform: () => fakePlatform,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(), FeatureFlags: () => TestFeatureFlags(),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
}); });
testUsingContext('not hidden if feature flag is enabled', () async { testUsingContext('not hidden if feature flag is enabled', () async {
...@@ -255,7 +280,7 @@ void main() { ...@@ -255,7 +280,7 @@ void main() {
Platform: () => fakePlatform, Platform: () => fakePlatform,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
}); });
testUsingContext('Defaults to web renderer auto mode when no option is specified', () async { testUsingContext('Defaults to web renderer auto mode when no option is specified', () async {
...@@ -270,7 +295,7 @@ void main() { ...@@ -270,7 +295,7 @@ void main() {
Platform: () => fakePlatform, Platform: () => fakePlatform,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)), BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
}); });
...@@ -295,7 +320,7 @@ void main() { ...@@ -295,7 +320,7 @@ void main() {
Platform: () => fakePlatform, Platform: () => fakePlatform,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)), BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
}); });
...@@ -311,7 +336,7 @@ void main() { ...@@ -311,7 +336,7 @@ void main() {
Platform: () => fakePlatform, Platform: () => fakePlatform,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)), BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
}); });
...@@ -327,7 +352,7 @@ void main() { ...@@ -327,7 +352,7 @@ void main() {
Platform: () => fakePlatform, Platform: () => fakePlatform,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)), BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
}); });
} }
......
...@@ -82,7 +82,7 @@ void main() { ...@@ -82,7 +82,7 @@ void main() {
'-S', '-S',
fileSystem.path.absolute(fileSystem.path.dirname(buildFilePath)), fileSystem.path.absolute(fileSystem.path.dirname(buildFilePath)),
'-B', '-B',
r'build\windows\x64', r'C:\build\windows\x64',
'-G', '-G',
generator, generator,
'-A', '-A',
...@@ -103,7 +103,7 @@ void main() { ...@@ -103,7 +103,7 @@ void main() {
command: <String>[ command: <String>[
_cmakePath, _cmakePath,
'--build', '--build',
r'build\windows\x64', r'C:\build\windows\x64',
'--config', '--config',
buildMode, buildMode,
...<String>['--target', 'INSTALL'], ...<String>['--target', 'INSTALL'],
......
// 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 'package:args/command_runner.dart';
import 'package:file_testing/file_testing.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/cache.dart';
import 'package:flutter_tools/src/commands/build_preview.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/test_flutter_command_runner.dart';
void main() {
Cache.disableLocking();
late Directory tempDir;
late BufferLogger logger;
final FileSystem fs = LocalFileSystemBlockingSetCurrentDirectory();
setUp(() {
tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
logger = BufferLogger.test();
});
tearDown(() {
tryToDelete(tempDir);
});
testUsingContext('flutter build _preview creates preview device', () async {
final String projectPath = await createProject(
tempDir,
arguments: <String>['--no-pub', '--template=app'],
);
final BuildPreviewCommand command = BuildPreviewCommand(
logger: logger,
verboseHelp: true,
fs: fs,
processUtils: globals.processUtils,
flutterRoot: Cache.flutterRoot!,
artifacts: globals.artifacts!,
);
final CommandRunner<void> runner = createTestCommandRunner(command);
await runner.run(<String>[
'_preview',
'--no-pub',
fs.path.join(projectPath, 'lib', 'main.dart'),
]);
expect(
fs
.directory(Cache.flutterRoot)
.childDirectory('bin')
.childDirectory('cache')
.childDirectory('artifacts')
.childDirectory('flutter_preview')
.childFile('flutter_preview.exe'),
exists,
);
}, skip: !const LocalPlatform().isWindows); // [intended] Flutter Preview only supported on Windows currently
}
...@@ -6,11 +6,13 @@ import 'package:args/command_runner.dart'; ...@@ -6,11 +6,13 @@ import 'package:args/command_runner.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_studio.dart'; import 'package:flutter_tools/src/android/android_studio.dart';
import 'package:flutter_tools/src/android/android_workflow.dart'; import 'package:flutter_tools/src/android/android_workflow.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/config.dart'; import 'package:flutter_tools/src/base/config.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/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/base/time.dart'; import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
...@@ -42,6 +44,7 @@ void main() { ...@@ -42,6 +44,7 @@ void main() {
late Directory tempDir; late Directory tempDir;
late Config testConfig; late Config testConfig;
late FileSystem fs; late FileSystem fs;
const String flutterRoot = '/path/to/flutter'; const String flutterRoot = '/path/to/flutter';
setUp(() { setUp(() {
...@@ -161,6 +164,9 @@ void main() { ...@@ -161,6 +164,9 @@ void main() {
late FakeClock fakeClock; late FakeClock fakeClock;
late FakeDoctor doctor; late FakeDoctor doctor;
late FakeAndroidStudio androidStudio; late FakeAndroidStudio androidStudio;
late ProcessManager processManager;
late BufferLogger logger;
late ProcessUtils processUtils;
setUp(() { setUp(() {
memoryFileSystem = MemoryFileSystem.test(); memoryFileSystem = MemoryFileSystem.test();
...@@ -169,6 +175,9 @@ void main() { ...@@ -169,6 +175,9 @@ void main() {
fakeClock = FakeClock(); fakeClock = FakeClock();
doctor = FakeDoctor(); doctor = FakeDoctor();
androidStudio = FakeAndroidStudio(); androidStudio = FakeAndroidStudio();
processManager = FakeProcessManager.empty();
logger = BufferLogger.test();
processUtils = ProcessUtils(logger: logger, processManager: processManager);
}); });
testUsingContext('flutter commands send timing events', () async { testUsingContext('flutter commands send timing events', () async {
...@@ -220,17 +229,17 @@ void main() { ...@@ -220,17 +229,17 @@ void main() {
testUsingContext('compound command usage path', () async { testUsingContext('compound command usage path', () async {
final BuildCommand buildCommand = BuildCommand( final BuildCommand buildCommand = BuildCommand(
artifacts: Artifacts.test(fileSystem: memoryFileSystem),
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: memoryFileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
final FlutterCommand buildApkCommand = buildCommand.subcommands['apk']! as FlutterCommand; final FlutterCommand buildApkCommand = buildCommand.subcommands['apk']! as FlutterCommand;
expect(await buildApkCommand.usagePath, 'build/apk'); expect(await buildApkCommand.usagePath, 'build/apk');
}, overrides: <Type, Generator>{
Usage: () => testUsage,
}); });
testUsingContext('command sends localtime', () async { testUsingContext('command sends localtime', () async {
...@@ -253,7 +262,7 @@ void main() { ...@@ -253,7 +262,7 @@ void main() {
expect(log.contains(formatDateTime(dateTime)), isTrue); expect(log.contains(formatDateTime(dateTime)), isTrue);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem, FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
SystemClock: () => fakeClock, SystemClock: () => fakeClock,
Platform: () => FakePlatform( Platform: () => FakePlatform(
environment: <String, String>{ environment: <String, String>{
...@@ -283,7 +292,7 @@ void main() { ...@@ -283,7 +292,7 @@ void main() {
expect(log.contains(formatDateTime(dateTime)), isTrue); expect(log.contains(formatDateTime(dateTime)), isTrue);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem, FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => processManager,
SystemClock: () => fakeClock, SystemClock: () => fakeClock,
Platform: () => FakePlatform( Platform: () => FakePlatform(
environment: <String, String>{ environment: <String, String>{
......
...@@ -6,10 +6,10 @@ import 'package:flutter_tools/src/base/file_system.dart'; ...@@ -6,10 +6,10 @@ import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/signals.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart';
void main() { void main() {
group('OperatingSystemUtils', () { group('OperatingSystemUtils', () {
...@@ -17,7 +17,7 @@ void main() { ...@@ -17,7 +17,7 @@ void main() {
late FileSystem fileSystem; late FileSystem fileSystem;
setUp(() { setUp(() {
fileSystem = LocalFileSystem.test(signals: Signals.test()); fileSystem = LocalFileSystem.test(signals: FakeSignals());
tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_tools_os_utils_test.'); tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_tools_os_utils_test.');
}); });
......
...@@ -5,10 +5,12 @@ ...@@ -5,10 +5,12 @@
import 'package:args/args.dart'; import 'package:args/args.dart';
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:file/memory.dart'; import 'package:file/memory.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/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/base/signals.dart'; import 'package:flutter_tools/src/base/signals.dart';
import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
...@@ -111,11 +113,19 @@ void main() { ...@@ -111,11 +113,19 @@ void main() {
}); });
testUsingContext('Include only supported sub commands', () { testUsingContext('Include only supported sub commands', () {
final BufferLogger logger = BufferLogger.test();
final ProcessUtils processUtils = ProcessUtils(
logger: logger,
processManager: FakeProcessManager.empty(),
);
final MemoryFileSystem fs = MemoryFileSystem.test();
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
artifacts: Artifacts.test(fileSystem: fs),
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(), fileSystem: fs,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
); );
for (final Command<void> x in command.subcommands.values) { for (final Command<void> x in command.subcommands.values) {
......
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/application_package.dart'; import 'package:flutter_tools/src/application_package.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/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
...@@ -22,20 +23,38 @@ import 'package:test/fake.dart'; ...@@ -22,20 +23,38 @@ import 'package:test/fake.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/context.dart'; import '../src/context.dart';
import '../src/fakes.dart';
void main() { void main() {
String? flutterRootBackup;
late MemoryFileSystem fs;
late File previewBinary;
setUp(() {
fs = MemoryFileSystem.test(style: FileSystemStyle.windows);
Cache.flutterRoot = r'C:\path\to\flutter';
previewBinary = fs.file('${Cache.flutterRoot}\\bin\\cache\\artifacts\\flutter_preview\\flutter_preview.exe');
previewBinary.createSync(recursive: true);
flutterRootBackup = Cache.flutterRoot;
});
tearDown(() {
Cache.flutterRoot = flutterRootBackup;
});
testWithoutContext('PreviewDevice defaults', () async { testWithoutContext('PreviewDevice defaults', () async {
final PreviewDevice device = PreviewDevice( final PreviewDevice device = PreviewDevice(
fileSystem: MemoryFileSystem.test(), artifacts: Artifacts.test(),
fileSystem: fs,
processManager: FakeProcessManager.any(), processManager: FakeProcessManager.any(),
previewBinary: previewBinary,
logger: BufferLogger.test(), logger: BufferLogger.test(),
platform: FakePlatform(),
); );
expect(await device.isLocalEmulator, false); expect(await device.isLocalEmulator, false);
expect(device.name, 'preview'); expect(device.name, 'preview');
expect(await device.sdkNameAndVersion, 'preview'); expect(await device.sdkNameAndVersion, 'preview');
expect(await device.targetPlatform, TargetPlatform.tester); expect(await device.targetPlatform, TargetPlatform.windows_x64);
expect(device.category, Category.desktop); expect(device.category, Category.desktop);
expect(device.ephemeral, false); expect(device.ephemeral, false);
expect(device.id, 'preview'); expect(device.id, 'preview');
...@@ -48,29 +67,29 @@ void main() { ...@@ -48,29 +67,29 @@ void main() {
}); });
testUsingContext('Can build a simulator app', () async { testUsingContext('Can build a simulator app', () async {
Cache.flutterRoot = '';
final Completer<void> completer = Completer<void>(); final Completer<void> completer = Completer<void>();
final FileSystem fileSystem = MemoryFileSystem.test();
final BufferLogger logger = BufferLogger.test(); final BufferLogger logger = BufferLogger.test();
final PreviewDevice device = PreviewDevice( final PreviewDevice device = PreviewDevice(
fileSystem: fileSystem, artifacts: Artifacts.test(),
fileSystem: fs,
previewBinary: previewBinary,
processManager: FakeProcessManager.list(<FakeCommand>[ processManager: FakeProcessManager.list(<FakeCommand>[
FakeCommand( FakeCommand(
command: const <String>[ command: const <String>[
'/.tmp_rand0/flutter_preview.rand0/splash', r'C:\.tmp_rand0\flutter_preview.rand0\flutter_preview.exe',
], ],
stdout: 'The Dart VM service is listening on http://127.0.0.1:64494/fZ_B2N6JRwY=/\n', stdout: 'The Dart VM service is listening on http://127.0.0.1:64494/fZ_B2N6JRwY=/\n',
completer: completer, completer: completer,
), ),
]), ]),
logger: logger, logger: logger,
platform: FakePlatform(), builderFactory: () => FakeBundleBuilder(fs),
builderFactory: () => FakeBundleBuilder(fileSystem),
); );
fileSystem final Directory previewDeviceCacheDir = fs
.directory('artifacts_temp') .directory('Artifact.windowsDesktopPath.TargetPlatform.windows_x64.debug')
.childDirectory('Debug') ..createSync(recursive: true);
.createSync(recursive: true); previewDeviceCacheDir.childFile('flutter_windows.dll').writeAsStringSync('1010101');
previewDeviceCacheDir.childFile('icudtl.dat').writeAsStringSync('1010101');
final LaunchResult result = await device.startApp( final LaunchResult result = await device.startApp(
FakeApplicationPackage(), FakeApplicationPackage(),
...@@ -80,6 +99,84 @@ void main() { ...@@ -80,6 +99,84 @@ void main() {
expect(result.started, true); expect(result.started, true);
expect(result.vmServiceUri, Uri.parse('http://127.0.0.1:64494/fZ_B2N6JRwY=/')); expect(result.vmServiceUri, Uri.parse('http://127.0.0.1:64494/fZ_B2N6JRwY=/'));
}); });
group('PreviewDeviceDiscovery', () {
late Artifacts artifacts;
late ProcessManager processManager;
final FakePlatform windowsPlatform = FakePlatform(operatingSystem: 'windows');
final FakePlatform macPlatform = FakePlatform(operatingSystem: 'macos');
final FakePlatform linuxPlatform = FakePlatform();
final TestFeatureFlags featureFlags = TestFeatureFlags(isPreviewDeviceEnabled: true);
setUp(() {
artifacts = Artifacts.test(fileSystem: fs);
processManager = FakeProcessManager.empty();
});
testWithoutContext('PreviewDeviceDiscovery on linux', () async {
final PreviewDeviceDiscovery discovery = PreviewDeviceDiscovery(
artifacts: artifacts,
fileSystem: fs,
logger: BufferLogger.test(),
processManager: processManager,
platform: linuxPlatform,
featureFlags: featureFlags,
);
final List<Device> devices = await discovery.devices();
expect(devices, isEmpty);
});
testWithoutContext('PreviewDeviceDiscovery on macOS', () async {
final PreviewDeviceDiscovery discovery = PreviewDeviceDiscovery(
artifacts: artifacts,
fileSystem: fs,
logger: BufferLogger.test(),
processManager: processManager,
platform: macPlatform,
featureFlags: featureFlags,
);
final List<Device> devices = await discovery.devices();
expect(devices, isEmpty);
});
testWithoutContext('PreviewDeviceDiscovery on Windows returns preview when binary exists', () async {
// ensure Flutter preview binary exists in cache.
fs.file(artifacts.getArtifactPath(Artifact.flutterPreviewDevice)).writeAsBytesSync(<int>[1, 0, 0, 1]);
final PreviewDeviceDiscovery discovery = PreviewDeviceDiscovery(
artifacts: artifacts,
fileSystem: fs,
logger: BufferLogger.test(),
processManager: processManager,
platform: windowsPlatform,
featureFlags: featureFlags,
);
final List<Device> devices = await discovery.devices();
expect(devices, hasLength(1));
final Device previewDevice = devices.first;
expect(previewDevice, isA<PreviewDevice>());
});
testWithoutContext('PreviewDeviceDiscovery on Windows returns nothing when binary does not exist', () async {
final PreviewDeviceDiscovery discovery = PreviewDeviceDiscovery(
artifacts: artifacts,
fileSystem: fs,
logger: BufferLogger.test(),
processManager: processManager,
platform: windowsPlatform,
featureFlags: featureFlags,
);
final List<Device> devices = await discovery.devices();
expect(devices, isEmpty);
});
});
} }
class FakeFlutterProject extends Fake implements FlutterProject { } class FakeFlutterProject extends Fake implements FlutterProject { }
......
...@@ -3,8 +3,10 @@ ...@@ -3,8 +3,10 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:file/memory.dart'; import 'package:file/memory.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/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart'; import 'package:flutter_tools/src/commands/build.dart';
...@@ -26,14 +28,24 @@ void main() { ...@@ -26,14 +28,24 @@ void main() {
late File registrant; late File registrant;
// Environment overrides // Environment overrides
late Artifacts artifacts;
late FileSystem fileSystem; late FileSystem fileSystem;
late ProcessManager processManager; late ProcessManager processManager;
late BuildSystem buildSystem; late BuildSystem buildSystem;
late ProcessUtils processUtils;
late BufferLogger logger;
setUp(() { setUp(() {
// Prepare environment overrides // Prepare environment overrides
fileSystem = MemoryFileSystem.test(); fileSystem = MemoryFileSystem.test();
artifacts = Artifacts.test(fileSystem: fileSystem);
processManager = FakeProcessManager.any(); processManager = FakeProcessManager.any();
logger = BufferLogger.test();
processUtils = ProcessUtils(
processManager: processManager,
logger: logger,
);
buildSystem = TestBuildSystem.all(BuildResult(success: true)); buildSystem = TestBuildSystem.all(BuildResult(success: true));
// Write some initial state into our testing filesystem // Write some initial state into our testing filesystem
setupFileSystemForEndToEndTest(fileSystem); setupFileSystemForEndToEndTest(fileSystem);
...@@ -47,11 +59,13 @@ void main() { ...@@ -47,11 +59,13 @@ void main() {
expect(registrant.existsSync(), isFalse); expect(registrant.existsSync(), isFalse);
await createTestCommandRunner(BuildCommand( await createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: buildSystem, buildSystem: buildSystem,
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
processUtils: processUtils,
)) ))
.run(<String>['build', 'web', '--no-pub']); .run(<String>['build', 'web', '--no-pub']);
...@@ -70,11 +84,13 @@ void main() { ...@@ -70,11 +84,13 @@ void main() {
expect(contentsBeforeBuild, isNot(contains('lib/generated_plugin_registrant.dart'))); expect(contentsBeforeBuild, isNot(contains('lib/generated_plugin_registrant.dart')));
await createTestCommandRunner(BuildCommand( await createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: buildSystem, buildSystem: buildSystem,
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
processUtils: processUtils,
)) ))
.run(<String>['build', 'web', '--no-pub']); .run(<String>['build', 'web', '--no-pub']);
...@@ -92,10 +108,12 @@ void main() { ...@@ -92,10 +108,12 @@ void main() {
expect(gitignore.readAsStringSync(), contains('lib/generated_plugin_registrant.dart')); expect(gitignore.readAsStringSync(), contains('lib/generated_plugin_registrant.dart'));
await createTestCommandRunner(BuildCommand( await createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: buildSystem, buildSystem: buildSystem,
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
)) ))
.run(<String>['build', 'web', '--no-pub']); .run(<String>['build', 'web', '--no-pub']);
...@@ -113,10 +131,12 @@ void main() { ...@@ -113,10 +131,12 @@ void main() {
expect(registrant.existsSync(), isTrue); expect(registrant.existsSync(), isTrue);
await createTestCommandRunner(BuildCommand( await createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: buildSystem, buildSystem: buildSystem,
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
)) ))
.run(<String>['build', 'web', '--no-pub']); .run(<String>['build', 'web', '--no-pub']);
...@@ -136,10 +156,12 @@ void main() { ...@@ -136,10 +156,12 @@ void main() {
expect(gitignore.readAsStringSync(), contains('lib/generated_plugin_registrant.dart')); expect(gitignore.readAsStringSync(), contains('lib/generated_plugin_registrant.dart'));
await createTestCommandRunner(BuildCommand( await createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(), androidSdk: FakeAndroidSdk(),
buildSystem: buildSystem, buildSystem: buildSystem,
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
)) ))
.run(<String>['build', 'web', '--no-pub']); .run(<String>['build', 'web', '--no-pub']);
......
...@@ -382,7 +382,8 @@ class NoopCrashReporter implements CrashReporter { ...@@ -382,7 +382,8 @@ class NoopCrashReporter implements CrashReporter {
} }
class LocalFileSystemBlockingSetCurrentDirectory extends LocalFileSystem { class LocalFileSystemBlockingSetCurrentDirectory extends LocalFileSystem {
// Use [FakeSignals] so developers running the test suite can kill the test runner. // Use [FakeSignals] so developers running the test suite can kill the test
// runner.
LocalFileSystemBlockingSetCurrentDirectory() : super.test(signals: FakeSignals()); LocalFileSystemBlockingSetCurrentDirectory() : super.test(signals: FakeSignals());
@override @override
......
...@@ -462,6 +462,7 @@ class TestFeatureFlags implements FeatureFlags { ...@@ -462,6 +462,7 @@ class TestFeatureFlags implements FeatureFlags {
this.isFlutterWebWasmEnabled = false, this.isFlutterWebWasmEnabled = false,
this.isCliAnimationEnabled = true, this.isCliAnimationEnabled = true,
this.isNativeAssetsEnabled = false, this.isNativeAssetsEnabled = false,
this.isPreviewDeviceEnabled = false,
}); });
@override @override
...@@ -497,31 +498,24 @@ class TestFeatureFlags implements FeatureFlags { ...@@ -497,31 +498,24 @@ class TestFeatureFlags implements FeatureFlags {
@override @override
final bool isNativeAssetsEnabled; final bool isNativeAssetsEnabled;
@override
final bool isPreviewDeviceEnabled;
@override @override
bool isEnabled(Feature feature) { bool isEnabled(Feature feature) {
switch (feature) { return switch (feature) {
case flutterWebFeature: flutterWebFeature => isWebEnabled,
return isWebEnabled; flutterLinuxDesktopFeature => isLinuxEnabled,
case flutterLinuxDesktopFeature: flutterMacOSDesktopFeature => isMacOSEnabled,
return isLinuxEnabled; flutterWindowsDesktopFeature => isWindowsEnabled,
case flutterMacOSDesktopFeature: flutterAndroidFeature => isAndroidEnabled,
return isMacOSEnabled; flutterIOSFeature => isIOSEnabled,
case flutterWindowsDesktopFeature: flutterFuchsiaFeature => isFuchsiaEnabled,
return isWindowsEnabled; flutterCustomDevicesFeature => areCustomDevicesEnabled,
case flutterAndroidFeature: cliAnimation => isCliAnimationEnabled,
return isAndroidEnabled; nativeAssets => isNativeAssetsEnabled,
case flutterIOSFeature: _ => false,
return isIOSEnabled; };
case flutterFuchsiaFeature:
return isFuchsiaEnabled;
case flutterCustomDevicesFeature:
return areCustomDevicesEnabled;
case cliAnimation:
return isCliAnimationEnabled;
case nativeAssets:
return isNativeAssetsEnabled;
}
return false;
} }
} }
......
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