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 /** | 5 /** |
6 * Classes and methods for executing tests. | 6 * Classes and methods for executing tests. |
7 * | 7 * |
8 * This module includes: | 8 * This module includes: |
9 * - Managing parallel execution of tests, including timeout checks. | 9 * - Managing parallel execution of tests, including timeout checks. |
10 * - Evaluating the output of each test as pass/fail/crash/timeout. | 10 * - Evaluating the output of each test as pass/fail/crash/timeout. |
(...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
379 RunningProcess(this.command, this.timeout, {this.configuration}); | 379 RunningProcess(this.command, this.timeout, {this.configuration}); |
380 | 380 |
381 Future<CommandOutput> run() { | 381 Future<CommandOutput> run() { |
382 completer = new Completer<CommandOutput>(); | 382 completer = new Completer<CommandOutput>(); |
383 startTime = new DateTime.now(); | 383 startTime = new DateTime.now(); |
384 _runCommand(); | 384 _runCommand(); |
385 return completer.future; | 385 return completer.future; |
386 } | 386 } |
387 | 387 |
388 void _runCommand() { | 388 void _runCommand() { |
389 command.outputIsUpToDate.then((bool isUpToDate) { | 389 if (command.outputIsUpToDate) { |
390 if (isUpToDate) { | 390 compilationSkipped = true; |
391 compilationSkipped = true; | 391 _commandComplete(0); |
392 _commandComplete(0); | 392 } else { |
393 } else { | 393 var processEnvironment = _createProcessEnvironment(); |
394 var processEnvironment = _createProcessEnvironment(); | 394 var args = command.arguments; |
395 var args = command.arguments; | 395 Future processFuture = io.Process.start(command.executable, args, |
396 Future processFuture = io.Process.start(command.executable, args, | 396 environment: processEnvironment, |
397 environment: processEnvironment, | 397 workingDirectory: command.workingDirectory); |
398 workingDirectory: command.workingDirectory); | 398 processFuture.then((io.Process process) { |
399 processFuture.then((io.Process process) { | 399 StreamSubscription stdoutSubscription = |
400 StreamSubscription stdoutSubscription = | 400 _drainStream(process.stdout, stdout); |
401 _drainStream(process.stdout, stdout); | 401 StreamSubscription stderrSubscription = |
402 StreamSubscription stderrSubscription = | 402 _drainStream(process.stderr, stderr); |
403 _drainStream(process.stderr, stderr); | |
404 | 403 |
405 var stdoutCompleter = new Completer<Null>(); | 404 var stdoutCompleter = new Completer<Null>(); |
406 var stderrCompleter = new Completer<Null>(); | 405 var stderrCompleter = new Completer<Null>(); |
407 | 406 |
408 bool stdoutDone = false; | 407 bool stdoutDone = false; |
409 bool stderrDone = false; | 408 bool stderrDone = false; |
410 pid = process.pid; | 409 pid = process.pid; |
411 | 410 |
412 // This timer is used to close stdio to the subprocess once we got | 411 // This timer is used to close stdio to the subprocess once we got |
413 // the exitCode. Sometimes descendants of the subprocess keep stdio | 412 // the exitCode. Sometimes descendants of the subprocess keep stdio |
414 // handles alive even though the direct subprocess is dead. | 413 // handles alive even though the direct subprocess is dead. |
415 Timer watchdogTimer; | 414 Timer watchdogTimer; |
416 | 415 |
417 closeStdout([_]) { | 416 closeStdout([_]) { |
418 if (!stdoutDone) { | 417 if (!stdoutDone) { |
419 stdoutCompleter.complete(); | 418 stdoutCompleter.complete(); |
420 stdoutDone = true; | 419 stdoutDone = true; |
421 if (stderrDone && watchdogTimer != null) { | 420 if (stderrDone && watchdogTimer != null) { |
422 watchdogTimer.cancel(); | 421 watchdogTimer.cancel(); |
| 422 } |
| 423 } |
| 424 } |
| 425 |
| 426 closeStderr([_]) { |
| 427 if (!stderrDone) { |
| 428 stderrCompleter.complete(); |
| 429 stderrDone = true; |
| 430 |
| 431 if (stdoutDone && watchdogTimer != null) { |
| 432 watchdogTimer.cancel(); |
| 433 } |
| 434 } |
| 435 } |
| 436 |
| 437 // Close stdin so that tests that try to block on input will fail. |
| 438 process.stdin.close(); |
| 439 timeoutHandler() async { |
| 440 timedOut = true; |
| 441 if (process != null) { |
| 442 String executable; |
| 443 if (io.Platform.isLinux) { |
| 444 executable = 'eu-stack'; |
| 445 } else if (io.Platform.isMacOS) { |
| 446 // Try to print stack traces of the timed out process. |
| 447 // `sample` is a sampling profiler but we ask it sample for 1 |
| 448 // second with a 4 second delay between samples so that we only |
| 449 // sample the threads once. |
| 450 executable = '/usr/bin/sample'; |
| 451 } else if (io.Platform.isWindows) { |
| 452 var isX64 = command.executable.contains("X64") || |
| 453 command.executable.contains("SIMARM64"); |
| 454 if (configuration.windowsSdkPath != null) { |
| 455 executable = configuration.windowsSdkPath + |
| 456 "\\Debuggers\\${isX64 ? 'x64' : 'x86'}\\cdb.exe"; |
| 457 diagnostics.add("Using $executable to print stack traces"); |
| 458 } else { |
| 459 diagnostics.add("win_sdk_path not found"); |
| 460 } |
| 461 } else { |
| 462 diagnostics.add("Capturing stack traces on" |
| 463 "${io.Platform.operatingSystem} not supported"); |
| 464 } |
| 465 if (executable != null) { |
| 466 var pids = await _getPidList(process.pid, diagnostics); |
| 467 diagnostics.add("Process list including children: $pids"); |
| 468 for (pid in pids) { |
| 469 List<String> arguments; |
| 470 if (io.Platform.isLinux) { |
| 471 arguments = ['-p $pid']; |
| 472 } else if (io.Platform.isMacOS) { |
| 473 arguments = ['$pid', '1', '4000', '-mayDie']; |
| 474 } else if (io.Platform.isWindows) { |
| 475 arguments = ['-p', '$pid', '-c', '!uniqstack;qd']; |
| 476 } else { |
| 477 assert(false); |
| 478 } |
| 479 diagnostics.add("Trying to capture stack trace for pid $pid"); |
| 480 try { |
| 481 var result = await io.Process.run(executable, arguments); |
| 482 diagnostics.addAll((result.stdout as String).split('\n')); |
| 483 diagnostics.addAll((result.stderr as String).split('\n')); |
| 484 } catch (error) { |
| 485 diagnostics.add("Unable to capture stack traces: $error"); |
| 486 } |
423 } | 487 } |
424 } | 488 } |
| 489 |
| 490 if (!process.kill()) { |
| 491 diagnostics.add("Unable to kill ${process.pid}"); |
| 492 } |
| 493 } |
| 494 } |
| 495 |
| 496 stdoutSubscription.asFuture().then(closeStdout); |
| 497 stderrSubscription.asFuture().then(closeStderr); |
| 498 |
| 499 process.exitCode.then((exitCode) { |
| 500 if (!stdoutDone || !stderrDone) { |
| 501 watchdogTimer = new Timer(MAX_STDIO_DELAY, () { |
| 502 DebugLogger.warning( |
| 503 "$MAX_STDIO_DELAY_PASSED_MESSAGE (command: $command)"); |
| 504 watchdogTimer = null; |
| 505 stdoutSubscription.cancel(); |
| 506 stderrSubscription.cancel(); |
| 507 closeStdout(); |
| 508 closeStderr(); |
| 509 }); |
425 } | 510 } |
426 | 511 |
427 closeStderr([_]) { | 512 Future |
428 if (!stderrDone) { | 513 .wait([stdoutCompleter.future, stderrCompleter.future]).then((_) { |
429 stderrCompleter.complete(); | 514 _commandComplete(exitCode); |
430 stderrDone = true; | 515 }); |
| 516 }); |
431 | 517 |
432 if (stdoutDone && watchdogTimer != null) { | 518 timeoutTimer = |
433 watchdogTimer.cancel(); | 519 new Timer(new Duration(seconds: timeout), timeoutHandler); |
434 } | 520 }).catchError((e) { |
435 } | 521 // TODO(floitsch): should we try to report the stacktrace? |
436 } | 522 print("Process error:"); |
437 | 523 print(" Command: $command"); |
438 // Close stdin so that tests that try to block on input will fail. | 524 print(" Error: $e"); |
439 process.stdin.close(); | 525 _commandComplete(-1); |
440 timeoutHandler() async { | 526 return true; |
441 timedOut = true; | 527 }); |
442 if (process != null) { | 528 } |
443 String executable; | |
444 if (io.Platform.isLinux) { | |
445 executable = 'eu-stack'; | |
446 } else if (io.Platform.isMacOS) { | |
447 // Try to print stack traces of the timed out process. | |
448 // `sample` is a sampling profiler but we ask it sample for 1 | |
449 // second with a 4 second delay between samples so that we only | |
450 // sample the threads once. | |
451 executable = '/usr/bin/sample'; | |
452 } else if (io.Platform.isWindows) { | |
453 var isX64 = command.executable.contains("X64") || | |
454 command.executable.contains("SIMARM64"); | |
455 if (configuration.windowsSdkPath != null) { | |
456 executable = configuration.windowsSdkPath + | |
457 "\\Debuggers\\${isX64 ? 'x64' : 'x86'}\\cdb.exe"; | |
458 diagnostics.add("Using $executable to print stack traces"); | |
459 } else { | |
460 diagnostics.add("win_sdk_path not found"); | |
461 } | |
462 } else { | |
463 diagnostics.add("Capturing stack traces on" | |
464 "${io.Platform.operatingSystem} not supported"); | |
465 } | |
466 if (executable != null) { | |
467 var pids = await _getPidList(process.pid, diagnostics); | |
468 diagnostics.add("Process list including children: $pids"); | |
469 for (pid in pids) { | |
470 List<String> arguments; | |
471 if (io.Platform.isLinux) { | |
472 arguments = ['-p $pid']; | |
473 } else if (io.Platform.isMacOS) { | |
474 arguments = ['$pid', '1', '4000', '-mayDie']; | |
475 } else if (io.Platform.isWindows) { | |
476 arguments = ['-p', '$pid', '-c', '!uniqstack;qd']; | |
477 } else { | |
478 assert(false); | |
479 } | |
480 diagnostics.add("Trying to capture stack trace for pid $pid"); | |
481 try { | |
482 var result = await io.Process.run(executable, arguments); | |
483 diagnostics.addAll((result.stdout as String).split('\n')); | |
484 diagnostics.addAll((result.stderr as String).split('\n')); | |
485 } catch (error) { | |
486 diagnostics.add("Unable to capture stack traces: $error"); | |
487 } | |
488 } | |
489 } | |
490 | |
491 if (!process.kill()) { | |
492 diagnostics.add("Unable to kill ${process.pid}"); | |
493 } | |
494 } | |
495 } | |
496 | |
497 stdoutSubscription.asFuture().then(closeStdout); | |
498 stderrSubscription.asFuture().then(closeStderr); | |
499 | |
500 process.exitCode.then((exitCode) { | |
501 if (!stdoutDone || !stderrDone) { | |
502 watchdogTimer = new Timer(MAX_STDIO_DELAY, () { | |
503 DebugLogger.warning( | |
504 "$MAX_STDIO_DELAY_PASSED_MESSAGE (command: $command)"); | |
505 watchdogTimer = null; | |
506 stdoutSubscription.cancel(); | |
507 stderrSubscription.cancel(); | |
508 closeStdout(); | |
509 closeStderr(); | |
510 }); | |
511 } | |
512 | |
513 Future.wait([stdoutCompleter.future, stderrCompleter.future]).then( | |
514 (_) { | |
515 _commandComplete(exitCode); | |
516 }); | |
517 }); | |
518 | |
519 timeoutTimer = | |
520 new Timer(new Duration(seconds: timeout), timeoutHandler); | |
521 }).catchError((e) { | |
522 // TODO(floitsch): should we try to report the stacktrace? | |
523 print("Process error:"); | |
524 print(" Command: $command"); | |
525 print(" Error: $e"); | |
526 _commandComplete(-1); | |
527 return true; | |
528 }); | |
529 } | |
530 }); | |
531 } | 529 } |
532 | 530 |
533 void _commandComplete(int exitCode) { | 531 void _commandComplete(int exitCode) { |
534 if (timeoutTimer != null) { | 532 if (timeoutTimer != null) { |
535 timeoutTimer.cancel(); | 533 timeoutTimer.cancel(); |
536 } | 534 } |
537 var commandOutput = _createCommandOutput(command, exitCode); | 535 var commandOutput = _createCommandOutput(command, exitCode); |
538 completer.complete(commandOutput); | 536 completer.complete(commandOutput); |
539 } | 537 } |
540 | 538 |
(...skipping 1124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1665 } | 1663 } |
1666 } | 1664 } |
1667 | 1665 |
1668 void eventAllTestsDone() { | 1666 void eventAllTestsDone() { |
1669 for (var listener in _eventListener) { | 1667 for (var listener in _eventListener) { |
1670 listener.allDone(); | 1668 listener.allDone(); |
1671 } | 1669 } |
1672 _allDone(); | 1670 _allDone(); |
1673 } | 1671 } |
1674 } | 1672 } |
OLD | NEW |