<?php
/**
 * Tests the support of PHP 8.4 asymmetric visibility.
 *
 * @author    Daniel Scherzer <daniel.e.scherzer@gmail.com>
 * @copyright 2025 PHPCSStandards and contributors
 * @license   https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/HEAD/licence.txt BSD Licence
 */

namespace PHP_CodeSniffer\Tests\Core\Tokenizers\PHP;

use PHP_CodeSniffer\Tests\Core\Tokenizers\AbstractTokenizerTestCase;

/**
 * Tests the support of PHP 8.4 asymmetric visibility.
 *
 * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
 */
final class BackfillAsymmetricVisibilityTest extends AbstractTokenizerTestCase
{


    /**
     * Test that the asymmetric visibility keywords are tokenized as such.
     *
     * @param string $testMarker  The comment which prefaces the target token in the test file.
     * @param string $testType    The expected token type.
     * @param string $testContent The token content to look for.
     *
     * @dataProvider dataAsymmetricVisibility
     *
     * @return void
     */
    public function testAsymmetricVisibility($testMarker, $testType, $testContent)
    {
        $tokens     = $this->phpcsFile->getTokens();
        $target     = $this->getTargetToken(
            $testMarker,
            [
                T_PUBLIC_SET,
                T_PROTECTED_SET,
                T_PRIVATE_SET,
            ]
        );
        $tokenArray = $tokens[$target];

        $this->assertSame(
            $testType,
            $tokenArray['type'],
            'Token tokenized as '.$tokenArray['type'].' (type)'
        );
        $this->assertSame(
            constant($testType),
            $tokenArray['code'],
            'Token tokenized as '.$tokenArray['type'].' (code)'
        );
        $this->assertSame(
            $testContent,
            $tokenArray['content'],
            'Token tokenized as '.$tokenArray['type'].' (content)'
        );

    }//end testAsymmetricVisibility()


