Index: tools/testing/dart/fletch_test_suite.dart |
diff --git a/tools/testing/dart/fletch_test_suite.dart b/tools/testing/dart/fletch_test_suite.dart |
deleted file mode 100644 |
index 33fc535dfb4e568515a13ddf950e59491a47cf22..0000000000000000000000000000000000000000 |
--- a/tools/testing/dart/fletch_test_suite.dart |
+++ /dev/null |
@@ -1,525 +0,0 @@ |
-// Copyright (c) 2015, the Dartino 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.md file. |
- |
-/// Test suite for running tests in a shared Dart VM. Take a look at |
-/// ../../../tests/fletch_tests/all_tests.dart for more information. |
-library test.fletch_test_suite; |
- |
-import 'dart:io' as io; |
- |
-import 'dart:convert' show |
- JSON, |
- LineSplitter, |
- UTF8, |
- Utf8Decoder; |
- |
-import 'dart:async' show |
- Completer, |
- Future, |
- Stream, |
- StreamIterator, |
- Timer; |
- |
-import 'test_suite.dart' show |
- TestSuite, |
- TestUtils; |
- |
-import 'test_runner.dart' show |
- Command, |
- CommandBuilder, |
- CommandOutput, |
- TestCase; |
- |
-import 'runtime_configuration.dart' show |
- RuntimeConfiguration; |
- |
-import 'status_file_parser.dart' show |
- Expectation, |
- ReadTestExpectationsInto, |
- TestExpectations; |
- |
-import '../../../tests/fletch_tests/messages.dart' show |
- ErrorMessage, |
- Info, |
- ListTests, |
- ListTestsReply, |
- Message, |
- NamedMessage, |
- RunTest, |
- TestFailed, |
- TestStdoutLine, |
- TimedOut, |
- messageTransformer; |
- |
-class FletchTestRuntimeConfiguration extends RuntimeConfiguration { |
- final String system; |
- final String dartBinary; |
- |
- FletchTestRuntimeConfiguration(Map configuration) |
- : system = configuration['system'], |
- dartBinary = '${TestUtils.buildDir(configuration)}' |
- '${io.Platform.pathSeparator}dart', |
- super.subclass(); |
-} |
- |
-class FletchTestSuite extends TestSuite { |
- final String testSuiteDir; |
- |
- TestCompleter completer; |
- |
- FletchTestSuite(Map configuration, this.testSuiteDir) |
- : super(configuration, "fletch_tests"); |
- |
- void forEachTest( |
- void onTest(TestCase testCase), |
- Map testCache, |
- [void onDone()]) { |
- this.doTest = onTest; |
- if (configuration['runtime'] != 'fletch_tests') { |
- onDone(); |
- return; |
- } |
- |
- FletchTestRuntimeConfiguration runtimeConfiguration = |
- new RuntimeConfiguration(configuration); |
- |
- TestExpectations expectations = new TestExpectations(); |
- String buildDir = TestUtils.buildDir(configuration); |
- String version; |
- |
- // Define a path for temporary output generated during tests. |
- String tempDirPath = '$buildDir/temporary_test_output'; |
- io.Directory tempDir = new io.Directory(tempDirPath); |
- try { |
- tempDir.deleteSync(recursive: true); |
- } on io.FileSystemException catch (e) { |
- // Ignored, we assume the file did not exist. |
- } |
- |
- String javaHome = _guessJavaHome(configuration["arch"]); |
- if (javaHome == null) { |
- String arch = configuration["arch"]; |
- print("Notice: Java tests are disabled"); |
- print("Unable to find a JDK installation for architecture $arch"); |
- print("Install a JDK or set JAVA_PATH to an existing installation."); |
- // TODO(zerny): Throw an error if no-java is not supplied. |
- } else { |
- print("Notice: Enabled Java tests using JDK at $javaHome"); |
- } |
- |
- bool helperProgramExited = false; |
- io.Process vmProcess; |
- ReadTestExpectationsInto( |
- expectations, '$testSuiteDir/fletch_tests.status', |
- configuration).then((_) { |
- return new io.File('$buildDir/gen/version.cc').readAsLines(); |
- }).then((List<String> versionFileLines) { |
- // Search for the 'return "version_string";' line. |
- for (String line in versionFileLines) { |
- if (line.contains('return')) { |
- version = line.substring( |
- line.indexOf('"') + 1, line.lastIndexOf('"')); |
- } |
- } |
- assert(version != null); |
- }).then((_) { |
- return io.ServerSocket.bind(io.InternetAddress.LOOPBACK_IP_V4, 0); |
- }).then((io.ServerSocket server) { |
- return io.Process.start( |
- runtimeConfiguration.dartBinary, |
- ['-Dfletch-vm=$buildDir/fletch-vm', |
- '-Dfletch.version=$version', |
- '-Ddart-sdk=third_party/dart/sdk/', |
- '-Dtests-dir=tests/', |
- '-Djava-home=$javaHome', |
- '-Dtest.dart.build-dir=$buildDir', |
- '-Dtest.dart.build-arch=${configuration["arch"]}', |
- '-Dtest.dart.build-system=${configuration["system"]}', |
- '-Dtest.dart.build-clang=${configuration["clang"]}', |
- '-Dtest.dart.build-asan=${configuration["asan"]}', |
- '-Dtest.dart.temp-dir=$tempDirPath', |
- '-Dtest.dart.servicec-dir=tools/servicec/', |
- '-c', |
- '--packages=.packages', |
- '-Dtest.fletch_test_suite.port=${server.port}', |
- '$testSuiteDir/fletch_test_suite.dart']).then((io.Process process) { |
- process.exitCode.then((_) { |
- helperProgramExited = true; |
- server.close(); |
- }); |
- vmProcess = process; |
- return process.stdin.close(); |
- }).then((_) { |
- return server.first.catchError((error) { |
- // The VM died before we got a connection. |
- assert(helperProgramExited); |
- return null; |
- }); |
- }).then((io.Socket socket) { |
- server.close(); |
- return socket; |
- }); |
- }).then((io.Socket socket) { |
- assert(socket != null || helperProgramExited); |
- completer = new TestCompleter(vmProcess, socket); |
- completer.initialize(); |
- return completer.requestTestNames(); |
- }).then((List<String> testNames) { |
- for (String name in testNames) { |
- Set<Expectation> expectedOutcomes = expectations.expectations(name); |
- TestCase testCase = new TestCase( |
- 'fletch_tests/$name', <Command>[], configuration, expectedOutcomes); |
- var command = new FletchTestCommand(name, completer); |
- testCase.commands.add(command); |
- if (!expectedOutcomes.contains(Expectation.SKIP)) { |
- completer.expect(command); |
- } |
- enqueueNewTestCase(testCase); |
- } |
- }).then((_) { |
- onDone(); |
- }); |
- } |
- |
- void cleanup() { |
- completer.allDone(); |
- } |
- |
- String _guessJavaHome(String buildArchitecture) { |
- String arch = buildArchitecture == 'ia32' ? '32' : '64'; |
- |
- // Try to locate a valid installation based on JAVA_HOME. |
- String javaHome = |
- _guessJavaHomeArch(io.Platform.environment['JAVA_HOME'], arch); |
- if (javaHome != null) return javaHome; |
- |
- // Try to locate a valid installation using the java_home utility. |
- String javaHomeUtil = '/usr/libexec/java_home'; |
- if (new io.File(javaHomeUtil).existsSync()) { |
- List<String> args = <String>['-v', '1.6+', '-d', arch]; |
- io.ProcessResult result = |
- io.Process.runSync(javaHomeUtil, args); |
- if (result.exitCode == 0) { |
- String javaHome = result.stdout.trim(); |
- if (_isValidJDK(javaHome)) return javaHome; |
- } |
- } |
- |
- // Try to locate a valid installation using the path to javac. |
- io.ProcessResult result = |
- io.Process.runSync('command', ['-v', 'javac'], runInShell: true); |
- if (result.exitCode == 0) { |
- String javac = result.stdout.trim(); |
- while (io.FileSystemEntity.isLinkSync(javac)) { |
- javac = new io.Link(javac).resolveSymbolicLinksSync(); |
- } |
- // TODO(zerny): Take into account Mac javac paths can be of the form: |
- // .../Versions/X/Commands/javac |
- String javaHome = |
- _guessJavaHomeArch(javac.replaceAll('/bin/javac', ''), arch); |
- if (javaHome != null) return javaHome; |
- } |
- |
- return null; |
- } |
- |
- String _guessJavaHomeArch(String javaHome, String arch) { |
- if (javaHome == null) return null; |
- |
- // Check if the java installation supports the requested architecture. |
- if (new io.File('$javaHome/bin/java').existsSync()) { |
- int supportsVersion = io.Process.runSync( |
- '$javaHome/bin/java', ['-d$arch', '-version']).exitCode; |
- if (supportsVersion == 0 && _isValidJDK(javaHome)) return javaHome; |
- } |
- |
- // Check for architecture specific installation by post-fixing arch. |
- String archPostfix = '${javaHome}-$arch'; |
- if (_isValidJDK(archPostfix)) return archPostfix; |
- |
- // Check for architecture specific installation by replacing amd64 and i386. |
- String archReplace; |
- if (arch == '32' && javaHome.contains('amd64')) { |
- archReplace = javaHome.replaceAll('amd64', 'i386'); |
- } else if (arch == '64' && javaHome.contains('i386')) { |
- archReplace = javaHome.replaceAll('i386', 'amd64'); |
- } |
- if (_isValidJDK(archReplace)) return archReplace; |
- |
- return null; |
- } |
- |
- bool _isValidJDK(String javaHome) { |
- if (javaHome == null) return false; |
- return new io.File('$javaHome/include/jni.h').existsSync(); |
- } |
-} |
- |
-/// Pattern that matches warnings (from dart2js) that contain a comment saying |
-/// "NO_LINT". |
-final RegExp noLintFilter = |
- new RegExp(r"[^\n]*\n[^\n]*\n[^\n]* // NO_LINT\n *\^+\n"); |
- |
-class FletchTestOutputCommand implements CommandOutput { |
- final Command command; |
- final Duration time; |
- final Message message; |
- final List<String> stdoutLines; |
- |
- FletchTestOutputCommand( |
- this.command, |
- this.message, |
- this.time, |
- this.stdoutLines); |
- |
- Expectation result(TestCase testCase) { |
- switch (message.type) { |
- case 'TestPassed': |
- return Expectation.PASS; |
- |
- case 'TestFailed': |
- return Expectation.FAIL; |
- |
- case 'TimedOut': |
- return Expectation.TIMEOUT; |
- |
- default: |
- return Expectation.CRASH; |
- } |
- } |
- |
- bool get hasCrashed => false; |
- |
- bool get hasTimedOut => false; |
- |
- bool didFail(testCase) => message.type != 'TestPassed'; |
- |
- bool hasFailed(TestCase testCase) { |
- return testCase.isNegative ? !didFail(testCase) : didFail(testCase); |
- } |
- |
- bool get canRunDependendCommands => false; |
- |
- bool get successful => true; |
- |
- int get exitCode => 0; |
- |
- int get pid => 0; |
- |
- List<int> get stdout { |
- if (stdoutLines != null) { |
- return UTF8.encode(stdoutLines.join("\n")); |
- } else { |
- return <int>[]; |
- } |
- } |
- |
- List<int> get stderr { |
- String result; |
- |
- switch (message.type) { |
- case 'TestPassed': |
- case 'TimedOut': |
- return <int>[]; |
- |
- case 'TestFailed': |
- TestFailed failed = message; |
- result = '${failed.error}\n${failed.stackTrace}'; |
- break; |
- |
- default: |
- result = '$message'; |
- break; |
- } |
- return UTF8.encode(result); |
- } |
- |
- List<String> get diagnostics => <String>[]; |
- |
- bool get compilationSkipped => false; |
-} |
- |
-class FletchTestCommand implements Command { |
- final String _name; |
- |
- final TestCompleter _completer; |
- |
- FletchTestCommand(this._name, this._completer); |
- |
- String get displayName => "fletch_test"; |
- |
- int get maxNumRetries => 0; |
- |
- Future<FletchTestOutputCommand> run(int timeout) { |
- Stopwatch sw = new Stopwatch()..start(); |
- return _completer.run(this, timeout).then((NamedMessage message) { |
- FletchTestOutputCommand output = |
- new FletchTestOutputCommand( |
- this, message, sw.elapsed, _completer.testOutput[message.name]); |
- _completer.done(this); |
- return output; |
- }); |
- } |
- |
- String toString() => 'FletchTestCommand($_name)'; |
- |
- set displayName(_) => throw "not supported"; |
- |
- get commandLine => throw "not supported"; |
- set commandLine(_) => throw "not supported"; |
- |
- String get reproductionCommand => throw "not supported"; |
- |
- get outputIsUpToDate => throw "not supported"; |
-} |
- |
-class TestCompleter { |
- final Map<String, FletchTestCommand> expected = |
- new Map<String, FletchTestCommand>(); |
- final Map<String, Completer> completers = new Map<String, Completer>(); |
- final Completer<List<String>> testNamesCompleter = |
- new Completer<List<String>>(); |
- final Map<String, List<String>> testOutput = new Map<String, List<String>>(); |
- final io.Process vmProcess; |
- final io.Socket socket; |
- |
- int exitCode; |
- String stderr = ""; |
- |
- TestCompleter(this.vmProcess, this.socket); |
- |
- void initialize() { |
- List<String> stderrLines = <String>[]; |
- Future stdoutFuture = |
- vmProcess.stdout.transform(UTF8.decoder).transform(new LineSplitter()) |
- .listen(io.stdout.writeln).asFuture(); |
- bool inDartVmUncaughtMessage = false; |
- Future stderrFuture = |
- vmProcess.stderr.transform(UTF8.decoder).transform(new LineSplitter()) |
- .listen((String line) { |
- io.stderr.writeln(line); |
- stderrLines.add(line); |
- }).asFuture(); |
- vmProcess.exitCode.then((value) { |
- exitCode = value; |
- stderr = stderrLines.join("\n"); |
- for (String name in completers.keys) { |
- Completer completer = completers[name]; |
- completer.complete( |
- new TestFailed( |
- name, |
- "Helper program exited prematurely with exit code $exitCode.", |
- stderr)); |
- } |
- if (exitCode != 0) { |
- stdoutFuture.then((_) => stderrFuture).then((_) { |
- throw "Helper program exited with exit code $exitCode.\n$stderr"; |
- }); |
- } |
- }); |
- // TODO(ahe): Don't use StreamIterator here, just use listen and |
- // processMessage. |
- StreamIterator<Message> messages; |
- if (socket == null) { |
- messages = new StreamIterator<Message>( |
- new Stream<Message>.fromIterable(<Message>[])); |
- } else { |
- messages = new StreamIterator<Message>( |
- socket |
- .transform(UTF8.decoder).transform(new LineSplitter()) |
- .transform(messageTransformer)); |
- } |
- process(messages); |
- } |
- |
- Future<List<String>> requestTestNames() { |
- if (socket == null) return new Future.value(<String>[]); |
- socket.writeln(JSON.encode(const ListTests().toJson())); |
- return testNamesCompleter.future; |
- } |
- |
- void expect(FletchTestCommand command) { |
- expected[command._name] = command; |
- } |
- |
- void done(FletchTestCommand command) { |
- expected.remove(command._name); |
- if (expected.isEmpty) { |
- allDone(); |
- } |
- } |
- |
- Future run(FletchTestCommand command, int timeout) { |
- if (command._name == "self/testNeverCompletes") { |
- // Ensure timeout test times out quickly. |
- timeout = 1; |
- } |
- socket.writeln( |
- JSON.encode(new RunTest(command._name).toJson())); |
- Timer timer = new Timer(new Duration(seconds: timeout), () { |
- socket.writeln( |
- JSON.encode(new TimedOut(command._name).toJson())); |
- }); |
- |
- Completer completer = new Completer(); |
- completers[command._name] = completer; |
- if (exitCode != null) { |
- completer.complete( |
- new TestFailed( |
- command._name, |
- "Helper program exited prematurely with exit code $exitCode.", |
- stderr)); |
- } |
- return completer.future.then((value) { |
- timer.cancel(); |
- return value; |
- }); |
- } |
- |
- void processMessage(Message message) { |
- switch (message.type) { |
- case 'Info': |
- Info info = message; |
- // For debugging, shouldn't normally be called. |
- print(info.data); |
- break; |
- case 'TestPassed': |
- case 'TestFailed': |
- case 'TimedOut': |
- NamedMessage namedMessage = message; |
- Completer completer = completers.remove(namedMessage.name); |
- completer.complete(message); |
- break; |
- case 'ListTestsReply': |
- ListTestsReply reply = message; |
- testNamesCompleter.complete(reply.tests); |
- break; |
- case 'InternalErrorMessage': |
- ErrorMessage error = message; |
- print(error.error); |
- print(error.stackTrace); |
- throw "Internal error in helper process: ${error.error}"; |
- case 'TestStdoutLine': |
- TestStdoutLine line = message; |
- testOutput.putIfAbsent(line.name, () => <String>[]).add(line.line); |
- break; |
- default: |
- throw "Unhandled message from helper process: $message"; |
- } |
- } |
- |
- void process(StreamIterator<Message> messages) { |
- messages.moveNext().then((bool hasNext) { |
- if (hasNext) { |
- processMessage(messages.current); |
- process(messages); |
- } |
- }); |
- } |
- |
- void allDone() { |
- // This should cause the vmProcess to exit. |
- socket.close(); |
- } |
-} |