| // 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. |
| |
| /// Looks for ".dart" files in "tests/" that appear to be orphaned. That means |
| /// they don't end in "_test.dart" so aren't run as tests by the test_runner, |
| /// but they also don't appear to be referenced by any other tests. |
| /// |
| /// Usually this means that someone accidentally left off the "_test" and the |
| /// file is supposed to be a test but is silently getting ignored. |
| import 'dart:io'; |
| |
| import 'package:analyzer/dart/analysis/analysis_context.dart'; |
| import 'package:analyzer/dart/analysis/context_builder.dart'; |
| import 'package:analyzer/dart/analysis/context_locator.dart'; |
| import 'package:analyzer/dart/analysis/results.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| |
| import 'package:test_runner/src/path.dart'; |
| |
| AnalysisContext _analysisContext; |
| |
| void main(List<String> arguments) { |
| _initAnalysisContext(); |
| |
| var suites = Directory('tests').listSync(); |
| suites.sort((a, b) => a.path.compareTo(b.path)); |
| |
| for (var entry in suites) { |
| // Skip the co19 tests since they don't use '_test.dart'. |
| if (entry is Directory && !entry.path.contains('co19')) { |
| _checkTestDirectory(entry); |
| } |
| } |
| } |
| |
| void _initAnalysisContext() { |
| var roots = ContextLocator().locateRoots(includedPaths: ['test']); |
| if (roots.length != 1) { |
| throw StateError('Expected to find exactly one context root, got $roots'); |
| } |
| |
| _analysisContext = ContextBuilder().createContext(contextRoot: roots[0]); |
| } |
| |
| void _checkTestDirectory(Directory directory) { |
| print('-- ${directory.path} --'); |
| var paths = directory |
| .listSync(recursive: true) |
| .map((entry) => entry.path) |
| .where((path) => path.endsWith('.dart')) |
| .toList(); |
| paths.sort(); |
| |
| // Collect the set of all files that are known to be referred to by others. |
| print('Finding referenced files...'); |
| var importedPaths = <String>{}; |
| for (var path in paths) { |
| _parseReferences(importedPaths, path); |
| } |
| |
| // Find the ".dart" files that don't end in "_test.dart" but also aren't used |
| // by another library. Those should probably be tests. |
| var hasOrphan = false; |
| for (var path in paths) { |
| if (!path.endsWith('_test.dart') && !importedPaths.contains(path)) { |
| print('Suspected orphan: $path'); |
| hasOrphan = true; |
| } |
| } |
| |
| if (!hasOrphan) print('No orphans :)'); |
| } |
| |
| void _parseReferences(Set<String> importedPaths, String filePath) { |
| var absolute = Path(filePath).absolute.toNativePath(); |
| var parseResult = _analysisContext.currentSession.getParsedUnit(absolute); |
| var unit = (parseResult as ParsedUnitResult).unit; |
| |
| void add(String importPath) { |
| if (importPath.startsWith('dart:')) return; |
| |
| var resolved = Uri.file(filePath).resolve(importPath).path; |
| importedPaths.add(resolved); |
| } |
| |
| for (var directive in unit.directives) { |
| if (directive is UriBasedDirective) { |
| add(directive.uri.stringValue); |
| } |
| } |
| } |