Make WordPress Core


Ignore:
Timestamp:
01/28/2019 02:10:24 PM (8 years ago)
Author:
SergeyBiryukov
Message:

Build/Test Tools: Add support for PHPUnit 7.x.

  • Create an abstract WP_UnitTestCase_Base class to share between PHPUnit 7.x and older versions.
  • Add a speed-trap loader to determine which SpeedTrapListener class needs to be loaded for the current PHPUnit version.
  • Remove unnecessary PHPUnit\Util\Test and PHPUnit_Util_Getopt inheritances.
  • Update Travis CI config to use PHPUnit 7.x for PHP 7.1, 7.2, and nightly PHP versions.

Props jipmoors, netweb, desrosj, ayeshrajans, soulseekah, SergeyBiryukov.
See #43218.

Location:
trunk/tests/phpunit/includes/phpunit7
Files:
1 added
1 copied

Legend:

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

    r44700 r44701  
    11<?php
    22
    3 require_once dirname( __FILE__ ) . '/factory.php';
    4 require_once dirname( __FILE__ ) . '/trac.php';
     3require_once dirname( dirname( __FILE__ ) ) . '/abstract-testcase.php';
    54
    65/**
    … …  
    1312 * All WordPress unit tests should inherit from this class.
    1413 */
    15 class WP_UnitTestCase 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
    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 
     14class WP_UnitTestCase extends WP_UnitTestCase_Base {
    58715        /**
    58816         * Asserts that a condition is not false.
    … …  
    59826         * @throws PHPUnit_Framework_AssertionFailedError
    59927         */
    600         public static function assertNotFalse( $condition, $message = '' ) {
     28        public static function assertNotFalse( $condition, string $message = '' ): void {
    60129                self::assertThat( $condition, self::logicalNot( self::isFalse() ), $message );
    60230        }
    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         }
    112731}
Note: See TracChangeset for help on using the changeset viewer.