Skip to content

Commit

Permalink
Build/Test Tools: Add a retry mechanism for tests that perform extern…
Browse files Browse the repository at this point in the history
…al HTTP requests.

While the `skipTestOnTimeout()` method will catch a timeout and prevent it from causing a test to fail, other errors such as a failed DNS lookup or HTTPS handshake can still cause a test to unnecessarily fail. This introduces a simple retry mechanism that will hopefully further reduce the flakiness of tests that perform HTTP API requests.

Fixes #62830

git-svn-id: https://develop.svn.wordpress.org/trunk@59729 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
johnbillion committed Jan 29, 2025
1 parent 4863a92 commit 9acdbb9
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 66 deletions.
115 changes: 115 additions & 0 deletions tests/phpunit/includes/abstract-testcase.php
Original file line number Diff line number Diff line change
Expand Up @@ -1694,4 +1694,119 @@ public static function touch( $file ) {

touch( $file );
}

/**
* Wrapper for `wp_safe_remote_request()` that retries on error and skips the test on timeout.
*
* @param string $url URL to retrieve.
* @param array $args Optional. Request arguments. Default empty array.
* @return array|WP_Error The response or WP_Error on failure.
*/
protected function wp_safe_remote_request( $url, $args = array() ) {
return self::retry_on_error( 'wp_safe_remote_request', $url, $args );
}

/**
* Wrapper for `wp_safe_remote_get()` that retries on error and skips the test on timeout.
*
* @param string $url URL to retrieve.
* @param array $args Optional. Request arguments. Default empty array.
* @return array|WP_Error The response or WP_Error on failure.
*/
protected function wp_safe_remote_get( $url, $args = array() ) {
return self::retry_on_error( 'wp_safe_remote_get', $url, $args );
}

/**
* Wrapper for `wp_safe_remote_post()` that retries on error and skips the test on timeout.
*
* @param string $url URL to retrieve.
* @param array $args Optional. Request arguments. Default empty array.
* @return array|WP_Error The response or WP_Error on failure.
*/
protected function wp_safe_remote_post( $url, $args = array() ) {
return self::retry_on_error( 'wp_safe_remote_post', $url, $args );
}

/**
* Wrapper for `wp_safe_remote_head()` that retries on error and skips the test on timeout.
*
* @param string $url URL to retrieve.
* @param array $args Optional. Request arguments. Default empty array.
* @return array|WP_Error The response or WP_Error on failure.
*/
protected function wp_safe_remote_head( $url, $args = array() ) {
return self::retry_on_error( 'wp_safe_remote_head', $url, $args );
}

/**
* Wrapper for `wp_remote_request()` that retries on error and skips the test on timeout.
*
* @param string $url URL to retrieve.
* @param array $args Optional. Request arguments. Default empty array.
* @return array|WP_Error The response or WP_Error on failure.
*/
protected function wp_remote_request( $url, $args = array() ) {
return self::retry_on_error( 'wp_remote_request', $url, $args );
}

/**
* Wrapper for `wp_remote_get()` that retries on error and skips the test on timeout.
*
* @param string $url URL to retrieve.
* @param array $args Optional. Request arguments. Default empty array.
* @return array|WP_Error The response or WP_Error on failure.
*/
protected function wp_remote_get( $url, $args = array() ) {
return self::retry_on_error( 'wp_remote_get', $url, $args );
}

/**
* Wrapper for `wp_remote_post()` that retries on error and skips the test on timeout.
*
* @param string $url URL to retrieve.
* @param array $args Optional. Request arguments. Default empty array.
* @return array|WP_Error The response or WP_Error on failure.
*/
protected function wp_remote_post( $url, $args = array() ) {
return self::retry_on_error( 'wp_remote_post', $url, $args );
}

/**
* Wrapper for `wp_remote_head()` that retries on error and skips the test on timeout.
*
* @param string $url URL to retrieve.
* @param array $args Optional. Request arguments. Default empty array.
* @return array|WP_Error The response or WP_Error on failure.
*/
protected function wp_remote_head( $url, $args = array() ) {
return self::retry_on_error( 'wp_remote_head', $url, $args );
}

/**
* Retries an HTTP API request up to three times and skips the test on timeout.
*
* @param callable $callback The HTTP API request function to call.
* @param string $url URL to retrieve.
* @param array $args Request arguments.
* @return array|WP_Error The response or WP_Error on failure.
*/
private function retry_on_error( callable $callback, $url, $args ) {
$attempts = 0;

while ( $attempts < 3 ) {
$result = call_user_func( $callback, $url, $args );

if ( ! is_wp_error( $result ) ) {
return $result;
}

++$attempts;
sleep( 5 );
}

$this->skipTestOnTimeout( $result );

return $result;
}
}
Loading

0 comments on commit 9acdbb9

Please sign in to comment.