output = new ConsoleOutput(OutputInterface::VERBOSITY_NORMAL, $colors); ConfigureIO::of(new ArgvInput(), $this->output); self::$verbose = $this->output->isVerbose(); $this->style = new Style($this->output); $this->state = new State(); } /** * If the printer instances should be compact. */ public static function compact(bool $value = null): bool { if (! is_null($value)) { self::$compact = $value; } return ! self::$verbose && self::$compact; } /** * If the printer instances should profile. */ public static function profile(bool $value = null): bool { if (! is_null($value)) { self::$profile = $value; } return self::$profile; } /** * Defines if the output should be decorated or not. */ public function setDecorated(bool $decorated): void { $this->output->setDecorated($decorated); } /** * Listen to the runner execution started event. */ public function testRunnerExecutionStarted(ExecutionStarted $executionStarted): void { // .. } /** * Listen to the test finished event. */ public function testFinished(Finished $event): void { $duration = (hrtime(true) - $this->testStartedAt) / 1_000_000; $test = $event->test(); if (! $test instanceof TestMethod) { throw new ShouldNotHappen(); } if (! $this->state->existsInTestCase($event->test())) { $this->state->add(TestResult::fromTestCase($event->test(), TestResult::PASS)); } $result = $this->state->setDuration($test, $duration); if (self::$profile) { $this->profileSlowTests[$event->test()->id()] = $result; // Sort the slow tests by time, and keep only 10 of them. uasort($this->profileSlowTests, static function (TestResult $a, TestResult $b) { return $b->duration <=> $a->duration; }); $this->profileSlowTests = array_slice($this->profileSlowTests, 0, 10); } } /** * Listen to the test prepared event. */ public function testPreparationStarted(PreparationStarted $event): void { $this->testStartedAt = hrtime(true); $test = $event->test(); if (! $test instanceof TestMethod) { throw new ShouldNotHappen(); } if ($this->state->testCaseHasChanged($test)) { $this->style->writeCurrentTestCaseSummary($this->state); $this->state->moveTo($test); } } /** * Listen to the test errored event. */ public function testBeforeFirstTestMethodErrored(BeforeFirstTestMethodErrored $event): void { $this->state->add(TestResult::fromBeforeFirstTestMethodErrored($event)); } /** * Listen to the test errored event. */ public function testErrored(Errored $event): void { $this->state->add(TestResult::fromTestCase($event->test(), TestResult::FAIL, $event->throwable())); } /** * Listen to the test failed event. */ public function testFailed(Failed $event): void { $throwable = $event->throwable(); $this->state->add(TestResult::fromTestCase($event->test(), TestResult::FAIL, $throwable)); } /** * Listen to the test marked incomplete event. */ public function testMarkedIncomplete(MarkedIncomplete $event): void { $this->state->add(TestResult::fromTestCase($event->test(), TestResult::INCOMPLETE, $event->throwable())); } /** * Listen to the test considered risky event. */ public function testConsideredRisky(ConsideredRisky $event): void { $throwable = ThrowableBuilder::from(new IncompleteTestError($event->message())); $this->state->add(TestResult::fromTestCase($event->test(), TestResult::RISKY, $throwable)); } /** * Listen to the test runner warning triggered. */ public function testRunnerWarningTriggered(TestRunnerWarningTriggered $event): void { if (! str_starts_with($event->message(), 'No tests found in class')) { $this->style->writeWarning($event->message()); } } /** * Listen to the test runner warning triggered. */ public function testPhpDeprecationTriggered(PhpDeprecationTriggered $event): void { $throwable = ThrowableBuilder::from(new TestOutcome($event->message())); $this->state->add(TestResult::fromTestCase($event->test(), TestResult::DEPRECATED, $throwable)); } /** * Listen to the test runner notice triggered. */ public function testPhpNoticeTriggered(PhpNoticeTriggered $event): void { $throwable = ThrowableBuilder::from(new TestOutcome($event->message())); $this->state->add(TestResult::fromTestCase($event->test(), TestResult::NOTICE, $throwable)); } /** * Listen to the test php warning triggered event. */ public function testPhpWarningTriggered(PhpWarningTriggered $event): void { $throwable = ThrowableBuilder::from(new TestOutcome($event->message())); $this->state->add(TestResult::fromTestCase($event->test(), TestResult::WARN, $throwable)); } /** * Listen to the test runner warning triggered. */ public function testPhpunitWarningTriggered(PhpunitWarningTriggered $event): void { $throwable = ThrowableBuilder::from(new TestOutcome($event->message())); $this->state->add(TestResult::fromTestCase($event->test(), TestResult::WARN, $throwable)); } /** * Listen to the test deprecation triggered event. */ public function testDeprecationTriggered(DeprecationTriggered $event): void { $throwable = ThrowableBuilder::from(new TestOutcome($event->message())); $this->state->add(TestResult::fromTestCase($event->test(), TestResult::DEPRECATED, $throwable)); } /** * Listen to the test warning triggered event. */ public function testNoticeTriggered(NoticeTriggered $event): void { $throwable = ThrowableBuilder::from(new TestOutcome($event->message())); $this->state->add(TestResult::fromTestCase($event->test(), TestResult::NOTICE, $throwable)); } /** * Listen to the test warning triggered event. */ public function testWarningTriggered(WarningTriggered $event): void { $throwable = ThrowableBuilder::from(new TestOutcome($event->message())); $this->state->add(TestResult::fromTestCase($event->test(), TestResult::WARN, $throwable)); } /** * Listen to the test skipped event. */ public function testSkipped(Skipped $event): void { if ($event->message() === '__TODO__') { $this->state->add(TestResult::fromTestCase($event->test(), TestResult::TODO)); return; } $throwable = ThrowableBuilder::from(new SkippedWithMessageException($event->message())); $this->state->add(TestResult::fromTestCase($event->test(), TestResult::SKIPPED, $throwable)); } /** * Listen to the test finished event. */ public function testPassed(Passed $event): void { if (! $this->state->existsInTestCase($event->test())) { $this->state->add(TestResult::fromTestCase($event->test(), TestResult::PASS)); } } /** * Listen to the runner execution finished event. */ public function testRunnerExecutionFinished(ExecutionFinished $event): void { $result = Facade::result(); if (Facade::result()->numberOfTests() === 0) { $this->output->writeln([ '', ' INFO No tests found.', '', ]); return; } $this->style->writeCurrentTestCaseSummary($this->state); if (self::$compact) { $this->output->writeln(['']); } $failed = class_exists(Result::class) ? Result::failed() : (! Facade::result()->wasSuccessful()); $this->style->writeErrorsSummary($this->state); $this->style->writeRecap($this->state, $event->telemetryInfo(), $result); if (! $failed && count($this->profileSlowTests) > 0) { $this->style->writeSlowTests($this->profileSlowTests, $event->telemetryInfo()); } } /** * Reports the given throwable. */ public function report(\Throwable $throwable): void { $this->style->writeError(ThrowableBuilder::from($throwable)); } }