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

Side by Side Diff: test/codegen_test.dart

Issue 1879373004: Implement modular compilation (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 8 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 | « test/codegen/unittest.dart ('k') | test/js/builder_test.dart » ('j') | 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) 2016, 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 /// Tests code generation. 5 /// Tests code generation.
6 /// Runs Dart Dev Compiler on all input in the `codegen` directory and checks 6 /// Runs Dart Dev Compiler on all input in the `codegen` directory and checks
7 /// that the output is what we expected. 7 /// that the output is what we expected.
8 library dev_compiler.test.codegen_test; 8 library dev_compiler.test.codegen_test;
9 9
10 import 'dart:io'; 10 import 'dart:convert' show JSON;
11 import 'package:analyzer/src/generated/engine.dart' 11 import 'dart:io' show Directory, File, Platform;
12 show AnalysisContext, AnalysisEngine, Logger; 12 import 'package:args/args.dart' show ArgParser, ArgResults;
13 import 'package:analyzer/src/generated/java_engine.dart' show CaughtException;
14 import 'package:analyzer/src/generated/source_io.dart';
15 import 'package:args/args.dart';
16 import 'package:cli_util/cli_util.dart' show getSdkDir;
17 import 'package:logging/logging.dart' show Level;
18 import 'package:path/path.dart' as path; 13 import 'package:path/path.dart' as path;
19 import 'package:test/test.dart'; 14 import 'package:test/test.dart' show group, test;
20 15
21 import 'package:dev_compiler/devc.dart'; 16 import 'package:analyzer/analyzer.dart'
22 import 'package:dev_compiler/src/analysis_context.dart'; 17 show
23 import 'package:dev_compiler/src/compiler.dart' show defaultRuntimeFiles; 18 ExportDirective,
24 import 'package:dev_compiler/src/options.dart'; 19 ImportDirective,
25 import 'package:dev_compiler/src/report.dart' show LogReporter; 20 StringLiteral,
26 21 UriBasedDirective,
22 parseDirectives;
23 import 'package:analyzer/src/generated/source.dart' show Source;
24 import 'package:dev_compiler/src/analyzer/context.dart' show AnalyzerOptions;
25 import 'package:dev_compiler/src/compiler/compiler.dart'
26 show BuildUnit, CompilerOptions, ModuleCompiler;
27 import 'testing.dart' show testDirectory; 27 import 'testing.dart' show testDirectory;
28 import 'multitest.dart'; 28 import 'multitest.dart' show extractTestsFromMultitest, isMultiTest;
29 import '../tool/build_sdk.dart' as build_sdk;
30 import 'package:dev_compiler/src/compiler/compiler.dart';
29 31
30 final ArgParser argParser = new ArgParser() 32 final ArgParser argParser = new ArgParser()
31 ..addOption('dart-sdk', help: 'Dart SDK Path', defaultsTo: null); 33 ..addOption('dart-sdk', help: 'Dart SDK Path', defaultsTo: null);
32 34
33 final inputDir = path.join(testDirectory, 'codegen');
34
35 Iterable<String> _findTests(String dir, RegExp filePattern) {
36 var files = new Directory(dir)
37 .listSync()
38 .where((f) => f is File)
39 .map((f) => f.path)
40 .where((p) => p.endsWith('.dart') && filePattern.hasMatch(p));
41 if (dir != inputDir) {
42 files = files
43 .where((p) => p.endsWith('_test.dart') || p.endsWith('_multi.dart'));
44 }
45 return files;
46 }
47
48 main(arguments) { 35 main(arguments) {
49 if (arguments == null) arguments = []; 36 if (arguments == null) arguments = [];
50 ArgResults args = argParser.parse(arguments); 37 ArgResults args = argParser.parse(arguments);
51 var filePattern = new RegExp(args.rest.length > 0 ? args.rest[0] : '.'); 38 var filePattern = new RegExp(args.rest.length > 0 ? args.rest[0] : '.');
52 var compilerMessages = new StringBuffer();
53 var loggerSub;
54 39
55 bool codeCoverage = Platform.environment.containsKey('COVERALLS_TOKEN'); 40 var expectDir = path.join(inputDir, 'expect');
41 var testDirs = ['language', path.join('lib', 'typed_data')];
56 42
57 setUp(() { 43 var multitests = expandMultiTests(testDirs, filePattern);
58 compilerMessages.clear(); 44
59 loggerSub = setupLogger(Level.CONFIG, compilerMessages.writeln); 45 // Build packages tests depend on
46 var compiler = new ModuleCompiler(
47 new AnalyzerOptions(customUrlMappings: packageUrlMappings));
48
49 group('dartdevc package', () {
50 _buildPackages(compiler, expectDir);
51
52 test('matcher', () {
53 _buildMatcher(compiler, expectDir);
54 });
60 }); 55 });
61 56
62 tearDown(() { 57 test('dartdevc sunflower', () {
63 if (loggerSub != null) { 58 _buildSunflower(compiler, expectDir);
64 loggerSub.cancel();
65 loggerSub = null;
66 }
67 }); 59 });
68 60
69 var expectDir = path.join(inputDir, 'expect'); 61 // Our default compiler options. Individual tests can override these.
70 62 var defaultOptions = ['--no-source-map', '--no-summarize'];
71 BatchCompiler createCompiler(DartUriResolver sdkResolver, 63 var compilerArgParser = CompilerOptions.addArguments(new ArgParser());
72 {bool checkSdk: false,
73 bool sourceMaps: false,
74 bool destructureNamedParams: false,
75 bool closure: false,
76 ModuleFormat moduleFormat: ModuleFormat.legacy}) {
77 String _testCodegenPath(String p1, [String p2]) =>
78 path.join(testDirectory, 'codegen', p1, p2);
79
80 var context = createAnalysisContextWithSources(
81 new SourceResolverOptions(customUrlMappings: {
82 'package:expect/expect.dart': _testCodegenPath('expect.dart'),
83 'package:async_helper/async_helper.dart':
84 _testCodegenPath('async_helper.dart'),
85 'package:unittest/unittest.dart': _testCodegenPath('unittest.dart'),
86 'package:dom/dom.dart': _testCodegenPath('sunflower', 'dom.dart')
87 }),
88 sdkResolver: sdkResolver);
89
90 // TODO(jmesserly): add a way to specify flags in the test file, so
91 // they're more self-contained.
92 var runtimeDir = path.join(path.dirname(testDirectory), 'lib', 'runtime');
93 var options = new CompilerOptions(
94 codegenOptions: new CodegenOptions(
95 outputDir: expectDir,
96 emitSourceMaps: sourceMaps,
97 closure: closure,
98 destructureNamedParams: destructureNamedParams,
99 forceCompile: checkSdk,
100 moduleFormat: moduleFormat),
101 useColors: false,
102 checkSdk: checkSdk,
103 runtimeDir: runtimeDir,
104 inputBaseDir: inputDir);
105 var reporter = new LogReporter(context);
106 return new BatchCompiler(context, options, reporter: reporter);
107 }
108
109 bool compile(BatchCompiler compiler, String filePath) {
110 compiler.compileFromUriString(filePath, (String url) {
111 // Write compiler messages to disk.
112 var messagePath = '${path.withoutExtension(url)}.txt';
113 var file = new File(messagePath);
114 var message = '''
115 // Messages from compiling ${path.basenameWithoutExtension(url)}.dart
116 $compilerMessages''';
117 var dir = file.parent;
118 if (!dir.existsSync()) dir.createSync(recursive: true);
119 file.writeAsStringSync(message);
120 compilerMessages.clear();
121 });
122 return !compiler.failure;
123 }
124
125 var testDirs = <String>['language', path.join('lib', 'typed_data')];
126
127 var multitests = new Set<String>();
128 {
129 // Expand wacky multitests into a bunch of test files.
130 // We'll compile each one as if it was an input.
131 for (var testDir in testDirs) {
132 var fullDir = path.join(inputDir, testDir);
133 var testFiles = _findTests(fullDir, filePattern);
134
135 for (var filePath in testFiles) {
136 if (filePath.endsWith('_multi.dart')) continue;
137
138 var contents = new File(filePath).readAsStringSync();
139 if (isMultiTest(contents)) {
140 multitests.add(filePath);
141
142 var tests = new Map<String, String>();
143 var outcomes = new Map<String, Set<String>>();
144 extractTestsFromMultitest(filePath, contents, tests, outcomes);
145
146 var filename = path.basenameWithoutExtension(filePath);
147 tests.forEach((name, contents) {
148 new File(path.join(fullDir, '${filename}_${name}_multi.dart'))
149 .writeAsStringSync(contents);
150 });
151 }
152 }
153 }
154 }
155
156 var realSdkResolver = createSdkPathResolver(getSdkDir().path);
157 var batchCompiler = createCompiler(realSdkResolver);
158 64
159 var allDirs = [null]; 65 var allDirs = [null];
160 allDirs.addAll(testDirs); 66 allDirs.addAll(testDirs);
161 for (var dir in allDirs) { 67 for (var dir in allDirs) {
162 if (codeCoverage && dir != null) continue; 68 if (codeCoverage && dir != null) continue;
163 69
164 group('dartdevc ' + path.join('test', 'codegen', dir), () { 70 group('dartdevc ' + path.join('test', 'codegen', dir), () {
165 var outDir = new Directory(path.join(expectDir, dir)); 71 var outDir = new Directory(path.join(expectDir, dir));
166 if (!outDir.existsSync()) outDir.createSync(recursive: true); 72 if (!outDir.existsSync()) outDir.createSync(recursive: true);
167 73
168 var testFiles = _findTests(path.join(inputDir, dir), filePattern); 74 var testFiles = _findTests(path.join(inputDir, dir), filePattern);
169 for (var filePath in testFiles) { 75 for (var filePath in testFiles) {
170 if (multitests.contains(filePath)) continue; 76 if (multitests.contains(filePath)) continue;
171 77
172 var filename = path.basenameWithoutExtension(filePath); 78 var filename = path.basenameWithoutExtension(filePath);
173 79
174 test('$filename.dart', () { 80 test('$filename.dart', () {
175 // TODO(jmesserly): this was added to get some coverage of source maps 81 // Check if we need to use special compile options.
176 // and closure annotations. 82 var contents = new File(filePath).readAsStringSync();
177 // We need a more comprehensive strategy to test them. 83 var match =
178 var sourceMaps = filename == 'map_keys'; 84 new RegExp(r'// compile options: (.*)').matchAsPrefix(contents);
179 var closure = filename == 'closure'; 85
180 var destructureNamedParams = filename == 'destructuring' || closure; 86 var args = new List.from(defaultOptions);
181 var moduleFormat = filename == 'es6_modules' || closure 87 if (match != null) {
182 ? ModuleFormat.es6 88 args.addAll(match.group(1).split(' '));
183 : filename == 'node_modules' 89 }
184 ? ModuleFormat.node 90 var options =
185 : ModuleFormat.legacy; 91 new CompilerOptions.fromArguments(compilerArgParser.parse(args));
186 var success; 92
187 // TODO(vsm): Is it okay to reuse the same context here? If there is 93 // Collect any other files we've imported.
188 // overlap between test files, we may need separate ones for each 94 var files = new Set<String>();
189 // compiler. 95 _collectTransitiveImports(contents, files, from: filePath);
190 var compiler = (sourceMaps || 96
191 closure || 97 var unit = new BuildUnit(filename, files.toList(), _moduleForLibrary);
192 destructureNamedParams || 98 var module = compiler.compile(unit, options);
193 moduleFormat != ModuleFormat.legacy) 99 _writeModule(path.join(outDir.path, filename), module);
194 ? createCompiler(realSdkResolver,
195 sourceMaps: sourceMaps,
196 destructureNamedParams: destructureNamedParams,
197 closure: closure,
198 moduleFormat: moduleFormat)
199 : batchCompiler;
200 success = compile(compiler, filePath);
201
202 var outFile = new File(path.join(outDir.path, '$filename.js'));
203 expect(!success || outFile.existsSync(), true,
204 reason: '${outFile.path} was created if compilation succeeds');
205 }); 100 });
206 } 101 }
207 }); 102 });
208 } 103 }
209 104
210 if (codeCoverage) { 105 if (codeCoverage) {
211 group('sdk', () { 106 test('build_sdk code coverage', () {
212 // The analyzer does not bubble exception messages for certain internal 107 var generatedSdkDir =
213 // dart:* library failures, such as failing to find 108 path.join(testDirectory, '..', 'tool', 'generated_sdk');
214 // "_internal/libraries.dart". Instead it produces an opaque "failed to 109 return build_sdk.main(['--dart-sdk', generatedSdkDir, '-o', expectDir]);
215 // instantiate dart:core" message. To remedy this we hook up an analysis
216 // logger that prints these messages.
217 var savedLogger;
218 setUp(() {
219 savedLogger = AnalysisEngine.instance.logger;
220 AnalysisEngine.instance.logger = new PrintLogger();
221 });
222 tearDown(() {
223 AnalysisEngine.instance.logger = savedLogger;
224 });
225
226 test('devc dart:core', () {
227 var testSdkResolver = createSdkPathResolver(
228 path.join(testDirectory, '..', 'tool', 'generated_sdk'));
229
230 // Get the test SDK. We use a checked in copy so test expectations can
231 // be generated against a specific SDK version.
232 var compiler = createCompiler(testSdkResolver, checkSdk: true);
233 compile(compiler, 'dart:core');
234 var outFile = new File(path.join(expectDir, 'dart/core.js'));
235 expect(outFile.existsSync(), true,
236 reason: '${outFile.path} was created for dart:core');
237 });
238 }); 110 });
239 } 111 }
240 112 }
241 var expectedRuntime = 113
242 defaultRuntimeFiles.map((f) => 'dev_compiler/runtime/$f'); 114 void _writeModule(String outPath, JSModuleFile result) {
243 115 new Directory(path.dirname(outPath)).createSync(recursive: true);
244 test('devc jscodegen sunflower.html', () { 116
245 var filePath = path.join(inputDir, 'sunflower', 'sunflower.html'); 117 result.errors.add(''); // for trailing newline
246 var success = compile(batchCompiler, filePath); 118 new File(outPath + '.txt').writeAsStringSync(result.errors.join('\n'));
247 119
248 var expectedFiles = ['sunflower.html', 'sunflower.js',]; 120 if (result.isValid) {
249 121 new File(outPath + '.js').writeAsStringSync(result.code);
250 for (var filepath in expectedFiles) { 122 if (result.sourceMap != null) {
251 var outFile = new File(path.join(expectDir, 'sunflower', filepath)); 123 var mapPath = outPath + '.js.map';
252 expect(outFile.existsSync(), success, 124 new File(mapPath)
253 reason: '${outFile.path} was created iff compilation succeeds'); 125 .writeAsStringSync(JSON.encode(result.placeSourceMap(mapPath)));
254 } 126 }
255 }); 127 }
256 128 }
257 test('devc jscodegen html_input.html', () { 129
258 var filePath = path.join(inputDir, 'html_input.html'); 130 void _buildSunflower(ModuleCompiler compiler, String expectDir) {
259 var success = compile(batchCompiler, filePath); 131 var files = ['sunflower', 'circle', 'painter']
260 132 .map((f) => path.join(inputDir, 'sunflower', '$f.dart'))
261 var expectedFiles = [ 133 .toList();
262 'html_input.html', 134 var input = new BuildUnit('sunflower', files, _moduleForLibrary);
263 'dir/html_input_a.js', 135 var options = new CompilerOptions(summarizeApi: false);
264 'dir/html_input_b.js', 136
265 'dir/html_input_c.js', 137 var built = compiler.compile(input, options);
266 'dir/html_input_d.js', 138 _writeModule(path.join(expectDir, 'sunflower', 'sunflower'), built);
267 'dir/html_input_e.js' 139 }
268 ]..addAll(expectedRuntime); 140
269 141 void _buildPackages(ModuleCompiler compiler, String expectDir) {
270 for (var filepath in expectedFiles) { 142 // Note: we don't summarize these, as we're going to rely on our in-memory
271 var outFile = new File(path.join(expectDir, filepath)); 143 // shared analysis context for caching, and `_moduleForLibrary` below
272 expect(outFile.existsSync(), success, 144 // understands these are from other modules.
273 reason: '${outFile.path} was created iff compilation succeeds'); 145 var options = new CompilerOptions(sourceMap: false, summarizeApi: false);
146
147 for (var uri in packageUrlMappings.keys) {
148 assert(uri.startsWith('package:'));
149 var uriPath = uri.substring('package:'.length);
150 var name = path.basenameWithoutExtension(uriPath);
151 test(name, () {
152 var input = new BuildUnit(name, [uri], _moduleForLibrary);
153 var built = compiler.compile(input, options);
154
155 var outPath = path.join(expectDir, path.withoutExtension(uriPath));
156 _writeModule(outPath, built);
157 });
158 }
159 }
160
161 void _buildMatcher(ModuleCompiler compiler, String expectDir) {
162 var options = new CompilerOptions(sourceMap: false, summarizeApi: false);
163
164 var filePath = path.join(inputDir, 'packages', 'matcher', 'matcher.dart');
165 var contents = new File(filePath).readAsStringSync();
166
167 // Collect any other files we've imported.
168 var files = new Set<String>();
169 _collectTransitiveImports(contents, files, from: filePath);
170
171 var unit = new BuildUnit('matcher', files.toList(), _moduleForLibrary);
172 var module = compiler.compile(unit, options);
173
174 var outPath = path.join(expectDir, 'matcher', 'matcher');
175 _writeModule(outPath, module);
176 }
177
178 String _moduleForLibrary(Source source) {
179 var scheme = source.uri.scheme;
180 if (scheme == 'package') {
181 return source.uri.pathSegments.first;
182 }
183 throw new Exception('Module not found for library "${source.fullName}"');
184 }
185
186 /// Expands wacky multitests into a bunch of test files.
187 ///
188 /// We'll compile each one as if it was an input.
189 /// NOTE: this will write the individual test files to disk.
190 Set<String> expandMultiTests(List testDirs, RegExp filePattern) {
191 var multitests = new Set<String>();
192
193 for (var testDir in testDirs) {
194 var fullDir = path.join(inputDir, testDir);
195 var testFiles = _findTests(fullDir, filePattern);
196
197 for (var filePath in testFiles) {
198 if (filePath.endsWith('_multi.dart')) continue;
199
200 var contents = new File(filePath).readAsStringSync();
201 if (isMultiTest(contents)) {
202 multitests.add(filePath);
203
204 var tests = new Map<String, String>();
205 var outcomes = new Map<String, Set<String>>();
206 extractTestsFromMultitest(filePath, contents, tests, outcomes);
207
208 var filename = path.basenameWithoutExtension(filePath);
209 tests.forEach((name, contents) {
210 new File(path.join(fullDir, '${filename}_${name}_multi.dart'))
211 .writeAsStringSync(contents);
212 });
213 }
274 } 214 }
275 }); 215 }
276 } 216 return multitests;
277 217 }
278 /// An implementation of analysis engine's [Logger] that prints. 218
279 class PrintLogger implements Logger { 219 // TODO(jmesserly): switch this to a .packages file.
280 @override 220 final packageUrlMappings = {
281 void logError(String message, [CaughtException exception]) { 221 'package:expect/expect.dart': path.join(inputDir, 'expect.dart'),
282 print('[AnalysisEngine] error $message $exception'); 222 'package:async_helper/async_helper.dart':
283 } 223 path.join(inputDir, 'async_helper.dart'),
284 224 'package:unittest/unittest.dart': path.join(inputDir, 'unittest.dart'),
285 void logInformation(String message, [CaughtException exception]) {} 225 'package:js/js.dart': path.join(inputDir, 'packages', 'js', 'js.dart')
286 void logInformation2(String message, Object exception) {} 226 };
287 } 227
228 final codeCoverage = Platform.environment.containsKey('COVERALLS_TOKEN');
229
230 final inputDir = path.join(testDirectory, 'codegen');
231
232 Iterable<String> _findTests(String dir, RegExp filePattern) {
233 var files = new Directory(dir)
234 .listSync()
235 .where((f) => f is File)
236 .map((f) => f.path)
237 .where((p) => p.endsWith('.dart') && filePattern.hasMatch(p));
238 if (dir != inputDir) {
239 files = files
240 .where((p) => p.endsWith('_test.dart') || p.endsWith('_multi.dart'));
241 }
242 return files;
243 }
244
245 /// Parse directives from [contents] and find the complete set of transitive
246 /// imports, reading files as needed.
247 ///
248 /// This will not include dart:* libraries, as those are implicitly available.
249 void _collectTransitiveImports(String contents, Set<String> libraries,
250 {String from}) {
251 if (!libraries.add(from)) return;
252
253 var unit = parseDirectives(contents, name: from, suppressErrors: true);
254 for (var d in unit.directives) {
255 if (d is ImportDirective || d is ExportDirective) {
256 String uri = _resolveDirective(d);
257 if (uri == null ||
258 uri.startsWith('dart:') ||
259 uri.startsWith('package:')) {
260 continue;
261 }
262
263 var f = new File(path.join(path.dirname(from), uri));
264 if (f.existsSync()) {
265 _collectTransitiveImports(f.readAsStringSync(), libraries,
266 from: f.path);
267 }
268 }
269 }
270 }
271
272 /// Simplified from ParseDartTask.resolveDirective.
273 String _resolveDirective(UriBasedDirective directive) {
274 StringLiteral uriLiteral = directive.uri;
275 String uriContent = uriLiteral.stringValue;
276 if (uriContent != null) {
277 uriContent = uriContent.trim();
278 directive.uriContent = uriContent;
279 }
280 return directive.validate() == null ? uriContent : null;
281 }
OLDNEW
« no previous file with comments | « test/codegen/unittest.dart ('k') | test/js/builder_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698