diff --git a/WordPress/Sniff.php b/WordPress/Sniff.php index 51bd48776a..e688876d45 100644 --- a/WordPress/Sniff.php +++ b/WordPress/Sniff.php @@ -1210,10 +1210,11 @@ protected function has_whitelist_comment( $comment, $stackPtr ) { * Check if a token is used within a unit test. * * Unit test methods are identified as such: - * - Method name starts with `test_`. - * - Method is within a unit test class. + * - Method is within a known unit test class; + * - or Method is within a class/trait which extends a known unit test class. * * @since 0.11.0 + * @since 1.1.0 Supports anonymous test classes and improved handling of nested scopes. * * @param int $stackPtr The position of the token to be examined. * @@ -1223,22 +1224,38 @@ protected function is_token_in_test_method( $stackPtr ) { // Is the token inside of a function definition ? $functionToken = $this->phpcsFile->getCondition( $stackPtr, \T_FUNCTION ); if ( false === $functionToken ) { + // No conditions or no function condition. return false; } - // Is this a method inside of a class or a trait ? - $classToken = $this->phpcsFile->getCondition( $functionToken, \T_CLASS ); - $traitToken = $this->phpcsFile->getCondition( $functionToken, \T_TRAIT ); - if ( false === $classToken && false === $traitToken ) { - return false; - } + /* + * Is this a method inside of a class or a trait ? If so, it is a test class/trait ? + * + * {@internal Once the minimum supported PHPCS version has gone up to 3.1.0, the + * local array here can be replace with Tokens::$ooScopeTokens.}} + */ + $oo_tokens = array( + \T_CLASS => true, + \T_TRAIT => true, + \T_ANON_CLASS => true, + ); + $conditions = $this->tokens[ $stackPtr ]['conditions']; - $structureToken = $classToken; - if ( false !== $traitToken ) { - $structureToken = $traitToken; + foreach ( $conditions as $token => $condition ) { + if ( $token === $functionToken ) { + // Only examine the conditions the function is nested in, not those nested within the function. + break; + } + + if ( isset( $oo_tokens[ $condition ] ) ) { + $is_test_class = $this->is_test_class( $token ); + if ( true === $is_test_class ) { + return true; + } + } } - return $this->is_test_class( $structureToken ); + return false; } /** diff --git a/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php b/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php index 41ed05b808..3f838831e8 100644 --- a/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php +++ b/WordPress/Sniffs/NamingConventions/PrefixAllGlobalsSniff.php @@ -166,13 +166,14 @@ class PrefixAllGlobalsSniff extends AbstractFunctionParameterSniff { */ public function register() { $targets = array( - \T_FUNCTION => \T_FUNCTION, - \T_CLASS => \T_CLASS, - \T_INTERFACE => \T_INTERFACE, - \T_TRAIT => \T_TRAIT, - \T_CONST => \T_CONST, - \T_VARIABLE => \T_VARIABLE, - \T_DOLLAR => \T_DOLLAR, // Variable variables. + \T_FUNCTION => \T_FUNCTION, + \T_CLASS => \T_CLASS, + \T_INTERFACE => \T_INTERFACE, + \T_TRAIT => \T_TRAIT, + \T_CONST => \T_CONST, + \T_VARIABLE => \T_VARIABLE, + \T_DOLLAR => \T_DOLLAR, // Variable variables. + \T_ANON_CLASS => \T_ANON_CLASS, // Only used for skipping over test classes. ); // Add function call target for hook names and constants defined using define(). @@ -241,7 +242,11 @@ public function process_token( $stackPtr ) { } // Ignore test classes. - if ( \T_CLASS === $this->tokens[ $stackPtr ]['code'] && true === $this->is_test_class( $stackPtr ) ) { + if ( ( \T_CLASS === $this->tokens[ $stackPtr ]['code'] + || \T_TRAIT === $this->tokens[ $stackPtr ]['code'] + || \T_ANON_CLASS === $this->tokens[ $stackPtr ]['code'] ) + && true === $this->is_test_class( $stackPtr ) + ) { if ( $this->tokens[ $stackPtr ]['scope_condition'] === $stackPtr && isset( $this->tokens[ $stackPtr ]['scope_closer'] ) ) { // Skip forward to end of test class. return $this->tokens[ $stackPtr ]['scope_closer']; @@ -249,6 +254,11 @@ public function process_token( $stackPtr ) { return; } + if ( \T_ANON_CLASS === $this->tokens[ $stackPtr ]['code'] ) { + // Token was only registered to allow skipping over test classes. + return; + } + if ( \T_STRING === $this->tokens[ $stackPtr ]['code'] ) { // Disallow excluding function groups for this sniff. $this->exclude = array(); diff --git a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc index abdce9f147..1f66755a9c 100644 --- a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc +++ b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.1.inc @@ -383,4 +383,19 @@ const SCRIPT_DEBUG = $develop_src; do_action( 'test-this-hookname' ); // OK. apply_filters( 'myplugin\filtername', $var ); // OK. +// Non-prefixed constant and action within a (nested) anonymous test class is fine. +class Some_Test_Class extends NonTestClass { // Bad. + public function some_test_method() { + define( 'SOME_GLOBAL', '4.0.0' ); // Bad. + + return new class extends \PHPUnit_Framework_TestCase { + public function testPass() { + define( 'SOME_GLOBAL', '4.0.0' ); // OK. + + do_action( 'some-action', $something ); // OK. + } + }; + } +} + // @codingStandardsChangeSetting WordPress.NamingConventions.PrefixAllGlobals prefixes false diff --git a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.3.inc b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.3.inc index 49bbfa07a8..d700c783c9 100644 --- a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.3.inc +++ b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.3.inc @@ -12,4 +12,13 @@ class Some_Test extends \PHPUnit_Framework_TestCase { } } +$acronym_test = new class extends \PHPUnit_Framework_TestCase { + + public function testPass() { + define( 'SOME_GLOBAL', '4.0.0' ); + + do_action( 'some-action', $something ); + } +}; + // @codingStandardsChangeSetting WordPress.NamingConventions.PrefixAllGlobals prefixes false diff --git a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php index 59789a39b5..efa97f0ad4 100644 --- a/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php +++ b/WordPress/Tests/NamingConventions/PrefixAllGlobalsUnitTest.php @@ -65,6 +65,8 @@ public function getErrorList( $testFile = 'PrefixAllGlobalsUnitTest.1.inc' ) { 349 => 1, 352 => 1, 357 => 1, + 387 => 1, + 389 => 1, ); case 'PrefixAllGlobalsUnitTest.2.inc':