@@ -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 */
0 commit comments