diff --git a/pkg/analysis_server/test/test_all.dart b/pkg/analysis_server/test/test_all.dart index 4c5a33f4bd9..3e5769cc603 100644 --- a/pkg/analysis_server/test/test_all.dart +++ b/pkg/analysis_server/test/test_all.dart @@ -26,6 +26,7 @@ import 'services/test_all.dart' as services; import 'socket_server_test.dart' as socket_server; import 'src/test_all.dart' as src; import 'tool/test_all.dart' as tool; +import 'verify_no_solo_test.dart' as verify_no_solo; import 'verify_sorted_test.dart' as verify_sorted; import 'verify_tests_test.dart' as verify_tests; @@ -52,6 +53,7 @@ void main() { socket_server.main(); src.main(); tool.main(); + verify_no_solo.main(); verify_sorted.main(); verify_tests.main(); defineReflectiveSuite(() { diff --git a/pkg/analysis_server/test/verify_no_solo_test.dart b/pkg/analysis_server/test/verify_no_solo_test.dart new file mode 100644 index 00000000000..150c73117a1 --- /dev/null +++ b/pkg/analysis_server/test/verify_no_solo_test.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, 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/analysis_context_collection.dart'; +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/dart/analysis/session.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:analyzer/file_system/file_system.dart'; +import 'package:analyzer/file_system/physical_file_system.dart'; +import 'package:analyzer_utilities/package_root.dart'; +import 'package:test/test.dart'; + +void main() { + group('analysis_server', () { + buildTests(packagePath: 'analysis_server'); + }); + + group('analyzer', () { + buildTests(packagePath: 'analyzer'); + }); + + group('analyzer_cli', () { + buildTests(packagePath: 'analyzer_cli'); + }); + + group('analyzer_plugin', () { + buildTests(packagePath: 'analyzer_plugin'); + }); +} + +void buildTests({required String packagePath}) { + var provider = PhysicalResourceProvider.INSTANCE; + var pkgRootPath = provider.pathContext.normalize(packageRoot); + + var testsPath = _toPlatformPath(pkgRootPath, '$packagePath/test'); + + var collection = AnalysisContextCollection( + includedPaths: [testsPath], + resourceProvider: provider, + ); + var contexts = collection.contexts; + if (contexts.length != 1) { + fail('The directory $testsPath contains multiple analysis contexts.'); + } + + test('no @soloTest', () { + var failures = []; + buildTestsIn(contexts[0].currentSession, testsPath, + provider.getFolder(testsPath), failures); + + if (failures.isNotEmpty) { + fail('@soloTest annotation found in:\n${failures.join('\n')}'); + } + }); +} + +void buildTestsIn(AnalysisSession session, String testDirPath, Folder directory, + List failures) { + var pathContext = session.resourceProvider.pathContext; + var children = directory.getChildren(); + children.sort((first, second) => first.shortName.compareTo(second.shortName)); + for (var child in children) { + if (child is Folder) { + buildTestsIn(session, testDirPath, child, failures); + } else if (child is File && child.shortName.endsWith('_test.dart')) { + var path = child.path; + var relativePath = pathContext.relative(path, from: testDirPath); + + var result = session.getParsedUnit(path); + if (result is! ParsedUnitResult) { + fail('Could not parse $path'); + } + var unit = result.unit; + var errors = result.errors; + if (errors.isNotEmpty) { + fail('Errors found when parsing $path'); + } + var tracker = SoloTestTracker(); + unit.accept(tracker); + if (tracker.found) { + failures.add(relativePath); + } + } + } +} + +String _toPlatformPath(String pathPath, String relativePosixPath) { + var pathContext = PhysicalResourceProvider.INSTANCE.pathContext; + return pathContext.joinAll([ + pathPath, + ...relativePosixPath.split('/'), + ]); +} + +/// A [RecursiveAstVisitor] that tracks whether any node is annotated with +/// an annotation named 'soloTest'. +class SoloTestTracker extends RecursiveAstVisitor { + bool found = false; + + @override + void visitAnnotation(Annotation node) { + if (node.name.name == 'soloTest') { + found = true; + } + super.visitAnnotation(node); + } +}