Index: tools/testing/dart/test_runner.dart |
=================================================================== |
--- tools/testing/dart/test_runner.dart (revision 4909) |
+++ tools/testing/dart/test_runner.dart (working copy) |
@@ -132,6 +132,8 @@ |
List<String> get batchTestArguments() => commands.last().arguments; |
void completed() { completedHandler(this); } |
+ |
+ bool get usesWebDriver() => configuration['component'] == 'webdriver'; |
} |
@@ -480,8 +482,8 @@ |
for (var line in testCase.output.stdout) print(line); |
} |
if (allowRetries != null && allowRetries |
- && testCase.configuration['component'] == 'webdriver' && |
- testCase.output.unexpectedOutput && testCase.numRetries > 0) { |
+ && testCase.usesWebDriver && testCase.output.unexpectedOutput |
+ && testCase.numRetries > 0) { |
// Selenium tests can be flaky. Try rerunning. |
testCase.output.requestRetry = true; |
} |
@@ -514,8 +516,8 @@ |
} else { |
stderr.add('test.dart: Compilion finished $suffix\n'); |
stdout.add('test.dart: Compilion finished $suffix\n'); |
- if (currentStep == totalSteps - 1 |
- && testCase.configuration['component'] == 'webdriver') { |
+ if (currentStep == totalSteps - 1 && testCase.usesWebDriver && |
+ !testCase.configuration['noBatch']) { |
// Note: processQueue will always be non-null for component == webdriver |
// (It is only null for component == vm) |
processQueue._getBatchRunner(testCase).startTest(testCase); |
@@ -593,7 +595,7 @@ |
BatchRunnerProcess(TestCase testCase) { |
_executable = testCase.commands.last().executable; |
_batchArguments = testCase.batchRunnerArguments; |
- _isWebDriver = testCase.configuration['component'] == 'webdriver'; |
+ _isWebDriver = testCase.usesWebDriver; |
} |
bool get active() => _currentTest != null; |
@@ -778,6 +780,7 @@ |
int _numProcesses = 0; |
int _activeTestListers = 0; |
int _maxProcesses; |
+ |
/** The number of tests we allow to actually fail before we stop retrying. */ |
int _MAX_FAILED_NO_RETRY = 4; |
bool _verbose; |
@@ -787,6 +790,7 @@ |
Queue<TestCase> _tests; |
ProgressIndicator _progress; |
String _temporaryDirectory; |
+ |
// For dartc/selenium batch processing we keep a list of batch processes. |
Map<String, List<BatchRunnerProcess>> _batchProcesses; |
@@ -794,12 +798,25 @@ |
// configurations there is no need to repeatedly search the file |
// system, generate tests, and search test files for options. |
Map<String, List<TestInformation>> _testCache; |
+ |
/** |
* String indicating the browser used to run the tests. Empty if no browser |
* used. |
*/ |
- String browserUsed; |
+ String browserUsed = ''; |
+ /** |
+ * Process running the selenium server .jar (only used for Safari and Opera |
+ * tests.) |
+ */ |
+ Process _seleniumServer = null; |
+ |
+ /** True if we are in the process of starting the server. */ |
+ bool _startingServer = false; |
+ |
+ /** True if we find that there is already a selenium jar running. */ |
+ bool _seleniumAlreadyRunning = false; |
+ |
ProcessQueue(int this._maxProcesses, |
String progress, |
Date startTime, |
@@ -815,7 +832,6 @@ |
_batchProcesses = new Map<String, List<BatchRunnerProcess>>(), |
_testCache = new Map<String, List<TestInformation>>() { |
if (!_enqueueMoreWork(this)) _progress.allDone(); |
- browserUsed = ''; |
} |
/** |
@@ -891,6 +907,9 @@ |
void _cleanupAndMarkDone() { |
if (browserUsed != '') { |
killZombieBrowsers(); |
+ if (_seleniumServer != null) { |
+ _seleniumServer.kill(); |
+ } |
} else { |
_progress.allDone(); |
} |
@@ -929,16 +948,119 @@ |
} |
} |
} |
+ |
+ /** |
+ * True if we are using a browser + platform combination that needs the |
+ * Selenium server jar. |
+ */ |
+ bool get _needsSelenium() => new Platform().operatingSystem() == 'macos' && |
+ browserUsed == 'safari'; |
+ /** True if the Selenium Server is ready to be used. */ |
+ bool get _isSeleniumAvailable() => _seleniumServer != null || |
+ _seleniumAlreadyRunning; |
+ |
+ /** |
+ * Restart all the processes that have been waiting/stopped for the server to |
+ * start up. If we just call this once we end up with a single-"threaded" run. |
+ */ |
+ void resumeTesting() { |
+ for (int i = 0; i < _maxProcesses; i++) _tryRunTest(); |
+ } |
+ |
+ /** Start the Selenium Server jar, if appropriate for this platform. */ |
+ void _ensureSeleniumServerRunning() { |
+ if (!_isSeleniumAvailable && !_startingServer) { |
+ _startingServer = true; |
+ |
+ // Check to see if the jar was already running before the program started. |
+ String cmd = 'ps'; |
+ var arg = ['aux']; |
+ if (new Platform().operatingSystem() == 'windows') { |
+ cmd = 'tasklist'; |
+ arg.add('/v'); |
+ } |
+ Process p = new Process.start(cmd, arg); |
+ final StringInputStream stdoutStringStream = |
+ new StringInputStream(p.stdout); |
+ stdoutStringStream.onLine = () { |
+ var line = stdoutStringStream.readLine(); |
+ while (null != line) { |
+ if (const RegExp(@".*selenium-server-standalone.*").hasMatch(line)) { |
+ _seleniumAlreadyRunning = true; |
+ resumeTesting(); |
+ } |
+ line = stdoutStringStream.readLine(); |
+ } |
+ if (!_isSeleniumAvailable) { |
+ _startSeleniumServer(); |
+ } |
+ }; |
+ } |
+ } |
+ |
void _runTest(TestCase test) { |
- if (test.configuration['component'] == 'webdriver') { |
+ if (test.usesWebDriver) { |
browserUsed = test.configuration['browser']; |
+ if (_needsSelenium) _ensureSeleniumServerRunning(); |
} |
_progress.testAdded(); |
_tests.add(test); |
_tryRunTest(); |
} |
+ /** |
+ * Monitor the output of the Selenium server, to know when we are ready to |
+ * begin running tests. |
+ * source: Output(Stream) from the Java server. |
+ */ |
+ Function makeSeleniumServerHandler(StringInputStream source) { |
+ return () { |
+ if (source.closed) return; // TODO(whesse): Remove when bug is fixed. |
+ var line = source.readLine(); |
+ while (null != line) { |
+ if (const RegExp(@".*Started.*Server.*").hasMatch(line) || |
+ const RegExp(@"Exception.*Selenium is already running.*").hasMatch( |
+ line)) { |
+ resumeTesting(); |
+ } |
+ line = source.readLine(); |
+ } |
+ }; |
+ } |
+ |
+ /** |
+ * For browser tests using Safari or Opera, we need to use the Selenium 1.0 |
+ * Java server. |
+ */ |
+ void _startSeleniumServer() { |
+ // Get the absolute path to the Selenium jar. |
+ String filePath = new Options().script; |
+ String pathSep = new Platform().pathSeparator(); |
+ int index = filePath.lastIndexOf(pathSep); |
+ filePath = filePath.substring(0, index) + '${pathSep}testing${pathSep}'; |
+ var dir = new Directory(filePath); |
+ dir.onFile = (String file) { |
+ if (const RegExp(@"selenium-server-standalone-.*\.jar").hasMatch(file) |
+ && _seleniumServer == null) { |
+ _seleniumServer = new Process.start('java', ['-jar', file]); |
+ // Heads up: there seems to an obscure data race of some form in |
+ // the VM between launching the server process and launching the test |
+ // tasks that disappears when you read IO (which is convenient, since |
+ // that is our condition for knowing that the server is ready). |
+ StringInputStream stdoutStringStream = |
+ new StringInputStream(_seleniumServer.stdout); |
+ StringInputStream stderrStringStream = |
+ new StringInputStream(_seleniumServer.stderr); |
+ stdoutStringStream.onLine = |
+ makeSeleniumServerHandler(stdoutStringStream); |
+ stderrStringStream.onLine = |
+ makeSeleniumServerHandler(stderrStringStream); |
+ } |
+ }; |
+ dir.list(); |
+ } |
+ |
void _terminateBatchRunners() { |
for (var runners in _batchProcesses.getValues()) { |
for (var runner in runners) { |
@@ -969,7 +1091,6 @@ |
_checkDone(); |
if (_numProcesses < _maxProcesses && !_tests.isEmpty()) { |
TestCase test = _tests.removeFirst(); |
- if (_verbose) print(test.commands.last().commandLine); |
if (_listTests) { |
final String tab = '\t'; |
String outcomes = |
@@ -978,6 +1099,13 @@ |
tab + Strings.join(test.commands.last().arguments, tab)); |
return; |
} |
+ if (test.usesWebDriver && _needsSelenium && !_isSeleniumAvailable) { |
+ // The server is not ready to run Selenium tests. Put the test back in |
+ // the queue. |
+ _tests.addFirst(test); |
+ return; |
+ } |
+ if (_verbose) print(test.commands.last().commandLine); |
_progress.start(test); |
Function oldCallback = test.completedHandler; |
Function wrapper = (TestCase test_arg) { |