| Index: utils/testrunner/layout_test_controller.dart
|
| ===================================================================
|
| --- utils/testrunner/layout_test_controller.dart (revision 21957)
|
| +++ utils/testrunner/layout_test_controller.dart (working copy)
|
| @@ -124,177 +124,223 @@
|
| notifyDone(failCount > 0 ? -1 : 0);
|
| }
|
|
|
| +/*
|
| + * Run an external process [cmd] with command line arguments [args].
|
| + * [timeout] can be used to forcefully terminate the process after
|
| + * some number of seconds. This is used by runCommand and startProcess.
|
| + * If [procId] is non-zero (i.e. called from startProcess) then a reference
|
| + * to the [Process] will be put in a map with key [procId]; in this case
|
| + * the process can be terminated later by calling [stopProcess] and
|
| + * passing in the [procId].
|
| + * [outputMonitor] is an optional function that will be called back with each
|
| + * line of output from the process.
|
| + * Returns a [Future] for when the process terminates.
|
| + */
|
| +Future _processHelper(String command, List<String> args,
|
| + List stdout, List stderr,
|
| + int timeout, int procId, Function outputMonitor, bool raw) {
|
| + var timer = null;
|
| + return Process.start(command, args).then((process) {
|
| +
|
| + timer = new Timer(new Duration(seconds: timeout), () {
|
| + timer = null;
|
| + process.kill();
|
| + });
|
| +
|
| + if (raw) {
|
| + process.stdout.listen((c) { stdout.addAll(c); });
|
| + } else {
|
| + _pipeStream(process.stdout, stdout, outputMonitor);
|
| + }
|
| + _pipeStream(process.stderr, stderr, outputMonitor);
|
| + return process.exitCode;
|
| + }).then((exitCode) {
|
| + if (timer != null) {
|
| + timer.cancel();
|
| + }
|
| + return exitCode;
|
| + })
|
| + .catchError((e) {
|
| + stderr.add("#Error starting process $command: ${e.error}");
|
| + });
|
| +}
|
| +
|
| +void _pipeStream(Stream stream, List<String> destination,
|
| + Function outputMonitor) {
|
| + stream
|
| + .transform(new StringDecoder())
|
| + .transform(new LineTransformer())
|
| + .listen((String line) {
|
| + if (outputMonitor != null) {
|
| + outputMonitor(line);
|
| + }
|
| + destination.add(line);
|
| + });
|
| +}
|
| +
|
| +/**
|
| + * Run an external process [cmd] with command line arguments [args].
|
| + * [timeout] can be used to forcefully terminate the process after
|
| + * some number of seconds.
|
| + * Returns a [Future] for when the process terminates.
|
| + */
|
| +Future runCommand(String command, List<String> args,
|
| + List stdout, List stderr,
|
| + {int timeout: 300, Function outputMonitor,
|
| + bool raw: false}) {
|
| + return _processHelper(command, args, stdout, stderr,
|
| + timeout, 0, outputMonitor, raw);
|
| +}
|
| +
|
| +String parseLabel(String line) {
|
| + if (line.startsWith('CONSOLE MESSAGE')) {
|
| + var idx = line.indexOf('#TEST ');
|
| + if (idx > 0) {
|
| + return line.substring(idx + 6);
|
| + }
|
| + }
|
| + return null;
|
| +}
|
| +
|
| runTextLayoutTest(testNum) {
|
| var url = '$baseUrl?test=$testNum';
|
| var stdout = new List();
|
| + var stderr = new List();
|
| start = new DateTime.now();
|
| - Process.start(drt, [url]).then((process) {
|
| - // Drain stderr to not leak resources.
|
| - process.stderr.onData = process.stderr.read;
|
| - StringInputStream stdoutStringStream =
|
| - new StringInputStream(process.stdout);
|
| - stdoutStringStream.onLine = () {
|
| - if (stdoutStringStream.closed) return;
|
| - var line = stdoutStringStream.readLine();
|
| - while (null != line) {
|
| - stdout.add(line);
|
| - line = stdoutStringStream.readLine();
|
| - }
|
| - };
|
| - process.onExit = (exitCode) {
|
| - process.close();
|
| - if (stdout.length > 0 && stdout[stdout.length-1].startsWith('#EOF')) {
|
| - stdout.removeLast();
|
| - }
|
| - var done = false;
|
| - var i = 0;
|
| - var label = null;
|
| - var labelMarker = 'CONSOLE MESSAGE: #TEST ';
|
| - var contentMarker = 'layer at ';
|
| - while (i < stdout.length) {
|
| - if (label == null && stdout[i].startsWith(labelMarker)) {
|
| - label = stdout[i].substring(labelMarker.length);
|
| - if (label == 'NONEXISTENT') {
|
| - complete();
|
| + runCommand(drt, [url], stdout, stderr).then((e) {
|
| + if (stdout.length > 0 && stdout[stdout.length-1].startsWith('#EOF')) {
|
| + stdout.removeLast();
|
| + }
|
| + var done = false;
|
| + var i = 0;
|
| + var label = null;
|
| + var contentMarker = 'layer at ';
|
| + while (i < stdout.length) {
|
| + if (label == null && (label = parseLabel(stdout[i])) != null) {
|
| + if (label == 'NONEXISTENT') {
|
| + complete();
|
| + return;
|
| + }
|
| + } else if (stdout[i].startsWith(contentMarker)) {
|
| + if (label == null) {
|
| + complete();
|
| + return;
|
| + }
|
| + var expectedFileName =
|
| + '$sourceDir${Platform.pathSeparator}'
|
| + '${label.replaceAll("###", "_")
|
| + .replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.txt';
|
| + var expected = new File(expectedFileName);
|
| + if (regenerate) {
|
| + var osink = expected.openWrite();
|
| + while (i < stdout.length) {
|
| + osink.write(stdout[i]);
|
| + osink.write('\n');
|
| + i++;
|
| }
|
| - } else if (stdout[i].startsWith(contentMarker)) {
|
| - if (label == null) {
|
| - complete();
|
| + osink.close();
|
| + pass(start, label);
|
| + } else if (!expected.existsSync()) {
|
| + fail(start, label, 'No expectation file');
|
| + } else {
|
| + var lines = expected.readAsLinesSync();
|
| + var actualLength = stdout.length - i;
|
| + var compareCount = min(lines.length, actualLength);
|
| + var match = true;
|
| + for (var j = 0; j < compareCount; j++) {
|
| + if (lines[j] != stdout[i + j]) {
|
| + fail(start, label, 'Expectation differs at line ${j + 1}');
|
| + match = false;
|
| + break;
|
| + }
|
| }
|
| - var expectedFileName =
|
| - '$sourceDir${Platform.pathSeparator}'
|
| - '${label.replaceAll("###", "_")
|
| - .replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.txt';
|
| - var expected = new File(expectedFileName);
|
| - if (regenerate) {
|
| - var ostream = expected.openOutputStream(FileMode.WRITE);
|
| - while (i < stdout.length) {
|
| - ostream.writeString(stdout[i]);
|
| - ostream.writeString('\n');
|
| - i++;
|
| + if (match) {
|
| + if (lines.length != actualLength) {
|
| + fail(start, label, 'Expectation file has wrong length');
|
| + } else {
|
| + pass(start, label);
|
| }
|
| - ostream.close();
|
| - pass(start, label);
|
| - } else if (!expected.existsSync()) {
|
| - fail(start, label, 'No expectation file');
|
| - } else {
|
| - var lines = expected.readAsLinesSync();
|
| - var actualLength = stdout.length - i;
|
| - var compareCount = min(lines.length, actualLength);
|
| - var match = true;
|
| - for (var j = 0; j < compareCount; j++) {
|
| - if (lines[j] != stdout[i + j]) {
|
| - fail(start, label, 'Expectation differs at line ${j + 1}');
|
| - match = false;
|
| - break;
|
| - }
|
| - }
|
| - if (match) {
|
| - if (lines.length != actualLength) {
|
| - fail(start, label, 'Expectation file has wrong length');
|
| - } else {
|
| - pass(start, label);
|
| - }
|
| - }
|
| }
|
| - done = true;
|
| - break;
|
| }
|
| - i++;
|
| + done = true;
|
| + break;
|
| }
|
| - if (label != null) {
|
| - if (!done) error(start, label, 'Failed to parse output');
|
| - runTextLayoutTest(testNum + 1);
|
| - }
|
| - };
|
| + i++;
|
| + }
|
| + if (label != null) {
|
| + if (!done) error(start, label, 'Failed to parse output');
|
| + runTextLayoutTest(testNum + 1);
|
| + }
|
| });
|
| }
|
|
|
| runPixelLayoutTest(int testNum) {
|
| var url = '$baseUrl?test=$testNum';
|
| var stdout = new List();
|
| + var stderr = new List();
|
| start = new DateTime.now();
|
| - Process.start(drt, ["$url'-p"]).then((process) {
|
| - // Drain stderr to not leak resources.
|
| - process.stderr.onData = process.stderr.read;
|
| - ListInputStream stdoutStream = process.stdout;
|
| - stdoutStream.onData = () {
|
| - if (!stdoutStream.closed) {
|
| - var data = stdoutStream.read();
|
| - stdout.addAll(data);
|
| + runCommand(drt, ["$url'-p"], stdout, stderr, raw:true).then((exitCode) {
|
| + var contentMarker = 'Content-Length: ';
|
| + var eol = '\n'.codeUnitAt(0);
|
| + var pos = 0;
|
| + var label = null;
|
| + var done = false;
|
| +
|
| + while(pos < stdout.length) {
|
| + StringBuffer sb = new StringBuffer();
|
| + while (pos < stdout.length && stdout[pos] != eol) {
|
| + sb.writeCharCode(stdout[pos++]);
|
| }
|
| - };
|
| - stdoutStream.onError = (e) {
|
| - print(e);
|
| - };
|
| - process.onExit = (exitCode) {
|
| - stdout.addAll(process.stdout.read());
|
| - process.close();
|
| - var labelMarker = 'CONSOLE MESSAGE: #TEST ';
|
| - var contentMarker = 'Content-Length: ';
|
| - var eol = '\n'.codeUnitAt(0);
|
| - var pos = -1;
|
| - var label = null;
|
| - var done = false;
|
| + if (++pos >= stdout.length && line == '') break;
|
| + var line = sb.toString();
|
|
|
| - while(pos < stdout.length) {
|
| - var idx = stdout.indexOf(eol, ++pos);
|
| - if (idx < 0) break;
|
| - StringBuffer sb = new StringBuffer();
|
| - for (var i = pos; i < idx; i++) {
|
| - sb.writeCharCode(stdout[i]);
|
| + if (label == null && (label = parseLabel(line)) != null) {
|
| + if (label == 'NONEXISTENT') {
|
| + complete();
|
| }
|
| - var line = sb.toString();
|
| -
|
| - if (label == null && line.startsWith(labelMarker)) {
|
| - label = line.substring(labelMarker.length);
|
| - if (label == 'NONEXISTENT') {
|
| - complete();
|
| - }
|
| - } else if (line.startsWith(contentMarker)) {
|
| - if (label == null) {
|
| - complete();
|
| - }
|
| - var len = int.parse(line.substring(contentMarker.length));
|
| - pos = idx + 1;
|
| - var expectedFileName =
|
| - '$sourceDir${Platform.pathSeparator}'
|
| - '${label.replaceAll("###","_").
|
| - replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.png';
|
| - var expected = new File(expectedFileName);
|
| - if (regenerate) {
|
| - var ostream = expected.openOutputStream(FileMode.WRITE);
|
| - ostream.writeFrom(stdout, pos, len);
|
| - ostream.close();
|
| - pass(start, label);
|
| - } else if (!expected.existsSync()) {
|
| - fail(start, label, 'No expectation file');
|
| + } else if (line.startsWith(contentMarker)) {
|
| + if (label == null) {
|
| + complete();
|
| + }
|
| + var len = int.parse(line.substring(contentMarker.length));
|
| + var expectedFileName =
|
| + '$sourceDir${Platform.pathSeparator}'
|
| + '${label.replaceAll("###","_").
|
| + replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.png';
|
| + var expected = new File(expectedFileName);
|
| + if (regenerate) {
|
| + var osink = expected.openWrite();
|
| + stdout.removeRange(0, pos);
|
| + stdout.length = len;
|
| + osink.add(stdout);
|
| + osink.close();
|
| + pass(start, label);
|
| + } else if (!expected.existsSync()) {
|
| + fail(start, label, 'No expectation file');
|
| + } else {
|
| + var bytes = expected.readAsBytesSync();
|
| + if (bytes.length != len) {
|
| + fail(start, label, 'Expectation file has wrong length');
|
| } else {
|
| - var bytes = expected.readAsBytesSync();
|
| - if (bytes.length != len) {
|
| - fail(start, label, 'Expectation file has wrong length');
|
| - } else {
|
| - var match = true;
|
| - for (var j = 0; j < len; j++) {
|
| - if (bytes[j] != stdout[pos + j]) {
|
| - fail(start, label, 'Expectation differs at byte ${j + 1}');
|
| - match = false;
|
| - break;
|
| - }
|
| + var match = true;
|
| + for (var j = 0; j < len; j++) {
|
| + if (bytes[j] != stdout[pos + j]) {
|
| + fail(start, label, 'Expectation differs at byte ${j + 1}');
|
| + match = false;
|
| + break;
|
| }
|
| - if (match) pass(start, label);
|
| }
|
| + if (match) pass(start, label);
|
| }
|
| - done = true;
|
| - break;
|
| }
|
| - pos = idx;
|
| + done = true;
|
| + break;
|
| }
|
| - if (label != null) {
|
| - if (!done) error(start, label, 'Failed to parse output');
|
| - runPixelLayoutTest(testNum + 1);
|
| - }
|
| - };
|
| + }
|
| + if (label != null) {
|
| + if (!done) error(start, label, 'Failed to parse output');
|
| + runPixelLayoutTest(testNum + 1);
|
| + }
|
| });
|
| }
|
|
|
|
|