diff --git a/bin/phpunit-zm b/bin/phpunit-zm index b6e68c7b..feaec63b 100755 --- a/bin/phpunit-zm +++ b/bin/phpunit-zm @@ -8,6 +8,7 @@ const ZM_TEST_LOG_DEBUG = false; use OneBot\Driver\Event\Process\WorkerStartEvent; use PHPUnit\TextUI\Command; use Swoole\Atomic; +use Tests\ZMResultPrinter; use ZM\Command\Server\ServerStartCommand; use ZM\Event\EventProvider; use ZM\Framework; @@ -37,11 +38,10 @@ ob_event_provider()->addEventListener(WorkerStartEvent::getName(), function () { if (($error['type'] ?? 0) != 0) { logger()->emergency(zm_internal_errcode('E00027') . 'Internal fatal error: ' . $error['message'] . ' at ' . $error['file'] . "({$error['line']})"); } - ob_dump($error); - ob_dump(func_get_args()); - ob_dump(debug_backtrace()); Framework::getInstance()->stop(); }); + $_SERVER['argv'][] = '--printer'; + $_SERVER['argv'][] = ZMResultPrinter::class; try { // 不退出,而是返回 code $retcode = Command::main(false); diff --git a/tests/ZMResultPrinter.php b/tests/ZMResultPrinter.php new file mode 100644 index 00000000..fa1d6ce7 --- /dev/null +++ b/tests/ZMResultPrinter.php @@ -0,0 +1,204 @@ + '│', + 'start' => '│', + 'message' => '│', + 'diff' => '│', + 'trace' => '│', + 'last' => '│', + ]; + + /** + * Colored Testdox use box-drawing for a more textured map of the message. + */ + private const PREFIX_DECORATED = [ + 'default' => '│', + 'start' => '┐', + 'message' => '→', + 'diff' => '┊', + 'trace' => '╵', + 'last' => '┴', + ]; + + private const SPINNER_ICONS = [ + " \e[36m◐\e[0m running tests", + " \e[36m◓\e[0m running tests", + " \e[36m◑\e[0m running tests", + " \e[36m◒\e[0m running tests", + ]; + + private const STATUS_STYLES = [ + BaseTestRunner::STATUS_PASSED => [ + 'symbol' => '✓', + 'color' => 'fg-green', + ], + BaseTestRunner::STATUS_ERROR => [ + 'symbol' => '✘', + 'color' => 'fg-yellow', + 'message' => 'bg-yellow,fg-black', + ], + BaseTestRunner::STATUS_FAILURE => [ + 'symbol' => '✘', + 'color' => 'fg-red', + 'message' => 'fg-red', + ], + BaseTestRunner::STATUS_SKIPPED => [ + 'symbol' => '↩', + 'color' => 'fg-cyan', + 'message' => 'fg-cyan', + ], + BaseTestRunner::STATUS_RISKY => [ + 'symbol' => '☢', + 'color' => 'fg-yellow', + 'message' => 'fg-yellow', + ], + BaseTestRunner::STATUS_INCOMPLETE => [ + 'symbol' => '∅', + 'color' => 'fg-yellow', + 'message' => 'fg-yellow', + ], + BaseTestRunner::STATUS_WARNING => [ + 'symbol' => '⚠', + 'color' => 'fg-yellow', + 'message' => 'fg-yellow', + ], + BaseTestRunner::STATUS_UNKNOWN => [ + 'symbol' => '?', + 'color' => 'fg-blue', + 'message' => 'fg-white,bg-blue', + ], + ]; + + public function printResult(TestResult $result): void + { + $this->writeNewLine(); + $this->write($this->colorizeTextBox('fg-white,bold', 'Tests: ')); + $counts = [ + BaseTestRunner::STATUS_FAILURE => ['failed', $result->failureCount()], + BaseTestRunner::STATUS_ERROR => ['errors', $result->errorCount()], + BaseTestRunner::STATUS_SKIPPED => ['skipped', $result->skippedCount()], + BaseTestRunner::STATUS_RISKY => ['risky', $result->riskyCount()], + BaseTestRunner::STATUS_INCOMPLETE => ['incomplete', $result->notImplementedCount()], + BaseTestRunner::STATUS_WARNING => ['warnings', $result->warningCount()], + BaseTestRunner::STATUS_PASSED => ['passed', count($result->passed())], + ]; + $counters = []; + foreach ($counts as $status => $count) { + if ($count[1] > 0) { + $counters[] = $this->colorizeTextBox(self::STATUS_STYLES[$status]['color'], "{$count[1]} {$count[0]}"); + } + } + $this->writeWithColor('fg-white,bold', implode(', ', $counters)); + + $this->write($this->colorizeTextBox('fg-white,bold', 'Time: ')); + $this->writeWithColor('fg-default', sprintf('%fs', $result->time())); + + $this->printFooter($result); + } + + protected function printFooter(TestResult $result): void + { + if ($result->failureCount() === 0) { + $color = 'bg-green,fg-white,bold'; + $text = '[OK] No errors'; + } else { + $color = 'bg-red,fg-white,bold'; + $text = '[ERROR] Found ' . $result->failureCount() . ' failures'; + } + $this->writeWithColor($color, "\n {$text} "); + } + + protected function writeTestResult(array $prevResult, array $result): void + { + // spacer line for new suite headers and after verbose messages + if ($prevResult['testName'] !== '' + && (!empty($prevResult['message']) || $prevResult['className'] !== $result['className'])) { + $this->write(PHP_EOL); + } + + // suite header + if ($prevResult['className'] !== $result['className']) { + [$class_short, $class_long] = explode(' (', $result['className']); + $this->write($this->colorizeTextBox('fg-black,bg-cyan', " {$class_short} ")); + $this->write(' ' . rtrim($class_long, ')') . PHP_EOL); + } + + // test result line + if ($this->colors && $result['className'] === PhptTestCase::class) { + $testName = Color::colorizePath($result['testName'], $prevResult['testName'], true); + } else { + $testName = $result['testMethod']; + } + $testName = strtolower($testName); + + $style = self::STATUS_STYLES[$result['status']]; + $line = sprintf( + ' %s %s', + $this->colorizeTextBox($style['color'], $style['symbol']), + $this->colorizeTextBox('dim', $testName), + ); + + $this->write($line); + + // additional information when verbose + $this->writeNewLine(); + if (!empty($result['message'])) { + $this->write(' ' . $result['message']); + } + } + + protected function formatTestResultMessage(Throwable $t, array $result, ?string $prefix = null): string + { + $message = $this->formatThrowable($t, $result['status']); + $diff = ''; + + if (!($this->verbose || $result['verbose'])) { + return ''; + } + + if ($message && $this->colors) { + $style = self::STATUS_STYLES[$result['status']]['message'] ?? ''; + [$message, $diff] = $this->colorizeMessageAndDiff($style, $message); + } + + if ($prefix === null || !$this->colors) { + $prefix = self::PREFIX_SIMPLE; + } + + if ($this->colors) { + $color = self::STATUS_STYLES[$result['status']]['color'] ?? ''; + $prefix = array_map(static function ($p) use ($color) { + return Color::colorize($color, $p); + }, self::PREFIX_DECORATED); + } + + $out = ''; + + if ($message) { + $out .= $this->prefixLines($prefix['message'], strtolower($message) . PHP_EOL) . PHP_EOL; + } + + if ($diff) { + $out .= $this->prefixLines($prefix['diff'], $diff . PHP_EOL) . PHP_EOL; + } + + return $out; + } +}