Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(355)

Unified Diff: lib/src/testing.dart

Issue 1174643003: expose strong checker API, for use by analyzer_cli (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: lib/src/testing.dart
diff --git a/lib/src/testing.dart b/lib/src/testing.dart
index 590298ee75645270505619afe6b31ddf63a7b638..a6033f6044205e784e40e0e5b98d64a0a1376ac8 100644
--- a/lib/src/testing.dart
+++ b/lib/src/testing.dart
@@ -4,22 +4,23 @@
library dev_compiler.src.testing;
+import 'dart:collection' show Queue;
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/src/generated/ast.dart';
-import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
+import 'package:analyzer/src/generated/engine.dart'
+ show AnalysisContext, AnalysisEngine;
+import 'package:analyzer/src/generated/error.dart';
import 'package:logging/logging.dart';
import 'package:source_span/source_span.dart';
import 'package:test/test.dart';
-import 'package:dev_compiler/config.dart';
-import 'package:dev_compiler/devc.dart' show Compiler;
+import 'package:dev_compiler/strong_mode.dart';
import 'analysis_context.dart';
import 'dependency_graph.dart' show runtimeFilesForServerMode;
import 'info.dart';
import 'options.dart';
-import 'report.dart';
import 'utils.dart';
/// Run the checker on a program with files contents as indicated in
@@ -46,97 +47,47 @@ import 'utils.dart';
/// '''
/// });
///
-CheckerResults testChecker(Map<String, String> testFiles, {String sdkDir,
- customUrlMappings: const {},
- CheckerReporter createReporter(AnalysisContext context), relaxedCasts: true,
- inferDownwards: RulesOptions.inferDownwardsDefault,
- inferFromOverrides: ResolverOptions.inferFromOverridesDefault,
- inferTransitively: ResolverOptions.inferTransitivelyDefault,
- nonnullableTypes: TypeOptions.NONNULLABLE_TYPES}) {
+void testChecker(Map<String, String> testFiles, {String sdkDir,
Jennifer Messerly 2015/06/10 22:34:17 big refactor here to switch it to test StrongCheck
+ customUrlMappings: const {}, relaxedCasts: true,
+ inferDownwards: StrongModeOptions.inferDownwardsDefault,
+ inferFromOverrides: StrongModeOptions.inferFromOverridesDefault,
+ inferTransitively: StrongModeOptions.inferTransitivelyDefault,
+ nonnullableTypes: StrongModeOptions.NONNULLABLE_TYPES}) {
expect(testFiles.containsKey('/main.dart'), isTrue,
reason: '`/main.dart` is missing in testFiles');
var provider = createTestResourceProvider(testFiles);
var uriResolver = new TestUriResolver(provider);
+ var context = AnalysisEngine.instance.createAnalysisContext();
+ context.sourceFactory = createSourceFactory(new SourceResolverOptions(
+ customUrlMappings: customUrlMappings,
+ useMockSdk: sdkDir == null,
+ dartSdkPath: sdkDir,
+ entryPointFile: '/main.dart'), fileResolvers: [uriResolver]);
- var options = new CompilerOptions(
+ var checker = new StrongChecker(context, new StrongModeOptions(
relaxedCasts: relaxedCasts,
inferDownwards: inferDownwards,
inferFromOverrides: inferFromOverrides,
inferTransitively: inferTransitively,
nonnullableTypes: nonnullableTypes,
- useMockSdk: sdkDir == null,
- dartSdkPath: sdkDir,
- runtimeDir: '/dev_compiler_runtime/',
- entryPointFile: '/main.dart',
- customUrlMappings: customUrlMappings);
-
- var context = createAnalysisContext(options, fileResolvers: [uriResolver]);
+ hints: true));
// Run the checker on /main.dart.
- var mainFile = new Uri.file('/main.dart');
- TestReporter testReporter;
- CheckerReporter reporter;
- if (createReporter == null) {
- reporter = testReporter = new TestReporter(context);
- } else {
- reporter = createReporter(context);
- }
- var results =
- new Compiler(options, context: context, reporter: reporter).run();
+ var mainSource = uriResolver.resolveAbsolute(new Uri.file('/main.dart'));
+ var initialLibrary = context.resolveCompilationUnit2(mainSource, mainSource);
- // Extract expectations from the comments in the test files.
- var expectedErrors = <AstNode, List<_ErrorExpectation>>{};
- var visitor = new _ErrorMarkerVisitor(expectedErrors);
- var initialLibrary =
- context.getLibraryElement(uriResolver.resolveAbsolute(mainFile));
- for (var lib in reachableLibraries(initialLibrary)) {
+ // Extract expectations from the comments in the test files, and
+ // check that all errors we emit are included in the expected map.
+ var allLibraries = reachableLibraries(initialLibrary.element.library);
+ for (var lib in allLibraries) {
for (var unit in lib.units) {
- unit.unit.accept(visitor);
- }
- }
-
- if (testReporter == null) return results;
-
- var total = expectedErrors.values.fold(0, (p, l) => p + l.length);
- // Check that all errors we emit are included in the expected map.
- for (var lib in results.libraries) {
- var uri = lib.library.source.uri;
- testReporter.infoMap[uri].forEach((node, actual) {
- var expected = expectedErrors[node];
- var expectedTotal = expected == null ? 0 : expected.length;
- if (actual.length != expectedTotal) {
- expect(actual.length, expectedTotal,
- reason: 'The checker found ${actual.length} errors on the '
- 'expression `$node`, but we expected $expectedTotal. These are the '
- 'errors the checker found:\n\n ${_unexpectedErrors(node, actual)}');
- }
+ if (unit.source.uri.scheme == 'dart') continue;
- for (int i = 0; i < expected.length; i++) {
- expect(actual[i].level, expected[i].level,
- reason: 'expected different logging level at:\n\n'
- '${_messageWithSpan(actual[i])}');
- expect(actual[i].runtimeType, expected[i].type,
- reason: 'expected different error type at:\n\n'
- '${_messageWithSpan(actual[i])}');
- }
- expectedErrors.remove(node);
- });
- }
-
- // Check that all expected errors are accounted for.
- if (!expectedErrors.isEmpty) {
- var newTotal = expectedErrors.values.fold(0, (p, l) => p + l.length);
- // Non empty error expectation lists remaining
- if (newTotal > 0) {
- fail('Not all expected errors were reported by the checker. Only'
- ' ${total - newTotal} out of $total expected errors were reported.\n'
- 'The following errors were not reported:\n'
- '${_unreportedErrors(expectedErrors)}');
+ var errorInfo = checker.computeErrors(unit.source);
+ new _ExpectedErrorVisitor(errorInfo.errors).validate(unit.unit);
}
}
-
- return results;
}
/// Creates a [MemoryResourceProvider] with test data
@@ -171,66 +122,27 @@ class TestUriResolver extends ResourceUriResolver {
}
}
-class TestReporter extends SummaryReporter {
- Map<Uri, Map<AstNode, List<StaticInfo>>> infoMap = {};
- Map<AstNode, List<StaticInfo>> _current;
+class _ExpectedErrorVisitor extends UnifyingAstVisitor {
+ final Set<AnalysisError> _actualErrors;
+ CompilationUnit _unit;
+ String _unitSourceCode;
- TestReporter(AnalysisContext context) : super(context);
+ _ExpectedErrorVisitor(List<AnalysisError> actualErrors)
+ : _actualErrors = new Set.from(actualErrors);
- void enterLibrary(Uri uri) {
- super.enterLibrary(uri);
- infoMap[uri] = _current = {};
- }
+ validate(CompilationUnit unit) {
+ _unit = unit;
+ // This reads the file. Only safe because tests use MemoryFileSystem.
+ _unitSourceCode = unit.element.source.contents.data;
+
+ // Visit the compilation unit.
+ unit.accept(this);
- void log(Message info) {
- super.log(info);
- if (info is StaticInfo) {
- _current.putIfAbsent(info.node, () => []).add(info);
+ if (_actualErrors.isNotEmpty) {
+ var actualMsgs = _actualErrors.map(_formatActualError).join('\n');
+ fail('Unexpected errors reported by checker:\n\n$actualMsgs');
}
}
-}
-
-/// Create an error explanation for errors that were not expected, but that the
-/// checker produced.
-String _unexpectedErrors(AstNode node, List errors) {
- final span = _spanFor(node);
- return errors.map((e) {
- var level = e.level.name.toLowerCase();
- return '$level: ${span.message(e.message, color: colorOf(level))}';
- }).join('\n');
-}
-
-String _unreportedErrors(Map<AstNode, List<_ErrorExpectation>> expected) {
- var sb = new StringBuffer();
- for (var node in expected.keys) {
- var span = _spanFor(node);
- expected[node].forEach((e) {
- var level = e.level.name.toLowerCase();
- sb.write('$level: ${span.message("${e.type}", color: colorOf(level))}\n');
- });
- }
- return sb.toString();
-}
-
-String _messageWithSpan(StaticInfo info) {
- var span = _spanFor(info.node);
- var level = info.level.name.toLowerCase();
- return '$level: ${span.message(info.message, color: colorOf(level))}';
-}
-
-SourceSpan _spanFor(AstNode node) {
- var unit = node.root as CompilationUnit;
- var source = unit.element.source;
- // This reads the file. Only safe in tests, because they use MemoryFileSystem.
- var content = source.contents.data;
- return createSpanHelper(unit, node.offset, node.end, source, content);
-}
-
-/// Visitor that extracts expected errors from comments.
-class _ErrorMarkerVisitor extends UnifyingAstVisitor {
- Map<AstNode, List<_ErrorExpectation>> expectedErrors;
-
- _ErrorMarkerVisitor(this.expectedErrors);
visitNode(AstNode node) {
var token = node.beginToken;
@@ -248,36 +160,79 @@ class _ErrorMarkerVisitor extends UnifyingAstVisitor {
if (start != -1 && end != -1) {
expect(start, lessThan(end));
var errors = commentText.substring(start + 2, end).split(',');
- var expectations = errors.map(_ErrorExpectation.parse);
- expectedErrors[node] = expectations.where((x) => x != null).toList();
+ var expectations =
+ errors.map(_ErrorExpectation.parse).where((x) => x != null);
+
+ for (var e in expectations) _expectError(node, e);
}
}
}
return super.visitNode(node);
}
+
+ void _expectError(AstNode node, _ErrorExpectation expected) {
+
+ // See if we can find the expected error in our actual errors
+ for (var actual in _actualErrors) {
+ if (actual.offset == node.offset && actual.length == node.length) {
+ var actualMsg = _formatActualError(actual);
+ expect(_actualErrorLevel(actual), expected.level,
+ reason: 'expected different error code at:\n\n$actualMsg');
+ expect(actual.errorCode.name, expected.typeName,
+ reason: 'expected different error type at:\n\n$actualMsg');
+
+ // We found it. Stop the search.
+ _actualErrors.remove(actual);
+ return;
+ }
+ }
+
+ var span = _createSpan(node.offset, node.length);
+ var levelName = expected.level.name.toLowerCase();
+ var location =
+ '$levelName: ${span.message(expected.typeName, color: colorOf(levelName))}';
Jennifer Messerly 2015/06/10 22:34:17 oops, forgot dart_style can't handle this. will fi
+ fail('expected error was not reported at:\n\n$location');
+ }
+
+ Level _actualErrorLevel(AnalysisError actual) {
+ return const <ErrorSeverity, Level>{
+ ErrorSeverity.ERROR: Level.SEVERE,
+ ErrorSeverity.WARNING: Level.WARNING,
+ ErrorSeverity.INFO: Level.INFO
+ }[actual.errorCode.errorSeverity];
+ }
+
+ String _formatActualError(AnalysisError actual) {
+ var span = _createSpan(actual.offset, actual.length);
+ var levelName = _actualErrorLevel(actual).name.toLowerCase();
+ return '$levelName: ${span.message(actual.message, color: colorOf(levelName))}';
Jennifer Messerly 2015/06/10 22:34:17 this too
+ }
+
+ SourceSpan _createSpan(int offset, int len) {
+ return createSpanHelper(
+ _unit, offset, offset + len, _unit.element.source, _unitSourceCode);
+ }
}
/// Describes an expected message that should be produced by the checker.
class _ErrorExpectation {
final Level level;
- final Type type;
- _ErrorExpectation(this.level, this.type);
+ final String typeName;
+ _ErrorExpectation(this.level, this.typeName);
static _ErrorExpectation _parse(String descriptor) {
var tokens = descriptor.split(':');
expect(tokens.length, 2, reason: 'invalid error descriptor');
var name = tokens[0].toUpperCase();
- var typeName = tokens[1].toLowerCase();
+ var typeName = tokens[1];
var level =
Level.LEVELS.firstWhere((l) => l.name == name, orElse: () => null);
expect(level, isNotNull,
reason: 'invalid level in error descriptor: `${tokens[0]}`');
- var type = infoTypes.firstWhere((t) => '$t'.toLowerCase() == typeName,
- orElse: () => null);
- expect(type, isNotNull,
+ expect(typeName, isNotNull,
reason: 'invalid type in error descriptor: ${tokens[1]}');
- return new _ErrorExpectation(level, type);
+ return new _ErrorExpectation(level, typeName);
}
static _ErrorExpectation parse(String descriptor) {
@@ -293,5 +248,5 @@ class _ErrorExpectation {
return _parse(tokens[0]);
}
- String toString() => '$level $type';
+ String toString() => '$level $typeName';
}

Powered by Google App Engine
This is Rietveld 408576698