    /**
     * Data provider.
     *
     * @see testAsymmetricVisibility()
     *
     * @return array<string, array<string, string>>
     */
    public static function dataAsymmetricVisibility()
    {
        return [
            // Normal property declarations.
            'property, public set, no read visibility, lowercase'      => [
                'testMarker'  => '/* testPublicSetProperty */',
                'testType'    => 'T_PUBLIC_SET',
                'testContent' => 'public(set)',
            ],
            'property, public set, no read visibility, uppercase'      => [
                'testMarker'  => '/* testPublicSetPropertyUC */',
                'testType'    => 'T_PUBLIC_SET',
                'testContent' => 'PUBLIC(SET)',
            ],
            'property, public set, has read visibility, lowercase'     => [
                'testMarker'  => '/* testPublicPublicSetProperty */',
                'testType'    => 'T_PUBLIC_SET',
                'testContent' => 'public(set)',
            ],
            'property, public set, has read visibility, uppercase'     => [
                'testMarker'  => '/* testPublicPublicSetPropertyUC */',
                'testType'    => 'T_PUBLIC_SET',
                'testContent' => 'PUBLIC(SET)',
            ],
            'property, protected set, no read visibility, lowercase'   => [
                'testMarker'  => '/* testProtectedSetProperty */',
                'testType'    => 'T_PROTECTED_SET',
                'testContent' => 'protected(set)',
            ],
            'property, protected set, no read visibility, uppercase'   => [
                'testMarker'  => '/* testProtectedSetPropertyUC */',
                'testType'    => 'T_PROTECTED_SET',
                'testContent' => 'PROTECTED(SET)',
            ],
            'property, protected set, has read visibility, lowercase'  => [
                'testMarker'  => '/* testPublicProtectedSetProperty */',
                'testType'    => 'T_PROTECTED_SET',
                'testContent' => 'protected(set)',
            ],
            'property, protected set, has read visibility, uppercase'  => [
                'testMarker'  => '/* testPublicProtectedSetPropertyUC */',
                'testType'    => 'T_PROTECTED_SET',
                'testContent' => 'PROTECTED(SET)',
            ],
            'property, private set, no read visibility, lowercase'     => [
                'testMarker'  => '/* testPrivateSetProperty */',
                'testType'    => 'T_PRIVATE_SET',
                'testContent' => 'private(set)',
            ],
            'property, private set, no read visibility, uppercase'     => [
                'testMarker'  => '/* testPrivateSetPropertyUC */',
                'testType'    => 'T_PRIVATE_SET',
                'testContent' => 'PRIVATE(SET)',
            ],
            'property, private set, has read visibility, lowercase'    => [
                'testMarker'  => '/* testPublicPrivateSetProperty */',
                'testType'    => 'T_PRIVATE_SET',
                'testContent' => 'private(set)',
            ],
            'property, private set, has read visibility, uppercase'    => [
                'testMarker'  => '/* testPublicPrivateSetPropertyUC */',
                'testType'    => 'T_PRIVATE_SET',
                'testContent' => 'PRIVATE(SET)',
            ],

            // Constructor property promotion.
            'promotion, public set, no read visibility, lowercase'     => [
                'testMarker'  => '/* testPublicSetCPP */',
                'testType'    => 'T_PUBLIC_SET',
                'testContent' => 'public(set)',
            ],
            'promotion, public set, no read visibility, uppercase'     => [
                'testMarker'  => '/* testPublicSetCPPUC */',
                'testType'    => 'T_PUBLIC_SET',
                'testContent' => 'PUBLIC(SET)',
            ],
            'promotion, public set, has read visibility, lowercase'    => [
                'testMarker'  => '/* testPublicPublicSetCPP */',
                'testType'    => 'T_PUBLIC_SET',
                'testContent' => 'public(set)',
            ],
            'promotion, public set, has read visibility, uppercase'    => [
                'testMarker'  => '/* testPublicPublicSetCPPUC */',
                'testType'    => 'T_PUBLIC_SET',
                'testContent' => 'PUBLIC(SET)',
            ],
            'promotion, protected set, no read visibility, lowercase'  => [
                'testMarker'  => '/* testProtectedSetCPP */',
                'testType'    => 'T_PROTECTED_SET',
                'testContent' => 'protected(set)',
            ],
            'promotion, protected set, no read visibility, uppercase'  => [
                'testMarker'  => '/* testProtectedSetCPPUC */',
                'testType'    => 'T_PROTECTED_SET',
                'testContent' => 'PROTECTED(SET)',
            ],
            'promotion, protected set, has read visibility, lowercase' => [
                'testMarker'  => '/* testPublicProtectedSetCPP */',
                'testType'    => 'T_PROTECTED_SET',
                'testContent' => 'protected(set)',
            ],
            'promotion, protected set, has read visibility, uppercase' => [
                'testMarker'  => '/* testPublicProtectedSetCPPUC */',
                'testType'    => 'T_PROTECTED_SET',
                'testContent' => 'PROTECTED(SET)',
            ],
            'promotion, private set, no read visibility, lowercase'    => [
                'testMarker'  => '/* testPrivateSetCPP */',
                'testType'    => 'T_PRIVATE_SET',
                'testContent' => 'private(set)',
            ],
            'promotion, private set, no read visibility, uppercase'    => [
                'testMarker'  => '/* testPrivateSetCPPUC */',
                'testType'    => 'T_PRIVATE_SET',
                'testContent' => 'PRIVATE(SET)',
            ],
            'promotion, private set, has read visibility, lowercase'   => [
                'testMarker'  => '/* testPublicPrivateSetCPP */',
                'testType'    => 'T_PRIVATE_SET',
                'testContent' => 'private(set)',
            ],
            'promotion, private set, has read visibility, uppercase'   => [
                'testMarker'  => '/* testPublicPrivateSetCPPUC */',
                'testType'    => 'T_PRIVATE_SET',
                'testContent' => 'PRIVATE(SET)',
            ],
        ];

    }//end dataAsymmetricVisibility()


