blob: 8eb7d7a3272f4f14aa8916aa5d1aec981840bf67 [file] [log] [blame]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. 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:analyzer/dart/analysis/features.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/context/packages.dart';
import 'package:analyzer/src/context/source.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/analysis/experiments_impl.dart';
import 'package:analyzer/src/dart/analysis/feature_set_provider.dart';
import 'package:analyzer/src/dart/sdk/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/source/package_map_resolver.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(FeatureSetProviderTest);
});
}
@reflectiveTest
class FeatureSetProviderTest with ResourceProviderMixin {
late SourceFactory sourceFactory;
late FeatureSetProvider provider;
Folder get sdkRoot => newFolder('/sdk');
void setUp() {
newFile('/test/lib/test.dart', '');
createMockSdk(
resourceProvider: resourceProvider,
root: sdkRoot,
);
_createSourceFactory();
}
test_getFeatureSet_allowedExperiments() {
var feature_a = ExperimentalFeature(
index: 0,
enableString: 'a',
isEnabledByDefault: false,
isExpired: false,
documentation: 'a',
experimentalReleaseVersion: null,
releaseVersion: null,
);
_newSdkExperimentsFile(r'''
{
"version": 1,
"experimentSets": {
"with_a": ["a"]
},
"sdk": {
"default": {
"experimentSet": "with_a"
}
},
"packages": {
"aaa": {
"experimentSet": "with_a"
}
}
}
''');
var packages = Packages(
{
'aaa': Package(
name: 'aaa',
rootFolder: newFolder('/packages/aaa'),
libFolder: newFolder('/packages/aaa/lib'),
languageVersion: null,
),
'bbb': Package(
name: 'bbb',
rootFolder: newFolder('/packages/bbb'),
libFolder: newFolder('/packages/bbb/lib'),
languageVersion: null,
),
},
);
_createSourceFactory(
packageUriResolver: _createPackageMapUriResolver(packages),
);
overrideKnownFeatures({'a': feature_a}, () {
provider = FeatureSetProvider.build(
sourceFactory: sourceFactory,
resourceProvider: resourceProvider,
packages: packages,
packageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
nonPackageDefaultLanguageVersion: ExperimentStatus.currentVersion,
nonPackageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
);
void assertHasFeature(String path, bool expected) {
_assertHasFeatureForPath(path, feature_a, expected);
}
assertHasFeature('/packages/aaa/lib/a.dart', true);
assertHasFeature('/packages/aaa/bin/b.dart', true);
assertHasFeature('/packages/aaa/test/c.dart', true);
assertHasFeature('/packages/bbb/lib/a.dart', false);
assertHasFeature('/packages/bbb/bin/b.dart', false);
assertHasFeature('/packages/bbb/test/c.dart', false);
assertHasFeature('/other/file.dart', false);
});
}
test_getFeatureSet_defaultForContext_hasExperiment() {
var feature_a = ExperimentalFeature(
index: 0,
enableString: 'a',
isEnabledByDefault: false,
isExpired: false,
documentation: 'a',
experimentalReleaseVersion: Version.parse('2.12.0'),
releaseVersion: null,
);
var packages = Packages(
{
'aaa': Package(
name: 'aaa',
rootFolder: newFolder('/packages/aaa'),
libFolder: newFolder('/packages/aaa/lib'),
languageVersion: null,
),
'bbb': Package(
name: 'bbb',
rootFolder: newFolder('/packages/bbb'),
libFolder: newFolder('/packages/bbb/lib'),
languageVersion: Version(2, 12, 0),
),
},
);
_createSourceFactory(
packageUriResolver: _createPackageMapUriResolver(packages),
);
overrideKnownFeatures({'a': feature_a}, () {
provider = FeatureSetProvider.build(
sourceFactory: sourceFactory,
resourceProvider: resourceProvider,
packages: packages,
packageDefaultFeatureSet: FeatureSet.fromEnableFlags2(
sdkLanguageVersion: Version.parse('2.12.0'),
flags: [feature_a.enableString],
),
nonPackageDefaultLanguageVersion: ExperimentStatus.currentVersion,
nonPackageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
);
void assertHasFeature(String path, bool expected) {
_assertHasFeatureForPath(path, feature_a, expected);
}
assertHasFeature('/packages/aaa/a.dart', true);
assertHasFeature('/packages/aaa/lib/b.dart', true);
assertHasFeature('/packages/aaa/test/c.dart', true);
assertHasFeature('/packages/bbb/a.dart', true);
assertHasFeature('/packages/bbb/lib/b.dart', true);
assertHasFeature('/packages/bbb/test/c.dart', true);
});
}
test_getFeatureSet_defaultForContext_noExperiments() {
var feature_a = ExperimentalFeature(
index: 0,
enableString: 'a',
isEnabledByDefault: false,
isExpired: false,
documentation: 'a',
experimentalReleaseVersion: Version.parse('2.12.0'),
releaseVersion: null,
);
var packages = Packages(
{
'aaa': Package(
name: 'aaa',
rootFolder: newFolder('/packages/aaa'),
libFolder: newFolder('/packages/aaa/lib'),
languageVersion: null,
),
'bbb': Package(
name: 'bbb',
rootFolder: newFolder('/packages/bbb'),
libFolder: newFolder('/packages/bbb/lib'),
languageVersion: Version(2, 12, 0),
),
},
);
_createSourceFactory(
packageUriResolver: _createPackageMapUriResolver(packages),
);
overrideKnownFeatures({'a': feature_a}, () {
provider = FeatureSetProvider.build(
sourceFactory: sourceFactory,
resourceProvider: resourceProvider,
packages: packages,
packageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
nonPackageDefaultLanguageVersion: ExperimentStatus.currentVersion,
nonPackageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
);
void assertHasFeature(String path, bool expected) {
_assertHasFeatureForPath(path, feature_a, expected);
}
assertHasFeature('/packages/aaa/a.dart', false);
assertHasFeature('/packages/aaa/lib/b.dart', false);
assertHasFeature('/packages/aaa/test/c.dart', false);
assertHasFeature('/packages/bbb/a.dart', false);
assertHasFeature('/packages/bbb/lib/b.dart', false);
assertHasFeature('/packages/bbb/test/c.dart', false);
});
}
test_packages_contextExperiments_nested() {
var packages = Packages(
{
'aaa': Package(
name: 'aaa',
rootFolder: getFolder('/packages/aaa'),
libFolder: getFolder('/packages/aaa/lib'),
languageVersion: Version.parse('2.5.0'),
),
'bbb': Package(
name: 'bbb',
rootFolder: getFolder('/packages/aaa/bbb'),
libFolder: getFolder('/packages/aaa/bbb/lib'),
languageVersion: Version.parse('2.6.0'),
),
'ccc': Package(
name: 'ccc',
rootFolder: getFolder('/packages/ccc'),
libFolder: getFolder('/packages/ccc/lib'),
languageVersion: Version.parse('2.7.0'),
),
},
);
provider = FeatureSetProvider.build(
sourceFactory: sourceFactory,
resourceProvider: resourceProvider,
packages: packages,
packageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
nonPackageDefaultLanguageVersion: ExperimentStatus.currentVersion,
nonPackageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
);
void check({
required String uriStr,
required String posixPath,
required Version expected,
}) {
var uri = Uri.parse(uriStr);
var path = convertPath(posixPath);
expect(
provider.getLanguageVersion(path, uri),
expected,
);
}
check(
uriStr: 'package:aaa/a.dart',
posixPath: '/packages/aaa/a.dart',
expected: Version.parse('2.5.0'),
);
check(
uriStr: toUriStr('/packages/aaa/test/a.dart'),
posixPath: '/packages/aaa/test/a.dart',
expected: Version.parse('2.5.0'),
);
check(
uriStr: 'package:bbb/b.dart',
posixPath: '/packages/aaa/bbb/b.dart',
expected: Version.parse('2.6.0'),
);
check(
uriStr: 'package:ccc/c.dart',
posixPath: '/packages/ccc/c.dart',
expected: Version.parse('2.7.0'),
);
check(
uriStr: 'package:ddd/a.dart',
posixPath: '/packages/ddd/d.dart',
expected: ExperimentStatus.currentVersion,
);
}
test_sdk_allowedExperiments_default() {
var feature_a = ExperimentalFeature(
index: 0,
enableString: 'a',
isEnabledByDefault: false,
isExpired: false,
documentation: 'a',
experimentalReleaseVersion: null,
releaseVersion: null,
);
_newSdkExperimentsFile(r'''
{
"version": 1,
"experimentSets": {
"with_a": ["a"]
},
"sdk": {
"default": {
"experimentSet": "with_a"
}
}
}
''');
overrideKnownFeatures({'a': feature_a}, () {
provider = FeatureSetProvider.build(
sourceFactory: sourceFactory,
resourceProvider: resourceProvider,
packages: findPackagesFrom(resourceProvider, getFolder('/test')),
packageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
nonPackageDefaultLanguageVersion: ExperimentStatus.currentVersion,
nonPackageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
);
var core_featureSet = _getSdkFeatureSet('dart:core');
expect(core_featureSet.isEnabled(feature_a), isTrue);
var math_featureSet = _getSdkFeatureSet('dart:math');
expect(math_featureSet.isEnabled(feature_a), isTrue);
});
}
test_sdk_allowedExperiments_library() {
var feature_a = ExperimentalFeature(
index: 0,
enableString: 'a',
isEnabledByDefault: false,
isExpired: false,
documentation: 'a',
experimentalReleaseVersion: null,
releaseVersion: null,
);
_newSdkExperimentsFile(r'''
{
"version": 1,
"experimentSets": {
"none": [],
"with_a": ["a"]
},
"sdk": {
"default": {
"experimentSet": "none"
},
"libraries": {
"math": {
"experimentSet": "with_a"
}
}
}
}
''');
overrideKnownFeatures({'a': feature_a}, () {
provider = FeatureSetProvider.build(
sourceFactory: sourceFactory,
resourceProvider: resourceProvider,
packages: findPackagesFrom(resourceProvider, getFolder('/test')),
packageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
nonPackageDefaultLanguageVersion: ExperimentStatus.currentVersion,
nonPackageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
);
var core_featureSet = _getSdkFeatureSet('dart:core');
expect(core_featureSet.isEnabled(feature_a), isFalse);
var math_featureSet = _getSdkFeatureSet('dart:math');
expect(math_featureSet.isEnabled(feature_a), isTrue);
});
}
test_sdk_allowedExperiments_mockDefault() {
provider = FeatureSetProvider.build(
sourceFactory: sourceFactory,
resourceProvider: resourceProvider,
packages: findPackagesFrom(resourceProvider, getFolder('/test')),
packageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
nonPackageDefaultLanguageVersion: ExperimentStatus.currentVersion,
nonPackageDefaultFeatureSet: FeatureSet.latestLanguageVersion(),
);
var featureSet = _getSdkFeatureSet('dart:math');
expect(featureSet.isEnabled(Feature.non_nullable), isTrue);
}
_assertHasFeatureForPath(String path, Feature feature, bool expected) {
var featureSet = _getPathFeatureSet(path);
expect(featureSet.isEnabled(feature), expected);
}
PackageMapUriResolver _createPackageMapUriResolver(Packages packages) {
var map = <String, List<Folder>>{};
for (var package in packages.packages) {
map[package.name] = [package.libFolder];
}
return PackageMapUriResolver(resourceProvider, map);
}
void _createSourceFactory({UriResolver? packageUriResolver}) {
var resolvers = <UriResolver>[];
if (packageUriResolver != null) {
resolvers.add(packageUriResolver);
}
resolvers.addAll([
DartUriResolver(
FolderBasedDartSdk(resourceProvider, sdkRoot),
),
ResourceUriResolver(resourceProvider),
]);
sourceFactory = SourceFactoryImpl(resolvers);
}
FeatureSet _getPathFeatureSet(String path) {
path = convertPath(path);
var uri = sourceFactory.pathToUri(path)!;
return provider.getFeatureSet(path, uri);
}
FeatureSet _getSdkFeatureSet(String uriStr) {
var uri = Uri.parse(uriStr);
var path = sourceFactory.forUri2(uri)!.fullName;
return provider.getFeatureSet(path, uri);
}
void _newSdkExperimentsFile(String content) {
newFile('$sdkRoot/lib/_internal/allowed_experiments.json', content);
}
}