From 5ac38988da0747668c6d56f002c4b0e781595da3 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 21 Jul 2026 11:50:56 -0400 Subject: [PATCH 1/2] Capture malformed PHPUnit bootstrap payloads --- .../runtime-playground/src/php-bootstrap.ts | 14 ++++++++++++- ...hpunit-runtime-failure-diagnostics.test.ts | 7 +++++-- ...unit-bootstrap-failure.integration.test.ts | 21 +++++++++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/packages/runtime-playground/src/php-bootstrap.ts b/packages/runtime-playground/src/php-bootstrap.ts index 93a06f60..3995da35 100644 --- a/packages/runtime-playground/src/php-bootstrap.ts +++ b/packages/runtime-playground/src/php-bootstrap.ts @@ -27,7 +27,7 @@ export function bootstrapPhpCode(spec: RuntimeCreateSpec, code: string, args: st const command = splitLeadingStrictTypesDeclare(code) - return `' . base64_decode(${JSON.stringify(Buffer.from(code, "utf8").toString("base64"))})); +} catch (Throwable $wp_codebox_bootstrap_throwable) { + @file_put_contents(${JSON.stringify(path)}, 'STAGE_FAIL:bootstrap:' . get_class($wp_codebox_bootstrap_throwable) . ': ' . $wp_codebox_bootstrap_throwable->getMessage() . ' at ' . $wp_codebox_bootstrap_throwable->getFile() . ':' . $wp_codebox_bootstrap_throwable->getLine() . "\\n", FILE_APPEND); + throw $wp_codebox_bootstrap_throwable; +}` } function phpFailureDiagnosticFilePhp(path: string): string { diff --git a/tests/phpunit-runtime-failure-diagnostics.test.ts b/tests/phpunit-runtime-failure-diagnostics.test.ts index 266d54c6..93ec3c23 100644 --- a/tests/phpunit-runtime-failure-diagnostics.test.ts +++ b/tests/phpunit-runtime-failure-diagnostics.test.ts @@ -42,8 +42,11 @@ const captured = await readFile(join(artifactRoot, "files", "phpunit", ".pg-test assert.match(captured, /Bootstrap failed with token: \[redacted\]/) assert.doesNotMatch(captured, new RegExp(secret)) -const preBootstrapRecorder = submittedCode.indexOf("STAGE_FATAL:bootstrap:") -const wordpressBootstrap = submittedCode.indexOf("require_once '/wordpress/wp-load.php';") +const encodedBootstrap = submittedCode.match(/base64_decode\("([A-Za-z0-9+/=]+)"\)/)?.[1] +assert.ok(encodedBootstrap, "PHPUnit payload must execute inside the bootstrap diagnostic wrapper") +const decodedBootstrap = Buffer.from(encodedBootstrap, "base64").toString("utf8") +const preBootstrapRecorder = decodedBootstrap.indexOf("STAGE_FATAL:bootstrap:") +const wordpressBootstrap = decodedBootstrap.indexOf("require_once '/wordpress/wp-load.php';") assert.ok(preBootstrapRecorder >= 0 && preBootstrapRecorder < wordpressBootstrap, "fatal diagnostics must be recorded before the WordPress bootstrap boundary") console.log("phpunit runtime failure diagnostics ok") diff --git a/tests/playground-phpunit-bootstrap-failure.integration.test.ts b/tests/playground-phpunit-bootstrap-failure.integration.test.ts index 86287f80..d1a3d140 100644 --- a/tests/playground-phpunit-bootstrap-failure.integration.test.ts +++ b/tests/playground-phpunit-bootstrap-failure.integration.test.ts @@ -12,6 +12,7 @@ const fatalMuPlugin = join(root, "fatal-bootstrap.php") const recipePath = join(root, "recipe.json") const artifactsPath = join(root, "artifacts") const exitArtifactsPath = join(root, "exit-artifacts") +const parseArtifactsPath = join(root, "parse-artifacts") try { await writeFile(fatalMuPlugin, ` Date: Tue, 21 Jul 2026 13:00:19 -0400 Subject: [PATCH 2/2] Treat empty PHPUnit suites as successful --- packages/runtime-playground/src/phpunit-command-handlers.ts | 6 +++--- tests/phpunit-project-autoload.test.ts | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/runtime-playground/src/phpunit-command-handlers.ts b/packages/runtime-playground/src/phpunit-command-handlers.ts index bf05da68..b3f7672c 100644 --- a/packages/runtime-playground/src/phpunit-command-handlers.ts +++ b/packages/runtime-playground/src/phpunit-command-handlers.ts @@ -1236,7 +1236,7 @@ try { pg_log('NO_TEST_FILES'); pg_log('NOTICE:tests directory not found at ' . $test_dir); pg_stage_ok('discover_tests'); - exit(1); + exit(0); } list($directories, $suffixes, $prefixes, $excludes, $configured_files) = wp_codebox_phpunit_parse_config(${JSON.stringify(options.phpunitXml)}, $test_dir); $test_files = wp_codebox_phpunit_discover($directories, $suffixes, $prefixes, $excludes, $configured_files); @@ -1261,7 +1261,7 @@ try { if (empty($test_files)) { pg_log('NO_TEST_FILES'); pg_stage_ok('discover_tests'); - exit(1); + exit(0); } pg_stage_ok('discover_tests'); } catch (Throwable $e) { @@ -1587,7 +1587,7 @@ try { if (empty($test_files)) { core_pg_log('NO_TEST_FILES'); core_pg_stage_ok('discover_tests'); - exit(1); + exit(0); } core_pg_stage_ok('discover_tests'); } catch (Throwable $e) { diff --git a/tests/phpunit-project-autoload.test.ts b/tests/phpunit-project-autoload.test.ts index f141bcbb..8c34005b 100644 --- a/tests/phpunit-project-autoload.test.ts +++ b/tests/phpunit-project-autoload.test.ts @@ -358,6 +358,7 @@ assert.ok(projectModeCode.includes("$test_files = wp_codebox_phpunit_discover($d assert.ok(projectModeCode.includes("' files=' . count($configured_files)")) assert.equal(projectModeCode.match(/return array\(\$directories, \$suffixes, \$prefixes, \$excludes\);/g)?.length ?? 0, 0) assert.equal(projectModeCode.match(/return \$return_values\(\);/g)?.length, 3) +assert.match(projectModeCode, /if \(empty\(\$test_files\)\) \{\s+pg_log\('NO_TEST_FILES'\);\s+pg_stage_ok\('discover_tests'\);\s+exit\(0\);/) assertPhpunitParseConfigFallbacksReturnFiveTuple(projectModeCode, "wp_codebox_phpunit_parse_config", "pg_log") assertSelectedTestFileResolution(projectModeCode) @@ -466,6 +467,7 @@ assert.ok(coreModeCode.includes("list($directories, $suffixes, $prefixes, $exclu assert.ok(coreModeCode.includes("$test_files = core_pg_discover_tests($directories, $suffixes, $prefixes, $excludes, $configured_files);")) assert.equal(coreModeCode.match(/return array\(\$directories, \$suffixes, \$prefixes, \$excludes\);/g)?.length ?? 0, 0) assert.equal(coreModeCode.match(/return \$return_values\(\);/g)?.length, 3) +assert.match(coreModeCode, /if \(empty\(\$test_files\)\) \{\s+core_pg_log\('NO_TEST_FILES'\);\s+core_pg_stage_ok\('discover_tests'\);\s+exit\(0\);/) assertPhpunitParseConfigFallbacksReturnFiveTuple(coreModeCode, "core_pg_parse_phpunit_config", "core_pg_log") for (const privateConstructor of [true, false]) { assertDiscoveredTestExecutes(projectModeCode, "pg", privateConstructor)