blob: 34a50ffacf5b59c23a75f334ec16b23e208127fd [file] [log] [blame]
// Copyright (c) 2024, 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/results.dart';
import 'package:analyzer/dart/analysis/session.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/byte_store.dart';
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
import 'package:analyzer/src/error/codes.g.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisEngine;
import 'package:analyzer/src/test_utilities/find_node.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:meta/meta.dart';
import 'package:test/test.dart';
/// A base test framework for writing tests that resolve a String as Dart source
/// code.
// TODO(srawlins): This is all copied from the `AbstractSingleUnitTest` and
// `AbstractContextTest` classes in the analysis_server package. It is pared
// down as the minimum amount of code needed to run the tests in this package.
class SingleUnitTest with ResourceProviderMixin {
static final ByteStore _byteStore = MemoryByteStore();
late String testCode;
late ParsedUnitResult testParsedResult;
late ResolvedUnitResult testAnalysisResult;
late FindNode findNode;
final Map<String, String> _declaredVariables = {};
AnalysisContextCollectionImpl? _analysisContextCollection;
File get testFile => getFile(_testFilePath);
List<String> get _collectionIncludedPaths => [_workspaceRootPath];
Folder get _sdkRoot => newFolder('/sdk');
String get _testFilePath => '$_testPackageLibPath/test.dart';
String get _testPackageLibPath => '$_testPackageRootPath/lib';
String get _testPackageRootPath => '$_workspaceRootPath/test';
String get _workspaceRootPath => '/home';
void addTestSource(String code) {
testCode = code;
newFile(testFile.path, code);
}
void changeFile(File file) {
_contextFor(file).driver.changeFile(file.path);
}
Future<ParsedUnitResult> getParsedUnit(File file) async {
var path = file.path;
var session = await _sessionFor(file);
var result = session.getParsedUnit(path) as ParsedUnitResult;
testParsedResult = result;
testCode = result.content;
var testUnit = result.unit;
findNode = FindNode(testCode, testUnit);
return result;
}
Future<ResolvedUnitResult> getResolvedUnit(File file) async {
var path = file.path;
var session = await _sessionFor(file);
var result = await session.getResolvedUnit(path) as ResolvedUnitResult;
testAnalysisResult = result;
testCode = result.content;
var testUnit = result.unit;
expect(result.errors.where((error) {
return error.errorCode != WarningCode.DEAD_CODE &&
error.errorCode != WarningCode.UNUSED_CATCH_CLAUSE &&
error.errorCode != WarningCode.UNUSED_CATCH_STACK &&
error.errorCode != WarningCode.UNUSED_ELEMENT &&
error.errorCode != WarningCode.UNUSED_FIELD &&
error.errorCode != WarningCode.UNUSED_IMPORT &&
error.errorCode != WarningCode.UNUSED_LOCAL_VARIABLE;
}), isEmpty);
findNode = FindNode(testCode, testUnit);
return result;
}
@override
File newFile(String path, String content) {
if (_analysisContextCollection != null && !path.endsWith('.dart')) {
throw StateError('Only dart files can be changed after analysis.');
}
var file = super.newFile(path, content);
_addAnalyzedFileToDrivers(file);
return file;
}
Future<void> parseTestCode(String code) async {
addTestSource(code);
await getParsedUnit(testFile);
}
Future<void> resolveTestCode(String code) async {
addTestSource(code);
await getResolvedUnit(testFile);
}
@mustCallSuper
void setUp() {
createMockSdk(resourceProvider: resourceProvider, root: _sdkRoot);
}
@mustCallSuper
Future<void> tearDown() async {
AnalysisEngine.instance.clearCaches();
await _analysisContextCollection?.dispose();
}
void _addAnalyzedFilesToDrivers() {
for (var analysisContext in _analysisContextCollection!.contexts) {
for (var path in analysisContext.contextRoot.analyzedFiles()) {
if (file_paths.isDart(resourceProvider.pathContext, path)) {
analysisContext.driver.addFile(path);
}
}
}
}
void _addAnalyzedFileToDrivers(File file) {
var collection = _analysisContextCollection;
if (collection != null) {
for (var analysisContext in collection.contexts) {
if (analysisContext.contextRoot.isAnalyzed(file.path)) {
analysisContext.driver.addFile(file.path);
}
}
}
}
DriverBasedAnalysisContext _contextFor(File file) {
_createAnalysisContexts();
return _analysisContextCollection!.contextFor(file.path);
}
/// Creates all analysis contexts in [_collectionIncludedPaths].
void _createAnalysisContexts() {
if (_analysisContextCollection != null) {
return;
}
_analysisContextCollection = AnalysisContextCollectionImpl(
byteStore: _byteStore,
declaredVariables: _declaredVariables,
enableIndex: true,
includedPaths: _collectionIncludedPaths.map(convertPath).toList(),
resourceProvider: resourceProvider,
sdkPath: _sdkRoot.path,
);
_addAnalyzedFilesToDrivers();
}
Future<AnalysisSession> _sessionFor(File file) async {
var analysisContext = _contextFor(file);
await analysisContext.applyPendingFileChanges();
return analysisContext.currentSession;
}
}