1
0
Fork 0
mirror of synced 2024-06-27 02:31:04 +12:00

Allow flaky tests to be automatically retried with an attribute

This commit is contained in:
Jake Barnby 2022-08-26 21:21:08 +12:00
parent e658457b1b
commit bbeeb356ab
5 changed files with 93 additions and 0 deletions

View file

@ -2,11 +2,14 @@
namespace Tests\E2E\Scopes;
use Appwrite\Tests\Retryable;
use Tests\E2E\Client;
use PHPUnit\Framework\TestCase;
abstract class Scope extends TestCase
{
use Retryable;
/**
* @var Client
*/

View file

@ -2,6 +2,7 @@
namespace Tests\E2E\Services\Functions;
use Appwrite\Tests\FlakyTest;
use CURLFile;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
@ -1206,6 +1207,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(204, $response['headers']['status-code']);
}
#[FlakyTest(retries: 1)]
public function testCreateCustomRubyExecution()
{
$name = 'ruby-3.1';

View file

@ -2,6 +2,7 @@
namespace Tests\E2E\Services\Webhooks;
use Appwrite\Tests\FlakyTest;
use CURLFile;
use Tests\E2E\Client;
@ -296,6 +297,7 @@ trait WebhooksBase
/**
* @depends testCreateCollection
*/
#[FlakyTest(retries: 1)]
public function testDeleteDocument(array $data): array
{
$actorsId = $data['actorsId'];

View file

@ -0,0 +1,16 @@
<?php
namespace Appwrite\Tests;
/**
* Allows test methods to be retried if they fail.
*
* Requires that the test class extends {@see TestCase} and has trait {@see Retryable}.
*/
#[\Attribute(\Attribute::TARGET_METHOD)]
class FlakyTest
{
public function __construct(protected int $retries = 1)
{
}
}

View file

@ -0,0 +1,70 @@
<?php
namespace Appwrite\Tests;
use PHPUnit\Framework\TestCase;
/**
* Allows test methods annotated with {@see FlakyTest} to be retried.
*/
trait Retryable
{
/**
* Custom runBare, hides and defers to PHPUnit {@see TestCase} runBare function,
* accounting for any retries configured by the {@see FlakyTest} annotation.
*
* @return void
* @throws \ReflectionException
* @throws \Throwable
*/
public function runBare(): void
{
$retries = $this->getNumberOfRetries();
$ex = null;
for ($i = 0; $i <= $retries; ++$i) {
try {
parent::runBare();
return;
} catch (\Throwable | \Exception $ex) {
// Swallow the exception until we have exhausted our retries.
if ($i !== $retries) {
echo 'Flaky test failed, retrying...' . PHP_EOL;
}
}
}
if ($ex) {
throw $ex;
}
}
/**
* @return int
* @throws \ReflectionException
*/
private function getNumberOfRetries(): int
{
$root = new \ReflectionClass($this);
$case = $this->getTestCaseRoot($root);
$name = $case->getProperty('name');
$name->setAccessible(true);
$name = $name->getValue($this);
$method = $root->getMethod($name);
$attributes = $method->getAttributes(FlakyTest::class);
$attribute = $attributes[0] ?? null;
$args = $attribute?->getArguments();
$retries = $args['retries'] ?? 0;
return \max(0, $retries);
}
/**
* @param \ReflectionClass $reflection
* @return \ReflectionClass
*/
private function getTestCaseRoot(\ReflectionClass $reflection): \ReflectionClass
{
if ($reflection->getName() === TestCase::class) {
return $reflection;
}
return $this->getTestCaseRoot($reflection->getParentClass());
}
}