4ef2725654
We intend to publish and maintain this as a set of testing-related utilities for the analyzer packages and for analyzer plugins. Work towards https://github.com/dart-lang/sdk/issues/55660 See the doc: https://docs.google.com/document/d/1jRtd8B1ijPAP6Pz89HRnyIZXw2VMjaZx0vRZTpoNO84/edit?tab=t.0#heading=h.2sz41a544qhi Change-Id: I2764b1357a932fa955060b26d78038997eaa9536 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/425080 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com> Reviewed-by: Paul Berry <paulberry@google.com>
154 lines
5.3 KiB
Dart
154 lines
5.3 KiB
Dart
// Copyright (c) 2023, 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 'dart:convert';
|
|
|
|
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_testing/package_root.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
Future<void> main() async {
|
|
var tests = <AnnotatedTest>[];
|
|
|
|
// Note: Unauthenticated GitHub API calls are limited to 60 per hour
|
|
// so this script cannot be run often and will fail if there are over
|
|
// 60 unique issues listed.
|
|
|
|
await findFailingTestAnnotations(tests, packagePath: 'analysis_server');
|
|
await findFailingTestAnnotations(tests, packagePath: 'analyzer');
|
|
await findFailingTestAnnotations(tests, packagePath: 'analyzer_cli');
|
|
await findFailingTestAnnotations(tests, packagePath: 'analyzer_plugin');
|
|
|
|
var issueUris = tests.map((test) => test.issueUri).toSet();
|
|
print('Found ${tests.length} with ${issueUris.length} unique issues.');
|
|
print('Fetching test statuses from GitHub...');
|
|
|
|
var closedIssues = <Uri>{};
|
|
for (var issueUri in issueUris) {
|
|
var response = await http.get(issueUri);
|
|
if (response.statusCode != 200) {
|
|
throw 'Failed to call GitHub API: ${response.statusCode} - ${response.reasonPhrase}\n${response.body}';
|
|
}
|
|
var issueData = jsonDecode(response.body) as Map<String, Object?>;
|
|
if (issueData['state'] != 'open') {
|
|
closedIssues.add(issueUri);
|
|
}
|
|
}
|
|
|
|
print('Found ${closedIssues.length} closed issues:');
|
|
for (var tests in tests.where(
|
|
(test) => closedIssues.contains(test.issueUri),
|
|
)) {
|
|
var relativePath = pathContext.relative(tests.file.path, from: packageRoot);
|
|
print('$relativePath ${tests.testName} ${_formatUri(tests.issueUri)}');
|
|
}
|
|
}
|
|
|
|
final pathContext = provider.pathContext;
|
|
|
|
final provider = PhysicalResourceProvider.INSTANCE;
|
|
|
|
Future<void> findFailingTestAnnotations(
|
|
List<AnnotatedTest> tests, {
|
|
required String packagePath,
|
|
}) async {
|
|
var pkgRootPath = pathContext.normalize(packageRoot);
|
|
var testsPath = pathContext.join(pkgRootPath, packagePath, 'test');
|
|
|
|
var collection = AnalysisContextCollection(
|
|
includedPaths: <String>[testsPath],
|
|
resourceProvider: provider,
|
|
);
|
|
var contexts = collection.contexts;
|
|
if (contexts.length != 1) {
|
|
fail('The directory $testsPath contains multiple analysis contexts.');
|
|
}
|
|
|
|
var session = contexts[0].currentSession;
|
|
var directory = provider.getFolder(testsPath);
|
|
|
|
print('Searching for FailedTest/SkippedTest annotations in $packagePath...');
|
|
await _findFailingTestAnnotationsIn(session, testsPath, directory, tests);
|
|
}
|
|
|
|
Future<void> _findFailingTestAnnotationsIn(
|
|
AnalysisSession session,
|
|
String testDirPath,
|
|
Folder directory,
|
|
List<AnnotatedTest> tests,
|
|
) async {
|
|
var children = directory.getChildren();
|
|
children.sort((first, second) => first.shortName.compareTo(second.shortName));
|
|
for (var child in children) {
|
|
if (child is Folder) {
|
|
await _findFailingTestAnnotationsIn(session, testDirPath, child, tests);
|
|
} else if (child is File && child.shortName.endsWith('_test.dart')) {
|
|
var path = child.path;
|
|
|
|
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 = FailingTestAnnotationTracker(child);
|
|
unit.accept(tracker);
|
|
tests.addAll(tracker.annotatedTests);
|
|
}
|
|
}
|
|
}
|
|
|
|
String _formatUri(Uri uri) =>
|
|
uri.path.replaceAll('/repos/', '').replaceAll('/issues/', '#');
|
|
|
|
class AnnotatedTest {
|
|
final File file;
|
|
final String testName;
|
|
final Uri issueUri;
|
|
|
|
AnnotatedTest(this.file, this.testName, this.issueUri);
|
|
}
|
|
|
|
/// A [RecursiveAstVisitor] that tracks nodes annotated with [FailingTest] or
|
|
/// [SkippedTest].
|
|
class FailingTestAnnotationTracker extends RecursiveAstVisitor<void> {
|
|
final annotatedTests = <AnnotatedTest>[];
|
|
final File file;
|
|
|
|
FailingTestAnnotationTracker(this.file);
|
|
|
|
@override
|
|
void visitAnnotation(Annotation node) {
|
|
if (node.name.name == 'FailingTest' || node.name.name == 'SkippedTest') {
|
|
var issue =
|
|
node.arguments?.arguments
|
|
.whereType<NamedExpression>()
|
|
.where((arg) => arg.name.label.name == 'issue')
|
|
.firstOrNull;
|
|
var issueUrl = (issue?.expression as SimpleStringLiteral?)?.value;
|
|
if (issueUrl != null && issueUrl.startsWith('https://github.com/')) {
|
|
var issueUri = Uri.parse(issueUrl);
|
|
var apiUri = issueUri.replace(
|
|
host: 'api.github.com',
|
|
pathSegments: ['repos', ...issueUri.pathSegments],
|
|
);
|
|
var method = node.parent as MethodDeclaration;
|
|
annotatedTests.add(AnnotatedTest(file, method.name.lexeme, apiUri));
|
|
}
|
|
}
|
|
super.visitAnnotation(node);
|
|
}
|
|
}
|