diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4b6c149867c7d..ef256ccc93308 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -32,6 +32,9 @@ html-api-html5lib-tests + + + diff --git a/tests/phpunit/includes/abstract-testcase.php b/tests/phpunit/includes/abstract-testcase.php index 3a5d52b0706a9..f0c702daf08d1 100644 --- a/tests/phpunit/includes/abstract-testcase.php +++ b/tests/phpunit/includes/abstract-testcase.php @@ -143,6 +143,31 @@ public function set_up() { add_filter( 'wp_hash_password_options', array( $this, 'wp_hash_password_options' ), 1, 2 ); } + /** + * Returns the default-excluded groups that apply to a test and were not explicitly requested. + * + * @param string[] $groups Test groups. + * @return string[] Matching excluded groups. + */ + public static function get_default_excluded_groups_for_test( array $groups ) { + $default_excluded_groups = WP_PHPUnit_Util_Getopt::get_skipped_groups(); + + foreach ( $groups as $group ) { + if ( WP_PHPUnit_Util_Getopt::is_group_requested( $group ) ) { + return array(); + } + } + + return array_values( + array_filter( + $groups, + static function ( $group ) use ( $default_excluded_groups ) { + return in_array( $group, $default_excluded_groups, true ); + } + ) + ); + } + /** * Sets the bcrypt cost option for password hashing during tests. * diff --git a/tests/phpunit/includes/bootstrap.php b/tests/phpunit/includes/bootstrap.php index e308dcb5a6e1c..a4b7025ed750c 100644 --- a/tests/phpunit/includes/bootstrap.php +++ b/tests/phpunit/includes/bootstrap.php @@ -345,42 +345,88 @@ function wp_tests_options( $value ) { * how you call phpunit has no effect. */ class WP_PHPUnit_Util_Getopt { - - public function __construct( $argv ) { + /** + * Groups that are skipped by default for the current test run. + * + * @var array + */ + protected static $skipped_groups = array(); + + /** + * Groups that were explicitly requested via the command line. + * + * @var array + */ + protected static $requested_groups = array(); + + /** + * Initializes the default skipped groups for the current test mode. + * + * @return array The groups skipped by default. + */ + protected static function get_default_skipped_groups() { $skipped_groups = array( - 'ajax' => true, - 'ms-files' => true, - 'external-http' => true, + 'ajax' => true, + 'ms-files' => true, + 'external-http' => true, + 'html-api-html5lib-tests' => true, ); - while ( current( $argv ) ) { - $option = current( $argv ); - $value = next( $argv ); + if ( is_multisite() ) { + $skipped_groups['ms-excluded'] = true; + $skipped_groups['oembed-headers'] = true; + } else { + $skipped_groups['ms-required'] = true; + } + + return $skipped_groups; + } + + /** + * Initializes the parsed command line state. + */ + protected static function reset() { + self::$skipped_groups = self::get_default_skipped_groups(); + self::$requested_groups = array(); + } + + public function __construct( $argv ) { + self::reset(); + + foreach ( $argv as $index => $argument ) { + $option = $argument; + $value = isset( $argv[ $index + 1 ] ) ? $argv[ $index + 1 ] : ''; + + if ( 0 === strpos( $option, '--group=' ) ) { + $value = substr( $option, strlen( '--group=' ) ); + $option = '--group'; + } switch ( $option ) { case '--exclude-group': - foreach ( $skipped_groups as $group_name => $skipped ) { - $skipped_groups[ $group_name ] = false; - } - continue 2; + // PHPUnit replaces XML exclusions when this option is provided. + // Keep our default skipped groups active unless they are requested explicitly. + break; case '--group': $groups = explode( ',', $value ); foreach ( $groups as $group ) { + self::$requested_groups[ $group ] = true; + if ( is_numeric( $group ) || preg_match( '/^(UT|Plugin)\d+$/', $group ) ) { WP_UnitTestCase::forceTicket( $group ); } } - foreach ( $skipped_groups as $group_name => $skipped ) { + foreach ( self::$skipped_groups as $group_name => $skipped ) { if ( in_array( $group_name, $groups, true ) ) { - $skipped_groups[ $group_name ] = false; + self::$skipped_groups[ $group_name ] = false; } } - continue 2; + break; } } - $skipped_groups = array_filter( $skipped_groups ); + $skipped_groups = array_filter( self::$skipped_groups ); foreach ( $skipped_groups as $group_name => $skipped ) { echo sprintf( 'Not running %1$s tests. To execute these, use --group %1$s.', $group_name ) . PHP_EOL; } @@ -392,5 +438,76 @@ public function __construct( $argv ) { echo PHP_EOL; } } + + /** + * Returns the groups that are still skipped by default for this run. + * + * @return string[] + */ + public static function get_skipped_groups() { + return array_keys( array_filter( self::$skipped_groups ) ); + } + + /** + * Determines whether a group was explicitly requested via the command line. + * + * @param string $group Group name. + * @return bool Whether the group was explicitly requested. + */ + public static function is_group_requested( $group ) { + return isset( self::$requested_groups[ $group ] ); + } } new WP_PHPUnit_Util_Getopt( $_SERVER['argv'] ); + +/** + * Removes default-excluded tests from PHPUnit suites before class fixtures run. + */ +trait WP_PHPUnit_Default_Group_Filter_Methods { + + /** + * Filters default-excluded tests from a suite and all nested suites. + * + * @param PHPUnit\Framework\TestSuite $suite Test suite. + */ + public function start_test_suite( $suite ) { + $this->filter_suite( $suite ); + } + + /** + * Filters a test suite recursively. + * + * @param PHPUnit\Framework\TestSuite $suite Test suite. + */ + protected function filter_suite( $suite ) { + $tests = array(); + + foreach ( $suite->tests() as $test ) { + if ( $test instanceof PHPUnit\Framework\TestSuite || $test instanceof PHPUnit_Framework_TestSuite ) { + $this->filter_suite( $test ); + + if ( count( $test ) ) { + $tests[] = $test; + } + } elseif ( ! method_exists( $test, 'getGroups' ) || ! WP_UnitTestCase_Base::get_default_excluded_groups_for_test( $test->getGroups() ) ) { + $tests[] = $test; + } + } + + $suite->setTests( $tests ); + } +} + +if ( version_compare( tests_get_phpunit_version(), '6.0', '<' ) ) { + class WP_PHPUnit_Default_Group_Filter implements PHPUnit_Framework_TestListener { + use Yoast\PHPUnitPolyfills\TestListeners\TestListenerDefaultImplementation, WP_PHPUnit_Default_Group_Filter_Methods { + WP_PHPUnit_Default_Group_Filter_Methods::start_test_suite insteadof Yoast\PHPUnitPolyfills\TestListeners\TestListenerDefaultImplementation; + } + } +} else { + class WP_PHPUnit_Default_Group_Filter implements PHPUnit\Framework\TestListener { + use Yoast\PHPUnitPolyfills\TestListeners\TestListenerDefaultImplementation, WP_PHPUnit_Default_Group_Filter_Methods { + WP_PHPUnit_Default_Group_Filter_Methods::start_test_suite insteadof Yoast\PHPUnitPolyfills\TestListeners\TestListenerDefaultImplementation; + } + } +} diff --git a/tests/phpunit/multisite.xml b/tests/phpunit/multisite.xml index d8f173c6167ac..c62304e896843 100644 --- a/tests/phpunit/multisite.xml +++ b/tests/phpunit/multisite.xml @@ -37,6 +37,9 @@ html-api-html5lib-tests + + + ../../src diff --git a/tests/phpunit/tests/phpunit/groupFilters.php b/tests/phpunit/tests/phpunit/groupFilters.php new file mode 100644 index 0000000000000..eea4333e7ce00 --- /dev/null +++ b/tests/phpunit/tests/phpunit/groupFilters.php @@ -0,0 +1,102 @@ +parse_arguments( array( 'phpunit' ) ); + } + + public function tear_down() { + $this->parse_arguments( $_SERVER['argv'] ); + parent::tear_down(); + } + + public function test_single_site_default_excluded_groups_are_reported() { + $this->assertSame( + array( 'ajax', 'external-http' ), + WP_UnitTestCase_Base::get_default_excluded_groups_for_test( array( 'ajax', 'external-http', 'query' ) ) + ); + } + + /** + * @dataProvider data_group_argument_syntax + * + * @param string[] $arguments Command line arguments. + */ + public function test_requested_group_is_not_reported_as_default_excluded( $arguments ) { + $this->parse_arguments( $arguments ); + + $this->assertSame( + array(), + WP_UnitTestCase_Base::get_default_excluded_groups_for_test( array( 'ajax' ) ) + ); + } + + /** + * Data provider. + * + * @return array>> + */ + public function data_group_argument_syntax() { + return array( + 'spaced' => array( array( 'phpunit', '--group', 'ajax' ) ), + 'equals' => array( array( 'phpunit', '--group=ajax' ) ), + ); + } + + public function test_exclude_group_does_not_clear_default_excluded_groups() { + $this->parse_arguments( + array( + 'phpunit', + '--exclude-group', + 'import', + ) + ); + + $multisite_excluded_group = is_multisite() ? 'ms-excluded' : 'ms-required'; + + $this->assertSame( + array( 'ajax', $multisite_excluded_group ), + WP_UnitTestCase_Base::get_default_excluded_groups_for_test( array( 'ajax', $multisite_excluded_group ) ) + ); + } + + public function test_requested_group_allows_a_test_in_a_default_excluded_group() { + $this->parse_arguments( array( 'phpunit', '--group', 'xdebug' ) ); + + $this->assertSame( + array(), + WP_UnitTestCase_Base::get_default_excluded_groups_for_test( array( 'ajax', 'xdebug' ) ) + ); + } + + public function test_default_group_filter_removes_tests_from_nested_suites() { + $class_suite = class_exists( 'PHPUnit_Framework_TestSuite' ) ? new PHPUnit_Framework_TestSuite() : new PHPUnit\Framework\TestSuite(); + $class_suite->addTest( clone $this, array( 'phpunit' ) ); + $class_suite->addTest( clone $this, array( 'ajax' ) ); + $class_suite->addTest( clone $this, array( 'phpunit' ) ); + + $root_suite = new PHPUnit\Framework\TestSuite(); + $root_suite->addTest( $class_suite ); + + $filter = new WP_PHPUnit_Default_Group_Filter(); + $filter->startTestSuite( $root_suite ); + + $this->assertCount( 2, $root_suite ); + $this->assertCount( 2, $class_suite ); + } + + /** + * Parses command line arguments without printing group notices. + * + * @param string[] $arguments Command line arguments. + */ + private function parse_arguments( $arguments ) { + ob_start(); + new WP_PHPUnit_Util_Getopt( $arguments ); + ob_end_clean(); + } +}