Files
sdk/pkg/analyzer/test/verify_docs_test.dart
T
Sam Rawlins 9e6486fe3f Analyzer: Make test fields late, late final, or hinted for migration.
Many fields can be late or late final, so adding hints for the migration tool.

In SnippetTester, the fields are all set in the constructor. Added a factory
constructor so the fields can be final. Renamed them to be private.

Change-Id: I559e7ba92c7c6749038630b03f67f8a77bdc704a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/170406
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
2020-11-05 17:45:17 +00:00

166 lines
5.5 KiB
Dart

// 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.
import 'dart:convert';
import 'package:analyzer/dart/analysis/analysis_context.dart';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/overlay_file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:test/test.dart';
import 'utils/package_root.dart' as package_root;
main() async {
SnippetTester tester = SnippetTester();
await tester.verify();
if (tester.output.isNotEmpty) {
fail(tester.output.toString());
}
}
class SnippetTester {
final OverlayResourceProvider provider;
final Folder docFolder;
final String snippetDirPath;
final String snippetPath;
final StringBuffer output = StringBuffer();
factory SnippetTester() {
var provider = OverlayResourceProvider(PhysicalResourceProvider.INSTANCE);
var packageRoot = provider.pathContext.normalize(package_root.packageRoot);
var analyzerPath = provider.pathContext.join(packageRoot, 'analyzer');
var docPath = provider.pathContext.join(analyzerPath, 'doc');
var docFolder = provider.getFolder(docPath);
var snippetDirPath =
provider.pathContext.join(analyzerPath, 'test', 'snippets');
var snippetPath = provider.pathContext.join(snippetDirPath, 'snippet.dart');
return SnippetTester._(provider, docFolder, snippetDirPath, snippetPath);
}
SnippetTester._(
this.provider, this.docFolder, this.snippetDirPath, this.snippetPath);
Future<void> verify() async {
await verifyFolder(docFolder);
}
Future<void> verifyFile(File file) async {
String content = file.readAsStringSync();
List<String> lines = const LineSplitter().convert(content);
List<String> codeLines = [];
bool inCode = false;
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
if (line == '```dart') {
if (inCode) {
// TODO(brianwilkerson) Report this.
}
inCode = true;
} else if (line == '```') {
if (!inCode) {
// TODO(brianwilkerson) Report this.
}
await verifySnippet(file, codeLines.join('\n'));
codeLines.clear();
inCode = false;
} else if (inCode) {
codeLines.add(line);
}
}
}
Future<void> verifyFolder(Folder folder) async {
for (Resource child in folder.getChildren()) {
if (child is File) {
if (child.shortName.endsWith('.md')) {
await verifyFile(child);
}
} else if (child is Folder) {
await verifyFolder(child);
}
}
}
Future<void> verifySnippet(File file, String snippet) async {
// TODO(brianwilkerson) When the files outside of 'src' contain only public
// API, write code to compute the list of imports so that new public API
// will automatically be allowed.
String imports = '''
import 'dart:math' as math;
import 'package:analyzer/dart/analysis/analysis_context.dart';
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/dart/element/element.dart';
import 'package:analyzer/dart/element/visitor.dart';
''';
provider.setOverlay(snippetPath,
content: '''
$imports
$snippet
''',
modificationStamp: 1);
try {
AnalysisContextCollection collection = AnalysisContextCollection(
includedPaths: <String>[snippetDirPath], resourceProvider: provider);
List<AnalysisContext> contexts = collection.contexts;
if (contexts.length != 1) {
fail('The snippets directory contains multiple analysis contexts.');
}
ErrorsResult results =
await contexts[0].currentSession.getErrors(snippetPath);
Iterable<AnalysisError> errors = results.errors.where((error) {
ErrorCode errorCode = error.errorCode;
return errorCode != HintCode.UNUSED_IMPORT &&
errorCode != HintCode.UNUSED_LOCAL_VARIABLE;
});
if (errors.isNotEmpty) {
String filePath =
provider.pathContext.relative(file.path, from: docFolder.path);
if (output.isNotEmpty) {
output.writeln();
}
output.writeln('Errors in snippet in "$filePath":');
output.writeln();
output.writeln(snippet);
output.writeln();
int importsLength = imports.length + 1; // account for the '\n'.
for (var error in errors) {
writeError(error, importsLength);
}
}
} catch (exception, stackTrace) {
if (output.isNotEmpty) {
output.writeln();
}
output.writeln('Exception while analyzing "$snippet"');
output.writeln();
output.writeln(exception);
output.writeln(stackTrace);
} finally {
provider.removeOverlay(snippetPath);
}
}
void writeError(AnalysisError error, int prefixLength) {
output.write(error.errorCode);
output.write(' (');
output.write(error.offset - prefixLength);
output.write(', ');
output.write(error.length);
output.write(') ');
output.writeln(error.message);
}
}