HEX
Server: LiteSpeed
System: Linux cluster02.load-balancer.x2.network 4.18.0-553.51.1.lve.1.el8.x86_64 #1 SMP Wed May 14 14:34:57 UTC 2025 x86_64
User: kbdhpghp (1098)
PHP: 8.2.29
Disabled: NONE
Upload Files
File: //home/kbdhpghp/www/leia/wp-content/plugins/ewww-image-optimizer/tests/test-plugin-headers.php
<?php
/**
 * Test Plugin Readme and PHP Headers. Adapted from @peterwilsoncc's plugin template.
 *
 * @link https://ewww.io
 * @package Ewww_Image_Optimizer
 */

const OPTIONAL  = 0;
const REQUIRED  = 1;
const FORBIDDEN = 2;

/**
 * Test Plugin Readme and PHP Headers.
 */
class EWWWIO_Test_Plugin_Headers extends WP_UnitTestCase {

	/**
	 * Readme headers specification.
	 *
	 * @var array<string,int> Headers defined in the readme spec. Key: Header; Value: OPTIONAL, REQUIRED, FORBIDDEN.
	 */
	public static $readme_headers = array(
		'Contributors'      => REQUIRED,
		'Tags'              => OPTIONAL,
		'Donate link'       => OPTIONAL,
		'Tested up to'      => REQUIRED,
		'Stable tag'        => REQUIRED,
		'License'           => REQUIRED,
		'License URI'       => OPTIONAL,

		// Plugin file headers that do not belong in the readme.
		'Plugin Name'       => FORBIDDEN,
		'Plugin URI'        => FORBIDDEN,
		'Description'       => FORBIDDEN,
		'Version'           => FORBIDDEN,
		'Author'            => FORBIDDEN,
		'Author URI'        => FORBIDDEN,
		'Text Domain'       => FORBIDDEN,
		'Domain Path'       => FORBIDDEN,
		'Network'           => FORBIDDEN,
		'Update URI'        => FORBIDDEN,
		'Requires at least' => FORBIDDEN, // Both WP and the plugin directory prefer the version in the plugin file.
		'Requires PHP'      => FORBIDDEN, // Both WP and the plugin directory prefer the version in the plugin file.
		'Requires Plugins'  => FORBIDDEN,
	);

	/**
	 * Plugin headers specification
	 *
	 * @var array<string,int> Headers defined in the plugin spec. Key: Header; Value: OPTIONAL, REQUIRED, FORBIDDEN.
	 */
	public static $plugin_headers = array(
		'Plugin Name'       => REQUIRED,
		'Plugin URI'        => OPTIONAL,
		'Description'       => REQUIRED,
		'Version'           => REQUIRED,
		'Requires at least' => REQUIRED, // Not required by the spec but I'm enforcing it.
		'Requires PHP'      => REQUIRED, // Not required by the spec but I'm enforcing it.
		'Author'            => REQUIRED,
		'Author URI'        => OPTIONAL,
		'License'           => REQUIRED,
		'License URI'       => OPTIONAL,
		'Text Domain'       => OPTIONAL,
		'Domain Path'       => OPTIONAL,
		'Network'           => OPTIONAL,
		'Update URI'        => OPTIONAL,
		'Requires Plugins'  => OPTIONAL,

		// Readme file headers that do not belong in the plugin file.
		'Contributors'      => FORBIDDEN,
		'Tags'              => FORBIDDEN,
		'Donate link'       => FORBIDDEN,
		'Stable tag'        => FORBIDDEN,

		/*
		 * Opinionated: Allowed by the spec.
		 *
		 * The WordPress plugin directory will use the plugin file headers if
		 * it exists, and fall back to the readme file if it does not.
		 *
		 * However, the 10up Github Action for deploying updates to the
		 * directory will require a version bump if the plugin file is
		 * modified, so it's best to keep tested up to in the readme file.
		 *
		 * WordPress Core doesn't use the header, it pulls the data in
		 * from the plugin API.
		 */
		'Tested up to'      => FORBIDDEN,
	);

	/**
	 * Headers defined in the plugins readme.text file.
	 *
	 * @var string[] Headers defined in the readme spec Header => value.
	 */
	public static $defined_readme_headers = array();

	/**
	 * Headers defined in the plugin file.
	 *
	 * @var string[] Headers defined in the plugin spec Header => value.
	 */
	public static $defined_plugin_headers = array();

	/**
	 * Set up shared fixtures.
	 */
	public static function wpSetupBeforeClass() {
		// Get the readme headers.
		$readme_file_data = array();
		foreach ( self::$readme_headers as $header => $required ) {
			$readme_file_data[ $header ] = $header;
		}
		self::$defined_readme_headers = get_file_data(
			__DIR__ . '/../readme.txt',
			$readme_file_data
		);
		self::$defined_readme_headers = array_filter( self::$defined_readme_headers );

		// Get the plugin headers.
		// Plugin name.
		$plugin_file_name = basename( dirname( __DIR__ ) ) . '.php';
		if ( ! file_exists( __DIR__ . "/../{$plugin_file_name}" ) ) {
			// Fallback to the generic plugin file name.
			$plugin_file_name = 'plugin.php';
		}

		$plugin_file_data = array();
		foreach ( self::$plugin_headers as $header => $required ) {
			$plugin_file_data[ $header ] = $header;
		}

		self::$defined_plugin_headers = get_file_data(
			__DIR__ . "/../{$plugin_file_name}",
			$plugin_file_data
		);
		self::$defined_plugin_headers = array_filter( self::$defined_plugin_headers );
	}

