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

Side by Side Diff: tools/testing/dart/fletch_test_suite.dart

Issue 1659163007: Rename fletch -> dartino (Closed) Base URL: https://github.com/dartino/sdk.git@master
Patch Set: address comments Created 4 years, 10 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
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file
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.md file.
4
5 /// Test suite for running tests in a shared Dart VM. Take a look at
6 /// ../../../tests/fletch_tests/all_tests.dart for more information.
7 library test.fletch_test_suite;
8
9 import 'dart:io' as io;
10
11 import 'dart:convert' show
12 JSON,
13 LineSplitter,
14 UTF8,
15 Utf8Decoder;
16
17 import 'dart:async' show
18 Completer,
19 Future,
20 Stream,
21 StreamIterator,
22 Timer;
23
24 import 'test_suite.dart' show
25 TestSuite,
26 TestUtils;
27
28 import 'test_runner.dart' show
29 Command,
30 CommandBuilder,
31 CommandOutput,
32 TestCase;
33
34 import 'runtime_configuration.dart' show
35 RuntimeConfiguration;
36
37 import 'status_file_parser.dart' show
38 Expectation,
39 ReadTestExpectationsInto,
40 TestExpectations;
41
42 import '../../../tests/fletch_tests/messages.dart' show
43 ErrorMessage,
44 Info,
45 ListTests,
46 ListTestsReply,
47 Message,
48 NamedMessage,
49 RunTest,
50 TestFailed,
51 TestStdoutLine,
52 TimedOut,
53 messageTransformer;
54
55 class FletchTestRuntimeConfiguration extends RuntimeConfiguration {
56 final String system;
57 final String dartBinary;
58
59 FletchTestRuntimeConfiguration(Map configuration)
60 : system = configuration['system'],
61 dartBinary = '${TestUtils.buildDir(configuration)}'
62 '${io.Platform.pathSeparator}dart',
63 super.subclass();
64 }
65
66 class FletchTestSuite extends TestSuite {
67 final String testSuiteDir;
68
69 TestCompleter completer;
70
71 FletchTestSuite(Map configuration, this.testSuiteDir)
72 : super(configuration, "fletch_tests");
73
74 void forEachTest(
75 void onTest(TestCase testCase),
76 Map testCache,
77 [void onDone()]) {
78 this.doTest = onTest;
79 if (configuration['runtime'] != 'fletch_tests') {
80 onDone();
81 return;
82 }
83
84 FletchTestRuntimeConfiguration runtimeConfiguration =
85 new RuntimeConfiguration(configuration);
86
87 TestExpectations expectations = new TestExpectations();
88 String buildDir = TestUtils.buildDir(configuration);
89 String version;
90
91 // Define a path for temporary output generated during tests.
92 String tempDirPath = '$buildDir/temporary_test_output';
93 io.Directory tempDir = new io.Directory(tempDirPath);
94 try {
95 tempDir.deleteSync(recursive: true);
96 } on io.FileSystemException catch (e) {
97 // Ignored, we assume the file did not exist.
98 }
99
100 String javaHome = _guessJavaHome(configuration["arch"]);
101 if (javaHome == null) {
102 String arch = configuration["arch"];
103 print("Notice: Java tests are disabled");
104 print("Unable to find a JDK installation for architecture $arch");
105 print("Install a JDK or set JAVA_PATH to an existing installation.");
106 // TODO(zerny): Throw an error if no-java is not supplied.
107 } else {
108 print("Notice: Enabled Java tests using JDK at $javaHome");
109 }
110
111 bool helperProgramExited = false;
112 io.Process vmProcess;
113 ReadTestExpectationsInto(
114 expectations, '$testSuiteDir/fletch_tests.status',
115 configuration).then((_) {
116 return new io.File('$buildDir/gen/version.cc').readAsLines();
117 }).then((List<String> versionFileLines) {
118 // Search for the 'return "version_string";' line.
119 for (String line in versionFileLines) {
120 if (line.contains('return')) {
121 version = line.substring(
122 line.indexOf('"') + 1, line.lastIndexOf('"'));
123 }
124 }
125 assert(version != null);
126 }).then((_) {
127 return io.ServerSocket.bind(io.InternetAddress.LOOPBACK_IP_V4, 0);
128 }).then((io.ServerSocket server) {
129 return io.Process.start(
130 runtimeConfiguration.dartBinary,
131 ['-Dfletch-vm=$buildDir/fletch-vm',
132 '-Dfletch.version=$version',
133 '-Ddart-sdk=third_party/dart/sdk/',
134 '-Dtests-dir=tests/',
135 '-Djava-home=$javaHome',
136 '-Dtest.dart.build-dir=$buildDir',
137 '-Dtest.dart.build-arch=${configuration["arch"]}',
138 '-Dtest.dart.build-system=${configuration["system"]}',
139 '-Dtest.dart.build-clang=${configuration["clang"]}',
140 '-Dtest.dart.build-asan=${configuration["asan"]}',
141 '-Dtest.dart.temp-dir=$tempDirPath',
142 '-Dtest.dart.servicec-dir=tools/servicec/',
143 '-c',
144 '--packages=.packages',
145 '-Dtest.fletch_test_suite.port=${server.port}',
146 '$testSuiteDir/fletch_test_suite.dart']).then((io.Process process) {
147 process.exitCode.then((_) {
148 helperProgramExited = true;
149 server.close();
150 });
151 vmProcess = process;
152 return process.stdin.close();
153 }).then((_) {
154 return server.first.catchError((error) {
155 // The VM died before we got a connection.
156 assert(helperProgramExited);
157 return null;
158 });
159 }).then((io.Socket socket) {
160 server.close();
161 return socket;
162 });
163 }).then((io.Socket socket) {
164 assert(socket != null || helperProgramExited);
165 completer = new TestCompleter(vmProcess, socket);
166 completer.initialize();
167 return completer.requestTestNames();
168 }).then((List<String> testNames) {
169 for (String name in testNames) {
170 Set<Expectation> expectedOutcomes = expectations.expectations(name);
171 TestCase testCase = new TestCase(
172 'fletch_tests/$name', <Command>[], configuration, expectedOutcomes);
173 var command = new FletchTestCommand(name, completer);
174 testCase.commands.add(command);
175 if (!expectedOutcomes.contains(Expectation.SKIP)) {
176 completer.expect(command);
177 }
178 enqueueNewTestCase(testCase);
179 }
180 }).then((_) {
181 onDone();
182 });
183 }
184
185 void cleanup() {
186 completer.allDone();
187 }
188
189 String _guessJavaHome(String buildArchitecture) {
190 String arch = buildArchitecture == 'ia32' ? '32' : '64';
191
192 // Try to locate a valid installation based on JAVA_HOME.
193 String javaHome =
194 _guessJavaHomeArch(io.Platform.environment['JAVA_HOME'], arch);
195 if (javaHome != null) return javaHome;
196
197 // Try to locate a valid installation using the java_home utility.
198 String javaHomeUtil = '/usr/libexec/java_home';
199 if (new io.File(javaHomeUtil).existsSync()) {
200 List<String> args = <String>['-v', '1.6+', '-d', arch];
201 io.ProcessResult result =
202 io.Process.runSync(javaHomeUtil, args);
203 if (result.exitCode == 0) {
204 String javaHome = result.stdout.trim();
205 if (_isValidJDK(javaHome)) return javaHome;
206 }
207 }
208
209 // Try to locate a valid installation using the path to javac.
210 io.ProcessResult result =
211 io.Process.runSync('command', ['-v', 'javac'], runInShell: true);
212 if (result.exitCode == 0) {
213 String javac = result.stdout.trim();
214 while (io.FileSystemEntity.isLinkSync(javac)) {
215 javac = new io.Link(javac).resolveSymbolicLinksSync();
216 }
217 // TODO(zerny): Take into account Mac javac paths can be of the form:
218 // .../Versions/X/Commands/javac
219 String javaHome =
220 _guessJavaHomeArch(javac.replaceAll('/bin/javac', ''), arch);
221 if (javaHome != null) return javaHome;
222 }
223
224 return null;
225 }
226
227 String _guessJavaHomeArch(String javaHome, String arch) {
228 if (javaHome == null) return null;
229
230 // Check if the java installation supports the requested architecture.
231 if (new io.File('$javaHome/bin/java').existsSync()) {
232 int supportsVersion = io.Process.runSync(
233 '$javaHome/bin/java', ['-d$arch', '-version']).exitCode;
234 if (supportsVersion == 0 && _isValidJDK(javaHome)) return javaHome;
235 }
236
237 // Check for architecture specific installation by post-fixing arch.
238 String archPostfix = '${javaHome}-$arch';
239 if (_isValidJDK(archPostfix)) return archPostfix;
240
241 // Check for architecture specific installation by replacing amd64 and i386.
242 String archReplace;
243 if (arch == '32' && javaHome.contains('amd64')) {
244 archReplace = javaHome.replaceAll('amd64', 'i386');
245 } else if (arch == '64' && javaHome.contains('i386')) {
246 archReplace = javaHome.replaceAll('i386', 'amd64');
247 }
248 if (_isValidJDK(archReplace)) return archReplace;
249
250 return null;
251 }
252
253 bool _isValidJDK(String javaHome) {
254 if (javaHome == null) return false;
255 return new io.File('$javaHome/include/jni.h').existsSync();
256 }
257 }
258
259 /// Pattern that matches warnings (from dart2js) that contain a comment saying
260 /// "NO_LINT".
261 final RegExp noLintFilter =
262 new RegExp(r"[^\n]*\n[^\n]*\n[^\n]* // NO_LINT\n *\^+\n");
263
264 class FletchTestOutputCommand implements CommandOutput {
265 final Command command;
266 final Duration time;
267 final Message message;
268 final List<String> stdoutLines;
269
270 FletchTestOutputCommand(
271 this.command,
272 this.message,
273 this.time,
274 this.stdoutLines);
275
276 Expectation result(TestCase testCase) {
277 switch (message.type) {
278 case 'TestPassed':
279 return Expectation.PASS;
280
281 case 'TestFailed':
282 return Expectation.FAIL;
283
284 case 'TimedOut':
285 return Expectation.TIMEOUT;
286
287 default:
288 return Expectation.CRASH;
289 }
290 }
291
292 bool get hasCrashed => false;
293
294 bool get hasTimedOut => false;
295
296 bool didFail(testCase) => message.type != 'TestPassed';
297
298 bool hasFailed(TestCase testCase) {
299 return testCase.isNegative ? !didFail(testCase) : didFail(testCase);
300 }
301
302 bool get canRunDependendCommands => false;
303
304 bool get successful => true;
305
306 int get exitCode => 0;
307
308 int get pid => 0;
309
310 List<int> get stdout {
311 if (stdoutLines != null) {
312 return UTF8.encode(stdoutLines.join("\n"));
313 } else {
314 return <int>[];
315 }
316 }
317
318 List<int> get stderr {
319 String result;
320
321 switch (message.type) {
322 case 'TestPassed':
323 case 'TimedOut':
324 return <int>[];
325
326 case 'TestFailed':
327 TestFailed failed = message;
328 result = '${failed.error}\n${failed.stackTrace}';
329 break;
330
331 default:
332 result = '$message';
333 break;
334 }
335 return UTF8.encode(result);
336 }
337
338 List<String> get diagnostics => <String>[];
339
340 bool get compilationSkipped => false;
341 }
342
343 class FletchTestCommand implements Command {
344 final String _name;
345
346 final TestCompleter _completer;
347
348 FletchTestCommand(this._name, this._completer);
349
350 String get displayName => "fletch_test";
351
352 int get maxNumRetries => 0;
353
354 Future<FletchTestOutputCommand> run(int timeout) {
355 Stopwatch sw = new Stopwatch()..start();
356 return _completer.run(this, timeout).then((NamedMessage message) {
357 FletchTestOutputCommand output =
358 new FletchTestOutputCommand(
359 this, message, sw.elapsed, _completer.testOutput[message.name]);
360 _completer.done(this);
361 return output;
362 });
363 }
364
365 String toString() => 'FletchTestCommand($_name)';
366
367 set displayName(_) => throw "not supported";
368
369 get commandLine => throw "not supported";
370 set commandLine(_) => throw "not supported";
371
372 String get reproductionCommand => throw "not supported";
373
374 get outputIsUpToDate => throw "not supported";
375 }
376
377 class TestCompleter {
378 final Map<String, FletchTestCommand> expected =
379 new Map<String, FletchTestCommand>();
380 final Map<String, Completer> completers = new Map<String, Completer>();
381 final Completer<List<String>> testNamesCompleter =
382 new Completer<List<String>>();
383 final Map<String, List<String>> testOutput = new Map<String, List<String>>();
384 final io.Process vmProcess;
385 final io.Socket socket;
386
387 int exitCode;
388 String stderr = "";
389
390 TestCompleter(this.vmProcess, this.socket);
391
392 void initialize() {
393 List<String> stderrLines = <String>[];
394 Future stdoutFuture =
395 vmProcess.stdout.transform(UTF8.decoder).transform(new LineSplitter())
396 .listen(io.stdout.writeln).asFuture();
397 bool inDartVmUncaughtMessage = false;
398 Future stderrFuture =
399 vmProcess.stderr.transform(UTF8.decoder).transform(new LineSplitter())
400 .listen((String line) {
401 io.stderr.writeln(line);
402 stderrLines.add(line);
403 }).asFuture();
404 vmProcess.exitCode.then((value) {
405 exitCode = value;
406 stderr = stderrLines.join("\n");
407 for (String name in completers.keys) {
408 Completer completer = completers[name];
409 completer.complete(
410 new TestFailed(
411 name,
412 "Helper program exited prematurely with exit code $exitCode.",
413 stderr));
414 }
415 if (exitCode != 0) {
416 stdoutFuture.then((_) => stderrFuture).then((_) {
417 throw "Helper program exited with exit code $exitCode.\n$stderr";
418 });
419 }
420 });
421 // TODO(ahe): Don't use StreamIterator here, just use listen and
422 // processMessage.
423 StreamIterator<Message> messages;
424 if (socket == null) {
425 messages = new StreamIterator<Message>(
426 new Stream<Message>.fromIterable(<Message>[]));
427 } else {
428 messages = new StreamIterator<Message>(
429 socket
430 .transform(UTF8.decoder).transform(new LineSplitter())
431 .transform(messageTransformer));
432 }
433 process(messages);
434 }
435
436 Future<List<String>> requestTestNames() {
437 if (socket == null) return new Future.value(<String>[]);
438 socket.writeln(JSON.encode(const ListTests().toJson()));
439 return testNamesCompleter.future;
440 }
441
442 void expect(FletchTestCommand command) {
443 expected[command._name] = command;
444 }
445
446 void done(FletchTestCommand command) {
447 expected.remove(command._name);
448 if (expected.isEmpty) {
449 allDone();
450 }
451 }
452
453 Future run(FletchTestCommand command, int timeout) {
454 if (command._name == "self/testNeverCompletes") {
455 // Ensure timeout test times out quickly.
456 timeout = 1;
457 }
458 socket.writeln(
459 JSON.encode(new RunTest(command._name).toJson()));
460 Timer timer = new Timer(new Duration(seconds: timeout), () {
461 socket.writeln(
462 JSON.encode(new TimedOut(command._name).toJson()));
463 });
464
465 Completer completer = new Completer();
466 completers[command._name] = completer;
467 if (exitCode != null) {
468 completer.complete(
469 new TestFailed(
470 command._name,
471 "Helper program exited prematurely with exit code $exitCode.",
472 stderr));
473 }
474 return completer.future.then((value) {
475 timer.cancel();
476 return value;
477 });
478 }
479
480 void processMessage(Message message) {
481 switch (message.type) {
482 case 'Info':
483 Info info = message;
484 // For debugging, shouldn't normally be called.
485 print(info.data);
486 break;
487 case 'TestPassed':
488 case 'TestFailed':
489 case 'TimedOut':
490 NamedMessage namedMessage = message;
491 Completer completer = completers.remove(namedMessage.name);
492 completer.complete(message);
493 break;
494 case 'ListTestsReply':
495 ListTestsReply reply = message;
496 testNamesCompleter.complete(reply.tests);
497 break;
498 case 'InternalErrorMessage':
499 ErrorMessage error = message;
500 print(error.error);
501 print(error.stackTrace);
502 throw "Internal error in helper process: ${error.error}";
503 case 'TestStdoutLine':
504 TestStdoutLine line = message;
505 testOutput.putIfAbsent(line.name, () => <String>[]).add(line.line);
506 break;
507 default:
508 throw "Unhandled message from helper process: $message";
509 }
510 }
511
512 void process(StreamIterator<Message> messages) {
513 messages.moveNext().then((bool hasNext) {
514 if (hasNext) {
515 processMessage(messages.current);
516 process(messages);
517 }
518 });
519 }
520
521 void allDone() {
522 // This should cause the vmProcess to exit.
523 socket.close();
524 }
525 }
OLDNEW
« no previous file with comments | « tools/testing/dart/fletch_session_command.dart ('k') | tools/testing/dart/fletch_warnings_suite.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698