| 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 dev_compiler.src.testing; | 5 library dev_compiler.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/in_memory.dart'; |
| 20 import 'package:dev_compiler/src/info.dart'; | 21 import 'package:dev_compiler/src/info.dart'; |
| 21 import 'package:dev_compiler/src/options.dart'; | 22 import 'package:dev_compiler/src/options.dart'; |
| 22 import 'package:dev_compiler/src/report.dart'; | 23 import 'package:dev_compiler/src/report.dart'; |
| 23 import 'package:dev_compiler/config.dart'; | 24 import 'package:dev_compiler/config.dart'; |
| 24 import 'package:dev_compiler/devc.dart' show Compiler; | 25 import 'package:dev_compiler/devc.dart' show Compiler; |
| 25 | 26 |
| 26 import 'dependency_graph.dart' show runtimeFilesForServerMode; | 27 import 'dependency_graph.dart' show runtimeFilesForServerMode; |
| 27 | 28 |
| 28 /// Run the checker on a program with files contents as indicated in | 29 /// Run the checker on a program with files contents as indicated in |
| 29 /// [testFiles]. | 30 /// [testFiles]. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 54 covariantGenerics: true, relaxedCasts: true, | 55 covariantGenerics: true, relaxedCasts: true, |
| 55 inferDownwards: RulesOptions.inferDownwardsDefault, | 56 inferDownwards: RulesOptions.inferDownwardsDefault, |
| 56 inferFromOverrides: ResolverOptions.inferFromOverridesDefault, | 57 inferFromOverrides: ResolverOptions.inferFromOverridesDefault, |
| 57 inferTransitively: ResolverOptions.inferTransitivelyDefault, | 58 inferTransitively: ResolverOptions.inferTransitivelyDefault, |
| 58 nonnullableTypes: TypeOptions.NONNULLABLE_TYPES, | 59 nonnullableTypes: TypeOptions.NONNULLABLE_TYPES, |
| 59 wrapClosures: RulesOptions.wrapClosuresDefault}) { | 60 wrapClosures: RulesOptions.wrapClosuresDefault}) { |
| 60 expect(testFiles.containsKey('/main.dart'), isTrue, | 61 expect(testFiles.containsKey('/main.dart'), isTrue, |
| 61 reason: '`/main.dart` is missing in testFiles'); | 62 reason: '`/main.dart` is missing in testFiles'); |
| 62 | 63 |
| 63 // Create a resolver that can load test files from memory. | 64 // Create a resolver that can load test files from memory. |
| 64 var testUriResolver = new TestUriResolver(testFiles); | 65 var testUriResolver = new InMemoryUriResolver(testFiles); |
| 65 var options = new CompilerOptions( | 66 var options = new CompilerOptions( |
| 66 allowConstCasts: allowConstCasts, | 67 allowConstCasts: allowConstCasts, |
| 67 covariantGenerics: covariantGenerics, | 68 covariantGenerics: covariantGenerics, |
| 68 relaxedCasts: relaxedCasts, | 69 relaxedCasts: relaxedCasts, |
| 69 inferDownwards: inferDownwards, | 70 inferDownwards: inferDownwards, |
| 70 inferFromOverrides: inferFromOverrides, | 71 inferFromOverrides: inferFromOverrides, |
| 71 inferTransitively: inferTransitively, | 72 inferTransitively: inferTransitively, |
| 72 nonnullableTypes: nonnullableTypes, | 73 nonnullableTypes: nonnullableTypes, |
| 73 wrapClosures: wrapClosures, | 74 wrapClosures: wrapClosures, |
| 74 useMockSdk: sdkDir == null, | 75 useMockSdk: sdkDir == null, |
| 75 dartSdkPath: sdkDir, | 76 dartSdkPath: sdkDir, |
| 76 runtimeDir: '/dev_compiler_runtime/', | 77 runtimeDir: '/dev_compiler_runtime/', |
| 77 entryPointFile: '/main.dart'); | 78 entryPointFile: '/main.dart'); |
| 78 var resolver = sdkDir == null | 79 var resolver = sdkDir == null |
| 79 ? new TypeResolver.fromMock(mockSdkSources, options, | 80 ? new TypeResolver.fromMock(mockSdkSources, options, |
| 80 otherResolvers: [testUriResolver]) | 81 otherResolvers: [testUriResolver]) |
| 81 : new TypeResolver.fromDir(sdkDir, options, | 82 : new TypeResolver.fromDir(sdkDir, options, |
| 82 otherResolvers: [testUriResolver]); | 83 otherResolvers: [testUriResolver]); |
| 83 | 84 |
| 84 // Run the checker on /main.dart. | 85 // Run the checker on /main.dart. |
| 85 var mainFile = new Uri.file('/main.dart'); | 86 var mainFile = new Uri.file('/main.dart'); |
| 86 var checkExpectations = reporter == null; | 87 var checkExpectations = reporter == null; |
| 87 if (reporter == null) reporter = new TestReporter(); | 88 if (reporter == null) reporter = new TestReporter(); |
| 88 var results = new Compiler(options, resolver, reporter).run(); | 89 var results = |
| 90 new Compiler(options, resolver: resolver, reporter: reporter).run(); |
| 89 | 91 |
| 90 // Extract expectations from the comments in the test files. | 92 // Extract expectations from the comments in the test files. |
| 91 var expectedErrors = <AstNode, List<_ErrorExpectation>>{}; | 93 var expectedErrors = <AstNode, List<_ErrorExpectation>>{}; |
| 92 var visitor = new _ErrorMarkerVisitor(expectedErrors); | 94 var visitor = new _ErrorMarkerVisitor(expectedErrors); |
| 93 var initialLibrary = | 95 var initialLibrary = |
| 94 resolver.context.getLibraryElement(testUriResolver.files[mainFile]); | 96 resolver.context.getLibraryElement(testUriResolver.files[mainFile]); |
| 95 for (var lib in reachableLibraries(initialLibrary)) { | 97 for (var lib in reachableLibraries(initialLibrary)) { |
| 96 for (var unit in lib.units) { | 98 for (var unit in lib.units) { |
| 97 unit.unit.accept(visitor); | 99 unit.unit.accept(visitor); |
| 98 } | 100 } |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 182 } | 184 } |
| 183 | 185 |
| 184 String _messageWithSpan(StaticInfo info) { | 186 String _messageWithSpan(StaticInfo info) { |
| 185 var span = _spanFor(info.node); | 187 var span = _spanFor(info.node); |
| 186 var level = info.level.name.toLowerCase(); | 188 var level = info.level.name.toLowerCase(); |
| 187 return '$level: ${span.message(info.message, color: colorOf(level))}'; | 189 return '$level: ${span.message(info.message, color: colorOf(level))}'; |
| 188 } | 190 } |
| 189 | 191 |
| 190 SourceSpan _spanFor(AstNode node) { | 192 SourceSpan _spanFor(AstNode node) { |
| 191 var root = node.root as CompilationUnit; | 193 var root = node.root as CompilationUnit; |
| 192 TestSource source = (root.element as CompilationUnitElementImpl).source; | 194 InMemorySource source = (root.element as CompilationUnitElementImpl).source; |
| 193 return source.spanFor(node); | 195 return source.spanFor(node); |
| 194 } | 196 } |
| 195 | 197 |
| 196 /// Visitor that extracts expected errors from comments. | 198 /// Visitor that extracts expected errors from comments. |
| 197 class _ErrorMarkerVisitor extends UnifyingAstVisitor { | 199 class _ErrorMarkerVisitor extends UnifyingAstVisitor { |
| 198 Map<AstNode, List<_ErrorExpectation>> expectedErrors; | 200 Map<AstNode, List<_ErrorExpectation>> expectedErrors; |
| 199 | 201 |
| 200 _ErrorMarkerVisitor(this.expectedErrors); | 202 _ErrorMarkerVisitor(this.expectedErrors); |
| 201 | 203 |
| 202 visitNode(AstNode node) { | 204 visitNode(AstNode node) { |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 263 expect(tokens[1], "should", reason: 'invalid error descriptor'); | 265 expect(tokens[1], "should", reason: 'invalid error descriptor'); |
| 264 expect(tokens[2], "be", reason: 'invalid error descriptor'); | 266 expect(tokens[2], "be", reason: 'invalid error descriptor'); |
| 265 if (tokens[0] == "pass") return null; | 267 if (tokens[0] == "pass") return null; |
| 266 // TODO(leafp) For now, we just use whatever the current expectation is, | 268 // TODO(leafp) For now, we just use whatever the current expectation is, |
| 267 // eventually we could do more automated reporting here. | 269 // eventually we could do more automated reporting here. |
| 268 return _parse(tokens[0]); | 270 return _parse(tokens[0]); |
| 269 } | 271 } |
| 270 | 272 |
| 271 String toString() => '$level $type'; | 273 String toString() => '$level $type'; |
| 272 } | 274 } |
| 273 | |
| 274 /// Uri resolver that can load test files from memory. | |
| 275 class TestUriResolver extends UriResolver { | |
| 276 final Map<Uri, TestSource> files = <Uri, TestSource>{}; | |
| 277 | |
| 278 /// Whether to represent a non-existing file with a [TestSource] (default | |
| 279 /// behavior from analyzer), or to use null (possible when overriding the | |
| 280 /// package-url-resolvers.) | |
| 281 final bool representNonExistingFiles; | |
| 282 | |
| 283 TestUriResolver(Map<String, String> allFiles, | |
| 284 {this.representNonExistingFiles: true}) { | |
| 285 allFiles.forEach((key, value) { | |
| 286 var uri = key.startsWith('package:') ? Uri.parse(key) : new Uri.file(key); | |
| 287 files[uri] = new TestSource(uri, value); | |
| 288 }); | |
| 289 | |
| 290 runtimeFilesForServerMode.forEach((filepath) { | |
| 291 var uri = Uri.parse('/dev_compiler_runtime/$filepath'); | |
| 292 files[uri] = new TestSource(uri, '/* test contents of $filepath */'); | |
| 293 }); | |
| 294 } | |
| 295 | |
| 296 Source resolveAbsolute(Uri uri) { | |
| 297 if (uri.scheme != 'file' && uri.scheme != 'package') return null; | |
| 298 if (!representNonExistingFiles) return files[uri]; | |
| 299 return files.putIfAbsent(uri, () => new TestSource(uri, null)); | |
| 300 } | |
| 301 } | |
| 302 | |
| 303 class TestContents implements TimestampedData<String> { | |
| 304 int modificationTime; | |
| 305 String data; | |
| 306 | |
| 307 TestContents(this.modificationTime, this.data); | |
| 308 } | |
| 309 | |
| 310 /// An in memory source file. | |
| 311 class TestSource implements Source { | |
| 312 final Uri uri; | |
| 313 TestContents contents; | |
| 314 final SourceFile _file; | |
| 315 final UriKind uriKind; | |
| 316 | |
| 317 TestSource(uri, contents) | |
| 318 : uri = uri, | |
| 319 contents = new TestContents(1, contents), | |
| 320 _file = contents != null ? new SourceFile(contents, url: uri) : null, | |
| 321 uriKind = uri.scheme == 'file' ? UriKind.FILE_URI : UriKind.PACKAGE_URI; | |
| 322 | |
| 323 bool exists() => contents.data != null; | |
| 324 | |
| 325 Source get source => this; | |
| 326 | |
| 327 String _encoding; | |
| 328 String get encoding => _encoding != null ? _encoding : (_encoding = '$uri'); | |
| 329 | |
| 330 String get fullName => uri.path; | |
| 331 | |
| 332 int get modificationStamp => contents.modificationTime; | |
| 333 String get shortName => path.basename(uri.path); | |
| 334 | |
| 335 operator ==(other) => other is TestSource && uri == other.uri; | |
| 336 int get hashCode => uri.hashCode; | |
| 337 bool get isInSystemLibrary => false; | |
| 338 | |
| 339 Uri resolveRelativeUri(Uri relativeUri) => uri.resolveUri(relativeUri); | |
| 340 | |
| 341 SourceSpan spanFor(AstNode node) { | |
| 342 final begin = node is AnnotatedNode | |
| 343 ? node.firstTokenAfterCommentAndMetadata.offset | |
| 344 : node.offset; | |
| 345 return _file.span(begin, node.end); | |
| 346 } | |
| 347 | |
| 348 String toString() => '[$runtimeType: $uri]'; | |
| 349 } | |
| OLD | NEW |