OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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 file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /** A pipeline task for running DumpRenderTree. */ | 5 /** A pipeline task for running DumpRenderTree. */ |
6 class DrtTask extends RunProcessTask { | 6 class DrtTask extends RunProcessTask { |
7 | 7 |
8 DrtTask(String htmlFileTemplate) { | 8 String _testFileTemplate; |
| 9 |
| 10 DrtTask(this._testFileTemplate, String htmlFileTemplate) { |
9 init(config.drtPath, ['--no-timeout', htmlFileTemplate], config.timeout); | 11 init(config.drtPath, ['--no-timeout', htmlFileTemplate], config.timeout); |
10 } | 12 } |
| 13 |
| 14 // In order to extract the relevant parts of the DRT render text |
| 15 // output we use a somewhat kludgy approach, but it should be robust. |
| 16 // DRT formats output with indentation, and we know that the test title |
| 17 // is on a line with 12 spaces indent followed by 'text run', while the |
| 18 // IFrame body elements are all indented at least 18 spaces. |
| 19 const TEST_LABEL_LINE_PREFIX = ' text run'; |
| 20 const BODY_LINE_PREFIX = ' '; |
| 21 |
| 22 void execute(Path testfile, List stdout, List stderr, bool logging, |
| 23 Function exitHandler) { |
| 24 |
| 25 var testname = expandMacros(_testFileTemplate, testfile); |
| 26 var isLayout = isLayoutRenderTest(testname) || config.generateRenders; |
| 27 |
| 28 if (!isLayout) { |
| 29 super.execute(testfile, stdout, stderr, logging, exitHandler); |
| 30 } else { |
| 31 var tmpLog = new List<String>(); |
| 32 super.execute(testfile, tmpLog, tmpLog, true, |
| 33 (code) { |
| 34 var layoutFile = layoutFileName(testname); |
| 35 var layouts = getFileContents(layoutFile, false); |
| 36 var i = 0; |
| 37 StringBuffer sbuf = null; |
| 38 if (config.generateRenders) { |
| 39 sbuf = new StringBuffer(); |
| 40 } |
| 41 while ( i < tmpLog.length) { |
| 42 var line = tmpLog[i]; |
| 43 if (logging) { |
| 44 stdout.add(line); |
| 45 } |
| 46 if (line.startsWith(TEST_LABEL_LINE_PREFIX)) { |
| 47 var j = i+1; |
| 48 var start = -1, end = -1; |
| 49 // Walk forward to the next label or end of log. |
| 50 while (j < tmpLog.length && |
| 51 !tmpLog[j].startsWith(TEST_LABEL_LINE_PREFIX)) { |
| 52 // Is this a body render line? |
| 53 if (tmpLog[j].startsWith(BODY_LINE_PREFIX)) { |
| 54 if (start < 0) { // Is it the first? |
| 55 start = j; |
| 56 } |
| 57 } else { // Not a render line. |
| 58 if (start >= 0 && end < 0) { |
| 59 // We were just in a set of render lines, so this |
| 60 // line is the first non-member. |
| 61 end = j; |
| 62 } |
| 63 } |
| 64 j++; |
| 65 } |
| 66 if (start >= 0) { // We have some render lines. |
| 67 if (end < 0) { |
| 68 end = tmpLog.length; // Sanity. |
| 69 } |
| 70 var actualLayout = new List<String>(); |
| 71 while (start < end) { |
| 72 actualLayout.add( |
| 73 tmpLog[start++].substring(BODY_LINE_PREFIX.length)); |
| 74 } |
| 75 var testName = checkTest(testfile, line, layouts, |
| 76 actualLayout, stdout); |
| 77 if (testName == null) { |
| 78 code = -1; |
| 79 } else if (config.generateRenders) { |
| 80 sbuf.add(testName); |
| 81 sbuf.add('\n'); |
| 82 for (var renderLine in actualLayout) { |
| 83 sbuf.add(renderLine); |
| 84 sbuf.add('\n'); |
| 85 } |
| 86 } |
| 87 } |
| 88 i = j; |
| 89 } else { |
| 90 i++; |
| 91 } |
| 92 } |
| 93 if (config.generateRenders) { |
| 94 createFile(layoutFile, sbuf.toString()); |
| 95 } |
| 96 exitHandler(code); |
| 97 }); |
| 98 } |
| 99 } |
| 100 |
| 101 /** |
| 102 * Verify whether a test passed - it must pass the code expectations, |
| 103 * and have validated layout. Report success or failure in a test |
| 104 * result message. Upon success the render section name is returned |
| 105 * (useful for `config.generateRenders`); otherwise null is returned. |
| 106 */ |
| 107 String checkTest(Path testfile, String label, List layouts, |
| 108 List actual, List out) { |
| 109 var testGroup = null; |
| 110 var testName = null; |
| 111 |
| 112 // The label line has the form: |
| 113 // "result:duration:<test>//message" |
| 114 // where <test> is a test name or series of one or more group names |
| 115 // followed by a test name, separated by ###. |
| 116 |
| 117 // First extract test state, duration, name and message. If the test |
| 118 // passed we can ignore these and continue to layout verification, but |
| 119 // if the test failed we want to know that so we can report failure. |
| 120 // |
| 121 // TODO(gram) - currently we lose the stack trace although the user |
| 122 // will get it in the overall output if they used --verbose. We may |
| 123 // want to fix this properly at some point. |
| 124 var labelParser = const RegExp('\"\([a-z]*\):\([0-9]*\):\(.*\)//\(.*\)\"'); |
| 125 Match match = labelParser.firstMatch(label); |
| 126 var result = match.group(1); |
| 127 var duration = parseDouble(match.group(2)) / 1000; |
| 128 var test = match.group(3); |
| 129 var message = match.group(4); |
| 130 |
| 131 // Split name up with group. |
| 132 var idx = test.lastIndexOf('###'); |
| 133 if (idx >= 0) { |
| 134 testGroup = test.substring(0, idx).replaceAll('###', ' '); |
| 135 testName = test.substring(idx+3); |
| 136 } else { |
| 137 testGroup = ''; |
| 138 testName = test; |
| 139 } |
| 140 var section = '[${_pad(testGroup)}$testName]'; |
| 141 |
| 142 if (config.generateRenders) { |
| 143 // Do nothing; fake a pass. |
| 144 out.add(_formatMessage(config.passFormat, |
| 145 testfile, testGroup, testName, duration, '')); |
| 146 } else if (result != 'pass') { |
| 147 // The test failed separately from layout; just report that |
| 148 // failure. |
| 149 out.add(_formatMessage( |
| 150 (result == 'fail' ? config.failFormat : config.errorFormat), |
| 151 testfile, testGroup, testName, duration, message)); |
| 152 return null; |
| 153 } else { |
| 154 // The test passed, at least the expectations. So check the layout. |
| 155 var expected = _getLayout(layouts, section); |
| 156 var failMessage = null; |
| 157 var lineNum = 0; |
| 158 if (expected != null) { |
| 159 while (lineNum < expected.length) { |
| 160 if (lineNum >= actual.length) { |
| 161 failMessage = 'expected "${expected[lineNum]}" but got nothing'; |
| 162 break; |
| 163 } else { |
| 164 if (expected[lineNum] != actual[lineNum]) { |
| 165 failMessage = 'expected "${expected[lineNum]}" ' |
| 166 'but got "${actual[lineNum]}"'; |
| 167 break; |
| 168 } |
| 169 } |
| 170 lineNum++; |
| 171 } |
| 172 if (failMessage == null && lineNum < actual.length) { |
| 173 failMessage = 'expected nothing but got "${actual[lineNum]}"'; |
| 174 } |
| 175 } |
| 176 if (failMessage != null) { |
| 177 out.add(_formatMessage(config.failFormat, |
| 178 testfile, testGroup, testName, duration, |
| 179 'Layout content mismatch at line $lineNum: ' |
| 180 '$failMessage')); |
| 181 return null; |
| 182 } else { |
| 183 out.add(_formatMessage(config.passFormat, |
| 184 testfile, testGroup, testName, duration, '')); |
| 185 } |
| 186 } |
| 187 return section; |
| 188 } |
| 189 |
| 190 /** Get the expected layout for a test. */ |
| 191 List _getLayout(List layouts, String section) { |
| 192 List layout = new List(); |
| 193 for (var i = 0; i < layouts.length; i++) { |
| 194 if (layouts[i] == section) { |
| 195 ++i; |
| 196 while (i < layouts.length && !layouts[i].startsWith('[')) { |
| 197 layout.add(layouts[i++]); |
| 198 } |
| 199 break; |
| 200 } |
| 201 } |
| 202 return layout; |
| 203 } |
| 204 |
| 205 /** Pad a string with a rightmost space unless it is empty. */ |
| 206 static String _pad(s) => (s.length > 0) ? '$s ' : s; |
| 207 |
| 208 /** Format a test result message. */ |
| 209 String _formatMessage(String format, |
| 210 Path testfile, String testGroup, String testName, |
| 211 double duration, String message) { |
| 212 String fname = makePathAbsolute(testfile.directoryPath.toString()); |
| 213 return "###${format. |
| 214 replaceAll(Macros.testTime, '${duration.toStringAsFixed(3)}s '). |
| 215 replaceAll(Macros.testfile, _pad(fname)). |
| 216 replaceAll(Macros.testGroup, _pad(testGroup)). |
| 217 replaceAll(Macros.testDescription, _pad(testName)). |
| 218 replaceAll(Macros.testMessage, _pad(message))}"; |
| 219 } |
11 } | 220 } |
OLD | NEW |