Skip to content

Commit f3963f7

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix/1838-cloudflare-bounded-browser
2 parents 16e324f + a9edf31 commit f3963f7

8 files changed

Lines changed: 325 additions & 79 deletions

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
"test:runtime-sources-playground-integration": "tsx tests/runtime-sources-playground-integration.test.ts",
113113
"test:playground-readonly-mounts": "tsx tests/playground-readonly-mounts.test.ts",
114114
"test:playground-readonly-mounts-integration": "tsx tests/playground-readonly-mounts-integration.test.ts",
115+
"test:playground-staged-upload-preview": "tsx tests/playground-staged-upload-preview.integration.test.ts",
115116
"test:playground-phpunit-readonly-cache-integration": "tsx tests/playground-phpunit-readonly-cache.integration.test.ts",
116117
"test:playground-phpunit-bootstrap-failure-integration": "tsx tests/playground-phpunit-bootstrap-failure.integration.test.ts",
117118
"test:playground-custom-archive-cache": "tsx tests/playground-custom-archive-cache.test.ts && tsx tests/playground-custom-archive-cache-process.test.ts && tsx tests/playground-custom-archive-cache.integration.test.ts",

packages/runtime-core/src/command-registry.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -944,8 +944,9 @@ export const commandRegistry = [
944944
{ name: "project-autoload-file", description: "Project/plugin autoload path loaded after the project bootstrap has provided WordPress functions.", format: "sandbox path" },
945945
{ name: "tests-dir", description: "WP PHPUnit tests directory inside the sandbox.", format: "sandbox path" },
946946
{ name: "phpunit-xml", description: "phpunit.xml path inside the plugin.", format: "path" },
947+
{ name: "phpunit-xml-default", description: "Marks phpunit-xml as the standard default path, allowing an adjacent phpunit.xml fallback only when phpunit.xml.dist is absent.", format: "boolean" },
947948
{ name: "test-file", description: "Single test file to run.", format: "path" },
948-
{ name: "changed-tests-json", description: "Changed test files for diagnostics.", format: "JSON array" },
949+
{ name: "changed-tests-json", description: "Optional changed-scope selection. A non-empty JSON array of test file paths runs only matching files and may legitimately select zero tests.", format: "JSON array of non-empty paths" },
949950
{ name: "env-json", description: "PHPUnit environment values.", format: "JSON object" },
950951
{ name: "wp-config-defines-json", description: "wp-config.php constants for the run.", format: "JSON object" },
951952
{ name: "dependency-mounts", description: "Comma-separated mounted dependency paths loaded and activated after managed PHPUnit installation, before tests execute.", format: "comma-separated sandbox paths" },
@@ -1062,8 +1063,9 @@ export const commandRegistry = [
10621063
{ name: "core-root", description: "WordPress develop checkout root inside the sandbox. Must contain vendor/ with Composer dev dependencies (PHPUnit + yoast/phpunit-polyfills) installed before mounting.", format: "sandbox path" },
10631064
{ name: "tests-dir", description: "Core tests directory inside the sandbox (expects includes/bootstrap.php under it).", format: "sandbox path" },
10641065
{ name: "phpunit-xml", description: "phpunit.xml path.", format: "path" },
1066+
{ name: "phpunit-xml-default", description: "Marks phpunit-xml as the standard default path, allowing an adjacent phpunit.xml fallback only when phpunit.xml.dist is absent.", format: "boolean" },
10651067
{ name: "test-file", description: "Single test file to run.", format: "path" },
1066-
{ name: "changed-tests-json", description: "Changed test files for diagnostics.", format: "JSON array" },
1068+
{ name: "changed-tests-json", description: "Optional changed-scope selection. A non-empty JSON array of test file paths runs only matching files and may legitimately select zero tests.", format: "JSON array of non-empty paths" },
10671069
{ name: "autoload-file", description: "Autoload path inside the sandbox (typically <core-root>/vendor/autoload.php from a completed composer install).", format: "sandbox path" },
10681070
{ name: "wp-config-defines-json", description: "wp-config.php constants for the run.", format: "JSON object" },
10691071
{ name: "multisite", description: "Run as multisite.", format: "boolean" },

packages/runtime-core/src/recipe-builders.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ export function buildWordPressPhpunitRecipe(options: WordPressPhpunitRecipeOptio
110110
commandArg("tests-dir", options.testsDir ?? "/wp-codebox-vendor/wp-phpunit/wp-phpunit"),
111111
commandArg("test-root", options.testRoot ?? `${pluginTarget}/tests`),
112112
commandArg("phpunit-xml", options.phpunitXml ?? `${pluginTarget}/phpunit.xml.dist`),
113+
commandArg("phpunit-xml-default", options.phpunitXml === undefined ? "1" : ""),
113114
commandStringListArg("dependency-mounts", options.dependencyMounts ?? []),
114115
commandJsonArg("bootstrap-files-json", options.bootstrapFiles ?? []),
115116
commandJsonArg("preload-files-json", options.preloadFiles ?? []),

packages/runtime-playground/src/phpunit-command-handlers.ts

Lines changed: 26 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ export interface PhpunitRunCodeOptions {
99
testsDir: string
1010
testRoot?: string
1111
phpunitXml: string
12+
phpunitXmlIsDefault: boolean
1213
selectedTestFile: string
13-
changedTestFiles: unknown[]
14+
changedTestFiles: string[]
1415
phpunitArgs: string[]
1516
env: Record<string, unknown>
1617
wpConfigDefines: Record<string, unknown>
@@ -32,8 +33,9 @@ export interface CorePhpunitRunCodeOptions {
3233
coreRoot: string
3334
testsDir: string
3435
phpunitXml: string
36+
phpunitXmlIsDefault: boolean
3537
selectedTestFile: string
36-
changedTestFiles: unknown[]
38+
changedTestFiles: string[]
3739
autoloadFile: string
3840
wpConfigDefines: Record<string, unknown>
3941
multisite: boolean
@@ -51,9 +53,6 @@ export const PLUGIN_PHPUNIT_RESULT_FILE = "/tmp/wp-codebox-phpunit-result.txt"
5153
interface PhpunitConfigDiscoveryPhpOptions {
5254
functionName: string
5355
logFunction: string
54-
missingConfigMessage: string
55-
parseFailureMessage: string
56-
includeParseFailureDetail: boolean
5756
loadedConfigMessage: string
5857
fallbackXmlDist: boolean
5958
restrictDirectoriesToTests: boolean
@@ -68,7 +67,6 @@ interface PhpunitChangedTestFilterPhpOptions {
6867
logFunction: string
6968
rootParameterName: string
7069
testsPathFallback: boolean
71-
emptyWantedNotice: boolean
7270
}
7371

7472
function phpunitConfigDiscoveryPhp(options: PhpunitConfigDiscoveryPhpOptions): string {
@@ -90,9 +88,6 @@ function phpunitConfigDiscoveryPhp(options: PhpunitConfigDiscoveryPhpOptions): s
9088
const returnValues = options.uniqueReturnValues
9189
? "array(array_values(array_unique($directories)), array_values(array_unique($suffixes)), array_values(array_unique($prefixes)), $excludes, array_values(array_unique($files)))"
9290
: "array($directories, $suffixes, $prefixes, $excludes, $files)"
93-
const parseFailureLog = options.includeParseFailureDetail
94-
? `${options.logFunction}('${options.parseFailureMessage}' . $first . '); using defaults');`
95-
: `${options.logFunction}('${options.parseFailureMessage}');`
9691
const suffixAssignment = options.replaceDefaultMatchers ? "$suffixes = $config_suffixes;" : "$suffixes = array_merge($suffixes, $config_suffixes);"
9792
const prefixAssignment = options.replaceDefaultMatchers ? "$prefixes = $config_prefixes;" : "$prefixes = array_merge($prefixes, $config_prefixes);"
9893

@@ -106,8 +101,7 @@ function phpunitConfigDiscoveryPhp(options: PhpunitConfigDiscoveryPhpOptions): s
106101
return ${returnValues};
107102
};${fallbackXmlDist}
108103
if (!is_readable($xml_path)) {
109-
${options.logFunction}('${options.missingConfigMessage}' . $xml_path . '; using defaults');
110-
return $return_values();
104+
throw new RuntimeException('PHPUnit config is not readable: ' . $xml_path);
111105
}
112106
$prev = libxml_use_internal_errors(true);
113107
$xml = @simplexml_load_file($xml_path);
@@ -116,8 +110,7 @@ function phpunitConfigDiscoveryPhp(options: PhpunitConfigDiscoveryPhpOptions): s
116110
libxml_use_internal_errors($prev);
117111
if ($xml === false) {
118112
$first = $errors ? trim($errors[0]->message) : 'unknown';
119-
${parseFailureLog}
120-
return $return_values();
113+
throw new RuntimeException('PHPUnit config could not be parsed at ' . $xml_path . ': ' . $first);
121114
}
122115
$base = ${options.basePathExpression};
123116
$config_dirs = array();
@@ -170,11 +163,10 @@ function phpunitDiscoveryPhp(functionName: string, logFunction: string): string
170163
$found = array();
171164
foreach ($files as $file) {
172165
if (!is_string($file) || $file === '') {
173-
continue;
166+
throw new RuntimeException('configured PHPUnit test file is invalid');
174167
}
175-
if (!is_file($file) || pathinfo($file, PATHINFO_EXTENSION) !== 'php') {
176-
${logFunction}('NOTICE:test file does not exist: ' . $file);
177-
continue;
168+
if (!is_file($file) || !is_readable($file) || pathinfo($file, PATHINFO_EXTENSION) !== 'php') {
169+
throw new RuntimeException('configured PHPUnit test file is not a readable PHP file: ' . $file);
178170
}
179171
foreach ($excludes as $exclude) {
180172
if (strpos($file, $exclude) === 0) {
@@ -184,9 +176,8 @@ function phpunitDiscoveryPhp(functionName: string, logFunction: string): string
184176
$found[] = $file;
185177
}
186178
foreach ($directories as $dir) {
187-
if (!is_dir($dir)) {
188-
${logFunction}('NOTICE:test directory does not exist: ' . $dir);
189-
continue;
179+
if (!is_dir($dir) || !is_readable($dir)) {
180+
throw new RuntimeException('configured PHPUnit test directory is not a readable directory: ' . $dir);
190181
}
191182
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::LEAVES_ONLY);
192183
foreach ($iterator as $file) {
@@ -229,12 +220,6 @@ function phpunitChangedTestFilterPhp(options: PhpunitChangedTestFilterPhpOptions
229220
const testsPathFallback = options.testsPathFallback ? ` elseif (strpos($path, '/tests/') !== false) {
230221
$path = substr($path, strpos($path, '/tests/') + 1);
231222
}` : ""
232-
const emptyWantedNotice = options.emptyWantedNotice ? `
233-
if (empty($wanted)) {
234-
${options.logFunction}('NOTICE:changed tests did not contain usable test paths');
235-
return array();
236-
}` : ""
237-
238223
return `function ${options.filterFunctionName}(array $test_files, string $changed_files_json, string $${options.rootParameterName}): array {
239224
$decoded = json_decode($changed_files_json, true);
240225
if (!is_array($decoded) || empty($decoded)) {
@@ -249,7 +234,7 @@ function phpunitChangedTestFilterPhp(options: PhpunitChangedTestFilterPhpOptions
249234
if ($normalized !== '') {
250235
$wanted[$normalized] = true;
251236
}
252-
}${emptyWantedNotice}
237+
}
253238
$filtered = array();
254239
foreach ($test_files as $file) {
255240
if (isset($wanted[${options.relativeFunctionName}((string) $file, $${options.rootParameterName})])) {
@@ -360,6 +345,7 @@ $tests_dir = ${JSON.stringify(options.testsDir)};
360345
$test_root = ${JSON.stringify(options.testRoot || `/wordpress/wp-content/plugins/${options.pluginSlug}/tests`)};
361346
$selected_test_file = ${JSON.stringify(options.selectedTestFile)};
362347
$changed_test_files_raw = ${JSON.stringify(JSON.stringify(options.changedTestFiles))};
348+
$changed_test_scope = ${JSON.stringify(options.changedTestFiles.length > 0)};
363349
$phpunit_args_raw = json_decode(${JSON.stringify(JSON.stringify(options.phpunitArgs))}, true);
364350
$bench_env = json_decode(${JSON.stringify(JSON.stringify(options.env))}, true);
365351
$wp_config_defines = json_decode(${JSON.stringify(JSON.stringify(options.wpConfigDefines))}, true);
@@ -763,8 +749,8 @@ function pg_plugin_real_path(string $relative_path, string $kind): ?string {
763749
return $real;
764750
}
765751
766-
function pg_project_bootstrap_from_config(string $xml_path): string {
767-
if (!is_readable($xml_path) && basename($xml_path) === 'phpunit.xml.dist') {
752+
function pg_project_bootstrap_from_config(string &$xml_path, bool $xml_is_default): string {
753+
if ($xml_is_default && !is_readable($xml_path) && basename($xml_path) === 'phpunit.xml.dist') {
768754
$alternate = dirname($xml_path) . '/phpunit.xml';
769755
if (is_readable($alternate)) {
770756
$xml_path = $alternate;
@@ -836,9 +822,10 @@ function pg_run_project_bootstrap_stage(array $cfg): void {
836822
try {
837823
$bootstrap = trim((string) ($cfg['project_bootstrap'] ?? ''));
838824
$phpunit_xml = (string) ($cfg['phpunit_xml'] ?? '');
825+
$phpunit_xml_is_default = !empty($cfg['phpunit_xml_is_default']);
839826
$from_config = false;
840827
if ($bootstrap === '') {
841-
$bootstrap = pg_project_bootstrap_from_config($phpunit_xml);
828+
$bootstrap = pg_project_bootstrap_from_config($phpunit_xml, $phpunit_xml_is_default);
842829
$from_config = $bootstrap !== '';
843830
}
844831
$bootstrap_real = pg_project_bootstrap_real_path($bootstrap, $phpunit_xml, $from_config);
@@ -1036,7 +1023,6 @@ ${phpunitChangedTestFilterPhp({
10361023
logFunction: "pg_log",
10371024
rootParameterName: "plugin_path",
10381025
testsPathFallback: true,
1039-
emptyWantedNotice: true,
10401026
})}
10411027
10421028
pg_install_diagnostics_handlers();
@@ -1087,7 +1073,7 @@ $config_path = pg_run_boot_stage(array('extra_defines' => $wp_config_defines, 't
10871073
if ($bootstrap_mode === 'project') {
10881074
pg_prepare_project_bootstrap_environment($config_path);
10891075
pg_skip_project_bootstrap_shell_install();
1090-
pg_run_project_bootstrap_stage(array('project_bootstrap' => $project_bootstrap, 'phpunit_xml' => ${JSON.stringify(options.phpunitXml)}));
1076+
pg_run_project_bootstrap_stage(array('project_bootstrap' => $project_bootstrap, 'phpunit_xml' => ${JSON.stringify(options.phpunitXml)}, 'phpunit_xml_is_default' => ${JSON.stringify(options.phpunitXmlIsDefault)}));
10911077
pg_run_project_autoload_stage($project_autoload_file !== '' ? $project_autoload_file : $legacy_project_autoload_file);
10921078
} else {
10931079
if ($bootstrap_mode !== 'managed') {
@@ -1214,11 +1200,8 @@ try {
12141200
${phpunitConfigDiscoveryPhp({
12151201
functionName: "wp_codebox_phpunit_parse_config",
12161202
logFunction: "pg_log",
1217-
missingConfigMessage: "NOTICE:phpunit.xml.dist not readable at ",
1218-
parseFailureMessage: "NOTICE:phpunit.xml.dist parse failed (",
1219-
includeParseFailureDetail: true,
12201203
loadedConfigMessage: "NOTICE:phpunit.xml.dist loaded from ",
1221-
fallbackXmlDist: true,
1204+
fallbackXmlDist: options.phpunitXmlIsDefault,
12221205
restrictDirectoriesToTests: !options.testRoot,
12231206
basePathExpression: "dirname($xml_path)",
12241207
uniqueReturnValues: false,
@@ -1231,10 +1214,7 @@ pg_stage_begin('discover_tests');
12311214
try {
12321215
$test_dir = $test_root;
12331216
if (!is_dir($test_dir)) {
1234-
pg_log('NO_TEST_FILES');
1235-
pg_log('NOTICE:tests directory not found at ' . $test_dir);
1236-
pg_stage_ok('discover_tests');
1237-
exit(0);
1217+
throw new RuntimeException('configured PHPUnit test root is not a readable directory: ' . $test_dir);
12381218
}
12391219
list($directories, $suffixes, $prefixes, $excludes, $configured_files) = wp_codebox_phpunit_parse_config(${JSON.stringify(options.phpunitXml)}, $test_dir);
12401220
$test_files = wp_codebox_phpunit_discover($directories, $suffixes, $prefixes, $excludes, $configured_files);
@@ -1256,10 +1236,9 @@ try {
12561236
$test_files = array($selected_abs);
12571237
}
12581238
pg_log('DISCOVERY: dirs=' . implode(',', $directories) . ' files=' . count($configured_files) . ' suffixes=' . implode(',', $suffixes) . ' prefixes=' . implode(',', $prefixes) . ' excludes=' . count($excludes) . ' found=' . count($test_files));
1259-
if (empty($test_files)) {
1239+
if (empty($test_files) && !$changed_test_scope) {
12601240
pg_log('NO_TEST_FILES');
1261-
pg_stage_ok('discover_tests');
1262-
exit(0);
1241+
throw new RuntimeException('PHPUnit discovery found no test files');
12631242
}
12641243
pg_stage_ok('discover_tests');
12651244
} catch (Throwable $e) {
@@ -1345,6 +1324,7 @@ $tests_dir = rtrim(${JSON.stringify(options.testsDir)}, '/');
13451324
$phpunit_xml = ${JSON.stringify(options.phpunitXml)};
13461325
$selected_test_file = ${JSON.stringify(options.selectedTestFile)};
13471326
$changed_test_files_raw = ${JSON.stringify(JSON.stringify(options.changedTestFiles))};
1327+
$changed_test_scope = ${JSON.stringify(options.changedTestFiles.length > 0)};
13481328
$autoload_file = ${JSON.stringify(options.autoloadFile)};
13491329
$wp_config_defines = json_decode(${JSON.stringify(JSON.stringify(options.wpConfigDefines))}, true);
13501330
$multisite = ${JSON.stringify(options.multisite)};
@@ -1480,11 +1460,8 @@ function core_pg_write_tests_config(string $core_root, array $extra_defines): st
14801460
${phpunitConfigDiscoveryPhp({
14811461
functionName: "core_pg_parse_phpunit_config",
14821462
logFunction: "core_pg_log",
1483-
missingConfigMessage: "NOTICE:phpunit config not readable at ",
1484-
parseFailureMessage: "NOTICE:phpunit config parse failed; using defaults",
1485-
includeParseFailureDetail: false,
14861463
loadedConfigMessage: "NOTICE:phpunit config loaded from ",
1487-
fallbackXmlDist: false,
1464+
fallbackXmlDist: options.phpunitXmlIsDefault,
14881465
restrictDirectoriesToTests: false,
14891466
basePathExpression: "dirname($xml_path)",
14901467
uniqueReturnValues: true,
@@ -1499,7 +1476,6 @@ ${phpunitChangedTestFilterPhp({
14991476
logFunction: "core_pg_log",
15001477
rootParameterName: "core_root",
15011478
testsPathFallback: false,
1502-
emptyWantedNotice: false,
15031479
})}
15041480
15051481
${phpunitArgsPhp("core_pg_phpunit_args", "core_pg_log")}
@@ -1582,10 +1558,9 @@ try {
15821558
$test_files = array($selected_abs);
15831559
}
15841560
core_pg_log('DISCOVERY: dirs=' . implode(',', $directories) . ' files=' . count($configured_files) . ' suffixes=' . implode(',', $suffixes) . ' prefixes=' . implode(',', $prefixes) . ' excludes=' . count($excludes) . ' found=' . count($test_files));
1585-
if (empty($test_files)) {
1561+
if (empty($test_files) && !$changed_test_scope) {
15861562
core_pg_log('NO_TEST_FILES');
1587-
core_pg_stage_ok('discover_tests');
1588-
exit(0);
1563+
throw new RuntimeException('PHPUnit discovery found no test files');
15891564
}
15901565
core_pg_stage_ok('discover_tests');
15911566
} catch (Throwable $e) {

packages/runtime-playground/src/programmatic-playground-runner.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export async function startProgrammaticPlaygroundServer(spec: RuntimeCreateSpec,
5050
}
5151
const requestHandler = await bootWordPressAndRequestHandler({
5252
createPhpRuntime: () => loadNodeRuntime(phpVersion, programmaticNodeRuntimeOptions(spec, nextProcessId++)),
53+
maxPhpInstances: 1,
5354
phpVersion,
5455
siteUrl: spec.preview?.siteUrl ?? "http://127.0.0.1",
5556
documentRoot: "/wordpress",

0 commit comments

Comments
 (0)