	/**
	 * Test that the readme file has all required headers.
	 *
	 * @dataProvider data_required_readme_headers
	 *
	 * @param string $header Header to test.
	 */
	public function test_required_readme_headers( $header ) {
		$this->assertArrayHasKey( $header, self::$defined_readme_headers, "The readme file header '{$header}' is missing." );
		$this->assertNotEmpty( self::$defined_readme_headers[ $header ], "The readme file header '{$header}' is empty." );
	}

	/**
	 * Data provider for test_required_readme_headers.
	 *
	 * @return array[] Data provider.
	 */
	public function data_required_readme_headers() {
		$required_headers = array_filter(
			self::$readme_headers,
			function ( $status ) {
				return REQUIRED === $status;
			}
		);
		$headers          = array();
		foreach ( $required_headers as $header => $required ) {
			$headers[ $header ] = array( $header );
		}
		return $headers;
	}

	/**
	 * Test that the readme file does not have any forbidden headers.
	 *
	 * @dataProvider data_forbidden_readme_headers
	 *
	 * @param string $header Header to test.
	 */
	public function test_forbidden_readme_headers( $header ) {
		$this->assertArrayNotHasKey( $header, self::$defined_readme_headers, "The readme file header '{$header}' is forbidden." );
	}

	/**
	 * Data provider for test_forbidden_readme_headers.
	 *
	 * @return array[] Data provider.
	 */
	public function data_forbidden_readme_headers() {
		$forbidden_headers = array_filter(
			self::$readme_headers,
			function ( $status ) {
				return FORBIDDEN === $status;
			}
		);
		$headers           = array();
		foreach ( $forbidden_headers as $header => $required ) {
			$headers[ $header ] = array( $header );
		}
		return $headers;
	}

	/**
	 * Test that the plugin file has all required headers.
	 *
	 * @dataProvider data_required_plugin_headers
	 *
	 * @param string $header Header to test.
	 */
	public function test_required_plugin_headers( $header ) {
		$this->assertArrayHasKey( $header, self::$defined_plugin_headers, "The plugin file header '{$header}' is missing." );
		$this->assertNotEmpty( self::$defined_plugin_headers[ $header ], "The readme file header '{$header}' is empty." );
	}

	/**
	 * Data provider for test_required_plugin_headers.
	 *
	 * @return array[] Data provider.
	 */
	public function data_required_plugin_headers() {
		$required_headers = array_filter(
			self::$plugin_headers,
			function ( $status ) {
				return REQUIRED === $status;
			}
		);
		$headers          = array();
		foreach ( $required_headers as $header => $required ) {
			$headers[ $header ] = array( $header );
		}
		return $headers;
	}

	/**
	 * Test that the plugin file does not have any forbidden headers.
	 *
	 * @dataProvider data_forbidden_plugin_headers
	 *
	 * @param string $header Header to test.
	 */
	public function test_forbidden_plugin_headers( $header ) {
		$this->assertArrayNotHasKey( $header, self::$defined_plugin_headers, "The plugin file header '{$header}' is forbidden." );
	}

	/**
	 * Data provider for test_forbidden_plugin_headers.
	 *
	 * @return array[] Data provider.
	 */
	public function data_forbidden_plugin_headers() {
		$forbidden_headers = array_filter(
			self::$plugin_headers,
			function ( $status ) {
				return FORBIDDEN === $status;
			}
		);
		$headers           = array();
		foreach ( $forbidden_headers as $header => $required ) {
			$headers[ $header ] = array( $header );
		}
		return $headers;
	}

	/**
	 * Test that headers defined in both the readme and plugin file match.
	 *
	 * @dataProvider data_common_headers_match
	 *
	 * @param string      $plugin_header_name Plugin file header name to test.
	 * @param string|null $readme_header_name Readme file header name to test. If null, the plugin header name will be used.
	 */
	public function test_common_headers_match( $plugin_header_name, $readme_header_name = null ) {
		$readme_header_name = $readme_header_name ?? $plugin_header_name;
		if ( empty( self::$defined_plugin_headers[ $plugin_header_name ] ) || empty( self::$defined_readme_headers[ $readme_header_name ] ) ) {
			// The header is not common to both files so the test passes.
			$this->assertTrue( true );
			return;
		}

		$plugin_header = self::$defined_plugin_headers[ $plugin_header_name ];
		$readme_header = self::$defined_readme_headers[ $readme_header_name ];

		$message = "The header '{$plugin_header_name}' does not match between the readme and plugin file.";
		if ( $plugin_header_name !== $readme_header_name ) {
			$message = "The plugin header '{$plugin_header_name}' does not match the readme header '{$readme_header_name}'.";
		}

		$this->assertSame( $plugin_header, $readme_header, $message );
	}

	/**
	 * Data provider for test_common_headers_match.
	 *
	 * @return array[] Data provider.
	 */
	public function data_common_headers_match() {
		// Can't use the defined headers as they are not defined until after this is called.
		$common_headers = array_intersect_key(
			self::$readme_headers,
			self::$plugin_headers
		);

		$headers = array();
		// Always test the version matches the stable tag.
		$headers['Stable tag matches version'] = array( 'Version', 'Stable tag' );

		foreach ( $common_headers as $header => $value ) {
			$headers[ $header ] = array( $header );
		}
		return $headers;
	}
}