From 3fedb8cb5ada929a697f29fb1e5783f9cfa76a15 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 22 Jul 2019 15:34:03 -0700 Subject: [PATCH] Reland: use flutter features for web and desktop (#36699) --- dev/bots/test.dart | 3 + .../lib/src/commands/assemble.dart | 14 -- .../lib/src/commands/build_bundle.dart | 16 +- .../lib/src/commands/build_fuchsia.dart | 3 - .../lib/src/commands/build_linux.dart | 7 +- .../lib/src/commands/build_macos.dart | 9 +- .../lib/src/commands/build_web.dart | 10 +- .../lib/src/commands/build_windows.dart | 9 +- .../lib/src/commands/create.dart | 3 +- .../flutter_tools/lib/src/commands/run.dart | 11 +- .../lib/src/commands/unpack.dart | 3 - packages/flutter_tools/lib/src/desktop.dart | 22 -- packages/flutter_tools/lib/src/device.dart | 25 +- packages/flutter_tools/lib/src/doctor.dart | 11 +- .../lib/src/linux/linux_workflow.dart | 8 +- .../lib/src/macos/macos_workflow.dart | 8 +- packages/flutter_tools/lib/src/plugins.dart | 4 +- packages/flutter_tools/lib/src/project.dart | 7 +- .../lib/src/runner/flutter_command.dart | 19 +- .../flutter_tools/lib/src/web/web_device.dart | 7 +- .../flutter_tools/lib/src/web/workflow.dart | 28 +-- .../lib/src/windows/windows_workflow.dart | 8 +- .../commands/build_bundle_test.dart | 225 +++++++++++++----- .../commands/build_linux_test.dart | 20 +- .../commands/build_macos_test.dart | 15 ++ .../commands/build_web_test.dart | 14 ++ .../commands/build_windows_test.dart | 6 + .../general.shard/commands/config_test.dart | 5 - .../general.shard/commands/devices_test.dart | 3 - .../commands/update_packages_test.dart | 18 -- .../test/general.shard/doctor.dart | 62 +++++ .../linux/linux_workflow_test.dart | 65 +++-- .../macos/macos_workflow_test.dart | 64 ++--- .../runner/flutter_command_test.dart | 24 -- .../test/general.shard/web/workflow_test.dart | 21 +- .../windows/windows_workflow_test.dart | 65 +++-- packages/flutter_tools/test/src/common.dart | 15 -- packages/flutter_tools/test/src/testbed.dart | 24 ++ 38 files changed, 505 insertions(+), 376 deletions(-) delete mode 100644 packages/flutter_tools/test/general.shard/commands/update_packages_test.dart create mode 100644 packages/flutter_tools/test/general.shard/doctor.dart diff --git a/dev/bots/test.dart b/dev/bots/test.dart index f1036743ed..48be0bb022 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -289,6 +289,9 @@ Future _flutterBuildDart2js(String relativePathToApplication) async { workingDirectory: path.join(flutterRoot, relativePathToApplication), expectNonZeroExit: false, timeout: _kShortTimeout, + environment: { + 'FLUTTER_WEB': 'true', + } ); print('Done.'); } diff --git a/packages/flutter_tools/lib/src/commands/assemble.dart b/packages/flutter_tools/lib/src/commands/assemble.dart index e5a6659310..9919e658cd 100644 --- a/packages/flutter_tools/lib/src/commands/assemble.dart +++ b/packages/flutter_tools/lib/src/commands/assemble.dart @@ -30,8 +30,6 @@ class AssembleCommand extends FlutterCommand { @override String get name => 'assemble'; - @override - bool get isExperimental => true; @override Future runCommand() { @@ -126,9 +124,6 @@ class AssembleRun extends AssembleBase { @override String get name => 'run'; - @override - bool get isExperimental => true; - @override Future runCommand() async { final BuildResult result = await buildSystem.build(targetName, environment, BuildSystemConfig( @@ -155,9 +150,6 @@ class AssembleDescribe extends AssembleBase { @override String get name => 'describe'; - @override - bool get isExperimental => true; - @override Future runCommand() { try { @@ -180,9 +172,6 @@ class AssembleListInputs extends AssembleBase { @override String get name => 'inputs'; - @override - bool get isExperimental => true; - @override Future runCommand() { try { @@ -209,9 +198,6 @@ class AssembleBuildDirectory extends AssembleBase { @override String get name => 'build-dir'; - @override - bool get isExperimental => true; - @override Future runCommand() { printStatus(environment.buildDir.path); diff --git a/packages/flutter_tools/lib/src/commands/build_bundle.dart b/packages/flutter_tools/lib/src/commands/build_bundle.dart index ef2235f5d0..d15cfa49a0 100644 --- a/packages/flutter_tools/lib/src/commands/build_bundle.dart +++ b/packages/flutter_tools/lib/src/commands/build_bundle.dart @@ -8,10 +8,10 @@ import '../base/common.dart'; import '../base/file_system.dart'; import '../build_info.dart'; import '../bundle.dart'; +import '../features.dart'; import '../project.dart'; import '../reporting/usage.dart'; import '../runner/flutter_command.dart' show FlutterOptions, FlutterCommandResult; -import '../version.dart'; import 'build.dart'; class BuildBundleCommand extends BuildSubCommand { @@ -98,13 +98,21 @@ class BuildBundleCommand extends BuildSubCommand { if (platform == null) { throwToolExit('Unknown platform: $targetPlatform'); } - // Check for target platforms that are only allowed on unstable Flutter. + // Check for target platforms that are only allowed via feature flags. switch (platform) { case TargetPlatform.darwin_x64: + if (!featureFlags.isMacOSEnabled) { + throwToolExit('macOS is not a supported target platform.'); + } + break; case TargetPlatform.windows_x64: + if (!featureFlags.isWindowsEnabled) { + throwToolExit('Windows is not a supported target platform.'); + } + break; case TargetPlatform.linux_x64: - if (!FlutterVersion.instance.isMaster) { - throwToolExit('$targetPlatform is not supported on stable Flutter.'); + if (!featureFlags.isLinuxEnabled) { + throwToolExit('Linux is not a supported target platform.'); } break; default: diff --git a/packages/flutter_tools/lib/src/commands/build_fuchsia.dart b/packages/flutter_tools/lib/src/commands/build_fuchsia.dart index 473f943de7..39e0a8425d 100644 --- a/packages/flutter_tools/lib/src/commands/build_fuchsia.dart +++ b/packages/flutter_tools/lib/src/commands/build_fuchsia.dart @@ -24,9 +24,6 @@ class BuildFuchsiaCommand extends BuildSubCommand { @override final String name = 'fuchsia'; - @override - bool isExperimental = true; - @override bool hidden = true; diff --git a/packages/flutter_tools/lib/src/commands/build_linux.dart b/packages/flutter_tools/lib/src/commands/build_linux.dart index 4175b6db82..e75e41186c 100644 --- a/packages/flutter_tools/lib/src/commands/build_linux.dart +++ b/packages/flutter_tools/lib/src/commands/build_linux.dart @@ -8,6 +8,7 @@ import '../base/common.dart'; import '../base/platform.dart'; import '../build_info.dart'; import '../cache.dart'; +import '../features.dart'; import '../linux/build_linux.dart'; import '../project.dart'; import '../runner/flutter_command.dart' show FlutterCommandResult; @@ -34,9 +35,6 @@ class BuildLinuxCommand extends BuildSubCommand { @override final String name = 'linux'; - @override - bool isExperimental = true; - @override bool hidden = true; @@ -54,6 +52,9 @@ class BuildLinuxCommand extends BuildSubCommand { Cache.releaseLockEarly(); final BuildInfo buildInfo = getBuildInfo(); final FlutterProject flutterProject = FlutterProject.current(); + if (!featureFlags.isLinuxEnabled) { + throwToolExit('"build linux" is not currently supported.'); + } if (!platform.isLinux) { throwToolExit('"build linux" only supported on Linux hosts.'); } diff --git a/packages/flutter_tools/lib/src/commands/build_macos.dart b/packages/flutter_tools/lib/src/commands/build_macos.dart index e773f42ca9..f87118651f 100644 --- a/packages/flutter_tools/lib/src/commands/build_macos.dart +++ b/packages/flutter_tools/lib/src/commands/build_macos.dart @@ -8,6 +8,7 @@ import '../base/common.dart'; import '../base/platform.dart'; import '../build_info.dart'; import '../cache.dart'; +import '../features.dart'; import '../macos/build_macos.dart'; import '../project.dart'; import '../runner/flutter_command.dart' show FlutterCommandResult; @@ -34,9 +35,6 @@ class BuildMacosCommand extends BuildSubCommand { @override final String name = 'macos'; - @override - bool isExperimental = true; - @override bool hidden = true; @@ -47,13 +45,16 @@ class BuildMacosCommand extends BuildSubCommand { }; @override - String get description => 'build the macOS desktop target (Experimental).'; + String get description => 'build the macOS desktop target.'; @override Future runCommand() async { Cache.releaseLockEarly(); final BuildInfo buildInfo = getBuildInfo(); final FlutterProject flutterProject = FlutterProject.current(); + if (!featureFlags.isMacOSEnabled) { + throwToolExit('"build macos" is not currently supported.'); + } if (!platform.isMacOS) { throwToolExit('"build macos" only supported on macOS hosts.'); } diff --git a/packages/flutter_tools/lib/src/commands/build_web.dart b/packages/flutter_tools/lib/src/commands/build_web.dart index 32cea06fdf..575d1b06d6 100644 --- a/packages/flutter_tools/lib/src/commands/build_web.dart +++ b/packages/flutter_tools/lib/src/commands/build_web.dart @@ -4,7 +4,9 @@ import 'dart:async'; +import '../base/common.dart'; import '../build_info.dart'; +import '../features.dart'; import '../project.dart'; import '../runner/flutter_command.dart' show DevelopmentArtifact, FlutterCommandResult; @@ -32,13 +34,13 @@ class BuildWebCommand extends BuildSubCommand { bool get hidden => true; @override - bool get isExperimental => true; - - @override - final String description = '(EXPERIMENTAL) build a web application bundle.'; + final String description = 'build a web application bundle.'; @override Future runCommand() async { + if (!featureFlags.isWebEnabled) { + throwToolExit('"build web" is not currently supported.'); + } final FlutterProject flutterProject = FlutterProject.current(); final String target = argResults['target']; final BuildInfo buildInfo = getBuildInfo(); diff --git a/packages/flutter_tools/lib/src/commands/build_windows.dart b/packages/flutter_tools/lib/src/commands/build_windows.dart index 884d26f864..e41de03e8a 100644 --- a/packages/flutter_tools/lib/src/commands/build_windows.dart +++ b/packages/flutter_tools/lib/src/commands/build_windows.dart @@ -8,6 +8,7 @@ import '../base/common.dart'; import '../base/platform.dart'; import '../build_info.dart'; import '../cache.dart'; +import '../features.dart'; import '../project.dart'; import '../runner/flutter_command.dart' show FlutterCommandResult; import '../windows/build_windows.dart'; @@ -34,9 +35,6 @@ class BuildWindowsCommand extends BuildSubCommand { @override final String name = 'windows'; - @override - bool isExperimental = true; - @override bool hidden = true; @@ -47,13 +45,16 @@ class BuildWindowsCommand extends BuildSubCommand { }; @override - String get description => 'build the desktop Windows target (Experimental).'; + String get description => 'build the desktop Windows target.'; @override Future runCommand() async { Cache.releaseLockEarly(); final FlutterProject flutterProject = FlutterProject.current(); final BuildInfo buildInfo = getBuildInfo(); + if (!featureFlags.isWindowsEnabled) { + throwToolExit('"build windows" is not currently supported.'); + } if (!platform.isWindows) { throwToolExit('"build windows" only supported on Windows hosts.'); } diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart index 17458ee605..875beaeabf 100644 --- a/packages/flutter_tools/lib/src/commands/create.dart +++ b/packages/flutter_tools/lib/src/commands/create.dart @@ -20,6 +20,7 @@ import '../cache.dart'; import '../convert.dart'; import '../dart/pub.dart'; import '../doctor.dart'; +import '../features.dart'; import '../globals.dart'; import '../project.dart'; import '../reporting/usage.dart'; @@ -613,7 +614,7 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi 'iosLanguage': iosLanguage, 'flutterRevision': FlutterVersion.instance.frameworkRevision, 'flutterChannel': FlutterVersion.instance.channel, - 'web': web && FlutterVersion.instance.isMaster + 'web': web && featureFlags.isWebEnabled, }; } diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 91de004a58..0254e3ead0 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -13,6 +13,7 @@ import '../base/utils.dart'; import '../build_info.dart'; import '../cache.dart'; import '../device.dart'; +import '../features.dart'; import '../globals.dart'; import '../macos/xcode.dart'; import '../project.dart'; @@ -410,11 +411,11 @@ class RunCommand extends RunCommandBase { ); flutterDevices.add(flutterDevice); } - // Only support "web mode" on non-stable branches with a single web device - // in a "hot mode". - final bool webMode = FlutterVersion.instance.isMaster - && devices.length == 1 - && await devices.single.targetPlatform == TargetPlatform.web_javascript; + // Only support "web mode" with a single web device due to resident runner + // refactoring required otherwise. + final bool webMode = featureFlags.isWebEnabled && + devices.length == 1 && + await devices.single.targetPlatform == TargetPlatform.web_javascript; ResidentRunner runner; final String applicationBinaryPath = argResults['use-application-binary']; diff --git a/packages/flutter_tools/lib/src/commands/unpack.dart b/packages/flutter_tools/lib/src/commands/unpack.dart index 799815e5dc..481c326483 100644 --- a/packages/flutter_tools/lib/src/commands/unpack.dart +++ b/packages/flutter_tools/lib/src/commands/unpack.dart @@ -69,9 +69,6 @@ class UnpackCommand extends FlutterCommand { @override bool get hidden => true; - @override - bool get isExperimental => true; - @override Future> get requiredArtifacts async { final Set result = { diff --git a/packages/flutter_tools/lib/src/desktop.dart b/packages/flutter_tools/lib/src/desktop.dart index 501e2417f2..e62c4e779c 100644 --- a/packages/flutter_tools/lib/src/desktop.dart +++ b/packages/flutter_tools/lib/src/desktop.dart @@ -4,32 +4,10 @@ import 'dart:async'; -import 'package:meta/meta.dart'; - -import 'base/common.dart'; import 'base/io.dart'; -import 'base/platform.dart'; import 'base/process_manager.dart'; import 'convert.dart'; import 'device.dart'; -import 'version.dart'; - -@visibleForTesting -bool debugDisableDesktop = false; - -/// Only launch or display desktop embedding devices from the command line -/// or if `ENABLE_FLUTTER_DESKTOP` environment variable is set to true. -bool get flutterDesktopEnabled { - if (debugDisableDesktop) { - return false; - } - if (isRunningFromDaemon) { - final bool platformEnabled = platform - .environment['ENABLE_FLUTTER_DESKTOP']?.toLowerCase() == 'true'; - return platformEnabled && FlutterVersion.instance.isMaster; - } - return FlutterVersion.instance.isMaster; -} /// Kills a process on linux or macOS. Future killProcess(String executable) async { diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 936bf15c7b..734172f7fd 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -14,9 +14,7 @@ import 'base/context.dart'; import 'base/file_system.dart'; import 'base/utils.dart'; import 'build_info.dart'; -import 'desktop.dart'; import 'fuchsia/fuchsia_device.dart'; - import 'globals.dart'; import 'ios/devices.dart'; import 'ios/simulators.dart'; @@ -25,7 +23,6 @@ import 'macos/macos_device.dart'; import 'project.dart'; import 'tester/flutter_tester.dart'; import 'web/web_device.dart'; -import 'web/workflow.dart'; import 'windows/windows_device.dart'; DeviceManager get deviceManager => context.get(); @@ -74,23 +71,11 @@ class DeviceManager { IOSSimulators(), FuchsiaDevices(), FlutterTesterDevices(), - ] + _conditionalDesktopDevices + _conditionalWebDevices); - - /// Only add desktop devices if the flag is enabled. - static List get _conditionalDesktopDevices { - return flutterDesktopEnabled ? [ - MacOSDevices(), - LinuxDevices(), - WindowsDevices(), - ] : []; - } - - /// Only add web devices if the flag is enabled. - static List get _conditionalWebDevices { - return flutterWebEnabled ? [ - WebDevices(), - ] : []; - } + MacOSDevices(), + LinuxDevices(), + WindowsDevices(), + WebDevices(), + ]); String _specifiedDeviceId; diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart index fa4081d951..cf697c7fe1 100644 --- a/packages/flutter_tools/lib/src/doctor.dart +++ b/packages/flutter_tools/lib/src/doctor.dart @@ -19,7 +19,6 @@ import 'base/user_messages.dart'; import 'base/utils.dart'; import 'base/version.dart'; import 'cache.dart'; -import 'desktop.dart'; import 'device.dart'; import 'fuchsia/fuchsia_workflow.dart'; import 'globals.dart'; @@ -74,12 +73,10 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider { GroupedValidator([xcodeValidator, cocoapodsValidator]), if (webWorkflow.appliesToHostPlatform) const WebValidator(), - // Add desktop doctors to workflow if the flag is enabled. - if (flutterDesktopEnabled) - ...[ - if (linuxWorkflow.appliesToHostPlatform) LinuxDoctorValidator(), - if (windowsWorkflow.appliesToHostPlatform) visualStudioValidator, - ], + if (linuxWorkflow.appliesToHostPlatform) + LinuxDoctorValidator(), + if (windowsWorkflow.appliesToHostPlatform) + visualStudioValidator, if (ideValidators.isNotEmpty) ...ideValidators else diff --git a/packages/flutter_tools/lib/src/linux/linux_workflow.dart b/packages/flutter_tools/lib/src/linux/linux_workflow.dart index 4959f72209..e51c26be7f 100644 --- a/packages/flutter_tools/lib/src/linux/linux_workflow.dart +++ b/packages/flutter_tools/lib/src/linux/linux_workflow.dart @@ -4,8 +4,8 @@ import '../base/context.dart'; import '../base/platform.dart'; -import '../desktop.dart'; import '../doctor.dart'; +import '../features.dart'; /// The [WindowsWorkflow] instance. LinuxWorkflow get linuxWorkflow => context.get(); @@ -18,13 +18,13 @@ class LinuxWorkflow implements Workflow { const LinuxWorkflow(); @override - bool get appliesToHostPlatform => platform.isLinux; + bool get appliesToHostPlatform => platform.isLinux && featureFlags.isLinuxEnabled; @override - bool get canLaunchDevices => platform.isLinux && flutterDesktopEnabled; + bool get canLaunchDevices => platform.isLinux && featureFlags.isLinuxEnabled; @override - bool get canListDevices => platform.isLinux && flutterDesktopEnabled; + bool get canListDevices => platform.isLinux && featureFlags.isLinuxEnabled; @override bool get canListEmulators => false; diff --git a/packages/flutter_tools/lib/src/macos/macos_workflow.dart b/packages/flutter_tools/lib/src/macos/macos_workflow.dart index e85e3d4054..1c0391fe7c 100644 --- a/packages/flutter_tools/lib/src/macos/macos_workflow.dart +++ b/packages/flutter_tools/lib/src/macos/macos_workflow.dart @@ -4,8 +4,8 @@ import '../base/context.dart'; import '../base/platform.dart'; -import '../desktop.dart'; import '../doctor.dart'; +import '../features.dart'; /// The [MacOSWorkflow] instance. MacOSWorkflow get macOSWorkflow => context.get(); @@ -18,13 +18,13 @@ class MacOSWorkflow implements Workflow { const MacOSWorkflow(); @override - bool get appliesToHostPlatform => platform.isMacOS; + bool get appliesToHostPlatform => platform.isMacOS && featureFlags.isMacOSEnabled; @override - bool get canLaunchDevices => platform.isMacOS && flutterDesktopEnabled; + bool get canLaunchDevices => platform.isMacOS && featureFlags.isMacOSEnabled; @override - bool get canListDevices => platform.isMacOS && flutterDesktopEnabled; + bool get canListDevices => platform.isMacOS && featureFlags.isMacOSEnabled; @override bool get canListEmulators => false; diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart index 0242a62b6e..021ec84dc3 100644 --- a/packages/flutter_tools/lib/src/plugins.dart +++ b/packages/flutter_tools/lib/src/plugins.dart @@ -9,7 +9,7 @@ import 'package:yaml/yaml.dart'; import 'base/file_system.dart'; import 'dart/package_map.dart'; -import 'desktop.dart'; +import 'features.dart'; import 'globals.dart'; import 'macos/cocoapods.dart'; import 'project.dart'; @@ -364,7 +364,7 @@ Future injectPlugins(FlutterProject project, {bool checkProjects = false}) // TODO(stuartmorgan): Revisit the condition here once the plans for handling // desktop in existing projects are in place. For now, ignore checkProjects // on desktop and always treat it as true. - if (flutterDesktopEnabled && project.macos.existsSync()) { + if (featureFlags.isMacOSEnabled && project.macos.existsSync()) { await _writeMacOSPluginRegistrant(project, plugins); } for (final XcodeBasedProject subproject in [project.ios, project.macos]) { diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index 0a1b5b848d..95b526ef90 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -14,7 +14,7 @@ import 'base/file_system.dart'; import 'build_info.dart'; import 'bundle.dart' as bundle; import 'cache.dart'; -import 'desktop.dart'; +import 'features.dart'; import 'flutter_manifest.dart'; import 'globals.dart'; import 'ios/ios_workflow.dart'; @@ -22,7 +22,6 @@ import 'ios/plist_utils.dart' as plist; import 'ios/xcodeproj.dart' as xcode; import 'plugins.dart'; import 'template.dart'; -import 'web/workflow.dart'; FlutterProjectFactory get projectFactory => context.get() ?? const FlutterProjectFactory(); @@ -206,10 +205,10 @@ class FlutterProject { } // TODO(stuartmorgan): Add checkProjects logic once a create workflow exists // for macOS. For now, always treat checkProjects as true for macOS. - if (flutterDesktopEnabled && macos.existsSync()) { + if (featureFlags.isMacOSEnabled && macos.existsSync()) { await macos.ensureReadyForPlatformSpecificTooling(); } - if (flutterWebEnabled && web.existsSync()) { + if (featureFlags.isWebEnabled && web.existsSync()) { await web.ensureReadyForPlatformSpecificTooling(); } await injectPlugins(this, checkProjects: checkProjects); diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index e9d9211b83..2f56364b83 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -25,10 +25,10 @@ import '../dart/package_map.dart'; import '../dart/pub.dart'; import '../device.dart'; import '../doctor.dart'; +import '../features.dart'; import '../globals.dart'; import '../project.dart'; import '../reporting/usage.dart'; -import '../version.dart'; import 'flutter_command_runner.dart'; export '../cache.dart' show DevelopmentArtifact; @@ -355,11 +355,6 @@ abstract class FlutterCommand extends Command { } } - /// Whether this feature should not be usable on stable branches. - /// - /// Defaults to false, meaning it is usable. - bool get isExperimental => false; - /// Additional usage values to be sent with the usage ping. Future> get usageValues async => const {}; @@ -555,12 +550,6 @@ abstract class FlutterCommand extends Command { @protected @mustCallSuper Future validateCommand() async { - // If we're on a stable branch, then don't allow the usage of - // "experimental" features. - if (isExperimental && !FlutterVersion.instance.isMaster) { - throwToolExit('Experimental feature $name is not supported on stable branches'); - } - if (_requiresPubspecYaml && !PackageMap.isUsingCustomPackagesPath) { // Don't expect a pubspec.yaml file if the user passed in an explicit .packages file path. if (!fs.isFileSync('pubspec.yaml')) { @@ -653,17 +642,17 @@ DevelopmentArtifact _artifactFromTargetPlatform(TargetPlatform targetPlatform) { case TargetPlatform.ios: return DevelopmentArtifact.iOS; case TargetPlatform.darwin_x64: - if (FlutterVersion.instance.isMaster) { + if (featureFlags.isMacOSEnabled) { return DevelopmentArtifact.macOS; } return null; case TargetPlatform.windows_x64: - if (!FlutterVersion.instance.isMaster) { + if (featureFlags.isWindowsEnabled) { return DevelopmentArtifact.windows; } return null; case TargetPlatform.linux_x64: - if (!FlutterVersion.instance.isMaster) { + if (featureFlags.isLinuxEnabled) { return DevelopmentArtifact.linux; } return null; diff --git a/packages/flutter_tools/lib/src/web/web_device.dart b/packages/flutter_tools/lib/src/web/web_device.dart index ecd8f4126a..689eb721aa 100644 --- a/packages/flutter_tools/lib/src/web/web_device.dart +++ b/packages/flutter_tools/lib/src/web/web_device.dart @@ -11,6 +11,7 @@ import '../base/platform.dart'; import '../base/process_manager.dart'; import '../build_info.dart'; import '../device.dart'; +import '../features.dart'; import '../project.dart'; import 'chrome.dart'; import 'workflow.dart'; @@ -74,7 +75,7 @@ class ChromeDevice extends Device { Future get emulatorId async => null; @override - bool isSupported() => flutterWebEnabled && canFindChrome(); + bool isSupported() => featureFlags.isWebEnabled && canFindChrome(); @override String get name => 'Chrome'; @@ -151,7 +152,7 @@ class WebDevices extends PollingDeviceDiscovery { final ChromeDevice _webDevice = ChromeDevice(); @override - bool get canListAnything => flutterWebEnabled; + bool get canListAnything => featureFlags.isWebEnabled; @override Future> pollingGetDevices() async { @@ -161,7 +162,7 @@ class WebDevices extends PollingDeviceDiscovery { } @override - bool get supportsPlatform => flutterWebEnabled; + bool get supportsPlatform => featureFlags.isWebEnabled; } @visibleForTesting diff --git a/packages/flutter_tools/lib/src/web/workflow.dart b/packages/flutter_tools/lib/src/web/workflow.dart index 925ec3d142..1f388ca50a 100644 --- a/packages/flutter_tools/lib/src/web/workflow.dart +++ b/packages/flutter_tools/lib/src/web/workflow.dart @@ -2,33 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:meta/meta.dart'; - -import '../base/common.dart'; import '../base/context.dart'; import '../base/platform.dart'; import '../base/process_manager.dart'; import '../doctor.dart'; -import '../version.dart'; +import '../features.dart'; import 'chrome.dart'; -@visibleForTesting -bool debugDisableWeb = false; - -/// Only launch or display web devices if `FLUTTER_WEB` -/// environment variable is set to true from the daemon. -bool get flutterWebEnabled { - if (debugDisableWeb) { - return false; - } - if (isRunningFromDaemon) { - final bool platformEnabled = platform - .environment['FLUTTER_WEB']?.toLowerCase() == 'true'; - return platformEnabled && FlutterVersion.instance.isMaster; - } - return FlutterVersion.instance.isMaster; -} - /// The web workflow instance. WebWorkflow get webWorkflow => context.get(); @@ -36,13 +16,13 @@ class WebWorkflow extends Workflow { const WebWorkflow(); @override - bool get appliesToHostPlatform => flutterWebEnabled && (platform.isWindows || platform.isMacOS || platform.isLinux); + bool get appliesToHostPlatform => featureFlags.isWebEnabled && (platform.isWindows || platform.isMacOS || platform.isLinux); @override - bool get canLaunchDevices => flutterWebEnabled && canFindChrome(); + bool get canLaunchDevices => featureFlags.isWebEnabled && canFindChrome(); @override - bool get canListDevices => flutterWebEnabled && canFindChrome(); + bool get canListDevices => featureFlags.isWebEnabled && canFindChrome(); @override bool get canListEmulators => false; diff --git a/packages/flutter_tools/lib/src/windows/windows_workflow.dart b/packages/flutter_tools/lib/src/windows/windows_workflow.dart index 8d8e34e0fe..2a423bba2b 100644 --- a/packages/flutter_tools/lib/src/windows/windows_workflow.dart +++ b/packages/flutter_tools/lib/src/windows/windows_workflow.dart @@ -4,8 +4,8 @@ import '../base/context.dart'; import '../base/platform.dart'; -import '../desktop.dart'; import '../doctor.dart'; +import '../features.dart'; /// The [WindowsWorkflow] instance. WindowsWorkflow get windowsWorkflow => context.get(); @@ -18,13 +18,13 @@ class WindowsWorkflow implements Workflow { const WindowsWorkflow(); @override - bool get appliesToHostPlatform => platform.isWindows; + bool get appliesToHostPlatform => platform.isWindows && featureFlags.isWindowsEnabled; @override - bool get canLaunchDevices => platform.isWindows && flutterDesktopEnabled; + bool get canLaunchDevices => platform.isWindows && featureFlags.isWindowsEnabled; @override - bool get canListDevices => platform.isWindows && flutterDesktopEnabled; + bool get canListDevices => platform.isWindows && featureFlags.isWindowsEnabled; @override bool get canListEmulators => false; diff --git a/packages/flutter_tools/test/general.shard/commands/build_bundle_test.dart b/packages/flutter_tools/test/general.shard/commands/build_bundle_test.dart index 23e7a41776..782ac266ad 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_bundle_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_bundle_test.dart @@ -3,93 +3,196 @@ // found in the LICENSE file. import 'package:args/command_runner.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build_bundle.dart'; import 'package:flutter_tools/src/bundle.dart'; +import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/reporting/usage.dart'; import 'package:mockito/mockito.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/testbed.dart'; void main() { Cache.disableLocking(); + Directory tempDir; + MockBundleBuilder mockBundleBuilder; - group('getUsage', () { - Directory tempDir; - MockBundleBuilder mockBundleBuilder; + setUp(() { + tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.'); - setUp(() { - tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.'); + mockBundleBuilder = MockBundleBuilder(); + when( + mockBundleBuilder.build( + platform: anyNamed('platform'), + buildMode: anyNamed('buildMode'), + mainPath: anyNamed('mainPath'), + manifestPath: anyNamed('manifestPath'), + applicationKernelFilePath: anyNamed('applicationKernelFilePath'), + depfilePath: anyNamed('depfilePath'), + privateKeyPath: anyNamed('privateKeyPath'), + assetDirPath: anyNamed('assetDirPath'), + packagesPath: anyNamed('packagesPath'), + precompiledSnapshot: anyNamed('precompiledSnapshot'), + reportLicensedPackages: anyNamed('reportLicensedPackages'), + trackWidgetCreation: anyNamed('trackWidgetCreation'), + extraFrontEndOptions: anyNamed('extraFrontEndOptions'), + extraGenSnapshotOptions: anyNamed('extraGenSnapshotOptions'), + fileSystemRoots: anyNamed('fileSystemRoots'), + fileSystemScheme: anyNamed('fileSystemScheme'), + ), + ).thenAnswer((_) => Future.value()); + }); - mockBundleBuilder = MockBundleBuilder(); - when( - mockBundleBuilder.build( - platform: anyNamed('platform'), - buildMode: anyNamed('buildMode'), - mainPath: anyNamed('mainPath'), - manifestPath: anyNamed('manifestPath'), - applicationKernelFilePath: anyNamed('applicationKernelFilePath'), - depfilePath: anyNamed('depfilePath'), - privateKeyPath: anyNamed('privateKeyPath'), - assetDirPath: anyNamed('assetDirPath'), - packagesPath: anyNamed('packagesPath'), - precompiledSnapshot: anyNamed('precompiledSnapshot'), - reportLicensedPackages: anyNamed('reportLicensedPackages'), - trackWidgetCreation: anyNamed('trackWidgetCreation'), - extraFrontEndOptions: anyNamed('extraFrontEndOptions'), - extraGenSnapshotOptions: anyNamed('extraGenSnapshotOptions'), - fileSystemRoots: anyNamed('fileSystemRoots'), - fileSystemScheme: anyNamed('fileSystemScheme'), - ), - ).thenAnswer((_) => Future.value()); - }); + tearDown(() { + tryToDelete(tempDir); + }); - tearDown(() { - tryToDelete(tempDir); - }); + Future runCommandIn(String projectPath, { List arguments }) async { + final BuildBundleCommand command = BuildBundleCommand(bundleBuilder: mockBundleBuilder); + final CommandRunner runner = createTestCommandRunner(command); + await runner.run([ + 'bundle', + ...?arguments, + '--target=$projectPath/lib/main.dart', + ]); + return command; + } - Future runCommandIn(String projectPath, { List arguments }) async { - final BuildBundleCommand command = BuildBundleCommand(bundleBuilder: mockBundleBuilder); - final CommandRunner runner = createTestCommandRunner(command); - await runner.run([ - 'bundle', - ...?arguments, - '--target=$projectPath/lib/main.dart', - ]); - return command; - } + testUsingContext('bundle getUsage indicate that project is a module', () async { + final String projectPath = await createProject(tempDir, + arguments: ['--no-pub', '--template=module']); - testUsingContext('indicate that project is a module', () async { - final String projectPath = await createProject(tempDir, - arguments: ['--no-pub', '--template=module']); + final BuildBundleCommand command = await runCommandIn(projectPath); - final BuildBundleCommand command = await runCommandIn(projectPath); + expect(await command.usageValues, + containsPair(kCommandBuildBundleIsModule, 'true')); + }, timeout: allowForCreateFlutterProject); - expect(await command.usageValues, - containsPair(kCommandBuildBundleIsModule, 'true')); - }, timeout: allowForCreateFlutterProject); + testUsingContext('bundle getUsage indicate that project is not a module', () async { + final String projectPath = await createProject(tempDir, + arguments: ['--no-pub', '--template=app']); - testUsingContext('indicate that project is not a module', () async { - final String projectPath = await createProject(tempDir, - arguments: ['--no-pub', '--template=app']); + final BuildBundleCommand command = await runCommandIn(projectPath); - final BuildBundleCommand command = await runCommandIn(projectPath); + expect(await command.usageValues, + containsPair(kCommandBuildBundleIsModule, 'false')); + }, timeout: allowForCreateFlutterProject); - expect(await command.usageValues, - containsPair(kCommandBuildBundleIsModule, 'false')); - }, timeout: allowForCreateFlutterProject); + testUsingContext('bundle getUsage indicate the target platform', () async { + final String projectPath = await createProject(tempDir, + arguments: ['--no-pub', '--template=app']); - testUsingContext('indicate the target platform', () async { - final String projectPath = await createProject(tempDir, - arguments: ['--no-pub', '--template=app']); + final BuildBundleCommand command = await runCommandIn(projectPath); - final BuildBundleCommand command = await runCommandIn(projectPath); + expect(await command.usageValues, + containsPair(kCommandBuildBundleTargetPlatform, 'android-arm')); + }, timeout: allowForCreateFlutterProject); - expect(await command.usageValues, - containsPair(kCommandBuildBundleTargetPlatform, 'android-arm')); - }, timeout: allowForCreateFlutterProject); + testUsingContext('bundle fails to build for Windows if feature is disabled', () async { + fs.file('lib/main.dart').createSync(recursive: true); + fs.file('pubspec.yaml').createSync(recursive: true); + fs.file('.packages').createSync(recursive: true); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand() + ..bundleBuilder = MockBundleBuilder()); + + expect(() => runner.run([ + 'bundle', + '--no-pub', + '--target-platform=windows-x64' + ]), throwsA(isInstanceOf())); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: false), + }); + + testUsingContext('bundle fails to build for Linux if feature is disabled', () async { + fs.file('lib/main.dart').createSync(recursive: true); + fs.file('pubspec.yaml').createSync(); + fs.file('.packages').createSync(); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand() + ..bundleBuilder = MockBundleBuilder()); + + expect(() => runner.run([ + 'bundle', + '--no-pub', + '--target-platform=linux-x64' + ]), throwsA(isInstanceOf())); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false), + }); + + testUsingContext('bundle fails to build for macOS if feature is disabled', () async { + fs.file('lib/main.dart').createSync(recursive: true); + fs.file('pubspec.yaml').createSync(); + fs.file('.packages').createSync(); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand() + ..bundleBuilder = MockBundleBuilder()); + + expect(() => runner.run([ + 'bundle', + '--no-pub', + '--target-platform=darwin-x64' + ]), throwsA(isInstanceOf())); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false), + }); + + testUsingContext('bundle can build for Windows if feature is enabled', () async { + fs.file('lib/main.dart').createSync(recursive: true); + fs.file('pubspec.yaml').createSync(); + fs.file('.packages').createSync(); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand() + ..bundleBuilder = MockBundleBuilder()); + + await runner.run([ + 'bundle', + '--no-pub', + '--target-platform=windows-x64' + ]); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), + }); + + testUsingContext('bundle can build for Linux if feature is enabled', () async { + fs.file('lib/main.dart').createSync(recursive: true); + fs.file('pubspec.yaml').createSync(); + fs.file('.packages').createSync(); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand() + ..bundleBuilder = MockBundleBuilder()); + + await runner.run([ + 'bundle', + '--no-pub', + '--target-platform=linux-x64' + ]); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + }); + + testUsingContext('bundle can build for macOS if feature is enabled', () async { + fs.file('lib/main.dart').createSync(recursive: true); + fs.file('pubspec.yaml').createSync(); + fs.file('.packages').createSync(); + final CommandRunner runner = createTestCommandRunner(BuildBundleCommand() + ..bundleBuilder = MockBundleBuilder()); + + await runner.run([ + 'bundle', + '--no-pub', + '--target-platform=darwin-x64' + ]); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true), }); } diff --git a/packages/flutter_tools/test/general.shard/commands/build_linux_test.dart b/packages/flutter_tools/test/general.shard/commands/build_linux_test.dart index 638a20be20..0e1c809a6e 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_linux_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_linux_test.dart @@ -2,6 +2,7 @@ // 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/memory.dart'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/file_system.dart'; @@ -9,6 +10,7 @@ import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build.dart'; +import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/linux/makefile.dart'; import 'package:flutter_tools/src/project.dart'; import 'package:mockito/mockito.dart'; @@ -17,6 +19,7 @@ import 'package:process/process.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/mocks.dart'; +import '../../src/testbed.dart'; void main() { MockProcessManager mockProcessManager; @@ -55,6 +58,7 @@ void main() { }, overrides: { Platform: () => linuxPlatform, FileSystem: () => MemoryFileSystem(), + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), }); testUsingContext('Linux build fails on non-linux platform', () async { @@ -71,6 +75,7 @@ void main() { }, overrides: { Platform: () => notLinuxPlatform, FileSystem: () => MemoryFileSystem(), + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), }); testUsingContext('Linux build invokes make and writes temporary files', () async { @@ -97,6 +102,7 @@ void main() { FileSystem: () => MemoryFileSystem(), ProcessManager: () => mockProcessManager, Platform: () => linuxPlatform, + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), }); testUsingContext('linux can extract binary name from Makefile', () async { @@ -112,7 +118,19 @@ BINARY_NAME=fizz_bar final FlutterProject flutterProject = FlutterProject.current(); expect(makefileExecutableName(flutterProject.linux), 'fizz_bar'); - }, overrides: {FileSystem: () => MemoryFileSystem()}); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + }); + + testUsingContext('Refuses to build for Linux when feature is disabled', () { + final CommandRunner runner = createTestCommandRunner(BuildCommand()); + + expect(() => runner.run(['build', 'linux']), + throwsA(isInstanceOf())); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false), + }); } class MockProcessManager extends Mock implements ProcessManager {} diff --git a/packages/flutter_tools/test/general.shard/commands/build_macos_test.dart b/packages/flutter_tools/test/general.shard/commands/build_macos_test.dart index ceb07b86d3..462e2d83e2 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_macos_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_macos_test.dart @@ -2,6 +2,7 @@ // 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/memory.dart'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/file_system.dart'; @@ -10,6 +11,7 @@ import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build.dart'; +import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/project.dart'; import 'package:mockito/mockito.dart'; import 'package:process/process.dart'; @@ -17,6 +19,7 @@ import 'package:process/process.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/mocks.dart'; +import '../../src/testbed.dart'; void main() { MockProcessManager mockProcessManager; @@ -56,6 +59,7 @@ void main() { ), throwsA(isInstanceOf())); }, overrides: { Platform: () => macosPlatform, + FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true), }); testUsingContext('macOS build fails on non-macOS platform', () async { @@ -71,6 +75,7 @@ void main() { }, overrides: { Platform: () => notMacosPlatform, FileSystem: () => memoryFilesystem, + FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true), }); testUsingContext('macOS build invokes build script', () async { @@ -104,6 +109,16 @@ void main() { FileSystem: () => memoryFilesystem, ProcessManager: () => mockProcessManager, Platform: () => macosPlatform, + FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true), + }); + + testUsingContext('Refuses to build for macOS when feature is disabled', () { + final CommandRunner runner = createTestCommandRunner(BuildCommand()); + + expect(() => runner.run(['build', 'macos']), + throwsA(isInstanceOf())); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false), }); } diff --git a/packages/flutter_tools/test/general.shard/commands/build_web_test.dart b/packages/flutter_tools/test/general.shard/commands/build_web_test.dart index 2adb8181f8..3046570dd5 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_web_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_web_test.dart @@ -2,12 +2,15 @@ // 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:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; +import 'package:flutter_tools/src/commands/build.dart'; import 'package:flutter_tools/src/device.dart'; +import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/resident_runner.dart'; import 'package:flutter_tools/src/resident_web_runner.dart'; @@ -24,6 +27,7 @@ void main() { MockPlatform mockPlatform; setUpAll(() { + Cache.flutterRoot = ''; Cache.disableLocking(); }); @@ -49,6 +53,7 @@ void main() { WebCompilationProxy: () => mockWebCompilationProxy, Platform: () => mockPlatform, FlutterVersion: () => MockFlutterVersion(), + FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), }); }); @@ -82,6 +87,15 @@ void main() { BuildInfo.debug, ); })); + + test('Refuses to build for web when feature is disabled', () => testbed.run(() async { + final CommandRunner runner = createTestCommandRunner(BuildCommand()); + + expect(() => runner.run(['build', 'web']), + throwsA(isInstanceOf())); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isWebEnabled: false), + })); } class MockWebCompilationProxy extends Mock implements WebCompilationProxy {} diff --git a/packages/flutter_tools/test/general.shard/commands/build_windows_test.dart b/packages/flutter_tools/test/general.shard/commands/build_windows_test.dart index 301fa7d42a..948eae0ee2 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_windows_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_windows_test.dart @@ -9,6 +9,7 @@ import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build.dart'; +import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/windows/visual_studio.dart'; import 'package:mockito/mockito.dart'; import 'package:process/process.dart'; @@ -17,6 +18,7 @@ import 'package:xml/xml.dart' as xml; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/mocks.dart'; +import '../../src/testbed.dart'; void main() { MockProcessManager mockProcessManager; @@ -65,6 +67,7 @@ void main() { Platform: () => windowsPlatform, FileSystem: () => memoryFilesystem, VisualStudio: () => mockVisualStudio, + FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), }); testUsingContext('Windows build fails when there is no windows project', () async { @@ -78,6 +81,7 @@ void main() { Platform: () => windowsPlatform, FileSystem: () => memoryFilesystem, VisualStudio: () => mockVisualStudio, + FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), }); testUsingContext('Windows build fails on non windows platform', () async { @@ -96,6 +100,7 @@ void main() { Platform: () => notWindowsPlatform, FileSystem: () => memoryFilesystem, VisualStudio: () => mockVisualStudio, + FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), }); testUsingContext('Windows build invokes msbuild and writes generated files', () async { @@ -132,6 +137,7 @@ void main() { ProcessManager: () => mockProcessManager, Platform: () => windowsPlatform, VisualStudio: () => mockVisualStudio, + FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), }); } diff --git a/packages/flutter_tools/test/general.shard/commands/config_test.dart b/packages/flutter_tools/test/general.shard/commands/config_test.dart index e7c87e8cf1..16fec40189 100644 --- a/packages/flutter_tools/test/general.shard/commands/config_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/config_test.dart @@ -12,9 +12,7 @@ import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/config.dart'; -import 'package:flutter_tools/src/desktop.dart'; import 'package:flutter_tools/src/version.dart'; -import 'package:flutter_tools/src/web/workflow.dart'; import 'package:mockito/mockito.dart'; import '../../src/common.dart'; @@ -26,9 +24,6 @@ void main() { MockFlutterVersion mockFlutterVersion; setUpAll(() { - // TODO(jonahwilliams): remove once features are landed. - debugDisableDesktop = true; - debugDisableWeb = true; Cache.disableLocking(); }); diff --git a/packages/flutter_tools/test/general.shard/commands/devices_test.dart b/packages/flutter_tools/test/general.shard/commands/devices_test.dart index 01bbb9a5d5..ee6e0950a4 100644 --- a/packages/flutter_tools/test/general.shard/commands/devices_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/devices_test.dart @@ -20,9 +20,6 @@ void main() { group('devices', () { setUpAll(() { Cache.disableLocking(); - // TODO(jonahwilliams): adjust the individual tests so they do not - // depend on the host environment. - debugDisableWebAndDesktop = true; }); testUsingContext('returns 0 when called', () async { diff --git a/packages/flutter_tools/test/general.shard/commands/update_packages_test.dart b/packages/flutter_tools/test/general.shard/commands/update_packages_test.dart deleted file mode 100644 index 6097ba0136..0000000000 --- a/packages/flutter_tools/test/general.shard/commands/update_packages_test.dart +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter_tools/src/commands/update_packages.dart'; - -import '../../src/common.dart'; -import '../../src/context.dart'; - -void main() { - group('UpdatePackagesCommand', () { - // Marking it as experimental breaks bots tests and packaging scripts on stable branches. - testUsingContext('is not marked as experimental', () async { - final UpdatePackagesCommand command = UpdatePackagesCommand(); - expect(command.isExperimental, isFalse); - }); - }); -} diff --git a/packages/flutter_tools/test/general.shard/doctor.dart b/packages/flutter_tools/test/general.shard/doctor.dart new file mode 100644 index 0000000000..db574e6172 --- /dev/null +++ b/packages/flutter_tools/test/general.shard/doctor.dart @@ -0,0 +1,62 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_tools/src/doctor.dart'; +import 'package:flutter_tools/src/features.dart'; +import 'package:flutter_tools/src/linux/linux_doctor.dart'; +import 'package:flutter_tools/src/web/web_validator.dart'; +import 'package:flutter_tools/src/windows/visual_studio_validator.dart'; + +import '../src/common.dart'; +import '../src/testbed.dart'; + +void main() { + Testbed testbed; + + setUp(() { + testbed = Testbed(); + }); + + test('doctor validators includes desktop when features are enabled', () => testbed.run(() { + expect(DoctorValidatorsProvider.defaultInstance.validators, + contains(isInstanceOf())); + expect(DoctorValidatorsProvider.defaultInstance.validators, + contains(isInstanceOf())); + }, overrides: { + FeatureFlags: () => TestFeatureFlags( + isLinuxEnabled: true, + isWindowsEnabled: true, + ) + })); + + test('doctor validators does not include desktop when features are enabled', () => testbed.run(() { + expect(DoctorValidatorsProvider.defaultInstance.validators, + isNot(contains(isInstanceOf()))); + expect(DoctorValidatorsProvider.defaultInstance.validators, + isNot(contains(isInstanceOf()))); + }, overrides: { + FeatureFlags: () => TestFeatureFlags( + isLinuxEnabled: false, + isWindowsEnabled: false, + ) + })); + + test('doctor validators includes web when feature is enabled', () => testbed.run(() { + expect(DoctorValidatorsProvider.defaultInstance.validators, + contains(isInstanceOf())); + }, overrides: { + FeatureFlags: () => TestFeatureFlags( + isWebEnabled: true, + ) + })); + + test('doctor validators does not include web when feature is disabled', () => testbed.run(() { + expect(DoctorValidatorsProvider.defaultInstance.validators, + isNot(contains(isInstanceOf()))); + }, overrides: { + FeatureFlags: () => TestFeatureFlags( + isWebEnabled: false, + ) + })); +} diff --git a/packages/flutter_tools/test/general.shard/linux/linux_workflow_test.dart b/packages/flutter_tools/test/general.shard/linux/linux_workflow_test.dart index 1445cff26d..8723c47f02 100644 --- a/packages/flutter_tools/test/general.shard/linux/linux_workflow_test.dart +++ b/packages/flutter_tools/test/general.shard/linux/linux_workflow_test.dart @@ -2,42 +2,57 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter_tools/src/features.dart'; import 'package:mockito/mockito.dart'; import 'package:flutter_tools/src/linux/linux_workflow.dart'; import 'package:flutter_tools/src/base/platform.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/testbed.dart'; void main() { - group(LinuxWorkflow, () { - final MockPlatform linux = MockPlatform(); - final MockPlatform linuxWithFde = MockPlatform() - ..environment['ENABLE_FLUTTER_DESKTOP'] = 'true'; - final MockPlatform notLinux = MockPlatform(); + MockPlatform linux; + MockPlatform notLinux; + Testbed testbed; + + setUp(() { + linux = MockPlatform(); + notLinux = MockPlatform(); when(linux.isLinux).thenReturn(true); - when(linuxWithFde.isLinux).thenReturn(true); when(notLinux.isLinux).thenReturn(false); - - testUsingContext('Applies to linux platform', () { - expect(linuxWorkflow.appliesToHostPlatform, true); - }, overrides: { - Platform: () => linux, - }); - testUsingContext('Does not apply to non-linux platform', () { - expect(linuxWorkflow.appliesToHostPlatform, false); - }, overrides: { - Platform: () => notLinux, - }); - - testUsingContext('defaults', () { - expect(linuxWorkflow.canListEmulators, false); - expect(linuxWorkflow.canLaunchDevices, true); - expect(linuxWorkflow.canListDevices, true); - }, overrides: { - Platform: () => linuxWithFde, - }); + testbed = Testbed( + overrides: { + Platform: () => linux, + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), + } + ); }); + + test('Applies to linux platform', () => testbed.run(() { + expect(linuxWorkflow.appliesToHostPlatform, true); + expect(linuxWorkflow.canLaunchDevices, true); + expect(linuxWorkflow.canListDevices, true); + expect(linuxWorkflow.canListEmulators, false); + })); + + test('Does not apply to non-linux platform', () => testbed.run(() { + expect(linuxWorkflow.appliesToHostPlatform, false); + expect(linuxWorkflow.canLaunchDevices, false); + expect(linuxWorkflow.canListDevices, false); + expect(linuxWorkflow.canListEmulators, false); + }, overrides: { + Platform: () => notLinux, + })); + + test('Does not apply when feature is disabled', () => testbed.run(() { + expect(linuxWorkflow.appliesToHostPlatform, false); + expect(linuxWorkflow.canLaunchDevices, false); + expect(linuxWorkflow.canListDevices, false); + expect(linuxWorkflow.canListEmulators, false); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false), + })); } class MockPlatform extends Mock implements Platform { diff --git a/packages/flutter_tools/test/general.shard/macos/macos_workflow_test.dart b/packages/flutter_tools/test/general.shard/macos/macos_workflow_test.dart index b63bdde561..9dadfe9816 100644 --- a/packages/flutter_tools/test/general.shard/macos/macos_workflow_test.dart +++ b/packages/flutter_tools/test/general.shard/macos/macos_workflow_test.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter_tools/src/base/io.dart'; +import 'package:flutter_tools/src/features.dart'; import 'package:mockito/mockito.dart'; import 'package:flutter_tools/src/base/platform.dart'; @@ -11,40 +11,48 @@ import 'package:process/process.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/testbed.dart'; void main() { - group(MacOSWorkflow, () { - final MockPlatform mac = MockPlatform(); - final MockPlatform macWithFde = MockPlatform() - ..environment['ENABLE_FLUTTER_DESKTOP'] = 'true'; - final MockPlatform notMac = MockPlatform(); + MockPlatform mac; + MockPlatform notMac; + Testbed testbed; + + setUp(() { + mac = MockPlatform(); + notMac = MockPlatform(); when(mac.isMacOS).thenReturn(true); - when(macWithFde.isMacOS).thenReturn(true); when(notMac.isMacOS).thenReturn(false); - - final MockProcessManager mockProcessManager = MockProcessManager(); - when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async { - return ProcessResult(0, 1, '', ''); - }); - testUsingContext('Applies to mac platform', () { - expect(macOSWorkflow.appliesToHostPlatform, true); - }, overrides: { + testbed = Testbed(overrides: { Platform: () => mac, - }); - testUsingContext('Does not apply to non-mac platform', () { - expect(macOSWorkflow.appliesToHostPlatform, false); - }, overrides: { - Platform: () => notMac, - }); - - testUsingContext('defaults', () { - expect(macOSWorkflow.canListEmulators, false); - expect(macOSWorkflow.canLaunchDevices, true); - expect(macOSWorkflow.canListDevices, true); - }, overrides: { - Platform: () => macWithFde, + FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true), }); }); + + test('Applies to macOS platform', () => testbed.run(() { + expect(macOSWorkflow.appliesToHostPlatform, true); + expect(macOSWorkflow.canListDevices, true); + expect(macOSWorkflow.canLaunchDevices, true); + expect(macOSWorkflow.canListEmulators, false); + })); + + test('Does not apply to non-macOS platform', () => testbed.run(() { + expect(macOSWorkflow.appliesToHostPlatform, false); + expect(macOSWorkflow.canListDevices, false); + expect(macOSWorkflow.canLaunchDevices, false); + expect(macOSWorkflow.canListEmulators, false); + }, overrides: { + Platform: () => notMac, + })); + + test('Does not apply when feature is disabled', () => testbed.run(() { + expect(macOSWorkflow.appliesToHostPlatform, false); + expect(macOSWorkflow.canListDevices, false); + expect(macOSWorkflow.canLaunchDevices, false); + expect(macOSWorkflow.canListEmulators, false); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false), + })); } class MockPlatform extends Mock implements Platform { diff --git a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart index c781e3c79d..eb06c9d192 100644 --- a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart @@ -243,27 +243,6 @@ void main() { SystemClock: () => clock, Usage: () => usage, }); - - }); - - group('Experimental commands', () { - final MockVersion stableVersion = MockVersion(); - final MockVersion betaVersion = MockVersion(); - final FakeCommand fakeCommand = FakeCommand(); - when(stableVersion.isMaster).thenReturn(false); - when(betaVersion.isMaster).thenReturn(true); - - testUsingContext('Can be disabled on stable branch', () async { - expect(() => fakeCommand.run(), throwsA(isA())); - }, overrides: { - FlutterVersion: () => stableVersion, - }); - - testUsingContext('Works normally on regular branches', () async { - expect(fakeCommand.run(), completes); - }, overrides: { - FlutterVersion: () => betaVersion, - }); }); } @@ -275,9 +254,6 @@ class FakeCommand extends FlutterCommand { @override String get name => 'fake'; - @override - bool get isExperimental => true; - @override Future runCommand() async { return null; diff --git a/packages/flutter_tools/test/general.shard/web/workflow_test.dart b/packages/flutter_tools/test/general.shard/web/workflow_test.dart index 86a8d6b369..ab11aea498 100644 --- a/packages/flutter_tools/test/general.shard/web/workflow_test.dart +++ b/packages/flutter_tools/test/general.shard/web/workflow_test.dart @@ -4,7 +4,7 @@ import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/platform.dart'; -import 'package:flutter_tools/src/version.dart'; +import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/web/chrome.dart'; import 'package:flutter_tools/src/web/workflow.dart'; import 'package:mockito/mockito.dart'; @@ -22,13 +22,9 @@ void main() { MockPlatform linux; MockPlatform macos; MockProcessManager mockProcessManager; - MockFlutterVersion unstable; - MockFlutterVersion stable; WebWorkflow workflow; setUpAll(() { - unstable = MockFlutterVersion(false); - stable = MockFlutterVersion(true); notSupported = MockPlatform(linux: false, windows: false, macos: false); windows = MockPlatform(windows: true); linux = MockPlatform(linux: true); @@ -39,7 +35,7 @@ void main() { fs.file('chrome').createSync(); when(mockProcessManager.canRun('chrome')).thenReturn(true); }, overrides: { - FlutterVersion: () => unstable, + FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), ProcessManager: () => mockProcessManager, }); }); @@ -81,27 +77,18 @@ void main() { Platform: () => notSupported, })); - test('does not apply on stable branch', () => testbed.run(() { + test('does not apply if feature flag is disabled', () => testbed.run(() { expect(workflow.appliesToHostPlatform, false); expect(workflow.canLaunchDevices, false); expect(workflow.canListDevices, false); expect(workflow.canListEmulators, false); }, overrides: { Platform: () => macos, - FlutterVersion: () => stable, + FeatureFlags: () => TestFeatureFlags(isWebEnabled: false), })); }); } -class MockFlutterVersion extends Mock implements FlutterVersion { - MockFlutterVersion(this.isStable); - - final bool isStable; - - @override - bool get isMaster => !isStable; -} - class MockProcessManager extends Mock implements ProcessManager {} class MockPlatform extends Mock implements Platform { diff --git a/packages/flutter_tools/test/general.shard/windows/windows_workflow_test.dart b/packages/flutter_tools/test/general.shard/windows/windows_workflow_test.dart index 4c9d202189..86a566b47b 100644 --- a/packages/flutter_tools/test/general.shard/windows/windows_workflow_test.dart +++ b/packages/flutter_tools/test/general.shard/windows/windows_workflow_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter_tools/src/features.dart'; import 'package:mockito/mockito.dart'; import 'package:flutter_tools/src/base/platform.dart'; @@ -9,36 +10,50 @@ import 'package:flutter_tools/src/windows/windows_workflow.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/testbed.dart'; void main() { - group(WindowsWorkflow, () { - final MockPlatform windows = MockPlatform(); - final MockPlatform windowsWithFde = MockPlatform() - ..environment['ENABLE_FLUTTER_DESKTOP'] = 'true'; - final MockPlatform notWindows = MockPlatform(); + Testbed testbed; + MockPlatform windows; + MockPlatform notWindows; + + setUp(() { + windows = MockPlatform(); + notWindows = MockPlatform(); when(windows.isWindows).thenReturn(true); - when(windowsWithFde.isWindows).thenReturn(true); when(notWindows.isWindows).thenReturn(false); - - testUsingContext('Applies to windows platform', () { - expect(windowsWorkflow.appliesToHostPlatform, true); - }, overrides: { - Platform: () => windows, - }); - testUsingContext('Does not apply to non-windows platform', () { - expect(windowsWorkflow.appliesToHostPlatform, false); - }, overrides: { - Platform: () => notWindows, - }); - - testUsingContext('defaults', () { - expect(windowsWorkflow.canListEmulators, false); - expect(windowsWorkflow.canLaunchDevices, true); - expect(windowsWorkflow.canListDevices, true); - }, overrides: { - Platform: () => windowsWithFde, - }); + testbed = Testbed( + overrides: { + Platform: () => windows, + FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), + } + ); }); + + test('Windows default workflow values', () => testbed.run(() { + expect(windowsWorkflow.appliesToHostPlatform, true); + expect(windowsWorkflow.canListDevices, true); + expect(windowsWorkflow.canLaunchDevices, true); + expect(windowsWorkflow.canListEmulators, false); + })); + + test('Windows defaults on non-windows platform', () => testbed.run(() { + expect(windowsWorkflow.appliesToHostPlatform, false); + expect(windowsWorkflow.canListDevices, false); + expect(windowsWorkflow.canLaunchDevices, false); + expect(windowsWorkflow.canListEmulators, false); + }, overrides: { + Platform: () => notWindows, + })); + + test('Windows defaults on non-windows platform', () => testbed.run(() { + expect(windowsWorkflow.appliesToHostPlatform, false); + expect(windowsWorkflow.canListDevices, false); + expect(windowsWorkflow.canLaunchDevices, false); + expect(windowsWorkflow.canListEmulators, false); + }, overrides: { + FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: false), + })); } class MockPlatform extends Mock implements Platform { diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart index 2b8f22a010..3a3a4ea3ef 100644 --- a/packages/flutter_tools/test/src/common.dart +++ b/packages/flutter_tools/test/src/common.dart @@ -5,8 +5,6 @@ import 'dart:async'; import 'package:args/command_runner.dart'; -import 'package:flutter_tools/src/desktop.dart'; -import 'package:flutter_tools/src/web/workflow.dart'; import 'package:test_api/test_api.dart' hide TypeMatcher, isInstanceOf; import 'package:test_api/test_api.dart' as test_package show TypeMatcher; @@ -20,19 +18,6 @@ import 'package:flutter_tools/src/runner/flutter_command_runner.dart'; export 'package:test_core/test_core.dart' hide TypeMatcher, isInstanceOf; // Defines a 'package:test' shim. -/// Disable both web and desktop to make testing easier. For example, prevent -/// them from showing up in the devices list if the host happens to be setup -/// properly. -set debugDisableWebAndDesktop(bool value) { - if (value) { - debugDisableDesktop = true; - debugDisableWeb = true; - } else { - debugDisableDesktop = false; - debugDisableWeb = false; - } -} - /// A matcher that compares the type of the actual value to the type argument T. // TODO(ianh): Remove this once https://github.com/dart-lang/matcher/issues/98 is fixed Matcher isInstanceOf() => test_package.TypeMatcher(); diff --git a/packages/flutter_tools/test/src/testbed.dart b/packages/flutter_tools/test/src/testbed.dart index ac4901cedc..c0a6873d1d 100644 --- a/packages/flutter_tools/test/src/testbed.dart +++ b/packages/flutter_tools/test/src/testbed.dart @@ -17,6 +17,7 @@ import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/context_runner.dart'; +import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/reporting/usage.dart'; import 'package:flutter_tools/src/version.dart'; @@ -687,3 +688,26 @@ class FakeFlutterVersion implements FlutterVersion { return null; } } + +// A test implementation of [FeatureFlags] that allows enabling without reading +// config. If not otherwise specified, all values default to false. +class TestFeatureFlags implements FeatureFlags { + TestFeatureFlags({ + this.isLinuxEnabled = false, + this.isMacOSEnabled = false, + this.isWebEnabled = false, + this.isWindowsEnabled = false, +}); + + @override + final bool isLinuxEnabled; + + @override + final bool isMacOSEnabled; + + @override + final bool isWebEnabled; + + @override + final bool isWindowsEnabled; +}