Index: test/codegen_test.dart |
diff --git a/test/codegen_test.dart b/test/codegen_test.dart |
index 9475dcaffab0a5864493b700d694bd6b00df2fe7..d16305e9b6d41899b0ecf8855cf2fd8ef7bfe43b 100644 |
--- a/test/codegen_test.dart |
+++ b/test/codegen_test.dart |
@@ -1,4 +1,4 @@ |
-// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
@@ -7,154 +7,60 @@ |
/// that the output is what we expected. |
library dev_compiler.test.codegen_test; |
-import 'dart:io'; |
-import 'package:analyzer/src/generated/engine.dart' |
- show AnalysisContext, AnalysisEngine, Logger; |
-import 'package:analyzer/src/generated/java_engine.dart' show CaughtException; |
-import 'package:analyzer/src/generated/source_io.dart'; |
-import 'package:args/args.dart'; |
-import 'package:cli_util/cli_util.dart' show getSdkDir; |
-import 'package:logging/logging.dart' show Level; |
+import 'dart:convert' show JSON; |
+import 'dart:io' show Directory, File, Platform; |
+import 'package:args/args.dart' show ArgParser, ArgResults; |
import 'package:path/path.dart' as path; |
-import 'package:test/test.dart'; |
- |
-import 'package:dev_compiler/devc.dart'; |
-import 'package:dev_compiler/src/analysis_context.dart'; |
-import 'package:dev_compiler/src/compiler.dart' show defaultRuntimeFiles; |
-import 'package:dev_compiler/src/options.dart'; |
-import 'package:dev_compiler/src/report.dart' show LogReporter; |
- |
+import 'package:test/test.dart' show group, test; |
+ |
+import 'package:analyzer/analyzer.dart' |
+ show |
+ ExportDirective, |
+ ImportDirective, |
+ StringLiteral, |
+ UriBasedDirective, |
+ parseDirectives; |
+import 'package:analyzer/src/generated/source.dart' show Source; |
+import 'package:dev_compiler/src/analyzer/context.dart' show AnalyzerOptions; |
+import 'package:dev_compiler/src/compiler/compiler.dart' |
+ show BuildUnit, CompilerOptions, ModuleCompiler; |
import 'testing.dart' show testDirectory; |
-import 'multitest.dart'; |
+import 'multitest.dart' show extractTestsFromMultitest, isMultiTest; |
+import '../tool/build_sdk.dart' as build_sdk; |
+import 'package:dev_compiler/src/compiler/compiler.dart'; |
final ArgParser argParser = new ArgParser() |
..addOption('dart-sdk', help: 'Dart SDK Path', defaultsTo: null); |
-final inputDir = path.join(testDirectory, 'codegen'); |
- |
-Iterable<String> _findTests(String dir, RegExp filePattern) { |
- var files = new Directory(dir) |
- .listSync() |
- .where((f) => f is File) |
- .map((f) => f.path) |
- .where((p) => p.endsWith('.dart') && filePattern.hasMatch(p)); |
- if (dir != inputDir) { |
- files = files |
- .where((p) => p.endsWith('_test.dart') || p.endsWith('_multi.dart')); |
- } |
- return files; |
-} |
- |
main(arguments) { |
if (arguments == null) arguments = []; |
ArgResults args = argParser.parse(arguments); |
var filePattern = new RegExp(args.rest.length > 0 ? args.rest[0] : '.'); |
- var compilerMessages = new StringBuffer(); |
- var loggerSub; |
- |
- bool codeCoverage = Platform.environment.containsKey('COVERALLS_TOKEN'); |
- |
- setUp(() { |
- compilerMessages.clear(); |
- loggerSub = setupLogger(Level.CONFIG, compilerMessages.writeln); |
- }); |
- |
- tearDown(() { |
- if (loggerSub != null) { |
- loggerSub.cancel(); |
- loggerSub = null; |
- } |
- }); |
var expectDir = path.join(inputDir, 'expect'); |
+ var testDirs = ['language', path.join('lib', 'typed_data')]; |
- BatchCompiler createCompiler(DartUriResolver sdkResolver, |
- {bool checkSdk: false, |
- bool sourceMaps: false, |
- bool destructureNamedParams: false, |
- bool closure: false, |
- ModuleFormat moduleFormat: ModuleFormat.legacy}) { |
- String _testCodegenPath(String p1, [String p2]) => |
- path.join(testDirectory, 'codegen', p1, p2); |
- |
- var context = createAnalysisContextWithSources( |
- new SourceResolverOptions(customUrlMappings: { |
- 'package:expect/expect.dart': _testCodegenPath('expect.dart'), |
- 'package:async_helper/async_helper.dart': |
- _testCodegenPath('async_helper.dart'), |
- 'package:unittest/unittest.dart': _testCodegenPath('unittest.dart'), |
- 'package:dom/dom.dart': _testCodegenPath('sunflower', 'dom.dart') |
- }), |
- sdkResolver: sdkResolver); |
- |
- // TODO(jmesserly): add a way to specify flags in the test file, so |
- // they're more self-contained. |
- var runtimeDir = path.join(path.dirname(testDirectory), 'lib', 'runtime'); |
- var options = new CompilerOptions( |
- codegenOptions: new CodegenOptions( |
- outputDir: expectDir, |
- emitSourceMaps: sourceMaps, |
- closure: closure, |
- destructureNamedParams: destructureNamedParams, |
- forceCompile: checkSdk, |
- moduleFormat: moduleFormat), |
- useColors: false, |
- checkSdk: checkSdk, |
- runtimeDir: runtimeDir, |
- inputBaseDir: inputDir); |
- var reporter = new LogReporter(context); |
- return new BatchCompiler(context, options, reporter: reporter); |
- } |
+ var multitests = expandMultiTests(testDirs, filePattern); |
- bool compile(BatchCompiler compiler, String filePath) { |
- compiler.compileFromUriString(filePath, (String url) { |
- // Write compiler messages to disk. |
- var messagePath = '${path.withoutExtension(url)}.txt'; |
- var file = new File(messagePath); |
- var message = ''' |
-// Messages from compiling ${path.basenameWithoutExtension(url)}.dart |
-$compilerMessages'''; |
- var dir = file.parent; |
- if (!dir.existsSync()) dir.createSync(recursive: true); |
- file.writeAsStringSync(message); |
- compilerMessages.clear(); |
- }); |
- return !compiler.failure; |
- } |
+ // Build packages tests depend on |
+ var compiler = new ModuleCompiler( |
+ new AnalyzerOptions(customUrlMappings: packageUrlMappings)); |
- var testDirs = <String>['language', path.join('lib', 'typed_data')]; |
+ group('dartdevc package', () { |
+ _buildPackages(compiler, expectDir); |
- var multitests = new Set<String>(); |
- { |
- // Expand wacky multitests into a bunch of test files. |
- // We'll compile each one as if it was an input. |
- for (var testDir in testDirs) { |
- var fullDir = path.join(inputDir, testDir); |
- var testFiles = _findTests(fullDir, filePattern); |
+ test('matcher', () { |
+ _buildMatcher(compiler, expectDir); |
+ }); |
+ }); |
- for (var filePath in testFiles) { |
- if (filePath.endsWith('_multi.dart')) continue; |
- |
- var contents = new File(filePath).readAsStringSync(); |
- if (isMultiTest(contents)) { |
- multitests.add(filePath); |
- |
- var tests = new Map<String, String>(); |
- var outcomes = new Map<String, Set<String>>(); |
- extractTestsFromMultitest(filePath, contents, tests, outcomes); |
- |
- var filename = path.basenameWithoutExtension(filePath); |
- tests.forEach((name, contents) { |
- new File(path.join(fullDir, '${filename}_${name}_multi.dart')) |
- .writeAsStringSync(contents); |
- }); |
- } |
- } |
- } |
- } |
+ test('dartdevc sunflower', () { |
+ _buildSunflower(compiler, expectDir); |
+ }); |
- var realSdkResolver = createSdkPathResolver(getSdkDir().path); |
- var batchCompiler = createCompiler(realSdkResolver); |
+ // Our default compiler options. Individual tests can override these. |
+ var defaultOptions = ['--no-source-map', '--no-summarize']; |
+ var compilerArgParser = CompilerOptions.addArguments(new ArgParser()); |
var allDirs = [null]; |
allDirs.addAll(testDirs); |
@@ -172,116 +78,204 @@ $compilerMessages'''; |
var filename = path.basenameWithoutExtension(filePath); |
test('$filename.dart', () { |
- // TODO(jmesserly): this was added to get some coverage of source maps |
- // and closure annotations. |
- // We need a more comprehensive strategy to test them. |
- var sourceMaps = filename == 'map_keys'; |
- var closure = filename == 'closure'; |
- var destructureNamedParams = filename == 'destructuring' || closure; |
- var moduleFormat = filename == 'es6_modules' || closure |
- ? ModuleFormat.es6 |
- : filename == 'node_modules' |
- ? ModuleFormat.node |
- : ModuleFormat.legacy; |
- var success; |
- // TODO(vsm): Is it okay to reuse the same context here? If there is |
- // overlap between test files, we may need separate ones for each |
- // compiler. |
- var compiler = (sourceMaps || |
- closure || |
- destructureNamedParams || |
- moduleFormat != ModuleFormat.legacy) |
- ? createCompiler(realSdkResolver, |
- sourceMaps: sourceMaps, |
- destructureNamedParams: destructureNamedParams, |
- closure: closure, |
- moduleFormat: moduleFormat) |
- : batchCompiler; |
- success = compile(compiler, filePath); |
- |
- var outFile = new File(path.join(outDir.path, '$filename.js')); |
- expect(!success || outFile.existsSync(), true, |
- reason: '${outFile.path} was created if compilation succeeds'); |
+ // Check if we need to use special compile options. |
+ var contents = new File(filePath).readAsStringSync(); |
+ var match = |
+ new RegExp(r'// compile options: (.*)').matchAsPrefix(contents); |
+ |
+ var args = new List.from(defaultOptions); |
+ if (match != null) { |
+ args.addAll(match.group(1).split(' ')); |
+ } |
+ var options = |
+ new CompilerOptions.fromArguments(compilerArgParser.parse(args)); |
+ |
+ // Collect any other files we've imported. |
+ var files = new Set<String>(); |
+ _collectTransitiveImports(contents, files, from: filePath); |
+ |
+ var unit = new BuildUnit(filename, files.toList(), _moduleForLibrary); |
+ var module = compiler.compile(unit, options); |
+ _writeModule(path.join(outDir.path, filename), module); |
}); |
} |
}); |
} |
if (codeCoverage) { |
- group('sdk', () { |
- // The analyzer does not bubble exception messages for certain internal |
- // dart:* library failures, such as failing to find |
- // "_internal/libraries.dart". Instead it produces an opaque "failed to |
- // instantiate dart:core" message. To remedy this we hook up an analysis |
- // logger that prints these messages. |
- var savedLogger; |
- setUp(() { |
- savedLogger = AnalysisEngine.instance.logger; |
- AnalysisEngine.instance.logger = new PrintLogger(); |
- }); |
- tearDown(() { |
- AnalysisEngine.instance.logger = savedLogger; |
- }); |
- |
- test('devc dart:core', () { |
- var testSdkResolver = createSdkPathResolver( |
- path.join(testDirectory, '..', 'tool', 'generated_sdk')); |
- |
- // Get the test SDK. We use a checked in copy so test expectations can |
- // be generated against a specific SDK version. |
- var compiler = createCompiler(testSdkResolver, checkSdk: true); |
- compile(compiler, 'dart:core'); |
- var outFile = new File(path.join(expectDir, 'dart/core.js')); |
- expect(outFile.existsSync(), true, |
- reason: '${outFile.path} was created for dart:core'); |
- }); |
+ test('build_sdk code coverage', () { |
+ var generatedSdkDir = |
+ path.join(testDirectory, '..', 'tool', 'generated_sdk'); |
+ return build_sdk.main(['--dart-sdk', generatedSdkDir, '-o', expectDir]); |
}); |
} |
+} |
- var expectedRuntime = |
- defaultRuntimeFiles.map((f) => 'dev_compiler/runtime/$f'); |
- |
- test('devc jscodegen sunflower.html', () { |
- var filePath = path.join(inputDir, 'sunflower', 'sunflower.html'); |
- var success = compile(batchCompiler, filePath); |
+void _writeModule(String outPath, JSModuleFile result) { |
+ new Directory(path.dirname(outPath)).createSync(recursive: true); |
- var expectedFiles = ['sunflower.html', 'sunflower.js',]; |
+ result.errors.add(''); // for trailing newline |
+ new File(outPath + '.txt').writeAsStringSync(result.errors.join('\n')); |
- for (var filepath in expectedFiles) { |
- var outFile = new File(path.join(expectDir, 'sunflower', filepath)); |
- expect(outFile.existsSync(), success, |
- reason: '${outFile.path} was created iff compilation succeeds'); |
+ if (result.isValid) { |
+ new File(outPath + '.js').writeAsStringSync(result.code); |
+ if (result.sourceMap != null) { |
+ var mapPath = outPath + '.js.map'; |
+ new File(mapPath) |
+ .writeAsStringSync(JSON.encode(result.placeSourceMap(mapPath))); |
} |
- }); |
+ } |
+} |
+ |
+void _buildSunflower(ModuleCompiler compiler, String expectDir) { |
+ var files = ['sunflower', 'circle', 'painter'] |
+ .map((f) => path.join(inputDir, 'sunflower', '$f.dart')) |
+ .toList(); |
+ var input = new BuildUnit('sunflower', files, _moduleForLibrary); |
+ var options = new CompilerOptions(summarizeApi: false); |
- test('devc jscodegen html_input.html', () { |
- var filePath = path.join(inputDir, 'html_input.html'); |
- var success = compile(batchCompiler, filePath); |
- |
- var expectedFiles = [ |
- 'html_input.html', |
- 'dir/html_input_a.js', |
- 'dir/html_input_b.js', |
- 'dir/html_input_c.js', |
- 'dir/html_input_d.js', |
- 'dir/html_input_e.js' |
- ]..addAll(expectedRuntime); |
- |
- for (var filepath in expectedFiles) { |
- var outFile = new File(path.join(expectDir, filepath)); |
- expect(outFile.existsSync(), success, |
- reason: '${outFile.path} was created iff compilation succeeds'); |
+ var built = compiler.compile(input, options); |
+ _writeModule(path.join(expectDir, 'sunflower', 'sunflower'), built); |
+} |
+ |
+void _buildPackages(ModuleCompiler compiler, String expectDir) { |
+ // Note: we don't summarize these, as we're going to rely on our in-memory |
+ // shared analysis context for caching, and `_moduleForLibrary` below |
+ // understands these are from other modules. |
+ var options = new CompilerOptions(sourceMap: false, summarizeApi: false); |
+ |
+ for (var uri in packageUrlMappings.keys) { |
+ assert(uri.startsWith('package:')); |
+ var uriPath = uri.substring('package:'.length); |
+ var name = path.basenameWithoutExtension(uriPath); |
+ test(name, () { |
+ var input = new BuildUnit(name, [uri], _moduleForLibrary); |
+ var built = compiler.compile(input, options); |
+ |
+ var outPath = path.join(expectDir, path.withoutExtension(uriPath)); |
+ _writeModule(outPath, built); |
+ }); |
+ } |
+} |
+ |
+void _buildMatcher(ModuleCompiler compiler, String expectDir) { |
+ var options = new CompilerOptions(sourceMap: false, summarizeApi: false); |
+ |
+ var filePath = path.join(inputDir, 'packages', 'matcher', 'matcher.dart'); |
+ var contents = new File(filePath).readAsStringSync(); |
+ |
+ // Collect any other files we've imported. |
+ var files = new Set<String>(); |
+ _collectTransitiveImports(contents, files, from: filePath); |
+ |
+ var unit = new BuildUnit('matcher', files.toList(), _moduleForLibrary); |
+ var module = compiler.compile(unit, options); |
+ |
+ var outPath = path.join(expectDir, 'matcher', 'matcher'); |
+ _writeModule(outPath, module); |
+} |
+ |
+String _moduleForLibrary(Source source) { |
+ var scheme = source.uri.scheme; |
+ if (scheme == 'package') { |
+ return source.uri.pathSegments.first; |
+ } |
+ throw new Exception('Module not found for library "${source.fullName}"'); |
+} |
+ |
+/// Expands wacky multitests into a bunch of test files. |
+/// |
+/// We'll compile each one as if it was an input. |
+/// NOTE: this will write the individual test files to disk. |
+Set<String> expandMultiTests(List testDirs, RegExp filePattern) { |
+ var multitests = new Set<String>(); |
+ |
+ for (var testDir in testDirs) { |
+ var fullDir = path.join(inputDir, testDir); |
+ var testFiles = _findTests(fullDir, filePattern); |
+ |
+ for (var filePath in testFiles) { |
+ if (filePath.endsWith('_multi.dart')) continue; |
+ |
+ var contents = new File(filePath).readAsStringSync(); |
+ if (isMultiTest(contents)) { |
+ multitests.add(filePath); |
+ |
+ var tests = new Map<String, String>(); |
+ var outcomes = new Map<String, Set<String>>(); |
+ extractTestsFromMultitest(filePath, contents, tests, outcomes); |
+ |
+ var filename = path.basenameWithoutExtension(filePath); |
+ tests.forEach((name, contents) { |
+ new File(path.join(fullDir, '${filename}_${name}_multi.dart')) |
+ .writeAsStringSync(contents); |
+ }); |
+ } |
} |
- }); |
+ } |
+ return multitests; |
+} |
+ |
+// TODO(jmesserly): switch this to a .packages file. |
+final packageUrlMappings = { |
+ 'package:expect/expect.dart': path.join(inputDir, 'expect.dart'), |
+ 'package:async_helper/async_helper.dart': |
+ path.join(inputDir, 'async_helper.dart'), |
+ 'package:unittest/unittest.dart': path.join(inputDir, 'unittest.dart'), |
+ 'package:js/js.dart': path.join(inputDir, 'packages', 'js', 'js.dart') |
+}; |
+ |
+final codeCoverage = Platform.environment.containsKey('COVERALLS_TOKEN'); |
+ |
+final inputDir = path.join(testDirectory, 'codegen'); |
+ |
+Iterable<String> _findTests(String dir, RegExp filePattern) { |
+ var files = new Directory(dir) |
+ .listSync() |
+ .where((f) => f is File) |
+ .map((f) => f.path) |
+ .where((p) => p.endsWith('.dart') && filePattern.hasMatch(p)); |
+ if (dir != inputDir) { |
+ files = files |
+ .where((p) => p.endsWith('_test.dart') || p.endsWith('_multi.dart')); |
+ } |
+ return files; |
} |
-/// An implementation of analysis engine's [Logger] that prints. |
-class PrintLogger implements Logger { |
- @override |
- void logError(String message, [CaughtException exception]) { |
- print('[AnalysisEngine] error $message $exception'); |
+/// Parse directives from [contents] and find the complete set of transitive |
+/// imports, reading files as needed. |
+/// |
+/// This will not include dart:* libraries, as those are implicitly available. |
+void _collectTransitiveImports(String contents, Set<String> libraries, |
+ {String from}) { |
+ if (!libraries.add(from)) return; |
+ |
+ var unit = parseDirectives(contents, name: from, suppressErrors: true); |
+ for (var d in unit.directives) { |
+ if (d is ImportDirective || d is ExportDirective) { |
+ String uri = _resolveDirective(d); |
+ if (uri == null || |
+ uri.startsWith('dart:') || |
+ uri.startsWith('package:')) { |
+ continue; |
+ } |
+ |
+ var f = new File(path.join(path.dirname(from), uri)); |
+ if (f.existsSync()) { |
+ _collectTransitiveImports(f.readAsStringSync(), libraries, |
+ from: f.path); |
+ } |
+ } |
} |
+} |
- void logInformation(String message, [CaughtException exception]) {} |
- void logInformation2(String message, Object exception) {} |
+/// Simplified from ParseDartTask.resolveDirective. |
+String _resolveDirective(UriBasedDirective directive) { |
+ StringLiteral uriLiteral = directive.uri; |
+ String uriContent = uriLiteral.stringValue; |
+ if (uriContent != null) { |
+ uriContent = uriContent.trim(); |
+ directive.uriContent = uriContent; |
+ } |
+ return directive.validate() == null ? uriContent : null; |
} |