Make WordPress Core

Changeset 59729 for trunk


Ignore:
Timestamp:
01/29/2025 06:17:34 PM (7 weeks ago)
Author:
johnbillion
Message:

Build/Test Tools: Add a retry mechanism for tests that perform external 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

Location:
trunk/tests/phpunit
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/includes/abstract-testcase.php

    r59057 r59729  
    16951695        touch( $file );
    16961696    }
     1697
     1698    /**
     1699     * Wrapper for `wp_safe_remote_request()` that retries on error and skips the test on timeout.
     1700     *
     1701     * @param string $url  URL to retrieve.
     1702     * @param array  $args Optional. Request arguments. Default empty array.
     1703     * @return array|WP_Error The response or WP_Error on failure.
     1704     */
     1705    protected function wp_safe_remote_request( $url, $args = array() ) {
     1706        return self::retry_on_error( 'wp_safe_remote_request', $url, $args );
     1707    }
     1708
     1709    /**
     1710     * Wrapper for `wp_safe_remote_get()` that retries on error and skips the test on timeout.
     1711     *
     1712     * @param string $url  URL to retrieve.
     1713     * @param array  $args Optional. Request arguments. Default empty array.
     1714     * @return array|WP_Error The response or WP_Error on failure.
     1715     */
     1716    protected function wp_safe_remote_get( $url, $args = array() ) {
     1717        return self::retry_on_error( 'wp_safe_remote_get', $url, $args );
     1718    }
     1719
     1720    /**
     1721     * Wrapper for `wp_safe_remote_post()` that retries on error and skips the test on timeout.
     1722     *
     1723     * @param string $url  URL to retrieve.
     1724     * @param array  $args Optional. Request arguments. Default empty array.
     1725     * @return array|WP_Error The response or WP_Error on failure.
     1726     */
     1727    protected function wp_safe_remote_post( $url, $args = array() ) {
     1728        return self::retry_on_error( 'wp_safe_remote_post', $url, $args );
     1729    }
     1730
     1731    /**
     1732     * Wrapper for `wp_safe_remote_head()` that retries on error and skips the test on timeout.
     1733     *
     1734     * @param string $url  URL to retrieve.
     1735     * @param array  $args Optional. Request arguments. Default empty array.
     1736     * @return array|WP_Error The response or WP_Error on failure.
     1737     */
     1738    protected function wp_safe_remote_head( $url, $args = array() ) {
     1739        return self::retry_on_error( 'wp_safe_remote_head', $url, $args );
     1740    }
     1741
     1742    /**
     1743     * Wrapper for `wp_remote_request()` that retries on error and skips the test on timeout.
     1744     *
     1745     * @param string $url  URL to retrieve.
     1746     * @param array  $args Optional. Request arguments. Default empty array.
     1747     * @return array|WP_Error The response or WP_Error on failure.
     1748     */
     1749    protected function wp_remote_request( $url, $args = array() ) {
     1750        return self::retry_on_error( 'wp_remote_request', $url, $args );
     1751    }
     1752
     1753    /**
     1754     * Wrapper for `wp_remote_get()` that retries on error and skips the test on timeout.
     1755     *
     1756     * @param string $url  URL to retrieve.
     1757     * @param array  $args Optional. Request arguments. Default empty array.
     1758     * @return array|WP_Error The response or WP_Error on failure.
     1759     */
     1760    protected function wp_remote_get( $url, $args = array() ) {
     1761        return self::retry_on_error( 'wp_remote_get', $url, $args );
     1762    }
     1763
     1764    /**
     1765     * Wrapper for `wp_remote_post()` that retries on error and skips the test on timeout.
     1766     *
     1767     * @param string $url  URL to retrieve.
     1768     * @param array  $args Optional. Request arguments. Default empty array.
     1769     * @return array|WP_Error The response or WP_Error on failure.
     1770     */
     1771    protected function wp_remote_post( $url, $args = array() ) {
     1772        return self::retry_on_error( 'wp_remote_post', $url, $args );
     1773    }
     1774
     1775    /**
     1776     * Wrapper for `wp_remote_head()` that retries on error and skips the test on timeout.
     1777     *
     1778     * @param string $url  URL to retrieve.
     1779     * @param array  $args Optional. Request arguments. Default empty array.
     1780     * @return array|WP_Error The response or WP_Error on failure.
     1781     */
     1782    protected function wp_remote_head( $url, $args = array() ) {
     1783        return self::retry_on_error( 'wp_remote_head', $url, $args );
     1784    }
     1785
     1786    /**
     1787     * Retries an HTTP API request up to three times and skips the test on timeout.
     1788     *
     1789     * @param callable $callback The HTTP API request function to call.
     1790     * @param string   $url      URL to retrieve.
     1791     * @param array    $args     Request arguments.
     1792     * @return array|WP_Error The response or WP_Error on failure.
     1793     */
     1794    private function retry_on_error( callable $callback, $url, $args ) {
     1795        $attempts = 0;
     1796
     1797        while ( $attempts < 3 ) {
     1798            $result = call_user_func( $callback, $url, $args );
     1799
     1800            if ( ! is_wp_error( $result ) ) {
     1801                return $result;
     1802            }
     1803
     1804            ++$attempts;
     1805            sleep( 5 );
     1806        }
     1807
     1808        $this->skipTestOnTimeout( $result );
     1809
     1810        return $result;
     1811    }
    16971812}
  • trunk/tests/phpunit/tests/http/base.php

    r58108 r59729  
    4545    public function test_redirect_on_301() {
    4646        // 5 : 5 & 301.
    47         $res = wp_remote_request( $this->redirection_script . '?code=301&rt=' . 5, array( 'redirection' => 5 ) );
    48 
    49         $this->skipTestOnTimeout( $res );
     47        $res = $this->wp_remote_request( $this->redirection_script . '?code=301&rt=' . 5, array( 'redirection' => 5 ) );
     48
    5049        $this->assertNotWPError( $res );
    5150        $this->assertSame( 200, (int) $res['response']['code'] );
     
    5756    public function test_redirect_on_302() {
    5857        // 5 : 5 & 302.
    59         $res = wp_remote_request( $this->redirection_script . '?code=302&rt=' . 5, array( 'redirection' => 5 ) );
    60 
    61         $this->skipTestOnTimeout( $res );
     58        $res = $this->wp_remote_request( $this->redirection_script . '?code=302&rt=' . 5, array( 'redirection' => 5 ) );
     59
    6260        $this->assertNotWPError( $res );
    6361        $this->assertSame( 200, (int) $res['response']['code'] );
     
    7169    public function test_redirect_on_301_no_redirect() {
    7270        // 5 > 0 & 301.
    73         $res = wp_remote_request( $this->redirection_script . '?code=301&rt=' . 5, array( 'redirection' => 0 ) );
    74 
    75         $this->skipTestOnTimeout( $res );
     71        $res = $this->wp_remote_request( $this->redirection_script . '?code=301&rt=' . 5, array( 'redirection' => 0 ) );
     72
    7673        $this->assertNotWPError( $res );
    7774        $this->assertSame( 301, (int) $res['response']['code'] );
     
    8582    public function test_redirect_on_302_no_redirect() {
    8683        // 5 > 0 & 302.
    87         $res = wp_remote_request( $this->redirection_script . '?code=302&rt=' . 5, array( 'redirection' => 0 ) );
    88 
    89         $this->skipTestOnTimeout( $res );
     84        $res = $this->wp_remote_request( $this->redirection_script . '?code=302&rt=' . 5, array( 'redirection' => 0 ) );
     85
    9086        $this->assertNotWPError( $res );
    9187        $this->assertSame( 302, (int) $res['response']['code'] );
     
    9793    public function test_redirections_equal() {
    9894        // 5 - 5.
    99         $res = wp_remote_request( $this->redirection_script . '?rt=' . 5, array( 'redirection' => 5 ) );
    100 
    101         $this->skipTestOnTimeout( $res );
     95        $res = $this->wp_remote_request( $this->redirection_script . '?rt=' . 5, array( 'redirection' => 5 ) );
     96
    10297        $this->assertNotWPError( $res );
    10398        $this->assertSame( 200, (int) $res['response']['code'] );
     
    109104    public function test_no_head_redirections() {
    110105        // No redirections on HEAD request.
    111         $res = wp_remote_request( $this->redirection_script . '?code=302&rt=' . 1, array( 'method' => 'HEAD' ) );
    112 
    113         $this->skipTestOnTimeout( $res );
     106        $res = $this->wp_remote_request( $this->redirection_script . '?code=302&rt=' . 1, array( 'method' => 'HEAD' ) );
     107
    114108        $this->assertNotWPError( $res );
    115109        $this->assertSame( 302, (int) $res['response']['code'] );
     
    123117    public function test_redirect_on_head() {
    124118        // Redirections on HEAD request when Requested.
    125         $res = wp_remote_request(
     119        $res = $this->wp_remote_request(
    126120            $this->redirection_script . '?rt=' . 5,
    127121            array(
     
    131125        );
    132126
    133         $this->skipTestOnTimeout( $res );
    134127        $this->assertNotWPError( $res );
    135128        $this->assertSame( 200, (int) $res['response']['code'] );
     
    141134    public function test_redirections_greater() {
    142135        // 10 > 5.
    143         $res = wp_remote_request( $this->redirection_script . '?rt=' . 10, array( 'redirection' => 5 ) );
    144 
    145         $this->skipTestOnTimeout( $res );
     136        $res = $this->wp_remote_request( $this->redirection_script . '?rt=' . 10, array( 'redirection' => 5 ) );
     137
    146138        $this->assertWPError( $res );
    147139    }
     
    152144    public function test_redirections_greater_edgecase() {
    153145        // 6 > 5 (close edge case).
    154         $res = wp_remote_request( $this->redirection_script . '?rt=' . 6, array( 'redirection' => 5 ) );
    155 
    156         $this->skipTestOnTimeout( $res );
     146        $res = $this->wp_remote_request( $this->redirection_script . '?rt=' . 6, array( 'redirection' => 5 ) );
     147
    157148        $this->assertWPError( $res );
    158149    }
     
    163154    public function test_redirections_less_edgecase() {
    164155        // 4 < 5 (close edge case).
    165         $res = wp_remote_request( $this->redirection_script . '?rt=' . 4, array( 'redirection' => 5 ) );
    166 
    167         $this->skipTestOnTimeout( $res );
     156        $res = $this->wp_remote_request( $this->redirection_script . '?rt=' . 4, array( 'redirection' => 5 ) );
     157
    168158        $this->assertNotWPError( $res );
    169159    }
     
    176166    public function test_redirections_zero_redirections_specified() {
    177167        // 0 redirections asked for, should return the document?
    178         $res = wp_remote_request( $this->redirection_script . '?code=302&rt=' . 5, array( 'redirection' => 0 ) );
    179 
    180         $this->skipTestOnTimeout( $res );
     168        $res = $this->wp_remote_request( $this->redirection_script . '?code=302&rt=' . 5, array( 'redirection' => 0 ) );
     169
    181170        $this->assertNotWPError( $res );
    182171        $this->assertSame( 302, (int) $res['response']['code'] );
     
    192181    public function test_location_header_on_201() {
    193182        // Prints PASS on initial load, FAIL if the client follows the specified redirection.
    194         $res = wp_remote_request( $this->redirection_script . '?201-location=true' );
    195 
    196         $this->skipTestOnTimeout( $res );
     183        $res = $this->wp_remote_request( $this->redirection_script . '?201-location=true' );
     184
    197185        $this->assertNotWPError( $res );
    198186        $this->assertSame( 'PASS', $res['body'] );
     
    211199
    212200        // Test 301 - POST to POST.
    213         $res = wp_remote_request(
     201        $res = $this->wp_remote_request(
    214202            $url,
    215203            array(
     
    219207        );
    220208
    221         $this->skipTestOnTimeout( $res );
    222209        $this->assertNotWPError( $res );
    223210        $this->assertSame( 'PASS', wp_remote_retrieve_body( $res ) );
     
    237224            'test3' => '',
    238225        );
    239         $res     = wp_remote_request( $this->redirection_script . '?header-check', array( 'headers' => $headers ) );
    240 
    241         $this->skipTestOnTimeout( $res );
     226        $res     = $this->wp_remote_request( $this->redirection_script . '?header-check', array( 'headers' => $headers ) );
     227
    242228        $this->assertNotWPError( $res );
    243229
     
    268254        $url  = $this->file_stream_url;
    269255        $size = 153204;
    270         $res  = wp_remote_request(
     256        $res  = $this->wp_remote_request(
    271257            $url,
    272258            array(
     
    282268        }
    283269
    284         $this->skipTestOnTimeout( $res );
    285270        $this->assertNotWPError( $res );
    286271        $this->assertSame( '', $res['body'] ); // The body should be empty.
     
    298283        $url  = $this->file_stream_url;
    299284        $size = 10000;
    300         $res  = wp_remote_request(
     285        $res  = $this->wp_remote_request(
    301286            $url,
    302287            array(
     
    313298        }
    314299
    315         $this->skipTestOnTimeout( $res );
    316300        $this->assertNotWPError( $res );
    317301        $this->assertSame( $size, $filesize ); // Check that the file is written to disk correctly without any extra characters.
     
    329313        $size = 10000;
    330314
    331         $res = wp_remote_request(
     315        $res = $this->wp_remote_request(
    332316            $url,
    333317            array(
     
    337321        );
    338322
    339         $this->skipTestOnTimeout( $res );
    340323        $this->assertNotWPError( $res );
    341324        $this->assertSame( $size, strlen( $res['body'] ) );
     
    355338        $url = 'http://api.wordpress.org/core/tests/1.0/redirection.php?post-redirect-to-method=1';
    356339
    357         $res = wp_remote_post( add_query_arg( 'response_code', $response_code, $url ), array( 'timeout' => 30 ) );
    358 
    359         $this->skipTestOnTimeout( $res );
     340        $res = $this->wp_remote_post( add_query_arg( 'response_code', $response_code, $url ), array( 'timeout' => 30 ) );
     341
    360342        $this->assertNotWPError( $res );
    361343        $this->assertSame( $method, wp_remote_retrieve_body( $res ) );
     
    406388        );
    407389
    408         $res = wp_remote_get( $url, $args );
    409 
    410         $this->skipTestOnTimeout( $res );
     390        $res = $this->wp_remote_get( $url, $args );
     391
    411392        $this->assertNotWPError( $res );
    412393        $this->assertSame( 'PASS', wp_remote_retrieve_body( $res ) );
     
    428409        add_filter( 'http_request_args', array( $this, 'filter_http_request_args' ) );
    429410
    430         $res = wp_remote_head( $url, $args );
     411        $res = $this->wp_remote_head( $url, $args );
    431412
    432413        remove_filter( 'http_request_args', array( $this, 'filter_http_request_args' ) );
    433414
    434         $this->skipTestOnTimeout( $res );
    435415        $this->assertNotEmpty( $this->http_request_args['sslcertificates'] );
    436416        $this->assertNotWPError( $res );
     
    448428        $url = 'http://api.wordpress.org/core/tests/1.0/redirection.php?cookie-test=1';
    449429
    450         $res = wp_remote_get( $url );
    451 
    452         $this->skipTestOnTimeout( $res );
     430        $res = $this->wp_remote_get( $url );
     431
    453432        $this->assertNotWPError( $res );
    454433        $this->assertSame( 'PASS', wp_remote_retrieve_body( $res ) );
     
    468447        }
    469448
    470         $res = wp_remote_get( 'https://wordpress.org/' );
    471 
    472         $this->skipTestOnTimeout( $res );
     449        $res = $this->wp_remote_get( 'https://wordpress.org/' );
     450
    473451        $this->assertNotWPError( $res );
    474452    }
     
    485463        $url  = str_replace( $path, '/' . $path, $url );
    486464
    487         $res = wp_remote_request( $url );
    488 
    489         $this->skipTestOnTimeout( $res );
     465        $res = $this->wp_remote_request( $url );
     466
    490467        $this->assertNotWPError( $res );
    491468    }
  • trunk/tests/phpunit/tests/http/functions.php

    r58108 r59729  
    1313        // This URL gives a direct 200 response.
    1414        $url      = 'https://s.w.org/screenshots/3.9/dashboard.png';
    15         $response = wp_remote_head( $url );
    16 
    17         $this->skipTestOnTimeout( $response );
     15        $response = $this->wp_remote_head( $url );
     16
    1817        $this->assertNotWPError( $response );
    1918
     
    3231        // This URL will 301 redirect.
    3332        $url      = 'https://wp.org/screenshots/3.9/dashboard.png';
    34         $response = wp_remote_head( $url );
    35 
    36         $this->skipTestOnTimeout( $response );
     33        $response = $this->wp_remote_head( $url );
     34
    3735        $this->assertNotWPError( $response );
    3836        $this->assertSame( 301, wp_remote_retrieve_response_code( $response ) );
     
    4442    public function test_head_404() {
    4543        $url      = 'https://wordpress.org/screenshots/3.9/awefasdfawef.jpg';
    46         $response = wp_remote_head( $url );
    47 
    48         $this->skipTestOnTimeout( $response );
     44        $response = $this->wp_remote_head( $url );
     45
    4946        $this->assertNotWPError( $response );
    5047        $this->assertSame( 404, wp_remote_retrieve_response_code( $response ) );
     
    5956        $url = 'https://s.w.org/screenshots/3.9/dashboard.png';
    6057
    61         $response = wp_remote_get( $url );
    62 
    63         $this->skipTestOnTimeout( $response );
     58        $response = $this->wp_remote_get( $url );
     59
    6460        $this->assertNotWPError( $response );
    6561
     
    8177        $url = 'https://wp.org/screenshots/3.9/dashboard.png';
    8278
    83         $response = wp_remote_get( $url );
    84 
    85         $this->skipTestOnTimeout( $response );
     79        $response = $this->wp_remote_get( $url );
     80
    8681        $this->assertNotWPError( $response );
    8782
     
    10297
    10398        // Pretend we've already redirected 5 times.
    104         $response = wp_remote_get( $url, array( 'redirection' => -1 ) );
    105 
    106         $this->skipTestOnTimeout( $response );
     99        $response = $this->wp_remote_get( $url, array( 'redirection' => -1 ) );
     100
    107101        $this->assertWPError( $response );
    108102    }
     
    119113        $url = 'https://login.wordpress.org/wp-login.php';
    120114
    121         $response = wp_remote_head( $url );
    122 
    123         $this->skipTestOnTimeout( $response );
     115        $response = $this->wp_remote_head( $url );
     116
    124117        $this->assertNotWPError( $response );
    125118
     
    153146        $url = 'https://login.wordpress.org/wp-login.php';
    154147
    155         $response = wp_remote_get(
     148        $response = $this->wp_remote_get(
    156149            $url,
    157150            array(
     
    167160        );
    168161
    169         $this->skipTestOnTimeout( $response );
    170162        $this->assertNotWPError( $response );
    171163
     
    190182        $url = 'https://login.wordpress.org/wp-login.php';
    191183
    192         $response = wp_remote_get(
     184        $response = $this->wp_remote_get(
    193185            $url,
    194186            array(
     
    199191        );
    200192
    201         $this->skipTestOnTimeout( $response );
    202193        $this->assertNotWPError( $response );
    203194
  • trunk/tests/phpunit/tests/readme.php

    r58108 r59729  
    9292     */
    9393    public function get_response_body( $url ) {
    94         $response = wp_remote_get( $url );
     94        $response = $this->wp_remote_get( $url );
    9595
    96         $this->skipTestOnTimeout( $response );
    9796        $this->assertNotWPError( $response );
    9897
Note: See TracChangeset for help on using the changeset viewer.