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 /// 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:io'; |
11 import 'package:cli_util/cli_util.dart' show getSdkDir; | 11 import 'package:cli_util/cli_util.dart' show getSdkDir; |
12 import 'package:analyzer/src/generated/engine.dart' show AnalysisEngine, Logger; | 12 import 'package:analyzer/src/generated/engine.dart' show AnalysisEngine, Logger; |
13 import 'package:analyzer/src/generated/java_engine.dart' show CaughtException; | 13 import 'package:analyzer/src/generated/java_engine.dart' show CaughtException; |
14 import 'package:args/args.dart'; | 14 import 'package:args/args.dart'; |
15 import 'package:logging/logging.dart' show Level; | 15 import 'package:logging/logging.dart' show Level; |
16 import 'package:path/path.dart' as path; | 16 import 'package:path/path.dart' as path; |
17 import 'package:test/test.dart'; | 17 import 'package:test/test.dart'; |
18 | 18 |
19 import 'package:dev_compiler/devc.dart'; | 19 import 'package:dev_compiler/devc.dart'; |
20 import 'package:dev_compiler/src/options.dart'; | 20 import 'package:dev_compiler/src/options.dart'; |
21 import 'package:dev_compiler/src/dependency_graph.dart' | 21 import 'package:dev_compiler/src/dependency_graph.dart' |
22 show defaultRuntimeFiles; | 22 show defaultRuntimeFiles; |
23 import 'package:dev_compiler/src/utils.dart' | 23 import 'package:dev_compiler/src/utils.dart' |
24 show computeHash, computeHashFromFile; | 24 show computeHash, computeHashFromFile; |
25 import 'package:html/parser.dart' as html; | 25 import 'package:html/parser.dart' as html; |
26 | 26 |
27 import 'test_util.dart' show testDirectory; | 27 import 'test_util.dart' show testDirectory; |
28 | 28 |
29 final ArgParser argParser = new ArgParser() | 29 final ArgParser argParser = new ArgParser() |
30 ..addOption('dart-sdk', help: 'Dart SDK Path', defaultsTo: null) | 30 ..addOption('dart-sdk', help: 'Dart SDK Path', defaultsTo: null); |
31 ..addFlag('dart-gen', | |
32 abbr: 'd', help: 'Generate dart output', defaultsTo: false); | |
33 | 31 |
34 main(arguments) { | 32 main(arguments) { |
35 if (arguments == null) arguments = []; | 33 if (arguments == null) arguments = []; |
36 ArgResults args = argParser.parse(arguments); | 34 ArgResults args = argParser.parse(arguments); |
37 var dartGen = args['dart-gen']; | |
38 var filePattern = new RegExp(args.rest.length > 0 ? args.rest[0] : '.'); | 35 var filePattern = new RegExp(args.rest.length > 0 ? args.rest[0] : '.'); |
39 var compilerMessages = new StringBuffer(); | 36 var compilerMessages = new StringBuffer(); |
40 var loggerSub; | 37 var loggerSub; |
41 | 38 |
42 setUp(() { | 39 setUp(() { |
43 compilerMessages.clear(); | 40 compilerMessages.clear(); |
44 loggerSub = setupLogger(Level.CONFIG, compilerMessages.writeln); | 41 loggerSub = setupLogger(Level.CONFIG, compilerMessages.writeln); |
45 }); | 42 }); |
46 | 43 |
47 tearDown(() { | 44 tearDown(() { |
48 if (loggerSub != null) { | 45 if (loggerSub != null) { |
49 loggerSub.cancel(); | 46 loggerSub.cancel(); |
50 loggerSub = null; | 47 loggerSub = null; |
51 } | 48 } |
52 }); | 49 }); |
53 | 50 |
54 var inputDir = path.join(testDirectory, dartGen ? 'dart_codegen' : 'codegen'); | 51 var inputDir = path.join(testDirectory, 'codegen'); |
55 var actualDir = path.join(inputDir, 'actual'); | 52 var actualDir = path.join(inputDir, 'actual'); |
56 var paths = new Directory(inputDir) | 53 var paths = new Directory(inputDir) |
57 .listSync() | 54 .listSync() |
58 .where((f) => f is File) | 55 .where((f) => f is File) |
59 .map((f) => f.path) | 56 .map((f) => f.path) |
60 .where((p) => p.endsWith('.dart') && filePattern.hasMatch(p)); | 57 .where((p) => p.endsWith('.dart') && filePattern.hasMatch(p)); |
61 | 58 |
62 compile(String entryPoint, String sdkPath, {bool checkSdk: false, | 59 compile(String entryPoint, String sdkPath, {bool checkSdk: false, |
63 bool serverMode: false, bool sourceMaps: false, String subDir}) { | 60 bool serverMode: false, bool sourceMaps: false, String subDir}) { |
64 // TODO(jmesserly): add a way to specify flags in the test file, so | 61 // TODO(jmesserly): add a way to specify flags in the test file, so |
65 // they're more self-contained. | 62 // they're more self-contained. |
66 var runtimeDir = path.join(path.dirname(testDirectory), 'lib', 'runtime'); | 63 var runtimeDir = path.join(path.dirname(testDirectory), 'lib', 'runtime'); |
67 var options = new CompilerOptions( | 64 var options = new CompilerOptions( |
68 allowConstCasts: !dartGen, | 65 allowConstCasts: true, |
69 outputDir: subDir == null ? actualDir : path.join(actualDir, subDir), | 66 outputDir: subDir == null ? actualDir : path.join(actualDir, subDir), |
70 useColors: false, | 67 useColors: false, |
71 outputDart: dartGen, | |
72 formatOutput: dartGen, | |
73 emitSourceMaps: sourceMaps, | 68 emitSourceMaps: sourceMaps, |
74 forceCompile: checkSdk, | 69 forceCompile: checkSdk, |
75 checkSdk: checkSdk, | 70 checkSdk: checkSdk, |
76 entryPointFile: entryPoint, | 71 entryPointFile: entryPoint, |
77 dartSdkPath: sdkPath, | 72 dartSdkPath: sdkPath, |
78 runtimeDir: runtimeDir, | 73 runtimeDir: runtimeDir, |
79 serverMode: serverMode, | 74 serverMode: serverMode, |
80 enableHashing: serverMode); | 75 enableHashing: serverMode); |
81 return new Compiler(options).run(); | 76 return new Compiler(options).run(); |
82 } | 77 } |
(...skipping 16 matching lines...) Expand all Loading... |
99 // TODO(jmesserly): this was added to get some coverage of source maps | 94 // TODO(jmesserly): this was added to get some coverage of source maps |
100 // We need a more comprehensive strategy to test them. | 95 // We need a more comprehensive strategy to test them. |
101 var sourceMaps = filename == 'map_keys'; | 96 var sourceMaps = filename == 'map_keys'; |
102 var result = compile(filePath, realSdk, sourceMaps: sourceMaps); | 97 var result = compile(filePath, realSdk, sourceMaps: sourceMaps); |
103 var success = !result.failure; | 98 var success = !result.failure; |
104 | 99 |
105 // Write compiler messages to disk. | 100 // Write compiler messages to disk. |
106 new File(path.join(actualDir, '$filename.txt')) | 101 new File(path.join(actualDir, '$filename.txt')) |
107 .writeAsStringSync(compilerMessages.toString()); | 102 .writeAsStringSync(compilerMessages.toString()); |
108 | 103 |
109 var outFile = dartGen | 104 var outFile = new File(path.join(actualDir, '$filename.js')); |
110 ? new File(path.join(actualDir, '$filename/$filename.dart')) | |
111 : new File(path.join(actualDir, '$filename.js')); | |
112 expect(outFile.existsSync(), success, | 105 expect(outFile.existsSync(), success, |
113 reason: '${outFile.path} was created iff compilation succeeds'); | 106 reason: '${outFile.path} was created iff compilation succeeds'); |
114 | 107 |
115 // TODO(jmesserly): ideally we'd diff the output here. For now it | 108 // TODO(jmesserly): ideally we'd diff the output here. For now it |
116 // happens in the containing shell script. | 109 // happens in the containing shell script. |
117 }); | 110 }); |
118 } | 111 } |
119 | 112 |
120 if (dartGen || Platform.environment.containsKey('COVERALLS_TOKEN')) { | 113 if (Platform.environment.containsKey('COVERALLS_TOKEN')) { |
121 group('sdk', () { | 114 group('sdk', () { |
122 // The analyzer does not bubble exception messages for certain internal | 115 // The analyzer does not bubble exception messages for certain internal |
123 // dart:* library failures, such as failing to find | 116 // dart:* library failures, such as failing to find |
124 // "_internal/libraries.dart". Instead it produces an opaque "failed to | 117 // "_internal/libraries.dart". Instead it produces an opaque "failed to |
125 // instantiate dart:core" message. To remedy this we hook up an analysis | 118 // instantiate dart:core" message. To remedy this we hook up an analysis |
126 // logger that prints these messages. | 119 // logger that prints these messages. |
127 var savedLogger; | 120 var savedLogger; |
128 setUp(() { | 121 setUp(() { |
129 savedLogger = AnalysisEngine.instance.logger; | 122 savedLogger = AnalysisEngine.instance.logger; |
130 AnalysisEngine.instance.logger = new PrintLogger(); | 123 AnalysisEngine.instance.logger = new PrintLogger(); |
131 }); | 124 }); |
132 tearDown(() { | 125 tearDown(() { |
133 AnalysisEngine.instance.logger = savedLogger; | 126 AnalysisEngine.instance.logger = savedLogger; |
134 }); | 127 }); |
135 | 128 |
136 test('devc dart:core', () { | 129 test('devc dart:core', () { |
137 // Get the test SDK. We use a checked in copy so test expectations can | 130 // Get the test SDK. We use a checked in copy so test expectations can |
138 // be generated against a specific SDK version. | 131 // be generated against a specific SDK version. |
139 var testSdk = dartGen | 132 var testSdk = path.join(testDirectory, 'generated_sdk'); |
140 ? path.join(testDirectory, '..', 'tool', 'input_sdk') | |
141 : path.join(testDirectory, 'generated_sdk'); | |
142 var result = compile('dart:core', testSdk, checkSdk: true); | 133 var result = compile('dart:core', testSdk, checkSdk: true); |
143 var outputDir = new Directory(path.join(actualDir, 'core')); | 134 var outputDir = new Directory(path.join(actualDir, 'core')); |
144 var outFile = new File( | 135 var outFile = new File(path.join(actualDir, 'dart/core.js')); |
145 path.join(actualDir, dartGen ? 'core/core' : 'dart/core.js')); | |
146 expect(outFile.existsSync(), true, | 136 expect(outFile.existsSync(), true, |
147 reason: '${outFile.path} was created for dart:core'); | 137 reason: '${outFile.path} was created for dart:core'); |
148 }); | 138 }); |
149 }); | 139 }); |
150 } | 140 } |
151 | 141 |
152 var expectedRuntime = | 142 var expectedRuntime = |
153 defaultRuntimeFiles.map((f) => 'dev_compiler/runtime/$f'); | 143 defaultRuntimeFiles.map((f) => 'dev_compiler/runtime/$f'); |
154 | 144 |
155 if (!dartGen) { | 145 test('devc jscodegen sunflower.html', () { |
156 test('devc jscodegen sunflower.html', () { | 146 var filePath = path.join(inputDir, 'sunflower', 'sunflower.html'); |
157 var filePath = path.join(inputDir, 'sunflower', 'sunflower.html'); | 147 compilerMessages.writeln('// Messages from compiling sunflower.html'); |
158 compilerMessages.writeln('// Messages from compiling sunflower.html'); | |
159 | 148 |
160 var result = compile(filePath, realSdk, subDir: 'sunflower'); | 149 var result = compile(filePath, realSdk, subDir: 'sunflower'); |
161 var success = !result.failure; | 150 var success = !result.failure; |
162 | 151 |
163 // Write compiler messages to disk. | 152 // Write compiler messages to disk. |
164 new File(path.join(actualDir, 'sunflower', 'sunflower.txt')) | 153 new File(path.join(actualDir, 'sunflower', 'sunflower.txt')) |
165 .writeAsStringSync(compilerMessages.toString()); | 154 .writeAsStringSync(compilerMessages.toString()); |
166 | 155 |
167 var expectedFiles = [ | 156 var expectedFiles = [ |
168 'sunflower.html', | 157 'sunflower.html', |
169 'sunflower.js', | 158 'sunflower.js', |
170 'sunflower.css', | 159 'sunflower.css', |
171 'math.png', | 160 'math.png', |
172 ]..addAll(expectedRuntime); | 161 ]..addAll(expectedRuntime); |
173 | 162 |
174 for (var filepath in expectedFiles) { | 163 for (var filepath in expectedFiles) { |
175 var outFile = new File(path.join(actualDir, 'sunflower', filepath)); | 164 var outFile = new File(path.join(actualDir, 'sunflower', filepath)); |
176 expect(outFile.existsSync(), success, | 165 expect(outFile.existsSync(), success, |
177 reason: '${outFile.path} was created iff compilation succeeds'); | 166 reason: '${outFile.path} was created iff compilation succeeds'); |
| 167 } |
| 168 }); |
| 169 |
| 170 test('devc jscodegen html_input.html', () { |
| 171 var filePath = path.join(inputDir, 'html_input.html'); |
| 172 compilerMessages.writeln('// Messages from compiling html_input.html'); |
| 173 |
| 174 var result = compile(filePath, realSdk); |
| 175 var success = !result.failure; |
| 176 |
| 177 // Write compiler messages to disk. |
| 178 new File(path.join(actualDir, 'html_input.txt')) |
| 179 .writeAsStringSync(compilerMessages.toString()); |
| 180 |
| 181 var expectedFiles = [ |
| 182 'html_input.html', |
| 183 'dir/html_input_a.js', |
| 184 'dir/html_input_b.js', |
| 185 'dir/html_input_c.js', |
| 186 'dir/html_input_d.js', |
| 187 'dir/html_input_e.js' |
| 188 ]..addAll(expectedRuntime); |
| 189 |
| 190 for (var filepath in expectedFiles) { |
| 191 var outFile = new File(path.join(actualDir, filepath)); |
| 192 expect(outFile.existsSync(), success, |
| 193 reason: '${outFile.path} was created iff compilation succeeds'); |
| 194 } |
| 195 |
| 196 var notExpectedFiles = [ |
| 197 'dev_compiler/runtime/messages_widget.js', |
| 198 'dev_compiler/runtime/messages.css' |
| 199 ]; |
| 200 for (var filepath in notExpectedFiles) { |
| 201 var outFile = new File(path.join(actualDir, filepath)); |
| 202 expect(outFile.existsSync(), isFalse, |
| 203 reason: '${outFile.path} should only be generated in server mode'); |
| 204 } |
| 205 }); |
| 206 |
| 207 test('devc jscodegen html_input.html server mode', () { |
| 208 var filePath = path.join(inputDir, 'html_input.html'); |
| 209 compilerMessages.writeln('// Messages from compiling html_input.html'); |
| 210 |
| 211 var result = |
| 212 compile(filePath, realSdk, serverMode: true, subDir: 'server_mode'); |
| 213 var success = !result.failure; |
| 214 |
| 215 // Write compiler messages to disk. |
| 216 new File(path.join(actualDir, 'server_mode', 'html_input.txt')) |
| 217 .writeAsStringSync(compilerMessages.toString()); |
| 218 |
| 219 var expectedFiles = [ |
| 220 'dir/html_input_a.js', |
| 221 'dir/html_input_b.js', |
| 222 'dir/html_input_c.js', |
| 223 'dir/html_input_d.js', |
| 224 'dir/html_input_e.js', |
| 225 'dev_compiler/runtime/messages_widget.js', |
| 226 'dev_compiler/runtime/messages.css' |
| 227 ]..addAll(expectedRuntime); |
| 228 |
| 229 // Parse the HTML file and verify its contents were expected. |
| 230 var htmlPath = path.join(actualDir, 'server_mode', 'html_input.html'); |
| 231 var doc = html.parse(new File(htmlPath).readAsStringSync()); |
| 232 |
| 233 for (var filepath in expectedFiles) { |
| 234 var outPath = path.join(actualDir, 'server_mode', filepath); |
| 235 expect(new File(outPath).existsSync(), success, |
| 236 reason: '$outPath was created iff compilation succeeds'); |
| 237 |
| 238 var query; |
| 239 if (filepath.endsWith('js')) { |
| 240 var hash; |
| 241 if (filepath.startsWith('dev_compiler')) { |
| 242 hash = computeHashFromFile(outPath); |
| 243 } else { |
| 244 // TODO(jmesserly): see if we can get this to return the same |
| 245 // answer as computeHashFromFile. |
| 246 hash = computeHash(new File(outPath).readAsStringSync()); |
| 247 } |
| 248 query = 'script[src="$filepath?____cached=$hash"]'; |
| 249 } else { |
| 250 var hash = computeHashFromFile(outPath); |
| 251 query = 'link[href="$filepath?____cached=$hash"]'; |
178 } | 252 } |
179 }); | 253 expect(doc.querySelector(query), isNotNull, |
| 254 reason: "should find `$query` in $htmlPath for $outPath"); |
| 255 } |
180 | 256 |
181 test('devc jscodegen html_input.html', () { | 257 // Clean up the server mode folder, otherwise it causes diff churn. |
182 var filePath = path.join(inputDir, 'html_input.html'); | 258 var dir = new Directory(path.join(actualDir, 'server_mode')); |
183 compilerMessages.writeln('// Messages from compiling html_input.html'); | 259 if (dir.existsSync()) dir.deleteSync(recursive: true); |
184 | 260 }); |
185 var result = compile(filePath, realSdk); | |
186 var success = !result.failure; | |
187 | |
188 // Write compiler messages to disk. | |
189 new File(path.join(actualDir, 'html_input.txt')) | |
190 .writeAsStringSync(compilerMessages.toString()); | |
191 | |
192 var expectedFiles = [ | |
193 'html_input.html', | |
194 'dir/html_input_a.js', | |
195 'dir/html_input_b.js', | |
196 'dir/html_input_c.js', | |
197 'dir/html_input_d.js', | |
198 'dir/html_input_e.js' | |
199 ]..addAll(expectedRuntime); | |
200 | |
201 for (var filepath in expectedFiles) { | |
202 var outFile = new File(path.join(actualDir, filepath)); | |
203 expect(outFile.existsSync(), success, | |
204 reason: '${outFile.path} was created iff compilation succeeds'); | |
205 } | |
206 | |
207 var notExpectedFiles = [ | |
208 'dev_compiler/runtime/messages_widget.js', | |
209 'dev_compiler/runtime/messages.css' | |
210 ]; | |
211 for (var filepath in notExpectedFiles) { | |
212 var outFile = new File(path.join(actualDir, filepath)); | |
213 expect(outFile.existsSync(), isFalse, | |
214 reason: '${outFile.path} should only be generated in server mode'); | |
215 } | |
216 }); | |
217 | |
218 test('devc jscodegen html_input.html server mode', () { | |
219 var filePath = path.join(inputDir, 'html_input.html'); | |
220 compilerMessages.writeln('// Messages from compiling html_input.html'); | |
221 | |
222 var result = | |
223 compile(filePath, realSdk, serverMode: true, subDir: 'server_mode'); | |
224 var success = !result.failure; | |
225 | |
226 // Write compiler messages to disk. | |
227 new File(path.join(actualDir, 'server_mode', 'html_input.txt')) | |
228 .writeAsStringSync(compilerMessages.toString()); | |
229 | |
230 var expectedFiles = [ | |
231 'dir/html_input_a.js', | |
232 'dir/html_input_b.js', | |
233 'dir/html_input_c.js', | |
234 'dir/html_input_d.js', | |
235 'dir/html_input_e.js', | |
236 'dev_compiler/runtime/messages_widget.js', | |
237 'dev_compiler/runtime/messages.css' | |
238 ]..addAll(expectedRuntime); | |
239 | |
240 // Parse the HTML file and verify its contents were expected. | |
241 var htmlPath = path.join(actualDir, 'server_mode', 'html_input.html'); | |
242 var doc = html.parse(new File(htmlPath).readAsStringSync()); | |
243 | |
244 for (var filepath in expectedFiles) { | |
245 var outPath = path.join(actualDir, 'server_mode', filepath); | |
246 expect(new File(outPath).existsSync(), success, | |
247 reason: '$outPath was created iff compilation succeeds'); | |
248 | |
249 var query; | |
250 if (filepath.endsWith('js')) { | |
251 var hash; | |
252 if (filepath.startsWith('dev_compiler')) { | |
253 hash = computeHashFromFile(outPath); | |
254 } else { | |
255 // TODO(jmesserly): see if we can get this to return the same | |
256 // answer as computeHashFromFile. | |
257 hash = computeHash(new File(outPath).readAsStringSync()); | |
258 } | |
259 query = 'script[src="$filepath?____cached=$hash"]'; | |
260 } else { | |
261 var hash = computeHashFromFile(outPath); | |
262 query = 'link[href="$filepath?____cached=$hash"]'; | |
263 } | |
264 expect(doc.querySelector(query), isNotNull, | |
265 reason: "should find `$query` in $htmlPath for $outPath"); | |
266 } | |
267 | |
268 // Clean up the server mode folder, otherwise it causes diff churn. | |
269 var dir = new Directory(path.join(actualDir, 'server_mode')); | |
270 if (dir.existsSync()) dir.deleteSync(recursive: true); | |
271 }); | |
272 } | |
273 } | 261 } |
274 | 262 |
275 /// An implementation of analysis engine's [Logger] that prints. | 263 /// An implementation of analysis engine's [Logger] that prints. |
276 class PrintLogger implements Logger { | 264 class PrintLogger implements Logger { |
277 @override void logError(String message, [CaughtException exception]) { | 265 @override void logError(String message, [CaughtException exception]) { |
278 print('[AnalysisEngine] error $message $exception'); | 266 print('[AnalysisEngine] error $message $exception'); |
279 } | 267 } |
280 | 268 |
281 @override void logError2(String message, Object exception) { | 269 @override void logError2(String message, Object exception) { |
282 print('[AnalysisEngine] error $message $exception'); | 270 print('[AnalysisEngine] error $message $exception'); |
283 } | 271 } |
284 | 272 |
285 void logInformation(String message, [CaughtException exception]) {} | 273 void logInformation(String message, [CaughtException exception]) {} |
286 void logInformation2(String message, Object exception) {} | 274 void logInformation2(String message, Object exception) {} |
287 } | 275 } |
OLD | NEW |