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.
This commit is contained in:
Christopher Fujino 2023-10-17 17:27:54 -07:00 committed by GitHub
parent 492b6f7ddf
commit 8a31a3a284
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1514 additions and 705 deletions

View File

@ -173,9 +173,11 @@ List<FlutterCommand> generateCommands({
fileSystem: globals.fs,
),
BuildCommand(
artifacts: globals.artifacts!,
fileSystem: globals.fs,
buildSystem: globals.buildSystem,
osUtils: globals.os,
processUtils: globals.processUtils,
verboseHelp: verboseHelp,
androidSdk: globals.androidSdk,
logger: globals.logger,

View File

@ -69,6 +69,9 @@ enum Artifact {
/// The location of file generators.
flutterToolsFileGenerators,
/// Pre-built desktop debug app.
flutterPreviewDevice,
}
/// A subset of [Artifact]s that are platform and build mode independent
@ -213,6 +216,8 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod
return 'const_finder.dart.snapshot';
case Artifact.flutterToolsFileGenerators:
return '';
case Artifact.flutterPreviewDevice:
return 'flutter_preview$exe';
}
}
@ -573,6 +578,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.windowsCppClientWrapper:
case Artifact.windowsDesktopPath:
case Artifact.flutterToolsFileGenerators:
case Artifact.flutterPreviewDevice:
return _getHostArtifactPath(artifact, platform, mode);
}
}
@ -612,6 +618,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.windowsCppClientWrapper:
case Artifact.windowsDesktopPath:
case Artifact.flutterToolsFileGenerators:
case Artifact.flutterPreviewDevice:
return _getHostArtifactPath(artifact, platform, mode);
}
}
@ -663,6 +670,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.windowsCppClientWrapper:
case Artifact.windowsDesktopPath:
case Artifact.flutterToolsFileGenerators:
case Artifact.flutterPreviewDevice:
return _getHostArtifactPath(artifact, platform, mode);
}
}
@ -745,6 +753,9 @@ class CachedArtifacts implements Artifacts {
throw StateError('Artifact $artifact not available for platform $platform.');
case Artifact.flutterToolsFileGenerators:
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 {
return _fileSystem.path.join(_getDartSdkPath(), 'bin', 'utils', artifactFileName);
case Artifact.flutterToolsFileGenerators:
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 {
_platform = platform,
_operatingSystemUtils = operatingSystemUtils;
final Artifacts _parent;
final String _webSdkPath;
final FileSystem _fileSystem;
@ -1169,6 +1181,7 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
case Artifact.fontSubset:
case Artifact.constFinder:
case Artifact.flutterToolsFileGenerators:
case Artifact.flutterPreviewDevice:
break;
}
}

View File

@ -5,11 +5,14 @@
import 'package:meta/meta.dart';
import '../android/android_sdk.dart';
import '../artifacts.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/os.dart';
import '../base/process.dart';
import '../build_info.dart';
import '../build_system/build_system.dart';
import '../cache.dart';
import '../commands/build_linux.dart';
import '../commands/build_macos.dart';
import '../commands/build_windows.dart';
@ -21,15 +24,18 @@ import 'build_bundle.dart';
import 'build_ios.dart';
import 'build_ios_framework.dart';
import 'build_macos_framework.dart';
import 'build_preview.dart';
import 'build_web.dart';
class BuildCommand extends FlutterCommand {
BuildCommand({
required Artifacts artifacts,
required FileSystem fileSystem,
required BuildSystem buildSystem,
required OperatingSystemUtils osUtils,
required Logger logger,
required AndroidSdk? androidSdk,
required ProcessUtils processUtils,
bool verboseHelp = false,
}){
_addSubcommand(
@ -67,6 +73,14 @@ class BuildCommand extends FlutterCommand {
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) {
@ -90,14 +104,15 @@ class BuildCommand extends FlutterCommand {
abstract class BuildSubCommand extends FlutterCommand {
BuildSubCommand({
required Logger logger,
required this.logger,
required bool verboseHelp
}): _logger = logger {
}) {
requiresPubspecYaml();
usesFatalWarningsOption(verboseHelp: verboseHelp);
}
final Logger _logger;
@protected
final Logger logger;
@override
bool get reportNullSafety => true;
@ -111,15 +126,15 @@ abstract class BuildSubCommand extends FlutterCommand {
@protected
void displayNullSafetyMode(BuildInfo buildInfo) {
if (buildInfo.nullSafetyMode != NullSafetyMode.sound) {
_logger.printStatus('');
_logger.printStatus(
logger.printStatus('');
logger.printStatus(
'Building without sound null safety ⚠️',
emphasis: true,
);
_logger.printStatus(
logger.printStatus(
'Dart 3 will only support sound null safety, see https://dart.dev/null-safety',
);
}
_logger.printStatus('');
logger.printStatus('');
}
}

View File

@ -0,0 +1,117 @@
// 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);
}
}

View File

@ -53,6 +53,9 @@ abstract class FeatureFlags {
/// Whether native assets compilation and bundling is enabled.
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.
///
/// Prefer using one of the specific getters above instead of this API.
@ -72,6 +75,7 @@ const List<Feature> allFeatures = <Feature>[
flutterWebWasm,
cliAnimation,
nativeAssets,
previewDevice,
];
/// All current Flutter feature flags that can be configured.
@ -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.
///
/// All settings are optional, and if not provided will generally default to

View File

@ -27,6 +27,7 @@ import 'macos/macos_device.dart';
import 'macos/macos_ipad_device.dart';
import 'macos/macos_workflow.dart';
import 'macos/xcdevice.dart';
import 'preview_device.dart';
import 'tester/flutter_tester.dart';
import 'version.dart';
import 'web/web_device.dart';
@ -104,6 +105,14 @@ class FlutterDeviceManager extends DeviceManager {
fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils,
),
PreviewDeviceDiscovery(
platform: platform,
artifacts: artifacts,
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
featureFlags: featureFlags,
),
LinuxDevices(
platform: platform,
featureFlags: featureFlags,

View File

@ -58,6 +58,9 @@ class FlutterFeatureFlags implements FeatureFlags {
@override
bool get isNativeAssetsEnabled => isEnabled(nativeAssets);
@override
bool get isPreviewDeviceEnabled => isEnabled(previewDevice);
@override
bool isEnabled(Feature feature) {
final String currentChannel = _flutterVersion.channel;

View File

@ -8,17 +8,18 @@ import 'package:meta/meta.dart';
import 'package:process/process.dart';
import 'application_package.dart';
import 'artifacts.dart';
import 'base/file_system.dart';
import 'base/io.dart';
import 'base/logger.dart';
import 'base/platform.dart';
import 'build_info.dart';
import 'bundle_builder.dart';
import 'cache.dart';
import 'desktop_device.dart';
import 'devfs.dart';
import 'device.dart';
import 'device_port_forwarder.dart';
import 'features.dart';
import 'project.dart';
import 'protocol_discovery.dart';
@ -28,30 +29,99 @@ BundleBuilder _defaultBundleBuilder() {
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.
///
/// 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 {
PreviewDevice({
required Platform platform,
required ProcessManager processManager,
required Logger logger,
required FileSystem fileSystem,
required Artifacts artifacts,
required File previewBinary,
@visibleForTesting BundleBuilderFactory builderFactory = _defaultBundleBuilder,
}) : _platform = platform,
}) : _previewBinary = previewBinary,
_processManager = processManager,
_logger = logger,
_fileSystem = fileSystem,
_bundleBuilderFactory = builderFactory,
_artifacts = artifacts,
super('preview', ephemeral: false, category: Category.desktop, platformType: PlatformType.custom);
final Platform _platform;
final ProcessManager _processManager;
final Logger _logger;
final FileSystem _fileSystem;
final BundleBuilderFactory _bundleBuilderFactory;
final Artifacts _artifacts;
final File _previewBinary;
@override
void clearLogs() { }
@ -116,7 +186,7 @@ class PreviewDevice extends Device {
await _bundleBuilderFactory().build(
buildInfo: debuggingOptions.buildInfo,
mainPath: mainPath,
platform: TargetPlatform.tester,
platform: TargetPlatform.windows_x64,
assetDirPath: getAssetBuildDirectory(),
);
copyDirectory(_fileSystem.directory(
@ -128,13 +198,18 @@ class PreviewDevice extends Device {
}
// Merge with precompiled executable.
final Directory precompiledDirectory = _fileSystem.directory(_fileSystem.path.join(Cache.flutterRoot!, 'artifacts_temp', 'Debug'));
copyDirectory(precompiledDirectory, assetDirectory);
final String copiedPreviewBinaryPath = assetDirectory.childFile(_previewBinary.basename).path;
_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(
<String>[
assetDirectory.childFile('splash').path,
],
<String>[copiedPreviewBinaryPath],
);
_process = process;
_logReader.initializeProcess(process);
@ -169,10 +244,7 @@ class PreviewDevice extends Device {
@override
Future<TargetPlatform> get targetPlatform async {
if (_platform.isWindows) {
return TargetPlatform.windows_x64;
}
return TargetPlatform.tester;
return TargetPlatform.windows_x64;
}
@override

View File

@ -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
const TargetPlatform targetPlatform = TargetPlatform.windows_x64;
final Directory buildDirectory = globals.fs.directory(
getWindowsBuildDirectory(targetPlatform)
);
final Directory buildDirectory = globals.fs.directory(globals.fs.path.join(
projectPath,
getWindowsBuildDirectory(targetPlatform),
));
final List<ProjectMigrator> migrators = <ProjectMigrator>[
CmakeCustomCommandMigration(windowsProject, globals.logger),

View File

@ -5,10 +5,12 @@
import 'package:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/base/utils.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart';
@ -47,13 +49,23 @@ void main() {
});
late FileSystem fileSystem;
late ProcessManager processManager;
late FakeProcessManager processManager;
late ProcessUtils processUtils;
late Logger logger;
late TestUsage usage;
late Artifacts artifacts;
setUp(() {
fileSystem = MemoryFileSystem.test();
artifacts = Artifacts.test(fileSystem: fileSystem);
Cache.flutterRoot = _kTestFlutterRoot;
usage = TestUsage();
logger = BufferLogger.test();
processManager = FakeProcessManager.empty();
processUtils = ProcessUtils(
logger: logger,
processManager: processManager,
);
});
// Creates the mock files necessary to look like a Flutter project.
@ -110,10 +122,12 @@ void main() {
testUsingContext('Linux build fails when there is no linux project', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
setUpMockCoreProjectFiles();
@ -126,16 +140,18 @@ void main() {
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
});
testUsingContext('Linux build fails on non-linux platform', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
setUpMockProjectFilesForBuild();
@ -146,16 +162,18 @@ void main() {
}, overrides: <Type, Generator>{
Platform: () => notLinuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
});
testUsingContext('Linux build fails when feature is disabled', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
setUpMockProjectFilesForBuild();
@ -166,19 +184,21 @@ void main() {
}, overrides: <Type, Generator>{
Platform: () => linuxPlatform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
FeatureFlags: () => TestFeatureFlags(),
});
testUsingContext('Linux build invokes CMake and ninja, and writes temporary files', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
processManager = FakeProcessManager.list(<FakeCommand>[
processManager.addCommands(<FakeCommand>[
cmakeCommand('release'),
ninjaCommand('release'),
]);
@ -199,10 +219,12 @@ void main() {
testUsingContext('Handles missing cmake', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
setUpMockProjectFilesForBuild();
@ -222,14 +244,16 @@ void main() {
testUsingContext('Handles argument error from missing ninja', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
processManager.addCommands(<FakeCommand>[
cmakeCommand('release'),
ninjaCommand('release', onRun: () {
throw ArgumentError();
@ -249,14 +273,16 @@ void main() {
testUsingContext('Linux build does not spew stdout to status logger', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
processManager.addCommands(<FakeCommand>[
cmakeCommand('debug'),
ninjaCommand('debug',
stdout: 'STDOUT STUFF',
@ -280,10 +306,12 @@ void main() {
testUsingContext('Linux build extracts errors from stdout', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
setUpMockProjectFilesForBuild();
@ -309,7 +337,7 @@ ninja: build stopped: subcommand failed.
ERROR: No file or variants found for asset: images/a_dot_burr.jpeg
''';
processManager = FakeProcessManager.list(<FakeCommand>[
processManager.addCommands(<FakeCommand>[
cmakeCommand('release'),
ninjaCommand('release',
stdout: stdout,
@ -340,14 +368,16 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg
testUsingContext('Linux verbose build sets VERBOSE_SCRIPT_LOGGING', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
processManager.addCommands(<FakeCommand>[
cmakeCommand('debug'),
ninjaCommand('debug',
environment: const <String, String>{
@ -366,6 +396,7 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg
expect(testLogger.errorText, isNot(contains('STDOUT STUFF')));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
Logger: () => logger,
ProcessManager: () => processManager,
Platform: () => linuxPlatform,
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
@ -374,14 +405,16 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg
testUsingContext('Linux on x64 build --debug passes debug mode to cmake and ninja', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
processManager.addCommands(<FakeCommand>[
cmakeCommand('debug'),
ninjaCommand('debug'),
]);
@ -391,6 +424,7 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg
);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
Logger: () => logger,
ProcessManager: () => processManager,
Platform: () => linuxPlatform,
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
@ -399,14 +433,16 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg
testUsingContext('Linux on ARM64 build --debug passes debug mode to cmake and ninja', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64),
);
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
processManager.addCommands(<FakeCommand>[
cmakeCommand('debug', target: 'arm64'),
ninjaCommand('debug', target: 'arm64'),
]);
@ -423,14 +459,16 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg
testUsingContext('Linux on x64 build --profile passes profile mode to make', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
processManager.addCommands(<FakeCommand>[
cmakeCommand('profile'),
ninjaCommand('profile'),
]);
@ -448,14 +486,16 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg
testUsingContext('Linux on ARM64 build --profile passes profile mode to make', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64),
);
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
processManager.addCommands(<FakeCommand>[
cmakeCommand('profile', target: 'arm64'),
ninjaCommand('profile', target: 'arm64'),
]);
@ -472,10 +512,12 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg
testUsingContext('Not support Linux cross-build for x64 on arm64', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64),
);
@ -489,14 +531,16 @@ ERROR: No file or variants found for asset: images/a_dot_burr.jpeg
testUsingContext('Linux build configures CMake exports', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
processManager.addCommands(<FakeCommand>[
cmakeCommand('release'),
ninjaCommand('release'),
]);
@ -576,21 +620,25 @@ set(BINARY_NAME "fizz_bar")
expect(getCmakeExecutableName(flutterProject.linux), 'fizz_bar');
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
});
testUsingContext('Refuses to build for Linux when feature is disabled', () {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
));
expect(() => runner.run(<String>['build', 'linux', '--no-pub']),
throwsToolExit());
expect(
() => runner.run(<String>['build', 'linux', '--no-pub']),
throwsToolExit(),
);
}, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(),
});
@ -611,14 +659,16 @@ set(BINARY_NAME "fizz_bar")
testUsingContext('Performs code size analysis and sends analytics', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
processManager.addCommands(<FakeCommand>[
cmakeCommand('release'),
ninjaCommand('release', onRun: () {
fileSystem.file('build/flutter_size_01/snapshot.linux-x64.json')
@ -662,14 +712,16 @@ set(BINARY_NAME "fizz_bar")
testUsingContext('Linux on ARM64 build --release passes, and check if the LinuxBuildDirectory for arm64 can be referenced correctly by using analytics', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: CustomFakeOperatingSystemUtils(hostPlatform: HostPlatform.linux_arm64),
);
setUpMockProjectFilesForBuild();
processManager = FakeProcessManager.list(<FakeCommand>[
processManager.addCommands(<FakeCommand>[
cmakeCommand('release', target: 'arm64'),
ninjaCommand('release', target: 'arm64', onRun: () {
fileSystem.file('build/flutter_size_01/snapshot.linux-arm64.json')

View File

@ -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/logger.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_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart';
@ -65,7 +66,10 @@ void main() {
late FileSystem fileSystem;
late TestUsage usage;
late FakeProcessManager fakeProcessManager;
late ProcessUtils processUtils;
late BufferLogger logger;
late XcodeProjectInterpreter xcodeProjectInterpreter;
late Artifacts artifacts;
setUpAll(() {
Cache.disableLocking();
@ -73,8 +77,14 @@ void main() {
setUp(() {
fileSystem = MemoryFileSystem.test();
artifacts = Artifacts.test(fileSystem: fileSystem);
logger = BufferLogger.test();
usage = TestUsage();
fakeProcessManager = FakeProcessManager.empty();
processUtils = ProcessUtils(
logger: logger,
processManager: fakeProcessManager,
);
xcodeProjectInterpreter = FakeXcodeProjectInterpreter();
});
@ -139,10 +149,12 @@ STDERR STUFF
testUsingContext('macOS build fails when there is no macos project', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
createCoreMockProjectFiles();
@ -161,10 +173,12 @@ STDERR STUFF
testUsingContext('macOS build successfully with renamed .xcodeproj/.xcworkspace files', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
@ -184,10 +198,12 @@ STDERR STUFF
testUsingContext('macOS build fails on non-macOS platform', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
fileSystem.file('pubspec.yaml').createSync();
@ -206,10 +222,12 @@ STDERR STUFF
testUsingContext('macOS build fails when feature is disabled', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
fileSystem.file('pubspec.yaml').createSync();
@ -228,10 +246,12 @@ STDERR STUFF
testUsingContext('macOS build forwards error stdout to status logger error', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
createMinimalMockProjectFiles();
@ -258,10 +278,12 @@ STDERR STUFF
testUsingContext('macOS build invokes xcode build (debug)', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
createMinimalMockProjectFiles();
@ -280,10 +302,12 @@ STDERR STUFF
testUsingContext('macOS build invokes xcode build (debug) with verbosity', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
createMinimalMockProjectFiles();
@ -303,10 +327,12 @@ STDERR STUFF
testUsingContext('macOS build invokes xcode build (profile)', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
createMinimalMockProjectFiles();
@ -326,10 +352,12 @@ STDERR STUFF
testUsingContext('macOS build invokes xcode build (release)', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
createMinimalMockProjectFiles();
@ -348,10 +376,12 @@ STDERR STUFF
testUsingContext('macOS build supports standard desktop build options', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
createMinimalMockProjectFiles();
@ -439,10 +469,12 @@ STDERR STUFF
]);
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: fileSystem,
logger: BufferLogger.test(),
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
@ -461,10 +493,12 @@ STDERR STUFF
testUsingContext('macOS build supports build-name and build-number', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
createMinimalMockProjectFiles();
@ -496,10 +530,12 @@ STDERR STUFF
testUsingContext('Refuses to build for macOS when feature is disabled', () {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
));
@ -526,10 +562,12 @@ STDERR STUFF
testUsingContext('Performs code size analysis and sends analytics', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
createMinimalMockProjectFiles();

View File

@ -4,8 +4,10 @@
import 'package:args/command_runner.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/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/cache.dart';
import 'package:flutter_tools/src/commands/build.dart';
@ -28,23 +30,37 @@ void main() {
]), throwsToolExit(message: '"--${FlutterOptions.kDartObfuscationOption}" can only be used in '
'combination with "--${FlutterOptions.kSplitDebugInfoOption}"'));
});
group('Fatal Logs', () {
late FakeBuildCommand command;
late MemoryFileSystem fs;
late BufferLogger logger;
late ProcessManager processManager;
late ProcessUtils processUtils;
late Artifacts artifacts;
setUp(() {
fs = MemoryFileSystem.test();
artifacts = Artifacts.test(fileSystem: fs);
fs.file('/package/pubspec.yaml').createSync(recursive: true);
fs.currentDirectory = '/package';
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 {
command = FakeBuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fs,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
try {
@ -58,15 +74,17 @@ void main() {
}
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
});
testUsingContext("doesn't fail if --fatal-warnings not specified", () async {
command = FakeBuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fs,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
testLogger.printWarning('Warning: Mild annoyance Will Robinson!');
@ -80,15 +98,17 @@ void main() {
}
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
});
testUsingContext('fails if --fatal-warnings specified and warnings emitted', () async {
command = FakeBuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fs,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
testLogger.printWarning('Warning: Mild annoyance Will Robinson!');
@ -99,15 +119,17 @@ void main() {
]), throwsToolExit(message: 'Logger received warning output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
});
testUsingContext('fails if --fatal-warnings specified and errors emitted', () async {
command = FakeBuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fs,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
testLogger.printError('Error: Danger Will Robinson!');
@ -118,7 +140,7 @@ void main() {
]), throwsToolExit(message: 'Logger received error output during the run, and "--${FlutterOptions.kFatalWarnings}" is enabled.'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
});
});
}
@ -149,8 +171,10 @@ class FakeBuildCommand extends BuildCommand {
required super.osUtils,
required Logger logger,
required super.androidSdk,
required super.processUtils,
required super.artifacts,
bool verboseHelp = false,
}) : super(logger: logger, verboseHelp: verboseHelp,) {
}) : super(logger: logger) {
addSubcommand(FakeBuildSubcommand(logger: logger, verboseHelp: verboseHelp));
}

View File

@ -4,9 +4,11 @@
import 'package:args/command_runner.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/logger.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_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart';
@ -28,6 +30,10 @@ void main() {
'FLUTTER_ROOT': '/',
},
);
late ProcessUtils processUtils;
late BufferLogger logger;
late ProcessManager processManager;
late Artifacts artifacts;
setUpAll(() {
Cache.flutterRoot = '';
@ -42,15 +48,24 @@ void main() {
fileSystem.file('.packages').createSync();
fileSystem.file(fileSystem.path.join('web', 'index.html')).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 {
fileSystem.file(fileSystem.path.join('web', 'index.html')).deleteSync();
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
));
@ -62,15 +77,17 @@ void main() {
Platform: () => fakePlatform,
FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
});
testUsingContext('Refuses to build a debug build for web', () async {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: fileSystem,
logger: BufferLogger.test(),
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
));
@ -79,15 +96,17 @@ void main() {
}, overrides: <Type, Generator>{
Platform: () => fakePlatform,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
});
testUsingContext('Refuses to build for web when feature is disabled', () async {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
));
@ -99,15 +118,17 @@ void main() {
Platform: () => fakePlatform,
FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(),
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
});
testUsingContext('Setup for a web build with default output directory', () async {
final BuildCommand buildCommand = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: fileSystem,
logger: BufferLogger.test(),
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
final CommandRunner<void> runner = createTestCommandRunner(buildCommand);
@ -121,7 +142,7 @@ void main() {
Platform: () => fakePlatform,
FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true), (Target target, Environment environment) {
expect(environment.defines, <String, String>{
'TargetFile': 'lib/main.dart',
@ -144,11 +165,13 @@ void main() {
testUsingContext('Does not allow -O0 optimization level', () async {
final BuildCommand buildCommand = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: fileSystem,
logger: BufferLogger.test(),
osUtils: FakeOperatingSystemUtils(),
processUtils: processUtils,
);
final CommandRunner<void> runner = createTestCommandRunner(buildCommand);
setupFileSystemForEndToEndTest(fileSystem);
@ -191,10 +214,12 @@ void main() {
testUsingContext('Setup for a web build with a user specified output directory',
() async {
final BuildCommand buildCommand = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: fileSystem,
logger: BufferLogger.test(),
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
final CommandRunner<void> runner = createTestCommandRunner(buildCommand);
@ -219,7 +244,7 @@ void main() {
Platform: () => fakePlatform,
FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true), (Target target, Environment environment) {
expect(environment.defines, <String, String>{
'TargetFile': 'lib/main.dart',
@ -246,7 +271,7 @@ void main() {
Platform: () => fakePlatform,
FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(),
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
});
testUsingContext('not hidden if feature flag is enabled', () async {
@ -255,7 +280,7 @@ void main() {
Platform: () => fakePlatform,
FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
});
testUsingContext('Defaults to web renderer auto mode when no option is specified', () async {
@ -270,7 +295,7 @@ void main() {
Platform: () => fakePlatform,
FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
});
@ -295,7 +320,7 @@ void main() {
Platform: () => fakePlatform,
FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
});
@ -311,7 +336,7 @@ void main() {
Platform: () => fakePlatform,
FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
});
@ -327,7 +352,7 @@ void main() {
Platform: () => fakePlatform,
FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
});
}

View File

@ -82,7 +82,7 @@ void main() {
'-S',
fileSystem.path.absolute(fileSystem.path.dirname(buildFilePath)),
'-B',
r'build\windows\x64',
r'C:\build\windows\x64',
'-G',
generator,
'-A',
@ -103,7 +103,7 @@ void main() {
command: <String>[
_cmakePath,
'--build',
r'build\windows\x64',
r'C:\build\windows\x64',
'--config',
buildMode,
...<String>['--target', 'INSTALL'],

View File

@ -0,0 +1,64 @@
// 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
}

View File

@ -6,11 +6,13 @@ import 'package:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_studio.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/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.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/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart';
@ -42,6 +44,7 @@ void main() {
late Directory tempDir;
late Config testConfig;
late FileSystem fs;
const String flutterRoot = '/path/to/flutter';
setUp(() {
@ -161,6 +164,9 @@ void main() {
late FakeClock fakeClock;
late FakeDoctor doctor;
late FakeAndroidStudio androidStudio;
late ProcessManager processManager;
late BufferLogger logger;
late ProcessUtils processUtils;
setUp(() {
memoryFileSystem = MemoryFileSystem.test();
@ -169,6 +175,9 @@ void main() {
fakeClock = FakeClock();
doctor = FakeDoctor();
androidStudio = FakeAndroidStudio();
processManager = FakeProcessManager.empty();
logger = BufferLogger.test();
processUtils = ProcessUtils(logger: logger, processManager: processManager);
});
testUsingContext('flutter commands send timing events', () async {
@ -220,17 +229,17 @@ void main() {
testUsingContext('compound command usage path', () async {
final BuildCommand buildCommand = BuildCommand(
artifacts: Artifacts.test(fileSystem: memoryFileSystem),
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: memoryFileSystem,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
final FlutterCommand buildApkCommand = buildCommand.subcommands['apk']! as FlutterCommand;
expect(await buildApkCommand.usagePath, 'build/apk');
}, overrides: <Type, Generator>{
Usage: () => testUsage,
});
testUsingContext('command sends localtime', () async {
@ -253,7 +262,7 @@ void main() {
expect(log.contains(formatDateTime(dateTime)), isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
SystemClock: () => fakeClock,
Platform: () => FakePlatform(
environment: <String, String>{
@ -283,7 +292,7 @@ void main() {
expect(log.contains(formatDateTime(dateTime)), isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
ProcessManager: () => processManager,
SystemClock: () => fakeClock,
Platform: () => FakePlatform(
environment: <String, String>{

View File

@ -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/os.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/signals.dart';
import 'package:process/process.dart';
import '../../src/common.dart';
import '../../src/context.dart';
void main() {
group('OperatingSystemUtils', () {
@ -17,7 +17,7 @@ void main() {
late FileSystem fileSystem;
setUp(() {
fileSystem = LocalFileSystem.test(signals: Signals.test());
fileSystem = LocalFileSystem.test(signals: FakeSignals());
tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_tools_os_utils_test.');
});

View File

@ -5,10 +5,12 @@
import 'package:args/args.dart';
import 'package:args/command_runner.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/io.dart';
import 'package:flutter_tools/src/base/logger.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/terminal.dart';
import 'package:flutter_tools/src/build_info.dart';
@ -111,11 +113,19 @@ void main() {
});
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(
artifacts: Artifacts.test(fileSystem: fs),
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
fileSystem: fs,
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
for (final Command<void> x in command.subcommands.values) {

View File

@ -6,6 +6,7 @@ import 'dart:async';
import 'package:file/memory.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/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
@ -22,20 +23,38 @@ import 'package:test/fake.dart';
import '../src/common.dart';
import '../src/context.dart';
import '../src/fakes.dart';
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 {
final PreviewDevice device = PreviewDevice(
fileSystem: MemoryFileSystem.test(),
artifacts: Artifacts.test(),
fileSystem: fs,
processManager: FakeProcessManager.any(),
previewBinary: previewBinary,
logger: BufferLogger.test(),
platform: FakePlatform(),
);
expect(await device.isLocalEmulator, false);
expect(device.name, '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.ephemeral, false);
expect(device.id, 'preview');
@ -48,29 +67,29 @@ void main() {
});
testUsingContext('Can build a simulator app', () async {
Cache.flutterRoot = '';
final Completer<void> completer = Completer<void>();
final FileSystem fileSystem = MemoryFileSystem.test();
final BufferLogger logger = BufferLogger.test();
final PreviewDevice device = PreviewDevice(
fileSystem: fileSystem,
artifacts: Artifacts.test(),
fileSystem: fs,
previewBinary: previewBinary,
processManager: FakeProcessManager.list(<FakeCommand>[
FakeCommand(
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',
completer: completer,
),
]),
logger: logger,
platform: FakePlatform(),
builderFactory: () => FakeBundleBuilder(fileSystem),
builderFactory: () => FakeBundleBuilder(fs),
);
fileSystem
.directory('artifacts_temp')
.childDirectory('Debug')
.createSync(recursive: true);
final Directory previewDeviceCacheDir = fs
.directory('Artifact.windowsDesktopPath.TargetPlatform.windows_x64.debug')
..createSync(recursive: true);
previewDeviceCacheDir.childFile('flutter_windows.dll').writeAsStringSync('1010101');
previewDeviceCacheDir.childFile('icudtl.dat').writeAsStringSync('1010101');
final LaunchResult result = await device.startApp(
FakeApplicationPackage(),
@ -80,6 +99,84 @@ void main() {
expect(result.started, true);
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 { }

View File

@ -3,8 +3,10 @@
// found in the LICENSE file.
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/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/cache.dart';
import 'package:flutter_tools/src/commands/build.dart';
@ -26,14 +28,24 @@ void main() {
late File registrant;
// Environment overrides
late Artifacts artifacts;
late FileSystem fileSystem;
late ProcessManager processManager;
late BuildSystem buildSystem;
late ProcessUtils processUtils;
late BufferLogger logger;
setUp(() {
// Prepare environment overrides
fileSystem = MemoryFileSystem.test();
artifacts = Artifacts.test(fileSystem: fileSystem);
processManager = FakeProcessManager.any();
logger = BufferLogger.test();
processUtils = ProcessUtils(
processManager: processManager,
logger: logger,
);
buildSystem = TestBuildSystem.all(BuildResult(success: true));
// Write some initial state into our testing filesystem
setupFileSystemForEndToEndTest(fileSystem);
@ -47,11 +59,13 @@ void main() {
expect(registrant.existsSync(), isFalse);
await createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: buildSystem,
fileSystem: fileSystem,
logger: BufferLogger.test(),
osUtils: FakeOperatingSystemUtils(),
processUtils: processUtils,
))
.run(<String>['build', 'web', '--no-pub']);
@ -70,11 +84,13 @@ void main() {
expect(contentsBeforeBuild, isNot(contains('lib/generated_plugin_registrant.dart')));
await createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: buildSystem,
fileSystem: fileSystem,
logger: BufferLogger.test(),
logger: logger,
osUtils: FakeOperatingSystemUtils(),
processUtils: processUtils,
))
.run(<String>['build', 'web', '--no-pub']);
@ -92,10 +108,12 @@ void main() {
expect(gitignore.readAsStringSync(), contains('lib/generated_plugin_registrant.dart'));
await createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: buildSystem,
fileSystem: fileSystem,
logger: BufferLogger.test(),
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
))
.run(<String>['build', 'web', '--no-pub']);
@ -113,10 +131,12 @@ void main() {
expect(registrant.existsSync(), isTrue);
await createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: buildSystem,
fileSystem: fileSystem,
logger: BufferLogger.test(),
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
))
.run(<String>['build', 'web', '--no-pub']);
@ -136,10 +156,12 @@ void main() {
expect(gitignore.readAsStringSync(), contains('lib/generated_plugin_registrant.dart'));
await createTestCommandRunner(BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: buildSystem,
fileSystem: fileSystem,
logger: BufferLogger.test(),
logger: logger,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
))
.run(<String>['build', 'web', '--no-pub']);

View File

@ -382,7 +382,8 @@ class NoopCrashReporter implements CrashReporter {
}
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());
@override

View File

@ -462,6 +462,7 @@ class TestFeatureFlags implements FeatureFlags {
this.isFlutterWebWasmEnabled = false,
this.isCliAnimationEnabled = true,
this.isNativeAssetsEnabled = false,
this.isPreviewDeviceEnabled = false,
});
@override
@ -497,31 +498,24 @@ class TestFeatureFlags implements FeatureFlags {
@override
final bool isNativeAssetsEnabled;
@override
final bool isPreviewDeviceEnabled;
@override
bool isEnabled(Feature feature) {
switch (feature) {
case flutterWebFeature:
return isWebEnabled;
case flutterLinuxDesktopFeature:
return isLinuxEnabled;
case flutterMacOSDesktopFeature:
return isMacOSEnabled;
case flutterWindowsDesktopFeature:
return isWindowsEnabled;
case flutterAndroidFeature:
return isAndroidEnabled;
case flutterIOSFeature:
return isIOSEnabled;
case flutterFuchsiaFeature:
return isFuchsiaEnabled;
case flutterCustomDevicesFeature:
return areCustomDevicesEnabled;
case cliAnimation:
return isCliAnimationEnabled;
case nativeAssets:
return isNativeAssetsEnabled;
}
return false;
return switch (feature) {
flutterWebFeature => isWebEnabled,
flutterLinuxDesktopFeature => isLinuxEnabled,
flutterMacOSDesktopFeature => isMacOSEnabled,
flutterWindowsDesktopFeature => isWindowsEnabled,
flutterAndroidFeature => isAndroidEnabled,
flutterIOSFeature => isIOSEnabled,
flutterFuchsiaFeature => isFuchsiaEnabled,
flutterCustomDevicesFeature => areCustomDevicesEnabled,
cliAnimation => isCliAnimationEnabled,
nativeAssets => isNativeAssetsEnabled,
_ => false,
};
}
}