From f9a6d8b4283b11f5b030cda9d6209572ef2ad3be Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Thu, 12 Jan 2023 18:30:33 +0000 Subject: [PATCH 1/8] create Checks class --- includes/Checker/Checks.php | 178 ++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 includes/Checker/Checks.php diff --git a/includes/Checker/Checks.php b/includes/Checker/Checks.php new file mode 100644 index 000000000..c680277a8 --- /dev/null +++ b/includes/Checker/Checks.php @@ -0,0 +1,178 @@ +check_context = new Check_Context( $plugin_main_file ); + } + + /** + * Prepares the environment for running checks and returns a cleanup function. + * + * @since n.e.x.t + * + * @return callable Cleanup function to revert any changes made here. + * + * @throws Exception Thrown when preparation fails. + */ + public function prepare() { + // TODO: Add preparations once implemented. + return function() {}; + } + + /** + * Runs all checks against the plugin. + * + * @since n.e.x.t + * + * @return Check_Result Object containing all check results. + * + * @throws Exception Thrown when checks fail with critical error. + */ + public function run_all_checks() { + if ( ! $this->prepared ) { + throw new Exception( + __( 'Environment not prepared to run checks. The Checks::prepare() method must be called first.', 'plugin-check' ) + ); + } + + $result = new Check_Result( $this->main_context, $this->check_context ); + $checks = $this->get_checks(); + + array_walk( + $checks, + function( Check $check ) use ( $result ) { + $this->run_check_with_result( $check, $result ); + } + ); + + return $result; + } + + /** + * Runs a single check against the plugin. + * + * @since n.e.x.t + * + * @param string $check Check class name. + * @return Check_Result Object containing all check results. + * + * @throws Exception Thrown when check fails with critical error. + */ + public function run_single_check( $check ) { + if ( ! $this->prepared ) { + throw new Exception( + __( 'Environment not prepared to run checks. The Checks::prepare() method must be called first.', 'plugin-check' ) + ); + } + + $result = new Check_Result( $this->main_context, $this->check_context ); + $checks = $this->get_checks(); + + // Look up the check based on the $check variable. + $check_index = array_search( $check, $checks, true ); + if ( false === $check_index ) { + throw new Exception( + sprintf( + /* translators: %s: class name */ + __( 'Invalid check class name %s.', 'plugin-check' ), + $check + ) + ); + } + + $this->run_check_with_result( $checks[ $check_index ], $result ); + + return $result; + } + + /** + * Runs a given check with the given result object to amend. + * + * @since n.e.x.t + * + * @param Check $check The check to run. + * @param Check_Result $result The result object to amend. + * + * @throws Exception Thrown when check fails with critical error. + */ + protected function run_check_with_result( Check $check, Check_Result $result ) { + // If $check implements Preparation interface, ensure the preparation and clean up is run. + if ( $check instanceof Preparation ) { + $cleanup = $check->prepare(); + + try { + $check->run( $result ); + } catch ( Exception $e ) { + // Run clean up in case of any exception thrown from check. + $cleanup(); + throw $e; + } + + $cleanup(); + return; + } + + // Otherwise, just run the check. + $check->run( $result ); + } + + /** + * Gets the available plugin check classes. + * + * @since n.e.x.t + * + * @return array List of plugin check class instances implementing the Check interface. + */ + protected function get_checks() { + // TODO: Add checks once implemented. + $checks = array(); + + /** + * Filters the available plugin check classes. + * + * @since n.e.x.t + * + * @param array $checks List of plugin check class instances implementing the Check interface. + */ + return apply_filters( 'wp_plugin_check_checks', $checks ); + } +} From b227986fa7a792ba5f6b51acf3945ae83021013a Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Thu, 12 Jan 2023 18:46:24 +0000 Subject: [PATCH 2/8] add tests --- includes/Checker/Checks.php | 8 +++++-- tests/Checker/Checks_Tests.php | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/Checker/Checks_Tests.php diff --git a/includes/Checker/Checks.php b/includes/Checker/Checks.php index c680277a8..af52c7d92 100644 --- a/includes/Checker/Checks.php +++ b/includes/Checker/Checks.php @@ -55,7 +55,11 @@ public function __construct( $plugin_main_file ) { */ public function prepare() { // TODO: Add preparations once implemented. - return function() {}; + $this->prepared = true; + + return function() { + $this->prepared = false; + }; } /** @@ -162,7 +166,7 @@ protected function run_check_with_result( Check $check, Check_Result $result ) { * * @return array List of plugin check class instances implementing the Check interface. */ - protected function get_checks() { + public function get_checks() { // TODO: Add checks once implemented. $checks = array(); diff --git a/tests/Checker/Checks_Tests.php b/tests/Checker/Checks_Tests.php new file mode 100644 index 000000000..99579f23e --- /dev/null +++ b/tests/Checker/Checks_Tests.php @@ -0,0 +1,44 @@ +checks = new Checks( 'test-plugin/test-plugin.php' ); + } + + public function test_prepare_returns_callable() { + $cleanup = $this->checks->prepare(); + + $this->assertIsCallable( $cleanup ); + } + + public function test_get_checks_returns_array() { + $checks = $this->checks->get_checks(); + + $this->assertIsArray( $checks ); + } + + public function test_run_all_checks_throws_exception_if_not_prepared() { + $this->expectException(Exception::class); + + $this->checks->run_all_checks(); + } + + public function test_run_single_check_throws_exception_if_not_prepared() { + $this->expectException(Exception::class); + + $this->checks->run_single_check( 'check' ); + } +} From b377148532c425a6a8a3b4e29a3494f661aeb231 Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Thu, 12 Jan 2023 18:51:11 +0000 Subject: [PATCH 3/8] update checks test --- tests/Checker/Checks_Tests.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/Checker/Checks_Tests.php b/tests/Checker/Checks_Tests.php index 99579f23e..2e4202d98 100644 --- a/tests/Checker/Checks_Tests.php +++ b/tests/Checker/Checks_Tests.php @@ -24,20 +24,32 @@ public function test_prepare_returns_callable() { $this->assertIsCallable( $cleanup ); } - public function test_get_checks_returns_array() { + public function test_get_checks_returns_array_of_expected_checks() { + $expected = array( + 'example_check_class', + ); + + add_filter( + 'wp_plugin_check_checks', + function( $checks ) use ( $expected ) { + return $expected; + } + ); + $checks = $this->checks->get_checks(); $this->assertIsArray( $checks ); + $this->assertSame( $expected, $checks ); } public function test_run_all_checks_throws_exception_if_not_prepared() { - $this->expectException(Exception::class); + $this->expectException( Exception::class ); $this->checks->run_all_checks(); } public function test_run_single_check_throws_exception_if_not_prepared() { - $this->expectException(Exception::class); + $this->expectException( Exception::class ); $this->checks->run_single_check( 'check' ); } From fb2792bb3f98a9a0336030ded2b09167fc158533 Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Mon, 16 Jan 2023 16:35:47 +0000 Subject: [PATCH 4/8] remove prepare method and property --- includes/Checker/Checks.php | 38 ------------------------------------- 1 file changed, 38 deletions(-) diff --git a/includes/Checker/Checks.php b/includes/Checker/Checks.php index af52c7d92..ad1b134a5 100644 --- a/includes/Checker/Checks.php +++ b/includes/Checker/Checks.php @@ -25,14 +25,6 @@ class Checks implements Preparation { */ protected $check_context; - /** - * Internal flag for whether the environment is prepared. - * - * @since n.e.x.t - * @var bool - */ - protected $prepared = false; - /** * Sets the main context and the main file of the plugin to check. * @@ -44,24 +36,6 @@ public function __construct( $plugin_main_file ) { $this->check_context = new Check_Context( $plugin_main_file ); } - /** - * Prepares the environment for running checks and returns a cleanup function. - * - * @since n.e.x.t - * - * @return callable Cleanup function to revert any changes made here. - * - * @throws Exception Thrown when preparation fails. - */ - public function prepare() { - // TODO: Add preparations once implemented. - $this->prepared = true; - - return function() { - $this->prepared = false; - }; - } - /** * Runs all checks against the plugin. * @@ -72,12 +46,6 @@ public function prepare() { * @throws Exception Thrown when checks fail with critical error. */ public function run_all_checks() { - if ( ! $this->prepared ) { - throw new Exception( - __( 'Environment not prepared to run checks. The Checks::prepare() method must be called first.', 'plugin-check' ) - ); - } - $result = new Check_Result( $this->main_context, $this->check_context ); $checks = $this->get_checks(); @@ -102,12 +70,6 @@ function( Check $check ) use ( $result ) { * @throws Exception Thrown when check fails with critical error. */ public function run_single_check( $check ) { - if ( ! $this->prepared ) { - throw new Exception( - __( 'Environment not prepared to run checks. The Checks::prepare() method must be called first.', 'plugin-check' ) - ); - } - $result = new Check_Result( $this->main_context, $this->check_context ); $checks = $this->get_checks(); From 538efefe002182af90d115a7071825f7b14947a6 Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Mon, 16 Jan 2023 16:36:49 +0000 Subject: [PATCH 5/8] remove references to main context --- includes/Checker/Checks.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/Checker/Checks.php b/includes/Checker/Checks.php index ad1b134a5..f402e64fe 100644 --- a/includes/Checker/Checks.php +++ b/includes/Checker/Checks.php @@ -15,7 +15,7 @@ * * @since n.e.x.t */ -class Checks implements Preparation { +class Checks { /** * Context for the plugin to check. @@ -46,7 +46,7 @@ public function __construct( $plugin_main_file ) { * @throws Exception Thrown when checks fail with critical error. */ public function run_all_checks() { - $result = new Check_Result( $this->main_context, $this->check_context ); + $result = new Check_Result( $this->check_context ); $checks = $this->get_checks(); array_walk( @@ -70,7 +70,7 @@ function( Check $check ) use ( $result ) { * @throws Exception Thrown when check fails with critical error. */ public function run_single_check( $check ) { - $result = new Check_Result( $this->main_context, $this->check_context ); + $result = new Check_Result( $this->check_context ); $checks = $this->get_checks(); // Look up the check based on the $check variable. From 493eb6291a4f967e06c952fab283c14344524790 Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Thu, 19 Jan 2023 19:56:17 +0000 Subject: [PATCH 6/8] add run_checks method --- composer.json | 5 +++ includes/Checker/Checks.php | 55 +++++++++------------------ tests/Checker/Checks_Tests.php | 47 ++++++++++++++++------- tests/testdata/Checks/Empty_Check.php | 12 ++++++ tests/testdata/Checks/Error_Check.php | 21 ++++++++++ 5 files changed, 90 insertions(+), 50 deletions(-) create mode 100644 tests/testdata/Checks/Empty_Check.php create mode 100644 tests/testdata/Checks/Error_Check.php diff --git a/composer.json b/composer.json index e52b08ca4..86cccf0a5 100644 --- a/composer.json +++ b/composer.json @@ -30,5 +30,10 @@ "psr-4": { "WordPress\\Plugin_Check\\": "includes/" } + }, + "autoload-dev": { + "psr-4": { + "WordPress\\Plugin_Check\\Tests\\": "tests/utils" + } } } diff --git a/includes/Checker/Checks.php b/includes/Checker/Checks.php index f402e64fe..8bdc2b4aa 100644 --- a/includes/Checker/Checks.php +++ b/includes/Checker/Checks.php @@ -8,6 +8,7 @@ namespace WordPress\Plugin_Check\Checker; use WordPress\Plugin_Check\Checker\Check_Context; +use ReflectionClass; use Exception; /** @@ -37,20 +38,31 @@ public function __construct( $plugin_main_file ) { } /** - * Runs all checks against the plugin. + * Runs a single check against the plugin. * * @since n.e.x.t * + * @param array $checks Ab array of Check class names to run. * @return Check_Result Object containing all check results. * - * @throws Exception Thrown when checks fail with critical error. + * @throws Exception Thrown when check fails with critical error. */ - public function run_all_checks() { - $result = new Check_Result( $this->check_context ); - $checks = $this->get_checks(); + public function run_checks( array $checks ) { + $result = new Check_Result( $this->check_context ); + $all_checks = $this->get_checks(); + + // Create an array of Check objects to run based on the check names passed. + $checks_to_run = array_filter( + $all_checks, + function( $check ) use ( $checks ) { + $check_name = ( new ReflectionClass( $check ) )->getShortName(); + return in_array( $check_name, $checks, true ); + } + ); + // Run the checks. array_walk( - $checks, + $checks_to_run, function( Check $check ) use ( $result ) { $this->run_check_with_result( $check, $result ); } @@ -59,37 +71,6 @@ function( Check $check ) use ( $result ) { return $result; } - /** - * Runs a single check against the plugin. - * - * @since n.e.x.t - * - * @param string $check Check class name. - * @return Check_Result Object containing all check results. - * - * @throws Exception Thrown when check fails with critical error. - */ - public function run_single_check( $check ) { - $result = new Check_Result( $this->check_context ); - $checks = $this->get_checks(); - - // Look up the check based on the $check variable. - $check_index = array_search( $check, $checks, true ); - if ( false === $check_index ) { - throw new Exception( - sprintf( - /* translators: %s: class name */ - __( 'Invalid check class name %s.', 'plugin-check' ), - $check - ) - ); - } - - $this->run_check_with_result( $checks[ $check_index ], $result ); - - return $result; - } - /** * Runs a given check with the given result object to amend. * diff --git a/tests/Checker/Checks_Tests.php b/tests/Checker/Checks_Tests.php index 2e4202d98..7292471fa 100644 --- a/tests/Checker/Checks_Tests.php +++ b/tests/Checker/Checks_Tests.php @@ -6,6 +6,7 @@ */ use Exception; +use WordPress\Plugin_Check\Checker\Check_Result; use WordPress\Plugin_Check\Checker\Checks; class Checks_Tests extends WP_UnitTestCase { @@ -18,15 +19,10 @@ public function set_up() { $this->checks = new Checks( 'test-plugin/test-plugin.php' ); } - public function test_prepare_returns_callable() { - $cleanup = $this->checks->prepare(); - - $this->assertIsCallable( $cleanup ); - } - public function test_get_checks_returns_array_of_expected_checks() { $expected = array( - 'example_check_class', + new WordPress\Plugin_Check\Tests\Empty_Check(), + new WordPress\Plugin_Check\Tests\Error_Check(), ); add_filter( @@ -42,15 +38,40 @@ function( $checks ) use ( $expected ) { $this->assertSame( $expected, $checks ); } - public function test_run_all_checks_throws_exception_if_not_prepared() { - $this->expectException( Exception::class ); + public function test_run_checks() { + $expected = array( + new WordPress\Plugin_Check\Tests\Empty_Check(), + ); + + add_filter( + 'wp_plugin_check_checks', + function( $checks ) use ( $expected ) { + return $expected; + } + ); - $this->checks->run_all_checks(); + $results = $this->checks->run_checks( array( 'Example_Check' ) ); + + $this->assertInstanceOf( Check_Result::class, $results ); + $this->assertEmpty( $results->get_warnings() ); + $this->assertEmpty( $results->get_errors() ); } - public function test_run_single_check_throws_exception_if_not_prepared() { - $this->expectException( Exception::class ); + public function test_run_checks_with_error() { + $expected = array( + new WordPress\Plugin_Check\Tests\Error_Check(), + ); + + add_filter( + 'wp_plugin_check_checks', + function( $checks ) use ( $expected ) { + return $expected; + } + ); + + $results = $this->checks->run_checks( array( 'Error_Check' ) ); - $this->checks->run_single_check( 'check' ); + $this->assertEmpty( $results->get_warnings() ); + $this->assertNotEmpty( $results->get_errors() ); } } diff --git a/tests/testdata/Checks/Empty_Check.php b/tests/testdata/Checks/Empty_Check.php new file mode 100644 index 000000000..7a81abff2 --- /dev/null +++ b/tests/testdata/Checks/Empty_Check.php @@ -0,0 +1,12 @@ +add_message( + true, + 'Error message', + array( + 'code' => 'check_error', + 'file' => 'error-file.php', + 'line' => 10, + 'column' => 5, + ) + ); + } +} From d7696885c205e6d44c6e5c9fe0b57df5504a424f Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Thu, 19 Jan 2023 20:08:37 +0000 Subject: [PATCH 7/8] update composer autoload-dev --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 86cccf0a5..9da9fd5d7 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ }, "autoload-dev": { "psr-4": { - "WordPress\\Plugin_Check\\Tests\\": "tests/utils" + "WordPress\\Plugin_Check\\Tests\\": "tests/testdata" } } } From 00487f7d6bde106447e54d938b99c29227703797 Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Tue, 24 Jan 2023 17:20:26 +0000 Subject: [PATCH 8/8] update check matching and tests --- composer.json | 2 +- includes/Checker/Checks.php | 12 ++++------ tests/Checker/Checks_Tests.php | 34 +++++++++++++++++---------- tests/testdata/Checks/Empty_Check.php | 2 +- tests/testdata/Checks/Error_Check.php | 2 +- 5 files changed, 30 insertions(+), 22 deletions(-) diff --git a/composer.json b/composer.json index 9da9fd5d7..18b9568c4 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ }, "autoload-dev": { "psr-4": { - "WordPress\\Plugin_Check\\Tests\\": "tests/testdata" + "WordPress\\Plugin_Check\\Test_Data\\": "tests/testdata" } } } diff --git a/includes/Checker/Checks.php b/includes/Checker/Checks.php index 8bdc2b4aa..7219269df 100644 --- a/includes/Checker/Checks.php +++ b/includes/Checker/Checks.php @@ -8,7 +8,6 @@ namespace WordPress\Plugin_Check\Checker; use WordPress\Plugin_Check\Checker\Check_Context; -use ReflectionClass; use Exception; /** @@ -38,11 +37,11 @@ public function __construct( $plugin_main_file ) { } /** - * Runs a single check against the plugin. + * Runs checks against the plugin. * * @since n.e.x.t * - * @param array $checks Ab array of Check class names to run. + * @param array $checks An array of Check objects to run. * @return Check_Result Object containing all check results. * * @throws Exception Thrown when check fails with critical error. @@ -53,10 +52,9 @@ public function run_checks( array $checks ) { // Create an array of Check objects to run based on the check names passed. $checks_to_run = array_filter( - $all_checks, - function( $check ) use ( $checks ) { - $check_name = ( new ReflectionClass( $check ) )->getShortName(); - return in_array( $check_name, $checks, true ); + $checks, + function( $check ) use ( $all_checks ) { + return in_array( $check, $all_checks, true ); } ); diff --git a/tests/Checker/Checks_Tests.php b/tests/Checker/Checks_Tests.php index 7292471fa..fd14b7702 100644 --- a/tests/Checker/Checks_Tests.php +++ b/tests/Checker/Checks_Tests.php @@ -21,8 +21,8 @@ public function set_up() { public function test_get_checks_returns_array_of_expected_checks() { $expected = array( - new WordPress\Plugin_Check\Tests\Empty_Check(), - new WordPress\Plugin_Check\Tests\Error_Check(), + new WordPress\Plugin_Check\Test_Data\Empty_Check(), + new WordPress\Plugin_Check\Test_Data\Error_Check(), ); add_filter( @@ -39,18 +39,23 @@ function( $checks ) use ( $expected ) { } public function test_run_checks() { - $expected = array( - new WordPress\Plugin_Check\Tests\Empty_Check(), + $all_checks = array( + new WordPress\Plugin_Check\Test_Data\Empty_Check(), + new WordPress\Plugin_Check\Test_Data\Error_Check(), + ); + + $checks_to_run = array( + $all_checks[0], ); add_filter( 'wp_plugin_check_checks', - function( $checks ) use ( $expected ) { - return $expected; + function( $checks ) use ( $all_checks ) { + return $all_checks; } ); - $results = $this->checks->run_checks( array( 'Example_Check' ) ); + $results = $this->checks->run_checks( $checks_to_run ); $this->assertInstanceOf( Check_Result::class, $results ); $this->assertEmpty( $results->get_warnings() ); @@ -58,18 +63,23 @@ function( $checks ) use ( $expected ) { } public function test_run_checks_with_error() { - $expected = array( - new WordPress\Plugin_Check\Tests\Error_Check(), + $all_checks = array( + new WordPress\Plugin_Check\Test_Data\Empty_Check(), + new WordPress\Plugin_Check\Test_Data\Error_Check(), + ); + + $checks_to_run = array( + $all_checks[1], ); add_filter( 'wp_plugin_check_checks', - function( $checks ) use ( $expected ) { - return $expected; + function( $checks ) use ( $all_checks ) { + return $all_checks; } ); - $results = $this->checks->run_checks( array( 'Error_Check' ) ); + $results = $this->checks->run_checks( $checks_to_run ); $this->assertEmpty( $results->get_warnings() ); $this->assertNotEmpty( $results->get_errors() ); diff --git a/tests/testdata/Checks/Empty_Check.php b/tests/testdata/Checks/Empty_Check.php index 7a81abff2..dcbbc2524 100644 --- a/tests/testdata/Checks/Empty_Check.php +++ b/tests/testdata/Checks/Empty_Check.php @@ -1,6 +1,6 @@