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

Side by Side Diff: pkg/analyzer/test/src/task/strong/strong_test_helper.dart

Issue 1673843003: improve debugging of strong mode checker/inference tests (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 10 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 unified diff | Download patch
« no previous file with comments | « pkg/analyzer/test/src/task/strong/inferred_type_test.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 // TODO(jmesserly): this file needs to be refactored, it's a port from 5 // TODO(jmesserly): this file needs to be refactored, it's a port from
6 // package:dev_compiler's tests 6 // package:dev_compiler's tests
7 library analyzer.test.src.task.strong.strong_test_helper; 7 library analyzer.test.src.task.strong.strong_test_helper;
8 8
9 import 'package:analyzer/dart/ast/ast.dart'; 9 import 'package:analyzer/dart/ast/ast.dart';
10 import 'package:analyzer/dart/ast/visitor.dart'; 10 import 'package:analyzer/dart/ast/visitor.dart';
11 import 'package:analyzer/dart/element/element.dart'; 11 import 'package:analyzer/dart/element/element.dart';
12 import 'package:analyzer/file_system/file_system.dart'; 12 import 'package:analyzer/file_system/file_system.dart';
13 import 'package:analyzer/file_system/memory_file_system.dart'; 13 import 'package:analyzer/file_system/memory_file_system.dart';
14 import 'package:analyzer/src/context/context.dart' show SdkAnalysisContext; 14 import 'package:analyzer/src/context/context.dart' show SdkAnalysisContext;
15 import 'package:analyzer/src/generated/engine.dart'; 15 import 'package:analyzer/src/generated/engine.dart';
16 import 'package:analyzer/src/generated/error.dart'; 16 import 'package:analyzer/src/generated/error.dart';
17 import 'package:analyzer/src/generated/sdk.dart'; 17 import 'package:analyzer/src/generated/sdk.dart';
18 import 'package:analyzer/src/generated/source.dart'; 18 import 'package:analyzer/src/generated/source.dart';
19 import 'package:analyzer/src/generated/type_system.dart'; 19 import 'package:analyzer/src/generated/type_system.dart';
20 import 'package:analyzer/src/task/strong/checker.dart'; 20 import 'package:analyzer/src/task/strong/checker.dart';
21 import 'package:logging/logging.dart'; 21 import 'package:logging/logging.dart';
22 import 'package:source_span/source_span.dart'; 22 import 'package:source_span/source_span.dart';
23 import 'package:unittest/unittest.dart'; 23 import 'package:unittest/unittest.dart';
24 24
25 const String GREEN_COLOR = '\u001b[32m';
26 25
27 const String NO_COLOR = '\u001b[0m'; 26 MemoryResourceProvider files;
27 bool _checkCalled;
28 28
29 const String _CYAN_COLOR = '\u001b[36m'; 29 initStrongModeTests() {
30 setUp(() {
31 AnalysisEngine.instance.processRequiredPlugins();
32 files = new MemoryResourceProvider();
33 _checkCalled = false;
34 });
30 35
31 const String _MAGENTA_COLOR = '\u001b[35m'; 36 tearDown(() {
37 // This is a sanity check, in case only addFile is called.
38 expect(_checkCalled, true, reason: 'must call check() method in test case');
39 files = null;
40 });
41 }
32 42
33 const String _RED_COLOR = '\u001b[31m'; 43 /// Adds a file using [addFile] and calls [check].
44 void checkFile(String content) {
45 addFile(content);
46 check();
47 }
48
49 /// Adds [files] using [addFiles] and calls [check].
50 void checkFiles(Map<String, String> files) {
51 addFiles(files);
52 check();
53 }
54
55 /// Adds a file to check. The file should contain:
56 ///
57 /// * all expected failures are listed in the source code using comments
58 /// immediately in front of the AST node that should contain the error.
59 ///
60 /// * errors are formatted as a token `level:Type`, where `level` is the
61 /// logging level were the error would be reported at, and `Type` is the
62 /// concrete subclass of [StaticInfo] that denotes the error.
63 ///
64 /// For example to check that an assignment produces a type error, you can
65 /// create a file like:
66 ///
67 /// addFile('''
68 /// String x = /*severe:STATIC_TYPE_ERROR*/3;
69 /// ''');
70 /// check();
71 ///
72 /// For a single file, you may also use [checkFile].
73 void addFile(String content, {String name: '/main.dart'}) {
74 name = name.replaceFirst('^package:', '/packages/');
75 files.newFile(name, content);
76 }
77
78 /// Run the checker on a program, staring from '/main.dart', and verifies that
79 /// errors/warnings/hints match the expected value.
80 ///
81 /// See [addFile] for more information about how to encode expectations in
82 /// the file text.
83 void check() {
84 _checkCalled = true;
85
86 expect(files.getFile('/main.dart').exists, true,
87 reason: '`/main.dart` is missing');
88
89 var uriResolver = new TestUriResolver(files);
90 // Enable task model strong mode
91 var context = AnalysisEngine.instance.createAnalysisContext();
92 context.analysisOptions.strongMode = true;
93 context.analysisOptions.strongModeHints = true;
94 context.sourceFactory = new SourceFactory([
95 new MockDartSdk(_mockSdkSources, reportMissing: true).resolver,
96 uriResolver
97 ]);
98
99 // Run the checker on /main.dart.
100 Source mainSource = uriResolver.resolveAbsolute(new Uri.file('/main.dart'));
101 var initialLibrary =
102 context.resolveCompilationUnit2(mainSource, mainSource);
103
104 var collector = new _ErrorCollector();
105 var checker = new CodeChecker(
106 context.typeProvider, new StrongTypeSystemImpl(), collector,
107 hints: true);
108
109 // Extract expectations from the comments in the test files, and
110 // check that all errors we emit are included in the expected map.
111 var allLibraries = reachableLibraries(initialLibrary.element.library);
112 for (var lib in allLibraries) {
113 for (var unit in lib.units) {
114 var errors = <AnalysisError>[];
115 collector.errors = errors;
116
117 var source = unit.source;
118 if (source.uri.scheme == 'dart') continue;
119
120 var librarySource = context.getLibrariesContaining(source).single;
121 var resolved = context.resolveCompilationUnit2(source, librarySource);
122 var analyzerErrors = context
123 .getErrors(source)
124 .errors
125 .where((error) =>
126 error.errorCode.name.startsWith('STRONG_MODE_INFERRED_TYPE'))
127 .toList();
128 errors.addAll(analyzerErrors);
129 checker.visitCompilationUnit(resolved);
130
131 new _ExpectedErrorVisitor(errors).validate(resolved);
132 }
133 }
134 }
34 135
35 /// Sample mock SDK sources. 136 /// Sample mock SDK sources.
36 final Map<String, String> mockSdkSources = { 137 final Map<String, String> _mockSdkSources = {
37 // The list of types below is derived from: 138 // The list of types below is derived from:
38 // * types we use via our smoke queries, including HtmlElement and 139 // * types we use via our smoke queries, including HtmlElement and
39 // types from `_typeHandlers` (deserialize.dart) 140 // types from `_typeHandlers` (deserialize.dart)
40 // * types that are used internally by the resolver (see 141 // * types that are used internally by the resolver (see
41 // _initializeFrom in resolver.dart). 142 // _initializeFrom in resolver.dart).
42 'dart:core': ''' 143 'dart:core': '''
43 library dart.core; 144 library dart.core;
44 145
45 void print(Object o) {} 146 void print(Object o) {}
46 147
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
121 222
122 'dart:_foreign_helper': ''' 223 'dart:_foreign_helper': '''
123 library dart._foreign_helper; 224 library dart._foreign_helper;
124 225
125 JS(String typeDescription, String codeTemplate, 226 JS(String typeDescription, String codeTemplate,
126 [arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11]) 227 [arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11])
127 {} 228 {}
128 ''' 229 '''
129 }; 230 };
130 231
131 /// Returns an ANSII color escape sequence corresponding to [levelName]. Colors
132 /// are defined for: severe, error, warning, or info. Returns null if the level
133 /// name is not recognized.
134 String colorOf(String levelName) {
135 levelName = levelName.toLowerCase();
136 if (levelName == 'shout' || levelName == 'severe' || levelName == 'error') {
137 return _RED_COLOR;
138 }
139 if (levelName == 'warning') return _MAGENTA_COLOR;
140 if (levelName == 'info') return _CYAN_COLOR;
141 return null;
142 }
143
144 SourceSpanWithContext createSpanHelper( 232 SourceSpanWithContext createSpanHelper(
145 LineInfo lineInfo, int start, int end, Source source, String content) { 233 LineInfo lineInfo, int start, int end, Source source, String content) {
146 var startLoc = locationForOffset(lineInfo, source.uri, start); 234 var startLoc = locationForOffset(lineInfo, source.uri, start);
147 var endLoc = locationForOffset(lineInfo, source.uri, end); 235 var endLoc = locationForOffset(lineInfo, source.uri, end);
148 236
149 var lineStart = startLoc.offset - startLoc.column; 237 var lineStart = startLoc.offset - startLoc.column;
150 // Find the end of the line. This is not exposed directly on LineInfo, but 238 // Find the end of the line. This is not exposed directly on LineInfo, but
151 // we can find it pretty easily. 239 // we can find it pretty easily.
152 // TODO(jmesserly): for now we do the simple linear scan. Ideally we can get 240 // TODO(jmesserly): for now we do the simple linear scan. Ideally we can get
153 // some help from the LineInfo API. 241 // some help from the LineInfo API.
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
188 if (seen.contains(lib)) return; 276 if (seen.contains(lib)) return;
189 seen.add(lib); 277 seen.add(lib);
190 results.add(lib); 278 results.add(lib);
191 lib.importedLibraries.forEach(find); 279 lib.importedLibraries.forEach(find);
192 lib.exportedLibraries.forEach(find); 280 lib.exportedLibraries.forEach(find);
193 } 281 }
194 find(start); 282 find(start);
195 return results; 283 return results;
196 } 284 }
197 285
198 /// Run the checker on a program with files contents as indicated in
199 /// [testFiles].
200 ///
201 /// This function makes several assumptions to make it easier to describe error
202 /// expectations:
203 ///
204 /// * a file named `/main.dart` exists in [testFiles].
205 /// * all expected failures are listed in the source code using comments
206 /// immediately in front of the AST node that should contain the error.
207 /// * errors are formatted as a token `level:Type`, where `level` is the
208 /// logging level were the error would be reported at, and `Type` is the
209 /// concrete subclass of [StaticInfo] that denotes the error.
210 ///
211 /// For example, to check that an assignment produces a warning about a boxing
212 /// conversion, you can describe the test as follows:
213 ///
214 /// testChecker({
215 /// '/main.dart': '''
216 /// testMethod() {
217 /// dynamic x = /*warning:Box*/3;
218 /// }
219 /// '''
220 /// });
221 ///
222 void testChecker(String name, Map<String, String> testFiles) {
223 test(name, () {
224 AnalysisEngine.instance.processRequiredPlugins();
225 expect(testFiles.containsKey('/main.dart'), isTrue,
226 reason: '`/main.dart` is missing in testFiles');
227
228 var provider = new MemoryResourceProvider();
229 testFiles.forEach((key, value) {
230 var scheme = 'package:';
231 if (key.startsWith(scheme)) {
232 key = '/packages/${key.substring(scheme.length)}';
233 }
234 provider.newFile(key, value);
235 });
236 var uriResolver = new TestUriResolver(provider);
237 // Enable task model strong mode
238 var context = AnalysisEngine.instance.createAnalysisContext();
239 context.analysisOptions.strongMode = true;
240 context.analysisOptions.strongModeHints = true;
241
242 context.sourceFactory = new SourceFactory([
243 new MockDartSdk(mockSdkSources, reportMissing: true).resolver,
244 uriResolver
245 ]);
246
247 // Run the checker on /main.dart.
248 Source mainSource = uriResolver.resolveAbsolute(new Uri.file('/main.dart'));
249 var initialLibrary =
250 context.resolveCompilationUnit2(mainSource, mainSource);
251
252 var collector = new _ErrorCollector();
253 var checker = new CodeChecker(
254 context.typeProvider, new StrongTypeSystemImpl(), collector,
255 hints: true);
256
257 // Extract expectations from the comments in the test files, and
258 // check that all errors we emit are included in the expected map.
259 var allLibraries = reachableLibraries(initialLibrary.element.library);
260 for (var lib in allLibraries) {
261 for (var unit in lib.units) {
262 var errors = <AnalysisError>[];
263 collector.errors = errors;
264
265 var source = unit.source;
266 if (source.uri.scheme == 'dart') continue;
267
268 var librarySource = context.getLibrariesContaining(source).single;
269 var resolved = context.resolveCompilationUnit2(source, librarySource);
270 var analyzerErrors = context
271 .getErrors(source)
272 .errors
273 .where((error) =>
274 error.errorCode.name.startsWith('STRONG_MODE_INFERRED_TYPE'))
275 .toList();
276 errors.addAll(analyzerErrors);
277 checker.visitCompilationUnit(resolved);
278
279 new _ExpectedErrorVisitor(errors).validate(resolved);
280 }
281 }
282 });
283 }
284
285 /// Dart SDK which contains a mock implementation of the SDK libraries. May be 286 /// Dart SDK which contains a mock implementation of the SDK libraries. May be
286 /// used to speed up execution when most of the core libraries is not needed. 287 /// used to speed up execution when most of the core libraries is not needed.
287 class MockDartSdk implements DartSdk { 288 class MockDartSdk implements DartSdk {
288 final Map<Uri, _MockSdkSource> _sources = {}; 289 final Map<Uri, _MockSdkSource> _sources = {};
289 final bool reportMissing; 290 final bool reportMissing;
290 final Map<String, SdkLibrary> _libs = {}; 291 final Map<String, SdkLibrary> _libs = {};
291 final String sdkVersion = '0'; 292 final String sdkVersion = '0';
292 final AnalysisContext context = new SdkAnalysisContext(); 293 final AnalysisContext context = new SdkAnalysisContext();
293 DartUriResolver _resolver; 294 DartUriResolver _resolver;
294 MockDartSdk(Map<String, String> sources, {this.reportMissing}) { 295 MockDartSdk(Map<String, String> sources, {this.reportMissing}) {
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
478 reason: 'expected different error type at:\n\n$actualMsg'); 479 reason: 'expected different error type at:\n\n$actualMsg');
479 480
480 // We found it. Stop the search. 481 // We found it. Stop the search.
481 _actualErrors.remove(actual); 482 _actualErrors.remove(actual);
482 return; 483 return;
483 } 484 }
484 } 485 }
485 486
486 var span = _createSpan(node.offset, node.length); 487 var span = _createSpan(node.offset, node.length);
487 var levelName = expected.level.name.toLowerCase(); 488 var levelName = expected.level.name.toLowerCase();
488 var msg = span.message(expected.typeName, color: colorOf(levelName)); 489 var msg = span.message(expected.typeName, color: _colorOf(levelName));
489 fail('expected error was not reported at:\n\n$levelName: $msg'); 490 fail('expected error was not reported at:\n\n$levelName: $msg');
490 } 491 }
491 492
492 String _formatActualError(AnalysisError actual) { 493 String _formatActualError(AnalysisError actual) {
493 var span = _createSpan(actual.offset, actual.length); 494 var span = _createSpan(actual.offset, actual.length);
494 var levelName = _actualErrorLevel(actual).name.toLowerCase(); 495 var levelName = _actualErrorLevel(actual).name.toLowerCase();
495 var msg = span.message(actual.message, color: colorOf(levelName)); 496 var msg = span.message(actual.message, color: _colorOf(levelName));
496 return '$levelName: [${errorCodeName(actual.errorCode)}] $msg'; 497 return '$levelName: [${errorCodeName(actual.errorCode)}] $msg';
497 } 498 }
499
500 /// Returns an ANSII color escape sequence corresponding to [levelName].
501 ///
502 /// Colors are defined for: severe, error, warning, or info.
503 /// Returns null if the level name is not recognized.
504 String _colorOf(String levelName) {
505 const String CYAN_COLOR = '\u001b[36m';
506 const String MAGENTA_COLOR = '\u001b[35m';
507 const String RED_COLOR = '\u001b[31m';
508
509 levelName = levelName.toLowerCase();
510 if (levelName == 'shout' || levelName == 'severe' || levelName == 'error') {
511 return RED_COLOR;
512 }
513 if (levelName == 'warning') return MAGENTA_COLOR;
514 if (levelName == 'info') return CYAN_COLOR;
515 return null;
516 }
498 } 517 }
499 518
500 class _MockSdkSource implements Source { 519 class _MockSdkSource implements Source {
501 /// Absolute URI which this source can be imported from. 520 /// Absolute URI which this source can be imported from.
502 final Uri uri; 521 final Uri uri;
503 final String _contents; 522 final String _contents;
504 523
505 final int modificationStamp = 1; 524 final int modificationStamp = 1;
506 525
507 _MockSdkSource(this.uri, this._contents); 526 _MockSdkSource(this.uri, this._contents);
(...skipping 16 matching lines...) Expand all
524 UriKind get uriKind => UriKind.DART_URI; 543 UriKind get uriKind => UriKind.DART_URI;
525 544
526 bool exists() => true; 545 bool exists() => true;
527 546
528 Source resolveRelative(Uri relativeUri) => 547 Source resolveRelative(Uri relativeUri) =>
529 throw new UnsupportedError('not expecting relative urls in dart: mocks'); 548 throw new UnsupportedError('not expecting relative urls in dart: mocks');
530 549
531 Uri resolveRelativeUri(Uri relativeUri) => 550 Uri resolveRelativeUri(Uri relativeUri) =>
532 throw new UnsupportedError('not expecting relative urls in dart: mocks'); 551 throw new UnsupportedError('not expecting relative urls in dart: mocks');
533 } 552 }
OLDNEW
« no previous file with comments | « pkg/analyzer/test/src/task/strong/inferred_type_test.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698