// 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/diagnostic/diagnostic.dart' as diag; 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/util/file_paths.dart' as file_paths; import 'package:analyzer_testing/resource_provider_mixin.dart'; 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 _declaredVariables = {}; AnalysisContextCollectionImpl? _analysisContextCollection; File get testFile => getFile(_testFilePath); List 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 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 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.diagnostics.where((d) { return d.diagnosticCode != diag.deadCode && d.diagnosticCode != diag.unusedCatchClause && d.diagnosticCode != diag.unusedCatchStack && d.diagnosticCode != diag.unusedElement && d.diagnosticCode != diag.unusedField && d.diagnosticCode != diag.unusedImport && d.diagnosticCode != diag.unusedLocalVariable; }), 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 parseTestCode(String code) async { addTestSource(code); await getParsedUnit(testFile); } Future resolveTestCode(String code) async { addTestSource(code); await getResolvedUnit(testFile); } @mustCallSuper void setUp() { createMockSdk(resourceProvider: resourceProvider, root: _sdkRoot); } @mustCallSuper Future 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, withFineDependencies: true, ); _addAnalyzedFilesToDrivers(); } Future _sessionFor(File file) async { var analysisContext = _contextFor(file); await analysisContext.applyPendingFileChanges(); return analysisContext.currentSession; } }