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

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) {
Bob Nystrom 2016/02/05 22:54:39 Does this have any uses? If not, may as well leave
Jennifer Messerly 2016/02/05 23:22:19 Good catch. Removed!
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 /// Calls [addFile] for each name, content pair.
79 void addFiles(Map<String, String> files) {
80 files.forEach((name, content) {
81 addFile(content, name: name);
82 });
83 }
84
85 /// Run the checker on a program, staring from '/main.dart', and verifies that
86 /// errors/warnings/hints match the expected value.
87 ///
88 /// See [addFile] for more information about how to encode expectations in
89 /// the file text.
90 void check() {
91 _checkCalled = true;
92
93 expect(files.getFile('/main.dart').exists, true,
94 reason: '`/main.dart` is missing');
95
96 var uriResolver = new TestUriResolver(files);
97 // Enable task model strong mode
98 var context = AnalysisEngine.instance.createAnalysisContext();
99 context.analysisOptions.strongMode = true;
100 context.analysisOptions.strongModeHints = true;
101 context.sourceFactory = new SourceFactory([
102 new MockDartSdk(_mockSdkSources, reportMissing: true).resolver,
103 uriResolver
104 ]);
105
106 // Run the checker on /main.dart.
107 Source mainSource = uriResolver.resolveAbsolute(new Uri.file('/main.dart'));
108 var initialLibrary =
109 context.resolveCompilationUnit2(mainSource, mainSource);
110
111 var collector = new _ErrorCollector();
112 var checker = new CodeChecker(
113 context.typeProvider, new StrongTypeSystemImpl(), collector,
114 hints: true);
115
116 // Extract expectations from the comments in the test files, and
117 // check that all errors we emit are included in the expected map.
118 var allLibraries = reachableLibraries(initialLibrary.element.library);
119 for (var lib in allLibraries) {
120 for (var unit in lib.units) {
121 var errors = <AnalysisError>[];
122 collector.errors = errors;
123
124 var source = unit.source;
125 if (source.uri.scheme == 'dart') continue;
126
127 var librarySource = context.getLibrariesContaining(source).single;
128 var resolved = context.resolveCompilationUnit2(source, librarySource);
129 var analyzerErrors = context
130 .getErrors(source)
131 .errors
132 .where((error) =>
133 error.errorCode.name.startsWith('STRONG_MODE_INFERRED_TYPE'))
134 .toList();
135 errors.addAll(analyzerErrors);
136 checker.visitCompilationUnit(resolved);
137
138 new _ExpectedErrorVisitor(errors).validate(resolved);
139 }
140 }
141 }
34 142
35 /// Sample mock SDK sources. 143 /// Sample mock SDK sources.
36 final Map<String, String> mockSdkSources = { 144 final Map<String, String> _mockSdkSources = {
37 // The list of types below is derived from: 145 // The list of types below is derived from:
38 // * types we use via our smoke queries, including HtmlElement and 146 // * types we use via our smoke queries, including HtmlElement and
39 // types from `_typeHandlers` (deserialize.dart) 147 // types from `_typeHandlers` (deserialize.dart)
40 // * types that are used internally by the resolver (see 148 // * types that are used internally by the resolver (see
41 // _initializeFrom in resolver.dart). 149 // _initializeFrom in resolver.dart).
42 'dart:core': ''' 150 'dart:core': '''
43 library dart.core; 151 library dart.core;
44 152
45 void print(Object o) {} 153 void print(Object o) {}
46 154
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
121 229
122 'dart:_foreign_helper': ''' 230 'dart:_foreign_helper': '''
123 library dart._foreign_helper; 231 library dart._foreign_helper;
124 232
125 JS(String typeDescription, String codeTemplate, 233 JS(String typeDescription, String codeTemplate,
126 [arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11]) 234 [arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11])
127 {} 235 {}
128 ''' 236 '''
129 }; 237 };
130 238
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( 239 SourceSpanWithContext createSpanHelper(
145 LineInfo lineInfo, int start, int end, Source source, String content) { 240 LineInfo lineInfo, int start, int end, Source source, String content) {
146 var startLoc = locationForOffset(lineInfo, source.uri, start); 241 var startLoc = locationForOffset(lineInfo, source.uri, start);
147 var endLoc = locationForOffset(lineInfo, source.uri, end); 242 var endLoc = locationForOffset(lineInfo, source.uri, end);
148 243
149 var lineStart = startLoc.offset - startLoc.column; 244 var lineStart = startLoc.offset - startLoc.column;
150 // Find the end of the line. This is not exposed directly on LineInfo, but 245 // Find the end of the line. This is not exposed directly on LineInfo, but
151 // we can find it pretty easily. 246 // we can find it pretty easily.
152 // TODO(jmesserly): for now we do the simple linear scan. Ideally we can get 247 // TODO(jmesserly): for now we do the simple linear scan. Ideally we can get
153 // some help from the LineInfo API. 248 // some help from the LineInfo API.
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
188 if (seen.contains(lib)) return; 283 if (seen.contains(lib)) return;
189 seen.add(lib); 284 seen.add(lib);
190 results.add(lib); 285 results.add(lib);
191 lib.importedLibraries.forEach(find); 286 lib.importedLibraries.forEach(find);
192 lib.exportedLibraries.forEach(find); 287 lib.exportedLibraries.forEach(find);
193 } 288 }
194 find(start); 289 find(start);
195 return results; 290 return results;
196 } 291 }
197 292
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 293 /// 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. 294 /// used to speed up execution when most of the core libraries is not needed.
287 class MockDartSdk implements DartSdk { 295 class MockDartSdk implements DartSdk {
288 final Map<Uri, _MockSdkSource> _sources = {}; 296 final Map<Uri, _MockSdkSource> _sources = {};
289 final bool reportMissing; 297 final bool reportMissing;
290 final Map<String, SdkLibrary> _libs = {}; 298 final Map<String, SdkLibrary> _libs = {};
291 final String sdkVersion = '0'; 299 final String sdkVersion = '0';
292 final AnalysisContext context = new SdkAnalysisContext(); 300 final AnalysisContext context = new SdkAnalysisContext();
293 DartUriResolver _resolver; 301 DartUriResolver _resolver;
294 MockDartSdk(Map<String, String> sources, {this.reportMissing}) { 302 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'); 486 reason: 'expected different error type at:\n\n$actualMsg');
479 487
480 // We found it. Stop the search. 488 // We found it. Stop the search.
481 _actualErrors.remove(actual); 489 _actualErrors.remove(actual);
482 return; 490 return;
483 } 491 }
484 } 492 }
485 493
486 var span = _createSpan(node.offset, node.length); 494 var span = _createSpan(node.offset, node.length);
487 var levelName = expected.level.name.toLowerCase(); 495 var levelName = expected.level.name.toLowerCase();
488 var msg = span.message(expected.typeName, color: colorOf(levelName)); 496 var msg = span.message(expected.typeName, color: _colorOf(levelName));
489 fail('expected error was not reported at:\n\n$levelName: $msg'); 497 fail('expected error was not reported at:\n\n$levelName: $msg');
490 } 498 }
491 499
492 String _formatActualError(AnalysisError actual) { 500 String _formatActualError(AnalysisError actual) {
493 var span = _createSpan(actual.offset, actual.length); 501 var span = _createSpan(actual.offset, actual.length);
494 var levelName = _actualErrorLevel(actual).name.toLowerCase(); 502 var levelName = _actualErrorLevel(actual).name.toLowerCase();
495 var msg = span.message(actual.message, color: colorOf(levelName)); 503 var msg = span.message(actual.message, color: _colorOf(levelName));
496 return '$levelName: [${errorCodeName(actual.errorCode)}] $msg'; 504 return '$levelName: [${errorCodeName(actual.errorCode)}] $msg';
497 } 505 }
506
507 /// Returns an ANSII color escape sequence corresponding to [levelName]. Color s
508 /// are defined for: severe, error, warning, or info. Returns null if the leve l
Bob Nystrom 2016/02/05 22:54:39 Long lines.
Jennifer Messerly 2016/02/05 23:22:19 Fixed.
509 /// name is not recognized.
510 String _colorOf(String levelName) {
511 const String CYAN_COLOR = '\u001b[36m';
512 const String MAGENTA_COLOR = '\u001b[35m';
513 const String RED_COLOR = '\u001b[31m';
514
515 levelName = levelName.toLowerCase();
516 if (levelName == 'shout' || levelName == 'severe' || levelName == 'error') {
517 return RED_COLOR;
518 }
519 if (levelName == 'warning') return MAGENTA_COLOR;
520 if (levelName == 'info') return CYAN_COLOR;
521 return null;
522 }
498 } 523 }
499 524
500 class _MockSdkSource implements Source { 525 class _MockSdkSource implements Source {
501 /// Absolute URI which this source can be imported from. 526 /// Absolute URI which this source can be imported from.
502 final Uri uri; 527 final Uri uri;
503 final String _contents; 528 final String _contents;
504 529
505 final int modificationStamp = 1; 530 final int modificationStamp = 1;
506 531
507 _MockSdkSource(this.uri, this._contents); 532 _MockSdkSource(this.uri, this._contents);
(...skipping 16 matching lines...) Expand all
524 UriKind get uriKind => UriKind.DART_URI; 549 UriKind get uriKind => UriKind.DART_URI;
525 550
526 bool exists() => true; 551 bool exists() => true;
527 552
528 Source resolveRelative(Uri relativeUri) => 553 Source resolveRelative(Uri relativeUri) =>
529 throw new UnsupportedError('not expecting relative urls in dart: mocks'); 554 throw new UnsupportedError('not expecting relative urls in dart: mocks');
530 555
531 Uri resolveRelativeUri(Uri relativeUri) => 556 Uri resolveRelativeUri(Uri relativeUri) =>
532 throw new UnsupportedError('not expecting relative urls in dart: mocks'); 557 throw new UnsupportedError('not expecting relative urls in dart: mocks');
533 } 558 }
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