    /**
     * Test that things that are not asymmetric visibility keywords are not
     * tokenized as such.
     *
     * @param string $testMarker  The comment which prefaces the target token in the test file.
     * @param string $testType    The expected token type.
     * @param string $testContent The token content to look for.
     *
     * @dataProvider dataNotAsymmetricVisibility
     *
     * @return void
     */
    public function testNotAsymmetricVisibility($testMarker, $testType, $testContent)
    {
        $tokens     = $this->phpcsFile->getTokens();
        $target     = $this->getTargetToken(
            $testMarker,
            [constant($testType)],
            $testContent
        );
        $tokenArray = $tokens[$target];

        $this->assertSame(
            $testType,
            $tokenArray['type'],
            'Token tokenized as '.$tokenArray['type'].' (type)'
        );
        $this->assertSame(
            constant($testType),
            $tokenArray['code'],
            'Token tokenized as '.$tokenArray['type'].' (code)'
        );

    }//end testNotAsymmetricVisibility()


    /**
     * Data provider.
     *
     * @see testNotAsymmetricVisibility()
     *
     * @return array<string, array<string, string>>
     */
    public static function dataNotAsymmetricVisibility()
    {
        return [
            'property, invalid case 1'   => [
                'testMarker'  => '/* testInvalidUnsetProperty */',
                'testType'    => 'T_PUBLIC',
                'testContent' => 'public',
            ],
            'property, invalid case 2'   => [
                'testMarker'  => '/* testInvalidSpaceProperty */',
                'testType'    => 'T_PUBLIC',
                'testContent' => 'public',
            ],
            'property, invalid case 3'   => [
                'testMarker'  => '/* testInvalidCommentProperty */',
                'testType'    => 'T_PROTECTED',
                'testContent' => 'protected',
            ],
            'property, invalid case 4'   => [
                'testMarker'  => '/* testInvalidGetProperty */',
                'testType'    => 'T_PRIVATE',
                'testContent' => 'private',
            ],
            'property, invalid case 5'   => [
                'testMarker'  => '/* testInvalidNoParenProperty */',
                'testType'    => 'T_PRIVATE',
                'testContent' => 'private',
            ],

            // Constructor property promotion.
            'promotion, invalid case 1'  => [
                'testMarker'  => '/* testInvalidUnsetCPP */',
                'testType'    => 'T_PUBLIC',
                'testContent' => 'public',
            ],
            'promotion, invalid case 2'  => [
                'testMarker'  => '/* testInvalidSpaceCPP */',
                'testType'    => 'T_PUBLIC',
                'testContent' => 'public',
            ],
            'promotion, invalid case 3'  => [
                'testMarker'  => '/* testInvalidCommentCPP */',
                'testType'    => 'T_PROTECTED',
                'testContent' => 'protected',
            ],
            'promotion, invalid case 4'  => [
                'testMarker'  => '/* testInvalidGetCPP */',
                'testType'    => 'T_PRIVATE',
                'testContent' => 'private',
            ],
            'promotion, invalid case 5'  => [
                'testMarker'  => '/* testInvalidNoParenCPP */',
                'testType'    => 'T_PRIVATE',
                'testContent' => 'private',
            ],

            // Context sensitivitiy.
            'protected as function name' => [
                'testMarker'  => '/* testProtectedFunctionName */',
                'testType'    => 'T_STRING',
                'testContent' => 'protected',
            ],
            'public as function name'    => [
                'testMarker'  => '/* testPublicFunctionName */',
                'testType'    => 'T_STRING',
                'testContent' => 'public',
            ],
            'set as parameter type'      => [
                'testMarker'  => '/* testSetParameterType */',
                'testType'    => 'T_STRING',
                'testContent' => 'Set',
            ],

            // Live coding.
            'live coding'                => [
                'testMarker'  => '/* testLiveCoding */',
                'testType'    => 'T_PRIVATE',
                'testContent' => 'private',
            ],
        ];

    }//end dataNotAsymmetricVisibility()


}//end class
