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

Use FlutterFeatures to configure web and desktop devices (#36465)

parent bffbad5c
...@@ -289,6 +289,9 @@ Future<void> _flutterBuildDart2js(String relativePathToApplication) async { ...@@ -289,6 +289,9 @@ Future<void> _flutterBuildDart2js(String relativePathToApplication) async {
workingDirectory: path.join(flutterRoot, relativePathToApplication), workingDirectory: path.join(flutterRoot, relativePathToApplication),
expectNonZeroExit: false, expectNonZeroExit: false,
timeout: _kShortTimeout, timeout: _kShortTimeout,
environment: <String, String>{
'FLUTTER_WEB': 'true',
}
); );
print('Done.'); print('Done.');
} }
......
...@@ -30,8 +30,6 @@ class AssembleCommand extends FlutterCommand { ...@@ -30,8 +30,6 @@ class AssembleCommand extends FlutterCommand {
@override @override
String get name => 'assemble'; String get name => 'assemble';
@override
bool get isExperimental => true;
@override @override
Future<FlutterCommandResult> runCommand() { Future<FlutterCommandResult> runCommand() {
...@@ -126,9 +124,6 @@ class AssembleRun extends AssembleBase { ...@@ -126,9 +124,6 @@ class AssembleRun extends AssembleBase {
@override @override
String get name => 'run'; String get name => 'run';
@override
bool get isExperimental => true;
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
final BuildResult result = await buildSystem.build(targetName, environment, BuildSystemConfig( final BuildResult result = await buildSystem.build(targetName, environment, BuildSystemConfig(
...@@ -155,9 +150,6 @@ class AssembleDescribe extends AssembleBase { ...@@ -155,9 +150,6 @@ class AssembleDescribe extends AssembleBase {
@override @override
String get name => 'describe'; String get name => 'describe';
@override
bool get isExperimental => true;
@override @override
Future<FlutterCommandResult> runCommand() { Future<FlutterCommandResult> runCommand() {
try { try {
...@@ -180,9 +172,6 @@ class AssembleListInputs extends AssembleBase { ...@@ -180,9 +172,6 @@ class AssembleListInputs extends AssembleBase {
@override @override
String get name => 'inputs'; String get name => 'inputs';
@override
bool get isExperimental => true;
@override @override
Future<FlutterCommandResult> runCommand() { Future<FlutterCommandResult> runCommand() {
try { try {
...@@ -209,9 +198,6 @@ class AssembleBuildDirectory extends AssembleBase { ...@@ -209,9 +198,6 @@ class AssembleBuildDirectory extends AssembleBase {
@override @override
String get name => 'build-dir'; String get name => 'build-dir';
@override
bool get isExperimental => true;
@override @override
Future<FlutterCommandResult> runCommand() { Future<FlutterCommandResult> runCommand() {
printStatus(environment.buildDir.path); printStatus(environment.buildDir.path);
......
...@@ -8,10 +8,10 @@ import '../base/common.dart'; ...@@ -8,10 +8,10 @@ import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../bundle.dart'; import '../bundle.dart';
import '../features.dart';
import '../project.dart'; import '../project.dart';
import '../reporting/usage.dart'; import '../reporting/usage.dart';
import '../runner/flutter_command.dart' show FlutterOptions, FlutterCommandResult; import '../runner/flutter_command.dart' show FlutterOptions, FlutterCommandResult;
import '../version.dart';
import 'build.dart'; import 'build.dart';
class BuildBundleCommand extends BuildSubCommand { class BuildBundleCommand extends BuildSubCommand {
...@@ -98,13 +98,21 @@ class BuildBundleCommand extends BuildSubCommand { ...@@ -98,13 +98,21 @@ class BuildBundleCommand extends BuildSubCommand {
if (platform == null) { if (platform == null) {
throwToolExit('Unknown platform: $targetPlatform'); 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) { switch (platform) {
case TargetPlatform.darwin_x64: case TargetPlatform.darwin_x64:
if (!featureFlags.isMacOSEnabled) {
throwToolExit('macOS is not a supported target platform.');
}
break;
case TargetPlatform.windows_x64: case TargetPlatform.windows_x64:
if (!featureFlags.isWindowsEnabled) {
throwToolExit('Windows is not a supported target platform.');
}
break;
case TargetPlatform.linux_x64: case TargetPlatform.linux_x64:
if (!FlutterVersion.instance.isMaster) { if (!featureFlags.isLinuxEnabled) {
throwToolExit('$targetPlatform is not supported on stable Flutter.'); throwToolExit('Linux is not a supported target platform.');
} }
break; break;
default: default:
......
...@@ -24,9 +24,6 @@ class BuildFuchsiaCommand extends BuildSubCommand { ...@@ -24,9 +24,6 @@ class BuildFuchsiaCommand extends BuildSubCommand {
@override @override
final String name = 'fuchsia'; final String name = 'fuchsia';
@override
bool isExperimental = true;
@override @override
bool hidden = true; bool hidden = true;
......
...@@ -8,6 +8,7 @@ import '../base/common.dart'; ...@@ -8,6 +8,7 @@ import '../base/common.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../cache.dart'; import '../cache.dart';
import '../features.dart';
import '../linux/build_linux.dart'; import '../linux/build_linux.dart';
import '../project.dart'; import '../project.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult; import '../runner/flutter_command.dart' show FlutterCommandResult;
...@@ -34,9 +35,6 @@ class BuildLinuxCommand extends BuildSubCommand { ...@@ -34,9 +35,6 @@ class BuildLinuxCommand extends BuildSubCommand {
@override @override
final String name = 'linux'; final String name = 'linux';
@override
bool isExperimental = true;
@override @override
bool hidden = true; bool hidden = true;
...@@ -54,6 +52,9 @@ class BuildLinuxCommand extends BuildSubCommand { ...@@ -54,6 +52,9 @@ class BuildLinuxCommand extends BuildSubCommand {
Cache.releaseLockEarly(); Cache.releaseLockEarly();
final BuildInfo buildInfo = getBuildInfo(); final BuildInfo buildInfo = getBuildInfo();
final FlutterProject flutterProject = FlutterProject.current(); final FlutterProject flutterProject = FlutterProject.current();
if (!featureFlags.isLinuxEnabled) {
throwToolExit('"build linux" is not currently supported.');
}
if (!platform.isLinux) { if (!platform.isLinux) {
throwToolExit('"build linux" only supported on Linux hosts.'); throwToolExit('"build linux" only supported on Linux hosts.');
} }
......
...@@ -8,6 +8,7 @@ import '../base/common.dart'; ...@@ -8,6 +8,7 @@ import '../base/common.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../cache.dart'; import '../cache.dart';
import '../features.dart';
import '../macos/build_macos.dart'; import '../macos/build_macos.dart';
import '../project.dart'; import '../project.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult; import '../runner/flutter_command.dart' show FlutterCommandResult;
...@@ -34,9 +35,6 @@ class BuildMacosCommand extends BuildSubCommand { ...@@ -34,9 +35,6 @@ class BuildMacosCommand extends BuildSubCommand {
@override @override
final String name = 'macos'; final String name = 'macos';
@override
bool isExperimental = true;
@override @override
bool hidden = true; bool hidden = true;
...@@ -47,13 +45,16 @@ class BuildMacosCommand extends BuildSubCommand { ...@@ -47,13 +45,16 @@ class BuildMacosCommand extends BuildSubCommand {
}; };
@override @override
String get description => 'build the macOS desktop target (Experimental).'; String get description => 'build the macOS desktop target.';
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
Cache.releaseLockEarly(); Cache.releaseLockEarly();
final BuildInfo buildInfo = getBuildInfo(); final BuildInfo buildInfo = getBuildInfo();
final FlutterProject flutterProject = FlutterProject.current(); final FlutterProject flutterProject = FlutterProject.current();
if (!featureFlags.isMacOSEnabled) {
throwToolExit('"build macos" is not currently supported.');
}
if (!platform.isMacOS) { if (!platform.isMacOS) {
throwToolExit('"build macos" only supported on macOS hosts.'); throwToolExit('"build macos" only supported on macOS hosts.');
} }
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
import 'dart:async'; import 'dart:async';
import '../base/common.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../features.dart';
import '../project.dart'; import '../project.dart';
import '../runner/flutter_command.dart' import '../runner/flutter_command.dart'
show DevelopmentArtifact, FlutterCommandResult; show DevelopmentArtifact, FlutterCommandResult;
...@@ -32,13 +34,13 @@ class BuildWebCommand extends BuildSubCommand { ...@@ -32,13 +34,13 @@ class BuildWebCommand extends BuildSubCommand {
bool get hidden => true; bool get hidden => true;
@override @override
bool get isExperimental => true; final String description = 'build a web application bundle.';
@override
final String description = '(EXPERIMENTAL) build a web application bundle.';
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
if (!featureFlags.isWebEnabled) {
throwToolExit('"build web" is not currently supported.');
}
final FlutterProject flutterProject = FlutterProject.current(); final FlutterProject flutterProject = FlutterProject.current();
final String target = argResults['target']; final String target = argResults['target'];
final BuildInfo buildInfo = getBuildInfo(); final BuildInfo buildInfo = getBuildInfo();
......
...@@ -8,6 +8,7 @@ import '../base/common.dart'; ...@@ -8,6 +8,7 @@ import '../base/common.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../cache.dart'; import '../cache.dart';
import '../features.dart';
import '../project.dart'; import '../project.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult; import '../runner/flutter_command.dart' show FlutterCommandResult;
import '../windows/build_windows.dart'; import '../windows/build_windows.dart';
...@@ -34,9 +35,6 @@ class BuildWindowsCommand extends BuildSubCommand { ...@@ -34,9 +35,6 @@ class BuildWindowsCommand extends BuildSubCommand {
@override @override
final String name = 'windows'; final String name = 'windows';
@override
bool isExperimental = true;
@override @override
bool hidden = true; bool hidden = true;
...@@ -47,13 +45,16 @@ class BuildWindowsCommand extends BuildSubCommand { ...@@ -47,13 +45,16 @@ class BuildWindowsCommand extends BuildSubCommand {
}; };
@override @override
String get description => 'build the desktop Windows target (Experimental).'; String get description => 'build the desktop Windows target.';
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
Cache.releaseLockEarly(); Cache.releaseLockEarly();
final FlutterProject flutterProject = FlutterProject.current(); final FlutterProject flutterProject = FlutterProject.current();
final BuildInfo buildInfo = getBuildInfo(); final BuildInfo buildInfo = getBuildInfo();
if (!featureFlags.isWindowsEnabled) {
throwToolExit('"build windows" is not currently supported.');
}
if (!platform.isWindows) { if (!platform.isWindows) {
throwToolExit('"build windows" only supported on Windows hosts.'); throwToolExit('"build windows" only supported on Windows hosts.');
} }
......
...@@ -20,6 +20,7 @@ import '../cache.dart'; ...@@ -20,6 +20,7 @@ import '../cache.dart';
import '../convert.dart'; import '../convert.dart';
import '../dart/pub.dart'; import '../dart/pub.dart';
import '../doctor.dart'; import '../doctor.dart';
import '../features.dart';
import '../globals.dart'; import '../globals.dart';
import '../project.dart'; import '../project.dart';
import '../reporting/usage.dart'; import '../reporting/usage.dart';
...@@ -613,7 +614,7 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi ...@@ -613,7 +614,7 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi
'iosLanguage': iosLanguage, 'iosLanguage': iosLanguage,
'flutterRevision': FlutterVersion.instance.frameworkRevision, 'flutterRevision': FlutterVersion.instance.frameworkRevision,
'flutterChannel': FlutterVersion.instance.channel, 'flutterChannel': FlutterVersion.instance.channel,
'web': web && FlutterVersion.instance.isMaster 'web': web && featureFlags.isWebEnabled,
}; };
} }
......
...@@ -13,6 +13,7 @@ import '../base/utils.dart'; ...@@ -13,6 +13,7 @@ import '../base/utils.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../cache.dart'; import '../cache.dart';
import '../device.dart'; import '../device.dart';
import '../features.dart';
import '../globals.dart'; import '../globals.dart';
import '../macos/xcode.dart'; import '../macos/xcode.dart';
import '../project.dart'; import '../project.dart';
...@@ -410,11 +411,11 @@ class RunCommand extends RunCommandBase { ...@@ -410,11 +411,11 @@ class RunCommand extends RunCommandBase {
); );
flutterDevices.add(flutterDevice); flutterDevices.add(flutterDevice);
} }
// Only support "web mode" on non-stable branches with a single web device // Only support "web mode" with a single web device due to resident runner
// in a "hot mode". // refactoring required otherwise.
final bool webMode = FlutterVersion.instance.isMaster final bool webMode = featureFlags.isWebEnabled &&
&& devices.length == 1 devices.length == 1 &&
&& await devices.single.targetPlatform == TargetPlatform.web_javascript; await devices.single.targetPlatform == TargetPlatform.web_javascript;
ResidentRunner runner; ResidentRunner runner;
final String applicationBinaryPath = argResults['use-application-binary']; final String applicationBinaryPath = argResults['use-application-binary'];
......
...@@ -69,9 +69,6 @@ class UnpackCommand extends FlutterCommand { ...@@ -69,9 +69,6 @@ class UnpackCommand extends FlutterCommand {
@override @override
bool get hidden => true; bool get hidden => true;
@override
bool get isExperimental => true;
@override @override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async { Future<Set<DevelopmentArtifact>> get requiredArtifacts async {
final Set<DevelopmentArtifact> result = <DevelopmentArtifact>{ final Set<DevelopmentArtifact> result = <DevelopmentArtifact>{
......
...@@ -4,32 +4,10 @@ ...@@ -4,32 +4,10 @@
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart';
import 'base/common.dart';
import 'base/io.dart'; import 'base/io.dart';
import 'base/platform.dart';
import 'base/process_manager.dart'; import 'base/process_manager.dart';
import 'convert.dart'; import 'convert.dart';
import 'device.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. /// Kills a process on linux or macOS.
Future<bool> killProcess(String executable) async { Future<bool> killProcess(String executable) async {
......
...@@ -14,9 +14,7 @@ import 'base/context.dart'; ...@@ -14,9 +14,7 @@ import 'base/context.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'base/utils.dart'; import 'base/utils.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'desktop.dart';
import 'fuchsia/fuchsia_device.dart'; import 'fuchsia/fuchsia_device.dart';
import 'globals.dart'; import 'globals.dart';
import 'ios/devices.dart'; import 'ios/devices.dart';
import 'ios/simulators.dart'; import 'ios/simulators.dart';
...@@ -25,7 +23,6 @@ import 'macos/macos_device.dart'; ...@@ -25,7 +23,6 @@ import 'macos/macos_device.dart';
import 'project.dart'; import 'project.dart';
import 'tester/flutter_tester.dart'; import 'tester/flutter_tester.dart';
import 'web/web_device.dart'; import 'web/web_device.dart';
import 'web/workflow.dart';
import 'windows/windows_device.dart'; import 'windows/windows_device.dart';
DeviceManager get deviceManager => context.get<DeviceManager>(); DeviceManager get deviceManager => context.get<DeviceManager>();
...@@ -74,23 +71,11 @@ class DeviceManager { ...@@ -74,23 +71,11 @@ class DeviceManager {
IOSSimulators(), IOSSimulators(),
FuchsiaDevices(), FuchsiaDevices(),
FlutterTesterDevices(), FlutterTesterDevices(),
] + _conditionalDesktopDevices + _conditionalWebDevices); MacOSDevices(),
LinuxDevices(),
/// Only add desktop devices if the flag is enabled. WindowsDevices(),
static List<DeviceDiscovery> get _conditionalDesktopDevices { WebDevices(),
return flutterDesktopEnabled ? <DeviceDiscovery>[ ]);
MacOSDevices(),
LinuxDevices(),
WindowsDevices(),
] : <DeviceDiscovery>[];
}
/// Only add web devices if the flag is enabled.
static List<DeviceDiscovery> get _conditionalWebDevices {
return flutterWebEnabled ? <DeviceDiscovery>[
WebDevices(),
] : <DeviceDiscovery>[];
}
String _specifiedDeviceId; String _specifiedDeviceId;
......
...@@ -19,7 +19,6 @@ import 'base/user_messages.dart'; ...@@ -19,7 +19,6 @@ import 'base/user_messages.dart';
import 'base/utils.dart'; import 'base/utils.dart';
import 'base/version.dart'; import 'base/version.dart';
import 'cache.dart'; import 'cache.dart';
import 'desktop.dart';
import 'device.dart'; import 'device.dart';
import 'fuchsia/fuchsia_workflow.dart'; import 'fuchsia/fuchsia_workflow.dart';
import 'globals.dart'; import 'globals.dart';
...@@ -74,12 +73,10 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider { ...@@ -74,12 +73,10 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider {
GroupedValidator(<DoctorValidator>[xcodeValidator, cocoapodsValidator]), GroupedValidator(<DoctorValidator>[xcodeValidator, cocoapodsValidator]),
if (webWorkflow.appliesToHostPlatform) if (webWorkflow.appliesToHostPlatform)
const WebValidator(), const WebValidator(),
// Add desktop doctors to workflow if the flag is enabled. if (linuxWorkflow.appliesToHostPlatform)
if (flutterDesktopEnabled) LinuxDoctorValidator(),
...<DoctorValidator>[ if (windowsWorkflow.appliesToHostPlatform)
if (linuxWorkflow.appliesToHostPlatform) LinuxDoctorValidator(), visualStudioValidator,
if (windowsWorkflow.appliesToHostPlatform) visualStudioValidator,
],
if (ideValidators.isNotEmpty) if (ideValidators.isNotEmpty)
...ideValidators ...ideValidators
else else
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
import '../base/context.dart'; import '../base/context.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../desktop.dart';
import '../doctor.dart'; import '../doctor.dart';
import '../features.dart';
/// The [WindowsWorkflow] instance. /// The [WindowsWorkflow] instance.
LinuxWorkflow get linuxWorkflow => context.get<LinuxWorkflow>(); LinuxWorkflow get linuxWorkflow => context.get<LinuxWorkflow>();
...@@ -18,13 +18,13 @@ class LinuxWorkflow implements Workflow { ...@@ -18,13 +18,13 @@ class LinuxWorkflow implements Workflow {
const LinuxWorkflow(); const LinuxWorkflow();
@override @override
bool get appliesToHostPlatform => platform.isLinux; bool get appliesToHostPlatform => platform.isLinux && featureFlags.isLinuxEnabled;
@override @override
bool get canLaunchDevices => platform.isLinux && flutterDesktopEnabled; bool get canLaunchDevices => platform.isLinux && featureFlags.isLinuxEnabled;
@override @override
bool get canListDevices => platform.isLinux && flutterDesktopEnabled; bool get canListDevices => platform.isLinux && featureFlags.isLinuxEnabled;
@override @override
bool get canListEmulators => false; bool get canListEmulators => false;
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
import '../base/context.dart'; import '../base/context.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../desktop.dart';
import '../doctor.dart'; import '../doctor.dart';
import '../features.dart';
/// The [MacOSWorkflow] instance. /// The [MacOSWorkflow] instance.
MacOSWorkflow get macOSWorkflow => context.get<MacOSWorkflow>(); MacOSWorkflow get macOSWorkflow => context.get<MacOSWorkflow>();
...@@ -18,13 +18,13 @@ class MacOSWorkflow implements Workflow { ...@@ -18,13 +18,13 @@ class MacOSWorkflow implements Workflow {
const MacOSWorkflow(); const MacOSWorkflow();
@override @override
bool get appliesToHostPlatform => platform.isMacOS; bool get appliesToHostPlatform => platform.isMacOS && featureFlags.isMacOSEnabled;
@override @override
bool get canLaunchDevices => platform.isMacOS && flutterDesktopEnabled; bool get canLaunchDevices => platform.isMacOS && featureFlags.isMacOSEnabled;
@override @override
bool get canListDevices => platform.isMacOS && flutterDesktopEnabled; bool get canListDevices => platform.isMacOS && featureFlags.isMacOSEnabled;
@override @override
bool get canListEmulators => false; bool get canListEmulators => false;
......
...@@ -9,7 +9,7 @@ import 'package:yaml/yaml.dart'; ...@@ -9,7 +9,7 @@ import 'package:yaml/yaml.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'dart/package_map.dart'; import 'dart/package_map.dart';
import 'desktop.dart'; import 'features.dart';
import 'globals.dart'; import 'globals.dart';
import 'macos/cocoapods.dart'; import 'macos/cocoapods.dart';
import 'project.dart'; import 'project.dart';
...@@ -364,7 +364,7 @@ Future<void> injectPlugins(FlutterProject project, {bool checkProjects = false}) ...@@ -364,7 +364,7 @@ Future<void> injectPlugins(FlutterProject project, {bool checkProjects = false})
// TODO(stuartmorgan): Revisit the condition here once the plans for handling // TODO(stuartmorgan): Revisit the condition here once the plans for handling
// desktop in existing projects are in place. For now, ignore checkProjects // desktop in existing projects are in place. For now, ignore checkProjects
// on desktop and always treat it as true. // on desktop and always treat it as true.
if (flutterDesktopEnabled && project.macos.existsSync()) { if (featureFlags.isMacOSEnabled && project.macos.existsSync()) {
await _writeMacOSPluginRegistrant(project, plugins); await _writeMacOSPluginRegistrant(project, plugins);
} }
for (final XcodeBasedProject subproject in <XcodeBasedProject>[project.ios, project.macos]) { for (final XcodeBasedProject subproject in <XcodeBasedProject>[project.ios, project.macos]) {
......
...@@ -14,7 +14,7 @@ import 'base/file_system.dart'; ...@@ -14,7 +14,7 @@ import 'base/file_system.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'bundle.dart' as bundle; import 'bundle.dart' as bundle;
import 'cache.dart'; import 'cache.dart';
import 'desktop.dart'; import 'features.dart';
import 'flutter_manifest.dart'; import 'flutter_manifest.dart';
import 'globals.dart'; import 'globals.dart';
import 'ios/ios_workflow.dart'; import 'ios/ios_workflow.dart';
...@@ -22,7 +22,6 @@ import 'ios/plist_utils.dart' as plist; ...@@ -22,7 +22,6 @@ import 'ios/plist_utils.dart' as plist;
import 'ios/xcodeproj.dart' as xcode; import 'ios/xcodeproj.dart' as xcode;
import 'plugins.dart'; import 'plugins.dart';
import 'template.dart'; import 'template.dart';
import 'web/workflow.dart';
FlutterProjectFactory get projectFactory => context.get<FlutterProjectFactory>() ?? const FlutterProjectFactory(); FlutterProjectFactory get projectFactory => context.get<FlutterProjectFactory>() ?? const FlutterProjectFactory();
...@@ -206,10 +205,10 @@ class FlutterProject { ...@@ -206,10 +205,10 @@ class FlutterProject {
} }
// TODO(stuartmorgan): Add checkProjects logic once a create workflow exists // TODO(stuartmorgan): Add checkProjects logic once a create workflow exists
// for macOS. For now, always treat checkProjects as true for macOS. // for macOS. For now, always treat checkProjects as true for macOS.
if (flutterDesktopEnabled && macos.existsSync()) { if (featureFlags.isMacOSEnabled && macos.existsSync()) {
await macos.ensureReadyForPlatformSpecificTooling(); await macos.ensureReadyForPlatformSpecificTooling();
} }
if (flutterWebEnabled && web.existsSync()) { if (featureFlags.isWebEnabled && web.existsSync()) {
await web.ensureReadyForPlatformSpecificTooling(); await web.ensureReadyForPlatformSpecificTooling();
} }
await injectPlugins(this, checkProjects: checkProjects); await injectPlugins(this, checkProjects: checkProjects);
......
...@@ -25,10 +25,10 @@ import '../dart/package_map.dart'; ...@@ -25,10 +25,10 @@ import '../dart/package_map.dart';
import '../dart/pub.dart'; import '../dart/pub.dart';
import '../device.dart'; import '../device.dart';
import '../doctor.dart'; import '../doctor.dart';
import '../features.dart';
import '../globals.dart'; import '../globals.dart';
import '../project.dart'; import '../project.dart';
import '../reporting/usage.dart'; import '../reporting/usage.dart';
import '../version.dart';
import 'flutter_command_runner.dart'; import 'flutter_command_runner.dart';
export '../cache.dart' show DevelopmentArtifact; export '../cache.dart' show DevelopmentArtifact;
...@@ -355,11 +355,6 @@ abstract class FlutterCommand extends Command<void> { ...@@ -355,11 +355,6 @@ abstract class FlutterCommand extends Command<void> {
} }
} }
/// 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. /// Additional usage values to be sent with the usage ping.
Future<Map<String, String>> get usageValues async => const <String, String>{}; Future<Map<String, String>> get usageValues async => const <String, String>{};
...@@ -555,12 +550,6 @@ abstract class FlutterCommand extends Command<void> { ...@@ -555,12 +550,6 @@ abstract class FlutterCommand extends Command<void> {
@protected @protected
@mustCallSuper @mustCallSuper
Future<void> validateCommand() async { Future<void> 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) { if (_requiresPubspecYaml && !PackageMap.isUsingCustomPackagesPath) {
// Don't expect a pubspec.yaml file if the user passed in an explicit .packages file path. // Don't expect a pubspec.yaml file if the user passed in an explicit .packages file path.
if (!fs.isFileSync('pubspec.yaml')) { if (!fs.isFileSync('pubspec.yaml')) {
...@@ -653,17 +642,17 @@ DevelopmentArtifact _artifactFromTargetPlatform(TargetPlatform targetPlatform) { ...@@ -653,17 +642,17 @@ DevelopmentArtifact _artifactFromTargetPlatform(TargetPlatform targetPlatform) {
case TargetPlatform.ios: case TargetPlatform.ios:
return DevelopmentArtifact.iOS; return DevelopmentArtifact.iOS;
case TargetPlatform.darwin_x64: case TargetPlatform.darwin_x64:
if (FlutterVersion.instance.isMaster) { if (featureFlags.isMacOSEnabled) {
return DevelopmentArtifact.macOS; return DevelopmentArtifact.macOS;
} }
return null; return null;
case TargetPlatform.windows_x64: case TargetPlatform.windows_x64:
if (!FlutterVersion.instance.isMaster) { if (featureFlags.isWindowsEnabled) {
return DevelopmentArtifact.windows; return DevelopmentArtifact.windows;
} }
return null; return null;
case TargetPlatform.linux_x64: case TargetPlatform.linux_x64:
if (!FlutterVersion.instance.isMaster) { if (featureFlags.isLinuxEnabled) {
return DevelopmentArtifact.linux; return DevelopmentArtifact.linux;
} }
return null; return null;
......
...@@ -11,6 +11,7 @@ import '../base/platform.dart'; ...@@ -11,6 +11,7 @@ import '../base/platform.dart';
import '../base/process_manager.dart'; import '../base/process_manager.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../device.dart'; import '../device.dart';
import '../features.dart';
import '../project.dart'; import '../project.dart';
import 'chrome.dart'; import 'chrome.dart';
import 'workflow.dart'; import 'workflow.dart';
...@@ -74,7 +75,7 @@ class ChromeDevice extends Device { ...@@ -74,7 +75,7 @@ class ChromeDevice extends Device {
Future<String> get emulatorId async => null; Future<String> get emulatorId async => null;
@override @override
bool isSupported() => flutterWebEnabled && canFindChrome(); bool isSupported() => featureFlags.isWebEnabled && canFindChrome();
@override @override
String get name => 'Chrome'; String get name => 'Chrome';
...@@ -151,7 +152,7 @@ class WebDevices extends PollingDeviceDiscovery { ...@@ -151,7 +152,7 @@ class WebDevices extends PollingDeviceDiscovery {
final ChromeDevice _webDevice = ChromeDevice(); final ChromeDevice _webDevice = ChromeDevice();
@override @override
bool get canListAnything => flutterWebEnabled; bool get canListAnything => featureFlags.isWebEnabled;
@override @override
Future<List<Device>> pollingGetDevices() async { Future<List<Device>> pollingGetDevices() async {
...@@ -161,7 +162,7 @@ class WebDevices extends PollingDeviceDiscovery { ...@@ -161,7 +162,7 @@ class WebDevices extends PollingDeviceDiscovery {
} }
@override @override
bool get supportsPlatform => flutterWebEnabled; bool get supportsPlatform => featureFlags.isWebEnabled;
} }
@visibleForTesting @visibleForTesting
......
...@@ -2,33 +2,13 @@ ...@@ -2,33 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:meta/meta.dart';
import '../base/common.dart';
import '../base/context.dart'; import '../base/context.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../base/process_manager.dart'; import '../base/process_manager.dart';
import '../doctor.dart'; import '../doctor.dart';
import '../version.dart'; import '../features.dart';
import 'chrome.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. /// The web workflow instance.
WebWorkflow get webWorkflow => context.get<WebWorkflow>(); WebWorkflow get webWorkflow => context.get<WebWorkflow>();
...@@ -36,13 +16,13 @@ class WebWorkflow extends Workflow { ...@@ -36,13 +16,13 @@ class WebWorkflow extends Workflow {
const WebWorkflow(); const WebWorkflow();
@override @override
bool get appliesToHostPlatform => flutterWebEnabled && (platform.isWindows || platform.isMacOS || platform.isLinux); bool get appliesToHostPlatform => featureFlags.isWebEnabled && (platform.isWindows || platform.isMacOS || platform.isLinux);
@override @override
bool get canLaunchDevices => flutterWebEnabled && canFindChrome(); bool get canLaunchDevices => featureFlags.isWebEnabled && canFindChrome();
@override @override
bool get canListDevices => flutterWebEnabled && canFindChrome(); bool get canListDevices => featureFlags.isWebEnabled && canFindChrome();
@override @override
bool get canListEmulators => false; bool get canListEmulators => false;
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
import '../base/context.dart'; import '../base/context.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../desktop.dart';
import '../doctor.dart'; import '../doctor.dart';
import '../features.dart';
/// The [WindowsWorkflow] instance. /// The [WindowsWorkflow] instance.
WindowsWorkflow get windowsWorkflow => context.get<WindowsWorkflow>(); WindowsWorkflow get windowsWorkflow => context.get<WindowsWorkflow>();
...@@ -18,13 +18,13 @@ class WindowsWorkflow implements Workflow { ...@@ -18,13 +18,13 @@ class WindowsWorkflow implements Workflow {
const WindowsWorkflow(); const WindowsWorkflow();
@override @override
bool get appliesToHostPlatform => platform.isWindows; bool get appliesToHostPlatform => platform.isWindows && featureFlags.isWindowsEnabled;
@override @override
bool get canLaunchDevices => platform.isWindows && flutterDesktopEnabled; bool get canLaunchDevices => platform.isWindows && featureFlags.isWindowsEnabled;
@override @override
bool get canListDevices => platform.isWindows && flutterDesktopEnabled; bool get canListDevices => platform.isWindows && featureFlags.isWindowsEnabled;
@override @override
bool get canListEmulators => false; bool get canListEmulators => false;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:args/command_runner.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
...@@ -9,6 +10,7 @@ import 'package:flutter_tools/src/base/io.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/base/platform.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart'; import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/linux/makefile.dart'; import 'package:flutter_tools/src/linux/makefile.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
...@@ -17,6 +19,7 @@ import 'package:process/process.dart'; ...@@ -17,6 +19,7 @@ import 'package:process/process.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/mocks.dart'; import '../../src/mocks.dart';
import '../../src/testbed.dart';
void main() { void main() {
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
...@@ -55,6 +58,7 @@ void main() { ...@@ -55,6 +58,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => linuxPlatform, Platform: () => linuxPlatform,
FileSystem: () => MemoryFileSystem(), FileSystem: () => MemoryFileSystem(),
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
}); });
testUsingContext('Linux build fails on non-linux platform', () async { testUsingContext('Linux build fails on non-linux platform', () async {
...@@ -71,6 +75,7 @@ void main() { ...@@ -71,6 +75,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => notLinuxPlatform, Platform: () => notLinuxPlatform,
FileSystem: () => MemoryFileSystem(), FileSystem: () => MemoryFileSystem(),
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
}); });
testUsingContext('Linux build invokes make and writes temporary files', () async { testUsingContext('Linux build invokes make and writes temporary files', () async {
...@@ -97,6 +102,7 @@ void main() { ...@@ -97,6 +102,7 @@ void main() {
FileSystem: () => MemoryFileSystem(), FileSystem: () => MemoryFileSystem(),
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
Platform: () => linuxPlatform, Platform: () => linuxPlatform,
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
}); });
testUsingContext('linux can extract binary name from Makefile', () async { testUsingContext('linux can extract binary name from Makefile', () async {
...@@ -112,7 +118,19 @@ BINARY_NAME=fizz_bar ...@@ -112,7 +118,19 @@ BINARY_NAME=fizz_bar
final FlutterProject flutterProject = FlutterProject.current(); final FlutterProject flutterProject = FlutterProject.current();
expect(makefileExecutableName(flutterProject.linux), 'fizz_bar'); expect(makefileExecutableName(flutterProject.linux), 'fizz_bar');
}, overrides: <Type, Generator>{FileSystem: () => MemoryFileSystem()}); }, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
});
testUsingContext('Refuses to build for Linux when feature is disabled', () {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand());
expect(() => runner.run(<String>['build', 'linux']),
throwsA(isInstanceOf<ToolExit>()));
}, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
});
} }
class MockProcessManager extends Mock implements ProcessManager {} class MockProcessManager extends Mock implements ProcessManager {}
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:args/command_runner.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
...@@ -10,6 +11,7 @@ import 'package:flutter_tools/src/base/platform.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/build_info.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart'; import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
...@@ -17,6 +19,7 @@ import 'package:process/process.dart'; ...@@ -17,6 +19,7 @@ import 'package:process/process.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/mocks.dart'; import '../../src/mocks.dart';
import '../../src/testbed.dart';
void main() { void main() {
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
...@@ -56,6 +59,7 @@ void main() { ...@@ -56,6 +59,7 @@ void main() {
), throwsA(isInstanceOf<ToolExit>())); ), throwsA(isInstanceOf<ToolExit>()));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => macosPlatform, Platform: () => macosPlatform,
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
}); });
testUsingContext('macOS build fails on non-macOS platform', () async { testUsingContext('macOS build fails on non-macOS platform', () async {
...@@ -71,6 +75,7 @@ void main() { ...@@ -71,6 +75,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => notMacosPlatform, Platform: () => notMacosPlatform,
FileSystem: () => memoryFilesystem, FileSystem: () => memoryFilesystem,
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
}); });
testUsingContext('macOS build invokes build script', () async { testUsingContext('macOS build invokes build script', () async {
...@@ -104,6 +109,16 @@ void main() { ...@@ -104,6 +109,16 @@ void main() {
FileSystem: () => memoryFilesystem, FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
Platform: () => macosPlatform, Platform: () => macosPlatform,
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
});
testUsingContext('Refuses to build for macOS when feature is disabled', () {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand());
expect(() => runner.run(<String>['build', 'macos']),
throwsA(isInstanceOf<ToolExit>()));
}, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false),
}); });
} }
......
...@@ -2,12 +2,15 @@ ...@@ -2,12 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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/common.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/device.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/project.dart';
import 'package:flutter_tools/src/resident_runner.dart'; import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/resident_web_runner.dart'; import 'package:flutter_tools/src/resident_web_runner.dart';
...@@ -24,6 +27,7 @@ void main() { ...@@ -24,6 +27,7 @@ void main() {
MockPlatform mockPlatform; MockPlatform mockPlatform;
setUpAll(() { setUpAll(() {
Cache.flutterRoot = '';
Cache.disableLocking(); Cache.disableLocking();
}); });
...@@ -49,6 +53,7 @@ void main() { ...@@ -49,6 +53,7 @@ void main() {
WebCompilationProxy: () => mockWebCompilationProxy, WebCompilationProxy: () => mockWebCompilationProxy,
Platform: () => mockPlatform, Platform: () => mockPlatform,
FlutterVersion: () => MockFlutterVersion(), FlutterVersion: () => MockFlutterVersion(),
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
}); });
}); });
...@@ -82,6 +87,15 @@ void main() { ...@@ -82,6 +87,15 @@ void main() {
BuildInfo.debug, BuildInfo.debug,
); );
})); }));
test('Refuses to build for web when feature is disabled', () => testbed.run(() async {
final CommandRunner<void> runner = createTestCommandRunner(BuildCommand());
expect(() => runner.run(<String>['build', 'web']),
throwsA(isInstanceOf<ToolExit>()));
}, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isWebEnabled: false),
}));
} }
class MockWebCompilationProxy extends Mock implements WebCompilationProxy {} class MockWebCompilationProxy extends Mock implements WebCompilationProxy {}
......
...@@ -9,6 +9,7 @@ import 'package:flutter_tools/src/base/io.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/base/platform.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart'; import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/windows/visual_studio.dart'; import 'package:flutter_tools/src/windows/visual_studio.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
...@@ -17,6 +18,7 @@ import 'package:xml/xml.dart' as xml; ...@@ -17,6 +18,7 @@ import 'package:xml/xml.dart' as xml;
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/mocks.dart'; import '../../src/mocks.dart';
import '../../src/testbed.dart';
void main() { void main() {
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
...@@ -65,6 +67,7 @@ void main() { ...@@ -65,6 +67,7 @@ void main() {
Platform: () => windowsPlatform, Platform: () => windowsPlatform,
FileSystem: () => memoryFilesystem, FileSystem: () => memoryFilesystem,
VisualStudio: () => mockVisualStudio, VisualStudio: () => mockVisualStudio,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
}); });
testUsingContext('Windows build fails when there is no windows project', () async { testUsingContext('Windows build fails when there is no windows project', () async {
...@@ -78,6 +81,7 @@ void main() { ...@@ -78,6 +81,7 @@ void main() {
Platform: () => windowsPlatform, Platform: () => windowsPlatform,
FileSystem: () => memoryFilesystem, FileSystem: () => memoryFilesystem,
VisualStudio: () => mockVisualStudio, VisualStudio: () => mockVisualStudio,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
}); });
testUsingContext('Windows build fails on non windows platform', () async { testUsingContext('Windows build fails on non windows platform', () async {
...@@ -96,6 +100,7 @@ void main() { ...@@ -96,6 +100,7 @@ void main() {
Platform: () => notWindowsPlatform, Platform: () => notWindowsPlatform,
FileSystem: () => memoryFilesystem, FileSystem: () => memoryFilesystem,
VisualStudio: () => mockVisualStudio, VisualStudio: () => mockVisualStudio,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
}); });
testUsingContext('Windows build invokes msbuild and writes generated files', () async { testUsingContext('Windows build invokes msbuild and writes generated files', () async {
...@@ -132,6 +137,7 @@ void main() { ...@@ -132,6 +137,7 @@ void main() {
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform, Platform: () => windowsPlatform,
VisualStudio: () => mockVisualStudio, VisualStudio: () => mockVisualStudio,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
}); });
} }
......
...@@ -20,9 +20,6 @@ void main() { ...@@ -20,9 +20,6 @@ void main() {
group('devices', () { group('devices', () {
setUpAll(() { setUpAll(() {
Cache.disableLocking(); 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 { testUsingContext('returns 0 when called', () async {
......
// 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);
});
});
}
// 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<LinuxDoctorValidator>()));
expect(DoctorValidatorsProvider.defaultInstance.validators,
contains(isInstanceOf<VisualStudioValidator>()));
}, overrides: <Type, Generator>{
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<LinuxDoctorValidator>())));
expect(DoctorValidatorsProvider.defaultInstance.validators,
isNot(contains(isInstanceOf<VisualStudioValidator>())));
}, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(
isLinuxEnabled: false,
isWindowsEnabled: false,
)
}));
test('doctor validators includes web when feature is enabled', () => testbed.run(() {
expect(DoctorValidatorsProvider.defaultInstance.validators,
contains(isInstanceOf<WebValidator>()));
}, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(
isWebEnabled: true,
)
}));
test('doctor validators does not include web when feature is disabled', () => testbed.run(() {
expect(DoctorValidatorsProvider.defaultInstance.validators,
isNot(contains(isInstanceOf<WebValidator>())));
}, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(
isWebEnabled: false,
)
}));
}
...@@ -2,42 +2,57 @@ ...@@ -2,42 +2,57 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter_tools/src/features.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:flutter_tools/src/linux/linux_workflow.dart'; import 'package:flutter_tools/src/linux/linux_workflow.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/testbed.dart';
void main() { void main() {
group(LinuxWorkflow, () { MockPlatform linux;
final MockPlatform linux = MockPlatform(); MockPlatform notLinux;
final MockPlatform linuxWithFde = MockPlatform() Testbed testbed;
..environment['ENABLE_FLUTTER_DESKTOP'] = 'true';
final MockPlatform notLinux = MockPlatform(); setUp(() {
linux = MockPlatform();
notLinux = MockPlatform();
when(linux.isLinux).thenReturn(true); when(linux.isLinux).thenReturn(true);
when(linuxWithFde.isLinux).thenReturn(true);
when(notLinux.isLinux).thenReturn(false); when(notLinux.isLinux).thenReturn(false);
testbed = Testbed(
testUsingContext('Applies to linux platform', () { overrides: <Type, Generator>{
expect(linuxWorkflow.appliesToHostPlatform, true); Platform: () => linux,
}, overrides: <Type, Generator>{ FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
Platform: () => linux, }
}); );
testUsingContext('Does not apply to non-linux platform', () {
expect(linuxWorkflow.appliesToHostPlatform, false);
}, overrides: <Type, Generator>{
Platform: () => notLinux,
});
testUsingContext('defaults', () {
expect(linuxWorkflow.canListEmulators, false);
expect(linuxWorkflow.canLaunchDevices, true);
expect(linuxWorkflow.canListDevices, true);
}, overrides: <Type, Generator>{
Platform: () => linuxWithFde,
});
}); });
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: <Type, Generator>{
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: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
}));
} }
class MockPlatform extends Mock implements Platform { class MockPlatform extends Mock implements Platform {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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:mockito/mockito.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
...@@ -11,40 +11,48 @@ import 'package:process/process.dart'; ...@@ -11,40 +11,48 @@ import 'package:process/process.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/testbed.dart';
void main() { void main() {
group(MacOSWorkflow, () { MockPlatform mac;
final MockPlatform mac = MockPlatform(); MockPlatform notMac;
final MockPlatform macWithFde = MockPlatform() Testbed testbed;
..environment['ENABLE_FLUTTER_DESKTOP'] = 'true';
final MockPlatform notMac = MockPlatform(); setUp(() {
mac = MockPlatform();
notMac = MockPlatform();
when(mac.isMacOS).thenReturn(true); when(mac.isMacOS).thenReturn(true);
when(macWithFde.isMacOS).thenReturn(true);
when(notMac.isMacOS).thenReturn(false); when(notMac.isMacOS).thenReturn(false);
testbed = Testbed(overrides: <Type, Generator>{
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: <Type, Generator>{
Platform: () => mac, Platform: () => mac,
}); FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
testUsingContext('Does not apply to non-mac platform', () {
expect(macOSWorkflow.appliesToHostPlatform, false);
}, overrides: <Type, Generator>{
Platform: () => notMac,
});
testUsingContext('defaults', () {
expect(macOSWorkflow.canListEmulators, false);
expect(macOSWorkflow.canLaunchDevices, true);
expect(macOSWorkflow.canListDevices, true);
}, overrides: <Type, Generator>{
Platform: () => macWithFde,
}); });
}); });
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: <Type, Generator>{
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: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false),
}));
} }
class MockPlatform extends Mock implements Platform { class MockPlatform extends Mock implements Platform {
......
...@@ -243,27 +243,6 @@ void main() { ...@@ -243,27 +243,6 @@ void main() {
SystemClock: () => clock, SystemClock: () => clock,
Usage: () => usage, 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<ToolExit>()));
}, overrides: <Type, Generator>{
FlutterVersion: () => stableVersion,
});
testUsingContext('Works normally on regular branches', () async {
expect(fakeCommand.run(), completes);
}, overrides: <Type, Generator>{
FlutterVersion: () => betaVersion,
});
}); });
} }
...@@ -275,9 +254,6 @@ class FakeCommand extends FlutterCommand { ...@@ -275,9 +254,6 @@ class FakeCommand extends FlutterCommand {
@override @override
String get name => 'fake'; String get name => 'fake';
@override
bool get isExperimental => true;
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
return null; return null;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.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/chrome.dart';
import 'package:flutter_tools/src/web/workflow.dart'; import 'package:flutter_tools/src/web/workflow.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
...@@ -22,13 +22,9 @@ void main() { ...@@ -22,13 +22,9 @@ void main() {
MockPlatform linux; MockPlatform linux;
MockPlatform macos; MockPlatform macos;
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
MockFlutterVersion unstable;
MockFlutterVersion stable;
WebWorkflow workflow; WebWorkflow workflow;
setUpAll(() { setUpAll(() {
unstable = MockFlutterVersion(false);
stable = MockFlutterVersion(true);
notSupported = MockPlatform(linux: false, windows: false, macos: false); notSupported = MockPlatform(linux: false, windows: false, macos: false);
windows = MockPlatform(windows: true); windows = MockPlatform(windows: true);
linux = MockPlatform(linux: true); linux = MockPlatform(linux: true);
...@@ -39,7 +35,7 @@ void main() { ...@@ -39,7 +35,7 @@ void main() {
fs.file('chrome').createSync(); fs.file('chrome').createSync();
when(mockProcessManager.canRun('chrome')).thenReturn(true); when(mockProcessManager.canRun('chrome')).thenReturn(true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FlutterVersion: () => unstable, FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
}); });
...@@ -81,27 +77,18 @@ void main() { ...@@ -81,27 +77,18 @@ void main() {
Platform: () => notSupported, 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.appliesToHostPlatform, false);
expect(workflow.canLaunchDevices, false); expect(workflow.canLaunchDevices, false);
expect(workflow.canListDevices, false); expect(workflow.canListDevices, false);
expect(workflow.canListEmulators, false); expect(workflow.canListEmulators, false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => macos, 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 MockProcessManager extends Mock implements ProcessManager {}
class MockPlatform extends Mock implements Platform { class MockPlatform extends Mock implements Platform {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter_tools/src/features.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
...@@ -9,36 +10,50 @@ import 'package:flutter_tools/src/windows/windows_workflow.dart'; ...@@ -9,36 +10,50 @@ import 'package:flutter_tools/src/windows/windows_workflow.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/testbed.dart';
void main() { void main() {
group(WindowsWorkflow, () { Testbed testbed;
final MockPlatform windows = MockPlatform(); MockPlatform windows;
final MockPlatform windowsWithFde = MockPlatform() MockPlatform notWindows;
..environment['ENABLE_FLUTTER_DESKTOP'] = 'true';
final MockPlatform notWindows = MockPlatform(); setUp(() {
windows = MockPlatform();
notWindows = MockPlatform();
when(windows.isWindows).thenReturn(true); when(windows.isWindows).thenReturn(true);
when(windowsWithFde.isWindows).thenReturn(true);
when(notWindows.isWindows).thenReturn(false); when(notWindows.isWindows).thenReturn(false);
testbed = Testbed(
testUsingContext('Applies to windows platform', () { overrides: <Type, Generator>{
expect(windowsWorkflow.appliesToHostPlatform, true); Platform: () => windows,
}, overrides: <Type, Generator>{ FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
Platform: () => windows, }
}); );
testUsingContext('Does not apply to non-windows platform', () {
expect(windowsWorkflow.appliesToHostPlatform, false);
}, overrides: <Type, Generator>{
Platform: () => notWindows,
});
testUsingContext('defaults', () {
expect(windowsWorkflow.canListEmulators, false);
expect(windowsWorkflow.canLaunchDevices, true);
expect(windowsWorkflow.canListDevices, true);
}, overrides: <Type, Generator>{
Platform: () => windowsWithFde,
});
}); });
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: <Type, Generator>{
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: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: false),
}));
} }
class MockPlatform extends Mock implements Platform { class MockPlatform extends Mock implements Platform {
......
...@@ -5,8 +5,6 @@ ...@@ -5,8 +5,6 @@
import 'dart:async'; import 'dart:async';
import 'package:args/command_runner.dart'; 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' hide TypeMatcher, isInstanceOf;
import 'package:test_api/test_api.dart' as test_package show TypeMatcher; 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'; ...@@ -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. 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. /// 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 // TODO(ianh): Remove this once https://github.com/dart-lang/matcher/issues/98 is fixed
Matcher isInstanceOf<T>() => test_package.TypeMatcher<T>(); Matcher isInstanceOf<T>() => test_package.TypeMatcher<T>();
......
...@@ -17,6 +17,7 @@ import 'package:flutter_tools/src/base/logger.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/base/terminal.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/context_runner.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/reporting/usage.dart';
import 'package:flutter_tools/src/version.dart'; import 'package:flutter_tools/src/version.dart';
...@@ -687,3 +688,26 @@ class FakeFlutterVersion implements FlutterVersion { ...@@ -687,3 +688,26 @@ class FakeFlutterVersion implements FlutterVersion {
return null; 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment