mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add flutter: config: {...} section to pubspec.yaml to influence FeatureFlags (#167953)
Closes https://github.com/flutter/flutter/issues/167667. In this PR: - `FlutterFeaturesConfig` reads from the global config, current pubspec.yaml, and environment - `FeatureFlags` now delegates "was this configured" to `FlutterFeaturesConfig` - `featureFlags` (`Context`) now is created with knowledge of the current `pubpec.yaml`, if any Once this lands and we play with it a bit (i.e. migrate `disable-swift-package-manager`), we can publicize it and update website documentation (it won't make it into this beta/stable release anyway). I did a significant rewrite of `feature_test`, as lots of what was being tested no longer made sense.
This commit is contained in:
parent
2a3e27fdd9
commit
f94a82d2e4
@ -41,6 +41,8 @@ import 'flutter_application_package.dart';
|
||||
import 'flutter_cache.dart';
|
||||
import 'flutter_device_manager.dart';
|
||||
import 'flutter_features.dart';
|
||||
import 'flutter_features_config.dart';
|
||||
import 'flutter_manifest.dart';
|
||||
import 'globals.dart' as globals;
|
||||
import 'ios/ios_workflow.dart';
|
||||
import 'ios/iproxy.dart';
|
||||
@ -233,7 +235,15 @@ Future<T> runInContext<T>(FutureOr<T> Function() runner, {Map<Type, Generator>?
|
||||
FeatureFlags:
|
||||
() => FlutterFeatureFlags(
|
||||
flutterVersion: globals.flutterVersion,
|
||||
config: globals.config,
|
||||
featuresConfig: FlutterFeaturesConfig(
|
||||
globalConfig: globals.config,
|
||||
platform: globals.platform,
|
||||
projectManifest: FlutterManifest.createFromPath(
|
||||
globals.fs.path.join(globals.fs.currentDirectory.path, 'pubspec.yaml'),
|
||||
fileSystem: globals.fs,
|
||||
logger: globals.logger,
|
||||
),
|
||||
),
|
||||
platform: globals.platform,
|
||||
),
|
||||
FlutterVersion: () => FlutterVersion(fs: globals.fs, flutterRoot: Cache.flutterRoot!),
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
// 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/context.dart';
|
||||
|
||||
/// The current [FeatureFlags] implementation.
|
||||
@ -275,7 +277,8 @@ class Feature {
|
||||
}
|
||||
|
||||
/// A description of the conditions to enable a feature for a particular channel.
|
||||
class FeatureChannelSetting {
|
||||
@immutable
|
||||
final class FeatureChannelSetting {
|
||||
const FeatureChannelSetting({this.available = false, this.enabledByDefault = false});
|
||||
|
||||
/// Whether the feature is available on this channel.
|
||||
@ -288,4 +291,19 @@ class FeatureChannelSetting {
|
||||
///
|
||||
/// If not provided, defaults to `false`.
|
||||
final bool enabledByDefault;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is FeatureChannelSetting &&
|
||||
available == other.available &&
|
||||
enabledByDefault == other.enabledByDefault;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(available, enabledByDefault);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FeatureChannelSetting <available: $available, enabledByDefault: $enabledByDefault>';
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,23 +2,17 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'base/config.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'base/platform.dart';
|
||||
import 'features.dart';
|
||||
import 'flutter_features_config.dart';
|
||||
import 'version.dart';
|
||||
|
||||
class FlutterFeatureFlags implements FeatureFlags {
|
||||
FlutterFeatureFlags({
|
||||
required FlutterVersion flutterVersion,
|
||||
required Config config,
|
||||
required Platform platform,
|
||||
}) : _flutterVersion = flutterVersion,
|
||||
_config = config,
|
||||
_platform = platform;
|
||||
|
||||
final FlutterVersion _flutterVersion;
|
||||
final Config _config;
|
||||
final Platform _platform;
|
||||
@visibleForTesting
|
||||
mixin FlutterFeatureFlagsIsEnabled implements FeatureFlags {
|
||||
@protected
|
||||
Platform get platform;
|
||||
|
||||
@override
|
||||
bool get isLinuxEnabled => isEnabled(flutterLinuxDesktopFeature);
|
||||
@ -46,7 +40,7 @@ class FlutterFeatureFlags implements FeatureFlags {
|
||||
|
||||
@override
|
||||
bool get isCliAnimationEnabled {
|
||||
if (_platform.environment['TERM'] == 'dumb') {
|
||||
if (platform.environment['TERM'] == 'dumb') {
|
||||
return false;
|
||||
}
|
||||
return isEnabled(cliAnimation);
|
||||
@ -60,26 +54,34 @@ class FlutterFeatureFlags implements FeatureFlags {
|
||||
|
||||
@override
|
||||
bool get isExplicitPackageDependenciesEnabled => isEnabled(explicitPackageDependencies);
|
||||
}
|
||||
|
||||
interface class FlutterFeatureFlags with FlutterFeatureFlagsIsEnabled implements FeatureFlags {
|
||||
FlutterFeatureFlags({
|
||||
required FlutterVersion flutterVersion,
|
||||
required FlutterFeaturesConfig featuresConfig,
|
||||
required this.platform,
|
||||
}) : _flutterVersion = flutterVersion,
|
||||
_featuresConfig = featuresConfig;
|
||||
|
||||
final FlutterVersion _flutterVersion;
|
||||
final FlutterFeaturesConfig _featuresConfig;
|
||||
|
||||
@override
|
||||
@protected
|
||||
final Platform platform;
|
||||
|
||||
@override
|
||||
bool isEnabled(Feature feature) {
|
||||
final String currentChannel = _flutterVersion.channel;
|
||||
final FeatureChannelSetting featureSetting = feature.getSettingForChannel(currentChannel);
|
||||
|
||||
// If unavailable, then no setting can enable this feature.
|
||||
if (!featureSetting.available) {
|
||||
return false;
|
||||
}
|
||||
bool isEnabled = featureSetting.enabledByDefault;
|
||||
if (feature.configSetting != null) {
|
||||
final bool? configOverride = _config.getValue(feature.configSetting!) as bool?;
|
||||
if (configOverride != null) {
|
||||
isEnabled = configOverride;
|
||||
}
|
||||
}
|
||||
if (feature.environmentOverride != null) {
|
||||
if (_platform.environment[feature.environmentOverride]?.toLowerCase() == 'true') {
|
||||
isEnabled = true;
|
||||
}
|
||||
}
|
||||
return isEnabled;
|
||||
|
||||
// Otherwise, read it from environment variable > project manifest > global config
|
||||
return _featuresConfig.isEnabled(feature) ?? featureSetting.enabledByDefault;
|
||||
}
|
||||
}
|
||||
|
||||
168
packages/flutter_tools/lib/src/flutter_features_config.dart
Normal file
168
packages/flutter_tools/lib/src/flutter_features_config.dart
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'base/common.dart';
|
||||
import 'base/config.dart';
|
||||
import 'base/platform.dart';
|
||||
import 'features.dart';
|
||||
import 'flutter_manifest.dart';
|
||||
|
||||
/// Reads configuration flags to possibly override the default flag value.
|
||||
///
|
||||
/// See [isEnabled] for details on how feature flag values are resolved.
|
||||
interface class FlutterFeaturesConfig {
|
||||
/// Creates a feature configuration reader from the provided sources.
|
||||
///
|
||||
/// [globalConfig] reads values stored by the `flutter config` tool, which
|
||||
/// are normally in the user's `%HOME` directory (varies by system), while
|
||||
/// [projectManifest] reads values from the _current_ Flutter project's
|
||||
/// `pubspec.yaml`
|
||||
const FlutterFeaturesConfig({
|
||||
required Config globalConfig,
|
||||
required Platform platform,
|
||||
required FlutterManifest? projectManifest,
|
||||
}) : _globalConfig = globalConfig,
|
||||
_platform = platform,
|
||||
_projectManifest = projectManifest;
|
||||
|
||||
final Config _globalConfig;
|
||||
final Platform _platform;
|
||||
|
||||
// Can be null if no manifest file exists in the current directory.
|
||||
final FlutterManifest? _projectManifest;
|
||||
|
||||
/// Returns whether [feature] has been turned on/off from configuration.
|
||||
///
|
||||
/// If the feature was not configured, or cannot be configured, returns `null`.
|
||||
///
|
||||
/// The value is resolved, if possible, in the following order, where if a
|
||||
/// step resolves to a boolean value, no further steps are attempted:
|
||||
///
|
||||
///
|
||||
/// ## 1. Local Project Configuration
|
||||
///
|
||||
/// If [Feature.configSetting] is `null`, this step is skipped.
|
||||
///
|
||||
/// If the value defined by the key `$configSetting` is set in `pubspec.yaml`,
|
||||
/// it is returned as a boolean value.
|
||||
///
|
||||
/// Assuming there is a setting where `configSetting: 'enable-foo'`:
|
||||
///
|
||||
/// ```yaml
|
||||
/// # true
|
||||
/// flutter:
|
||||
/// config:
|
||||
/// enable-foo: true
|
||||
///
|
||||
/// # false
|
||||
/// flutter:
|
||||
/// config:
|
||||
/// enable-foo: false
|
||||
/// ```
|
||||
///
|
||||
/// ## 2. Global Tool Configuration
|
||||
///
|
||||
/// If [Feature.configSetting] is `null`, this step is skipped.
|
||||
///
|
||||
/// If the value defined by the key `$configSetting` is set in the global
|
||||
/// (platform dependent) configuration file, it is returned as a boolean
|
||||
/// value.
|
||||
///
|
||||
/// Assuming there is a setting where `configSetting: 'enable-foo'`:
|
||||
///
|
||||
/// ```sh
|
||||
/// # future runs will treat the value as true
|
||||
/// flutter config --enable-foo
|
||||
///
|
||||
/// # future runs will treat the value as false
|
||||
/// flutter config --no-enable-foo
|
||||
/// ```
|
||||
///
|
||||
/// ## 3. Environment Variable
|
||||
///
|
||||
/// If [Feature.environmentOverride] is `null`, this step is skipped.
|
||||
///
|
||||
/// If the value defined by the key `$environmentOverride` is equal to the
|
||||
/// string `'true'` (case insensitive), returns `true`, or `false` otherwise.
|
||||
///
|
||||
/// Assuming there is a flag where `environmentOverride: 'ENABLE_FOO'`:
|
||||
///
|
||||
/// ```sh
|
||||
/// # true
|
||||
/// ENABLE_FOO=true flutter some-command
|
||||
///
|
||||
/// # true
|
||||
/// ENABLE_FOO=TRUE flutter some-command
|
||||
///
|
||||
/// # false
|
||||
/// ENABLE_FOO=false flutter some-command
|
||||
///
|
||||
/// # false
|
||||
/// ENABLE_FOO=any-other-value flutter some-command
|
||||
/// ```
|
||||
bool? isEnabled(Feature feature) {
|
||||
return _isEnabledByConfigValue(feature) ?? _isEnabledByPlatformEnvironment(feature);
|
||||
}
|
||||
|
||||
bool? _isEnabledByConfigValue(Feature feature) {
|
||||
// If the feature cannot be configured by local/global config settings, return null.
|
||||
final String? featureName = feature.configSetting;
|
||||
if (featureName == null) {
|
||||
return null;
|
||||
}
|
||||
return _isEnabledAtProjectLevel(featureName) ?? _isEnabledByGlobalConfig(featureName);
|
||||
}
|
||||
|
||||
bool? _isEnabledByPlatformEnvironment(Feature feature) {
|
||||
// If the feature cannot be configured by an environment variable, return null.
|
||||
final String? environmentName = feature.environmentOverride;
|
||||
if (environmentName == null) {
|
||||
return null;
|
||||
}
|
||||
final Object? environmentValue = _platform.environment[environmentName]?.toLowerCase();
|
||||
if (environmentValue == null) {
|
||||
return null;
|
||||
}
|
||||
return environmentValue == 'true';
|
||||
}
|
||||
|
||||
bool? _isEnabledAtProjectLevel(String featureName) {
|
||||
final Object? configSection = _projectManifest?.flutterDescriptor['config'];
|
||||
if (configSection == null) {
|
||||
return null;
|
||||
}
|
||||
if (configSection is! Map) {
|
||||
throwToolExit(
|
||||
'The "config" property of "flutter" in pubspec.yaml must be a map, but '
|
||||
'got $configSection (${configSection.runtimeType})',
|
||||
);
|
||||
}
|
||||
return _requireBoolOrNull(
|
||||
configSection[featureName],
|
||||
featureName: featureName,
|
||||
source: '"flutter: config:" in pubspec.yaml',
|
||||
);
|
||||
}
|
||||
|
||||
bool? _isEnabledByGlobalConfig(String featureName) {
|
||||
return _requireBoolOrNull(
|
||||
_globalConfig.getValue(featureName),
|
||||
featureName: featureName,
|
||||
source: '"${_globalConfig.configPath}"',
|
||||
);
|
||||
}
|
||||
|
||||
static bool? _requireBoolOrNull(
|
||||
Object? value, {
|
||||
required String featureName,
|
||||
required String source,
|
||||
}) {
|
||||
if (value is bool?) {
|
||||
return value;
|
||||
}
|
||||
throwToolExit(
|
||||
'The "$featureName" property in $source must be a boolean, but got $value (${value.runtimeType})',
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -608,6 +608,9 @@ void _validateFlutter(YamlMap? yaml, List<String> errors) {
|
||||
errors.addAll(pluginErrors);
|
||||
case 'generate':
|
||||
break;
|
||||
case 'config':
|
||||
// Futher validation is defined in FlutterFeaturesConfig.
|
||||
break;
|
||||
case 'deferred-components':
|
||||
_validateDeferredComponents(kvp, errors);
|
||||
case 'disable-swift-package-manager':
|
||||
|
||||
@ -0,0 +1,169 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_tools/src/base/config.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/flutter_features_config.dart';
|
||||
import 'package:flutter_tools/src/flutter_manifest.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
|
||||
const Feature _noConfigFeature = Feature(name: 'example');
|
||||
const Feature _configOnlyFeature = Feature(name: 'example', configSetting: 'enable-flag');
|
||||
const Feature _envOnlyFeature = Feature(name: 'example', environmentOverride: 'ENABLE_FLAG');
|
||||
const Feature _configAndEnvFeature = Feature(
|
||||
name: 'example',
|
||||
configSetting: 'enable-flag',
|
||||
environmentOverride: 'ENABLE_FLAG',
|
||||
);
|
||||
|
||||
void main() {
|
||||
bool? isEnabled(
|
||||
Feature feature, {
|
||||
Map<String, String> environment = const <String, String>{},
|
||||
Map<String, Object> globalConfig = const <String, Object>{},
|
||||
String? projectManifest,
|
||||
}) {
|
||||
final Config globalConfigReader = Config.test();
|
||||
for (final MapEntry<String, Object>(:String key, :Object value) in globalConfig.entries) {
|
||||
globalConfigReader.setValue(key, value);
|
||||
}
|
||||
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final FlutterManifest? flutterManifest =
|
||||
projectManifest != null
|
||||
? FlutterManifest.createFromString(projectManifest, logger: logger)
|
||||
: FlutterManifest.empty(logger: logger);
|
||||
if (flutterManifest == null) {
|
||||
fail(logger.errorText);
|
||||
}
|
||||
|
||||
final FlutterFeaturesConfig featuresConfig = FlutterFeaturesConfig(
|
||||
globalConfig: globalConfigReader,
|
||||
platform: FakePlatform(environment: <String, String>{...environment}),
|
||||
projectManifest: flutterManifest,
|
||||
);
|
||||
return featuresConfig.isEnabled(feature);
|
||||
}
|
||||
|
||||
test('returns null if cannot be overriden', () {
|
||||
expect(
|
||||
isEnabled(
|
||||
_noConfigFeature,
|
||||
environment: <String, String>{'ENABLE_FLAG': 'true'},
|
||||
globalConfig: <String, Object>{'enable-flag': true},
|
||||
projectManifest: '''
|
||||
flutter:
|
||||
config:
|
||||
enable-flag: true
|
||||
''',
|
||||
),
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
|
||||
test('returns null if every source is omitted', () {
|
||||
expect(isEnabled(_configAndEnvFeature), isNull);
|
||||
});
|
||||
|
||||
test('overrides from local manifest', () {
|
||||
expect(
|
||||
isEnabled(
|
||||
_configOnlyFeature,
|
||||
projectManifest: '''
|
||||
flutter:
|
||||
config:
|
||||
enable-flag: true
|
||||
''',
|
||||
),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
test('local manifest config must be a map', () {
|
||||
expect(
|
||||
() => isEnabled(
|
||||
_configOnlyFeature,
|
||||
projectManifest: '''
|
||||
flutter:
|
||||
config: true
|
||||
''',
|
||||
),
|
||||
throwsToolExit(message: 'must be a map'),
|
||||
);
|
||||
});
|
||||
|
||||
test('local manifest value must be a boolean', () {
|
||||
expect(
|
||||
() => isEnabled(
|
||||
_configOnlyFeature,
|
||||
projectManifest: '''
|
||||
flutter:
|
||||
config:
|
||||
enable-flag: NOT-A-BOOLEAN
|
||||
''',
|
||||
),
|
||||
throwsToolExit(message: 'must be a boolean'),
|
||||
);
|
||||
});
|
||||
|
||||
test('overrides from global configuration', () {
|
||||
expect(
|
||||
isEnabled(_configOnlyFeature, globalConfig: <String, Object>{'enable-flag': true}),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
test('global configuration value must be a boolean', () {
|
||||
expect(
|
||||
() => isEnabled(
|
||||
_configOnlyFeature,
|
||||
globalConfig: <String, Object>{'enable-flag': 'NOT-A-BOOLEAN'},
|
||||
),
|
||||
throwsToolExit(message: 'must be a boolean'),
|
||||
);
|
||||
});
|
||||
|
||||
test('overrides from local manifest take precedence over global configuration', () {
|
||||
expect(
|
||||
isEnabled(
|
||||
_configOnlyFeature,
|
||||
projectManifest: '''
|
||||
flutter:
|
||||
config:
|
||||
enable-flag: true
|
||||
''',
|
||||
globalConfig: <String, Object>{'enable-flag': false},
|
||||
),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
test('overrides from environment', () {
|
||||
expect(
|
||||
isEnabled(_envOnlyFeature, environment: <String, String>{'ENABLE_FLAG': 'true'}),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
test('overrides from environment are case insensitive', () {
|
||||
expect(
|
||||
isEnabled(_envOnlyFeature, environment: <String, String>{'ENABLE_FLAG': 'tRuE'}),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
test('overrides from environment are lowest priority', () {
|
||||
expect(
|
||||
isEnabled(
|
||||
_configAndEnvFeature,
|
||||
environment: <String, String>{'ENABLE_FLAG': 'true'},
|
||||
globalConfig: <String, Object>{'enable-flag': false},
|
||||
),
|
||||
isFalse,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -2,43 +2,19 @@
|
||||
// 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/config.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/flutter_features.dart';
|
||||
import 'package:flutter_tools/src/flutter_features_config.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
import '../src/context.dart';
|
||||
import '../src/fakes.dart';
|
||||
|
||||
void main() {
|
||||
group('Features', () {
|
||||
late Config testConfig;
|
||||
late FakePlatform platform;
|
||||
late FlutterFeatureFlags featureFlags;
|
||||
|
||||
setUp(() {
|
||||
testConfig = Config.test();
|
||||
platform = FakePlatform(environment: <String, String>{});
|
||||
|
||||
for (final Feature feature in allConfigurableFeatures) {
|
||||
testConfig.setValue(feature.configSetting!, false);
|
||||
}
|
||||
|
||||
featureFlags = FlutterFeatureFlags(
|
||||
flutterVersion: FakeFlutterVersion(),
|
||||
config: testConfig,
|
||||
platform: platform,
|
||||
);
|
||||
});
|
||||
|
||||
FeatureFlags createFlags(String channel) {
|
||||
return FlutterFeatureFlags(
|
||||
flutterVersion: FakeFlutterVersion(branch: channel),
|
||||
config: testConfig,
|
||||
platform: platform,
|
||||
);
|
||||
}
|
||||
|
||||
testWithoutContext('setting has safe defaults', () {
|
||||
const FeatureChannelSetting featureSetting = FeatureChannelSetting();
|
||||
|
||||
@ -71,358 +47,416 @@ void main() {
|
||||
expect(feature.getSettingForChannel('unknown'), masterSetting);
|
||||
});
|
||||
|
||||
testWithoutContext('env variables are only enabled with "true" string', () {
|
||||
platform.environment = <String, String>{'FLUTTER_WEB': 'hello'};
|
||||
|
||||
expect(featureFlags.isWebEnabled, false);
|
||||
|
||||
platform.environment = <String, String>{'FLUTTER_WEB': 'true'};
|
||||
|
||||
expect(featureFlags.isWebEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter web help string', () {
|
||||
expect(flutterWebFeature.generateHelpMessage(), 'Enable or disable Flutter for web.');
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter macOS desktop help string', () {
|
||||
expect(
|
||||
flutterMacOSDesktopFeature.generateHelpMessage(),
|
||||
'Enable or disable support for desktop on macOS.',
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter Linux desktop help string', () {
|
||||
expect(
|
||||
flutterLinuxDesktopFeature.generateHelpMessage(),
|
||||
'Enable or disable support for desktop on Linux.',
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter Windows desktop help string', () {
|
||||
expect(
|
||||
flutterWindowsDesktopFeature.generateHelpMessage(),
|
||||
'Enable or disable support for desktop on Windows.',
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('help string on multiple channels', () {
|
||||
const Feature testWithoutContextFeature = Feature(
|
||||
testWithoutContext('reads from configuration if available', () {
|
||||
const Feature exampleFeature = Feature(
|
||||
name: 'example',
|
||||
master: FeatureChannelSetting(available: true),
|
||||
beta: FeatureChannelSetting(available: true),
|
||||
stable: FeatureChannelSetting(available: true),
|
||||
configSetting: 'foo',
|
||||
);
|
||||
|
||||
expect(testWithoutContextFeature.generateHelpMessage(), 'Enable or disable example.');
|
||||
final FlutterFeatureFlags flags = FlutterFeatureFlags(
|
||||
flutterVersion: FakeFlutterVersion(),
|
||||
featuresConfig: _FakeFeaturesConfig()..cannedResponse[exampleFeature] = true,
|
||||
platform: FakePlatform(),
|
||||
);
|
||||
expect(flags.isEnabled(exampleFeature), true);
|
||||
});
|
||||
|
||||
/// Flutter Web
|
||||
testWithoutContext('returns false if not available', () {
|
||||
const Feature exampleFeature = Feature(name: 'example');
|
||||
|
||||
testWithoutContext('Flutter web off by default on master', () {
|
||||
final FeatureFlags featureFlags = createFlags('master');
|
||||
|
||||
expect(featureFlags.isWebEnabled, false);
|
||||
final FlutterFeatureFlags flags = FlutterFeatureFlags(
|
||||
flutterVersion: FakeFlutterVersion(),
|
||||
featuresConfig: _FakeFeaturesConfig()..cannedResponse[exampleFeature] = true,
|
||||
platform: FakePlatform(),
|
||||
);
|
||||
expect(flags.isEnabled(exampleFeature), false);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter web enabled with config on master', () {
|
||||
final FeatureFlags featureFlags = createFlags('master');
|
||||
testConfig.setValue('enable-web', true);
|
||||
|
||||
expect(featureFlags.isWebEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter web enabled with environment variable on master', () {
|
||||
final FeatureFlags featureFlags = createFlags('master');
|
||||
platform.environment = <String, String>{'FLUTTER_WEB': 'true'};
|
||||
|
||||
expect(featureFlags.isWebEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter web off by default on beta', () {
|
||||
final FeatureFlags featureFlags = createFlags('beta');
|
||||
|
||||
expect(featureFlags.isWebEnabled, false);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter web enabled with config on beta', () {
|
||||
final FeatureFlags featureFlags = createFlags('beta');
|
||||
testConfig.setValue('enable-web', true);
|
||||
|
||||
expect(featureFlags.isWebEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter web not enabled with environment variable on beta', () {
|
||||
final FeatureFlags featureFlags = createFlags('beta');
|
||||
platform.environment = <String, String>{'FLUTTER_WEB': 'true'};
|
||||
|
||||
expect(featureFlags.isWebEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter web on by default on stable', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
testConfig.removeValue('enable-web');
|
||||
|
||||
expect(featureFlags.isWebEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter web enabled with config on stable', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
testConfig.setValue('enable-web', true);
|
||||
|
||||
expect(featureFlags.isWebEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter web not enabled with environment variable on stable', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
platform.environment = <String, String>{'FLUTTER_WEB': 'enabled'};
|
||||
|
||||
expect(featureFlags.isWebEnabled, false);
|
||||
});
|
||||
|
||||
/// Flutter macOS desktop.
|
||||
|
||||
testWithoutContext('Flutter macos desktop off by default on master', () {
|
||||
final FeatureFlags featureFlags = createFlags('master');
|
||||
|
||||
expect(featureFlags.isMacOSEnabled, false);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter macos desktop enabled with config on master', () {
|
||||
final FeatureFlags featureFlags = createFlags('master');
|
||||
testConfig.setValue('enable-macos-desktop', true);
|
||||
|
||||
expect(featureFlags.isMacOSEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter macos desktop enabled with environment variable on master', () {
|
||||
final FeatureFlags featureFlags = createFlags('master');
|
||||
platform.environment = <String, String>{'FLUTTER_MACOS': 'true'};
|
||||
|
||||
expect(featureFlags.isMacOSEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter macos desktop off by default on beta', () {
|
||||
final FeatureFlags featureFlags = createFlags('beta');
|
||||
|
||||
expect(featureFlags.isMacOSEnabled, false);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter macos desktop enabled with config on beta', () {
|
||||
final FeatureFlags featureFlags = createFlags('beta');
|
||||
testConfig.setValue('enable-macos-desktop', true);
|
||||
|
||||
expect(featureFlags.isMacOSEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter macos desktop enabled with environment variable on beta', () {
|
||||
final FeatureFlags featureFlags = createFlags('beta');
|
||||
platform.environment = <String, String>{'FLUTTER_MACOS': 'true'};
|
||||
|
||||
expect(featureFlags.isMacOSEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter macos desktop off by default on stable', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
|
||||
expect(featureFlags.isMacOSEnabled, false);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter macos desktop enabled with config on stable', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
testConfig.setValue('enable-macos-desktop', true);
|
||||
|
||||
expect(featureFlags.isMacOSEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter macos desktop enabled with environment variable on stable', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
platform.environment = <String, String>{'FLUTTER_MACOS': 'true'};
|
||||
|
||||
expect(featureFlags.isMacOSEnabled, true);
|
||||
});
|
||||
|
||||
/// Flutter Linux Desktop
|
||||
testWithoutContext('Flutter linux desktop off by default on master', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
|
||||
expect(featureFlags.isLinuxEnabled, false);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter linux desktop enabled with config on master', () {
|
||||
final FeatureFlags featureFlags = createFlags('master');
|
||||
testConfig.setValue('enable-linux-desktop', true);
|
||||
|
||||
expect(featureFlags.isLinuxEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter linux desktop enabled with environment variable on master', () {
|
||||
final FeatureFlags featureFlags = createFlags('master');
|
||||
platform.environment = <String, String>{'FLUTTER_LINUX': 'true'};
|
||||
|
||||
expect(featureFlags.isLinuxEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter linux desktop off by default on beta', () {
|
||||
final FeatureFlags featureFlags = createFlags('beta');
|
||||
|
||||
expect(featureFlags.isLinuxEnabled, false);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter linux desktop enabled with config on beta', () {
|
||||
final FeatureFlags featureFlags = createFlags('beta');
|
||||
testConfig.setValue('enable-linux-desktop', true);
|
||||
|
||||
expect(featureFlags.isLinuxEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter linux desktop enabled with environment variable on beta', () {
|
||||
final FeatureFlags featureFlags = createFlags('beta');
|
||||
platform.environment = <String, String>{'FLUTTER_LINUX': 'true'};
|
||||
|
||||
expect(featureFlags.isLinuxEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter linux desktop off by default on stable', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
|
||||
expect(featureFlags.isLinuxEnabled, false);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter linux desktop enabled with config on stable', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
testConfig.setValue('enable-linux-desktop', true);
|
||||
|
||||
expect(featureFlags.isLinuxEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter linux desktop enabled with environment variable on stable', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
platform.environment = <String, String>{'FLUTTER_LINUX': 'true'};
|
||||
|
||||
expect(featureFlags.isLinuxEnabled, true);
|
||||
});
|
||||
|
||||
/// Flutter Windows desktop.
|
||||
testWithoutContext('Flutter Windows desktop off by default on master', () {
|
||||
final FeatureFlags featureFlags = createFlags('master');
|
||||
|
||||
expect(featureFlags.isWindowsEnabled, false);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter Windows desktop enabled with config on master', () {
|
||||
final FeatureFlags featureFlags = createFlags('master');
|
||||
testConfig.setValue('enable-windows-desktop', true);
|
||||
|
||||
expect(featureFlags.isWindowsEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter Windows desktop enabled with environment variable on master', () {
|
||||
final FeatureFlags featureFlags = createFlags('master');
|
||||
platform.environment = <String, String>{'FLUTTER_WINDOWS': 'true'};
|
||||
|
||||
expect(featureFlags.isWindowsEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter Windows desktop off by default on beta', () {
|
||||
final FeatureFlags featureFlags = createFlags('beta');
|
||||
|
||||
expect(featureFlags.isWindowsEnabled, false);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter Windows desktop enabled with config on beta', () {
|
||||
final FeatureFlags featureFlags = createFlags('beta');
|
||||
testConfig.setValue('enable-windows-desktop', true);
|
||||
|
||||
expect(featureFlags.isWindowsEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter Windows desktop enabled with environment variable on beta', () {
|
||||
final FeatureFlags featureFlags = createFlags('beta');
|
||||
platform.environment = <String, String>{'FLUTTER_WINDOWS': 'true'};
|
||||
|
||||
expect(featureFlags.isWindowsEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter Windows desktop off by default on stable', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
|
||||
expect(featureFlags.isWindowsEnabled, false);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter Windows desktop enabled with config on stable', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
testConfig.setValue('enable-windows-desktop', true);
|
||||
|
||||
expect(featureFlags.isWindowsEnabled, true);
|
||||
});
|
||||
|
||||
testWithoutContext('Flutter Windows desktop enabled with environment variable on stable', () {
|
||||
final FeatureFlags featureFlags = createFlags('stable');
|
||||
platform.environment = <String, String>{'FLUTTER_WINDOWS': 'true'};
|
||||
|
||||
expect(featureFlags.isWindowsEnabled, true);
|
||||
});
|
||||
|
||||
for (final Feature feature in <Feature>[
|
||||
flutterWindowsDesktopFeature,
|
||||
flutterMacOSDesktopFeature,
|
||||
flutterLinuxDesktopFeature,
|
||||
]) {
|
||||
test('${feature.name} available and enabled by default on master', () {
|
||||
expect(feature.master.enabledByDefault, true);
|
||||
expect(feature.master.available, true);
|
||||
});
|
||||
test('${feature.name} available and enabled by default on beta', () {
|
||||
expect(feature.beta.enabledByDefault, true);
|
||||
expect(feature.beta.available, true);
|
||||
});
|
||||
test('${feature.name} available and enabled by default on stable', () {
|
||||
expect(feature.stable.enabledByDefault, true);
|
||||
expect(feature.stable.available, true);
|
||||
});
|
||||
FileSystem createFsWithPubspec() {
|
||||
final FileSystem fs = MemoryFileSystem.test();
|
||||
fs.currentDirectory.childFile('pubspec.yaml').writeAsStringSync('''
|
||||
flutter:
|
||||
config:
|
||||
enable-foo: true
|
||||
enable-bar: false
|
||||
enable-baz: true
|
||||
''');
|
||||
return fs;
|
||||
}
|
||||
|
||||
// Custom devices on all channels
|
||||
for (final String channel in <String>['master', 'beta', 'stable']) {
|
||||
testWithoutContext('Custom devices are enabled with flag on $channel', () {
|
||||
final FeatureFlags featureFlags = createFlags(channel);
|
||||
testConfig.setValue('enable-custom-devices', true);
|
||||
expect(featureFlags.areCustomDevicesEnabled, true);
|
||||
});
|
||||
testUsingContext(
|
||||
'FeatureFlags is influenced by the CWD',
|
||||
() {
|
||||
// This test intentionally uses Context, as featureFlags is read that way at runtime.
|
||||
final FeatureFlags featureFlagsFromContext = featureFlags;
|
||||
|
||||
testWithoutContext('Custom devices are enabled with environment variable on $channel', () {
|
||||
final FeatureFlags featureFlags = createFlags(channel);
|
||||
platform.environment = <String, String>{'FLUTTER_CUSTOM_DEVICES': 'true'};
|
||||
expect(featureFlags.areCustomDevicesEnabled, true);
|
||||
});
|
||||
}
|
||||
// Try a few flags that don't actually exist, but we want to check configuration more e2e-y.
|
||||
expect(
|
||||
featureFlagsFromContext.isEnabled(
|
||||
const Feature(
|
||||
name: 'foo',
|
||||
configSetting: 'enable-foo',
|
||||
master: FeatureChannelSetting(available: true),
|
||||
),
|
||||
),
|
||||
isTrue,
|
||||
reason: 'enable-foo: true is in pubspec.yaml',
|
||||
);
|
||||
|
||||
test('${nativeAssets.name} availability and default enabled', () {
|
||||
expect(nativeAssets.master.enabledByDefault, false);
|
||||
expect(nativeAssets.master.available, true);
|
||||
expect(nativeAssets.beta.enabledByDefault, false);
|
||||
expect(nativeAssets.beta.available, false);
|
||||
expect(nativeAssets.stable.enabledByDefault, false);
|
||||
expect(nativeAssets.stable.available, false);
|
||||
expect(
|
||||
featureFlagsFromContext.isEnabled(
|
||||
const Feature.fullyEnabled(name: 'bar', configSetting: 'enable-bar'),
|
||||
),
|
||||
isFalse,
|
||||
reason: 'enable-bar: false is in pubspec.yaml',
|
||||
);
|
||||
|
||||
expect(
|
||||
featureFlagsFromContext.isEnabled(
|
||||
const Feature(name: 'baz', configSetting: 'enable-baz'),
|
||||
),
|
||||
isFalse,
|
||||
reason: 'Is not available',
|
||||
);
|
||||
},
|
||||
overrides: <Type, Generator>{
|
||||
ProcessManager: FakeProcessManager.empty,
|
||||
FileSystem: createFsWithPubspec,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('Linux Destkop', () {
|
||||
test('is fully enabled', () {
|
||||
expect(flutterLinuxDesktopFeature, _isFullyEnabled);
|
||||
});
|
||||
|
||||
group('Swift Package Manager feature', () {
|
||||
test('availability and default enabled', () {
|
||||
expect(swiftPackageManager.master.enabledByDefault, false);
|
||||
expect(swiftPackageManager.master.available, true);
|
||||
expect(swiftPackageManager.beta.enabledByDefault, false);
|
||||
expect(swiftPackageManager.beta.available, true);
|
||||
expect(swiftPackageManager.stable.enabledByDefault, false);
|
||||
expect(swiftPackageManager.stable.available, true);
|
||||
});
|
||||
test('can be configured', () {
|
||||
expect(flutterLinuxDesktopFeature.configSetting, 'enable-linux-desktop');
|
||||
expect(flutterLinuxDesktopFeature.environmentOverride, 'FLUTTER_LINUX');
|
||||
});
|
||||
|
||||
test('can be enabled', () {
|
||||
platform.environment = <String, String>{'FLUTTER_SWIFT_PACKAGE_MANAGER': 'true'};
|
||||
test('forwards to isEnabled', () {
|
||||
final _TestIsGetterForwarding checkFlags = _TestIsGetterForwarding(
|
||||
shouldInvoke: flutterLinuxDesktopFeature,
|
||||
);
|
||||
expect(checkFlags.isLinuxEnabled, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
expect(featureFlags.isSwiftPackageManagerEnabled, isTrue);
|
||||
});
|
||||
group('MacOS Desktop', () {
|
||||
test('is fully enabled', () {
|
||||
expect(flutterMacOSDesktopFeature, _isFullyEnabled);
|
||||
});
|
||||
|
||||
test('can be configured', () {
|
||||
expect(flutterMacOSDesktopFeature.configSetting, 'enable-macos-desktop');
|
||||
expect(flutterMacOSDesktopFeature.environmentOverride, 'FLUTTER_MACOS');
|
||||
});
|
||||
|
||||
test('forwards to isEnabled', () {
|
||||
final _TestIsGetterForwarding checkFlags = _TestIsGetterForwarding(
|
||||
shouldInvoke: flutterMacOSDesktopFeature,
|
||||
);
|
||||
expect(checkFlags.isMacOSEnabled, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('Windows Desktop', () {
|
||||
test('is fully enabled', () {
|
||||
expect(flutterWindowsDesktopFeature, _isFullyEnabled);
|
||||
});
|
||||
|
||||
test('can be configured', () {
|
||||
expect(flutterWindowsDesktopFeature.configSetting, 'enable-windows-desktop');
|
||||
expect(flutterWindowsDesktopFeature.environmentOverride, 'FLUTTER_WINDOWS');
|
||||
});
|
||||
|
||||
test('forwards to isEnabled', () {
|
||||
final _TestIsGetterForwarding checkFlags = _TestIsGetterForwarding(
|
||||
shouldInvoke: flutterWindowsDesktopFeature,
|
||||
);
|
||||
expect(checkFlags.isWindowsEnabled, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('Web', () {
|
||||
test('is fully enabled', () {
|
||||
expect(flutterWebFeature, _isFullyEnabled);
|
||||
});
|
||||
|
||||
test('can be configured', () {
|
||||
expect(flutterWebFeature.configSetting, 'enable-web');
|
||||
expect(flutterWebFeature.environmentOverride, 'FLUTTER_WEB');
|
||||
});
|
||||
|
||||
test('forwards to isEnabled', () {
|
||||
final _TestIsGetterForwarding checkFlags = _TestIsGetterForwarding(
|
||||
shouldInvoke: flutterWebFeature,
|
||||
);
|
||||
expect(checkFlags.isWebEnabled, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('Android', () {
|
||||
test('is fully enabled', () {
|
||||
expect(flutterAndroidFeature, _isFullyEnabled);
|
||||
});
|
||||
|
||||
test('can be configured', () {
|
||||
expect(flutterAndroidFeature.configSetting, 'enable-android');
|
||||
expect(flutterAndroidFeature.environmentOverride, isNull);
|
||||
});
|
||||
|
||||
test('forwards to isEnabled', () {
|
||||
final _TestIsGetterForwarding checkFlags = _TestIsGetterForwarding(
|
||||
shouldInvoke: flutterAndroidFeature,
|
||||
);
|
||||
expect(checkFlags.isAndroidEnabled, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('iOS', () {
|
||||
test('is fully enabled', () {
|
||||
expect(flutterIOSFeature, _isFullyEnabled);
|
||||
});
|
||||
|
||||
test('can be configured', () {
|
||||
expect(flutterIOSFeature.configSetting, 'enable-ios');
|
||||
expect(flutterIOSFeature.environmentOverride, isNull);
|
||||
});
|
||||
|
||||
test('forwards to isEnabled', () {
|
||||
final _TestIsGetterForwarding checkFlags = _TestIsGetterForwarding(
|
||||
shouldInvoke: flutterIOSFeature,
|
||||
);
|
||||
expect(checkFlags.isIOSEnabled, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('Fuchsia', () {
|
||||
test('is only available on master', () {
|
||||
expect(
|
||||
flutterFuchsiaFeature,
|
||||
allOf(<Matcher>[
|
||||
_onChannelIs('master', available: true, enabledByDefault: false),
|
||||
_onChannelIs('stable', available: false, enabledByDefault: false),
|
||||
_onChannelIs('beta', available: false, enabledByDefault: false),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('can be configured', () {
|
||||
expect(flutterFuchsiaFeature.configSetting, 'enable-fuchsia');
|
||||
expect(flutterFuchsiaFeature.environmentOverride, 'FLUTTER_FUCHSIA');
|
||||
});
|
||||
|
||||
test('forwards to isEnabled', () {
|
||||
final _TestIsGetterForwarding checkFlags = _TestIsGetterForwarding(
|
||||
shouldInvoke: flutterFuchsiaFeature,
|
||||
);
|
||||
expect(checkFlags.isFuchsiaEnabled, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('Custom Devices', () {
|
||||
test('is always available but not enabled by default', () {
|
||||
expect(
|
||||
flutterCustomDevicesFeature,
|
||||
allOf(<Matcher>[
|
||||
_onChannelIs('master', available: true, enabledByDefault: false),
|
||||
_onChannelIs('stable', available: true, enabledByDefault: false),
|
||||
_onChannelIs('beta', available: true, enabledByDefault: false),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('can be configured', () {
|
||||
expect(flutterCustomDevicesFeature.configSetting, 'enable-custom-devices');
|
||||
expect(flutterCustomDevicesFeature.environmentOverride, 'FLUTTER_CUSTOM_DEVICES');
|
||||
});
|
||||
|
||||
test('forwards to isEnabled', () {
|
||||
final _TestIsGetterForwarding checkFlags = _TestIsGetterForwarding(
|
||||
shouldInvoke: flutterCustomDevicesFeature,
|
||||
);
|
||||
expect(checkFlags.areCustomDevicesEnabled, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('CLI Animations', () {
|
||||
test('is always enabled', () {
|
||||
expect(cliAnimation, _isFullyEnabled);
|
||||
});
|
||||
|
||||
test('can be disabled by TERM=dumb', () {
|
||||
final FlutterFeatureFlags features = FlutterFeatureFlags(
|
||||
flutterVersion: FakeFlutterVersion(),
|
||||
featuresConfig: _FakeFeaturesConfig(),
|
||||
platform: FakePlatform(environment: <String, String>{'TERM': 'dumb'}),
|
||||
);
|
||||
|
||||
expect(features.isCliAnimationEnabled, isFalse);
|
||||
});
|
||||
|
||||
test('forwards to isEnabled', () {
|
||||
final _TestIsGetterForwarding checkFlags = _TestIsGetterForwarding(
|
||||
shouldInvoke: cliAnimation,
|
||||
);
|
||||
expect(checkFlags.isCliAnimationEnabled, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('Native Assets', () {
|
||||
test('is available on master', () {
|
||||
expect(
|
||||
nativeAssets,
|
||||
allOf(<Matcher>[
|
||||
_onChannelIs('master', available: true, enabledByDefault: false),
|
||||
_onChannelIs('stable', available: false, enabledByDefault: false),
|
||||
_onChannelIs('beta', available: false, enabledByDefault: false),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('can be configured', () {
|
||||
expect(nativeAssets.configSetting, 'enable-native-assets');
|
||||
expect(nativeAssets.environmentOverride, 'FLUTTER_NATIVE_ASSETS');
|
||||
});
|
||||
|
||||
test('forwards to isEnabled', () {
|
||||
final _TestIsGetterForwarding checkFlags = _TestIsGetterForwarding(
|
||||
shouldInvoke: nativeAssets,
|
||||
);
|
||||
expect(checkFlags.isNativeAssetsEnabled, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('Swift Package Manager', () {
|
||||
test('is available on all channels', () {
|
||||
expect(
|
||||
swiftPackageManager,
|
||||
allOf(<Matcher>[
|
||||
_onChannelIs('master', available: true, enabledByDefault: false),
|
||||
_onChannelIs('stable', available: true, enabledByDefault: false),
|
||||
_onChannelIs('beta', available: true, enabledByDefault: false),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('can be configured', () {
|
||||
expect(swiftPackageManager.configSetting, 'enable-swift-package-manager');
|
||||
expect(swiftPackageManager.environmentOverride, 'FLUTTER_SWIFT_PACKAGE_MANAGER');
|
||||
});
|
||||
|
||||
test('forwards to isEnabled', () {
|
||||
final _TestIsGetterForwarding checkFlags = _TestIsGetterForwarding(
|
||||
shouldInvoke: swiftPackageManager,
|
||||
);
|
||||
expect(checkFlags.isSwiftPackageManagerEnabled, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('Explicit Package Dependencies', () {
|
||||
test('is fully enabled', () {
|
||||
expect(explicitPackageDependencies, _isFullyEnabled);
|
||||
});
|
||||
|
||||
test('can be configured', () {
|
||||
expect(explicitPackageDependencies.configSetting, 'explicit-package-dependencies');
|
||||
expect(explicitPackageDependencies.environmentOverride, isNull);
|
||||
});
|
||||
|
||||
test('forwards to isEnabled', () {
|
||||
final _TestIsGetterForwarding checkFlags = _TestIsGetterForwarding(
|
||||
shouldInvoke: explicitPackageDependencies,
|
||||
);
|
||||
expect(checkFlags.isExplicitPackageDependenciesEnabled, isTrue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
final class _FakeFeaturesConfig implements FlutterFeaturesConfig {
|
||||
final Map<Feature, bool?> cannedResponse = <Feature, bool?>{};
|
||||
|
||||
@override
|
||||
bool? isEnabled(Feature feature) => cannedResponse[feature];
|
||||
}
|
||||
|
||||
Matcher _onChannelIs(String channel, {required bool available, required bool enabledByDefault}) {
|
||||
return _FeaturesMatcher(
|
||||
channel: channel,
|
||||
available: available,
|
||||
enabledByDefault: enabledByDefault,
|
||||
);
|
||||
}
|
||||
|
||||
Matcher get _isFullyEnabled {
|
||||
return allOf(const <_FeaturesMatcher>[
|
||||
_FeaturesMatcher(channel: 'master', available: true, enabledByDefault: true),
|
||||
_FeaturesMatcher(channel: 'stable', available: true, enabledByDefault: true),
|
||||
_FeaturesMatcher(channel: 'beta', available: true, enabledByDefault: true),
|
||||
]);
|
||||
}
|
||||
|
||||
final class _FeaturesMatcher extends Matcher {
|
||||
const _FeaturesMatcher({
|
||||
required this.channel,
|
||||
required this.available,
|
||||
required this.enabledByDefault,
|
||||
});
|
||||
|
||||
final String channel;
|
||||
final bool available;
|
||||
final bool enabledByDefault;
|
||||
|
||||
@override
|
||||
Description describe(Description description) {
|
||||
description = description.add('feature on the "$channel" channel ');
|
||||
if (available) {
|
||||
description = description.add('is available ');
|
||||
} else {
|
||||
description = description.add('is not available');
|
||||
}
|
||||
description = description.add('and is ');
|
||||
if (enabledByDefault) {
|
||||
description = description.add('is enabled by default');
|
||||
} else {
|
||||
description = description.add('is not enabled by default');
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@override
|
||||
bool matches(Object? item, Map<Object?, Object?> matchState) {
|
||||
if (item is! Feature) {
|
||||
return false;
|
||||
}
|
||||
final FeatureChannelSetting setting = switch (channel) {
|
||||
'master' => item.master,
|
||||
'stable' => item.stable,
|
||||
'beta' => item.beta,
|
||||
_ => throw StateError('Invalid channel: "$channel"'),
|
||||
};
|
||||
if (setting.available != available) {
|
||||
return false;
|
||||
}
|
||||
if (setting.enabledByDefault != enabledByDefault) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
final class _TestIsGetterForwarding with FlutterFeatureFlagsIsEnabled {
|
||||
_TestIsGetterForwarding({required this.shouldInvoke});
|
||||
|
||||
final Feature shouldInvoke;
|
||||
@override
|
||||
final Platform platform = FakePlatform();
|
||||
|
||||
@override
|
||||
bool isEnabled(Feature feature) {
|
||||
return feature == shouldInvoke;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user