| 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 // The following set of variables should be set by the caller that | 5 // The following set of variables should be set by the caller that |
| 6 // #sources this file. | 6 // #sources this file. |
| 7 /** Whether to include elapsed time. */ | 7 /** Whether to include elapsed time. */ |
| 8 | 8 |
| 9 part of test_controller; | 9 part of test_controller; |
| 10 | 10 |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 117 } | 117 } |
| 118 } | 118 } |
| 119 | 119 |
| 120 complete() { | 120 complete() { |
| 121 if (summarize) { | 121 if (summarize) { |
| 122 printSummary(testfile, passCount, failCount, errorCount); | 122 printSummary(testfile, passCount, failCount, errorCount); |
| 123 } | 123 } |
| 124 notifyDone(failCount > 0 ? -1 : 0); | 124 notifyDone(failCount > 0 ? -1 : 0); |
| 125 } | 125 } |
| 126 | 126 |
| 127 /* |
| 128 * Run an external process [cmd] with command line arguments [args]. |
| 129 * [timeout] can be used to forcefully terminate the process after |
| 130 * some number of seconds. This is used by runCommand and startProcess. |
| 131 * If [procId] is non-zero (i.e. called from startProcess) then a reference |
| 132 * to the [Process] will be put in a map with key [procId]; in this case |
| 133 * the process can be terminated later by calling [stopProcess] and |
| 134 * passing in the [procId]. |
| 135 * [outputMonitor] is an optional function that will be called back with each |
| 136 * line of output from the process. |
| 137 * Returns a [Future] for when the process terminates. |
| 138 */ |
| 139 Future _processHelper(String command, List<String> args, |
| 140 List stdout, List stderr, |
| 141 int timeout, int procId, Function outputMonitor, bool raw) { |
| 142 var timer = null; |
| 143 return Process.start(command, args).then((process) { |
| 144 |
| 145 timer = new Timer(new Duration(seconds: timeout), () { |
| 146 timer = null; |
| 147 process.kill(); |
| 148 }); |
| 149 |
| 150 if (raw) { |
| 151 process.stdout.listen((c) { stdout.addAll(c); }); |
| 152 } else { |
| 153 _pipeStream(process.stdout, stdout, outputMonitor); |
| 154 } |
| 155 _pipeStream(process.stderr, stderr, outputMonitor); |
| 156 return process.exitCode; |
| 157 }).then((exitCode) { |
| 158 if (timer != null) { |
| 159 timer.cancel(); |
| 160 } |
| 161 return exitCode; |
| 162 }) |
| 163 .catchError((e) { |
| 164 stderr.add("#Error starting process $command: ${e.error}"); |
| 165 }); |
| 166 } |
| 167 |
| 168 void _pipeStream(Stream stream, List<String> destination, |
| 169 Function outputMonitor) { |
| 170 stream |
| 171 .transform(new StringDecoder()) |
| 172 .transform(new LineTransformer()) |
| 173 .listen((String line) { |
| 174 if (outputMonitor != null) { |
| 175 outputMonitor(line); |
| 176 } |
| 177 destination.add(line); |
| 178 }); |
| 179 } |
| 180 |
| 181 /** |
| 182 * Run an external process [cmd] with command line arguments [args]. |
| 183 * [timeout] can be used to forcefully terminate the process after |
| 184 * some number of seconds. |
| 185 * Returns a [Future] for when the process terminates. |
| 186 */ |
| 187 Future runCommand(String command, List<String> args, |
| 188 List stdout, List stderr, |
| 189 {int timeout: 300, Function outputMonitor, |
| 190 bool raw: false}) { |
| 191 return _processHelper(command, args, stdout, stderr, |
| 192 timeout, 0, outputMonitor, raw); |
| 193 } |
| 194 |
| 195 String parseLabel(String line) { |
| 196 if (line.startsWith('CONSOLE MESSAGE')) { |
| 197 var idx = line.indexOf('#TEST '); |
| 198 if (idx > 0) { |
| 199 return line.substring(idx + 6); |
| 200 } |
| 201 } |
| 202 return null; |
| 203 } |
| 204 |
| 127 runTextLayoutTest(testNum) { | 205 runTextLayoutTest(testNum) { |
| 128 var url = '$baseUrl?test=$testNum'; | 206 var url = '$baseUrl?test=$testNum'; |
| 129 var stdout = new List(); | 207 var stdout = new List(); |
| 208 var stderr = new List(); |
| 130 start = new DateTime.now(); | 209 start = new DateTime.now(); |
| 131 Process.start(drt, [url]).then((process) { | 210 runCommand(drt, [url], stdout, stderr).then((e) { |
| 132 // Drain stderr to not leak resources. | 211 if (stdout.length > 0 && stdout[stdout.length-1].startsWith('#EOF')) { |
| 133 process.stderr.onData = process.stderr.read; | 212 stdout.removeLast(); |
| 134 StringInputStream stdoutStringStream = | 213 } |
| 135 new StringInputStream(process.stdout); | 214 var done = false; |
| 136 stdoutStringStream.onLine = () { | 215 var i = 0; |
| 137 if (stdoutStringStream.closed) return; | 216 var label = null; |
| 138 var line = stdoutStringStream.readLine(); | 217 var contentMarker = 'layer at '; |
| 139 while (null != line) { | 218 while (i < stdout.length) { |
| 140 stdout.add(line); | 219 if (label == null && (label = parseLabel(stdout[i])) != null) { |
| 141 line = stdoutStringStream.readLine(); | 220 if (label == 'NONEXISTENT') { |
| 221 complete(); |
| 222 return; |
| 223 } |
| 224 } else if (stdout[i].startsWith(contentMarker)) { |
| 225 if (label == null) { |
| 226 complete(); |
| 227 return; |
| 228 } |
| 229 var expectedFileName = |
| 230 '$sourceDir${Platform.pathSeparator}' |
| 231 '${label.replaceAll("###", "_") |
| 232 .replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.txt'; |
| 233 var expected = new File(expectedFileName); |
| 234 if (regenerate) { |
| 235 var osink = expected.openWrite(); |
| 236 while (i < stdout.length) { |
| 237 osink.write(stdout[i]); |
| 238 osink.write('\n'); |
| 239 i++; |
| 240 } |
| 241 osink.close(); |
| 242 pass(start, label); |
| 243 } else if (!expected.existsSync()) { |
| 244 fail(start, label, 'No expectation file'); |
| 245 } else { |
| 246 var lines = expected.readAsLinesSync(); |
| 247 var actualLength = stdout.length - i; |
| 248 var compareCount = min(lines.length, actualLength); |
| 249 var match = true; |
| 250 for (var j = 0; j < compareCount; j++) { |
| 251 if (lines[j] != stdout[i + j]) { |
| 252 fail(start, label, 'Expectation differs at line ${j + 1}'); |
| 253 match = false; |
| 254 break; |
| 255 } |
| 256 } |
| 257 if (match) { |
| 258 if (lines.length != actualLength) { |
| 259 fail(start, label, 'Expectation file has wrong length'); |
| 260 } else { |
| 261 pass(start, label); |
| 262 } |
| 263 } |
| 264 } |
| 265 done = true; |
| 266 break; |
| 142 } | 267 } |
| 143 }; | 268 i++; |
| 144 process.onExit = (exitCode) { | 269 } |
| 145 process.close(); | 270 if (label != null) { |
| 146 if (stdout.length > 0 && stdout[stdout.length-1].startsWith('#EOF')) { | 271 if (!done) error(start, label, 'Failed to parse output'); |
| 147 stdout.removeLast(); | 272 runTextLayoutTest(testNum + 1); |
| 273 } |
| 274 }); |
| 275 } |
| 276 |
| 277 runPixelLayoutTest(int testNum) { |
| 278 var url = '$baseUrl?test=$testNum'; |
| 279 var stdout = new List(); |
| 280 var stderr = new List(); |
| 281 start = new DateTime.now(); |
| 282 runCommand(drt, ["$url'-p"], stdout, stderr, raw:true).then((exitCode) { |
| 283 var contentMarker = 'Content-Length: '; |
| 284 var eol = '\n'.codeUnitAt(0); |
| 285 var pos = 0; |
| 286 var label = null; |
| 287 var done = false; |
| 288 |
| 289 while(pos < stdout.length) { |
| 290 StringBuffer sb = new StringBuffer(); |
| 291 while (pos < stdout.length && stdout[pos] != eol) { |
| 292 sb.writeCharCode(stdout[pos++]); |
| 148 } | 293 } |
| 149 var done = false; | 294 if (++pos >= stdout.length && line == '') break; |
| 150 var i = 0; | 295 var line = sb.toString(); |
| 151 var label = null; | 296 |
| 152 var labelMarker = 'CONSOLE MESSAGE: #TEST '; | 297 if (label == null && (label = parseLabel(line)) != null) { |
| 153 var contentMarker = 'layer at '; | 298 if (label == 'NONEXISTENT') { |
| 154 while (i < stdout.length) { | 299 complete(); |
| 155 if (label == null && stdout[i].startsWith(labelMarker)) { | 300 } |
| 156 label = stdout[i].substring(labelMarker.length); | 301 } else if (line.startsWith(contentMarker)) { |
| 157 if (label == 'NONEXISTENT') { | 302 if (label == null) { |
| 158 complete(); | 303 complete(); |
| 159 } | 304 } |
| 160 } else if (stdout[i].startsWith(contentMarker)) { | 305 var len = int.parse(line.substring(contentMarker.length)); |
| 161 if (label == null) { | 306 var expectedFileName = |
| 162 complete(); | 307 '$sourceDir${Platform.pathSeparator}' |
| 163 } | 308 '${label.replaceAll("###","_"). |
| 164 var expectedFileName = | 309 replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.png'; |
| 165 '$sourceDir${Platform.pathSeparator}' | 310 var expected = new File(expectedFileName); |
| 166 '${label.replaceAll("###", "_") | 311 if (regenerate) { |
| 167 .replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.txt'; | 312 var osink = expected.openWrite(); |
| 168 var expected = new File(expectedFileName); | 313 stdout.removeRange(0, pos); |
| 169 if (regenerate) { | 314 stdout.length = len; |
| 170 var ostream = expected.openOutputStream(FileMode.WRITE); | 315 osink.add(stdout); |
| 171 while (i < stdout.length) { | 316 osink.close(); |
| 172 ostream.writeString(stdout[i]); | 317 pass(start, label); |
| 173 ostream.writeString('\n'); | 318 } else if (!expected.existsSync()) { |
| 174 i++; | 319 fail(start, label, 'No expectation file'); |
| 175 } | 320 } else { |
| 176 ostream.close(); | 321 var bytes = expected.readAsBytesSync(); |
| 177 pass(start, label); | 322 if (bytes.length != len) { |
| 178 } else if (!expected.existsSync()) { | 323 fail(start, label, 'Expectation file has wrong length'); |
| 179 fail(start, label, 'No expectation file'); | |
| 180 } else { | 324 } else { |
| 181 var lines = expected.readAsLinesSync(); | |
| 182 var actualLength = stdout.length - i; | |
| 183 var compareCount = min(lines.length, actualLength); | |
| 184 var match = true; | 325 var match = true; |
| 185 for (var j = 0; j < compareCount; j++) { | 326 for (var j = 0; j < len; j++) { |
| 186 if (lines[j] != stdout[i + j]) { | 327 if (bytes[j] != stdout[pos + j]) { |
| 187 fail(start, label, 'Expectation differs at line ${j + 1}'); | 328 fail(start, label, 'Expectation differs at byte ${j + 1}'); |
| 188 match = false; | 329 match = false; |
| 189 break; | 330 break; |
| 190 } | 331 } |
| 191 } | 332 } |
| 192 if (match) { | 333 if (match) pass(start, label); |
| 193 if (lines.length != actualLength) { | 334 } |
| 194 fail(start, label, 'Expectation file has wrong length'); | 335 } |
| 195 } else { | 336 done = true; |
| 196 pass(start, label); | 337 break; |
| 197 } | |
| 198 } | |
| 199 } | |
| 200 done = true; | |
| 201 break; | |
| 202 } | |
| 203 i++; | |
| 204 } | 338 } |
| 205 if (label != null) { | 339 } |
| 206 if (!done) error(start, label, 'Failed to parse output'); | 340 if (label != null) { |
| 207 runTextLayoutTest(testNum + 1); | 341 if (!done) error(start, label, 'Failed to parse output'); |
| 208 } | 342 runPixelLayoutTest(testNum + 1); |
| 209 }; | 343 } |
| 210 }); | 344 }); |
| 211 } | 345 } |
| 212 | 346 |
| 213 runPixelLayoutTest(int testNum) { | |
| 214 var url = '$baseUrl?test=$testNum'; | |
| 215 var stdout = new List(); | |
| 216 start = new DateTime.now(); | |
| 217 Process.start(drt, ["$url'-p"]).then((process) { | |
| 218 // Drain stderr to not leak resources. | |
| 219 process.stderr.onData = process.stderr.read; | |
| 220 ListInputStream stdoutStream = process.stdout; | |
| 221 stdoutStream.onData = () { | |
| 222 if (!stdoutStream.closed) { | |
| 223 var data = stdoutStream.read(); | |
| 224 stdout.addAll(data); | |
| 225 } | |
| 226 }; | |
| 227 stdoutStream.onError = (e) { | |
| 228 print(e); | |
| 229 }; | |
| 230 process.onExit = (exitCode) { | |
| 231 stdout.addAll(process.stdout.read()); | |
| 232 process.close(); | |
| 233 var labelMarker = 'CONSOLE MESSAGE: #TEST '; | |
| 234 var contentMarker = 'Content-Length: '; | |
| 235 var eol = '\n'.codeUnitAt(0); | |
| 236 var pos = -1; | |
| 237 var label = null; | |
| 238 var done = false; | |
| 239 | |
| 240 while(pos < stdout.length) { | |
| 241 var idx = stdout.indexOf(eol, ++pos); | |
| 242 if (idx < 0) break; | |
| 243 StringBuffer sb = new StringBuffer(); | |
| 244 for (var i = pos; i < idx; i++) { | |
| 245 sb.writeCharCode(stdout[i]); | |
| 246 } | |
| 247 var line = sb.toString(); | |
| 248 | |
| 249 if (label == null && line.startsWith(labelMarker)) { | |
| 250 label = line.substring(labelMarker.length); | |
| 251 if (label == 'NONEXISTENT') { | |
| 252 complete(); | |
| 253 } | |
| 254 } else if (line.startsWith(contentMarker)) { | |
| 255 if (label == null) { | |
| 256 complete(); | |
| 257 } | |
| 258 var len = int.parse(line.substring(contentMarker.length)); | |
| 259 pos = idx + 1; | |
| 260 var expectedFileName = | |
| 261 '$sourceDir${Platform.pathSeparator}' | |
| 262 '${label.replaceAll("###","_"). | |
| 263 replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.png'; | |
| 264 var expected = new File(expectedFileName); | |
| 265 if (regenerate) { | |
| 266 var ostream = expected.openOutputStream(FileMode.WRITE); | |
| 267 ostream.writeFrom(stdout, pos, len); | |
| 268 ostream.close(); | |
| 269 pass(start, label); | |
| 270 } else if (!expected.existsSync()) { | |
| 271 fail(start, label, 'No expectation file'); | |
| 272 } else { | |
| 273 var bytes = expected.readAsBytesSync(); | |
| 274 if (bytes.length != len) { | |
| 275 fail(start, label, 'Expectation file has wrong length'); | |
| 276 } else { | |
| 277 var match = true; | |
| 278 for (var j = 0; j < len; j++) { | |
| 279 if (bytes[j] != stdout[pos + j]) { | |
| 280 fail(start, label, 'Expectation differs at byte ${j + 1}'); | |
| 281 match = false; | |
| 282 break; | |
| 283 } | |
| 284 } | |
| 285 if (match) pass(start, label); | |
| 286 } | |
| 287 } | |
| 288 done = true; | |
| 289 break; | |
| 290 } | |
| 291 pos = idx; | |
| 292 } | |
| 293 if (label != null) { | |
| 294 if (!done) error(start, label, 'Failed to parse output'); | |
| 295 runPixelLayoutTest(testNum + 1); | |
| 296 } | |
| 297 }; | |
| 298 }); | |
| 299 } | |
| 300 | |
| 301 void init() { | 347 void init() { |
| 302 // Get the name of the directory that has the expectation files | 348 // Get the name of the directory that has the expectation files |
| 303 // (by stripping .dart suffix from test file path). | 349 // (by stripping .dart suffix from test file path). |
| 304 // Create it if it does not exist. | 350 // Create it if it does not exist. |
| 305 sourceDir = testfile.substring(0, testfile.length - 5); | 351 sourceDir = testfile.substring(0, testfile.length - 5); |
| 306 if (regenerate) { | 352 if (regenerate) { |
| 307 var d = new Directory(sourceDir); | 353 var d = new Directory(sourceDir); |
| 308 if (!d.existsSync()) { | 354 if (!d.existsSync()) { |
| 309 d.createSync(); | 355 d.createSync(); |
| 310 } | 356 } |
| 311 } | 357 } |
| 312 } | 358 } |
| 313 | 359 |
| 314 void runPixelLayoutTests() { | 360 void runPixelLayoutTests() { |
| 315 init(); | 361 init(); |
| 316 runPixelLayoutTest(0); | 362 runPixelLayoutTest(0); |
| 317 } | 363 } |
| 318 | 364 |
| 319 void runTextLayoutTests() { | 365 void runTextLayoutTests() { |
| 320 init(); | 366 init(); |
| 321 runTextLayoutTest(0); | 367 runTextLayoutTest(0); |
| 322 } | 368 } |
| OLD | NEW |