Skip to content

Commit 2fb3d1e

Browse files
committed
transpile #758
1 parent 9f78e69 commit 2fb3d1e

File tree

4 files changed

+89
-0
lines changed

4 files changed

+89
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
44

55
## [unreleased] Unreleased
66

7+
### Changed
8+
9+
- Improve `LoadSandbox` error and messaging (used by the `WPLoader` module when `loadOnly: true`) around Codeception early exits. (thanks @andronocean)
10+
711
## [3.7.5] 2024-09-13;
812

913
## Fixed

src/WordPress/InstallationException.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class InstallationException extends Exception
4343
public const SQLITE_PLUGIN_NOT_FOUND = 37;
4444
public const DB_DROPIN_ALREADY_EXISTS = 38;
4545
public const WORDPRESS_NOT_FOUND = 39;
46+
public const COMMAND_DID_NOT_FINISH_PROPERLY = 40;
4647

4748
public static function becauseWordPressFailedToLoad(string $bodyContent): self
4849
{
@@ -71,4 +72,14 @@ public static function becauseWordPressMultsiteIsNotInstalled(bool $isSubdomainI
7172

7273
return new self('WordPress multisite (sub-folder) is not installed.', self::MULTISITE_SUBFOLDER_NOT_INSTALLED);
7374
}
75+
76+
public static function becauseCodeceptionCommandDidNotFinish(): self
77+
{
78+
return new self(
79+
"The current Codeception command did not finish properly. WordPress exited early while loading. "
80+
."A plugin, theme, or WP-CLI package may have exited before the wp_loaded action could be fired. " .
81+
"If there is error output above, it may provide clues.",
82+
self::COMMAND_DID_NOT_FINISH_PROPERLY
83+
);
84+
}
7485
}

src/WordPress/LoadSandbox.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ class_exists(InstallationException::class);
134134
}
135135
}
136136

137+
if ($bodyContent === 'COMMAND DID NOT FINISH PROPERLY.') {
138+
// We got here from \Codeception\Subscriber\ErrorHandler::shutdownHandler().
139+
// We'll try to provide some clues to the user in the exception message.
140+
codecept_debug('Codeception error: ' . $bodyContent . ' Check logs for details.');
141+
throw InstallationException::becauseCodeceptionCommandDidNotFinish();
142+
}
143+
137144
// We do not know what happened, throw and try to be helpful.
138145
throw InstallationException::becauseWordPressFailedToLoad($bodyContent ?: $reason);
139146
}

tests/unit/lucatume/WPBrowser/WordPress/LoadSandboxTest.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55

66
use Codeception\Test\Unit;
77
use Exception;
8+
use lucatume\WPBrowser\Tests\Traits\Fork;
89
use lucatume\WPBrowser\Tests\Traits\LoopIsolation;
910
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
11+
use lucatume\WPBrowser\Traits\UopzFunctions;
1012
use lucatume\WPBrowser\Utils\Env;
1113
use lucatume\WPBrowser\Utils\Filesystem as FS;
1214
use lucatume\WPBrowser\Utils\Random;
@@ -24,6 +26,7 @@ class LoadSandboxTest extends Unit
2426
{
2527
use LoopIsolation;
2628
use TmpFilesCleanup;
29+
use UopzFunctions;
2730

2831
/**
2932
* It should correctly load installed WordPress
@@ -322,4 +325,68 @@ public function should_handle_wp_die_called_during_loading(): void
322325
$loadSandbox->load();
323326
});
324327
}
328+
329+
/**
330+
* It should handle an unexpected early exit if something interferes with Codeception
331+
*
332+
* @test
333+
*/
334+
public function should_handle_codeception_command_not_finished_error(): void {
335+
$wpRootDir = FS::tmpDir('sandbox_');
336+
$dbName = Random::dbName();
337+
$dbHost = Env::get('WORDPRESS_DB_HOST');
338+
$dbUser = Env::get('WORDPRESS_DB_USER');
339+
$dbPassword = Env::get('WORDPRESS_DB_PASSWORD');
340+
$db = new MysqlDatabase($dbName, $dbUser, $dbPassword, $dbHost, 'wp_');
341+
$installation = Installation::scaffold($wpRootDir, '6.1.1')
342+
->configure($db)
343+
->install(
344+
'http://wordpress.test',
345+
'admin',
346+
'admin',
347+
'admin@wordpress.test',
348+
'Sandbox'
349+
);
350+
351+
$exitingPluginCode = <<<'PHP'
352+
<?php
353+
/**
354+
* Plugin Name: Codeception Early Shutdown Mock
355+
*
356+
* Suppose a plugin or CLI package messes up and exits early, e.g. `exit(1)`, prior to `wp_loaded`.
357+
* That will trigger Codeception's shutdown handler. If the suite is not finished running, and an error has not occurred,
358+
* Codeception echoes a message and exits. LoadSandbox's output buffer will catch this.
359+
* This plugin mocks the Codeception behavior.
360+
*
361+
* @see \Codeception\Subscriber\ErrorHandler::shutdownHandler()
362+
*/
363+
add_action('after_setup_theme', function () {
364+
// Output and exit from \Codeception\Subscriber\ErrorHandler::shutdownHandler.
365+
echo "\n\n\nCOMMAND DID NOT FINISH PROPERLY.\n";
366+
exit(125);
367+
});
368+
PHP;
369+
370+
$muPluginsDir = $installation->getMuPluginsDir();
371+
if (
372+
!is_dir($muPluginsDir)
373+
&& !(
374+
mkdir($muPluginsDir, 0755, true)
375+
&& is_dir($muPluginsDir)
376+
)
377+
) {
378+
throw new \RuntimeException('Could not create mu-plugins directory.');
379+
}
380+
if(!file_put_contents($muPluginsDir . '/exiting-mu-plugin.php', $exitingPluginCode)){
381+
throw new \RuntimeException('Could not write exiting-mu-plugin.php.');
382+
}
383+
384+
$this->expectException(InstallationException::class);
385+
$this->expectExceptionMessage(InstallationException::becauseCodeceptionCommandDidNotFinish()->getMessage());
386+
387+
$this->assertInIsolation(static function () use ($wpRootDir) {
388+
$loadSandbox = new LoadSandbox($wpRootDir, 'wordpress.test');
389+
$loadSandbox->load();
390+
});
391+
}
325392
}

0 commit comments

Comments
 (0)