Skip to content

Commit 4126aa3

Browse files
authored
Complete packaged Playground PHPUnit runtime support (#1850)
* Forward PHPUnit runtime package options * Forward extensions to packaged Playground CLI * Expose PHPUnit arguments during project bootstrap * Fail closed on PHPUnit bootstrap diagnostics
1 parent 2d5d000 commit 4126aa3

8 files changed

Lines changed: 46 additions & 10 deletions

File tree

packages/cli/src/commands/recipe-build.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { readFile, writeFile } from "node:fs/promises"
2-
import { buildGenericAbilityRuntimeRunRecipe, buildRuntimePackageRunRecipe, buildWordPressBenchRecipe, buildWordPressPhpunitRecipe, compileRecipeTemplate, type GenericAbilityRuntimeRunOptions, type RecipeTemplateInput, type RuntimePackageRunRecipeOptions, type WorkspaceRecipe, type WorkspaceRecipeExtraPlugin, type WorkspaceRecipeMount, type WorkspaceRecipeRuntimeService, type WorkspaceRecipeStep } from "@automattic/wp-codebox-core"
2+
import { buildGenericAbilityRuntimeRunRecipe, buildRuntimePackageRunRecipe, buildWordPressBenchRecipe, buildWordPressPhpunitRecipe, compileRecipeTemplate, type GenericAbilityRuntimeRunOptions, type RecipeTemplateInput, type RuntimePackageRunRecipeOptions, type WorkspaceRecipe, type WorkspaceRecipeExtraPlugin, type WorkspaceRecipeMount, type WorkspaceRecipePHPWasmExtensionManifest, type WorkspaceRecipeRuntimeBackendPackage, type WorkspaceRecipeRuntimeService, type WorkspaceRecipeStep } from "@automattic/wp-codebox-core"
33

44
interface RecipeBuildOptions {
55
recipeType: "phpunit" | "bench" | "template" | "generic-ability-runtime-run" | "runtime-package-run"
@@ -10,6 +10,9 @@ interface RecipeBuildOptions {
1010
interface WordPressPhpunitBuilderOptions {
1111
blueprint?: unknown
1212
wordpressVersion?: string
13+
phpVersion?: string
14+
extensions?: WorkspaceRecipePHPWasmExtensionManifest[]
15+
backendPackage?: WorkspaceRecipeRuntimeBackendPackage
1316
mounts?: WorkspaceRecipeMount[]
1417
services?: WorkspaceRecipeRuntimeService[]
1518
extra_plugins?: WorkspaceRecipeExtraPlugin[]
@@ -76,6 +79,9 @@ function buildRecipe(recipeType: RecipeBuildOptions["recipeType"], options: Word
7679
return buildWordPressPhpunitRecipe({
7780
blueprint: phpunitOptions.blueprint,
7881
wordpressVersion: stringOrUndefined(phpunitOptions.wordpressVersion),
82+
phpVersion: stringOrUndefined(phpunitOptions.phpVersion),
83+
extensions: Array.isArray(phpunitOptions.extensions) ? phpunitOptions.extensions : [],
84+
backendPackage: phpunitOptions.backendPackage,
7985
mounts: Array.isArray(phpunitOptions.mounts) ? phpunitOptions.mounts : [],
8086
services: Array.isArray(phpunitOptions.services) ? phpunitOptions.services : [],
8187
extra_plugins: Array.isArray(phpunitOptions.extra_plugins) ? phpunitOptions.extra_plugins : [],

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { WorkspaceRecipe, WorkspaceRecipeExtraPlugin, WorkspaceRecipeMount, WorkspaceRecipeRuntimeService, WorkspaceRecipeStep } from "./runtime-contracts.js"
1+
import type { WorkspaceRecipe, WorkspaceRecipeExtraPlugin, WorkspaceRecipeMount, WorkspaceRecipePHPWasmExtensionManifest, WorkspaceRecipeRuntimeBackendPackage, WorkspaceRecipeRuntimeService, WorkspaceRecipeStep } from "./runtime-contracts.js"
22
import { commandArg, commandJsonArg, commandStringListArg } from "./command-codecs.js"
33
export { buildRuntimePackageRunRecipe, CODEBOX_RUN_RUNTIME_PACKAGE_ABILITY, RUNTIME_PACKAGE_ARTIFACT_DECLARATION_SCHEMA, RUNTIME_PACKAGE_EXECUTION_INPUT_SCHEMA, RUNTIME_PACKAGE_EXECUTION_RESULT_SCHEMA, RUNTIME_PACKAGE_OUTPUT_PROJECTION_SCHEMA, runtimePackageExecutionInput, type RuntimePackageArtifactDeclaration, type RuntimePackageExecutionInput, type RuntimePackageOutputProjection, type RuntimePackageRunRecipeOptions } from "./runtime-package-execution.js"
44
export { RUNTIME_PACKAGE_DIAGNOSTIC_SCHEMA, RUNTIME_PACKAGE_RESULT_SCHEMA, RUNTIME_PACKAGE_TASK_SCHEMA, normalizeRuntimePackageResult, normalizeRuntimePackageTask, validateRuntimePackageTask, type RuntimePackageDiagnostic, type RuntimePackageResult, type RuntimePackageTask } from "./runtime-package-contracts.js"
@@ -13,7 +13,10 @@ export interface NormalizeRecipeMountsOptions {
1313

1414
export interface WordPressPhpunitRecipeOptions {
1515
wordpressVersion?: string
16+
phpVersion?: string
1617
blueprint?: unknown
18+
extensions?: WorkspaceRecipePHPWasmExtensionManifest[]
19+
backendPackage?: WorkspaceRecipeRuntimeBackendPackage
1720
mounts?: WorkspaceRecipeMount[]
1821
services?: WorkspaceRecipeRuntimeService[]
1922
extra_plugins?: WorkspaceRecipeExtraPlugin[]
@@ -73,7 +76,10 @@ export function buildWordPressPhpunitRecipe(options: WordPressPhpunitRecipeOptio
7376
schema: "wp-codebox/workspace-recipe/v1",
7477
runtime: {
7578
wp: options.wordpressVersion ?? DEFAULT_WORDPRESS_VERSION,
79+
...(options.phpVersion ? { phpVersion: options.phpVersion } : {}),
7680
blueprint: options.blueprint ?? { steps: [] },
81+
...(options.extensions?.length ? { extensions: options.extensions } : {}),
82+
...(options.backendPackage ? { backendPackage: options.backendPackage } : {}),
7783
},
7884
inputs: {
7985
extra_plugins: normalizeExtraPlugins(options.extra_plugins),

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,18 @@ $bootstrap_mode = ${JSON.stringify(options.bootstrapMode || "managed")};
370370
$project_bootstrap = ${JSON.stringify(options.projectBootstrap)};
371371
$multisite = ${JSON.stringify(options.multisite)};
372372
373+
function pg_build_phpunit_argv($raw): array {
374+
$phpunit_argv = array('phpunit');
375+
if (is_array($raw)) {
376+
foreach ($raw as $arg) {
377+
if (is_scalar($arg)) {
378+
$phpunit_argv[] = (string) $arg;
379+
}
380+
}
381+
}
382+
return $phpunit_argv;
383+
}
384+
373385
@file_put_contents($result_file, '');
374386
375387
function pg_log($msg) {
@@ -1045,6 +1057,10 @@ try {
10451057
}
10461058
10471059
pg_apply_env($bench_env);
1060+
$phpunit_argv = pg_build_phpunit_argv($phpunit_args_raw);
1061+
$argv = $phpunit_argv;
1062+
$_SERVER['argv'] = $phpunit_argv;
1063+
$_SERVER['argc'] = count($phpunit_argv);
10481064
10491065
if (!is_array($wp_config_defines)) {
10501066
$wp_config_defines = array();
@@ -1291,14 +1307,6 @@ function wp_codebox_phpunit_print_test_list($test) {
12911307
pg_stage_begin('run_tests');
12921308
pg_log('RUNNING ' . count($test_files) . ' TEST FILES');
12931309
try {
1294-
$phpunit_argv = array('phpunit');
1295-
if (is_array($phpunit_args_raw)) {
1296-
foreach ($phpunit_args_raw as $arg) {
1297-
if (is_scalar($arg)) {
1298-
$phpunit_argv[] = (string) $arg;
1299-
}
1300-
}
1301-
}
13021310
$phpunit_args = wp_codebox_phpunit_args($phpunit_argv);
13031311
if (!empty($phpunit_args['listTests'])) {
13041312
wp_codebox_phpunit_print_test_list($suite);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export interface PlaygroundCliModule {
3333
wordpressInstallMode?: "install-from-existing-files" | "install-from-existing-files-if-needed" | "do-not-attempt-installing"
3434
"site-url"?: string
3535
phpIniEntries?: Record<string, string>
36+
phpExtension?: string[]
3637
}): Promise<PlaygroundCliServer>
3738
}
3839

@@ -137,6 +138,7 @@ export async function startPlaygroundCliServer(spec: RuntimeCreateSpec, mounts:
137138
} : {}),
138139
wp: localAssetServer?.url ?? wordpressStartupAsset?.wp,
139140
php: spec.environment.phpVersion,
141+
...(spec.environment.extensions?.length ? { phpExtension: spec.environment.extensions.map((extension) => extension.manifest) } : {}),
140142
phpIniEntries: pluginRuntimePhpIniEntries(spec),
141143
"site-url": spec.preview?.siteUrl,
142144
blueprint: playgroundCliBlueprint(spec),

packages/runtime-playground/src/wordpress-command-runners.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,10 @@ export async function runPhpunitCommand({
952952
await persistPluginPhpunitResult(server, resultFile, artifactRoot)
953953
await persistVfsDiagnosticFileToHost(server, resultFile, `/wordpress/wp-content/plugins/${pluginSlug}/.pg-test-result.txt`, mounts)
954954
assertPlaygroundResponseOk("wordpress.phpunit", response)
955+
const structured = await readPluginPhpunitDiagnostic(server, resultFile)
956+
if (structured) {
957+
throw new Error(`wordpress.phpunit could not run: ${structured}`)
958+
}
955959

956960
return response.text
957961
}

tests/phpunit-project-autoload.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,9 @@ const projectModeCode = phpunitRunCode({
254254
const bootIndex = projectModeCode.indexOf("$config_path = pg_run_boot_stage")
255255
const projectBootstrapIndex = projectModeCode.indexOf("pg_run_project_bootstrap_stage", bootIndex)
256256
const projectAutoloadIndex = projectModeCode.indexOf("pg_run_project_autoload_stage", projectBootstrapIndex)
257+
const phpunitArgvIndex = projectModeCode.indexOf("$_SERVER['argv'] = $phpunit_argv;")
257258
assert.ok(bootIndex > 0)
259+
assert.ok(phpunitArgvIndex > 0 && phpunitArgvIndex < projectBootstrapIndex, "project bootstrap must receive forwarded PHPUnit arguments")
258260
assert.ok(projectBootstrapIndex > bootIndex)
259261
assert.ok(projectAutoloadIndex > projectBootstrapIndex)
260262
assert.ok(projectModeCode.includes("'autoload_required' => $bootstrap_mode !== 'project' || $harness_autoload_file !== ''"))

tests/playground-cli-runner-bootstrap-ini.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ try {
3333
phpVersion: "8.4",
3434
wordpressInstallMode: "do-not-attempt-installing",
3535
assets: { wordpressDirectory },
36+
extensions: [{ manifest: "/tmp/sodium/manifest.json" }],
3637
blueprint: {},
3738
},
3839
policy: {
@@ -68,6 +69,7 @@ try {
6869
assert.equal(calls[0].workers, 6)
6970
assert.equal(calls[0].wordpressInstallMode, "do-not-attempt-installing")
7071
assert.deepEqual(calls[0].phpIniEntries, { memory_limit: "512M" })
72+
assert.deepEqual(calls[0].phpExtension, ["/tmp/sodium/manifest.json"])
7173
const sharedMount = calls[0]["mount-before-install"]?.[0]?.hostPath
7274
assert.equal(typeof sharedMount, "string")
7375
const sharedPhpIni = await readFile(join(sharedMount as string, "php.ini"), "utf8")

tests/runtime-services.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,19 @@ try {
2929
const recipePath = join(builderDirectory, "recipe.json")
3030
await writeFile(optionsPath, JSON.stringify({
3131
pluginSlug: "example",
32+
phpVersion: "8.3",
33+
extensions: [{ manifest: "./sodium/manifest.json" }],
34+
backendPackage: { kind: "playground", source: "./playground-cli", package: "@wp-playground/cli" },
3235
testRoot: "/home/example/bin/tests/core",
3336
phpunitXml: "/home/example/bin/tests/core/phpunit.xml",
3437
}))
3538
assert.equal(await runRecipeBuildCommand(["phpunit", "--options", optionsPath, "--output", recipePath]), 0)
3639
const builtRecipe = JSON.parse(await readFile(recipePath, "utf8")) as WorkspaceRecipe
3740
assert.ok(builtRecipe.workflow.steps[0].args?.includes("test-root=/home/example/bin/tests/core"))
3841
assert.ok(builtRecipe.workflow.steps[0].args?.includes("phpunit-xml=/home/example/bin/tests/core/phpunit.xml"))
42+
assert.equal(builtRecipe.runtime?.phpVersion, "8.3")
43+
assert.deepEqual(builtRecipe.runtime?.extensions, [{ manifest: "./sodium/manifest.json" }])
44+
assert.deepEqual(builtRecipe.runtime?.backendPackage, { kind: "playground", source: "./playground-cli", package: "@wp-playground/cli" })
3945

4046
await writeFile(optionsPath, JSON.stringify({ pluginSlug: "example" }))
4147
assert.equal(await runRecipeBuildCommand(["phpunit", "--options", optionsPath, "--output", recipePath]), 0)

0 commit comments

Comments
 (0)