| Index: utils/testrunner/drt_task.dart
|
| ===================================================================
|
| --- utils/testrunner/drt_task.dart (revision 11963)
|
| +++ utils/testrunner/drt_task.dart (working copy)
|
| @@ -5,7 +5,216 @@
|
| /** A pipeline task for running DumpRenderTree. */
|
| class DrtTask extends RunProcessTask {
|
|
|
| - DrtTask(String htmlFileTemplate) {
|
| + String _testFileTemplate;
|
| +
|
| + DrtTask(this._testFileTemplate, String htmlFileTemplate) {
|
| init(config.drtPath, ['--no-timeout', htmlFileTemplate], config.timeout);
|
| }
|
| +
|
| + // In order to extract the relevant parts of the DRT render text
|
| + // output we use a somewhat kludgy approach, but it should be robust.
|
| + // DRT formats output with indentation, and we know that the test title
|
| + // is on a line with 12 spaces indent followed by 'text run', while the
|
| + // IFrame body elements are all indented at least 18 spaces.
|
| + const TEST_LABEL_LINE_PREFIX = ' text run';
|
| + const BODY_LINE_PREFIX = ' ';
|
| +
|
| + void execute(Path testfile, List stdout, List stderr, bool logging,
|
| + Function exitHandler) {
|
| +
|
| + var testname = expandMacros(_testFileTemplate, testfile);
|
| + var isLayout = isLayoutRenderTest(testname) || config.generateRenders;
|
| +
|
| + if (!isLayout) {
|
| + super.execute(testfile, stdout, stderr, logging, exitHandler);
|
| + } else {
|
| + var tmpLog = new List<String>();
|
| + super.execute(testfile, tmpLog, tmpLog, true,
|
| + (code) {
|
| + var layoutFile = layoutFileName(testname);
|
| + var layouts = getFileContents(layoutFile, false);
|
| + var i = 0;
|
| + StringBuffer sbuf = null;
|
| + if (config.generateRenders) {
|
| + sbuf = new StringBuffer();
|
| + }
|
| + while ( i < tmpLog.length) {
|
| + var line = tmpLog[i];
|
| + if (logging) {
|
| + stdout.add(line);
|
| + }
|
| + if (line.startsWith(TEST_LABEL_LINE_PREFIX)) {
|
| + var j = i+1;
|
| + var start = -1, end = -1;
|
| + // Walk forward to the next label or end of log.
|
| + while (j < tmpLog.length &&
|
| + !tmpLog[j].startsWith(TEST_LABEL_LINE_PREFIX)) {
|
| + // Is this a body render line?
|
| + if (tmpLog[j].startsWith(BODY_LINE_PREFIX)) {
|
| + if (start < 0) { // Is it the first?
|
| + start = j;
|
| + }
|
| + } else { // Not a render line.
|
| + if (start >= 0 && end < 0) {
|
| + // We were just in a set of render lines, so this
|
| + // line is the first non-member.
|
| + end = j;
|
| + }
|
| + }
|
| + j++;
|
| + }
|
| + if (start >= 0) { // We have some render lines.
|
| + if (end < 0) {
|
| + end = tmpLog.length; // Sanity.
|
| + }
|
| + var actualLayout = new List<String>();
|
| + while (start < end) {
|
| + actualLayout.add(
|
| + tmpLog[start++].substring(BODY_LINE_PREFIX.length));
|
| + }
|
| + var testName = checkTest(testfile, line, layouts,
|
| + actualLayout, stdout);
|
| + if (testName == null) {
|
| + code = -1;
|
| + } else if (config.generateRenders) {
|
| + sbuf.add(testName);
|
| + sbuf.add('\n');
|
| + for (var renderLine in actualLayout) {
|
| + sbuf.add(renderLine);
|
| + sbuf.add('\n');
|
| + }
|
| + }
|
| + }
|
| + i = j;
|
| + } else {
|
| + i++;
|
| + }
|
| + }
|
| + if (config.generateRenders) {
|
| + createFile(layoutFile, sbuf.toString());
|
| + }
|
| + exitHandler(code);
|
| + });
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Verify whether a test passed - it must pass the code expectations,
|
| + * and have validated layout. Report success or failure in a test
|
| + * result message. Upon success the render section name is returned
|
| + * (useful for `config.generateRenders`); otherwise null is returned.
|
| + */
|
| + String checkTest(Path testfile, String label, List layouts,
|
| + List actual, List out) {
|
| + var testGroup = null;
|
| + var testName = null;
|
| +
|
| + // The label line has the form:
|
| + // "result:duration:<test>//message"
|
| + // where <test> is a test name or series of one or more group names
|
| + // followed by a test name, separated by ###.
|
| +
|
| + // First extract test state, duration, name and message. If the test
|
| + // passed we can ignore these and continue to layout verification, but
|
| + // if the test failed we want to know that so we can report failure.
|
| + //
|
| + // TODO(gram) - currently we lose the stack trace although the user
|
| + // will get it in the overall output if they used --verbose. We may
|
| + // want to fix this properly at some point.
|
| + var labelParser = const RegExp('\"\([a-z]*\):\([0-9]*\):\(.*\)//\(.*\)\"');
|
| + Match match = labelParser.firstMatch(label);
|
| + var result = match.group(1);
|
| + var duration = parseDouble(match.group(2)) / 1000;
|
| + var test = match.group(3);
|
| + var message = match.group(4);
|
| +
|
| + // Split name up with group.
|
| + var idx = test.lastIndexOf('###');
|
| + if (idx >= 0) {
|
| + testGroup = test.substring(0, idx).replaceAll('###', ' ');
|
| + testName = test.substring(idx+3);
|
| + } else {
|
| + testGroup = '';
|
| + testName = test;
|
| + }
|
| + var section = '[${_pad(testGroup)}$testName]';
|
| +
|
| + if (config.generateRenders) {
|
| + // Do nothing; fake a pass.
|
| + out.add(_formatMessage(config.passFormat,
|
| + testfile, testGroup, testName, duration, ''));
|
| + } else if (result != 'pass') {
|
| + // The test failed separately from layout; just report that
|
| + // failure.
|
| + out.add(_formatMessage(
|
| + (result == 'fail' ? config.failFormat : config.errorFormat),
|
| + testfile, testGroup, testName, duration, message));
|
| + return null;
|
| + } else {
|
| + // The test passed, at least the expectations. So check the layout.
|
| + var expected = _getLayout(layouts, section);
|
| + var failMessage = null;
|
| + var lineNum = 0;
|
| + if (expected != null) {
|
| + while (lineNum < expected.length) {
|
| + if (lineNum >= actual.length) {
|
| + failMessage = 'expected "${expected[lineNum]}" but got nothing';
|
| + break;
|
| + } else {
|
| + if (expected[lineNum] != actual[lineNum]) {
|
| + failMessage = 'expected "${expected[lineNum]}" '
|
| + 'but got "${actual[lineNum]}"';
|
| + break;
|
| + }
|
| + }
|
| + lineNum++;
|
| + }
|
| + if (failMessage == null && lineNum < actual.length) {
|
| + failMessage = 'expected nothing but got "${actual[lineNum]}"';
|
| + }
|
| + }
|
| + if (failMessage != null) {
|
| + out.add(_formatMessage(config.failFormat,
|
| + testfile, testGroup, testName, duration,
|
| + 'Layout content mismatch at line $lineNum: '
|
| + '$failMessage'));
|
| + return null;
|
| + } else {
|
| + out.add(_formatMessage(config.passFormat,
|
| + testfile, testGroup, testName, duration, ''));
|
| + }
|
| + }
|
| + return section;
|
| + }
|
| +
|
| + /** Get the expected layout for a test. */
|
| + List _getLayout(List layouts, String section) {
|
| + List layout = new List();
|
| + for (var i = 0; i < layouts.length; i++) {
|
| + if (layouts[i] == section) {
|
| + ++i;
|
| + while (i < layouts.length && !layouts[i].startsWith('[')) {
|
| + layout.add(layouts[i++]);
|
| + }
|
| + break;
|
| + }
|
| + }
|
| + return layout;
|
| + }
|
| +
|
| + /** Pad a string with a rightmost space unless it is empty. */
|
| + static String _pad(s) => (s.length > 0) ? '$s ' : s;
|
| +
|
| + /** Format a test result message. */
|
| + String _formatMessage(String format,
|
| + Path testfile, String testGroup, String testName,
|
| + double duration, String message) {
|
| + String fname = makePathAbsolute(testfile.directoryPath.toString());
|
| + return "###${format.
|
| + replaceAll(Macros.testTime, '${duration.toStringAsFixed(3)}s ').
|
| + replaceAll(Macros.testfile, _pad(fname)).
|
| + replaceAll(Macros.testGroup, _pad(testGroup)).
|
| + replaceAll(Macros.testDescription, _pad(testName)).
|
| + replaceAll(Macros.testMessage, _pad(message))}";
|
| + }
|
| }
|
|
|