| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 2 | // This file is part of Moodle - https://moodle.org/
 | 
        
           |  |  | 3 | //
 | 
        
           |  |  | 4 | // Moodle is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 5 | // it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 6 | // the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 7 | // (at your option) any later version.
 | 
        
           |  |  | 8 | //
 | 
        
           |  |  | 9 | // Moodle is distributed in the hope that it will be useful,
 | 
        
           |  |  | 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 12 | // GNU General Public License for more details.
 | 
        
           |  |  | 13 | //
 | 
        
           |  |  | 14 | // You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 15 | // along with Moodle.  If not, see <https://www.gnu.org/licenses/>.
 | 
        
           |  |  | 16 |   | 
        
           |  |  | 17 | namespace core\hook;
 | 
        
           |  |  | 18 |   | 
        
           |  |  | 19 | use core\di;
 | 
        
           |  |  | 20 |   | 
        
           |  |  | 21 | /**
 | 
        
           |  |  | 22 |  * Hooks tests.
 | 
        
           |  |  | 23 |  *
 | 
        
           |  |  | 24 |  * @package   core
 | 
        
           |  |  | 25 |  * @author    Petr Skoda
 | 
        
           |  |  | 26 |  * @copyright 2022 Open LMS
 | 
        
           |  |  | 27 |  * @license   https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 28 |  * @covers \core\hook\manager
 | 
        
           |  |  | 29 |  */
 | 
        
           |  |  | 30 | final class manager_test extends \advanced_testcase {
 | 
        
           |  |  | 31 |     /**
 | 
        
           |  |  | 32 |      * Test public factory method to get hook manager.
 | 
        
           |  |  | 33 |      */
 | 
        
           |  |  | 34 |     public function test_get_instance(): void {
 | 
        
           |  |  | 35 |         $manager = manager::get_instance();
 | 
        
           |  |  | 36 |         $this->assertInstanceOf(manager::class, $manager);
 | 
        
           |  |  | 37 |   | 
        
           |  |  | 38 |         $this->assertSame($manager, manager::get_instance());
 | 
        
           |  |  | 39 |     }
 | 
        
           |  |  | 40 |   | 
        
           |  |  | 41 |     /**
 | 
        
           |  |  | 42 |      * Test getting of manager test instance.
 | 
        
           |  |  | 43 |      */
 | 
        
           |  |  | 44 |     public function test_phpunit_get_instance(): void {
 | 
        
           |  |  | 45 |         $testmanager = manager::phpunit_get_instance([]);
 | 
        
           |  |  | 46 |         $this->assertSame([], $testmanager->get_hooks_with_callbacks());
 | 
        
           |  |  | 47 |   | 
        
           |  |  | 48 |         // We get a new instance every time.
 | 
        
           |  |  | 49 |         $this->assertNotSame($testmanager, manager::phpunit_get_instance([]));
 | 
        
           |  |  | 50 |   | 
        
           |  |  | 51 |         $componentfiles = [
 | 
        
           |  |  | 52 |             'test_plugin1' => __DIR__ . '/../fixtures/hook/hooks1_valid.php',
 | 
        
           |  |  | 53 |         ];
 | 
        
           |  |  | 54 |         $testmanager = manager::phpunit_get_instance($componentfiles);
 | 
        
           |  |  | 55 |         $this->assertSame(['test_plugin\\hook\\hook'], $testmanager->get_hooks_with_callbacks());
 | 
        
           |  |  | 56 |     }
 | 
        
           |  |  | 57 |   | 
        
           |  |  | 58 |     /**
 | 
        
           |  |  | 59 |      * Test loading and parsing of callbacks from files.
 | 
        
           |  |  | 60 |      */
 | 
        
           |  |  | 61 |     public function test_callbacks(): void {
 | 
        
           |  |  | 62 |         $componentfiles = [
 | 
        
           |  |  | 63 |             'test_plugin1' => __DIR__ . '/../fixtures/hook/hooks1_valid.php',
 | 
        
           |  |  | 64 |             'test_plugin2' => __DIR__ . '/../fixtures/hook/hooks2_valid.php',
 | 
        
           |  |  | 65 |         ];
 | 
        
           |  |  | 66 |         $testmanager = manager::phpunit_get_instance($componentfiles);
 | 
        
           |  |  | 67 |         $this->assertSame(['test_plugin\\hook\\hook'], $testmanager->get_hooks_with_callbacks());
 | 
        
           |  |  | 68 |         $callbacks = $testmanager->get_callbacks_for_hook('test_plugin\\hook\\hook');
 | 
        
           |  |  | 69 |         $this->assertCount(2, $callbacks);
 | 
        
           |  |  | 70 |         $this->assertSame([
 | 
        
           |  |  | 71 |             'callback' => 'test_plugin\\callbacks::test2',
 | 
        
           |  |  | 72 |             'component' => 'test_plugin2',
 | 
        
           |  |  | 73 |             'disabled' => false,
 | 
        
           |  |  | 74 |             'priority' => 200,
 | 
        
           |  |  | 75 |         ], $callbacks[0]);
 | 
        
           |  |  | 76 |         $this->assertSame([
 | 
        
           |  |  | 77 |             'callback' => 'test_plugin\\callbacks::test1',
 | 
        
           |  |  | 78 |             'component' => 'test_plugin1',
 | 
        
           |  |  | 79 |             'disabled' => false,
 | 
        
           |  |  | 80 |             'priority' => 100,
 | 
        
           |  |  | 81 |         ], $callbacks[1]);
 | 
        
           |  |  | 82 |   | 
        
           |  |  | 83 |         $this->assertDebuggingNotCalled();
 | 
        
           |  |  | 84 |         $componentfiles = [
 | 
        
           |  |  | 85 |             'test_plugin1' => __DIR__ . '/../fixtures/hook/hooks1_broken.php',
 | 
        
           |  |  | 86 |         ];
 | 
        
           |  |  | 87 |         $testmanager = manager::phpunit_get_instance($componentfiles);
 | 
        
           |  |  | 88 |         $this->assertSame([], $testmanager->get_hooks_with_callbacks());
 | 
        
           |  |  | 89 |         $debuggings = $this->getDebuggingMessages();
 | 
        
           |  |  | 90 |         $this->resetDebugging();
 | 
        
           |  |  | 91 |         $this->assertSame(
 | 
        
           |  |  | 92 |             'Hook callback definition requires \'hook\' name in \'test_plugin1\'',
 | 
        
           |  |  | 93 |             $debuggings[0]->message
 | 
        
           |  |  | 94 |         );
 | 
        
           |  |  | 95 |         $this->assertSame(
 | 
        
           |  |  | 96 |             'Hook callback definition requires \'callback\' callable in \'test_plugin1\'',
 | 
        
           |  |  | 97 |             $debuggings[1]->message
 | 
        
           |  |  | 98 |         );
 | 
        
           |  |  | 99 |         $this->assertSame(
 | 
        
           |  |  | 100 |             'Hook callback definition contains invalid \'callback\' static class method string in \'test_plugin1\'',
 | 
        
           |  |  | 101 |             $debuggings[2]->message
 | 
        
           |  |  | 102 |         );
 | 
        
           |  |  | 103 |         $this->assertCount(3, $debuggings);
 | 
        
           |  |  | 104 |     }
 | 
        
           |  |  | 105 |   | 
        
           |  |  | 106 |     /**
 | 
        
           |  |  | 107 |      * Test hook dispatching, that is callback execution.
 | 
        
           |  |  | 108 |      */
 | 
        
           |  |  | 109 |     public function test_dispatch(): void {
 | 
        
           |  |  | 110 |         require_once(__DIR__ . '/../fixtures/hook/hook.php');
 | 
        
           |  |  | 111 |         require_once(__DIR__ . '/../fixtures/hook/callbacks.php');
 | 
        
           |  |  | 112 |   | 
        
           |  |  | 113 |         $componentfiles = [
 | 
        
           |  |  | 114 |             'test_plugin1' => __DIR__ . '/../fixtures/hook/hooks1_valid.php',
 | 
        
           |  |  | 115 |             'test_plugin2' => __DIR__ . '/../fixtures/hook/hooks2_valid.php',
 | 
        
           |  |  | 116 |         ];
 | 
        
           |  |  | 117 |         $testmanager = manager::phpunit_get_instance($componentfiles);
 | 
        
           |  |  | 118 |         \test_plugin\callbacks::$calls = [];
 | 
        
           |  |  | 119 |         $hook = new \test_plugin\hook\hook();
 | 
        
           |  |  | 120 |         $result = $testmanager->dispatch($hook);
 | 
        
           |  |  | 121 |         $this->assertSame($hook, $result);
 | 
        
           |  |  | 122 |         $this->assertSame(['test2', 'test1'], \test_plugin\callbacks::$calls);
 | 
        
           |  |  | 123 |         \test_plugin\callbacks::$calls = [];
 | 
        
           |  |  | 124 |         $this->assertDebuggingNotCalled();
 | 
        
           |  |  | 125 |     }
 | 
        
           |  |  | 126 |   | 
        
           |  |  | 127 |     /**
 | 
        
           |  |  | 128 |      * Test hook dispatching, that is callback execution.
 | 
        
           |  |  | 129 |      */
 | 
        
           |  |  | 130 |     public function test_dispatch_with_exception(): void {
 | 
        
           |  |  | 131 |         require_once(__DIR__ . '/../fixtures/hook/hook.php');
 | 
        
           |  |  | 132 |         require_once(__DIR__ . '/../fixtures/hook/callbacks.php');
 | 
        
           |  |  | 133 |   | 
        
           |  |  | 134 |         $componentfiles = [
 | 
        
           |  |  | 135 |             'test_plugin1' => __DIR__ . '/../fixtures/hook/hooks1_exception.php',
 | 
        
           |  |  | 136 |             'test_plugin2' => __DIR__ . '/../fixtures/hook/hooks2_valid.php',
 | 
        
           |  |  | 137 |         ];
 | 
        
           |  |  | 138 |         $testmanager = manager::phpunit_get_instance($componentfiles);
 | 
        
           |  |  | 139 |   | 
        
           |  |  | 140 |         $hook = new \test_plugin\hook\hook();
 | 
        
           |  |  | 141 |   | 
        
           |  |  | 142 |         $this->expectException(\Exception::class);
 | 
        
           |  |  | 143 |         $this->expectExceptionMessage('grrr');
 | 
        
           |  |  | 144 |   | 
        
           |  |  | 145 |         $testmanager->dispatch($hook);
 | 
        
           |  |  | 146 |     }
 | 
        
           |  |  | 147 |   | 
        
           |  |  | 148 |     /**
 | 
        
           |  |  | 149 |      * Test hook dispatching, that is callback execution.
 | 
        
           |  |  | 150 |      */
 | 
        
           |  |  | 151 |     public function test_dispatch_with_invalid(): void {
 | 
        
           |  |  | 152 |         require_once(__DIR__ . '/../fixtures/hook/hook.php');
 | 
        
           |  |  | 153 |         require_once(__DIR__ . '/../fixtures/hook/callbacks.php');
 | 
        
           |  |  | 154 |   | 
        
           |  |  | 155 |         // Missing callbacks is ignored.
 | 
        
           |  |  | 156 |         $componentfiles = [
 | 
        
           |  |  | 157 |             'test_plugin1' => __DIR__ . '/../fixtures/hook/hooks1_missing.php',
 | 
        
           |  |  | 158 |             'test_plugin2' => __DIR__ . '/../fixtures/hook/hooks2_valid.php',
 | 
        
           |  |  | 159 |         ];
 | 
        
           |  |  | 160 |         $testmanager = manager::phpunit_get_instance($componentfiles);
 | 
        
           |  |  | 161 |         \test_plugin\callbacks::$calls = [];
 | 
        
           |  |  | 162 |   | 
        
           |  |  | 163 |         $hook = new \test_plugin\hook\hook();
 | 
        
           |  |  | 164 |   | 
        
           |  |  | 165 |         $testmanager->dispatch($hook);
 | 
        
           |  |  | 166 |         $this->assertDebuggingCalled(
 | 
        
           |  |  | 167 |             "Hook callback definition contains invalid 'callback' method name in 'test_plugin1'. Callback method not found.",
 | 
        
           |  |  | 168 |         );
 | 
        
           |  |  | 169 |         $this->assertSame(['test2'], \test_plugin\callbacks::$calls);
 | 
        
           |  |  | 170 |     }
 | 
        
           |  |  | 171 |   | 
        
           |  |  | 172 |     /**
 | 
        
           |  |  | 173 |      * Test stoppping of hook dispatching.
 | 
        
           |  |  | 174 |      */
 | 
        
           |  |  | 175 |     public function test_dispatch_stoppable(): void {
 | 
        
           |  |  | 176 |         require_once(__DIR__ . '/../fixtures/hook/stoppablehook.php');
 | 
        
           |  |  | 177 |         require_once(__DIR__ . '/../fixtures/hook/callbacks.php');
 | 
        
           |  |  | 178 |   | 
        
           |  |  | 179 |         $componentfiles = [
 | 
        
           |  |  | 180 |             'test_plugin1' => __DIR__ . '/../fixtures/hook/hooks1_stoppable.php',
 | 
        
           |  |  | 181 |             'test_plugin2' => __DIR__ . '/../fixtures/hook/hooks2_stoppable.php',
 | 
        
           |  |  | 182 |         ];
 | 
        
           |  |  | 183 |         $testmanager = manager::phpunit_get_instance($componentfiles);
 | 
        
           |  |  | 184 |         \test_plugin\callbacks::$calls = [];
 | 
        
           |  |  | 185 |         $hook = new \test_plugin\hook\stoppablehook();
 | 
        
           |  |  | 186 |         $result = $testmanager->dispatch($hook);
 | 
        
           |  |  | 187 |         $this->assertSame($hook, $result);
 | 
        
           |  |  | 188 |         $this->assertSame(['stop1'], \test_plugin\callbacks::$calls);
 | 
        
           |  |  | 189 |         \test_plugin\callbacks::$calls = [];
 | 
        
           |  |  | 190 |         $this->assertDebuggingNotCalled();
 | 
        
           |  |  | 191 |     }
 | 
        
           |  |  | 192 |   | 
        
           |  |  | 193 |     /**
 | 
        
           |  |  | 194 |      * Tests callbacks can be overridden via CFG settings.
 | 
        
           |  |  | 195 |      */
 | 
        
           |  |  | 196 |     public function test_callback_overriding(): void {
 | 
        
           |  |  | 197 |         global $CFG;
 | 
        
           |  |  | 198 |         $this->resetAfterTest();
 | 
        
           |  |  | 199 |   | 
        
           |  |  | 200 |         $componentfiles = [
 | 
        
           |  |  | 201 |             'test_plugin1' => __DIR__ . '/../fixtures/hook/hooks1_valid.php',
 | 
        
           |  |  | 202 |             'test_plugin2' => __DIR__ . '/../fixtures/hook/hooks2_valid.php',
 | 
        
           |  |  | 203 |         ];
 | 
        
           |  |  | 204 |   | 
        
           |  |  | 205 |         $testmanager = manager::phpunit_get_instance($componentfiles);
 | 
        
           |  |  | 206 |         $this->assertSame(['test_plugin\\hook\\hook'], $testmanager->get_hooks_with_callbacks());
 | 
        
           |  |  | 207 |         $callbacks = $testmanager->get_callbacks_for_hook('test_plugin\\hook\\hook');
 | 
        
           |  |  | 208 |         $this->assertCount(2, $callbacks);
 | 
        
           |  |  | 209 |         $this->assertSame([
 | 
        
           |  |  | 210 |             'callback' => 'test_plugin\\callbacks::test2',
 | 
        
           |  |  | 211 |             'component' => 'test_plugin2',
 | 
        
           |  |  | 212 |             'disabled' => false,
 | 
        
           |  |  | 213 |             'priority' => 200,
 | 
        
           |  |  | 214 |         ], $callbacks[0]);
 | 
        
           |  |  | 215 |         $this->assertSame([
 | 
        
           |  |  | 216 |             'callback' => 'test_plugin\\callbacks::test1',
 | 
        
           |  |  | 217 |             'component' => 'test_plugin1',
 | 
        
           |  |  | 218 |             'disabled' => false,
 | 
        
           |  |  | 219 |             'priority' => 100,
 | 
        
           |  |  | 220 |         ], $callbacks[1]);
 | 
        
           |  |  | 221 |   | 
        
           |  |  | 222 |         $CFG->hooks_callback_overrides = [
 | 
        
           |  |  | 223 |             'test_plugin\\hook\\hook' => [
 | 
        
           |  |  | 224 |                 'test_plugin\\callbacks::test2' => ['priority' => 33],
 | 
        
           |  |  | 225 |             ],
 | 
        
           |  |  | 226 |         ];
 | 
        
           |  |  | 227 |   | 
        
           |  |  | 228 |         $testmanager = manager::phpunit_get_instance($componentfiles);
 | 
        
           |  |  | 229 |         $this->assertSame(['test_plugin\\hook\\hook'], $testmanager->get_hooks_with_callbacks());
 | 
        
           |  |  | 230 |         $callbacks = $testmanager->get_callbacks_for_hook('test_plugin\\hook\\hook');
 | 
        
           |  |  | 231 |         $this->assertCount(2, $callbacks);
 | 
        
           |  |  | 232 |         $this->normalise_callbacks($callbacks);
 | 
        
           |  |  | 233 |         $this->assertSame([
 | 
        
           |  |  | 234 |             'callback' => 'test_plugin\\callbacks::test1',
 | 
        
           |  |  | 235 |             'component' => 'test_plugin1',
 | 
        
           |  |  | 236 |             'disabled' => false,
 | 
        
           |  |  | 237 |             'priority' => 100,
 | 
        
           |  |  | 238 |         ], $callbacks[0]);
 | 
        
           |  |  | 239 |         $this->assertSame([
 | 
        
           |  |  | 240 |             'callback' => 'test_plugin\\callbacks::test2',
 | 
        
           |  |  | 241 |             'component' => 'test_plugin2',
 | 
        
           |  |  | 242 |             'defaultpriority' => 200,
 | 
        
           |  |  | 243 |             'disabled' => false,
 | 
        
           |  |  | 244 |             'priority' => 33,
 | 
        
           |  |  | 245 |         ], $callbacks[1]);
 | 
        
           |  |  | 246 |   | 
        
           |  |  | 247 |         $CFG->hooks_callback_overrides = [
 | 
        
           |  |  | 248 |             'test_plugin\\hook\\hook' => [
 | 
        
           |  |  | 249 |                 'test_plugin\\callbacks::test2' => ['priority' => 33, 'disabled' => true],
 | 
        
           |  |  | 250 |             ],
 | 
        
           |  |  | 251 |         ];
 | 
        
           |  |  | 252 |         $testmanager = manager::phpunit_get_instance($componentfiles);
 | 
        
           |  |  | 253 |         $this->assertSame(['test_plugin\\hook\\hook'], $testmanager->get_hooks_with_callbacks());
 | 
        
           |  |  | 254 |         $callbacks = $testmanager->get_callbacks_for_hook('test_plugin\\hook\\hook');
 | 
        
           |  |  | 255 |         $this->assertCount(2, $callbacks);
 | 
        
           |  |  | 256 |         $this->normalise_callbacks($callbacks);
 | 
        
           |  |  | 257 |         $this->assertSame([
 | 
        
           |  |  | 258 |             'callback' => 'test_plugin\\callbacks::test1',
 | 
        
           |  |  | 259 |             'component' => 'test_plugin1',
 | 
        
           |  |  | 260 |             'disabled' => false,
 | 
        
           |  |  | 261 |             'priority' => 100,
 | 
        
           |  |  | 262 |         ], $callbacks[0]);
 | 
        
           |  |  | 263 |         $this->assertSame([
 | 
        
           |  |  | 264 |             'callback' => 'test_plugin\\callbacks::test2',
 | 
        
           |  |  | 265 |             'component' => 'test_plugin2',
 | 
        
           |  |  | 266 |             'defaultpriority' => 200,
 | 
        
           |  |  | 267 |             'disabled' => true,
 | 
        
           |  |  | 268 |             'priority' => 33,
 | 
        
           |  |  | 269 |         ], $callbacks[1]);
 | 
        
           |  |  | 270 |   | 
        
           |  |  | 271 |         $CFG->hooks_callback_overrides = [
 | 
        
           |  |  | 272 |             'test_plugin\\hook\\hook' => [
 | 
        
           |  |  | 273 |                 'test_plugin\\callbacks::test2' => ['disabled' => true],
 | 
        
           |  |  | 274 |             ],
 | 
        
           |  |  | 275 |         ];
 | 
        
           |  |  | 276 |         $testmanager = manager::phpunit_get_instance($componentfiles);
 | 
        
           |  |  | 277 |         $this->assertSame(['test_plugin\\hook\\hook'], $testmanager->get_hooks_with_callbacks());
 | 
        
           |  |  | 278 |         $callbacks = $testmanager->get_callbacks_for_hook('test_plugin\\hook\\hook');
 | 
        
           |  |  | 279 |         $this->assertCount(2, $callbacks);
 | 
        
           |  |  | 280 |         $this->assertSame([
 | 
        
           |  |  | 281 |             'callback' => 'test_plugin\\callbacks::test2',
 | 
        
           |  |  | 282 |             'component' => 'test_plugin2',
 | 
        
           |  |  | 283 |             'disabled' => true,
 | 
        
           |  |  | 284 |             'priority' => 200,
 | 
        
           |  |  | 285 |         ], $callbacks[0]);
 | 
        
           |  |  | 286 |         $this->assertSame([
 | 
        
           |  |  | 287 |             'callback' => 'test_plugin\\callbacks::test1',
 | 
        
           |  |  | 288 |             'component' => 'test_plugin1',
 | 
        
           |  |  | 289 |             'disabled' => false,
 | 
        
           |  |  | 290 |             'priority' => 100,
 | 
        
           |  |  | 291 |         ], $callbacks[1]);
 | 
        
           |  |  | 292 |   | 
        
           |  |  | 293 |         require_once(__DIR__ . '/../fixtures/hook/hook.php');
 | 
        
           |  |  | 294 |         require_once(__DIR__ . '/../fixtures/hook/callbacks.php');
 | 
        
           |  |  | 295 |   | 
        
           |  |  | 296 |         \test_plugin\callbacks::$calls = [];
 | 
        
           |  |  | 297 |         $hook = new \test_plugin\hook\hook();
 | 
        
           |  |  | 298 |         $result = $testmanager->dispatch($hook);
 | 
        
           |  |  | 299 |         $this->assertSame($hook, $result);
 | 
        
           |  |  | 300 |         $this->assertSame(['test1'], \test_plugin\callbacks::$calls);
 | 
        
           |  |  | 301 |         \test_plugin\callbacks::$calls = [];
 | 
        
           |  |  | 302 |         $this->assertDebuggingNotCalled();
 | 
        
           |  |  | 303 |         $CFG->hooks_callback_overrides = [];
 | 
        
           |  |  | 304 |     }
 | 
        
           |  |  | 305 |   | 
        
           |  |  | 306 |     /**
 | 
        
           |  |  | 307 |      * Register a fake plugin called hooktest in the component manager.
 | 
        
           |  |  | 308 |      *
 | 
        
           |  |  | 309 |      * Tests consuming this helpers must run in a separate process.
 | 
        
           |  |  | 310 |      */
 | 
        
           |  |  | 311 |     protected function setup_hooktest_plugin(): void {
 | 
        
           |  |  | 312 |         global $CFG;
 | 
        
           |  |  | 313 |   | 
        
           |  |  | 314 |         $this->add_mocked_plugintype('fake', "{$CFG->dirroot}/lib/tests/fixtures/hook/fakeplugins");
 | 
        
           |  |  | 315 |         $this->add_mocked_plugin('fake', 'hooktest', "{$CFG->dirroot}/lib/tests/fixtures/hook/fakeplugins/hooktest");
 | 
        
           |  |  | 316 |     }
 | 
        
           |  |  | 317 |   | 
        
           |  |  | 318 |     /**
 | 
        
           |  |  | 319 |      * Call a plugin callback that has been replaced by a hook, but has no hook callback.
 | 
        
           |  |  | 320 |      *
 | 
        
           |  |  | 321 |      * The original callback should be called, but a debugging message should be output.
 | 
        
           |  |  | 322 |      *
 | 
        
           |  |  | 323 |      * @runInSeparateProcess
 | 
        
           |  |  | 324 |      */
 | 
        
           |  |  | 325 |     public function test_migrated_callback(): void {
 | 
        
           |  |  | 326 |         $this->resetAfterTest(true);
 | 
        
           |  |  | 327 |         // Include plugin hook discovery agent, and the hook that replaces the callback.
 | 
        
           |  |  | 328 |         require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hooks.php');
 | 
        
           |  |  | 329 |         require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook/hook_replacing_callback.php');
 | 
        
           |  |  | 330 |         // Register the fake plugin with the component manager.
 | 
        
           |  |  | 331 |         $this->setup_hooktest_plugin();
 | 
        
           |  |  | 332 |   | 
        
           |  |  | 333 |         // Register the fake plugin with the hook manager, but don't define any hook callbacks.
 | 
        
           |  |  | 334 |         di::set(
 | 
        
           |  |  | 335 |             manager::class,
 | 
        
           |  |  | 336 |             manager::phpunit_get_instance(
 | 
        
           |  |  | 337 |                 [
 | 
        
           |  |  | 338 |                     'fake_hooktest' => __DIR__ . '/../fixtures/hook/fakeplugins/hooktest/db/hooks_nocallbacks.php',
 | 
        
           |  |  | 339 |                 ],
 | 
        
           |  |  | 340 |             ),
 | 
        
           |  |  | 341 |         );
 | 
        
           |  |  | 342 |   | 
        
           |  |  | 343 |         // Confirm a non-deprecated callback is called as expected.
 | 
        
           |  |  | 344 |         $this->assertEquals('Called current callback', component_callback('fake_hooktest', 'current_callback'));
 | 
        
           |  |  | 345 |   | 
        
           |  |  | 346 |         // Confirm the deprecated callback is called as expected.
 | 
        
           |  |  | 347 |         $this->assertEquals(
 | 
        
           |  |  | 348 |             'Called deprecated callback',
 | 
        
           |  |  | 349 |             component_callback('fake_hooktest', 'old_callback', [], null, true)
 | 
        
           |  |  | 350 |         );
 | 
        
           |  |  | 351 |         $this->assertDebuggingCalled(
 | 
        
           |  |  | 352 |             'Callback old_callback in fake_hooktest component should be migrated to new hook ' .
 | 
        
           |  |  | 353 |                 'callback for fake_hooktest\hook\hook_replacing_callback'
 | 
        
           |  |  | 354 |         );
 | 
        
           |  |  | 355 |     }
 | 
        
           |  |  | 356 |   | 
        
           |  |  | 357 |     /**
 | 
        
           |  |  | 358 |      * Call a plugin callback that has been replaced by a hook, and has a hook callback.
 | 
        
           |  |  | 359 |      *
 | 
        
           |  |  | 360 |      * The original callback should not be called, and no debugging should be output.
 | 
        
           |  |  | 361 |      *
 | 
        
           |  |  | 362 |      * @runInSeparateProcess
 | 
        
           |  |  | 363 |      */
 | 
        
           |  |  | 364 |     public function test_migrated_callback_with_replacement(): void {
 | 
        
           |  |  | 365 |         $this->resetAfterTest(true);
 | 
        
           |  |  | 366 |         // Include plugin hook discovery agent, and the hook that replaces the callback, and a hook callback for the hook.
 | 
        
           |  |  | 367 |         require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hooks.php');
 | 
        
           |  |  | 368 |         require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook/hook_replacing_callback.php');
 | 
        
           |  |  | 369 |         require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook_callbacks.php');
 | 
        
           |  |  | 370 |         // Register the fake plugin with the component manager.
 | 
        
           |  |  | 371 |         $this->setup_hooktest_plugin();
 | 
        
           |  |  | 372 |   | 
        
           |  |  | 373 |         // Register the fake plugin with the hook manager, including the hook callback.
 | 
        
           |  |  | 374 |         di::set(
 | 
        
           |  |  | 375 |             manager::class,
 | 
        
           |  |  | 376 |             manager::phpunit_get_instance([
 | 
        
           |  |  | 377 |                 'fake_hooktest' => __DIR__ . '/../fixtures/hook/fakeplugins/hooktest/db/hooks.php',
 | 
        
           |  |  | 378 |             ]),
 | 
        
           |  |  | 379 |         );
 | 
        
           |  |  | 380 |   | 
        
           |  |  | 381 |         // Confirm a non-deprecated callback is called as expected.
 | 
        
           |  |  | 382 |         $this->assertEquals('Called current callback', component_callback('fake_hooktest', 'current_callback'));
 | 
        
           |  |  | 383 |   | 
        
           |  |  | 384 |         // Confirm the deprecated callback is not called, as expected.
 | 
        
           |  |  | 385 |         $this->assertNull(component_callback('fake_hooktest', 'old_callback', [], null, true));
 | 
        
           |  |  | 386 |         $this->assertDebuggingNotCalled();
 | 
        
           |  |  | 387 |     }
 | 
        
           |  |  | 388 |   | 
        
           |  |  | 389 |     /**
 | 
        
           |  |  | 390 |      * Call a plugin class callback that has been replaced by a hook, but has no hook callback.
 | 
        
           |  |  | 391 |      *
 | 
        
           |  |  | 392 |      * The original class callback should be called, but a debugging message should be output.
 | 
        
           |  |  | 393 |      *
 | 
        
           |  |  | 394 |      * @runInSeparateProcess
 | 
        
           |  |  | 395 |      */
 | 
        
           |  |  | 396 |     public function test_migrated_class_callback(): void {
 | 
        
           |  |  | 397 |         $this->resetAfterTest(true);
 | 
        
           |  |  | 398 |         // Include plugin hook discovery agent, the class containing callbacks, and the hook that replaces the class callback.
 | 
        
           |  |  | 399 |         require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/callbacks.php');
 | 
        
           |  |  | 400 |         require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hooks.php');
 | 
        
           |  |  | 401 |         require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook/hook_replacing_class_callback.php');
 | 
        
           |  |  | 402 |         // Register the fake plugin with the component manager.
 | 
        
           |  |  | 403 |         $this->setup_hooktest_plugin();
 | 
        
           |  |  | 404 |   | 
        
           |  |  | 405 |         // Register the fake plugin with the hook manager, but don't define any hook callbacks.
 | 
        
           |  |  | 406 |         di::set(
 | 
        
           |  |  | 407 |             manager::class,
 | 
        
           |  |  | 408 |             manager::phpunit_get_instance([
 | 
        
           |  |  | 409 |                 'fake_hooktest' => __DIR__ . '/../fixtures/hook/fakeplugins/hooktest/db/hooks_nocallbacks.php',
 | 
        
           |  |  | 410 |             ]),
 | 
        
           |  |  | 411 |         );
 | 
        
           |  |  | 412 |   | 
        
           |  |  | 413 |         // Confirm a non-deprecated class callback is called as expected.
 | 
        
           |  |  | 414 |         $this->assertEquals(
 | 
        
           |  |  | 415 |             'Called current class callback',
 | 
        
           |  |  | 416 |             component_class_callback('fake_hooktest\callbacks', 'current_class_callback', [])
 | 
        
           |  |  | 417 |         );
 | 
        
           |  |  | 418 |   | 
        
           |  |  | 419 |         // Confirm the deprecated class callback is called as expected.
 | 
        
           |  |  | 420 |         $this->assertEquals(
 | 
        
           |  |  | 421 |             'Called deprecated class callback',
 | 
        
           |  |  | 422 |             component_class_callback('fake_hooktest\callbacks', 'old_class_callback', [], null, true)
 | 
        
           |  |  | 423 |         );
 | 
        
           |  |  | 424 |         $this->assertDebuggingCalled(
 | 
        
           |  |  | 425 |             'Callback callbacks::old_class_callback in fake_hooktest component should be migrated to new hook ' .
 | 
        
           |  |  | 426 |                 'callback for fake_hooktest\hook\hook_replacing_class_callback'
 | 
        
           |  |  | 427 |         );
 | 
        
           |  |  | 428 |     }
 | 
        
           |  |  | 429 |   | 
        
           |  |  | 430 |     /**
 | 
        
           |  |  | 431 |      * Call a plugin class callback that has been replaced by a hook, and has a hook callback.
 | 
        
           |  |  | 432 |      *
 | 
        
           |  |  | 433 |      * The original callback should not be called, and no debugging should be output.
 | 
        
           |  |  | 434 |      *
 | 
        
           |  |  | 435 |      * @runInSeparateProcess
 | 
        
           |  |  | 436 |      */
 | 
        
           |  |  | 437 |     public function test_migrated_class_callback_with_replacement(): void {
 | 
        
           |  |  | 438 |         $this->resetAfterTest(true);
 | 
        
           |  |  | 439 |         // Include plugin hook discovery agent, the class containing callbacks, the hook that replaces the class callback,
 | 
        
           |  |  | 440 |         // and a hook callback for the new hook.
 | 
        
           |  |  | 441 |         require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/callbacks.php');
 | 
        
           |  |  | 442 |         require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hooks.php');
 | 
        
           |  |  | 443 |         require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook/hook_replacing_class_callback.php');
 | 
        
           |  |  | 444 |         require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook_callbacks.php');
 | 
        
           |  |  | 445 |         // Register the fake plugin with the component manager.
 | 
        
           |  |  | 446 |         $this->setup_hooktest_plugin();
 | 
        
           |  |  | 447 |   | 
        
           |  |  | 448 |         // Register the fake plugin with the hook manager, including the hook callback.
 | 
        
           |  |  | 449 |         di::set(
 | 
        
           |  |  | 450 |             manager::class,
 | 
        
           |  |  | 451 |             manager::phpunit_get_instance([
 | 
        
           |  |  | 452 |                 'fake_hooktest' => __DIR__ . '/../fixtures/hook/fakeplugins/hooktest/db/hooks.php',
 | 
        
           |  |  | 453 |             ]),
 | 
        
           |  |  | 454 |         );
 | 
        
           |  |  | 455 |   | 
        
           |  |  | 456 |         // Confirm a non-deprecated class callback is called as expected.
 | 
        
           |  |  | 457 |         $this->assertEquals(
 | 
        
           |  |  | 458 |             'Called current class callback',
 | 
        
           |  |  | 459 |             component_class_callback('fake_hooktest\callbacks', 'current_class_callback', [])
 | 
        
           |  |  | 460 |         );
 | 
        
           |  |  | 461 |   | 
        
           |  |  | 462 |         // Confirm the deprecated class callback is not called, as expected.
 | 
        
           |  |  | 463 |         $this->assertNull(component_class_callback('fake_hooktest\callbacks', 'old_class_callback', [], null, true));
 | 
        
           |  |  | 464 |         $this->assertDebuggingNotCalled();
 | 
        
           |  |  | 465 |     }
 | 
        
           |  |  | 466 |   | 
        
           |  |  | 467 |     /**
 | 
        
           |  |  | 468 |      * Normalise the sort order of callbacks to help with asserts.
 | 
        
           |  |  | 469 |      *
 | 
        
           |  |  | 470 |      * @param array $callbacks
 | 
        
           |  |  | 471 |      */
 | 
        
           |  |  | 472 |     private function normalise_callbacks(array &$callbacks): void {
 | 
        
           |  |  | 473 |         foreach ($callbacks as &$callback) {
 | 
        
           |  |  | 474 |             ksort($callback);
 | 
        
           |  |  | 475 |         }
 | 
        
           |  |  | 476 |     }
 | 
        
           |  |  | 477 | }
 |