Skip to content

Commit 7856ef2

Browse files
test: support retries in test methods
1 parent 76a0e36 commit 7856ef2

2 files changed

Lines changed: 68 additions & 10 deletions

File tree

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Core/TestCase.inc

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,36 @@ class TestCase {
6868

6969
# Set the current method undergoing testing
7070
$this->method = $method;
71+
$ref_method = new ReflectionMethod($this, $method);
7172

7273
# Gather the description for this test method from its doc comment
73-
$this->method_docstring = (new ReflectionMethod($this, $method))->getDocComment();
74+
$this->method_docstring = $ref_method->getDocComment();
7475
$this->method_docstring = str_replace([PHP_EOL, '/**', '*/', '*', ' '], '', $this->method_docstring);
7576

76-
# Try to run the test. On failure, restore the original config and throw the exception
77-
try {
78-
$this->$method();
79-
} catch (Error | Exception $e) {
80-
# Restore the original configuration, teardown the TestCase and throw the encountered error
81-
$config = $original_config;
82-
write_config("Restored config after API test '$method'");
83-
$this->teardown();
84-
throw $e;
77+
# Get the retry settings for this method
78+
$retry_settings = $this->get_method_retry_settings($ref_method);
79+
80+
# Always attempt once, then add retries if configured
81+
for ($attempt = 0; $attempt <= $retry_settings->retries; $attempt++) {
82+
try {
83+
# Try to run the test
84+
$this->$method();
85+
break;
86+
} catch (Error | Exception $e) {
87+
# Tear down any resources created by this test before retrying or exiting
88+
$config = $original_config;
89+
write_config("Restored config after API test '$method'");
90+
$this->teardown();
91+
92+
# If we have retries left, wait the configured delay and try again
93+
if ($attempt < $retry_settings->retries) {
94+
sleep($retry_settings->delay);
95+
continue;
96+
}
97+
98+
# If we made it here, we have no retries left. Throw the exception to the caller.
99+
throw $e;
100+
}
85101
}
86102

87103
# Restore the config as it was when the test began.
@@ -137,6 +153,23 @@ class TestCase {
137153
*/
138154
public function teardown(): void {}
139155

156+
/**
157+
* @param ReflectionMethod $method The method to check for a TestCaseRetry attribute.
158+
* @return TestCaseRetry The TestCaseRetry attribute found on the method, or a default TestCaseRetry object
159+
*/
160+
protected function get_method_retry_settings(ReflectionMethod $method): TestCaseRetry {
161+
# Use default retry settings if no attribute is found
162+
$retries = new TestCaseRetry();
163+
164+
# If this method has a TestCaseRetry attribute, use it
165+
$attributes = $method->getAttributes(TestCaseRetry::class);
166+
if (count($attributes) > 0) {
167+
$retries = $attributes[0]->newInstance();
168+
}
169+
170+
return $retries;
171+
}
172+
140173
/**
141174
* Obtains the environment variable with a given name.
142175
*/
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace RESTAPI\Core;
4+
5+
use Attribute;
6+
7+
/**
8+
* A class to represent retry settings for test cases. This is intended to be defined in methods as a method attribute.
9+
*/
10+
#[Attribute(Attribute::TARGET_METHOD)]
11+
class TestCaseRetry
12+
{
13+
14+
/**
15+
* @param int $retries The number of retries to attempt
16+
* @param int $delay The delay in seconds between retries
17+
*/
18+
public function __construct(public int $retries = 0, public int $delay = 0)
19+
{
20+
# Ensure values are non-negative
21+
$this->retries = max(0, $retries);
22+
$this->delay = max(0, $delay);
23+
}
24+
25+
}

0 commit comments

Comments
 (0)