File: //usr/local/wp/vendor/squizlabs/php_codesniffer/tests/Core/Files/File/GetConditionTest.php
<?php
/**
* Tests for the \PHP_CodeSniffer\Files\File:getCondition and \PHP_CodeSniffer\Files\File:hasCondition methods.
*
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
* @copyright 2022-2024 PHPCSStandards Contributors
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Tests\Core\Files\File;
use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
use PHP_CodeSniffer\Util\Tokens;
/**
* Tests for the \PHP_CodeSniffer\Files\File:getCondition and \PHP_CodeSniffer\Files\File:hasCondition methods.
*
* @covers \PHP_CodeSniffer\Files\File::getCondition
* @covers \PHP_CodeSniffer\Files\File::hasCondition
*/
final class GetConditionTest extends AbstractMethodUnitTest
{
/**
* List of all the test markers with their target token in the test case file.
*
* - The startPoint token is left out as it is tested separately.
* - The key is the type of token to look for after the test marker.
*
* @var array<int|string, string>
*/
protected static $testTargets = [
T_VARIABLE => '/* testSeriouslyNestedMethod */',
T_RETURN => '/* testDeepestNested */',
T_ECHO => '/* testInException */',
T_CONSTANT_ENCAPSED_STRING => '/* testInDefault */',
];
/**
* List of all the condition markers in the test case file.
*
* @var array<string>
*/
protected $conditionMarkers = [
'/* condition 0: namespace */',
'/* condition 1: if */',
'/* condition 2: function */',
'/* condition 3-1: if */',
'/* condition 3-2: else */',
'/* condition 4: if */',
'/* condition 5: nested class */',
'/* condition 6: class method */',
'/* condition 7: switch */',
'/* condition 8a: case */',
'/* condition 9: while */',
'/* condition 10-1: if */',
'/* condition 11-1: nested anonymous class */',
'/* condition 12: nested anonymous class method */',
'/* condition 13: closure */',
'/* condition 10-2: elseif */',
'/* condition 10-3: foreach */',
'/* condition 11-2: try */',
'/* condition 11-3: catch */',
'/* condition 11-4: finally */',
'/* condition 8b: default */',
];
/**
* Base array with all the scope opening tokens.
*
* This array is merged with expected result arrays for various unit tests
* to make sure all possible conditions are tested.
*
* This array should be kept in sync with the Tokens::$scopeOpeners array.
* This array isn't auto-generated based on the array in Tokens as for these
* tests we want to have access to the token constant names, not just their values.
*
* @var array<string, bool>
*/
protected $conditionDefaults = [
'T_CLASS' => false,
'T_ANON_CLASS' => false,
'T_INTERFACE' => false,
'T_TRAIT' => false,
'T_NAMESPACE' => false,
'T_FUNCTION' => false,
'T_CLOSURE' => false,
'T_IF' => false,
'T_SWITCH' => false,
'T_CASE' => false,
'T_DECLARE' => false,
'T_DEFAULT' => false,
'T_WHILE' => false,
'T_ELSE' => false,
'T_ELSEIF' => false,
'T_FOR' => false,
'T_FOREACH' => false,
'T_DO' => false,
'T_TRY' => false,
'T_CATCH' => false,
'T_FINALLY' => false,
'T_PROPERTY' => false,
'T_OBJECT' => false,
'T_USE' => false,
];
/**
* Cache for the test token stack pointers.
*
* @var array<string, int>
*/
protected static $testTokens = [];
/**
* Cache for the marker token stack pointers.
*
* @var array<string, int>
*/
protected static $markerTokens = [];
/**
* Set up the token position caches for the tests.
*
* Retrieves the test tokens and marker token stack pointer positions
* only once and caches them as they won't change between the tests anyway.
*
* @before
*
* @return void
*/
protected function setUpCaches()
{
if (empty(self::$testTokens) === true) {
foreach (self::$testTargets as $targetToken => $marker) {
self::$testTokens[$marker] = $this->getTargetToken($marker, $targetToken);
}
}
if (empty(self::$markerTokens) === true) {
foreach ($this->conditionMarkers as $marker) {
self::$markerTokens[$marker] = $this->getTargetToken($marker, Tokens::$scopeOpeners);
}
}
}//end setUpCaches()
/**
* Test passing a non-existent token pointer.
*
* @return void
*/
public function testNonExistentToken()
{
$result = self::$phpcsFile->getCondition(100000, T_CLASS);
$this->assertFalse($result);
$result = self::$phpcsFile->hasCondition(100000, T_IF);
$this->assertFalse($result);
}//end testNonExistentToken()
/**
* Test passing a non conditional token.
*
* @return void
*/
public function testNonConditionalToken()
{
$targetType = T_STRING;
$stackPtr = $this->getTargetToken('/* testStartPoint */', $targetType);
$result = self::$phpcsFile->getCondition($stackPtr, T_IF);
$this->assertFalse($result);
$result = self::$phpcsFile->hasCondition($stackPtr, Tokens::$ooScopeTokens);
$this->assertFalse($result);
}//end testNonConditionalToken()
/**
* Test retrieving a specific condition from a tokens "conditions" array.
*
* @param string $testMarker The comment which prefaces the target token in the test file.
* @param array<string, string> $expectedResults Array with the condition token type to search for as key
* and the marker for the expected stack pointer result as a value.
*
* @dataProvider dataGetCondition
*
* @return void
*/
public function testGetCondition($testMarker, $expectedResults)
{
$stackPtr = self::$testTokens[$testMarker];
// Add expected results for all test markers not listed in the data provider.
$expectedResults += $this->conditionDefaults;
foreach ($expectedResults as $conditionType => $expected) {
if (is_string($expected) === true) {
$expected = self::$markerTokens[$expected];
}
$result = self::$phpcsFile->getCondition($stackPtr, constant($conditionType));
$this->assertSame(
$expected,
$result,
"Assertion failed for test marker '{$testMarker}' with condition {$conditionType}"
);
}
}//end testGetCondition()
/**
* Data provider.
*
* Only the conditions which are expected to be *found* need to be listed here.
* All other potential conditions will automatically also be tested and will expect
* `false` as a result.
*
* @see testGetCondition() For the array format.
*
* @return array<string, array<string, string|array<string, string>>>
*/
public static function dataGetCondition()
{
return [
'testSeriouslyNestedMethod' => [
'testMarker' => '/* testSeriouslyNestedMethod */',
'expectedResults' => [
'T_CLASS' => '/* condition 5: nested class */',
'T_NAMESPACE' => '/* condition 0: namespace */',
'T_FUNCTION' => '/* condition 2: function */',
'T_IF' => '/* condition 1: if */',
'T_ELSE' => '/* condition 3-2: else */',
],
],
'testDeepestNested' => [
'testMarker' => '/* testDeepestNested */',
'expectedResults' => [
'T_CLASS' => '/* condition 5: nested class */',
'T_ANON_CLASS' => '/* condition 11-1: nested anonymous class */',
'T_NAMESPACE' => '/* condition 0: namespace */',
'T_FUNCTION' => '/* condition 2: function */',
'T_CLOSURE' => '/* condition 13: closure */',
'T_IF' => '/* condition 1: if */',
'T_SWITCH' => '/* condition 7: switch */',
'T_CASE' => '/* condition 8a: case */',
'T_WHILE' => '/* condition 9: while */',
'T_ELSE' => '/* condition 3-2: else */',
],
],
'testInException' => [
'testMarker' => '/* testInException */',
'expectedResults' => [
'T_CLASS' => '/* condition 5: nested class */',
'T_NAMESPACE' => '/* condition 0: namespace */',
'T_FUNCTION' => '/* condition 2: function */',
'T_IF' => '/* condition 1: if */',
'T_SWITCH' => '/* condition 7: switch */',
'T_CASE' => '/* condition 8a: case */',
'T_WHILE' => '/* condition 9: while */',
'T_ELSE' => '/* condition 3-2: else */',
'T_FOREACH' => '/* condition 10-3: foreach */',
'T_CATCH' => '/* condition 11-3: catch */',
],
],
'testInDefault' => [
'testMarker' => '/* testInDefault */',
'expectedResults' => [
'T_CLASS' => '/* condition 5: nested class */',
'T_NAMESPACE' => '/* condition 0: namespace */',
'T_FUNCTION' => '/* condition 2: function */',
'T_IF' => '/* condition 1: if */',
'T_SWITCH' => '/* condition 7: switch */',
'T_DEFAULT' => '/* condition 8b: default */',
'T_ELSE' => '/* condition 3-2: else */',
],
],
];
}//end dataGetCondition()
/**
* Test retrieving a specific condition from a tokens "conditions" array.
*
* @param string $testMarker The comment which prefaces the target token in the test file.
* @param array<string, string> $expectedResults Array with the condition token type to search for as key
* and the marker for the expected stack pointer result as a value.
*
* @dataProvider dataGetConditionReversed
*
* @return void
*/
public function testGetConditionReversed($testMarker, $expectedResults)
{
$stackPtr = self::$testTokens[$testMarker];
// Add expected results for all test markers not listed in the data provider.
$expectedResults += $this->conditionDefaults;
foreach ($expectedResults as $conditionType => $expected) {
if (is_string($expected) === true) {
$expected = self::$markerTokens[$expected];
}
$result = self::$phpcsFile->getCondition($stackPtr, constant($conditionType), false);
$this->assertSame(
$expected,
$result,
"Assertion failed for test marker '{$testMarker}' with condition {$conditionType} (reversed)"
);
}
}//end testGetConditionReversed()
/**
* Data provider.
*
* Only the conditions which are expected to be *found* need to be listed here.
* All other potential conditions will automatically also be tested and will expect
* `false` as a result.
*
* @see testGetConditionReversed() For the array format.
*
* @return array<string, array<string, string|array<string, string>>>
*/
public static function dataGetConditionReversed()
{
$data = self::dataGetCondition();
// Set up the data for the reversed results.
$data['testSeriouslyNestedMethod']['expectedResults']['T_IF'] = '/* condition 4: if */';
$data['testDeepestNested']['expectedResults']['T_FUNCTION'] = '/* condition 12: nested anonymous class method */';
$data['testDeepestNested']['expectedResults']['T_IF'] = '/* condition 10-1: if */';
$data['testInException']['expectedResults']['T_FUNCTION'] = '/* condition 6: class method */';
$data['testInException']['expectedResults']['T_IF'] = '/* condition 4: if */';
$data['testInDefault']['expectedResults']['T_FUNCTION'] = '/* condition 6: class method */';
$data['testInDefault']['expectedResults']['T_IF'] = '/* condition 4: if */';
return $data;
}//end dataGetConditionReversed()
/**
* Test whether a token has a condition of a certain type.
*
* @param string $testMarker The comment which prefaces the target token in the test file.
* @param array<string, bool> $expectedResults Array with the condition token type to search for as key
* and the expected result as a value.
*
* @dataProvider dataHasCondition
*
* @return void
*/
public function testHasCondition($testMarker, $expectedResults)
{
$stackPtr = self::$testTokens[$testMarker];
// Add expected results for all test markers not listed in the data provider.
$expectedResults += $this->conditionDefaults;
foreach ($expectedResults as $conditionType => $expected) {
$result = self::$phpcsFile->hasCondition($stackPtr, constant($conditionType));
$this->assertSame(
$expected,
$result,
"Assertion failed for test marker '{$testMarker}' with condition {$conditionType}"
);
}
}//end testHasCondition()
/**
* Data Provider.
*
* Only list the "true" conditions in the $results array.
* All other potential conditions will automatically also be tested
* and will expect "false" as a result.
*
* @see testHasCondition() For the array format.
*
* @return array<string, array<string, string|array<string, bool>>>
*/
public static function dataHasCondition()
{
return [
'testSeriouslyNestedMethod' => [
'testMarker' => '/* testSeriouslyNestedMethod */',
'expectedResults' => [
'T_CLASS' => true,
'T_NAMESPACE' => true,
'T_FUNCTION' => true,
'T_IF' => true,
'T_ELSE' => true,
],
],
'testDeepestNested' => [
'testMarker' => '/* testDeepestNested */',
'expectedResults' => [
'T_CLASS' => true,
'T_ANON_CLASS' => true,
'T_NAMESPACE' => true,
'T_FUNCTION' => true,
'T_CLOSURE' => true,
'T_IF' => true,
'T_SWITCH' => true,
'T_CASE' => true,
'T_WHILE' => true,
'T_ELSE' => true,
],
],
'testInException' => [
'testMarker' => '/* testInException */',
'expectedResults' => [
'T_CLASS' => true,
'T_NAMESPACE' => true,
'T_FUNCTION' => true,
'T_IF' => true,
'T_SWITCH' => true,
'T_CASE' => true,
'T_WHILE' => true,
'T_ELSE' => true,
'T_FOREACH' => true,
'T_CATCH' => true,
],
],
'testInDefault' => [
'testMarker' => '/* testInDefault */',
'expectedResults' => [
'T_CLASS' => true,
'T_NAMESPACE' => true,
'T_FUNCTION' => true,
'T_IF' => true,
'T_SWITCH' => true,
'T_DEFAULT' => true,
'T_ELSE' => true,
],
],
];
}//end dataHasCondition()
/**
* Test whether a token has a condition of a certain type, with multiple allowed possibilities.
*
* @return void
*/
public function testHasConditionMultipleTypes()
{
$stackPtr = self::$testTokens['/* testInException */'];
$result = self::$phpcsFile->hasCondition($stackPtr, [T_TRY, T_FINALLY]);
$this->assertFalse(
$result,
'Failed asserting that "testInException" does not have a "try" nor a "finally" condition'
);
$result = self::$phpcsFile->hasCondition($stackPtr, [T_TRY, T_CATCH, T_FINALLY]);
$this->assertTrue(
$result,
'Failed asserting that "testInException" has a "try", "catch" or "finally" condition'
);
$stackPtr = self::$testTokens['/* testSeriouslyNestedMethod */'];
$result = self::$phpcsFile->hasCondition($stackPtr, [T_ANON_CLASS, T_CLOSURE]);
$this->assertFalse(
$result,
'Failed asserting that "testSeriouslyNestedMethod" does not have an anonymous class nor a closure condition'
);
$result = self::$phpcsFile->hasCondition($stackPtr, Tokens::$ooScopeTokens);
$this->assertTrue(
$result,
'Failed asserting that "testSeriouslyNestedMethod" has an OO Scope token condition'
);
}//end testHasConditionMultipleTypes()
}//end class