<?php
/**
 * PHPCompatibility, an external standard for PHP_CodeSniffer.
 *
 * @package   PHPCompatibility
 * @copyright 2012-2020 PHPCompatibility Contributors
 * @license   https://opensource.org/licenses/LGPL-3.0 LGPL3
 * @link      https://github.com/PHPCompatibility/PHPCompatibility
 */

namespace PHPCompatibility\Tests\FunctionUse;

use PHPCompatibility\Tests\BaseSniffTestCase;

/**
 * Test the RemovedFunctionParameters sniff.
 *
 * @group removedFunctionParameters
 * @group functionUse
 *
 * @covers \PHPCompatibility\Sniffs\FunctionUse\RemovedFunctionParametersSniff
 *
 * @since 7.0.0
 */
class RemovedFunctionParametersUnitTest extends BaseSniffTestCase
{

    /**
     * testRemovedParameter
     *
     * @dataProvider dataRemovedParameter
     *
     * @param string $functionName  Function name.
     * @param string $parameterName Parameter name.
     * @param string $removedIn     The PHP version in which the parameter was removed.
     * @param array  $lines         The line numbers in the test file which apply to this parameter.
     * @param string $okVersion     A PHP version in which the parameter was ok to be used.
     * @param string $testVersion   Optional. A PHP version in which to test for the removal message.
     *
     * @return void
     */
    public function testRemovedParameter($functionName, $parameterName, $removedIn, $lines, $okVersion, $testVersion = null)
    {
        $file = $this->sniffFile(__FILE__, $okVersion);
        foreach ($lines as $line) {
            $this->assertNoViolation($file, $line);
        }

        $errorVersion = (isset($testVersion)) ? $testVersion : $removedIn;
        $file         = $this->sniffFile(__FILE__, $errorVersion);
        $error        = "The \"{$parameterName}\" parameter for function {$functionName}() is removed since PHP {$removedIn}";
        foreach ($lines as $line) {
            $this->assertError($file, $line, $error);
        }
    }

    /**
     * Data provider.
     *
     * @see testRemovedParameter()
     *
     * @return array
     */
    public static function dataRemovedParameter()
    {
        return [
            ['ldap_first_attribute', 'ber_identifier', '5.2.4', [11, 59], '5.2', '5.3'],
            ['ldap_next_attribute', 'ber_identifier', '5.2.4', [12], '5.2', '5.3'],
            ['mb_decode_numericentity', 'is_hex', '8.0', [24], '7.4'],
            ['pg_connect', 'options', '8.0', [27], '7.4'],
            ['pg_connect', 'tty', '8.0', [27], '7.4'],
            ['pg_connect', 'dbname', '8.0', [27], '7.4'],
            ['imap_headerinfo', 'defaulthost', '8.0', [30], '7.4'],
            ['odbc_exec', 'flags', '8.0', [33], '7.4'],
            ['odbc_do', 'flags', '8.0', [34], '7.4'],
            ['imagerotate', 'ignore_transparent', '8.3', [63], '8.2'],
        ];
    }


    /**
     * testDeprecatedRemovedParameter
     *
     * @dataProvider dataDeprecatedRemovedParameter
     *
     * @param string $functionName  Function name.
     * @param string $parameterName Parameter name.
     * @param string $deprecatedIn  The PHP version in which the parameter was deprecated.
     * @param string $removedIn     The PHP version in which the parameter was removed.
     * @param array  $lines         The line numbers in the test file which apply to this parameter.
     * @param string $okVersion     A PHP version in which the parameter was ok to be used.
     *
     * @return void
     */
    public function testDeprecatedRemovedParameter($functionName, $parameterName, $deprecatedIn, $removedIn, $lines, $okVersion)
    {
        $file = $this->sniffFile(__FILE__, $okVersion);
        foreach ($lines as $line) {
            $this->assertNoViolation($file, $line);
        }

        $file  = $this->sniffFile(__FILE__, $deprecatedIn);
        $error = "The \"{$parameterName}\" parameter for function {$functionName}() is deprecated since PHP {$deprecatedIn}";
        foreach ($lines as $line) {
            $this->assertWarning($file, $line, $error);
        }

        $file  = $this->sniffFile(__FILE__, $removedIn);
        $error = "The \"{$parameterName}\" parameter for function {$functionName}() is deprecated since PHP {$deprecatedIn} and removed since PHP {$removedIn}";
        foreach ($lines as $line) {
            $this->assertError($file, $line, $error);
        }
    }

