diff --git a/lib/classes/hook/manager.php b/lib/classes/hook/manager.php index ca275516f12..1b29e4f2add 100644 --- a/lib/classes/hook/manager.php +++ b/lib/classes/hook/manager.php @@ -54,6 +54,9 @@ final class manager implements /** @var array list of all deprecated lib.php plugin callbacks. */ private $alldeprecations = []; + /** @var array list of redirected callbacks in PHPUnit tests */ + private $redirectedcallbacks = []; + /** * Constructor can be used only from factory methods. */ @@ -89,6 +92,32 @@ final class manager implements return $instance; } + /** + * Override hook callbacks for testing purposes. + * + * @param string $hookname + * @param callable $callback + * @return void + */ + public function phpunit_redirect_hook(string $hookname, callable $callback): void { + if (!PHPUNIT_TEST) { + throw new \coding_exception('Invalid call of manager::phpunit_redirect_hook() outside of tests'); + } + $this->redirectedcallbacks[$hookname] = $callback; + } + + /** + * Cancel all redirections of hook callbacks. + * + * @return void + */ + public function phpunit_stop_redirections(): void { + if (!PHPUNIT_TEST) { + throw new \coding_exception('Invalid call of manager::phpunit_stop_redirections() outside of tests'); + } + $this->redirectedcallbacks = []; + } + /** * Returns list of callbacks for given hook name. * @@ -215,6 +244,14 @@ final class manager implements return $event; } + if (PHPUNIT_TEST) { + $hookclassname = get_class($event); + if (isset($this->redirectedcallbacks[$hookclassname])) { + call_user_func($this->redirectedcallbacks[$hookclassname], $event); + return $event; + } + } + $callbacks = $this->getListenersForEvent($event); if (empty($callbacks)) { diff --git a/lib/phpunit/classes/advanced_testcase.php b/lib/phpunit/classes/advanced_testcase.php index fa7f8a14439..25ceef15107 100644 --- a/lib/phpunit/classes/advanced_testcase.php +++ b/lib/phpunit/classes/advanced_testcase.php @@ -499,6 +499,26 @@ abstract class advanced_testcase extends base_testcase { return phpunit_util::start_event_redirection(); } + /** + * Override hook callbacks. + * + * @param string $hookname + * @param callable $callback + * @return void + */ + public function redirectHook(string $hookname, callable $callback): void { + \core\hook\manager::get_instance()->phpunit_redirect_hook($hookname, $callback); + } + + /** + * Remove all hook overrides. + * + * @return void + */ + public function stopHookRedirections(): void { + \core\hook\manager::get_instance()->phpunit_stop_redirections(); + } + /** * Reset all database tables, restore global state and clear caches and optionally purge dataroot dir. * diff --git a/lib/phpunit/classes/util.php b/lib/phpunit/classes/util.php index 59b92725a26..fc131dc2614 100644 --- a/lib/phpunit/classes/util.php +++ b/lib/phpunit/classes/util.php @@ -104,6 +104,9 @@ class phpunit_util extends testing_util { public static function reset_all_data($detectchanges = false) { global $DB, $CFG, $USER, $SITE, $COURSE, $PAGE, $OUTPUT, $SESSION, $FULLME, $FILTERLIB_PRIVATE; + // Stop all hook redirections. + \core\hook\manager::get_instance()->phpunit_stop_redirections(); + // Stop any message redirection. self::stop_message_redirection();