Make WordPress Core

Ticket #43218: 43218.5.diff

File 43218.5.diff, 89.5 KB (added by SergeyBiryukov, 7 years ago)
  • .travis.yml

     
    6767  # Install the specified version of PHPUnit depending on the PHP version:
    6868  if [[ "$WP_TRAVISCI" == "travis:phpunit" ]]; then
    6969    case "$TRAVIS_PHP_VERSION" in
    70       7.3|7.2|7.1|7.0|nightly)
     70      7.3|7.2|7.1|nightly)
     71        echo "Using PHPUnit 7.x"
     72        travis_retry composer global require "phpunit/phpunit:^7"
     73        ;;
     74      7.0)
    7175        echo "Using PHPUnit 6.x"
    7276        travis_retry composer global require "phpunit/phpunit:^6"
    7377        ;;
  • phpunit.xml.dist

     
    4040        <const name="WP_RUN_CORE_TESTS" value="1" />
    4141    </php>
    4242        <listeners>
    43                 <listener class="SpeedTrapListener" file="tests/phpunit/includes/speed-trap-listener.php">
     43                <listener class="SpeedTrapListener" file="tests/phpunit/includes/listener-loader.php">
    4444                        <arguments>
    4545                                <array>
    4646                                        <element key="slowThreshold">
  • tests/phpunit/includes/abstract-testcase.php

     
     1<?php
     2
     3require_once dirname( __FILE__ ) . '/factory.php';
     4require_once dirname( __FILE__ ) . '/trac.php';
     5
     6/**
     7 * Defines a basic fixture to run multiple tests.
     8 *
     9 * Resets the state of the WordPress installation before and after every test.
     10 *
     11 * Includes utility functions and assertions useful for testing WordPress.
     12 *
     13 * All WordPress unit tests should inherit from this class.
     14 */
     15abstract class WP_UnitTestCase_Base extends PHPUnit_Framework_TestCase {
     16
     17        protected static $forced_tickets   = array();
     18        protected $expected_deprecated     = array();
     19        protected $caught_deprecated       = array();
     20        protected $expected_doing_it_wrong = array();
     21        protected $caught_doing_it_wrong   = array();
     22
     23        protected static $hooks_saved = array();
     24        protected static $ignore_files;
     25
     26        function __isset( $name ) {
     27                return 'factory' === $name;
     28        }
     29
     30        function __get( $name ) {
     31                if ( 'factory' === $name ) {
     32                        return self::factory();
     33                }
     34        }
     35
     36        /**
     37         * Fetches the factory object for generating WordPress fixtures.
     38         *
     39         * @return WP_UnitTest_Factory The fixture factory.
     40         */
     41        protected static function factory() {
     42                static $factory = null;
     43                if ( ! $factory ) {
     44                        $factory = new WP_UnitTest_Factory();
     45                }
     46                return $factory;
     47        }
     48
     49        public static function get_called_class() {
     50                if ( function_exists( 'get_called_class' ) ) {
     51                        return get_called_class();
     52                }
     53
     54                // PHP 5.2 only
     55                $backtrace = debug_backtrace();
     56                // [0] WP_UnitTestCase::get_called_class()
     57                // [1] WP_UnitTestCase::setUpBeforeClass()
     58                if ( 'call_user_func' === $backtrace[2]['function'] ) {
     59                        return $backtrace[2]['args'][0][0];
     60                }
     61                return $backtrace[2]['class'];
     62        }
     63
     64        public static function setUpBeforeClass() {
     65                global $wpdb;
     66
     67                $wpdb->suppress_errors = false;
     68                $wpdb->show_errors     = true;
     69                $wpdb->db_connect();
     70                ini_set( 'display_errors', 1 );
     71
     72                parent::setUpBeforeClass();
     73
     74                $c = self::get_called_class();
     75                if ( ! method_exists( $c, 'wpSetUpBeforeClass' ) ) {
     76                        self::commit_transaction();
     77                        return;
     78                }
     79
     80                call_user_func( array( $c, 'wpSetUpBeforeClass' ), self::factory() );
     81
     82                self::commit_transaction();
     83        }
     84
     85        public static function tearDownAfterClass() {
     86                parent::tearDownAfterClass();
     87
     88                _delete_all_data();
     89                self::flush_cache();
     90
     91                $c = self::get_called_class();
     92                if ( ! method_exists( $c, 'wpTearDownAfterClass' ) ) {
     93                        self::commit_transaction();
     94                        return;
     95                }
     96
     97                call_user_func( array( $c, 'wpTearDownAfterClass' ) );
     98
     99                self::commit_transaction();
     100        }
     101
     102        function setUp() {
     103                set_time_limit( 0 );
     104
     105                if ( ! self::$ignore_files ) {
     106                        self::$ignore_files = $this->scan_user_uploads();
     107                }
     108
     109                if ( ! self::$hooks_saved ) {
     110                        $this->_backup_hooks();
     111                }
     112
     113                global $wp_rewrite;
     114
     115                $this->clean_up_global_scope();
     116
     117                /*
     118                 * When running core tests, ensure that post types and taxonomies
     119                 * are reset for each test. We skip this step for non-core tests,
     120                 * given the large number of plugins that register post types and
     121                 * taxonomies at 'init'.
     122                 */
     123                if ( defined( 'WP_RUN_CORE_TESTS' ) && WP_RUN_CORE_TESTS ) {
     124                        $this->reset_post_types();
     125                        $this->reset_taxonomies();
     126                        $this->reset_post_statuses();
     127                        $this->reset__SERVER();
     128
     129                        if ( $wp_rewrite->permalink_structure ) {
     130                                $this->set_permalink_structure( '' );
     131                        }
     132                }
     133
     134                $this->start_transaction();
     135                $this->expectDeprecated();
     136                add_filter( 'wp_die_handler', array( $this, 'get_wp_die_handler' ) );
     137        }
     138
     139        /**
     140         * Detect post-test failure conditions.
     141         *
     142         * We use this method to detect expectedDeprecated and expectedIncorrectUsage annotations.
     143         *
     144         * @since 4.2.0
     145         */
     146        protected function assertPostConditions() {
     147                $this->expectedDeprecated();
     148        }
     149
     150        /**
     151         * After a test method runs, reset any state in WordPress the test method might have changed.
     152         */
     153        function tearDown() {
     154                global $wpdb, $wp_query, $wp;
     155                $wpdb->query( 'ROLLBACK' );
     156                if ( is_multisite() ) {
     157                        while ( ms_is_switched() ) {
     158                                restore_current_blog();
     159                        }
     160                }
     161                $wp_query = new WP_Query();
     162                $wp       = new WP();
     163
     164                // Reset globals related to the post loop and `setup_postdata()`.
     165                $post_globals = array( 'post', 'id', 'authordata', 'currentday', 'currentmonth', 'page', 'pages', 'multipage', 'more', 'numpages' );
     166                foreach ( $post_globals as $global ) {
     167                        $GLOBALS[ $global ] = null;
     168                }
     169
     170                $this->unregister_all_meta_keys();
     171                remove_theme_support( 'html5' );
     172                remove_filter( 'query', array( $this, '_create_temporary_tables' ) );
     173                remove_filter( 'query', array( $this, '_drop_temporary_tables' ) );
     174                remove_filter( 'wp_die_handler', array( $this, 'get_wp_die_handler' ) );
     175                $this->_restore_hooks();
     176                wp_set_current_user( 0 );
     177        }
     178
     179        function clean_up_global_scope() {
     180                $_GET  = array();
     181                $_POST = array();
     182                self::flush_cache();
     183        }
     184
     185        /**
     186         * Allow tests to be skipped on some automated runs
     187         *
     188         * For test runs on Travis for something other than trunk/master
     189         * we want to skip tests that only need to run for master.
     190         */
     191        public function skipOnAutomatedBranches() {
     192                // gentenv can be disabled
     193                if ( ! function_exists( 'getenv' ) ) {
     194                        return false;
     195                }
     196
     197                // https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
     198                $travis_branch       = getenv( 'TRAVIS_BRANCH' );
     199                $travis_pull_request = getenv( 'TRAVIS_PULL_REQUEST' );
     200
     201                if ( false !== $travis_pull_request && 'master' !== $travis_branch ) {
     202                        $this->markTestSkipped( 'For automated test runs, this test is only run on trunk/master' );
     203                }
     204        }
     205
     206        /**
     207         * Allow tests to be skipped when Multisite is not in use.
     208         *
     209         * Use in conjunction with the ms-required group.
     210         */
     211        public function skipWithoutMultisite() {
     212                if ( ! is_multisite() ) {
     213                        $this->markTestSkipped( 'Test only runs on Multisite' );
     214                }
     215        }
     216
     217        /**
     218         * Allow tests to be skipped when Multisite is in use.
     219         *
     220         * Use in conjunction with the ms-excluded group.
     221         */
     222        public function skipWithMultisite() {
     223                if ( is_multisite() ) {
     224                        $this->markTestSkipped( 'Test does not run on Multisite' );
     225                }
     226        }
     227
     228        /**
     229         * Unregister existing post types and register defaults.
     230         *
     231         * Run before each test in order to clean up the global scope, in case
     232         * a test forgets to unregister a post type on its own, or fails before
     233         * it has a chance to do so.
     234         */
     235        protected function reset_post_types() {
     236                foreach ( get_post_types( array(), 'objects' ) as $pt ) {
     237                        if ( empty( $pt->tests_no_auto_unregister ) ) {
     238                                _unregister_post_type( $pt->name );
     239                        }
     240                }
     241                create_initial_post_types();
     242        }
     243
     244        /**
     245         * Unregister existing taxonomies and register defaults.
     246         *
     247         * Run before each test in order to clean up the global scope, in case
     248         * a test forgets to unregister a taxonomy on its own, or fails before
     249         * it has a chance to do so.
     250         */
     251        protected function reset_taxonomies() {
     252                foreach ( get_taxonomies() as $tax ) {
     253                        _unregister_taxonomy( $tax );
     254                }
     255                create_initial_taxonomies();
     256        }
     257
     258        /**
     259         * Unregister non-built-in post statuses.
     260         */
     261        protected function reset_post_statuses() {
     262                foreach ( get_post_stati( array( '_builtin' => false ) ) as $post_status ) {
     263                        _unregister_post_status( $post_status );
     264                }
     265        }
     266
     267        /**
     268         * Reset `$_SERVER` variables
     269         */
     270        protected function reset__SERVER() {
     271                tests_reset__SERVER();
     272        }
     273
     274        /**
     275         * Saves the action and filter-related globals so they can be restored later.
     276         *
     277         * Stores $wp_actions, $wp_current_filter, and $wp_filter on a class variable
     278         * so they can be restored on tearDown() using _restore_hooks().
     279         *
     280         * @global array $wp_actions
     281         * @global array $wp_current_filter
     282         * @global array $wp_filter
     283         * @return void
     284         */
     285        protected function _backup_hooks() {
     286                $globals = array( 'wp_actions', 'wp_current_filter' );
     287                foreach ( $globals as $key ) {
     288                        self::$hooks_saved[ $key ] = $GLOBALS[ $key ];
     289                }
     290                self::$hooks_saved['wp_filter'] = array();
     291                foreach ( $GLOBALS['wp_filter'] as $hook_name => $hook_object ) {
     292                        self::$hooks_saved['wp_filter'][ $hook_name ] = clone $hook_object;
     293                }
     294        }
     295
     296        /**
     297         * Restores the hook-related globals to their state at setUp()
     298         * so that future tests aren't affected by hooks set during this last test.
     299         *
     300         * @global array $wp_actions
     301         * @global array $wp_current_filter
     302         * @global array $wp_filter
     303         * @return void
     304         */
     305        protected function _restore_hooks() {
     306                $globals = array( 'wp_actions', 'wp_current_filter' );
     307                foreach ( $globals as $key ) {
     308                        if ( isset( self::$hooks_saved[ $key ] ) ) {
     309                                $GLOBALS[ $key ] = self::$hooks_saved[ $key ];
     310                        }
     311                }
     312                if ( isset( self::$hooks_saved['wp_filter'] ) ) {
     313                        $GLOBALS['wp_filter'] = array();
     314                        foreach ( self::$hooks_saved['wp_filter'] as $hook_name => $hook_object ) {
     315                                $GLOBALS['wp_filter'][ $hook_name ] = clone $hook_object;
     316                        }
     317                }
     318        }
     319
     320        static function flush_cache() {
     321                global $wp_object_cache;
     322                $wp_object_cache->group_ops      = array();
     323                $wp_object_cache->stats          = array();
     324                $wp_object_cache->memcache_debug = array();
     325                $wp_object_cache->cache          = array();
     326                if ( method_exists( $wp_object_cache, '__remoteset' ) ) {
     327                        $wp_object_cache->__remoteset();
     328                }
     329                wp_cache_flush();
     330                wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites', 'site-details', 'blog_meta' ) );
     331                wp_cache_add_non_persistent_groups( array( 'comment', 'counts', 'plugins' ) );
     332        }
     333
     334        /**
     335         * Clean up any registered meta keys.
     336         *
     337         * @since 5.1.0
     338         *
     339         * @global array $wp_meta_keys
     340         */
     341        function unregister_all_meta_keys() {
     342                global $wp_meta_keys;
     343                if ( ! is_array( $wp_meta_keys ) ) {
     344                        return;
     345                }
     346                foreach ( $wp_meta_keys as $object_type => $type_keys ) {
     347                        foreach ( $type_keys as $object_subtype => $subtype_keys ) {
     348                                foreach ( $subtype_keys as $key => $value ) {
     349                                        unregister_meta_key( $object_type, $key, $object_subtype );
     350                                }
     351                        }
     352                }
     353        }
     354
     355        function start_transaction() {
     356                global $wpdb;
     357                $wpdb->query( 'SET autocommit = 0;' );
     358                $wpdb->query( 'START TRANSACTION;' );
     359                add_filter( 'query', array( $this, '_create_temporary_tables' ) );
     360                add_filter( 'query', array( $this, '_drop_temporary_tables' ) );
     361        }
     362
     363        /**
     364         * Commit the queries in a transaction.
     365         *
     366         * @since 4.1.0
     367         */
     368        public static function commit_transaction() {
     369                global $wpdb;
     370                $wpdb->query( 'COMMIT;' );
     371        }
     372
     373        function _create_temporary_tables( $query ) {
     374                if ( 'CREATE TABLE' === substr( trim( $query ), 0, 12 ) ) {
     375                        return substr_replace( trim( $query ), 'CREATE TEMPORARY TABLE', 0, 12 );
     376                }
     377                return $query;
     378        }
     379
     380        function _drop_temporary_tables( $query ) {
     381                if ( 'DROP TABLE' === substr( trim( $query ), 0, 10 ) ) {
     382                        return substr_replace( trim( $query ), 'DROP TEMPORARY TABLE', 0, 10 );
     383                }
     384                return $query;
     385        }
     386
     387        function get_wp_die_handler( $handler ) {
     388                return array( $this, 'wp_die_handler' );
     389        }
     390
     391        function wp_die_handler( $message ) {
     392                if ( ! is_scalar( $message ) ) {
     393                        $message = '0';
     394                }
     395
     396                throw new WPDieException( $message );
     397        }
     398
     399        function expectDeprecated() {
     400                $annotations = $this->getAnnotations();
     401                foreach ( array( 'class', 'method' ) as $depth ) {
     402                        if ( ! empty( $annotations[ $depth ]['expectedDeprecated'] ) ) {
     403                                $this->expected_deprecated = array_merge( $this->expected_deprecated, $annotations[ $depth ]['expectedDeprecated'] );
     404                        }
     405                        if ( ! empty( $annotations[ $depth ]['expectedIncorrectUsage'] ) ) {
     406                                $this->expected_doing_it_wrong = array_merge( $this->expected_doing_it_wrong, $annotations[ $depth ]['expectedIncorrectUsage'] );
     407                        }
     408                }
     409                add_action( 'deprecated_function_run', array( $this, 'deprecated_function_run' ) );
     410                add_action( 'deprecated_argument_run', array( $this, 'deprecated_function_run' ) );
     411                add_action( 'deprecated_hook_run', array( $this, 'deprecated_function_run' ) );
     412                add_action( 'doing_it_wrong_run', array( $this, 'doing_it_wrong_run' ) );
     413                add_action( 'deprecated_function_trigger_error', '__return_false' );
     414                add_action( 'deprecated_argument_trigger_error', '__return_false' );
     415                add_action( 'deprecated_hook_trigger_error', '__return_false' );
     416                add_action( 'doing_it_wrong_trigger_error', '__return_false' );
     417        }
     418
     419        function expectedDeprecated() {
     420                $errors = array();
     421
     422                $not_caught_deprecated = array_diff( $this->expected_deprecated, $this->caught_deprecated );
     423                foreach ( $not_caught_deprecated as $not_caught ) {
     424                        $errors[] = "Failed to assert that $not_caught triggered a deprecated notice";
     425                }
     426
     427                $unexpected_deprecated = array_diff( $this->caught_deprecated, $this->expected_deprecated );
     428                foreach ( $unexpected_deprecated as $unexpected ) {
     429                        $errors[] = "Unexpected deprecated notice for $unexpected";
     430                }
     431
     432                $not_caught_doing_it_wrong = array_diff( $this->expected_doing_it_wrong, $this->caught_doing_it_wrong );
     433                foreach ( $not_caught_doing_it_wrong as $not_caught ) {
     434                        $errors[] = "Failed to assert that $not_caught triggered an incorrect usage notice";
     435                }
     436
     437                $unexpected_doing_it_wrong = array_diff( $this->caught_doing_it_wrong, $this->expected_doing_it_wrong );
     438                foreach ( $unexpected_doing_it_wrong as $unexpected ) {
     439                        $errors[] = "Unexpected incorrect usage notice for $unexpected";
     440                }
     441
     442                // Perform an assertion, but only if there are expected or unexpected deprecated calls or wrongdoings
     443                if ( ! empty( $this->expected_deprecated ) ||
     444                         ! empty( $this->expected_doing_it_wrong ) ||
     445                         ! empty( $this->caught_deprecated ) ||
     446                         ! empty( $this->caught_doing_it_wrong ) ) {
     447                        $this->assertEmpty( $errors, implode( "\n", $errors ) );
     448                }
     449        }
     450
     451        /**
     452         * Declare an expected `_deprecated_function()` or `_deprecated_argument()` call from within a test.
     453         *
     454         * @since 4.2.0
     455         *
     456         * @param string $deprecated Name of the function, method, class, or argument that is deprecated. Must match
     457         *                           first parameter of the `_deprecated_function()` or `_deprecated_argument()` call.
     458         */
     459        public function setExpectedDeprecated( $deprecated ) {
     460                array_push( $this->expected_deprecated, $deprecated );
     461        }
     462
     463        /**
     464         * Declare an expected `_doing_it_wrong()` call from within a test.
     465         *
     466         * @since 4.2.0
     467         *
     468         * @param string $deprecated Name of the function, method, or class that appears in the first argument of the
     469         *                           source `_doing_it_wrong()` call.
     470         */
     471        public function setExpectedIncorrectUsage( $doing_it_wrong ) {
     472                array_push( $this->expected_doing_it_wrong, $doing_it_wrong );
     473        }
     474
     475        /**
     476         * PHPUnit 6+ compatibility shim.
     477         *
     478         * @param mixed      $exception
     479         * @param string     $message
     480         * @param int|string $code
     481         */
     482        public function setExpectedException( $exception, $message = '', $code = null ) {
     483                if ( method_exists( 'PHPUnit_Framework_TestCase', 'setExpectedException' ) ) {
     484                        parent::setExpectedException( $exception, $message, $code );
     485                } else {
     486                        $this->expectException( $exception );
     487                        if ( '' !== $message ) {
     488                                $this->expectExceptionMessage( $message );
     489                        }
     490                        if ( null !== $code ) {
     491                                $this->expectExceptionCode( $code );
     492                        }
     493                }
     494        }
     495
     496        function deprecated_function_run( $function ) {
     497                if ( ! in_array( $function, $this->caught_deprecated ) ) {
     498                        $this->caught_deprecated[] = $function;
     499                }
     500        }
     501
     502        function doing_it_wrong_run( $function ) {
     503                if ( ! in_array( $function, $this->caught_doing_it_wrong ) ) {
     504                        $this->caught_doing_it_wrong[] = $function;
     505                }
     506        }
     507
     508        function assertWPError( $actual, $message = '' ) {
     509                $this->assertInstanceOf( 'WP_Error', $actual, $message );
     510        }
     511
     512        function assertNotWPError( $actual, $message = '' ) {
     513                if ( is_wp_error( $actual ) && '' === $message ) {
     514                        $message = $actual->get_error_message();
     515                }
     516                $this->assertNotInstanceOf( 'WP_Error', $actual, $message );
     517        }
     518
     519        function assertIXRError( $actual, $message = '' ) {
     520                $this->assertInstanceOf( 'IXR_Error', $actual, $message );
     521        }
     522
     523        function assertNotIXRError( $actual, $message = '' ) {
     524                if ( $actual instanceof IXR_Error && '' === $message ) {
     525                        $message = $actual->message;
     526                }
     527                $this->assertNotInstanceOf( 'IXR_Error', $actual, $message );
     528        }
     529
     530        function assertEqualFields( $object, $fields ) {
     531                foreach ( $fields as $field_name => $field_value ) {
     532                        if ( $object->$field_name != $field_value ) {
     533                                $this->fail();
     534                        }
     535                }
     536        }
     537
     538        function assertDiscardWhitespace( $expected, $actual ) {
     539                $this->assertEquals( preg_replace( '/\s*/', '', $expected ), preg_replace( '/\s*/', '', $actual ) );
     540        }
     541
     542        /**
     543         * Asserts that the contents of two un-keyed, single arrays are equal, without accounting for the order of elements.
     544         *
     545         * @since 3.5.0
     546         *
     547         * @param array $expected Expected array.
     548         * @param array $actual   Array to check.
     549         */
     550        function assertEqualSets( $expected, $actual ) {
     551                sort( $expected );
     552                sort( $actual );
     553                $this->assertEquals( $expected, $actual );
     554        }
     555
     556        /**
     557         * Asserts that the contents of two keyed, single arrays are equal, without accounting for the order of elements.
     558         *
     559         * @since 4.1.0
     560         *
     561         * @param array $expected Expected array.
     562         * @param array $actual   Array to check.
     563         */
     564        function assertEqualSetsWithIndex( $expected, $actual ) {
     565                ksort( $expected );
     566                ksort( $actual );
     567                $this->assertEquals( $expected, $actual );
     568        }
     569
     570        /**
     571         * Asserts that the given variable is a multidimensional array, and that all arrays are non-empty.
     572         *
     573         * @since 4.8.0
     574         *
     575         * @param array $array Array to check.
     576         */
     577        function assertNonEmptyMultidimensionalArray( $array ) {
     578                $this->assertTrue( is_array( $array ) );
     579                $this->assertNotEmpty( $array );
     580
     581                foreach ( $array as $sub_array ) {
     582                        $this->assertTrue( is_array( $sub_array ) );
     583                        $this->assertNotEmpty( $sub_array );
     584                }
     585        }
     586
     587        /**
     588         * Sets the global state to as if a given URL has been requested.
     589         *
     590         * This sets:
     591         * - The super globals.
     592         * - The globals.
     593         * - The query variables.
     594         * - The main query.
     595         *
     596         * @since 3.5.0
     597         *
     598         * @param string $url The URL for the request.
     599         */
     600        function go_to( $url ) {
     601                // note: the WP and WP_Query classes like to silently fetch parameters
     602                // from all over the place (globals, GET, etc), which makes it tricky
     603                // to run them more than once without very carefully clearing everything
     604                $_GET = $_POST = array();
     605                foreach ( array( 'query_string', 'id', 'postdata', 'authordata', 'day', 'currentmonth', 'page', 'pages', 'multipage', 'more', 'numpages', 'pagenow' ) as $v ) {
     606                        if ( isset( $GLOBALS[ $v ] ) ) {
     607                                unset( $GLOBALS[ $v ] );
     608                        }
     609                }
     610                $parts = parse_url( $url );
     611                if ( isset( $parts['scheme'] ) ) {
     612                        $req = isset( $parts['path'] ) ? $parts['path'] : '';
     613                        if ( isset( $parts['query'] ) ) {
     614                                $req .= '?' . $parts['query'];
     615                                // parse the url query vars into $_GET
     616                                parse_str( $parts['query'], $_GET );
     617                        }
     618                } else {
     619                        $req = $url;
     620                }
     621                if ( ! isset( $parts['query'] ) ) {
     622                        $parts['query'] = '';
     623                }
     624
     625                $_SERVER['REQUEST_URI'] = $req;
     626                unset( $_SERVER['PATH_INFO'] );
     627
     628                self::flush_cache();
     629                unset( $GLOBALS['wp_query'], $GLOBALS['wp_the_query'] );
     630                $GLOBALS['wp_the_query'] = new WP_Query();
     631                $GLOBALS['wp_query']     = $GLOBALS['wp_the_query'];
     632
     633                $public_query_vars  = $GLOBALS['wp']->public_query_vars;
     634                $private_query_vars = $GLOBALS['wp']->private_query_vars;
     635
     636                $GLOBALS['wp']                     = new WP();
     637                $GLOBALS['wp']->public_query_vars  = $public_query_vars;
     638                $GLOBALS['wp']->private_query_vars = $private_query_vars;
     639
     640                _cleanup_query_vars();
     641
     642                $GLOBALS['wp']->main( $parts['query'] );
     643        }
     644
     645        /**
     646         * Allows tests to be skipped on single or multisite installs by using @group annotations.
     647         *
     648         * This is a custom extension of the PHPUnit requirements handling.
     649         *
     650         * Contains legacy code for skipping tests that are associated with an open Trac ticket. Core tests no longer
     651         * support this behaviour.
     652         *
     653         * @since 3.5.0
     654         */
     655        protected function checkRequirements() {
     656                parent::checkRequirements();
     657
     658                $annotations = $this->getAnnotations();
     659
     660                $groups = array();
     661                if ( ! empty( $annotations['class']['group'] ) ) {
     662                        $groups = array_merge( $groups, $annotations['class']['group'] );
     663                }
     664                if ( ! empty( $annotations['method']['group'] ) ) {
     665                        $groups = array_merge( $groups, $annotations['method']['group'] );
     666                }
     667
     668                if ( ! empty( $groups ) ) {
     669                        if ( in_array( 'ms-required', $groups, true ) ) {
     670                                $this->skipWithoutMultisite();
     671                        }
     672
     673                        if ( in_array( 'ms-excluded', $groups, true ) ) {
     674                                $this->skipWithMultisite();
     675                        }
     676                }
     677
     678                // Core tests no longer check against open Trac tickets, but others using WP_UnitTestCase may do so.
     679                if ( defined( 'WP_RUN_CORE_TESTS' ) && WP_RUN_CORE_TESTS ) {
     680                        return;
     681                }
     682
     683                if ( WP_TESTS_FORCE_KNOWN_BUGS ) {
     684                        return;
     685                }
     686                $tickets = PHPUnit_Util_Test::getTickets( get_class( $this ), $this->getName( false ) );
     687                foreach ( $tickets as $ticket ) {
     688                        if ( is_numeric( $ticket ) ) {
     689                                $this->knownWPBug( $ticket );
     690                        } elseif ( 'Plugin' == substr( $ticket, 0, 6 ) ) {
     691                                $ticket = substr( $ticket, 6 );
     692                                if ( $ticket && is_numeric( $ticket ) ) {
     693                                        $this->knownPluginBug( $ticket );
     694                                }
     695                        }
     696                }
     697        }
     698
     699        /**
     700         * Skips the current test if there is an open Trac ticket associated with it.
     701         *
     702         * @since 3.5.0
     703         *
     704         * @param int $ticket_id Ticket number.
     705         */
     706        function knownWPBug( $ticket_id ) {
     707                if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( $ticket_id, self::$forced_tickets ) ) {
     708                        return;
     709                }
     710                if ( ! TracTickets::isTracTicketClosed( 'https://core.trac.wordpress.org', $ticket_id ) ) {
     711                        $this->markTestSkipped( sprintf( 'WordPress Ticket #%d is not fixed', $ticket_id ) );
     712                }
     713        }
     714
     715        /**
     716         * Skips the current test if there is an open Unit Test Trac ticket associated with it.
     717         *
     718         * @since 3.5.0
     719         *
     720         * @deprecated No longer used since the Unit Test Trac was merged into the Core Trac.
     721         *
     722         * @param int $ticket_id Ticket number.
     723         */
     724        function knownUTBug( $ticket_id ) {
     725                return;
     726        }
     727
     728        /**
     729         * Skips the current test if there is an open Plugin Trac ticket associated with it.
     730         *
     731         * @since 3.5.0
     732         *
     733         * @param int $ticket_id Ticket number.
     734         */
     735        function knownPluginBug( $ticket_id ) {
     736                if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( 'Plugin' . $ticket_id, self::$forced_tickets ) ) {
     737                        return;
     738                }
     739                if ( ! TracTickets::isTracTicketClosed( 'https://plugins.trac.wordpress.org', $ticket_id ) ) {
     740                        $this->markTestSkipped( sprintf( 'WordPress Plugin Ticket #%d is not fixed', $ticket_id ) );
     741                }
     742        }
     743
     744        /**
     745         * Adds a Trac ticket number to the `$forced_tickets` property.
     746         *
     747         * @since 3.5.0
     748         *
     749         * @param int $ticket Ticket number.
     750         */
     751        public static function forceTicket( $ticket ) {
     752                self::$forced_tickets[] = $ticket;
     753        }
     754
     755        /**
     756         * Custom preparations for the PHPUnit process isolation template.
     757         *
     758         * When restoring global state between tests, PHPUnit defines all the constants that were already defined, and then
     759         * includes included files. This does not work with WordPress, as the included files define the constants.
     760         *
     761         * This method defines the constants after including files.
     762         *
     763         * @param Text_Template $template
     764         */
     765        function prepareTemplate( Text_Template $template ) {
     766                $template->setVar( array( 'constants' => '' ) );
     767                $template->setVar( array( 'wp_constants' => PHPUnit_Util_GlobalState::getConstantsAsString() ) );
     768                parent::prepareTemplate( $template );
     769        }
     770
     771        /**
     772         * Creates a unique temporary file name.
     773         *
     774         * The directory in which the file is created depends on the environment configuration.
     775         *
     776         * @since 3.5.0
     777         *
     778         * @return string|bool Path on success, else false.
     779         */
     780        function temp_filename() {
     781                $tmp_dir = '';
     782                $dirs    = array( 'TMP', 'TMPDIR', 'TEMP' );
     783                foreach ( $dirs as $dir ) {
     784                        if ( isset( $_ENV[ $dir ] ) && ! empty( $_ENV[ $dir ] ) ) {
     785                                $tmp_dir = $dir;
     786                                break;
     787                        }
     788                }
     789                if ( empty( $tmp_dir ) ) {
     790                        $tmp_dir = '/tmp';
     791                }
     792                $tmp_dir = realpath( $tmp_dir );
     793                return tempnam( $tmp_dir, 'wpunit' );
     794        }
     795
     796        /**
     797         * Checks each of the WP_Query is_* functions/properties against expected boolean value.
     798         *
     799         * Any properties that are listed by name as parameters will be expected to be true; all others are
     800         * expected to be false. For example, assertQueryTrue('is_single', 'is_feed') means is_single()
     801         * and is_feed() must be true and everything else must be false to pass.
     802         *
     803         * @since 2.5.0
     804         * @since 3.8.0 Moved from `Tests_Query_Conditionals` to `WP_UnitTestCase`.
     805         *
     806         * @param string $prop,... Any number of WP_Query properties that are expected to be true for the current request.
     807         */
     808        function assertQueryTrue() {
     809                global $wp_query;
     810                $all  = array(
     811                        'is_404',
     812                        'is_admin',
     813                        'is_archive',
     814                        'is_attachment',
     815                        'is_author',
     816                        'is_category',
     817                        'is_comment_feed',
     818                        'is_date',
     819                        'is_day',
     820                        'is_embed',
     821                        'is_feed',
     822                        'is_front_page',
     823                        'is_home',
     824                        'is_month',
     825                        'is_page',
     826                        'is_paged',
     827                        'is_post_type_archive',
     828                        'is_posts_page',
     829                        'is_preview',
     830                        'is_robots',
     831                        'is_search',
     832                        'is_single',
     833                        'is_singular',
     834                        'is_tag',
     835                        'is_tax',
     836                        'is_time',
     837                        'is_trackback',
     838                        'is_year',
     839                );
     840                $true = func_get_args();
     841
     842                foreach ( $true as $true_thing ) {
     843                        $this->assertContains( $true_thing, $all, "Unknown conditional: {$true_thing}." );
     844                }
     845
     846                $passed  = true;
     847                $message = '';
     848
     849                foreach ( $all as $query_thing ) {
     850                        $result = is_callable( $query_thing ) ? call_user_func( $query_thing ) : $wp_query->$query_thing;
     851
     852                        if ( in_array( $query_thing, $true ) ) {
     853                                if ( ! $result ) {
     854                                        $message .= $query_thing . ' is false but is expected to be true. ' . PHP_EOL;
     855                                        $passed   = false;
     856                                }
     857                        } elseif ( $result ) {
     858                                $message .= $query_thing . ' is true but is expected to be false. ' . PHP_EOL;
     859                                $passed   = false;
     860                        }
     861                }
     862
     863                if ( ! $passed ) {
     864                        $this->fail( $message );
     865                }
     866        }
     867
     868        /**
     869         * Selectively deletes a file.
     870         *
     871         * Does not delete a file if its path is set in the `$ignore_files` property.
     872         *
     873         * @param string $file File path.
     874         */
     875        function unlink( $file ) {
     876                $exists = is_file( $file );
     877                if ( $exists && ! in_array( $file, self::$ignore_files ) ) {
     878                        //error_log( $file );
     879                        unlink( $file );
     880                } elseif ( ! $exists ) {
     881                        $this->fail( "Trying to delete a file that doesn't exist: $file" );
     882                }
     883        }
     884
     885        /**
     886         * Selectively deletes files from a directory.
     887         *
     888         * Does not delete files if their paths are set in the `$ignore_files` property.
     889         *
     890         * @param string $path Directory path.
     891         */
     892        function rmdir( $path ) {
     893                $files = $this->files_in_dir( $path );
     894                foreach ( $files as $file ) {
     895                        if ( ! in_array( $file, self::$ignore_files ) ) {
     896                                $this->unlink( $file );
     897                        }
     898                }
     899        }
     900
     901        /**
     902         * Deletes files added to the `uploads` directory during tests.
     903         *
     904         * This method works in tandem with the `setUp()` and `rmdir()` methods:
     905         * - `setUp()` scans the `uploads` directory before every test, and stores its contents inside of the
     906         *   `$ignore_files` property.
     907         * - `rmdir()` and its helper methods only delete files that are not listed in the `$ignore_files` property. If
     908         *   called during `tearDown()` in tests, this will only delete files added during the previously run test.
     909         */
     910        function remove_added_uploads() {
     911                $uploads = wp_upload_dir();
     912                $this->rmdir( $uploads['basedir'] );
     913        }
     914
     915        /**
     916         * Returns a list of all files contained inside a directory.
     917         *
     918         * @since 4.0.0
     919         *
     920         * @param string $dir Path to the directory to scan.
     921         *
     922         * @return array List of file paths.
     923         */
     924        function files_in_dir( $dir ) {
     925                $files = array();
     926
     927                $iterator = new RecursiveDirectoryIterator( $dir );
     928                $objects  = new RecursiveIteratorIterator( $iterator );
     929                foreach ( $objects as $name => $object ) {
     930                        if ( is_file( $name ) ) {
     931                                $files[] = $name;
     932                        }
     933                }
     934
     935                return $files;
     936        }
     937
     938        /**
     939         * Returns a list of all files contained inside the `uploads` directory.
     940         *
     941         * @since 4.0.0
     942         *
     943         * @return array List of file paths.
     944         */
     945        function scan_user_uploads() {
     946                static $files = array();
     947                if ( ! empty( $files ) ) {
     948                        return $files;
     949                }
     950
     951                $uploads = wp_upload_dir();
     952                $files   = $this->files_in_dir( $uploads['basedir'] );
     953                return $files;
     954        }
     955
     956        /**
     957         * Deletes all directories contained inside a directory.
     958         *
     959         * @since 4.1.0
     960         *
     961         * @param string $path Path to the directory to scan.
     962         */
     963        function delete_folders( $path ) {
     964                $this->matched_dirs = array();
     965                if ( ! is_dir( $path ) ) {
     966                        return;
     967                }
     968
     969                $this->scandir( $path );
     970                foreach ( array_reverse( $this->matched_dirs ) as $dir ) {
     971                        rmdir( $dir );
     972                }
     973                rmdir( $path );
     974        }
     975
     976        /**
     977         * Retrieves all directories contained inside a directory and stores them in the `$matched_dirs` property. Hidden
     978         * directories are ignored.
     979         *
     980         * This is a helper for the `delete_folders()` method.
     981         *
     982         * @since 4.1.0
     983         *
     984         * @param string $dir Path to the directory to scan.
     985         */
     986        function scandir( $dir ) {
     987                foreach ( scandir( $dir ) as $path ) {
     988                        if ( 0 !== strpos( $path, '.' ) && is_dir( $dir . '/' . $path ) ) {
     989                                $this->matched_dirs[] = $dir . '/' . $path;
     990                                $this->scandir( $dir . '/' . $path );
     991                        }
     992                }
     993        }
     994
     995        /**
     996         * Converts a microtime string into a float.
     997         *
     998         * @since 4.1.0
     999         *
     1000         * @param string $microtime Time string generated by `microtime()`.
     1001         *
     1002         * @return float `microtime()` output as a float.
     1003         */
     1004        protected function _microtime_to_float( $microtime ) {
     1005                $time_array = explode( ' ', $microtime );
     1006                return array_sum( $time_array );
     1007        }
     1008
     1009        /**
     1010         * Deletes a user from the database in a Multisite-agnostic way.
     1011         *
     1012         * @since 4.3.0
     1013         *
     1014         * @param int $user_id User ID.
     1015         *
     1016         * @return bool True if the user was deleted.
     1017         */
     1018        public static function delete_user( $user_id ) {
     1019                if ( is_multisite() ) {
     1020                        return wpmu_delete_user( $user_id );
     1021                } else {
     1022                        return wp_delete_user( $user_id );
     1023                }
     1024        }
     1025
     1026        /**
     1027         * Resets permalinks and flushes rewrites.
     1028         *
     1029         * @since 4.4.0
     1030         *
     1031         * @global WP_Rewrite $wp_rewrite
     1032         *
     1033         * @param string $structure Optional. Permalink structure to set. Default empty.
     1034         */
     1035        public function set_permalink_structure( $structure = '' ) {
     1036                global $wp_rewrite;
     1037
     1038                $wp_rewrite->init();
     1039                $wp_rewrite->set_permalink_structure( $structure );
     1040                $wp_rewrite->flush_rules();
     1041        }
     1042
     1043        /**
     1044         * Creates an attachment post from an uploaded file.
     1045         *
     1046         * @since 4.4.0
     1047         *
     1048         * @param array $upload         Array of information about the uploaded file, provided by wp_upload_bits().
     1049         * @param int   $parent_post_id Optional. Parent post ID.
     1050         *
     1051         * @return int|WP_Error The attachment ID on success. The value 0 or WP_Error on failure.
     1052         */
     1053        function _make_attachment( $upload, $parent_post_id = 0 ) {
     1054                $type = '';
     1055                if ( ! empty( $upload['type'] ) ) {
     1056                        $type = $upload['type'];
     1057                } else {
     1058                        $mime = wp_check_filetype( $upload['file'] );
     1059                        if ( $mime ) {
     1060                                $type = $mime['type'];
     1061                        }
     1062                }
     1063
     1064                $attachment = array(
     1065                        'post_title'     => basename( $upload['file'] ),
     1066                        'post_content'   => '',
     1067                        'post_type'      => 'attachment',
     1068                        'post_parent'    => $parent_post_id,
     1069                        'post_mime_type' => $type,
     1070                        'guid'           => $upload['url'],
     1071                );
     1072
     1073                $id = wp_insert_attachment( $attachment, $upload['file'], $parent_post_id );
     1074                wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
     1075                return $id;
     1076        }
     1077
     1078        /**
     1079         * Updates the modified and modified GMT date of a post in the database.
     1080         *
     1081         * @since 4.8.0
     1082         *
     1083         * @global wpdb $wpdb WordPress database abstraction object.
     1084         *
     1085         * @param int    $post_id Post ID.
     1086         * @param string $date    Post date, in the format YYYY-MM-DD HH:MM:SS.
     1087         *
     1088         * @return int|false 1 on success, or false on error.
     1089         */
     1090        protected function update_post_modified( $post_id, $date ) {
     1091                global $wpdb;
     1092                return $wpdb->update(
     1093                        $wpdb->posts,
     1094                        array(
     1095                                'post_modified'     => $date,
     1096                                'post_modified_gmt' => $date,
     1097                        ),
     1098                        array(
     1099                                'ID' => $post_id,
     1100                        ),
     1101                        array(
     1102                                '%s',
     1103                                '%s',
     1104                        ),
     1105                        array(
     1106                                '%d',
     1107                        )
     1108                );
     1109        }
     1110}
  • tests/phpunit/includes/bootstrap.php

     
    118118// Delete any default posts & related data
    119119_delete_all_posts();
    120120
    121 require dirname( __FILE__ ) . '/testcase.php';
     121if ( version_compare( tests_get_phpunit_version(), '7.0', '>=' ) ) {
     122        require dirname( __FILE__ ) . '/phpunit7/testcase.php';
     123} else {
     124        require dirname( __FILE__ ) . '/testcase.php';
     125}
     126
    122127require dirname( __FILE__ ) . '/testcase-rest-api.php';
    123128require dirname( __FILE__ ) . '/testcase-rest-controller.php';
    124129require dirname( __FILE__ ) . '/testcase-rest-post-type-controller.php';
     
    144149 * If WP_TESTS_FORCE_KNOWN_BUGS is already set in wp-tests-config.php, then
    145150 * how you call phpunit has no effect.
    146151 */
    147 class WP_PHPUnit_Util_Getopt extends PHPUnit_Util_Getopt {
     152class WP_PHPUnit_Util_Getopt {
    148153        protected $longOptions = array(
    149154                'exclude-group=',
    150155                'group=',
     156                'verbose=',
    151157        );
    152158        function __construct( $argv ) {
    153159                array_shift( $argv );
     
    157163                        next( $argv );
    158164                        try {
    159165                                if ( strlen( $arg ) > 1 && $arg[0] === '-' && $arg[1] === '-' ) {
    160                                         PHPUnit_Util_Getopt::parseLongOption( substr( $arg, 2 ), $this->longOptions, $options, $argv );
     166                                        self::parseLongOption( substr( $arg, 2 ), $this->longOptions, $options, $argv );
    161167                                }
    162168                        } catch ( PHPUnit_Framework_Exception $e ) {
    163169                                // Enforcing recognized arguments or correctly formed arguments is
     
    208214                        echo PHP_EOL;
    209215                }
    210216        }
     217
     218        /**
     219         * Copied from https://raw.githubusercontent.com/sebastianbergmann/phpunit/6.5.7/src/Util/Getopt.php
     220         *
     221         * @param $arg
     222         * @param $long_options
     223         * @param $opts
     224         * @param $args
     225         */
     226        protected static function parseLongOption( $arg, $long_options, &$opts, &$args ) {
     227                $count   = count( $long_options );
     228                $list    = explode( '=', $arg );
     229                $opt     = $list[0];
     230                $opt_arg = null;
     231
     232                if ( count( $list ) > 1 ) {
     233                        $opt_arg = $list[1];
     234                }
     235
     236                $opt_len = strlen( $opt );
     237
     238                for ( $i = 0; $i < $count; $i++ ) {
     239                        $long_opt  = $long_options[ $i ];
     240                        $opt_start = substr( $long_opt, 0, $opt_len );
     241
     242                        if ( $opt_start != $opt ) {
     243                                continue;
     244                        }
     245
     246                        $opt_rest = substr( $long_opt, $opt_len );
     247
     248                        if ( $opt_rest != '' && $opt[0] != '=' && $i + 1 < $count &&
     249                                $opt == substr( $long_options[ $i + 1 ], 0, $opt_len ) ) {
     250                                throw new Exception(
     251                                        "option --$opt is ambiguous"
     252                                );
     253                        }
     254
     255                        if ( substr( $long_opt, -1 ) == '=' ) {
     256                                if ( substr( $long_opt, -2 ) != '==' ) {
     257                                        if ( ! strlen( $opt_arg ) ) {
     258                                                if ( false === $opt_arg = current( $args ) ) {
     259                                                        throw new Exception(
     260                                                                "option --$opt requires an argument"
     261                                                        );
     262                                                }
     263                                                next( $args );
     264                                        }
     265                                }
     266                        } elseif ( $opt_arg ) {
     267                                throw new Exception(
     268                                        "option --$opt doesn't allow an argument"
     269                                );
     270                        }
     271
     272                        $full_option = '--' . preg_replace( '/={1,2}$/', '', $long_opt );
     273                        $opts[]      = array( $full_option, $opt_arg );
     274
     275                        return;
     276                }
     277
     278                throw new Exception( "unrecognized option --$opt" );
     279        }
    211280}
    212281new WP_PHPUnit_Util_Getopt( $_SERVER['argv'] );
  • tests/phpunit/includes/functions.php

     
    11<?php
    22
    33/**
     4 * Retrieves PHPUnit runner version.
     5 */
     6function tests_get_phpunit_version() {
     7        if ( class_exists( 'PHPUnit_Runner_Version' ) ) {
     8                $version = PHPUnit_Runner_Version::id();
     9        } elseif ( class_exists( 'PHPUnit\Runner\Version' ) ) {
     10                // Must be parsable by PHP 5.2.x.
     11                $version = call_user_func( 'PHPUnit\Runner\Version::id' );
     12        } else {
     13                $version = 0;
     14        }
     15
     16        return $version;
     17}
     18
     19/**
    420 * Resets various `$_SERVER` variables that can get altered during tests.
    521 */
    622function tests_reset__SERVER() {
  • tests/phpunit/includes/listener-loader.php

     
     1<?php
     2
     3if ( version_compare( tests_get_phpunit_version(), '7.1', '>=' ) ) {
     4        require dirname( __FILE__ ) . '/phpunit7/speed-trap-listener.php';
     5} else {
     6        require dirname( __FILE__ ) . '/speed-trap-listener.php';
     7}
  • tests/phpunit/includes/phpunit6-compat.php

     
    1515        class_alias( 'PHPUnit\Util\GlobalState', 'PHPUnit_Util_GlobalState' );
    1616        class_alias( 'PHPUnit\Util\Getopt', 'PHPUnit_Util_Getopt' );
    1717
    18         class PHPUnit_Util_Test extends PHPUnit\Util\Test {
     18        class PHPUnit_Util_Test {
    1919
    2020                public static function getTickets( $className, $methodName ) {
    21                         $annotations = self::parseTestMethodAnnotations( $className, $methodName );
     21                        $annotations = PHPUnit\Util\Test::parseTestMethodAnnotations( $className, $methodName );
    2222
    2323                        $tickets = array();
    2424
  • tests/phpunit/includes/phpunit7/speed-trap-listener.php

     
     1<?php
     2
     3/**
     4 * A PHPUnit TestListener that exposes your slowest running tests by outputting
     5 * results directly to the console.
     6 */
     7class SpeedTrapListener implements PHPUnit_Framework_TestListener {
     8
     9        /**
     10         * Internal tracking for test suites.
     11         *
     12         * Increments as more suites are run, then decremented as they finish. All
     13         * suites have been run when returns to 0.
     14         *
     15         * @var integer
     16         */
     17        protected $suites = 0;
     18
     19        /**
     20         * Time in milliseconds at which a test will be considered "slow" and be
     21         * reported by this listener.
     22         *
     23         * @var int
     24         */
     25        protected $slowThreshold;
     26
     27        /**
     28         * Number of tests to report on for slowness.
     29         *
     30         * @var int
     31         */
     32        protected $reportLength;
     33
     34        /**
     35         * Collection of slow tests.
     36         *
     37         * @var array
     38         */
     39        protected $slow = array();
     40
     41        /**
     42         * Construct a new instance.
     43         *
     44         * @param array $options
     45         */
     46        public function __construct( array $options = array() ) {
     47                $this->loadOptions( $options );
     48        }
     49
     50        /**
     51         * An error occurred.
     52         *
     53         * @param PHPUnit_Framework_Test $test
     54         * @param Exception              $e
     55         * @param float                   $time
     56         */
     57        public function addError( PHPUnit\Framework\Test $test, Throwable $t, float $time ): void {
     58        }
     59
     60        /**
     61         * A warning occurred.
     62         *
     63         * @param PHPUnit_Framework_Test    $test
     64         * @param PHPUnit_Framework_Warning $e
     65         * @param float                     $time
     66         * @since Method available since Release 5.1.0
     67         */
     68        public function addWarning( PHPUnit\Framework\Test $test, PHPUnit\Framework\Warning $e, float $time ): void {
     69        }
     70
     71        /**
     72         * A failure occurred.
     73         *
     74         * @param PHPUnit_Framework_Test                 $test
     75         * @param PHPUnit_Framework_AssertionFailedError $e
     76         * @param float                                   $time
     77         */
     78        public function addFailure( PHPUnit\Framework\Test $test, PHPUnit\Framework\AssertionFailedError $e, float $time ): void {
     79        }
     80
     81        /**
     82         * Incomplete test.
     83         *
     84         * @param PHPUnit_Framework_Test $test
     85         * @param Exception              $e
     86         * @param float                   $time
     87         */
     88        public function addIncompleteTest( PHPUnit\Framework\Test $test, Throwable $t, float $time ): void {
     89        }
     90
     91        /**
     92         * Risky test.
     93         *
     94         * @param PHPUnit_Framework_Test $test
     95         * @param Exception              $e
     96         * @param float                   $time
     97         * @since  Method available since Release 4.0.0
     98         */
     99        public function addRiskyTest( PHPUnit\Framework\Test $test, Throwable $t, float $time ): void {
     100        }
     101
     102        /**
     103         * Skipped test.
     104         *
     105         * @param PHPUnit_Framework_Test $test
     106         * @param Exception              $e
     107         * @param float                   $time
     108         */
     109        public function addSkippedTest( PHPUnit\Framework\Test $test, Throwable $t, float $time ): void {
     110        }
     111
     112        /**
     113         * A test started.
     114         *
     115         * @param PHPUnit_Framework_Test $test
     116         */
     117        public function startTest( PHPUnit\Framework\Test $test ): void {
     118        }
     119
     120        /**
     121         * A test ended.
     122         *
     123         * @param PHPUnit_Framework_Test $test
     124         * @param float                   $time
     125         */
     126        public function endTest( PHPUnit\Framework\Test $test, float $time ): void {
     127                if ( ! $test instanceof PHPUnit_Framework_TestCase ) {
     128                        return;
     129                }
     130
     131                $time      = $this->toMilliseconds( $time );
     132                $threshold = $this->getSlowThreshold( $test );
     133
     134                if ( $this->isSlow( $time, $threshold ) ) {
     135                        $this->addSlowTest( $test, $time );
     136                }
     137        }
     138
     139        /**
     140         * A test suite started.
     141         *
     142         * @param PHPUnit_Framework_TestSuite $suite
     143         */
     144        public function startTestSuite( PHPUnit\Framework\TestSuite $suite ): void {
     145                $this->suites++;
     146        }
     147
     148        /**
     149         * A test suite ended.
     150         *
     151         * @param PHPUnit_Framework_TestSuite $suite
     152         */
     153        public function endTestSuite( PHPUnit\Framework\TestSuite $suite ): void {
     154                $this->suites--;
     155
     156                if ( 0 === $this->suites && $this->hasSlowTests() ) {
     157                        arsort( $this->slow ); // Sort longest running tests to the top
     158
     159                        $this->renderHeader();
     160                        $this->renderBody();
     161                        $this->renderFooter();
     162                }
     163        }
     164
     165        /**
     166         * Whether the given test execution time is considered slow.
     167         *
     168         * @param int $time          Test execution time in milliseconds
     169         * @param int $slowThreshold Test execution time at which a test should be considered slow (milliseconds)
     170         * @return bool
     171         */
     172        protected function isSlow( $time, $slowThreshold ) {
     173                return $time >= $slowThreshold;
     174        }
     175
     176        /**
     177         * Stores a test as slow.
     178         *
     179         * @param PHPUnit_Framework_TestCase $test
     180         * @param int                         $time Test execution time in milliseconds
     181         */
     182        protected function addSlowTest( PHPUnit_Framework_TestCase $test, $time ) {
     183                $label = $this->makeLabel( $test );
     184
     185                $this->slow[ $label ] = $time;
     186        }
     187
     188        /**
     189         * Whether at least one test has been considered slow.
     190         *
     191         * @return bool
     192         */
     193        protected function hasSlowTests() {
     194                return ! empty( $this->slow );
     195        }
     196
     197        /**
     198         * Convert PHPUnit's reported test time (microseconds) to milliseconds.
     199         *
     200         * @param float $time
     201         * @return int
     202         */
     203        protected function toMilliseconds( $time ) {
     204                return (int) round( $time * 1000 );
     205        }
     206
     207        /**
     208         * Label for describing a test.
     209         *
     210         * @param PHPUnit_Framework_TestCase $test
     211         * @return string
     212         */
     213        protected function makeLabel( PHPUnit_Framework_TestCase $test ) {
     214                return sprintf( '%s:%s', get_class( $test ), $test->getName() );
     215        }
     216
     217        /**
     218         * Calculate number of slow tests to report about.
     219         *
     220         * @return int
     221         */
     222        protected function getReportLength() {
     223                return min( count( $this->slow ), $this->reportLength );
     224        }
     225
     226        /**
     227         * Find how many slow tests occurred that won't be shown due to list length.
     228         *
     229         * @return int Number of hidden slow tests
     230         */
     231        protected function getHiddenCount() {
     232                $total   = count( $this->slow );
     233                $showing = $this->getReportLength( $this->slow );
     234
     235                $hidden = 0;
     236                if ( $total > $showing ) {
     237                        $hidden = $total - $showing;
     238                }
     239
     240                return $hidden;
     241        }
     242
     243        /**
     244         * Renders slow test report header.
     245         */
     246        protected function renderHeader() {
     247                echo sprintf( "\n\nYou should really fix these slow tests (>%sms)...\n", $this->slowThreshold );
     248        }
     249
     250        /**
     251         * Renders slow test report body.
     252         */
     253        protected function renderBody() {
     254                $slowTests = $this->slow;
     255
     256                $length = $this->getReportLength( $slowTests );
     257                for ( $i = 1; $i <= $length; ++$i ) {
     258                        $label = key( $slowTests );
     259                        $time  = array_shift( $slowTests );
     260
     261                        echo sprintf( " %s. %sms to run %s\n", $i, $time, $label );
     262                }
     263        }
     264
     265        /**
     266         * Renders slow test report footer.
     267         */
     268        protected function renderFooter() {
     269                if ( $hidden = $this->getHiddenCount( $this->slow ) ) {
     270                        echo sprintf( '...and there %s %s more above your threshold hidden from view', $hidden == 1 ? 'is' : 'are', $hidden );
     271                }
     272        }
     273
     274        /**
     275         * Populate options into class internals.
     276         *
     277         * @param array $options
     278         */
     279        protected function loadOptions( array $options ) {
     280                $this->slowThreshold = isset( $options['slowThreshold'] ) ? $options['slowThreshold'] : 500;
     281                $this->reportLength  = isset( $options['reportLength'] ) ? $options['reportLength'] : 10;
     282        }
     283
     284        /**
     285         * Get slow test threshold for given test. A TestCase can override the
     286         * suite-wide slow threshold by using the annotation @slowThreshold with
     287         * the threshold value in milliseconds.
     288         *
     289         * The following test will only be considered slow when its execution time
     290         * reaches 5000ms (5 seconds):
     291         *
     292         * <code>
     293         *
     294         * @slowThreshold 5000
     295         * public function testLongRunningProcess() {}
     296         * </code>
     297         *
     298         * @param PHPUnit_Framework_TestCase $test
     299         * @return int
     300         */
     301        protected function getSlowThreshold( PHPUnit_Framework_TestCase $test ) {
     302                $ann = $test->getAnnotations();
     303
     304                return isset( $ann['method']['slowThreshold'][0] ) ? $ann['method']['slowThreshold'][0] : $this->slowThreshold;
     305        }
     306}
  • tests/phpunit/includes/phpunit7/speed-trap-listener.php

     
     1<?php
     2
     3/**
     4 * A PHPUnit TestListener that exposes your slowest running tests by outputting
     5 * results directly to the console.
     6 */
     7class SpeedTrapListener implements PHPUnit_Framework_TestListener {
     8
     9        /**
     10         * Internal tracking for test suites.
     11         *
     12         * Increments as more suites are run, then decremented as they finish. All
     13         * suites have been run when returns to 0.
     14         *
     15         * @var integer
     16         */
     17        protected $suites = 0;
     18
     19        /**
     20         * Time in milliseconds at which a test will be considered "slow" and be
     21         * reported by this listener.
     22         *
     23         * @var int
     24         */
     25        protected $slowThreshold;
     26
     27        /**
     28         * Number of tests to report on for slowness.
     29         *
     30         * @var int
     31         */
     32        protected $reportLength;
     33
     34        /**
     35         * Collection of slow tests.
     36         *
     37         * @var array
     38         */
     39        protected $slow = array();
     40
     41        /**
     42         * Construct a new instance.
     43         *
     44         * @param array $options
     45         */
     46        public function __construct( array $options = array() ) {
     47                $this->loadOptions( $options );
     48        }
     49
     50        /**
     51         * An error occurred.
     52         *
     53         * @param PHPUnit_Framework_Test $test
     54         * @param Exception              $e
     55         * @param float                   $time
     56         */
     57        public function addError( PHPUnit\Framework\Test $test, Throwable $t, float $time ): void {
     58        }
     59
     60        /**
     61         * A warning occurred.
     62         *
     63         * @param PHPUnit_Framework_Test    $test
     64         * @param PHPUnit_Framework_Warning $e
     65         * @param float                     $time
     66         * @since Method available since Release 5.1.0
     67         */
     68        public function addWarning( PHPUnit\Framework\Test $test, PHPUnit\Framework\Warning $e, float $time ): void {
     69        }
     70
     71        /**
     72         * A failure occurred.
     73         *
     74         * @param PHPUnit_Framework_Test                 $test
     75         * @param PHPUnit_Framework_AssertionFailedError $e
     76         * @param float                                   $time
     77         */
     78        public function addFailure( PHPUnit\Framework\Test $test, PHPUnit\Framework\AssertionFailedError $e, float $time ): void {
     79        }
     80
     81        /**
     82         * Incomplete test.
     83         *
     84         * @param PHPUnit_Framework_Test $test
     85         * @param Exception              $e
     86         * @param float                   $time
     87         */
     88        public function addIncompleteTest( PHPUnit\Framework\Test $test, Throwable $t, float $time ): void {
     89        }
     90
     91        /**
     92         * Risky test.
     93         *
     94         * @param PHPUnit_Framework_Test $test
     95         * @param Exception              $e
     96         * @param float                   $time
     97         * @since  Method available since Release 4.0.0
     98         */
     99        public function addRiskyTest( PHPUnit\Framework\Test $test, Throwable $t, float $time ): void {
     100        }
     101
     102        /**
     103         * Skipped test.
     104         *
     105         * @param PHPUnit_Framework_Test $test
     106         * @param Exception              $e
     107         * @param float                   $time
     108         */
     109        public function addSkippedTest( PHPUnit\Framework\Test $test, Throwable $t, float $time ): void {
     110        }
     111
     112        /**
     113         * A test started.
     114         *
     115         * @param PHPUnit_Framework_Test $test
     116         */
     117        public function startTest( PHPUnit\Framework\Test $test ): void {
     118        }
     119
     120        /**
     121         * A test ended.
     122         *
     123         * @param PHPUnit_Framework_Test $test
     124         * @param float                   $time
     125         */
     126        public function endTest( PHPUnit\Framework\Test $test, float $time ): void {
     127                if ( ! $test instanceof PHPUnit_Framework_TestCase ) {
     128                        return;
     129                }
     130
     131                $time      = $this->toMilliseconds( $time );
     132                $threshold = $this->getSlowThreshold( $test );
     133
     134                if ( $this->isSlow( $time, $threshold ) ) {
     135                        $this->addSlowTest( $test, $time );
     136                }
     137        }
     138
     139        /**
     140         * A test suite started.
     141         *
     142         * @param PHPUnit_Framework_TestSuite $suite
     143         */
     144        public function startTestSuite( PHPUnit\Framework\TestSuite $suite ): void {
     145                $this->suites++;
     146        }
     147
     148        /**
     149         * A test suite ended.
     150         *
     151         * @param PHPUnit_Framework_TestSuite $suite
     152         */
     153        public function endTestSuite( PHPUnit\Framework\TestSuite $suite ): void {
     154                $this->suites--;
     155
     156                if ( 0 === $this->suites && $this->hasSlowTests() ) {
     157                        arsort( $this->slow ); // Sort longest running tests to the top
     158
     159                        $this->renderHeader();
     160                        $this->renderBody();
     161                        $this->renderFooter();
     162                }
     163        }
     164
     165        /**
     166         * Whether the given test execution time is considered slow.
     167         *
     168         * @param int $time          Test execution time in milliseconds
     169         * @param int $slowThreshold Test execution time at which a test should be considered slow (milliseconds)
     170         * @return bool
     171         */
     172        protected function isSlow( $time, $slowThreshold ) {
     173                return $time >= $slowThreshold;
     174        }
     175
     176        /**
     177         * Stores a test as slow.
     178         *
     179         * @param PHPUnit_Framework_TestCase $test
     180         * @param int                         $time Test execution time in milliseconds
     181         */
     182        protected function addSlowTest( PHPUnit_Framework_TestCase $test, $time ) {
     183                $label = $this->makeLabel( $test );
     184
     185                $this->slow[ $label ] = $time;
     186        }
     187
     188        /**
     189         * Whether at least one test has been considered slow.
     190         *
     191         * @return bool
     192         */
     193        protected function hasSlowTests() {
     194                return ! empty( $this->slow );
     195        }
     196
     197        /**
     198         * Convert PHPUnit's reported test time (microseconds) to milliseconds.
     199         *
     200         * @param float $time
     201         * @return int
     202         */
     203        protected function toMilliseconds( $time ) {
     204                return (int) round( $time * 1000 );
     205        }
     206
     207        /**
     208         * Label for describing a test.
     209         *
     210         * @param PHPUnit_Framework_TestCase $test
     211         * @return string
     212         */
     213        protected function makeLabel( PHPUnit_Framework_TestCase $test ) {
     214                return sprintf( '%s:%s', get_class( $test ), $test->getName() );
     215        }
     216
     217        /**
     218         * Calculate number of slow tests to report about.
     219         *
     220         * @return int
     221         */
     222        protected function getReportLength() {
     223                return min( count( $this->slow ), $this->reportLength );
     224        }
     225
     226        /**
     227         * Find how many slow tests occurred that won't be shown due to list length.
     228         *
     229         * @return int Number of hidden slow tests
     230         */
     231        protected function getHiddenCount() {
     232                $total   = count( $this->slow );
     233                $showing = $this->getReportLength( $this->slow );
     234
     235                $hidden = 0;
     236                if ( $total > $showing ) {
     237                        $hidden = $total - $showing;
     238                }
     239
     240                return $hidden;
     241        }
     242
     243        /**
     244         * Renders slow test report header.
     245         */
     246        protected function renderHeader() {
     247                echo sprintf( "\n\nYou should really fix these slow tests (>%sms)...\n", $this->slowThreshold );
     248        }
     249
     250        /**
     251         * Renders slow test report body.
     252         */
     253        protected function renderBody() {
     254                $slowTests = $this->slow;
     255
     256                $length = $this->getReportLength( $slowTests );
     257                for ( $i = 1; $i <= $length; ++$i ) {
     258                        $label = key( $slowTests );
     259                        $time  = array_shift( $slowTests );
     260
     261                        echo sprintf( " %s. %sms to run %s\n", $i, $time, $label );
     262                }
     263        }
     264
     265        /**
     266         * Renders slow test report footer.
     267         */
     268        protected function renderFooter() {
     269                if ( $hidden = $this->getHiddenCount( $this->slow ) ) {
     270                        echo sprintf( '...and there %s %s more above your threshold hidden from view', $hidden == 1 ? 'is' : 'are', $hidden );
     271                }
     272        }
     273
     274        /**
     275         * Populate options into class internals.
     276         *
     277         * @param array $options
     278         */
     279        protected function loadOptions( array $options ) {
     280                $this->slowThreshold = isset( $options['slowThreshold'] ) ? $options['slowThreshold'] : 500;
     281                $this->reportLength  = isset( $options['reportLength'] ) ? $options['reportLength'] : 10;
     282        }
     283
     284        /**
     285         * Get slow test threshold for given test. A TestCase can override the
     286         * suite-wide slow threshold by using the annotation @slowThreshold with
     287         * the threshold value in milliseconds.
     288         *
     289         * The following test will only be considered slow when its execution time
     290         * reaches 5000ms (5 seconds):
     291         *
     292         * <code>
     293         *
     294         * @slowThreshold 5000
     295         * public function testLongRunningProcess() {}
     296         * </code>
     297         *
     298         * @param PHPUnit_Framework_TestCase $test
     299         * @return int
     300         */
     301        protected function getSlowThreshold( PHPUnit_Framework_TestCase $test ) {
     302                $ann = $test->getAnnotations();
     303
     304                return isset( $ann['method']['slowThreshold'][0] ) ? $ann['method']['slowThreshold'][0] : $this->slowThreshold;
     305        }
     306}
  • tests/phpunit/includes/phpunit7/testcase.php

     
     1<?php
     2
     3require_once dirname( dirname( __FILE__ ) ) . '/abstract-testcase.php';
     4
     5/**
     6 * Defines a basic fixture to run multiple tests.
     7 *
     8 * Resets the state of the WordPress installation before and after every test.
     9 *
     10 * Includes utility functions and assertions useful for testing WordPress.
     11 *
     12 * All WordPress unit tests should inherit from this class.
     13 */
     14class WP_UnitTestCase extends WP_UnitTestCase_Base {
     15        /**
     16         * Asserts that a condition is not false.
     17         *
     18         * This method has been backported from a more recent PHPUnit version, as tests running on PHP 5.2 use
     19         * PHPUnit 3.6.x.
     20         *
     21         * @since 4.7.4
     22         *
     23         * @param bool   $condition Condition to check.
     24         * @param string $message   Optional. Message to display when the assertion fails.
     25         *
     26         * @throws PHPUnit_Framework_AssertionFailedError
     27         */
     28        public static function assertNotFalse( $condition, string $message = '' ): void {
     29                self::assertThat( $condition, self::logicalNot( self::isFalse() ), $message );
     30        }
     31}
  • tests/phpunit/includes/testcase-canonical.php

     
    236236        public function assertCanonical( $test_url, $expected, $ticket = 0, $expected_doing_it_wrong = array() ) {
    237237                $this->expected_doing_it_wrong = array_merge( $this->expected_doing_it_wrong, (array) $expected_doing_it_wrong );
    238238
    239                 $ticket_ref = ( $ticket > 0 ) ? 'Ticket #' . $ticket : null;
     239                $ticket_ref = ( $ticket > 0 ) ? 'Ticket #' . $ticket : '';
    240240
    241241                if ( is_string( $expected ) ) {
    242242                        $expected = array( 'url' => $expected );
  • tests/phpunit/includes/testcase.php

     
    11<?php
    22
    3 require_once dirname( __FILE__ ) . '/factory.php';
    4 require_once dirname( __FILE__ ) . '/trac.php';
     3require_once dirname( __FILE__ ) . '/abstract-testcase.php';
    54
     5
    66/**
    77 * Defines a basic fixture to run multiple tests.
    88 *
     
    1212 *
    1313 * All WordPress unit tests should inherit from this class.
    1414 */
    15 class WP_UnitTestCase extends PHPUnit_Framework_TestCase {
     15class WP_UnitTestCase extends WP_UnitTestCase_Base {
    1616
    17         protected static $forced_tickets   = array();
    18         protected $expected_deprecated     = array();
    19         protected $caught_deprecated       = array();
    20         protected $expected_doing_it_wrong = array();
    21         protected $caught_doing_it_wrong   = array();
    22 
    23         protected static $hooks_saved = array();
    24         protected static $ignore_files;
    25 
    26         function __isset( $name ) {
    27                 return 'factory' === $name;
    28         }
    29 
    30         function __get( $name ) {
    31                 if ( 'factory' === $name ) {
    32                         return self::factory();
    33                 }
    34         }
    35 
    3617        /**
    37          * Fetches the factory object for generating WordPress fixtures.
    38          *
    39          * @return WP_UnitTest_Factory The fixture factory.
    40          */
    41         protected static function factory() {
    42                 static $factory = null;
    43                 if ( ! $factory ) {
    44                         $factory = new WP_UnitTest_Factory();
    45                 }
    46                 return $factory;
    47         }
    48 
    49         public static function get_called_class() {
    50                 if ( function_exists( 'get_called_class' ) ) {
    51                         return get_called_class();
    52                 }
    53 
    54                 // PHP 5.2 only
    55                 $backtrace = debug_backtrace();
    56                 // [0] WP_UnitTestCase::get_called_class()
    57                 // [1] WP_UnitTestCase::setUpBeforeClass()
    58                 if ( 'call_user_func' === $backtrace[2]['function'] ) {
    59                         return $backtrace[2]['args'][0][0];
    60                 }
    61                 return $backtrace[2]['class'];
    62         }
    63 
    64         public static function setUpBeforeClass() {
    65                 global $wpdb;
    66 
    67                 $wpdb->suppress_errors = false;
    68                 $wpdb->show_errors     = true;
    69                 $wpdb->db_connect();
    70                 ini_set( 'display_errors', 1 );
    71 
    72                 parent::setUpBeforeClass();
    73 
    74                 $c = self::get_called_class();
    75                 if ( ! method_exists( $c, 'wpSetUpBeforeClass' ) ) {
    76                         self::commit_transaction();
    77                         return;
    78                 }
    79 
    80                 call_user_func( array( $c, 'wpSetUpBeforeClass' ), self::factory() );
    81 
    82                 self::commit_transaction();
    83         }
    84 
    85         public static function tearDownAfterClass() {
    86                 parent::tearDownAfterClass();
    87 
    88                 _delete_all_data();
    89                 self::flush_cache();
    90 
    91                 $c = self::get_called_class();
    92                 if ( ! method_exists( $c, 'wpTearDownAfterClass' ) ) {
    93                         self::commit_transaction();
    94                         return;
    95                 }
    96 
    97                 call_user_func( array( $c, 'wpTearDownAfterClass' ) );
    98 
    99                 self::commit_transaction();
    100         }
    101 
    102         function setUp() {
    103                 set_time_limit( 0 );
    104 
    105                 if ( ! self::$ignore_files ) {
    106                         self::$ignore_files = $this->scan_user_uploads();
    107                 }
    108 
    109                 if ( ! self::$hooks_saved ) {
    110                         $this->_backup_hooks();
    111                 }
    112 
    113                 global $wp_rewrite;
    114 
    115                 $this->clean_up_global_scope();
    116 
    117                 /*
    118                  * When running core tests, ensure that post types and taxonomies
    119                  * are reset for each test. We skip this step for non-core tests,
    120                  * given the large number of plugins that register post types and
    121                  * taxonomies at 'init'.
    122                  */
    123                 if ( defined( 'WP_RUN_CORE_TESTS' ) && WP_RUN_CORE_TESTS ) {
    124                         $this->reset_post_types();
    125                         $this->reset_taxonomies();
    126                         $this->reset_post_statuses();
    127                         $this->reset__SERVER();
    128 
    129                         if ( $wp_rewrite->permalink_structure ) {
    130                                 $this->set_permalink_structure( '' );
    131                         }
    132                 }
    133 
    134                 $this->start_transaction();
    135                 $this->expectDeprecated();
    136                 add_filter( 'wp_die_handler', array( $this, 'get_wp_die_handler' ) );
    137         }
    138 
    139         /**
    140          * Detect post-test failure conditions.
    141          *
    142          * We use this method to detect expectedDeprecated and expectedIncorrectUsage annotations.
    143          *
    144          * @since 4.2.0
    145          */
    146         protected function assertPostConditions() {
    147                 $this->expectedDeprecated();
    148         }
    149 
    150         /**
    151          * After a test method runs, reset any state in WordPress the test method might have changed.
    152          */
    153         function tearDown() {
    154                 global $wpdb, $wp_query, $wp;
    155                 $wpdb->query( 'ROLLBACK' );
    156                 if ( is_multisite() ) {
    157                         while ( ms_is_switched() ) {
    158                                 restore_current_blog();
    159                         }
    160                 }
    161                 $wp_query = new WP_Query();
    162                 $wp       = new WP();
    163 
    164                 // Reset globals related to the post loop and `setup_postdata()`.
    165                 $post_globals = array( 'post', 'id', 'authordata', 'currentday', 'currentmonth', 'page', 'pages', 'multipage', 'more', 'numpages' );
    166                 foreach ( $post_globals as $global ) {
    167                         $GLOBALS[ $global ] = null;
    168                 }
    169 
    170                 $this->unregister_all_meta_keys();
    171                 remove_theme_support( 'html5' );
    172                 remove_filter( 'query', array( $this, '_create_temporary_tables' ) );
    173                 remove_filter( 'query', array( $this, '_drop_temporary_tables' ) );
    174                 remove_filter( 'wp_die_handler', array( $this, 'get_wp_die_handler' ) );
    175                 $this->_restore_hooks();
    176                 wp_set_current_user( 0 );
    177         }
    178 
    179         function clean_up_global_scope() {
    180                 $_GET  = array();
    181                 $_POST = array();
    182                 self::flush_cache();
    183         }
    184 
    185         /**
    186          * Allow tests to be skipped on some automated runs
    187          *
    188          * For test runs on Travis for something other than trunk/master
    189          * we want to skip tests that only need to run for master.
    190          */
    191         public function skipOnAutomatedBranches() {
    192                 // gentenv can be disabled
    193                 if ( ! function_exists( 'getenv' ) ) {
    194                         return false;
    195                 }
    196 
    197                 // https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
    198                 $travis_branch       = getenv( 'TRAVIS_BRANCH' );
    199                 $travis_pull_request = getenv( 'TRAVIS_PULL_REQUEST' );
    200 
    201                 if ( false !== $travis_pull_request && 'master' !== $travis_branch ) {
    202                         $this->markTestSkipped( 'For automated test runs, this test is only run on trunk/master' );
    203                 }
    204         }
    205 
    206         /**
    207          * Allow tests to be skipped when Multisite is not in use.
    208          *
    209          * Use in conjunction with the ms-required group.
    210          */
    211         public function skipWithoutMultisite() {
    212                 if ( ! is_multisite() ) {
    213                         $this->markTestSkipped( 'Test only runs on Multisite' );
    214                 }
    215         }
    216 
    217         /**
    218          * Allow tests to be skipped when Multisite is in use.
    219          *
    220          * Use in conjunction with the ms-excluded group.
    221          */
    222         public function skipWithMultisite() {
    223                 if ( is_multisite() ) {
    224                         $this->markTestSkipped( 'Test does not run on Multisite' );
    225                 }
    226         }
    227 
    228         /**
    229          * Unregister existing post types and register defaults.
    230          *
    231          * Run before each test in order to clean up the global scope, in case
    232          * a test forgets to unregister a post type on its own, or fails before
    233          * it has a chance to do so.
    234          */
    235         protected function reset_post_types() {
    236                 foreach ( get_post_types( array(), 'objects' ) as $pt ) {
    237                         if ( empty( $pt->tests_no_auto_unregister ) ) {
    238                                 _unregister_post_type( $pt->name );
    239                         }
    240                 }
    241                 create_initial_post_types();
    242         }
    243 
    244         /**
    245          * Unregister existing taxonomies and register defaults.
    246          *
    247          * Run before each test in order to clean up the global scope, in case
    248          * a test forgets to unregister a taxonomy on its own, or fails before
    249          * it has a chance to do so.
    250          */
    251         protected function reset_taxonomies() {
    252                 foreach ( get_taxonomies() as $tax ) {
    253                         _unregister_taxonomy( $tax );
    254                 }
    255                 create_initial_taxonomies();
    256         }
    257 
    258         /**
    259          * Unregister non-built-in post statuses.
    260          */
    261         protected function reset_post_statuses() {
    262                 foreach ( get_post_stati( array( '_builtin' => false ) ) as $post_status ) {
    263                         _unregister_post_status( $post_status );
    264                 }
    265         }
    266 
    267         /**
    268          * Reset `$_SERVER` variables
    269          */
    270         protected function reset__SERVER() {
    271                 tests_reset__SERVER();
    272         }
    273 
    274         /**
    275          * Saves the action and filter-related globals so they can be restored later.
    276          *
    277          * Stores $wp_actions, $wp_current_filter, and $wp_filter
    278          * on a class variable so they can be restored on tearDown() using _restore_hooks().
    279          *
    280          * @global array $wp_actions
    281          * @global array $wp_current_filter
    282          * @global array $wp_filter
    283          * @return void
    284          */
    285         protected function _backup_hooks() {
    286                 $globals = array( 'wp_actions', 'wp_current_filter' );
    287                 foreach ( $globals as $key ) {
    288                         self::$hooks_saved[ $key ] = $GLOBALS[ $key ];
    289                 }
    290                 self::$hooks_saved['wp_filter'] = array();
    291                 foreach ( $GLOBALS['wp_filter'] as $hook_name => $hook_object ) {
    292                         self::$hooks_saved['wp_filter'][ $hook_name ] = clone $hook_object;
    293                 }
    294         }
    295 
    296         /**
    297          * Restores the hook-related globals to their state at setUp()
    298          * so that future tests aren't affected by hooks set during this last test.
    299          *
    300          * @global array $wp_actions
    301          * @global array $wp_current_filter
    302          * @global array $wp_filter
    303          * @return void
    304          */
    305         protected function _restore_hooks() {
    306                 $globals = array( 'wp_actions', 'wp_current_filter' );
    307                 foreach ( $globals as $key ) {
    308                         if ( isset( self::$hooks_saved[ $key ] ) ) {
    309                                 $GLOBALS[ $key ] = self::$hooks_saved[ $key ];
    310                         }
    311                 }
    312                 if ( isset( self::$hooks_saved['wp_filter'] ) ) {
    313                         $GLOBALS['wp_filter'] = array();
    314                         foreach ( self::$hooks_saved['wp_filter'] as $hook_name => $hook_object ) {
    315                                 $GLOBALS['wp_filter'][ $hook_name ] = clone $hook_object;
    316                         }
    317                 }
    318         }
    319 
    320         static function flush_cache() {
    321                 global $wp_object_cache;
    322                 $wp_object_cache->group_ops      = array();
    323                 $wp_object_cache->stats          = array();
    324                 $wp_object_cache->memcache_debug = array();
    325                 $wp_object_cache->cache          = array();
    326                 if ( method_exists( $wp_object_cache, '__remoteset' ) ) {
    327                         $wp_object_cache->__remoteset();
    328                 }
    329                 wp_cache_flush();
    330                 wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites', 'site-details', 'blog_meta' ) );
    331                 wp_cache_add_non_persistent_groups( array( 'comment', 'counts', 'plugins' ) );
    332         }
    333 
    334         /**
    335          * Clean up any registered meta keys.
    336          *
    337          * @since 5.1.0
    338          *
    339          * @global array $wp_meta_keys
    340          */
    341         function unregister_all_meta_keys() {
    342                 global $wp_meta_keys;
    343                 if ( ! is_array( $wp_meta_keys ) ) {
    344                         return;
    345                 }
    346                 foreach ( $wp_meta_keys as $object_type => $type_keys ) {
    347                         foreach ( $type_keys as $object_subtype => $subtype_keys ) {
    348                                 foreach ( $subtype_keys as $key => $value ) {
    349                                         unregister_meta_key( $object_type, $key, $object_subtype );
    350                                 }
    351                         }
    352                 }
    353         }
    354 
    355         function start_transaction() {
    356                 global $wpdb;
    357                 $wpdb->query( 'SET autocommit = 0;' );
    358                 $wpdb->query( 'START TRANSACTION;' );
    359                 add_filter( 'query', array( $this, '_create_temporary_tables' ) );
    360                 add_filter( 'query', array( $this, '_drop_temporary_tables' ) );
    361         }
    362 
    363         /**
    364          * Commit the queries in a transaction.
    365          *
    366          * @since 4.1.0
    367          */
    368         public static function commit_transaction() {
    369                 global $wpdb;
    370                 $wpdb->query( 'COMMIT;' );
    371         }
    372 
    373         function _create_temporary_tables( $query ) {
    374                 if ( 'CREATE TABLE' === substr( trim( $query ), 0, 12 ) ) {
    375                         return substr_replace( trim( $query ), 'CREATE TEMPORARY TABLE', 0, 12 );
    376                 }
    377                 return $query;
    378         }
    379 
    380         function _drop_temporary_tables( $query ) {
    381                 if ( 'DROP TABLE' === substr( trim( $query ), 0, 10 ) ) {
    382                         return substr_replace( trim( $query ), 'DROP TEMPORARY TABLE', 0, 10 );
    383                 }
    384                 return $query;
    385         }
    386 
    387         function get_wp_die_handler( $handler ) {
    388                 return array( $this, 'wp_die_handler' );
    389         }
    390 
    391         function wp_die_handler( $message ) {
    392                 if ( ! is_scalar( $message ) ) {
    393                         $message = '0';
    394                 }
    395 
    396                 throw new WPDieException( $message );
    397         }
    398 
    399         function expectDeprecated() {
    400                 $annotations = $this->getAnnotations();
    401                 foreach ( array( 'class', 'method' ) as $depth ) {
    402                         if ( ! empty( $annotations[ $depth ]['expectedDeprecated'] ) ) {
    403                                 $this->expected_deprecated = array_merge( $this->expected_deprecated, $annotations[ $depth ]['expectedDeprecated'] );
    404                         }
    405                         if ( ! empty( $annotations[ $depth ]['expectedIncorrectUsage'] ) ) {
    406                                 $this->expected_doing_it_wrong = array_merge( $this->expected_doing_it_wrong, $annotations[ $depth ]['expectedIncorrectUsage'] );
    407                         }
    408                 }
    409                 add_action( 'deprecated_function_run', array( $this, 'deprecated_function_run' ) );
    410                 add_action( 'deprecated_argument_run', array( $this, 'deprecated_function_run' ) );
    411                 add_action( 'deprecated_hook_run', array( $this, 'deprecated_function_run' ) );
    412                 add_action( 'doing_it_wrong_run', array( $this, 'doing_it_wrong_run' ) );
    413                 add_action( 'deprecated_function_trigger_error', '__return_false' );
    414                 add_action( 'deprecated_argument_trigger_error', '__return_false' );
    415                 add_action( 'deprecated_hook_trigger_error', '__return_false' );
    416                 add_action( 'doing_it_wrong_trigger_error', '__return_false' );
    417         }
    418 
    419         function expectedDeprecated() {
    420                 $errors = array();
    421 
    422                 $not_caught_deprecated = array_diff( $this->expected_deprecated, $this->caught_deprecated );
    423                 foreach ( $not_caught_deprecated as $not_caught ) {
    424                         $errors[] = "Failed to assert that $not_caught triggered a deprecated notice";
    425                 }
    426 
    427                 $unexpected_deprecated = array_diff( $this->caught_deprecated, $this->expected_deprecated );
    428                 foreach ( $unexpected_deprecated as $unexpected ) {
    429                         $errors[] = "Unexpected deprecated notice for $unexpected";
    430                 }
    431 
    432                 $not_caught_doing_it_wrong = array_diff( $this->expected_doing_it_wrong, $this->caught_doing_it_wrong );
    433                 foreach ( $not_caught_doing_it_wrong as $not_caught ) {
    434                         $errors[] = "Failed to assert that $not_caught triggered an incorrect usage notice";
    435                 }
    436 
    437                 $unexpected_doing_it_wrong = array_diff( $this->caught_doing_it_wrong, $this->expected_doing_it_wrong );
    438                 foreach ( $unexpected_doing_it_wrong as $unexpected ) {
    439                         $errors[] = "Unexpected incorrect usage notice for $unexpected";
    440                 }
    441 
    442                 // Perform an assertion, but only if there are expected or unexpected deprecated calls or wrongdoings
    443                 if ( ! empty( $this->expected_deprecated ) ||
    444                         ! empty( $this->expected_doing_it_wrong ) ||
    445                         ! empty( $this->caught_deprecated ) ||
    446                         ! empty( $this->caught_doing_it_wrong ) ) {
    447                         $this->assertEmpty( $errors, implode( "\n", $errors ) );
    448                 }
    449         }
    450 
    451         /**
    452          * Declare an expected `_deprecated_function()` or `_deprecated_argument()` call from within a test.
    453          *
    454          * @since 4.2.0
    455          *
    456          * @param string $deprecated Name of the function, method, class, or argument that is deprecated. Must match
    457          *                           first parameter of the `_deprecated_function()` or `_deprecated_argument()` call.
    458          */
    459         public function setExpectedDeprecated( $deprecated ) {
    460                 array_push( $this->expected_deprecated, $deprecated );
    461         }
    462 
    463         /**
    464          * Declare an expected `_doing_it_wrong()` call from within a test.
    465          *
    466          * @since 4.2.0
    467          *
    468          * @param string $deprecated Name of the function, method, or class that appears in the first argument of the
    469          *                           source `_doing_it_wrong()` call.
    470          */
    471         public function setExpectedIncorrectUsage( $doing_it_wrong ) {
    472                 array_push( $this->expected_doing_it_wrong, $doing_it_wrong );
    473         }
    474 
    475         /**
    476          * PHPUnit 6+ compatibility shim.
    477          *
    478          * @param mixed      $exception
    479          * @param string     $message
    480          * @param int|string $code
    481          */
    482         public function setExpectedException( $exception, $message = '', $code = null ) {
    483                 if ( method_exists( 'PHPUnit_Framework_TestCase', 'setExpectedException' ) ) {
    484                         parent::setExpectedException( $exception, $message, $code );
    485                 } else {
    486                         $this->expectException( $exception );
    487                         if ( '' !== $message ) {
    488                                 $this->expectExceptionMessage( $message );
    489                         }
    490                         if ( null !== $code ) {
    491                                 $this->expectExceptionCode( $code );
    492                         }
    493                 }
    494         }
    495 
    496         function deprecated_function_run( $function ) {
    497                 if ( ! in_array( $function, $this->caught_deprecated ) ) {
    498                         $this->caught_deprecated[] = $function;
    499                 }
    500         }
    501 
    502         function doing_it_wrong_run( $function ) {
    503                 if ( ! in_array( $function, $this->caught_doing_it_wrong ) ) {
    504                         $this->caught_doing_it_wrong[] = $function;
    505                 }
    506         }
    507 
    508         function assertWPError( $actual, $message = '' ) {
    509                 $this->assertInstanceOf( 'WP_Error', $actual, $message );
    510         }
    511 
    512         function assertNotWPError( $actual, $message = '' ) {
    513                 if ( is_wp_error( $actual ) && '' === $message ) {
    514                         $message = $actual->get_error_message();
    515                 }
    516                 $this->assertNotInstanceOf( 'WP_Error', $actual, $message );
    517         }
    518 
    519         function assertIXRError( $actual, $message = '' ) {
    520                 $this->assertInstanceOf( 'IXR_Error', $actual, $message );
    521         }
    522 
    523         function assertNotIXRError( $actual, $message = '' ) {
    524                 if ( $actual instanceof IXR_Error && '' === $message ) {
    525                         $message = $actual->message;
    526                 }
    527                 $this->assertNotInstanceOf( 'IXR_Error', $actual, $message );
    528         }
    529 
    530         function assertEqualFields( $object, $fields ) {
    531                 foreach ( $fields as $field_name => $field_value ) {
    532                         if ( $object->$field_name != $field_value ) {
    533                                 $this->fail();
    534                         }
    535                 }
    536         }
    537 
    538         function assertDiscardWhitespace( $expected, $actual ) {
    539                 $this->assertEquals( preg_replace( '/\s*/', '', $expected ), preg_replace( '/\s*/', '', $actual ) );
    540         }
    541 
    542         /**
    543          * Asserts that the contents of two un-keyed, single arrays are equal, without accounting for the order of elements.
    544          *
    545          * @since 3.5.0
    546          *
    547          * @param array $expected Expected array.
    548          * @param array $actual   Array to check.
    549          */
    550         function assertEqualSets( $expected, $actual ) {
    551                 sort( $expected );
    552                 sort( $actual );
    553                 $this->assertEquals( $expected, $actual );
    554         }
    555 
    556         /**
    557          * Asserts that the contents of two keyed, single arrays are equal, without accounting for the order of elements.
    558          *
    559          * @since 4.1.0
    560          *
    561          * @param array $expected Expected array.
    562          * @param array $actual   Array to check.
    563          */
    564         function assertEqualSetsWithIndex( $expected, $actual ) {
    565                 ksort( $expected );
    566                 ksort( $actual );
    567                 $this->assertEquals( $expected, $actual );
    568         }
    569 
    570         /**
    571          * Asserts that the given variable is a multidimensional array, and that all arrays are non-empty.
    572          *
    573          * @since 4.8.0
    574          *
    575          * @param array $array Array to check.
    576          */
    577         function assertNonEmptyMultidimensionalArray( $array ) {
    578                 $this->assertTrue( is_array( $array ) );
    579                 $this->assertNotEmpty( $array );
    580 
    581                 foreach ( $array as $sub_array ) {
    582                         $this->assertTrue( is_array( $sub_array ) );
    583                         $this->assertNotEmpty( $sub_array );
    584                 }
    585         }
    586 
    587         /**
    58818         * Asserts that a condition is not false.
    58919         *
    59020         * This method has been backported from a more recent PHPUnit version, as tests running on PHP 5.2 use
     
    60030        public static function assertNotFalse( $condition, $message = '' ) {
    60131                self::assertThat( $condition, self::logicalNot( self::isFalse() ), $message );
    60232        }
    603 
    604         /**
    605          * Sets the global state to as if a given URL has been requested.
    606          *
    607          * This sets:
    608          * - The super globals.
    609          * - The globals.
    610          * - The query variables.
    611          * - The main query.
    612          *
    613          * @since 3.5.0
    614          *
    615          * @param string $url The URL for the request.
    616          */
    617         function go_to( $url ) {
    618                 // note: the WP and WP_Query classes like to silently fetch parameters
    619                 // from all over the place (globals, GET, etc), which makes it tricky
    620                 // to run them more than once without very carefully clearing everything
    621                 $_GET = $_POST = array();
    622                 foreach ( array( 'query_string', 'id', 'postdata', 'authordata', 'day', 'currentmonth', 'page', 'pages', 'multipage', 'more', 'numpages', 'pagenow' ) as $v ) {
    623                         if ( isset( $GLOBALS[ $v ] ) ) {
    624                                 unset( $GLOBALS[ $v ] );
    625                         }
    626                 }
    627                 $parts = parse_url( $url );
    628                 if ( isset( $parts['scheme'] ) ) {
    629                         $req = isset( $parts['path'] ) ? $parts['path'] : '';
    630                         if ( isset( $parts['query'] ) ) {
    631                                 $req .= '?' . $parts['query'];
    632                                 // parse the url query vars into $_GET
    633                                 parse_str( $parts['query'], $_GET );
    634                         }
    635                 } else {
    636                         $req = $url;
    637                 }
    638                 if ( ! isset( $parts['query'] ) ) {
    639                         $parts['query'] = '';
    640                 }
    641 
    642                 $_SERVER['REQUEST_URI'] = $req;
    643                 unset( $_SERVER['PATH_INFO'] );
    644 
    645                 self::flush_cache();
    646                 unset( $GLOBALS['wp_query'], $GLOBALS['wp_the_query'] );
    647                 $GLOBALS['wp_the_query'] = new WP_Query();
    648                 $GLOBALS['wp_query']     = $GLOBALS['wp_the_query'];
    649 
    650                 $public_query_vars  = $GLOBALS['wp']->public_query_vars;
    651                 $private_query_vars = $GLOBALS['wp']->private_query_vars;
    652 
    653                 $GLOBALS['wp']                     = new WP();
    654                 $GLOBALS['wp']->public_query_vars  = $public_query_vars;
    655                 $GLOBALS['wp']->private_query_vars = $private_query_vars;
    656 
    657                 _cleanup_query_vars();
    658 
    659                 $GLOBALS['wp']->main( $parts['query'] );
    660         }
    661 
    662         /**
    663          * Allows tests to be skipped on single or multisite installs by using @group annotations.
    664          *
    665          * This is a custom extension of the PHPUnit requirements handling.
    666          *
    667          * Contains legacy code for skipping tests that are associated with an open Trac ticket. Core tests no longer
    668          * support this behaviour.
    669          *
    670          * @since 3.5.0
    671          */
    672         protected function checkRequirements() {
    673                 parent::checkRequirements();
    674 
    675                 $annotations = $this->getAnnotations();
    676 
    677                 $groups = array();
    678                 if ( ! empty( $annotations['class']['group'] ) ) {
    679                         $groups = array_merge( $groups, $annotations['class']['group'] );
    680                 }
    681                 if ( ! empty( $annotations['method']['group'] ) ) {
    682                         $groups = array_merge( $groups, $annotations['method']['group'] );
    683                 }
    684 
    685                 if ( ! empty( $groups ) ) {
    686                         if ( in_array( 'ms-required', $groups, true ) ) {
    687                                 $this->skipWithoutMultisite();
    688                         }
    689 
    690                         if ( in_array( 'ms-excluded', $groups, true ) ) {
    691                                 $this->skipWithMultisite();
    692                         }
    693                 }
    694 
    695                 // Core tests no longer check against open Trac tickets, but others using WP_UnitTestCase may do so.
    696                 if ( defined( 'WP_RUN_CORE_TESTS' ) && WP_RUN_CORE_TESTS ) {
    697                         return;
    698                 }
    699 
    700                 if ( WP_TESTS_FORCE_KNOWN_BUGS ) {
    701                         return;
    702                 }
    703                 $tickets = PHPUnit_Util_Test::getTickets( get_class( $this ), $this->getName( false ) );
    704                 foreach ( $tickets as $ticket ) {
    705                         if ( is_numeric( $ticket ) ) {
    706                                 $this->knownWPBug( $ticket );
    707                         } elseif ( 'Plugin' == substr( $ticket, 0, 6 ) ) {
    708                                 $ticket = substr( $ticket, 6 );
    709                                 if ( $ticket && is_numeric( $ticket ) ) {
    710                                         $this->knownPluginBug( $ticket );
    711                                 }
    712                         }
    713                 }
    714         }
    715 
    716         /**
    717          * Skips the current test if there is an open Trac ticket associated with it.
    718          *
    719          * @since 3.5.0
    720          *
    721          * @param int $ticket_id Ticket number.
    722          */
    723         function knownWPBug( $ticket_id ) {
    724                 if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( $ticket_id, self::$forced_tickets ) ) {
    725                         return;
    726                 }
    727                 if ( ! TracTickets::isTracTicketClosed( 'https://core.trac.wordpress.org', $ticket_id ) ) {
    728                         $this->markTestSkipped( sprintf( 'WordPress Ticket #%d is not fixed', $ticket_id ) );
    729                 }
    730         }
    731 
    732         /**
    733          * Skips the current test if there is an open Unit Test Trac ticket associated with it.
    734          *
    735          * @since 3.5.0
    736          *
    737          * @deprecated No longer used since the Unit Test Trac was merged into the Core Trac.
    738          *
    739          * @param int $ticket_id Ticket number.
    740          */
    741         function knownUTBug( $ticket_id ) {
    742                 return;
    743         }
    744 
    745         /**
    746          * Skips the current test if there is an open Plugin Trac ticket associated with it.
    747          *
    748          * @since 3.5.0
    749          *
    750          * @param int $ticket_id Ticket number.
    751          */
    752         function knownPluginBug( $ticket_id ) {
    753                 if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( 'Plugin' . $ticket_id, self::$forced_tickets ) ) {
    754                         return;
    755                 }
    756                 if ( ! TracTickets::isTracTicketClosed( 'https://plugins.trac.wordpress.org', $ticket_id ) ) {
    757                         $this->markTestSkipped( sprintf( 'WordPress Plugin Ticket #%d is not fixed', $ticket_id ) );
    758                 }
    759         }
    760 
    761         /**
    762          * Adds a Trac ticket number to the `$forced_tickets` property.
    763          *
    764          * @since 3.5.0
    765          *
    766          * @param int $ticket Ticket number.
    767          */
    768         public static function forceTicket( $ticket ) {
    769                 self::$forced_tickets[] = $ticket;
    770         }
    771 
    772         /**
    773          * Custom preparations for the PHPUnit process isolation template.
    774          *
    775          * When restoring global state between tests, PHPUnit defines all the constants that were already defined, and then
    776          * includes included files. This does not work with WordPress, as the included files define the constants.
    777          *
    778          * This method defines the constants after including files.
    779          *
    780          * @param Text_Template $template
    781          */
    782         function prepareTemplate( Text_Template $template ) {
    783                 $template->setVar( array( 'constants' => '' ) );
    784                 $template->setVar( array( 'wp_constants' => PHPUnit_Util_GlobalState::getConstantsAsString() ) );
    785                 parent::prepareTemplate( $template );
    786         }
    787 
    788         /**
    789          * Creates a unique temporary file name.
    790          *
    791          * The directory in which the file is created depends on the environment configuration.
    792          *
    793          * @since 3.5.0
    794          *
    795          * @return string|bool Path on success, else false.
    796          */
    797         function temp_filename() {
    798                 $tmp_dir = '';
    799                 $dirs    = array( 'TMP', 'TMPDIR', 'TEMP' );
    800                 foreach ( $dirs as $dir ) {
    801                         if ( isset( $_ENV[ $dir ] ) && ! empty( $_ENV[ $dir ] ) ) {
    802                                 $tmp_dir = $dir;
    803                                 break;
    804                         }
    805                 }
    806                 if ( empty( $tmp_dir ) ) {
    807                         $tmp_dir = '/tmp';
    808                 }
    809                 $tmp_dir = realpath( $tmp_dir );
    810                 return tempnam( $tmp_dir, 'wpunit' );
    811         }
    812 
    813         /**
    814          * Checks each of the WP_Query is_* functions/properties against expected boolean value.
    815          *
    816          * Any properties that are listed by name as parameters will be expected to be true; all others are
    817          * expected to be false. For example, assertQueryTrue('is_single', 'is_feed') means is_single()
    818          * and is_feed() must be true and everything else must be false to pass.
    819          *
    820          * @since 2.5.0
    821          * @since 3.8.0 Moved from `Tests_Query_Conditionals` to `WP_UnitTestCase`.
    822          *
    823          * @param string $prop,... Any number of WP_Query properties that are expected to be true for the current request.
    824          */
    825         function assertQueryTrue() {
    826                 global $wp_query;
    827                 $all  = array(
    828                         'is_404',
    829                         'is_admin',
    830                         'is_archive',
    831                         'is_attachment',
    832                         'is_author',
    833                         'is_category',
    834                         'is_comment_feed',
    835                         'is_date',
    836                         'is_day',
    837                         'is_embed',
    838                         'is_feed',
    839                         'is_front_page',
    840                         'is_home',
    841                         'is_month',
    842                         'is_page',
    843                         'is_paged',
    844                         'is_post_type_archive',
    845                         'is_posts_page',
    846                         'is_preview',
    847                         'is_robots',
    848                         'is_search',
    849                         'is_single',
    850                         'is_singular',
    851                         'is_tag',
    852                         'is_tax',
    853                         'is_time',
    854                         'is_trackback',
    855                         'is_year',
    856                 );
    857                 $true = func_get_args();
    858 
    859                 foreach ( $true as $true_thing ) {
    860                         $this->assertContains( $true_thing, $all, "Unknown conditional: {$true_thing}." );
    861                 }
    862 
    863                 $passed  = true;
    864                 $message = '';
    865 
    866                 foreach ( $all as $query_thing ) {
    867                         $result = is_callable( $query_thing ) ? call_user_func( $query_thing ) : $wp_query->$query_thing;
    868 
    869                         if ( in_array( $query_thing, $true ) ) {
    870                                 if ( ! $result ) {
    871                                         $message .= $query_thing . ' is false but is expected to be true. ' . PHP_EOL;
    872                                         $passed   = false;
    873                                 }
    874                         } elseif ( $result ) {
    875                                 $message .= $query_thing . ' is true but is expected to be false. ' . PHP_EOL;
    876                                 $passed   = false;
    877                         }
    878                 }
    879 
    880                 if ( ! $passed ) {
    881                         $this->fail( $message );
    882                 }
    883         }
    884 
    885         /**
    886          * Selectively deletes a file.
    887          *
    888          * Does not delete a file if its path is set in the `$ignore_files` property.
    889          *
    890          * @param string $file File path.
    891          */
    892         function unlink( $file ) {
    893                 $exists = is_file( $file );
    894                 if ( $exists && ! in_array( $file, self::$ignore_files ) ) {
    895                         //error_log( $file );
    896                         unlink( $file );
    897                 } elseif ( ! $exists ) {
    898                         $this->fail( "Trying to delete a file that doesn't exist: $file" );
    899                 }
    900         }
    901 
    902         /**
    903          * Selectively deletes files from a directory.
    904          *
    905          * Does not delete files if their paths are set in the `$ignore_files` property.
    906          *
    907          * @param string $path Directory path.
    908          */
    909         function rmdir( $path ) {
    910                 $files = $this->files_in_dir( $path );
    911                 foreach ( $files as $file ) {
    912                         if ( ! in_array( $file, self::$ignore_files ) ) {
    913                                 $this->unlink( $file );
    914                         }
    915                 }
    916         }
    917 
    918         /**
    919          * Deletes files added to the `uploads` directory during tests.
    920          *
    921          * This method works in tandem with the `setUp()` and `rmdir()` methods:
    922          * - `setUp()` scans the `uploads` directory before every test, and stores its contents inside of the
    923          *   `$ignore_files` property.
    924          * - `rmdir()` and its helper methods only delete files that are not listed in the `$ignore_files` property. If
    925          *   called during `tearDown()` in tests, this will only delete files added during the previously run test.
    926          */
    927         function remove_added_uploads() {
    928                 $uploads = wp_upload_dir();
    929                 $this->rmdir( $uploads['basedir'] );
    930         }
    931 
    932         /**
    933          * Returns a list of all files contained inside a directory.
    934          *
    935          * @since 4.0.0
    936          *
    937          * @param string $dir Path to the directory to scan.
    938          *
    939          * @return array List of file paths.
    940          */
    941         function files_in_dir( $dir ) {
    942                 $files = array();
    943 
    944                 $iterator = new RecursiveDirectoryIterator( $dir );
    945                 $objects  = new RecursiveIteratorIterator( $iterator );
    946                 foreach ( $objects as $name => $object ) {
    947                         if ( is_file( $name ) ) {
    948                                 $files[] = $name;
    949                         }
    950                 }
    951 
    952                 return $files;
    953         }
    954 
    955         /**
    956          * Returns a list of all files contained inside the `uploads` directory.
    957          *
    958          * @since 4.0.0
    959          *
    960          * @return array List of file paths.
    961          */
    962         function scan_user_uploads() {
    963                 static $files = array();
    964                 if ( ! empty( $files ) ) {
    965                         return $files;
    966                 }
    967 
    968                 $uploads = wp_upload_dir();
    969                 $files   = $this->files_in_dir( $uploads['basedir'] );
    970                 return $files;
    971         }
    972 
    973         /**
    974          * Deletes all directories contained inside a directory.
    975          *
    976          * @since 4.1.0
    977          *
    978          * @param string $path Path to the directory to scan.
    979          */
    980         function delete_folders( $path ) {
    981                 $this->matched_dirs = array();
    982                 if ( ! is_dir( $path ) ) {
    983                         return;
    984                 }
    985 
    986                 $this->scandir( $path );
    987                 foreach ( array_reverse( $this->matched_dirs ) as $dir ) {
    988                         rmdir( $dir );
    989                 }
    990                 rmdir( $path );
    991         }
    992 
    993         /**
    994          * Retrieves all directories contained inside a directory and stores them in the `$matched_dirs` property. Hidden
    995          * directories are ignored.
    996          *
    997          * This is a helper for the `delete_folders()` method.
    998          *
    999          * @since 4.1.0
    1000          *
    1001          * @param string $dir Path to the directory to scan.
    1002          */
    1003         function scandir( $dir ) {
    1004                 foreach ( scandir( $dir ) as $path ) {
    1005                         if ( 0 !== strpos( $path, '.' ) && is_dir( $dir . '/' . $path ) ) {
    1006                                 $this->matched_dirs[] = $dir . '/' . $path;
    1007                                 $this->scandir( $dir . '/' . $path );
    1008                         }
    1009                 }
    1010         }
    1011 
    1012         /**
    1013          * Converts a microtime string into a float.
    1014          *
    1015          * @since 4.1.0
    1016          *
    1017          * @param string $microtime Time string generated by `microtime()`.
    1018          *
    1019          * @return float `microtime()` output as a float.
    1020          */
    1021         protected function _microtime_to_float( $microtime ) {
    1022                 $time_array = explode( ' ', $microtime );
    1023                 return array_sum( $time_array );
    1024         }
    1025 
    1026         /**
    1027          * Deletes a user from the database in a Multisite-agnostic way.
    1028          *
    1029          * @since 4.3.0
    1030          *
    1031          * @param int $user_id User ID.
    1032          *
    1033          * @return bool True if the user was deleted.
    1034          */
    1035         public static function delete_user( $user_id ) {
    1036                 if ( is_multisite() ) {
    1037                         return wpmu_delete_user( $user_id );
    1038                 } else {
    1039                         return wp_delete_user( $user_id );
    1040                 }
    1041         }
    1042 
    1043         /**
    1044          * Resets permalinks and flushes rewrites.
    1045          *
    1046          * @since 4.4.0
    1047          *
    1048          * @global WP_Rewrite $wp_rewrite
    1049          *
    1050          * @param string $structure Optional. Permalink structure to set. Default empty.
    1051          */
    1052         public function set_permalink_structure( $structure = '' ) {
    1053                 global $wp_rewrite;
    1054 
    1055                 $wp_rewrite->init();
    1056                 $wp_rewrite->set_permalink_structure( $structure );
    1057                 $wp_rewrite->flush_rules();
    1058         }
    1059 
    1060         /**
    1061          * Creates an attachment post from an uploaded file.
    1062          *
    1063          * @since 4.4.0
    1064          *
    1065          * @param array $upload         Array of information about the uploaded file, provided by wp_upload_bits().
    1066          * @param int   $parent_post_id Optional. Parent post ID.
    1067          *
    1068          * @return int|WP_Error The attachment ID on success. The value 0 or WP_Error on failure.
    1069          */
    1070         function _make_attachment( $upload, $parent_post_id = 0 ) {
    1071                 $type = '';
    1072                 if ( ! empty( $upload['type'] ) ) {
    1073                         $type = $upload['type'];
    1074                 } else {
    1075                         $mime = wp_check_filetype( $upload['file'] );
    1076                         if ( $mime ) {
    1077                                 $type = $mime['type'];
    1078                         }
    1079                 }
    1080 
    1081                 $attachment = array(
    1082                         'post_title'     => basename( $upload['file'] ),
    1083                         'post_content'   => '',
    1084                         'post_type'      => 'attachment',
    1085                         'post_parent'    => $parent_post_id,
    1086                         'post_mime_type' => $type,
    1087                         'guid'           => $upload['url'],
    1088                 );
    1089 
    1090                 $id = wp_insert_attachment( $attachment, $upload['file'], $parent_post_id );
    1091                 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
    1092                 return $id;
    1093         }
    1094 
    1095         /**
    1096          * Updates the modified and modified GMT date of a post in the database.
    1097          *
    1098          * @since 4.8.0
    1099          *
    1100          * @global wpdb $wpdb WordPress database abstraction object.
    1101          *
    1102          * @param int    $post_id Post ID.
    1103          * @param string $date    Post date, in the format YYYY-MM-DD HH:MM:SS.
    1104          *
    1105          * @return int|false 1 on success, or false on error.
    1106          */
    1107         protected function update_post_modified( $post_id, $date ) {
    1108                 global $wpdb;
    1109                 return $wpdb->update(
    1110                         $wpdb->posts,
    1111                         array(
    1112                                 'post_modified'     => $date,
    1113                                 'post_modified_gmt' => $date,
    1114                         ),
    1115                         array(
    1116                                 'ID' => $post_id,
    1117                         ),
    1118                         array(
    1119                                 '%s',
    1120                                 '%s',
    1121                         ),
    1122                         array(
    1123                                 '%d',
    1124                         )
    1125                 );
    1126         }
    112733}
  • tests/phpunit/multisite.xml

     
    4545        <const name="WP_RUN_CORE_TESTS" value="1" />
    4646    </php>
    4747    <listeners>
    48         <listener class="SpeedTrapListener" file="tests/phpunit/includes/speed-trap-listener.php">
     48        <listener class="SpeedTrapListener" file="tests/phpunit/includes/listener-loader.php">
    4949            <arguments>
    5050                <array>
    5151                    <element key="slowThreshold">
  • tests/phpunit/tests/post/query.php

     
    719719
    720720                $q->posts = $posts;
    721721
    722                 $methd = new \ReflectionMethod( 'WP_Query', 'set_found_posts' );
     722                $methd = new ReflectionMethod( 'WP_Query', 'set_found_posts' );
    723723                $methd->setAccessible( true );
    724724                $methd->invoke( $q, array( 'no_found_rows' => false ), array() );
    725725