From b2b9a04947408fed890357b7bd154af219e98f48 Mon Sep 17 00:00:00 2001 From: Shail Mehta Date: Mon, 19 Oct 2026 14:24:44 +0530 Subject: [PATCH 1/7] Tests: Add unit tests for `wp_start_scraping_edited_file_errors()` --- .../load/wpStartScrapingEditedFileErrors.php | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php diff --git a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php new file mode 100644 index 0000000000000..6274905f3de51 --- /dev/null +++ b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php @@ -0,0 +1,106 @@ +original_request[ $key ] = array_key_exists( $key, $_REQUEST ) ? $_REQUEST[ $key ] : null; + unset( $_REQUEST[ $key ] ); + } + } + + public function tear_down() { + foreach ( $this->original_request as $key => $value ) { + if ( null !== $value ) { + $_REQUEST[ $key ] = $value; + } else { + unset( $_REQUEST[ $key ] ); + } + } + + foreach ( $this->created_transients as $transient ) { + delete_transient( $transient ); + } + + parent::tear_down(); + } + + /** + * @ticket 65819 + */ + public function test_returns_early_without_scrape_request_params() { + wp_start_scraping_edited_file_errors(); + + $this->assertFalse( defined( 'WP_SANDBOX_SCRAPING' ) ); + } + + /** + * @ticket 65819 + */ + public function test_returns_early_when_scrape_params_are_empty() { + $_REQUEST['wp_scrape_key'] = ''; + $_REQUEST['wp_scrape_nonce'] = ''; + + wp_start_scraping_edited_file_errors(); + + $this->assertFalse( defined( 'WP_SANDBOX_SCRAPING' ) ); + } + + /** + * @ticket 65819 + */ + public function test_returns_early_when_scrape_transient_is_missing() { + $_REQUEST['wp_scrape_key'] = 'missingkey1234567890123456789012'; + $_REQUEST['wp_scrape_nonce'] = 'nonce-value'; + + wp_start_scraping_edited_file_errors(); + + $this->assertFalse( defined( 'WP_SANDBOX_SCRAPING' ) ); + } + + /** + * @ticket 65819 + * + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function test_defines_sandbox_constant_when_scrape_nonce_matches() { + $key = 'validkey123456789012345678901234'; + $nonce = 'matching-nonce'; + + $transient = 'scrape_key_' . $key; + set_transient( $transient, $nonce, MINUTE_IN_SECONDS ); + $this->created_transients[] = $transient; + + $_REQUEST['wp_scrape_key'] = $key; + $_REQUEST['wp_scrape_nonce'] = $nonce; + + wp_start_scraping_edited_file_errors(); + + $this->assertTrue( defined( 'WP_SANDBOX_SCRAPING' ) ); + $this->assertTrue( WP_SANDBOX_SCRAPING ); + } +} From d87762e7f9a7a81e09c6d377bfeed30a9730b2ba Mon Sep 17 00:00:00 2001 From: Shail Mehta Date: Mon, 19 Oct 2026 14:29:00 +0530 Subject: [PATCH 2/7] Add annotations for test process and global state Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php index 6274905f3de51..fc6d38894402b 100644 --- a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php +++ b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php @@ -5,6 +5,9 @@ * * @group load * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + * * @covers ::wp_start_scraping_edited_file_errors */ class Tests_Load_WpStartScrapingEditedFileErrors extends WP_UnitTestCase { From b18fe18fe83bdaca5f65854e645aa38c08482f09 Mon Sep 17 00:00:00 2001 From: Shail Mehta Date: Mon, 19 Oct 2026 16:55:05 +0530 Subject: [PATCH 3/7] Apply Feedback Changes --- .../phpunit/tests/load/wpStartScrapingEditedFileErrors.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php index fc6d38894402b..67793ec7b2fb6 100644 --- a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php +++ b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php @@ -5,9 +5,6 @@ * * @group load * - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - * * @covers ::wp_start_scraping_edited_file_errors */ class Tests_Load_WpStartScrapingEditedFileErrors extends WP_UnitTestCase { @@ -101,6 +98,9 @@ public function test_defines_sandbox_constant_when_scrape_nonce_matches() { $_REQUEST['wp_scrape_key'] = $key; $_REQUEST['wp_scrape_nonce'] = $nonce; + // This registers wp_finalize_scraping_edited_file_errors() as a shutdown function. + // It only prints scrape markers when the isolated child process exits, after + // PHPUnit has reported the result, so it doesn't affect the test outcome. wp_start_scraping_edited_file_errors(); $this->assertTrue( defined( 'WP_SANDBOX_SCRAPING' ) ); From 491092e36451adf3ea2f1d7700a4a17f6501db23 Mon Sep 17 00:00:00 2001 From: Shail Mehta Date: Mon, 19 Oct 2026 17:34:09 +0530 Subject: [PATCH 4/7] Clean up transient handling in test case --- .../load/wpStartScrapingEditedFileErrors.php | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php index 67793ec7b2fb6..d71db7bae5dd5 100644 --- a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php +++ b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php @@ -16,13 +16,6 @@ class Tests_Load_WpStartScrapingEditedFileErrors extends WP_UnitTestCase { */ private $original_request = array(); - /** - * Transient keys created during the test. - * - * @var string[] - */ - private $created_transients = array(); - public function set_up() { parent::set_up(); @@ -41,10 +34,6 @@ public function tear_down() { } } - foreach ( $this->created_transients as $transient ) { - delete_transient( $transient ); - } - parent::tear_down(); } @@ -80,30 +69,4 @@ public function test_returns_early_when_scrape_transient_is_missing() { $this->assertFalse( defined( 'WP_SANDBOX_SCRAPING' ) ); } - - /** - * @ticket 65819 - * - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function test_defines_sandbox_constant_when_scrape_nonce_matches() { - $key = 'validkey123456789012345678901234'; - $nonce = 'matching-nonce'; - - $transient = 'scrape_key_' . $key; - set_transient( $transient, $nonce, MINUTE_IN_SECONDS ); - $this->created_transients[] = $transient; - - $_REQUEST['wp_scrape_key'] = $key; - $_REQUEST['wp_scrape_nonce'] = $nonce; - - // This registers wp_finalize_scraping_edited_file_errors() as a shutdown function. - // It only prints scrape markers when the isolated child process exits, after - // PHPUnit has reported the result, so it doesn't affect the test outcome. - wp_start_scraping_edited_file_errors(); - - $this->assertTrue( defined( 'WP_SANDBOX_SCRAPING' ) ); - $this->assertTrue( WP_SANDBOX_SCRAPING ); - } } From 4dd9f1941da1dab581e018eb527ae7e0c717f8a2 Mon Sep 17 00:00:00 2001 From: Shail Mehta Date: Mon, 19 Oct 2026 17:49:27 +0530 Subject: [PATCH 5/7] Add test for defining WP_SANDBOX_SCRAPING --- .../load/wpStartScrapingEditedFileErrors.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php index d71db7bae5dd5..6cad1b107e2a2 100644 --- a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php +++ b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php @@ -69,4 +69,35 @@ public function test_returns_early_when_scrape_transient_is_missing() { $this->assertFalse( defined( 'WP_SANDBOX_SCRAPING' ) ); } + + /** + * Only this test defines WP_SANDBOX_SCRAPING, so it is the only one that + * needs its own process. The tests above can run in the normal process. + * + * @ticket 65819 + * + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function test_defines_sandbox_scraping_when_nonce_matches() { + $key = 'matchingkey12345678901234567890'; + $nonce = 'nonce-value'; + + set_transient( 'scrape_key_' . $key, $nonce, 60 ); + + $_REQUEST['wp_scrape_key'] = $key; + $_REQUEST['wp_scrape_nonce'] = $nonce; + + // A matching nonce registers wp_finalize_scraping_edited_file_errors() as a + // shutdown function. It prints the scrape markers when this child process + // exits, after PHPUnit has already recorded the test result. This is safe + // because the test runs in an isolated process, so the output does not + // affect the result or any other test. + wp_start_scraping_edited_file_errors(); + + $this->assertTrue( defined( 'WP_SANDBOX_SCRAPING' ) ); + $this->assertTrue( WP_SANDBOX_SCRAPING ); + + delete_transient( 'scrape_key_' . $key ); + } } From 223fcbc6a1714c3933a379cd03c4f7521ffc20a5 Mon Sep 17 00:00:00 2001 From: Shail Mehta Date: Mon, 19 Oct 2026 18:03:11 +0530 Subject: [PATCH 6/7] Update wpStartScrapingEditedFileErrors.php --- .../load/wpStartScrapingEditedFileErrors.php | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php index 6cad1b107e2a2..2f791bf50cc5a 100644 --- a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php +++ b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php @@ -88,11 +88,23 @@ public function test_defines_sandbox_scraping_when_nonce_matches() { $_REQUEST['wp_scrape_key'] = $key; $_REQUEST['wp_scrape_nonce'] = $nonce; - // A matching nonce registers wp_finalize_scraping_edited_file_errors() as a - // shutdown function. It prints the scrape markers when this child process - // exits, after PHPUnit has already recorded the test result. This is safe - // because the test runs in an isolated process, so the output does not - // affect the result or any other test. + /* + * A matching nonce registers wp_finalize_scraping_edited_file_errors() as a + * shutdown function. It prints the scrape markers when this child process + * exits, which is after PHPUnit has written the serialized test result to + * stdout. That trailing output would corrupt the result the parent process + * reads back, so discard it. + * + * Shutdown functions run in the order they were registered, so this one + * runs first and opens an output buffer that throws away everything the + * scraping function prints afterwards. + */ + register_shutdown_function( + static function () { + ob_start( static fn() => '' ); + } + ); + wp_start_scraping_edited_file_errors(); $this->assertTrue( defined( 'WP_SANDBOX_SCRAPING' ) ); From 568a29e932d6e68351a5df4715e4bc84035f1450 Mon Sep 17 00:00:00 2001 From: Shail Mehta Date: Mon, 19 Oct 2026 18:17:29 +0530 Subject: [PATCH 7/7] Refactor tests for wpStartScrapingEditedFileErrors --- .../load/wpStartScrapingEditedFileErrors.php | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php index 2f791bf50cc5a..91fdb5b52249c 100644 --- a/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php +++ b/tests/phpunit/tests/load/wpStartScrapingEditedFileErrors.php @@ -41,9 +41,15 @@ public function tear_down() { * @ticket 65819 */ public function test_returns_early_without_scrape_request_params() { + // Other tests in the same run may already have defined the constant, + // so check that this call does not change its state. + $was_defined = defined( 'WP_SANDBOX_SCRAPING' ); + + $this->expectOutputString( '' ); + wp_start_scraping_edited_file_errors(); - $this->assertFalse( defined( 'WP_SANDBOX_SCRAPING' ) ); + $this->assertSame( $was_defined, defined( 'WP_SANDBOX_SCRAPING' ) ); } /** @@ -53,9 +59,13 @@ public function test_returns_early_when_scrape_params_are_empty() { $_REQUEST['wp_scrape_key'] = ''; $_REQUEST['wp_scrape_nonce'] = ''; + $was_defined = defined( 'WP_SANDBOX_SCRAPING' ); + + $this->expectOutputString( '' ); + wp_start_scraping_edited_file_errors(); - $this->assertFalse( defined( 'WP_SANDBOX_SCRAPING' ) ); + $this->assertSame( $was_defined, defined( 'WP_SANDBOX_SCRAPING' ) ); } /** @@ -65,14 +75,20 @@ public function test_returns_early_when_scrape_transient_is_missing() { $_REQUEST['wp_scrape_key'] = 'missingkey1234567890123456789012'; $_REQUEST['wp_scrape_nonce'] = 'nonce-value'; + $was_defined = defined( 'WP_SANDBOX_SCRAPING' ); + + // A nonce mismatch would print an error and die, so returning quietly + // proves the missing transient path was taken. + $this->expectOutputString( '' ); + wp_start_scraping_edited_file_errors(); - $this->assertFalse( defined( 'WP_SANDBOX_SCRAPING' ) ); + $this->assertSame( $was_defined, defined( 'WP_SANDBOX_SCRAPING' ) ); } /** * Only this test defines WP_SANDBOX_SCRAPING, so it is the only one that - * needs its own process. The tests above can run in the normal process. + * needs its own process. * * @ticket 65819 * @@ -88,23 +104,11 @@ public function test_defines_sandbox_scraping_when_nonce_matches() { $_REQUEST['wp_scrape_key'] = $key; $_REQUEST['wp_scrape_nonce'] = $nonce; - /* - * A matching nonce registers wp_finalize_scraping_edited_file_errors() as a - * shutdown function. It prints the scrape markers when this child process - * exits, which is after PHPUnit has written the serialized test result to - * stdout. That trailing output would corrupt the result the parent process - * reads back, so discard it. - * - * Shutdown functions run in the order they were registered, so this one - * runs first and opens an output buffer that throws away everything the - * scraping function prints afterwards. - */ - register_shutdown_function( - static function () { - ob_start( static fn() => '' ); - } - ); - + // A matching nonce registers wp_finalize_scraping_edited_file_errors() as a + // shutdown function. It prints the scrape markers when this child process + // exits, after PHPUnit has already recorded the test result. This is safe + // because the test runs in an isolated process, so the output does not + // affect the result or any other test. wp_start_scraping_edited_file_errors(); $this->assertTrue( defined( 'WP_SANDBOX_SCRAPING' ) );