Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 81 additions & 11 deletions tests/_support/Traits/WithIncorrectUsage.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* @package Nexcess\PluginAbsorber
*/

declare( strict_types=1 );

namespace Nexcess\PluginAbsorber\Tests\Support\Traits;

/**
Expand All @@ -20,6 +22,12 @@
* The expectation is registered from the report itself, so an unexpected report still fails the test:
* anything the library reports is recorded here and asserted over.
*
* The *message* is recorded alongside the name, because "something was reported" is a weak thing to
* assert on its own: a failed registry read, a missing hook prefix and the gate a test is actually
* about all satisfy it equally. A test about one particular failure asserts through
* `assert_the_library_reported_incorrect_usage_saying()` instead, which is the only assertion here
* that can tell two report sites apart.
*
* Requires `Codeception\TestCase\WPTestCase`, for `setExpectedIncorrectUsage()`.
*
* @since 1.0.0
Expand All @@ -32,6 +40,13 @@ trait WithIncorrectUsage {
*/
private $incorrect_usage_reports = [];

/**
* Every `_doing_it_wrong()` message seen since the listener went on, in the same order.
*
* @var string[]
*/
private $incorrect_usage_messages = [];

/**
* @var callable|null
*/
Expand All @@ -40,26 +55,39 @@ trait WithIncorrectUsage {
/**
* Start accepting — and recording — incorrect-usage reports.
*
* Safe to call more than once: whatever this trait already put on the hook comes off first.
* `Scenario\Bootstrap_Test_Case::run_halted_request()` calls this once per halted request, so a
* scenario running two of them would otherwise strand the first listener on `doing_it_wrong_run`
* for the rest of the process, bound to a test object that has already finished and writing into
* properties nothing will ever read.
*
* What was already recorded stays. A caller that expected a report before the second call still
* has to be able to assert on it afterwards.
*
* @since 1.0.0
*
* @return void
*/
protected function expect_incorrect_usage(): void {
$reports = &$this->incorrect_usage_reports;
$this->remove_incorrect_usage_listener();

$listener = function ( $function_name ) use ( &$reports ): void {
$reports = &$this->incorrect_usage_reports;
$messages = &$this->incorrect_usage_messages;

$listener = function ( $function_name, $message = '' ) use ( &$reports, &$messages ): void {
if ( ! is_string( $function_name ) ) {
return;
}

$reports[] = $function_name;
$reports[] = $function_name;
$messages[] = is_string( $message ) ? $message : '';

$this->setExpectedIncorrectUsage( $function_name );
};

$this->incorrect_usage_listener = $listener;

add_action( 'doing_it_wrong_run', $listener );
add_action( 'doing_it_wrong_run', $listener, 10, 2 );
}

/**
Expand All @@ -84,22 +112,64 @@ protected function assert_the_library_reported_incorrect_usage(): void {
}

/**
* Take the listener back off. Call from tearDown.
* The same, and that one of the reports says which failure it was raised for.
*
* Removed by identity rather than by clearing the hook, which WordPress and the rest of the suite
* are also on.
* The assertion above is deliberately loose, which makes it the wrong one for a test about a
* particular gate: every other reason this library reports — an unreadable registry, a bootstrap
* with no hook prefix, a step that threw — satisfies it just as well, so the test would go on
* passing after the gate it was written for stopped running at all.
*
* @since 1.0.0
*
* @param string $needle Text one report has to carry, distinctive enough to name only that report
* site.
* @param string $why Why that report and no other is the one under test.
*
* @return void
*/
protected function assert_the_library_reported_incorrect_usage_saying( string $needle, string $why ): void {
$this->assert_the_library_reported_incorrect_usage();

$this->assertStringContainsString(
$needle,
implode( PHP_EOL, $this->incorrect_usage_messages ),
$why
);
}

/**
* Take the listener back off, and forget what it recorded. Call from tearDown.
*
* Idempotent, and safe on a test that never expected a report at all.
*
* @since 1.0.0
*
* @return void
*/
protected function stop_expecting_incorrect_usage(): void {
if ( $this->incorrect_usage_listener !== null ) {
remove_action( 'doing_it_wrong_run', $this->incorrect_usage_listener );
$this->remove_incorrect_usage_listener();

$this->incorrect_usage_reports = [];
$this->incorrect_usage_messages = [];
}

$this->incorrect_usage_listener = null;
/**
* Unhook whatever this trait last installed, if anything.
*
* Removed by identity rather than by clearing the hook, which WordPress and the rest of the suite
* are also on.
*
* @since 1.0.0
*
* @return void
*/
private function remove_incorrect_usage_listener(): void {
if ( $this->incorrect_usage_listener === null ) {
return;
}

$this->incorrect_usage_reports = [];
remove_action( 'doing_it_wrong_run', $this->incorrect_usage_listener );

$this->incorrect_usage_listener = null;
}
}
Loading
Loading