    /**
     * Data provider.
     *
     * @see testDeprecatedRemovedParameter()
     *
     * @return array
     */
    public static function dataDeprecatedRemovedParameter()
    {
        return [
            ['mktime', 'isDST', '5.1', '7.0', [8], '5.0'],
            ['gmmktime', 'isDST', '5.1', '7.0', [9], '5.0'],
            ['define', 'case_insensitive', '7.3', '8.0', [15, 44], '7.2'],

            ['curl_version', 'age', '7.4', '8.0', [18], '7.3'],
            ['curl_version', 'age', '7.4', '8.0', [19], '7.3'],
            ['curl_version', 'age', '7.4', '8.0', [20], '7.3'],
            ['curl_version', 'age', '7.4', '8.0', [21], '7.3'],
        ];
    }


    /**
     * Test that use of a deprecated parameter is detected correctly.
     *
     * @dataProvider dataDeprecatedParameter
     *
     * @param string $functionName  Function name.
     * @param string $parameterName Parameter name.
     * @param string $deprecatedIn  The PHP version in which the parameter was deprecated.
     * @param array  $lines         The line numbers in the test file which apply to this parameter.
     * @param string $okVersion     A PHP version in which the parameter was ok to be used.
     * @param string $testVersion   Optional. A PHP version in which to test for the deprecation message.
     *
     * @return void
     */
    public function testDeprecatedParameter($functionName, $parameterName, $deprecatedIn, $lines, $okVersion, $testVersion = null)
    {
        $file = $this->sniffFile(__FILE__, $okVersion);
        foreach ($lines as $line) {
            $this->assertNoViolation($file, $line);
        }

        $errorVersion = (isset($testVersion)) ? $testVersion : $deprecatedIn;
        $file         = $this->sniffFile(__FILE__, $errorVersion);
        $error        = "The \"{$parameterName}\" parameter for function {$functionName}() is deprecated since PHP {$deprecatedIn}";
        foreach ($lines as $line) {
            $this->assertWarning($file, $line, $error);
        }
    }

    /**
     * Data provider.
     *
     * @see testDeprecatedParameter()
     *
     * @return array
     */
    public static function dataDeprecatedParameter()
    {
        return [
            ['imagepolygon', 'num_points', '8.1', [36, 47], '8.0'],
            ['imageopenpolygon', 'num_points', '8.1', [37], '8.0'],
            ['imagefilledpolygon', 'num_points', '8.1', [38], '8.0'],
            ['mysqli_get_client_info', 'mysql', '8.1', [40], '8.0'],
            ['ldap_exop', 'response_data', '8.4', [65], '8.3'],
            ['ldap_exop', 'response_oid', '8.4', [65], '8.3'],
            ['session_set_save_handler', 'read', '8.4', [68], '8.3'],
            ['session_set_save_handler', 'write', '8.4', [68], '8.3'],
            ['session_set_save_handler', 'destroy', '8.4', [68], '8.3'],
            ['session_set_save_handler', 'gc', '8.4', [68], '8.3'],
            ['session_set_save_handler', 'create_sid', '8.4', [68], '8.3'],
            ['session_set_save_handler', 'validate_sid', '8.4', [68], '8.3'],
            ['session_set_save_handler', 'update_timestamp', '8.4', [68], '8.3'],
            ['mysqli_store_result', 'mode', '8.4', [71], '8.3'],
        ];
    }


    /**
     * testNoFalsePositives
     *
     * @dataProvider dataNoFalsePositives
     *
     * @param int $line The line number.
     *
     * @return void
     */
    public function testNoFalsePositives($line)
    {
        $file = $this->sniffFile(__FILE__, '99.0'); // High version beyond latest deprecation.
        $this->assertNoViolation($file, $line);
    }

    /**
     * Data provider.
     *
     * @see testNoFalsePositives()
     *
     * @return array
     */
    public static function dataNoFalsePositives()
    {
        return [
            [4],
            [5],
            [14],
            [17],
            [23],
            [26],
            [29],
            [32],
            [50],
            [53],
            [62],
            [67],
        ];
    }


    /**
     * Verify no notices are thrown at all.
     *
     * @return void
     */
    public function testNoViolationsInFileOnValidVersion()
    {
        $file = $this->sniffFile(__FILE__, '5.0'); // Low version below the first deprecation.
        $this->assertNoViolation($file);
    }
}
