| OLD | NEW |
| 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 library ddc.src.testing; | 5 library ddc.src.testing; |
| 6 | 6 |
| 7 import 'package:analyzer/src/generated/ast.dart'; | 7 import 'package:analyzer/src/generated/ast.dart'; |
| 8 import 'package:analyzer/src/generated/element.dart'; | 8 import 'package:analyzer/src/generated/element.dart'; |
| 9 import 'package:analyzer/src/generated/engine.dart' show TimestampedData; | 9 import 'package:analyzer/src/generated/engine.dart' show TimestampedData; |
| 10 import 'package:analyzer/src/generated/source.dart'; | 10 import 'package:analyzer/src/generated/source.dart'; |
| 11 import 'package:logging/logging.dart'; | 11 import 'package:logging/logging.dart'; |
| 12 import 'package:path/path.dart' as path; | 12 import 'package:path/path.dart' as path; |
| 13 import 'package:source_span/source_span.dart'; | 13 import 'package:source_span/source_span.dart'; |
| 14 import 'package:unittest/unittest.dart'; | 14 import 'package:unittest/unittest.dart'; |
| 15 | 15 |
| 16 import 'package:dev_compiler/src/checker/dart_sdk.dart' | 16 import 'package:dev_compiler/src/checker/dart_sdk.dart' |
| 17 show mockSdkSources, dartSdkDirectory; | 17 show mockSdkSources, dartSdkDirectory; |
| 18 import 'package:dev_compiler/src/checker/resolver.dart' show TypeResolver; | 18 import 'package:dev_compiler/src/checker/resolver.dart' show TypeResolver; |
| 19 import 'package:dev_compiler/src/utils.dart'; | 19 import 'package:dev_compiler/src/utils.dart'; |
| 20 import 'package:dev_compiler/src/info.dart'; | 20 import 'package:dev_compiler/src/info.dart'; |
| 21 import 'package:dev_compiler/src/options.dart'; | 21 import 'package:dev_compiler/src/options.dart'; |
| 22 import 'package:dev_compiler/src/report.dart'; | 22 import 'package:dev_compiler/src/report.dart'; |
| 23 import 'package:dev_compiler/config.dart'; | 23 import 'package:dev_compiler/config.dart'; |
| 24 import 'package:dev_compiler/devc.dart' show compile; | 24 import 'package:dev_compiler/devc.dart' show Compiler; |
| 25 | 25 |
| 26 /// Run the checker on a program with files contents as indicated in | 26 /// Run the checker on a program with files contents as indicated in |
| 27 /// [testFiles]. | 27 /// [testFiles]. |
| 28 /// | 28 /// |
| 29 /// This function makes several assumptions to make it easier to describe error | 29 /// This function makes several assumptions to make it easier to describe error |
| 30 /// expectations: | 30 /// expectations: |
| 31 /// | 31 /// |
| 32 /// * a file named `/main.dart` exists in [testFiles]. | 32 /// * a file named `/main.dart` exists in [testFiles]. |
| 33 /// * all expected failures are listed in the source code using comments | 33 /// * all expected failures are listed in the source code using comments |
| 34 /// immediately in front of the AST node that should contain the error. | 34 /// immediately in front of the AST node that should contain the error. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 49 /// | 49 /// |
| 50 CheckerResults testChecker(Map<String, String> testFiles, | 50 CheckerResults testChecker(Map<String, String> testFiles, |
| 51 {bool allowConstCasts: true, String sdkDir, CheckerReporter reporter, | 51 {bool allowConstCasts: true, String sdkDir, CheckerReporter reporter, |
| 52 covariantGenerics: true, relaxedCasts: true, inferFromOverrides: true, | 52 covariantGenerics: true, relaxedCasts: true, inferFromOverrides: true, |
| 53 inferStaticsFromIdentifiers: false, inferInNonStableOrder: false, | 53 inferStaticsFromIdentifiers: false, inferInNonStableOrder: false, |
| 54 nonnullableTypes: TypeOptions.NONNULLABLE_TYPES}) { | 54 nonnullableTypes: TypeOptions.NONNULLABLE_TYPES}) { |
| 55 expect(testFiles.containsKey('/main.dart'), isTrue, | 55 expect(testFiles.containsKey('/main.dart'), isTrue, |
| 56 reason: '`/main.dart` is missing in testFiles'); | 56 reason: '`/main.dart` is missing in testFiles'); |
| 57 | 57 |
| 58 // Create a resolver that can load test files from memory. | 58 // Create a resolver that can load test files from memory. |
| 59 var testUriResolver = new _TestUriResolver(testFiles); | 59 var testUriResolver = new TestUriResolver(testFiles); |
| 60 var options = new CompilerOptions( | 60 var options = new CompilerOptions( |
| 61 allowConstCasts: allowConstCasts, | 61 allowConstCasts: allowConstCasts, |
| 62 covariantGenerics: covariantGenerics, | 62 covariantGenerics: covariantGenerics, |
| 63 relaxedCasts: relaxedCasts, | 63 relaxedCasts: relaxedCasts, |
| 64 inferFromOverrides: inferFromOverrides, | 64 inferFromOverrides: inferFromOverrides, |
| 65 inferStaticsFromIdentifiers: inferStaticsFromIdentifiers, | 65 inferStaticsFromIdentifiers: inferStaticsFromIdentifiers, |
| 66 inferInNonStableOrder: inferInNonStableOrder, | 66 inferInNonStableOrder: inferInNonStableOrder, |
| 67 nonnullableTypes: nonnullableTypes); | 67 nonnullableTypes: nonnullableTypes, |
| 68 useMockSdk: sdkDir == null, |
| 69 dartSdkPath: sdkDir, |
| 70 entryPointFile: '/main.dart'); |
| 68 var resolver = sdkDir == null | 71 var resolver = sdkDir == null |
| 69 ? new TypeResolver.fromMock(mockSdkSources, options, | 72 ? new TypeResolver.fromMock(mockSdkSources, options, |
| 70 otherResolvers: [testUriResolver]) | 73 otherResolvers: [testUriResolver]) |
| 71 : new TypeResolver.fromDir(sdkDir, options, | 74 : new TypeResolver.fromDir(sdkDir, options, |
| 72 otherResolvers: [testUriResolver]); | 75 otherResolvers: [testUriResolver]); |
| 73 | 76 |
| 74 // Run the checker on /main.dart. | 77 // Run the checker on /main.dart. |
| 75 var mainFile = new Uri.file('/main.dart'); | 78 var mainFile = new Uri.file('/main.dart'); |
| 76 var checkExpectations = reporter == null; | 79 var checkExpectations = reporter == null; |
| 77 if (reporter == null) reporter = new TestReporter(); | 80 if (reporter == null) reporter = new TestReporter(); |
| 78 var results = compile('/main.dart', resolver, options, reporter); | 81 var results = new Compiler(options, resolver, reporter).run(); |
| 79 | 82 |
| 80 // Extract expectations from the comments in the test files. | 83 // Extract expectations from the comments in the test files. |
| 81 var expectedErrors = <AstNode, List<_ErrorExpectation>>{}; | 84 var expectedErrors = <AstNode, List<_ErrorExpectation>>{}; |
| 82 var visitor = new _ErrorMarkerVisitor(expectedErrors); | 85 var visitor = new _ErrorMarkerVisitor(expectedErrors); |
| 83 var initialLibrary = | 86 var initialLibrary = |
| 84 resolver.context.getLibraryElement(testUriResolver.files[mainFile]); | 87 resolver.context.getLibraryElement(testUriResolver.files[mainFile]); |
| 85 for (var lib in reachableLibraries(initialLibrary)) { | 88 for (var lib in reachableLibraries(initialLibrary)) { |
| 86 for (var unit in lib.units) { | 89 for (var unit in lib.units) { |
| 87 unit.unit.accept(visitor); | 90 unit.unit.accept(visitor); |
| 88 } | 91 } |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 } | 172 } |
| 170 | 173 |
| 171 String _messageWithSpan(StaticInfo info) { | 174 String _messageWithSpan(StaticInfo info) { |
| 172 var span = _spanFor(info.node); | 175 var span = _spanFor(info.node); |
| 173 var level = info.level.name.toLowerCase(); | 176 var level = info.level.name.toLowerCase(); |
| 174 return '$level: ${span.message(info.message, color: colorOf(level))}'; | 177 return '$level: ${span.message(info.message, color: colorOf(level))}'; |
| 175 } | 178 } |
| 176 | 179 |
| 177 SourceSpan _spanFor(AstNode node) { | 180 SourceSpan _spanFor(AstNode node) { |
| 178 var root = node.root as CompilationUnit; | 181 var root = node.root as CompilationUnit; |
| 179 _TestSource source = (root.element as CompilationUnitElementImpl).source; | 182 TestSource source = (root.element as CompilationUnitElementImpl).source; |
| 180 return source.spanFor(node); | 183 return source.spanFor(node); |
| 181 } | 184 } |
| 182 | 185 |
| 183 /// Visitor that extracts expected errors from comments. | 186 /// Visitor that extracts expected errors from comments. |
| 184 class _ErrorMarkerVisitor extends UnifyingAstVisitor { | 187 class _ErrorMarkerVisitor extends UnifyingAstVisitor { |
| 185 Map<AstNode, List<_ErrorExpectation>> expectedErrors; | 188 Map<AstNode, List<_ErrorExpectation>> expectedErrors; |
| 186 | 189 |
| 187 _ErrorMarkerVisitor(this.expectedErrors); | 190 _ErrorMarkerVisitor(this.expectedErrors); |
| 188 | 191 |
| 189 visitNode(AstNode node) { | 192 visitNode(AstNode node) { |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 252 if (tokens[0] == "pass") return null; | 255 if (tokens[0] == "pass") return null; |
| 253 // TODO(leafp) For now, we just use whatever the current expectation is, | 256 // TODO(leafp) For now, we just use whatever the current expectation is, |
| 254 // eventually we could do more automated reporting here. | 257 // eventually we could do more automated reporting here. |
| 255 return _parse(tokens[0]); | 258 return _parse(tokens[0]); |
| 256 } | 259 } |
| 257 | 260 |
| 258 String toString() => '$level $type'; | 261 String toString() => '$level $type'; |
| 259 } | 262 } |
| 260 | 263 |
| 261 /// Uri resolver that can load test files from memory. | 264 /// Uri resolver that can load test files from memory. |
| 262 class _TestUriResolver extends UriResolver { | 265 class TestUriResolver extends UriResolver { |
| 263 final Map<Uri, _TestSource> files = <Uri, _TestSource>{}; | 266 final Map<Uri, TestSource> files = <Uri, TestSource>{}; |
| 264 | 267 |
| 265 _TestUriResolver(Map<String, String> allFiles) { | 268 TestUriResolver(Map<String, String> allFiles) { |
| 266 allFiles.forEach((key, value) { | 269 allFiles.forEach((key, value) { |
| 267 var uri = key.startsWith('package:') ? Uri.parse(key) : new Uri.file(key); | 270 var uri = key.startsWith('package:') ? Uri.parse(key) : new Uri.file(key); |
| 268 files[uri] = new _TestSource(uri, value); | 271 files[uri] = new TestSource(uri, value); |
| 269 }); | 272 }); |
| 270 } | 273 } |
| 271 | 274 |
| 272 Source resolveAbsolute(Uri uri) { | 275 Source resolveAbsolute(Uri uri) { |
| 273 if (uri.scheme != 'file' && uri.scheme != 'package') return null; | 276 if (uri.scheme != 'file' && uri.scheme != 'package') return null; |
| 274 return files[uri]; | 277 return files.putIfAbsent(uri, () => new TestSource(uri, null)); |
| 275 } | 278 } |
| 276 } | 279 } |
| 277 | 280 |
| 281 class TestContents implements TimestampedData<String> { |
| 282 int modificationTime; |
| 283 String data; |
| 284 |
| 285 TestContents(this.modificationTime, this.data); |
| 286 } |
| 287 |
| 278 /// An in memory source file. | 288 /// An in memory source file. |
| 279 class _TestSource implements Source { | 289 class TestSource implements Source { |
| 280 final Uri uri; | 290 final Uri uri; |
| 281 final TimestampedData<String> contents; | 291 TestContents contents; |
| 282 final SourceFile _file; | 292 final SourceFile _file; |
| 283 final UriKind uriKind; | 293 final UriKind uriKind; |
| 294 bool _exists; |
| 284 | 295 |
| 285 _TestSource(uri, contents) | 296 TestSource(uri, contents) |
| 286 : uri = uri, | 297 : uri = uri, |
| 287 contents = new TimestampedData<String>(0, contents), | 298 _exists = contents != null, |
| 288 _file = new SourceFile(contents, url: uri), | 299 contents = new TestContents(1, contents), |
| 300 _file = contents != null ? new SourceFile(contents, url: uri) : null, |
| 289 uriKind = uri.scheme == 'file' ? UriKind.FILE_URI : UriKind.PACKAGE_URI; | 301 uriKind = uri.scheme == 'file' ? UriKind.FILE_URI : UriKind.PACKAGE_URI; |
| 290 | 302 |
| 291 bool exists() => true; | 303 bool exists() => _exists; |
| 292 | 304 |
| 293 Source get source => this; | 305 Source get source => this; |
| 294 | 306 |
| 295 String _encoding; | 307 String _encoding; |
| 296 String get encoding => _encoding != null ? _encoding : (_encoding = '$uri'); | 308 String get encoding => _encoding != null ? _encoding : (_encoding = '$uri'); |
| 297 | 309 |
| 298 String get fullName => uri.path; | 310 String get fullName => uri.path; |
| 299 | 311 |
| 300 int get modificationStamp => 0; | 312 int get modificationStamp => contents.modificationTime; |
| 301 String get shortName => path.basename(uri.path); | 313 String get shortName => path.basename(uri.path); |
| 302 | 314 |
| 303 operator ==(other) => other is _TestSource && uri == other.uri; | 315 operator ==(other) => other is TestSource && uri == other.uri; |
| 304 int get hashCode => uri.hashCode; | 316 int get hashCode => uri.hashCode; |
| 305 bool get isInSystemLibrary => false; | 317 bool get isInSystemLibrary => false; |
| 306 | 318 |
| 307 Uri resolveRelativeUri(Uri relativeUri) => uri.resolveUri(relativeUri); | 319 Uri resolveRelativeUri(Uri relativeUri) => uri.resolveUri(relativeUri); |
| 308 | 320 |
| 309 SourceSpan spanFor(AstNode node) { | 321 SourceSpan spanFor(AstNode node) { |
| 310 final begin = node is AnnotatedNode | 322 final begin = node is AnnotatedNode |
| 311 ? node.firstTokenAfterCommentAndMetadata.offset | 323 ? node.firstTokenAfterCommentAndMetadata.offset |
| 312 : node.offset; | 324 : node.offset; |
| 313 return _file.span(begin, node.end); | 325 return _file.span(begin, node.end); |
| 314 } | 326 } |
| 315 | 327 |
| 316 String toString() => '[$runtimeType: $uri]'; | 328 String toString() => '[$runtimeType: $uri]'; |
| 317 } | 329 } |
| OLD | NEW |