Skip to content

Commit be85eec

Browse files
committed
Load PHPUnit dependencies after install
1 parent 3d9cf9f commit be85eec

5 files changed

Lines changed: 45 additions & 4 deletions

File tree

docs/recipe-contract.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,13 @@ The runtime provides:
716716
- Structured diagnostics in the recipe artifact bundle, including the raw test
717717
result log collected from `/tmp/wp-codebox-phpunit-result.txt`.
718718

719+
`dependencyMounts` is the canonical PHPUnit builder input for plugins that need
720+
the PHPUnit database during activation. Mount each dependency through
721+
`inputs.extra_plugins` with `activate: false`, then provide its sandbox plugin
722+
directory in `dependencyMounts`. In managed bootstrap mode, `wordpress.phpunit`
723+
loads and activates those dependencies after the PHPUnit install stage has
724+
created test tables and before test discovery and execution.
725+
719726
Use `recipe build phpunit` when generating recipes for plugin CI or offloaded lab
720727
runners:
721728

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@ export const commandRegistry = [
948948
{ name: "changed-tests-json", description: "Changed test files for diagnostics.", format: "JSON array" },
949949
{ name: "env-json", description: "PHPUnit environment values.", format: "JSON object" },
950950
{ name: "wp-config-defines-json", description: "wp-config.php constants for the run.", format: "JSON object" },
951-
{ name: "dependency-mounts", description: "Comma-separated mounted dependency paths.", format: "comma-separated sandbox paths" },
951+
{ name: "dependency-mounts", description: "Comma-separated mounted dependency paths loaded and activated after managed PHPUnit installation, before tests execute.", format: "comma-separated sandbox paths" },
952952
{ name: "bootstrap-files-json", description: "Plugin-relative bootstrap file fallbacks loaded in managed mode after wp-phpunit fixtures.", format: "JSON array" },
953953
{ name: "phpunit-args-json", description: "Structured PHPUnit CLI arguments such as [\"--filter\", \"MyTest::test_case\"].", format: "JSON array" },
954954
{ name: "bootstrap-mode", description: "Bootstrap strategy: managed keeps WP Codebox-owned setup; project requires the plugin's native PHPUnit bootstrap.", format: "managed|project" },

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,15 +1104,15 @@ $loaded_dep_files = array();
11041104
$loaded_component_file = null;
11051105
11061106
require_once $tests_dir . '/includes/functions.php';
1107-
tests_add_filter('muplugins_loaded', function () use ($plugin_slug, $plugin_path, $dep_mounts, $pre_component_plugins_loaded_callbacks, $pre_component_init_callbacks, &$deferred_install_plugins_loaded_callbacks, &$deferred_install_init_callbacks, &$loaded_dep_files, &$loaded_component_file) {
1108-
$loaded_dep_files = pg_run_load_deps_stage(array('dep_mounts' => $dep_mounts));
1107+
tests_add_filter('muplugins_loaded', function () use ($plugin_slug, $plugin_path, $pre_component_plugins_loaded_callbacks, $pre_component_init_callbacks, &$deferred_install_plugins_loaded_callbacks, &$deferred_install_init_callbacks, &$loaded_component_file) {
11091108
$loaded_component_file = pg_run_load_component_stage(array('plugin_slug' => $plugin_slug, 'plugin_path' => $plugin_path, 'activate' => false));
11101109
$deferred_install_plugins_loaded_callbacks = pg_defer_new_wordpress_hook_callbacks('plugins_loaded', $pre_component_plugins_loaded_callbacks);
11111110
$deferred_install_init_callbacks = pg_defer_new_wordpress_hook_callbacks('init', $pre_component_init_callbacks);
11121111
});
11131112
11141113
pg_run_install_stage(array('config_path' => $config_path, 'tests_dir' => $tests_dir, 'multisite' => $multisite));
11151114
pg_remove_new_wordpress_hook_callbacks('shutdown', $pre_component_shutdown_callbacks);
1115+
$loaded_dep_files = pg_run_load_deps_stage(array('dep_mounts' => $dep_mounts));
11161116
$activation_files = $loaded_dep_files;
11171117
if ($loaded_component_file !== null) {
11181118
$activation_files[] = $loaded_component_file;

tests/phpunit-project-autoload.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,30 @@ const managedModeCode = phpunitRunCode({
410410

411411
assert.ok(managedModeCode.includes("configured PHPUnit harness autoload file is not readable"))
412412
assert.ok(managedModeCode.includes("'cacheResult' => false"))
413+
const installStageIndex = managedModeCode.indexOf("pg_run_install_stage(array(")
414+
const dependencyLoadStageIndex = managedModeCode.indexOf("$loaded_dep_files = pg_run_load_deps_stage", installStageIndex)
415+
const activationStageIndex = managedModeCode.indexOf("pg_run_activation_stage", dependencyLoadStageIndex)
416+
assert.ok(installStageIndex > 0)
417+
assert.ok(dependencyLoadStageIndex > installStageIndex, "dependency plugins must load after managed PHPUnit installation")
418+
assert.ok(activationStageIndex > dependencyLoadStageIndex, "dependency plugins must activate after loading and before tests execute")
419+
420+
const dependencyRecipe = buildWordPressPhpunitRecipe({
421+
pluginSlug: "demo-plugin",
422+
extra_plugins: [{
423+
source: "/workspace/dependency",
424+
slug: "dependency",
425+
pluginFile: "dependency/dependency.php",
426+
activate: false,
427+
}],
428+
dependencyMounts: ["/wordpress/wp-content/plugins/dependency"],
429+
})
430+
assert.deepEqual(dependencyRecipe.inputs.extra_plugins, [{
431+
source: "/workspace/dependency",
432+
slug: "dependency",
433+
pluginFile: "dependency/dependency.php",
434+
activate: false,
435+
}])
436+
assert.ok(dependencyRecipe.workflow.steps[0].args.includes("dependency-mounts=/wordpress/wp-content/plugins/dependency"))
413437

414438
const phpunitCacheAllocator = extractPhpFunction(managedModeCode, "wp_codebox_phpunit_args_private_cache_result_file")
415439
const phpunitArgsFunction = extractPhpFunction(managedModeCode, "wp_codebox_phpunit_args")

tests/playground-phpunit-readonly-cache.integration.test.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { buildWordPressPhpunitRecipe } from "../packages/runtime-core/src/recipe
1111
const execFileAsync = promisify(execFile)
1212
const root = await mkdtemp(join(tmpdir(), "wp-codebox-phpunit-readonly-"))
1313
const plugin = join(root, "plugin")
14+
const dependency = join(root, "dependency")
1415
const harness = join(root, "harness")
1516
const recipePath = join(root, "recipe.json")
1617
const artifactsPath = join(root, "artifacts")
@@ -24,6 +25,13 @@ try {
2425

2526
const recipe = buildWordPressPhpunitRecipe({
2627
pluginSlug: "readonly-phpunit-fixture",
28+
extra_plugins: [{
29+
source: dependency,
30+
slug: "activation-dependency",
31+
pluginFile: "activation-dependency/activation-dependency.php",
32+
activate: false,
33+
}],
34+
dependencyMounts: ["/wordpress/wp-content/plugins/activation-dependency"],
2735
mounts: [
2836
{ source: plugin, target: "/wordpress/wp-content/plugins/readonly-phpunit-fixture", mode: "readonly" },
2937
{ source: join(harness, "vendor"), target: "/wp-codebox-vendor", mode: "readonly" },
@@ -49,9 +57,11 @@ try {
4957

5058
async function writeFixture(): Promise<void> {
5159
await mkdir(join(plugin, "tests"), { recursive: true })
60+
await mkdir(dependency, { recursive: true })
5261
await writeFile(join(plugin, "readonly-phpunit-fixture.php"), "<?php\n/**\n * Plugin Name: Readonly PHPUnit Fixture\n */\n")
5362
await writeFile(join(plugin, "source-sentinel.bin"), sentinel)
54-
await writeFile(join(plugin, "tests", "ReadonlyCacheTest.php"), "<?php\nclass ReadonlyCacheTest extends WP_UnitTestCase { public function test_sentinel_is_available(): void { $this->assertGreaterThan(0, filesize(dirname(__DIR__) . \'/source-sentinel.bin\')); } }\n")
63+
await writeFile(join(plugin, "tests", "ReadonlyCacheTest.php"), "<?php\nclass ReadonlyCacheTest extends WP_UnitTestCase { public function test_sentinel_is_available(): void { $this->assertGreaterThan(0, filesize(dirname(__DIR__) . \'/source-sentinel.bin\')); } public function test_dependency_activation_runs_after_install(): void { $this->assertGreaterThanOrEqual(1, get_option(\'wp_codebox_dependency_activation_users\')); } }\n")
64+
await writeFile(join(dependency, "activation-dependency.php"), "<?php\n/**\n * Plugin Name: Activation Dependency\n */\nregister_activation_hook(__FILE__, static function (): void { update_option('wp_codebox_dependency_activation_users', count(get_users(array('number' => 1)))); });\n")
5565
}
5666

5767
async function digestTree(directory: string): Promise<string> {

0 commit comments

Comments
 (0)