class ResultPrinter
Same name in this branch
- 11.1.x vendor/phpunit/phpunit/src/TextUI/Output/TestDox/ResultPrinter.php \PHPUnit\TextUI\Output\TestDox\ResultPrinter
@no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
@internal This class is not covered by the backward compatibility promise for PHPUnit
Hierarchy
- class \PHPUnit\TextUI\Output\Default\ResultPrinter
Expanded class hierarchy of ResultPrinter
1 file declares its use of ResultPrinter
- Facade.php in vendor/
phpunit/ phpunit/ src/ TextUI/ Output/ Facade.php
File
-
vendor/
phpunit/ phpunit/ src/ TextUI/ Output/ Default/ ResultPrinter.php, line 50
Namespace
PHPUnit\TextUI\Output\DefaultView source
final class ResultPrinter {
private readonly Printer $printer;
private readonly bool $displayPhpunitErrors;
private readonly bool $displayPhpunitWarnings;
private readonly bool $displayTestsWithErrors;
private readonly bool $displayTestsWithFailedAssertions;
private readonly bool $displayRiskyTests;
private readonly bool $displayPhpunitDeprecations;
private readonly bool $displayDetailsOnIncompleteTests;
private readonly bool $displayDetailsOnSkippedTests;
private readonly bool $displayDetailsOnTestsThatTriggerDeprecations;
private readonly bool $displayDetailsOnTestsThatTriggerErrors;
private readonly bool $displayDetailsOnTestsThatTriggerNotices;
private readonly bool $displayDetailsOnTestsThatTriggerWarnings;
private readonly bool $displayDefectsInReverseOrder;
private bool $listPrinted = false;
public function __construct(Printer $printer, bool $displayPhpunitErrors, bool $displayPhpunitWarnings, bool $displayPhpunitDeprecations, bool $displayTestsWithErrors, bool $displayTestsWithFailedAssertions, bool $displayRiskyTests, bool $displayDetailsOnIncompleteTests, bool $displayDetailsOnSkippedTests, bool $displayDetailsOnTestsThatTriggerDeprecations, bool $displayDetailsOnTestsThatTriggerErrors, bool $displayDetailsOnTestsThatTriggerNotices, bool $displayDetailsOnTestsThatTriggerWarnings, bool $displayDefectsInReverseOrder) {
$this->printer = $printer;
$this->displayPhpunitErrors = $displayPhpunitErrors;
$this->displayPhpunitWarnings = $displayPhpunitWarnings;
$this->displayPhpunitDeprecations = $displayPhpunitDeprecations;
$this->displayTestsWithErrors = $displayTestsWithErrors;
$this->displayTestsWithFailedAssertions = $displayTestsWithFailedAssertions;
$this->displayRiskyTests = $displayRiskyTests;
$this->displayDetailsOnIncompleteTests = $displayDetailsOnIncompleteTests;
$this->displayDetailsOnSkippedTests = $displayDetailsOnSkippedTests;
$this->displayDetailsOnTestsThatTriggerDeprecations = $displayDetailsOnTestsThatTriggerDeprecations;
$this->displayDetailsOnTestsThatTriggerErrors = $displayDetailsOnTestsThatTriggerErrors;
$this->displayDetailsOnTestsThatTriggerNotices = $displayDetailsOnTestsThatTriggerNotices;
$this->displayDetailsOnTestsThatTriggerWarnings = $displayDetailsOnTestsThatTriggerWarnings;
$this->displayDefectsInReverseOrder = $displayDefectsInReverseOrder;
}
public function print(TestResult $result) : void {
if ($this->displayPhpunitErrors) {
$this->printPhpunitErrors($result);
}
if ($this->displayPhpunitWarnings) {
$this->printTestRunnerWarnings($result);
}
if ($this->displayPhpunitDeprecations) {
$this->printTestRunnerDeprecations($result);
}
if ($this->displayTestsWithErrors) {
$this->printTestsWithErrors($result);
}
if ($this->displayTestsWithFailedAssertions) {
$this->printTestsWithFailedAssertions($result);
}
if ($this->displayPhpunitWarnings) {
$this->printDetailsOnTestsThatTriggeredPhpunitWarnings($result);
}
if ($this->displayPhpunitDeprecations) {
$this->printDetailsOnTestsThatTriggeredPhpunitDeprecations($result);
}
if ($this->displayRiskyTests) {
$this->printRiskyTests($result);
}
if ($this->displayDetailsOnIncompleteTests) {
$this->printIncompleteTests($result);
}
if ($this->displayDetailsOnSkippedTests) {
$this->printSkippedTestSuites($result);
$this->printSkippedTests($result);
}
if ($this->displayDetailsOnTestsThatTriggerErrors) {
$this->printIssueList('error', $result->errors());
}
if ($this->displayDetailsOnTestsThatTriggerWarnings) {
$this->printIssueList('PHP warning', $result->phpWarnings());
$this->printIssueList('warning', $result->warnings());
}
if ($this->displayDetailsOnTestsThatTriggerNotices) {
$this->printIssueList('PHP notice', $result->phpNotices());
$this->printIssueList('notice', $result->notices());
}
if ($this->displayDetailsOnTestsThatTriggerDeprecations) {
$this->printIssueList('PHP deprecation', $result->phpDeprecations());
$this->printIssueList('deprecation', $result->deprecations());
}
}
private function printPhpunitErrors(TestResult $result) : void {
if (!$result->hasTestTriggeredPhpunitErrorEvents()) {
return;
}
$elements = $this->mapTestsWithIssuesEventsToElements($result->testTriggeredPhpunitErrorEvents());
$this->printListHeaderWithNumber($elements['numberOfTestsWithIssues'], 'PHPUnit error');
$this->printList($elements['elements']);
}
private function printDetailsOnTestsThatTriggeredPhpunitDeprecations(TestResult $result) : void {
if (!$result->hasTestTriggeredPhpunitDeprecationEvents()) {
return;
}
$elements = $this->mapTestsWithIssuesEventsToElements($result->testTriggeredPhpunitDeprecationEvents());
$this->printListHeaderWithNumberOfTestsAndNumberOfIssues($elements['numberOfTestsWithIssues'], $elements['numberOfIssues'], 'PHPUnit deprecation');
$this->printList($elements['elements']);
}
private function printTestRunnerWarnings(TestResult $result) : void {
if (!$result->hasTestRunnerTriggeredWarningEvents()) {
return;
}
$elements = [];
foreach ($result->testRunnerTriggeredWarningEvents() as $event) {
$elements[] = [
'title' => $event->message(),
'body' => '',
];
}
$this->printListHeaderWithNumber(count($elements), 'PHPUnit test runner warning');
$this->printList($elements);
}
private function printTestRunnerDeprecations(TestResult $result) : void {
if (!$result->hasTestRunnerTriggeredDeprecationEvents()) {
return;
}
$elements = [];
foreach ($result->testRunnerTriggeredDeprecationEvents() as $event) {
$elements[] = [
'title' => $event->message(),
'body' => '',
];
}
$this->printListHeaderWithNumber(count($elements), 'PHPUnit test runner deprecation');
$this->printList($elements);
}
private function printDetailsOnTestsThatTriggeredPhpunitWarnings(TestResult $result) : void {
if (!$result->hasTestTriggeredPhpunitWarningEvents()) {
return;
}
$elements = $this->mapTestsWithIssuesEventsToElements($result->testTriggeredPhpunitWarningEvents());
$this->printListHeaderWithNumberOfTestsAndNumberOfIssues($elements['numberOfTestsWithIssues'], $elements['numberOfIssues'], 'PHPUnit warning');
$this->printList($elements['elements']);
}
private function printTestsWithErrors(TestResult $result) : void {
if (!$result->hasTestErroredEvents()) {
return;
}
$elements = [];
foreach ($result->testErroredEvents() as $event) {
if ($event instanceof BeforeFirstTestMethodErrored) {
$title = $event->testClassName();
}
else {
$title = $this->name($event->test());
}
$elements[] = [
'title' => $title,
'body' => $event->throwable()
->asString(),
];
}
$this->printListHeaderWithNumber(count($elements), 'error');
$this->printList($elements);
}
private function printTestsWithFailedAssertions(TestResult $result) : void {
if (!$result->hasTestFailedEvents()) {
return;
}
$elements = [];
foreach ($result->testFailedEvents() as $event) {
$body = $event->throwable()
->asString();
if (str_starts_with($body, 'AssertionError: ')) {
$body = substr($body, strlen('AssertionError: '));
}
$elements[] = [
'title' => $this->name($event->test()),
'body' => $body,
];
}
$this->printListHeaderWithNumber(count($elements), 'failure');
$this->printList($elements);
}
private function printRiskyTests(TestResult $result) : void {
if (!$result->hasTestConsideredRiskyEvents()) {
return;
}
$elements = $this->mapTestsWithIssuesEventsToElements($result->testConsideredRiskyEvents());
$this->printListHeaderWithNumber($elements['numberOfTestsWithIssues'], 'risky test');
$this->printList($elements['elements']);
}
private function printIncompleteTests(TestResult $result) : void {
if (!$result->hasTestMarkedIncompleteEvents()) {
return;
}
$elements = [];
foreach ($result->testMarkedIncompleteEvents() as $event) {
$elements[] = [
'title' => $this->name($event->test()),
'body' => $event->throwable()
->asString(),
];
}
$this->printListHeaderWithNumber(count($elements), 'incomplete test');
$this->printList($elements);
}
private function printSkippedTestSuites(TestResult $result) : void {
if (!$result->hasTestSuiteSkippedEvents()) {
return;
}
$elements = [];
foreach ($result->testSuiteSkippedEvents() as $event) {
$elements[] = [
'title' => $event->testSuite()
->name(),
'body' => $event->message(),
];
}
$this->printListHeaderWithNumber(count($elements), 'skipped test suite');
$this->printList($elements);
}
private function printSkippedTests(TestResult $result) : void {
if (!$result->hasTestSkippedEvents()) {
return;
}
$elements = [];
foreach ($result->testSkippedEvents() as $event) {
$elements[] = [
'title' => $this->name($event->test()),
'body' => $event->message(),
];
}
$this->printListHeaderWithNumber(count($elements), 'skipped test');
$this->printList($elements);
}
/**
* @psalm-param non-empty-string $type
* @psalm-param list<Issue> $issues
*/
private function printIssueList(string $type, array $issues) : void {
if (empty($issues)) {
return;
}
$numberOfUniqueIssues = count($issues);
$triggeringTests = [];
foreach ($issues as $issue) {
$triggeringTests = array_merge($triggeringTests, array_keys($issue->triggeringTests()));
}
$numberOfTests = count(array_unique($triggeringTests));
unset($triggeringTests);
$this->printListHeader(sprintf('%d test%s triggered %d %s%s:' . PHP_EOL . PHP_EOL, $numberOfTests, $numberOfTests !== 1 ? 's' : '', $numberOfUniqueIssues, $type, $numberOfUniqueIssues !== 1 ? 's' : ''));
$i = 1;
foreach ($issues as $issue) {
$title = sprintf('%s:%d', $issue->file(), $issue->line());
$body = trim($issue->description()) . PHP_EOL . PHP_EOL . 'Triggered by:';
$triggeringTests = $issue->triggeringTests();
ksort($triggeringTests);
foreach ($triggeringTests as $triggeringTest) {
$body .= PHP_EOL . PHP_EOL . '* ' . $triggeringTest['test']->id();
if ($triggeringTest['count'] > 1) {
$body .= sprintf(' (%d times)', $triggeringTest['count']);
}
if ($triggeringTest['test']->isTestMethod()) {
$body .= PHP_EOL . ' ' . $triggeringTest['test']->file() . ':' . $triggeringTest['test']->line();
}
}
$this->printIssueListElement($i++, $title, $body);
$this->printer
->print(PHP_EOL);
}
}
private function printListHeaderWithNumberOfTestsAndNumberOfIssues(int $numberOfTestsWithIssues, int $numberOfIssues, string $type) : void {
$this->printListHeader(sprintf("%d test%s triggered %d %s%s:\n\n", $numberOfTestsWithIssues, $numberOfTestsWithIssues !== 1 ? 's' : '', $numberOfIssues, $type, $numberOfIssues !== 1 ? 's' : ''));
}
private function printListHeaderWithNumber(int $number, string $type) : void {
$this->printListHeader(sprintf("There %s %d %s%s:\n\n", $number === 1 ? 'was' : 'were', $number, $type, $number === 1 ? '' : 's'));
}
private function printListHeader(string $header) : void {
if ($this->listPrinted) {
$this->printer
->print("--\n\n");
}
$this->listPrinted = true;
$this->printer
->print($header);
}
/**
* @psalm-param list<array{title: string, body: string}> $elements
*/
private function printList(array $elements) : void {
$i = 1;
if ($this->displayDefectsInReverseOrder) {
$elements = array_reverse($elements);
}
foreach ($elements as $element) {
$this->printListElement($i++, $element['title'], $element['body']);
}
$this->printer
->print("\n");
}
private function printListElement(int $number, string $title, string $body) : void {
$body = trim($body);
$this->printer
->print(sprintf("%s%d) %s\n%s%s", $number > 1 ? "\n" : '', $number, $title, $body, !empty($body) ? "\n" : ''));
}
private function printIssueListElement(int $number, string $title, string $body) : void {
$body = trim($body);
$this->printer
->print(sprintf("%d) %s\n%s%s", $number, $title, $body, !empty($body) ? "\n" : ''));
}
private function name(Test $test) : string {
if ($test->isTestMethod()) {
assert($test instanceof TestMethod);
if (!$test->testData()
->hasDataFromDataProvider()) {
return $test->nameWithClass();
}
return $test->className() . '::' . $test->methodName() . $test->testData()
->dataFromDataProvider()
->dataAsStringForResultOutput();
}
return $test->name();
}
/**
* @psalm-param array<string,list<ConsideredRisky|DeprecationTriggered|PhpDeprecationTriggered|PhpunitDeprecationTriggered|ErrorTriggered|NoticeTriggered|PhpNoticeTriggered|WarningTriggered|PhpWarningTriggered|PhpunitErrorTriggered|PhpunitWarningTriggered>> $events
*
* @psalm-return array{numberOfTestsWithIssues: int, numberOfIssues: int, elements: list<array{title: string, body: string}>}
*/
private function mapTestsWithIssuesEventsToElements(array $events) : array {
$elements = [];
$issues = 0;
foreach ($events as $reasons) {
$test = $reasons[0]->test();
$testLocation = $this->testLocation($test);
$title = $this->name($test);
$body = '';
$first = true;
$single = count($reasons) === 1;
foreach ($reasons as $reason) {
if ($first) {
$first = false;
}
else {
$body .= PHP_EOL;
}
$body .= $this->reasonMessage($reason, $single);
$body .= $this->reasonLocation($reason, $single);
$issues++;
}
if (!empty($testLocation)) {
$body .= $testLocation;
}
$elements[] = [
'title' => $title,
'body' => $body,
];
}
return [
'numberOfTestsWithIssues' => count($events),
'numberOfIssues' => $issues,
'elements' => $elements,
];
}
private function testLocation(Test $test) : string {
if (!$test->isTestMethod()) {
return '';
}
assert($test instanceof TestMethod);
return sprintf('%s%s:%d%s', PHP_EOL, $test->file(), $test->line(), PHP_EOL);
}
private function reasonMessage(ConsideredRisky|DeprecationTriggered|ErrorTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpunitDeprecationTriggered|PhpunitErrorTriggered|PhpunitWarningTriggered|PhpWarningTriggered|WarningTriggered $reason, bool $single) : string {
$message = trim($reason->message());
if ($single) {
return $message . PHP_EOL;
}
$lines = explode(PHP_EOL, $message);
$buffer = '* ' . $lines[0] . PHP_EOL;
if (count($lines) > 1) {
foreach (range(1, count($lines) - 1) as $line) {
$buffer .= ' ' . $lines[$line] . PHP_EOL;
}
}
return $buffer;
}
private function reasonLocation(ConsideredRisky|DeprecationTriggered|ErrorTriggered|NoticeTriggered|PhpDeprecationTriggered|PhpNoticeTriggered|PhpunitDeprecationTriggered|PhpunitErrorTriggered|PhpunitWarningTriggered|PhpWarningTriggered|WarningTriggered $reason, bool $single) : string {
if (!$reason instanceof DeprecationTriggered && !$reason instanceof PhpDeprecationTriggered && !$reason instanceof ErrorTriggered && !$reason instanceof NoticeTriggered && !$reason instanceof PhpNoticeTriggered && !$reason instanceof WarningTriggered && !$reason instanceof PhpWarningTriggered) {
return '';
}
return sprintf('%s%s:%d%s', $single ? '' : ' ', $reason->file(), $reason->line(), PHP_EOL);
}
}