*/ private static array $retryCounts = []; public function notify(Failed $event): void { $this->handleTestFailure($event->test(), $event->throwable()->asString()); } private function handleTestFailure($test, string $errorMessage): void { if (!$test instanceof TestMethod) { return; } $testId = $test->className() . '::' . $test->methodName(); $retryCount = $this->getRetryCountForTest($test); if ($retryCount <= 0) { return; } $currentAttempt = self::$retryCounts[$testId] ?? 0; if ($currentAttempt < $retryCount) { self::$retryCounts[$testId] = $currentAttempt + 1; $remainingRetries = $retryCount - self::$retryCounts[$testId]; fwrite( STDOUT, sprintf( "\e[33m[RETRY] Test %s failed (attempt %d/%d). %s\e[0m\n", $testId, $currentAttempt + 1, $retryCount + 1, $remainingRetries > 0 ? "Will retry {$remainingRetries} more time(s)." : "No more retries." ) ); } } private function getRetryCountForTest(TestMethod $test): int { try { $className = $test->className(); $methodName = $test->methodName(); if (!class_exists($className)) { return 0; } $reflection = new ReflectionClass($className); if (!$reflection->hasMethod($methodName)) { return 0; } $method = $reflection->getMethod($methodName); $attributes = $method->getAttributes(Retry::class); if (empty($attributes)) { return 0; } $attribute = $attributes[0]; $args = $attribute->getArguments(); return max(0, $args['count'] ?? $args[0] ?? 0); } catch (\Throwable) { return 0; } } /** * Reset retry counts between test runs (useful for testing) */ public static function reset(): void { self::$retryCounts = []; } }