blob: 7b32b4899e02dc31c448c89d0b8d14b6a62e9345 [file] [log] [blame]
// Copyright (c) 2018, 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/context_root.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/src/dart/analysis/context_locator.dart';
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
import 'package:analyzer/src/utilities/extensions/file_system.dart';
import 'package:analyzer/src/workspace/basic.dart';
import 'package:analyzer/src/workspace/pub.dart';
import 'package:analyzer/src/workspace/workspace.dart';
import 'package:linter/src/rules.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../util/tree_string_sink.dart';
import '../resolution/context_collection_resolution.dart';
import '../resolution/node_text_expectations.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(AnalysisContextCollectionLowTest);
defineReflectiveTests(
AnalysisContextCollectionLowTest_SingleOptionsPerContext);
defineReflectiveTests(AnalysisContextCollectionTest);
defineReflectiveTests(
AnalysisContextCollectionTest_SingleOptionsPerContext);
defineReflectiveTests(UpdateNodeTextExpectations);
});
}
/// Cached state to restore on test tearDown.
final _singleOptionsContextsDefault = ContextLocatorImpl.singleOptionContexts;
@reflectiveTest
class AnalysisContextCollectionLowTest
with ResourceProviderMixin, AnalysisContextCollectionLowTestMixin {}
/// To be removed when `singleOptionContexts` defaults to false.
@reflectiveTest
class AnalysisContextCollectionLowTest_SingleOptionsPerContext
with ResourceProviderMixin, AnalysisContextCollectionLowTestMixin {
@override
bool get enableSingleOptionContexts => true;
@override
test_new_outer_inner() {
var outerFolder = newFolder('/test/outer');
newFile('/test/outer/lib/outer.dart', '');
var innerFolder = newFolder('/test/outer/inner');
newAnalysisOptionsYamlFile('/test/outer/inner', '');
newFile('/test/outer/inner/inner.dart', '');
var collection = _newCollection(includedPaths: [outerFolder.path]);
expect(collection.contexts, hasLength(2));
var outerContext = collection.contexts
.singleWhere((c) => c.contextRoot.root == outerFolder);
var innerContext = collection.contexts
.singleWhere((c) => c.contextRoot.root == innerFolder);
expect(innerContext, isNot(same(outerContext)));
// Outer and inner contexts own corresponding files.
expect(collection.contextFor(convertPath('/test/outer/lib/outer.dart')),
same(outerContext));
expect(collection.contextFor(convertPath('/test/outer/inner/inner.dart')),
same(innerContext));
// The file does not have to exist, during creation, or at all.
expect(collection.contextFor(convertPath('/test/outer/lib/outer2.dart')),
same(outerContext));
expect(collection.contextFor(convertPath('/test/outer/inner/inner2.dart')),
same(innerContext));
}
}
mixin AnalysisContextCollectionLowTestMixin on ResourceProviderMixin {
bool get enableSingleOptionContexts => false;
Folder get sdkRoot => newFolder('/sdk');
void setUp() {
ContextLocatorImpl.singleOptionContexts = enableSingleOptionContexts;
createMockSdk(
resourceProvider: resourceProvider,
root: sdkRoot,
);
registerLintRules();
}
void tearDown() {
ContextLocatorImpl.singleOptionContexts = _singleOptionsContextsDefault;
}
test_contextFor_noContext() {
var collection = _newCollection(includedPaths: [convertPath('/root')]);
expect(
() => collection.contextFor(convertPath('/other/test.dart')),
throwsStateError,
);
}
test_contextFor_notAbsolute() {
var collection = _newCollection(includedPaths: [convertPath('/root')]);
expect(
() => collection.contextFor(convertPath('test.dart')),
throwsArgumentError,
);
}
test_contextFor_notNormalized() {
var collection = _newCollection(includedPaths: [convertPath('/root')]);
expect(
() => collection.contextFor(convertPath('/test/lib/../lib/test.dart')),
throwsArgumentError,
);
}
test_new_analysisOptions_includes() {
var rootFolder = newFolder('/home/test');
var fooFolder = newFolder('/home/packages/foo');
newFile('${fooFolder.path}/lib/included.yaml', r'''
linter:
rules:
- empty_statements
''');
var packageConfigFileBuilder = PackageConfigFileBuilder()
..add(name: 'foo', rootPath: fooFolder.path);
newPackageConfigJsonFile(
rootFolder.path,
packageConfigFileBuilder.toContent(toUriStr: toUriStr),
);
var optionsFile = newAnalysisOptionsYamlFile(rootFolder.path, r'''
include: package:foo/included.yaml
linter:
rules:
- unnecessary_parenthesis
''');
var collection = _newCollection(includedPaths: [rootFolder.path]);
var analysisContext = collection.contextFor(rootFolder.path);
var analysisOptions =
analysisContext.getAnalysisOptionsForFile(optionsFile);
expect(
analysisOptions.lintRules.map((e) => e.name),
unorderedEquals(['empty_statements', 'unnecessary_parenthesis']),
);
}
test_new_analysisOptions_lintRules() {
var rootFolder = newFolder('/home/test');
var optionsFile = newAnalysisOptionsYamlFile(rootFolder.path, r'''
linter:
rules:
- non_existent_lint_rule
- unnecessary_parenthesis
''');
var collection = _newCollection(includedPaths: [rootFolder.path]);
var analysisContext = collection.contextFor(rootFolder.path);
var analysisOptions =
analysisContext.getAnalysisOptionsForFile(optionsFile);
expect(
analysisOptions.lintRules.map((e) => e.name),
unorderedEquals(['unnecessary_parenthesis']),
);
}
test_new_includedPaths_notAbsolute() {
expect(
() => AnalysisContextCollectionImpl(includedPaths: ['root']),
throwsArgumentError,
);
}
test_new_includedPaths_notNormalized() {
expect(
() => AnalysisContextCollectionImpl(
includedPaths: [convertPath('/root/lib/../lib')]),
throwsArgumentError,
);
}
test_new_outer_inner() {
var outerFolder = newFolder('/test/outer');
newFile('/test/outer/lib/outer.dart', '');
newFolder('/test/outer/inner');
newAnalysisOptionsYamlFile('/test/outer/inner', '');
newFile('/test/outer/inner/inner.dart', '');
var collection = _newCollection(includedPaths: [outerFolder.path]);
expect(collection.contexts, hasLength(1));
}
test_new_sdkPath_notAbsolute() {
expect(
() => AnalysisContextCollectionImpl(
includedPaths: ['/root'], sdkPath: 'sdk'),
throwsArgumentError,
);
}
test_new_sdkPath_notNormalized() {
expect(
() => AnalysisContextCollectionImpl(
includedPaths: [convertPath('/root')], sdkPath: '/home/sdk/../sdk'),
throwsArgumentError,
);
}
AnalysisContextCollectionImpl _newCollection(
{required List<String> includedPaths}) {
return AnalysisContextCollectionImpl(
resourceProvider: resourceProvider,
includedPaths: includedPaths,
sdkPath: sdkRoot.path,
);
}
}
@reflectiveTest
class AnalysisContextCollectionTest
with ResourceProviderMixin, AnalysisContextCollectionTestMixin {}
/// To be removed when `singleOptionContexts` defaults to false.
@reflectiveTest
class AnalysisContextCollectionTest_SingleOptionsPerContext
with ResourceProviderMixin, AnalysisContextCollectionTestMixin {
@override
bool get enableSingleOptionContexts => true;
@override
test_packageConfigWorkspace_multipleAnalysisOptions() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newAnalysisOptionsYamlFile(testPackageRootPath, '');
newFile('$testPackageLibPath/a.dart', '');
final nestedPath = '$testPackageLibPath/nested';
newAnalysisOptionsYamlFile(nestedPath, '');
newFile('$nestedPath/b.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/lib/nested
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_1
analyzedFiles
/home/test/lib/nested/b.dart
uri: package:test/nested/b.dart
analysisOptions_1
workspacePackage_1_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
analysisOptions_1: /home/test/lib/nested/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
workspace_1: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_1_0: PubPackage
root: /home/test
''');
}
@override
test_packageConfigWorkspace_multipleAnalysisOptions_nestedExclude() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newAnalysisOptionsYamlFile(testPackageRootPath, '');
newFile('$testPackageLibPath/a.dart', '');
final nestedPath = '$testPackageLibPath/nested';
newAnalysisOptionsYamlFile(nestedPath, r'''
analyzer:
exclude:
- excluded/**
''');
newFile('$nestedPath/b.dart', '');
newFile('$nestedPath/excluded/b.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/lib/nested
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_1
analyzedFiles
/home/test/lib/nested/b.dart
uri: package:test/nested/b.dart
analysisOptions_1
workspacePackage_1_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
analysisOptions_1: /home/test/lib/nested/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
workspace_1: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_1_0: PubPackage
root: /home/test
''');
}
@override
test_packageConfigWorkspace_multipleAnalysisOptions_nestedNestedExclude() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newAnalysisOptionsYamlFile(testPackageRootPath, '');
newFile('$testPackageLibPath/a.dart', '');
final nestedPath = '$testPackageLibPath/nested';
newAnalysisOptionsYamlFile(nestedPath, r'''
analyzer:
exclude:
- excluded/**
''');
newFile('$nestedPath/b.dart', '');
newFile('$nestedPath/excluded/b.dart', '');
final nestedNestedPath = '$nestedPath/nested';
newAnalysisOptionsYamlFile(nestedNestedPath, r'''
analyzer:
exclude:
- excluded/**
''');
newFile('$nestedNestedPath/c.dart', '');
newFile('$nestedNestedPath/excluded/d.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/lib/nested
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_1
analyzedFiles
/home/test/lib/nested/b.dart
uri: package:test/nested/b.dart
analysisOptions_1
workspacePackage_1_0
/home/test/lib/nested/nested
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_2
analyzedFiles
/home/test/lib/nested/nested/c.dart
uri: package:test/nested/nested/c.dart
analysisOptions_2
workspacePackage_2_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
analysisOptions_1: /home/test/lib/nested/analysis_options.yaml
analysisOptions_2: /home/test/lib/nested/nested/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
workspace_1: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_1_0: PubPackage
root: /home/test
workspace_2: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_2_0: PubPackage
root: /home/test
''');
}
@override
test_packageConfigWorkspace_multipleAnalysisOptions_outerExclude() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newAnalysisOptionsYamlFile(testPackageRootPath, r'''
analyzer:
exclude:
- excluded/**
''');
newFile('$testPackageLibPath/a.dart', '');
newFile('$testPackageRootPath/excluded/b.dart', '');
final nestedPath = '$testPackageLibPath/nested';
newAnalysisOptionsYamlFile(nestedPath, '');
newFile('$nestedPath/b.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/lib/nested
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_1
analyzedFiles
/home/test/lib/nested/b.dart
uri: package:test/nested/b.dart
analysisOptions_1
workspacePackage_1_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
analysisOptions_1: /home/test/lib/nested/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
workspace_1: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_1_0: PubPackage
root: /home/test
''');
}
@override
test_packageConfigWorkspace_multipleAnalysisOptions_overridingOptions() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
var rootOptionsFile = newAnalysisOptionsYamlFile(testPackageRootPath, '');
newFile('$testPackageLibPath/a.dart', '');
final nestedPath = '$testPackageLibPath/nested';
newAnalysisOptionsYamlFile(nestedPath, '');
newFile('$nestedPath/b.dart', '');
// Verify that despite the nested options file
// (/home/test/nested/analysis_options.yaml), the nested file gets analyzed
// with the outer one (/home/test/analysis_options.yaml) as passed into
// the AnalysisContextCollection.
_assertWorkspaceCollectionText(
workspaceRootPath, optionsFile: rootOptionsFile, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/lib/nested/b.dart
uri: package:test/nested/b.dart
analysisOptions_0
workspacePackage_0_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
''');
}
@override
test_packageConfigWorkspace_singleAnalysisOptions_multipleContexts() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newAnalysisOptionsYamlFile(testPackageRootPath, '');
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newFile('$testPackageLibPath/a.dart', '');
final nestedPackageRootPath = '$testPackageRootPath/nested';
newPubspecYamlFile(nestedPackageRootPath, r'''
name: nested
''');
newSinglePackageConfigJsonFile(
packagePath: nestedPackageRootPath,
name: 'nested',
);
newFile('$nestedPackageRootPath/lib/b.dart', '');
// TODO(pq): there should only be one shared options instance
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/nested
packagesFile: /home/test/nested/.dart_tool/package_config.json
workspace: workspace_1
analyzedFiles
/home/test/nested/lib/b.dart
uri: package:nested/b.dart
analysisOptions_1
workspacePackage_1_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
analysisOptions_1: /home/test/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
workspace_1: PackageConfigWorkspace
root: /home/test/nested
pubPackages
workspacePackage_1_0: PubPackage
root: /home/test/nested
''');
}
}
mixin AnalysisContextCollectionTestMixin on ResourceProviderMixin {
final _AnalysisContextCollectionPrinterConfiguration configuration =
_AnalysisContextCollectionPrinterConfiguration();
bool get enableSingleOptionContexts => false;
Folder get sdkRoot => newFolder('/sdk');
void setUp() {
ContextLocatorImpl.singleOptionContexts = enableSingleOptionContexts;
createMockSdk(
resourceProvider: resourceProvider,
root: sdkRoot,
);
registerLintRules();
}
void tearDown() {
ContextLocatorImpl.singleOptionContexts = _singleOptionsContextsDefault;
}
test_basicWorkspace() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
newFile('$testPackageRootPath/lib/a.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
workspacePackage_0_0
workspaces
workspace_0: BasicWorkspace
root: /home
workspacePackage_0_0
''');
}
test_packageConfigWorkspace_enabledExperiment() async {
configuration.showEnabledFeatures = true;
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newAnalysisOptionsYamlFile(testPackageRootPath, '');
newFile('$testPackageLibPath/a.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, updateAnalysisOptions: (
{required AnalysisOptions analysisOptions,
required ContextRoot contextRoot,
required DartSdk sdk}) {
(analysisOptions as AnalysisOptionsImpl).contextFeatures =
FeatureSet.fromEnableFlags2(
sdkLanguageVersion: ExperimentStatus.currentVersion,
flags: ['variance'],
);
}, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
features
class-modifiers
constant-update-2018
constructor-tearoffs
control-flow-collections
enhanced-enums
extension-methods
generic-metadata
inference-update-1
inference-update-2
inference-update-3
inline-class
named-arguments-anywhere
non-nullable
nonfunction-type-aliases
patterns
records
sealed-class
set-literals
spread-collections
super-parameters
triple-shift
unnamed-libraries
variance
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
''');
}
test_packageConfigWorkspace_enabledExperiment_noAnalysisOptionsFile() async {
configuration.withAnalysisOptionsWithoutFiles = true;
configuration.showEnabledFeatures = true;
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newFile('$testPackageLibPath/a.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, updateAnalysisOptions: (
{required AnalysisOptions analysisOptions,
required ContextRoot contextRoot,
required DartSdk sdk}) {
(analysisOptions as AnalysisOptionsImpl).contextFeatures =
FeatureSet.fromEnableFlags2(
sdkLanguageVersion: ExperimentStatus.currentVersion,
flags: ['variance'],
);
}, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
analysisOptions
analysisOptions_0: <no file>
features
class-modifiers
constant-update-2018
constructor-tearoffs
control-flow-collections
enhanced-enums
extension-methods
generic-metadata
inference-update-1
inference-update-2
inference-update-3
inline-class
named-arguments-anywhere
non-nullable
nonfunction-type-aliases
patterns
records
sealed-class
set-literals
spread-collections
super-parameters
triple-shift
unnamed-libraries
variance
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
''');
}
test_packageConfigWorkspace_multipleAnalysisOptions() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newAnalysisOptionsYamlFile(testPackageRootPath, '');
newFile('$testPackageLibPath/a.dart', '');
final nestedPath = '$testPackageLibPath/nested';
newAnalysisOptionsYamlFile(nestedPath, '');
newFile('$nestedPath/b.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/lib/nested/b.dart
uri: package:test/nested/b.dart
analysisOptions_1
workspacePackage_0_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
analysisOptions_1: /home/test/lib/nested/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
''');
}
test_packageConfigWorkspace_multipleAnalysisOptions_nestedExclude() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newAnalysisOptionsYamlFile(testPackageRootPath, '');
newFile('$testPackageLibPath/a.dart', '');
final nestedPath = '$testPackageLibPath/nested';
newAnalysisOptionsYamlFile(nestedPath, r'''
analyzer:
exclude:
- excluded/**
''');
newFile('$nestedPath/b.dart', '');
newFile('$nestedPath/excluded/b.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/lib/nested/b.dart
uri: package:test/nested/b.dart
analysisOptions_1
workspacePackage_0_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
analysisOptions_1: /home/test/lib/nested/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
''');
}
test_packageConfigWorkspace_multipleAnalysisOptions_nestedNestedExclude() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newAnalysisOptionsYamlFile(testPackageRootPath, '');
newFile('$testPackageLibPath/a.dart', '');
final nestedPath = '$testPackageLibPath/nested';
newAnalysisOptionsYamlFile(nestedPath, r'''
analyzer:
exclude:
- excluded/**
''');
newFile('$nestedPath/b.dart', '');
newFile('$nestedPath/excluded/b.dart', '');
final nestedNestedPath = '$nestedPath/nested';
newAnalysisOptionsYamlFile(nestedNestedPath, r'''
analyzer:
exclude:
- excluded2/**
''');
newFile('$nestedNestedPath/c.dart', '');
newFile('$nestedNestedPath/excluded2/d.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/lib/nested/b.dart
uri: package:test/nested/b.dart
analysisOptions_1
workspacePackage_0_0
/home/test/lib/nested/nested/c.dart
uri: package:test/nested/nested/c.dart
analysisOptions_2
workspacePackage_0_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
analysisOptions_1: /home/test/lib/nested/analysis_options.yaml
analysisOptions_2: /home/test/lib/nested/nested/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
''');
}
test_packageConfigWorkspace_multipleAnalysisOptions_outerExclude() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newAnalysisOptionsYamlFile(testPackageRootPath, r'''
analyzer:
exclude:
- excluded/**
''');
newFile('$testPackageLibPath/a.dart', '');
newFile('$testPackageRootPath/excluded/b.dart', '');
final nestedPath = '$testPackageLibPath/nested';
newAnalysisOptionsYamlFile(nestedPath, '');
newFile('$nestedPath/b.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/lib/nested/b.dart
uri: package:test/nested/b.dart
analysisOptions_1
workspacePackage_0_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
analysisOptions_1: /home/test/lib/nested/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
''');
}
test_packageConfigWorkspace_multipleAnalysisOptions_overridingOptions() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
var rootOptionsFile = newAnalysisOptionsYamlFile(testPackageRootPath, '');
newFile('$testPackageLibPath/a.dart', '');
final nestedPath = '$testPackageLibPath/nested';
newAnalysisOptionsYamlFile(nestedPath, '');
newFile('$nestedPath/b.dart', '');
// Verify that despite the nested options file
// (/home/test/nested/analysis_options.yaml), the nested file gets analyzed
// with the outer one (/home/test/analysis_options.yaml) as passed into
// the AnalysisContextCollection.
_assertWorkspaceCollectionText(
workspaceRootPath, optionsFile: rootOptionsFile, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/lib/nested/b.dart
uri: package:test/nested/b.dart
analysisOptions_0
workspacePackage_0_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
''');
}
test_packageConfigWorkspace_multipleAnalysisOptions_overridingOptions_outsideWorspaceRoot() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
var definedOptionsFile = newAnalysisOptionsYamlFile('/outside', '');
newFile('$testPackageLibPath/a.dart', '');
final nestedPath = '$testPackageLibPath/nested';
newAnalysisOptionsYamlFile(nestedPath, '');
newFile('$nestedPath/b.dart', '');
// Verify that despite the nested options file
// (/home/test/nested/analysis_options.yaml), the nested file gets analyzed
// with the defined one which is outside the workspace
// (/outside/analysis_options.yaml) as passed into the
// AnalysisContextCollection.
_assertWorkspaceCollectionText(
workspaceRootPath, optionsFile: definedOptionsFile, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/lib/nested/b.dart
uri: package:test/nested/b.dart
analysisOptions_0
workspacePackage_0_0
analysisOptions
analysisOptions_0: /outside/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
''');
}
test_packageConfigWorkspace_multiplePackageConfigs() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newFile('$testPackageLibPath/a.dart', '');
final nestedPackageRootPath = '$testPackageRootPath/nested';
newPubspecYamlFile(nestedPackageRootPath, r'''
name: nested
''');
newSinglePackageConfigJsonFile(
packagePath: nestedPackageRootPath,
name: 'nested',
);
newFile('$nestedPackageRootPath/lib/b.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
workspacePackage_0_0
/home/test/nested
packagesFile: /home/test/nested/.dart_tool/package_config.json
workspace: workspace_1
analyzedFiles
/home/test/nested/lib/b.dart
uri: package:nested/b.dart
workspacePackage_1_0
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
workspace_1: PackageConfigWorkspace
root: /home/test/nested
pubPackages
workspacePackage_1_0: PubPackage
root: /home/test/nested
''');
}
test_packageConfigWorkspace_sdkVersionConstraint() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
newPubspecYamlFile(testPackageRootPath, r'''
environment:
sdk: ^3.0.0
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newFile('$testPackageRootPath/lib/a.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
workspacePackage_0_0
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
sdkVersionConstraint: ^3.0.0
''');
}
test_packageConfigWorkspace_singleAnalysisOptions() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newAnalysisOptionsYamlFile(testPackageRootPath, '');
newFile('$testPackageLibPath/a.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
''');
}
test_packageConfigWorkspace_singleAnalysisOptions_exclude() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newAnalysisOptionsYamlFile(testPackageRootPath, r'''
analyzer:
exclude:
- lib/nested/**
''');
newFile('$testPackageLibPath/a.dart', '');
final nestedPath = '$testPackageLibPath/nested';
newFile('$nestedPath/b.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
''');
}
test_packageConfigWorkspace_singleAnalysisOptions_multipleContexts() async {
final workspaceRootPath = '/home';
final testPackageRootPath = '$workspaceRootPath/test';
final testPackageLibPath = '$testPackageRootPath/lib';
newAnalysisOptionsYamlFile(testPackageRootPath, '');
newPubspecYamlFile(testPackageRootPath, r'''
name: test
''');
newSinglePackageConfigJsonFile(
packagePath: testPackageRootPath,
name: 'test',
);
newFile('$testPackageLibPath/a.dart', '');
final nestedPackageRootPath = '$testPackageRootPath/nested';
newPubspecYamlFile(nestedPackageRootPath, r'''
name: nested
''');
newSinglePackageConfigJsonFile(
packagePath: nestedPackageRootPath,
name: 'nested',
);
newFile('$nestedPackageRootPath/lib/b.dart', '');
_assertWorkspaceCollectionText(workspaceRootPath, r'''
contexts
/home/test
packagesFile: /home/test/.dart_tool/package_config.json
workspace: workspace_0
analyzedFiles
/home/test/lib/a.dart
uri: package:test/a.dart
analysisOptions_0
workspacePackage_0_0
/home/test/nested
packagesFile: /home/test/nested/.dart_tool/package_config.json
workspace: workspace_1
analyzedFiles
/home/test/nested/lib/b.dart
uri: package:nested/b.dart
analysisOptions_0
workspacePackage_1_0
analysisOptions
analysisOptions_0: /home/test/analysis_options.yaml
workspaces
workspace_0: PackageConfigWorkspace
root: /home/test
pubPackages
workspacePackage_0_0: PubPackage
root: /home/test
workspace_1: PackageConfigWorkspace
root: /home/test/nested
pubPackages
workspacePackage_1_0: PubPackage
root: /home/test/nested
''');
}
void _assertCollectionText(
AnalysisContextCollectionImpl collection,
String expected,
) {
final actual = _getContextCollectionText(collection);
if (actual != expected) {
print('-------- Actual --------');
print('$actual------------------------');
NodeTextExpectationsCollector.add(actual);
}
expect(actual, expected);
}
/// Asserts the text of a context collection created for a single included
/// workspace path, without any excludes.
void _assertWorkspaceCollectionText(
String workspaceRootPath,
String expected, {
File? optionsFile,
void Function({
required AnalysisOptionsImpl analysisOptions,
required ContextRoot contextRoot,
required DartSdk sdk,
})? updateAnalysisOptions,
}) {
if (optionsFile != null) {
expect(optionsFile.exists, isTrue);
}
final collection = AnalysisContextCollectionImpl(
resourceProvider: resourceProvider,
sdkPath: sdkRoot.path,
includedPaths: [
getFolder(workspaceRootPath).path,
],
optionsFile: optionsFile?.path,
updateAnalysisOptions2: updateAnalysisOptions,
);
_assertCollectionText(collection, expected);
}
String _getContextCollectionText(
AnalysisContextCollectionImpl contextCollection,
) {
final buffer = StringBuffer();
_AnalysisContextCollectionPrinter(
configuration: configuration,
resourceProvider: resourceProvider,
sink: TreeStringSink(sink: buffer, indent: ''),
).write(contextCollection);
return buffer.toString();
}
}
class _AnalysisContextCollectionPrinter {
final _AnalysisContextCollectionPrinterConfiguration configuration;
final ResourceProvider resourceProvider;
final TreeStringSink sink;
final Map<AnalysisOptionsImpl, String> _analysisOptions = Map.identity();
final Map<Workspace, (int, String)> _workspaces = Map.identity();
final Map<Workspace, Map<WorkspacePackage, String>> _workspacePackages =
Map.identity();
_AnalysisContextCollectionPrinter({
required this.configuration,
required this.resourceProvider,
required this.sink,
});
void write(AnalysisContextCollectionImpl contextCollection) {
sink.writeElements(
'contexts',
contextCollection.contexts,
_writeAnalysisContext,
);
_writeAnalysisOptions();
sink.writeElements(
'workspaces',
_workspaces.keys.toList(),
_writeWorkspace,
);
}
String _idOfAnalysisOptions(AnalysisOptionsImpl analysisOptions) {
return _analysisOptions[analysisOptions] ??=
'analysisOptions_${_analysisOptions.length}';
}
String _idOfWorkspace(Workspace workspace) {
return _indexIdOfWorkspace(workspace).$2;
}
String _idOfWorkspacePackage(WorkspacePackage package) {
final workspace = package.workspace;
final packages = _workspacePackages[workspace] ??= Map.identity();
if (packages[package] case final id?) {
return id;
} else {
final workspaceIndex = _indexIdOfWorkspace(workspace).$1;
final id = 'workspacePackage_${workspaceIndex}_${packages.length}';
return packages[package] ??= id;
}
}
(int, String) _indexIdOfWorkspace(Workspace workspace) {
if (_workspaces[workspace] case final existing?) {
return existing;
}
final index = _workspaces.length;
final id = 'workspace_$index';
return _workspaces[workspace] = (index, id);
}
bool _isDartFile(File file) {
return file_paths.isDart(resourceProvider.pathContext, file.path);
}
void _writeAnalysisContext(DriverBasedAnalysisContext analysisContext) {
final contextRoot = analysisContext.contextRoot;
final fsState = analysisContext.driver.fsState;
final analyzedFiles = contextRoot.analyzedFiles().toList();
if (!configuration.withEmptyContextRoots && analyzedFiles.isEmpty) {
return;
}
sink.writelnWithIndent(contextRoot.root.posixPath);
sink.withIndent(() {
_writeNamedFile('packagesFile', contextRoot.packagesFile);
sink.writelnWithIndent(
'workspace: ${_idOfWorkspace(contextRoot.workspace)}',
);
sink.writeElements('analyzedFiles', analyzedFiles, (path) {
final file = resourceProvider.getFile(path);
if (_isDartFile(file)) {
_writeDartFile(fsState, file);
}
});
});
}
void _writeAnalysisOptions() {
var filtered = _analysisOptions.keys
.map((analysisOption) {
var file = analysisOption.file;
return configuration.withAnalysisOptionsWithoutFiles || file != null
? (analysisOption, file)
: null;
})
.nonNulls
.toList();
sink.writeElements('analysisOptions', filtered, (pair) {
var id = _idOfAnalysisOptions(pair.$1);
var file = pair.$2;
if (file == null && configuration.withAnalysisOptionsWithoutFiles) {
sink.writelnWithIndent('$id: <no file>');
} else {
_writeNamedFile(id, file);
}
if (configuration.showEnabledFeatures) {
var options = pair.$1;
var contextFeatures = options.contextFeatures;
var enabledFeatures = ExperimentStatus.knownFeatures.values
.where((f) => contextFeatures.isEnabled(f))
.toList();
sink.writeElements('features', enabledFeatures, (feature) {
sink.writelnWithIndent(feature);
});
}
});
}
void _writeDartFile(FileSystemState fsState, File file) {
sink.writelnWithIndent(file.posixPath);
sink.withIndent(() {
final fileState = fsState.getFileForPath(file.path);
var uri = fileState.uri;
// If file uri, don't print it out, causes test failure on Windows.
if (uri.scheme != 'file') {
sink.writelnWithIndent('uri: $uri');
}
final analysisOptions = fileState.analysisOptions;
if (configuration.withAnalysisOptionsWithoutFiles ||
analysisOptions.file != null) {
final id = _idOfAnalysisOptions(analysisOptions);
sink.writelnWithIndent(id);
}
final workspacePackage = fileState.workspacePackage;
if (workspacePackage != null) {
final id = _idOfWorkspacePackage(workspacePackage);
sink.writelnWithIndent(id);
}
});
}
void _writeNamedFile(String name, File? file) {
if (file != null) {
sink.writelnWithIndent('$name: ${file.posixPath}');
}
}
void _writeWorkspace(Workspace workspace) {
final id = _idOfWorkspace(workspace);
switch (workspace) {
case BasicWorkspace():
sink.writelnWithIndent('$id: BasicWorkspace');
sink.withIndent(() {
final root = resourceProvider.getFolder(workspace.root);
sink.writelnWithIndent('root: ${root.posixPath}');
sink.writelnWithIndent(
_idOfWorkspacePackage(workspace.theOnlyPackage),
);
});
case PackageConfigWorkspace():
sink.writelnWithIndent('$id: PackageConfigWorkspace');
sink.withIndent(() {
final root = resourceProvider.getFolder(workspace.root);
sink.writelnWithIndent('root: ${root.posixPath}');
sink.writeElements(
'pubPackages',
workspace.allPackages.toList(),
_writeWorkspacePackage,
);
});
default:
throw UnimplementedError('${workspace.runtimeType}');
}
}
void _writeWorkspacePackage(WorkspacePackage package) {
final id = _idOfWorkspacePackage(package);
switch (package) {
case BasicWorkspacePackage():
sink.writelnWithIndent('$id: BasicWorkspacePackage');
sink.withIndent(() {
final root = resourceProvider.getFolder(package.root);
sink.writelnWithIndent('root: ${root.posixPath}');
});
case PubPackage():
sink.writelnWithIndent('$id: PubPackage');
sink.withIndent(() {
final root = resourceProvider.getFolder(package.root);
sink.writelnWithIndent('root: ${root.posixPath}');
final sdkVersionConstraint = package.sdkVersionConstraint;
if (sdkVersionConstraint != null) {
sink.writelnWithIndent(
'sdkVersionConstraint: $sdkVersionConstraint');
}
});
default:
throw UnimplementedError('${package.runtimeType}');
}
}
}
class _AnalysisContextCollectionPrinterConfiguration {
bool showEnabledFeatures = false;
bool withEmptyContextRoots = false;
bool withAnalysisOptionsWithoutFiles = false;
}