Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://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 <http://www.gnu.org/licenses/>.
  16  
  17  namespace core\event;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  require_once (__DIR__.'/..//fixtures/event_fixtures.php');
  22  
  23  /**
  24   * Tests for event manager, base event and observers.
  25   *
  26   * @package    core
  27   * @category   phpunit
  28   * @copyright  2013 Petr Skoda {@link http://skodak.org}
  29   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   */
  31  class base_test extends \advanced_testcase {
  32  
  33      const DEBUGGING_MSG = 'Events API using $handlers array has been deprecated in favour of Events 2 API, please use it instead.';
  34  
  35      public function test_event_properties() {
  36          global $USER;
  37  
  38          $system = \context_system::instance();
  39          $event = \core_tests\event\unittest_executed::create(array('context'=>$system, 'objectid'=>5, 'other'=>array('sample'=>null, 'xx'=>10)));
  40  
  41          $this->assertSame('\core_tests\event\unittest_executed', $event->eventname);
  42          $this->assertSame('core_tests', $event->component);
  43          $this->assertSame('executed', $event->action);
  44          $this->assertSame('unittest', $event->target);
  45          $this->assertSame(5, $event->objectid);
  46          $this->assertSame('u', $event->crud);
  47          $this->assertSame(\core\event\base::LEVEL_PARTICIPATING, $event->edulevel);
  48  
  49          $this->assertEquals($system, $event->get_context());
  50          $this->assertSame($system->id, $event->contextid);
  51          $this->assertSame($system->contextlevel, $event->contextlevel);
  52          $this->assertSame($system->instanceid, $event->contextinstanceid);
  53  
  54          $this->assertSame($USER->id, $event->userid);
  55          $this->assertSame(0, $event->courseid);
  56  
  57          $this->assertNull($event->relateduserid);
  58          $this->assertFalse(isset($event->relateduserid));
  59  
  60          $this->assertSame(0, $event->anonymous);
  61  
  62          $this->assertSame(array('sample'=>null, 'xx'=>10), $event->other);
  63          $this->assertTrue(isset($event->other['xx']));
  64          $this->assertFalse(isset($event->other['sample']));
  65  
  66          $this->assertLessThanOrEqual(time(), $event->timecreated);
  67  
  68          try {
  69              $event->courseid = 2;
  70              $this->fail('Exception expected on event modification');
  71          } catch (\moodle_exception $e) {
  72              $this->assertInstanceOf(\coding_exception::class, $e);
  73          }
  74  
  75          try {
  76              $event->xxxx = 1;
  77              $this->fail('Exception expected on event modification');
  78          } catch (\moodle_exception $e) {
  79              $this->assertInstanceOf(\coding_exception::class, $e);
  80          }
  81  
  82          $event2 = \core_tests\event\unittest_executed::create(array('contextid'=>$system->id, 'objectid'=>5, 'anonymous'=>1, 'other'=>array('sample'=>null, 'xx'=>10)));
  83          $this->assertEquals($event->get_context(), $event2->get_context());
  84          $this->assertSame(1, $event2->anonymous);
  85  
  86          $event3 = \core_tests\event\unittest_executed::create(array('contextid'=>$system->id, 'objectid'=>5, 'anonymous'=>true, 'other'=>array('sample'=>null, 'xx'=>10)));
  87          $this->assertSame(1, $event3->anonymous);
  88      }
  89  
  90      public function test_event_properties_guessing() {
  91          global $USER;
  92          $this->resetAfterTest();
  93  
  94          $course = $this->getDataGenerator()->create_course();
  95          $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id));
  96          $context = \context_module::instance($forum->cmid);
  97          $event = \core_tests\event\unittest_executed::create(array('context' => $context, 'objectid' => 5));
  98  
  99          // Check guessed course ID, and default properties.
 100          $this->assertSame('\core_tests\event\unittest_executed', $event->eventname);
 101          $this->assertSame('core_tests', $event->component);
 102          $this->assertSame('executed', $event->action);
 103          $this->assertSame('unittest', $event->target);
 104          $this->assertSame(5, $event->objectid);
 105          $this->assertEquals($context, $event->get_context());
 106          $this->assertEquals($course->id, $event->courseid);
 107          $this->assertSame($USER->id, $event->userid);
 108          $this->assertNull($event->relateduserid);
 109  
 110          $user = $this->getDataGenerator()->create_user();
 111          $context = \context_user::instance($user->id);
 112          $event = \core_tests\event\unittest_executed::create(array('contextid' => $context->id, 'objectid' => 5));
 113  
 114          // Check guessing on contextid, and user context level.
 115          $this->assertEquals($context, $event->get_context());
 116          $this->assertEquals($context->id, $event->contextid);
 117          $this->assertEquals($context->contextlevel, $event->contextlevel);
 118          $this->assertSame(0, $event->courseid);
 119          $this->assertSame($USER->id, $event->userid);
 120          $this->assertSame($user->id, $event->relateduserid);
 121      }
 122  
 123      public function test_observers_parsing() {
 124          global $CFG;
 125  
 126          $observers = array(
 127              array(
 128                  'eventname'   => '*',
 129                  'callback'    => array('\core_tests\event\unittest_observer', 'observe_all_alt'),
 130              ),
 131              array(
 132                  'eventname'   => '\core_tests\event\unittest_executed',
 133                  'callback'    => '\core_tests\event\unittest_observer::observe_one',
 134                  'includefile' => 'lib/tests/fixtures/event_fixtures.php',
 135              ),
 136              array(
 137                  'eventname'   => '*',
 138                  'callback'    => array('\core_tests\event\unittest_observer', 'observe_all'),
 139                  'includefile' => null,
 140                  'internal'    => 1,
 141                  'priority'    => 10,
 142              ),
 143              array(
 144                  'eventname'   => '\core\event\unknown_executed',
 145                  'callback'    => '\core_tests\event\unittest_observer::broken_observer',
 146                  'priority'    => 100,
 147              ),
 148              array(
 149                  'eventname'   => '\core_tests\event\unittest_executed',
 150                  'callback'    => '\core_tests\event\unittest_observer::external_observer',
 151                  'priority'    => 200,
 152                  'internal'    => 0,
 153              ),
 154          );
 155  
 156          $result = \core\event\manager::phpunit_replace_observers($observers);
 157          $this->assertCount(3, $result);
 158  
 159          $expected = array();
 160          $observer = new \stdClass();
 161          $observer->callable = '\core_tests\event\unittest_observer::external_observer';
 162          $observer->priority = 200;
 163          $observer->internal = false;
 164          $observer->includefile = null;
 165          $observer->plugintype = null;
 166          $observer->plugin = null;
 167          $expected[0] = $observer;
 168          $observer = new \stdClass();
 169          $observer->callable = '\core_tests\event\unittest_observer::observe_one';
 170          $observer->priority = 0;
 171          $observer->internal = true;
 172          $observer->includefile = $CFG->dirroot.'/lib/tests/fixtures/event_fixtures.php';
 173          $observer->plugintype = null;
 174          $observer->plugin = null;
 175          $expected[1] = $observer;
 176  
 177          $this->assertEquals($expected, $result['\core_tests\event\unittest_executed']);
 178  
 179          $expected = array();
 180          $observer = new \stdClass();
 181          $observer->callable = '\core_tests\event\unittest_observer::broken_observer';
 182          $observer->priority = 100;
 183          $observer->internal = true;
 184          $observer->includefile = null;
 185          $observer->plugintype = null;
 186          $observer->plugin = null;
 187          $expected[0] = $observer;
 188  
 189          $this->assertEquals($expected, $result['\core\event\unknown_executed']);
 190  
 191          $expected = array();
 192          $observer = new \stdClass();
 193          $observer->callable = array('\core_tests\event\unittest_observer', 'observe_all');
 194          $observer->priority = 10;
 195          $observer->internal = true;
 196          $observer->includefile = null;
 197          $observer->plugintype = null;
 198          $observer->plugin = null;
 199          $expected[0] = $observer;
 200          $observer = new \stdClass();
 201          $observer->callable = array('\core_tests\event\unittest_observer', 'observe_all_alt');
 202          $observer->priority = 0;
 203          $observer->internal = true;
 204          $observer->includefile = null;
 205          $observer->plugintype = null;
 206          $observer->plugin = null;
 207          $expected[1] = $observer;
 208  
 209          $this->assertEquals($expected, $result['\core\event\base']);
 210  
 211          // Now test broken stuff...
 212  
 213          $observers = array(
 214              array(
 215                  'eventname'   => 'core_tests\event\unittest_executed', // Fix leading backslash.
 216                  'callback'    => '\core_tests\event\unittest_observer::observe_one',
 217                  'includefile' => 'lib/tests/fixtures/event_fixtures.php',
 218                  'internal'    => 1, // Cast to bool.
 219              ),
 220          );
 221          $result = \core\event\manager::phpunit_replace_observers($observers);
 222          $this->assertCount(1, $result);
 223          $expected = array();
 224          $observer = new \stdClass();
 225          $observer->callable = '\core_tests\event\unittest_observer::observe_one';
 226          $observer->priority = 0;
 227          $observer->internal = true;
 228          $observer->includefile = $CFG->dirroot.'/lib/tests/fixtures/event_fixtures.php';
 229          $observer->plugintype = null;
 230          $observer->plugin = null;
 231          $expected[0] = $observer;
 232          $this->assertEquals($expected, $result['\core_tests\event\unittest_executed']);
 233  
 234          $observers = array(
 235              array(
 236                  // Missing eventclass.
 237                  'callback'    => '\core_tests\event\unittest_observer::observe_one',
 238                  'includefile' => 'lib/tests/fixtures/event_fixtures.php',
 239              ),
 240          );
 241          $result = \core\event\manager::phpunit_replace_observers($observers);
 242          $this->assertCount(0, $result);
 243          $this->assertDebuggingCalled();
 244  
 245          $observers = array(
 246              array(
 247                  'eventname'   => '', // Empty eventclass.
 248                  'callback'    => '\core_tests\event\unittest_observer::observe_one',
 249                  'includefile' => 'lib/tests/fixtures/event_fixtures.php',
 250              ),
 251          );
 252          $result = \core\event\manager::phpunit_replace_observers($observers);
 253          $this->assertCount(0, $result);
 254          $this->assertDebuggingCalled();
 255  
 256          $observers = array(
 257              array(
 258                  'eventname'   => '\core_tests\event\unittest_executed',
 259                  // Missing callable.
 260                  'includefile' => 'lib/tests/fixtures/event_fixtures.php',
 261              ),
 262          );
 263          $result = \core\event\manager::phpunit_replace_observers($observers);
 264          $this->assertCount(0, $result);
 265          $this->assertDebuggingCalled();
 266  
 267          $observers = array(
 268              array(
 269                  'eventname'   => '\core_tests\event\unittest_executed',
 270                  'callback'    => '', // Empty callable.
 271                  'includefile' => 'lib/tests/fixtures/event_fixtures.php',
 272              ),
 273          );
 274          $result = \core\event\manager::phpunit_replace_observers($observers);
 275          $this->assertCount(0, $result);
 276          $this->assertDebuggingCalled();
 277  
 278          $observers = array(
 279              array(
 280                  'eventname'   => '\core_tests\event\unittest_executed',
 281                  'callback'    => '\core_tests\event\unittest_observer::observe_one',
 282                  'includefile' => 'lib/tests/fixtures/event_fixtures.php_xxx', // Missing file.
 283              ),
 284          );
 285          $result = \core\event\manager::phpunit_replace_observers($observers);
 286          $this->assertCount(0, $result);
 287          $this->assertDebuggingCalled();
 288      }
 289  
 290      public function test_normal_dispatching() {
 291          $observers = array(
 292              array(
 293                  'eventname'   => '\core_tests\event\unittest_executed',
 294                  'callback'    => '\core_tests\event\unittest_observer::observe_one',
 295              ),
 296              array(
 297                  'eventname'   => '*',
 298                  'callback'    => '\core_tests\event\unittest_observer::observe_all',
 299                  'includefile' => null,
 300                  'internal'    => 1,
 301                  'priority'    => 9999,
 302              ),
 303          );
 304  
 305          \core\event\manager::phpunit_replace_observers($observers);
 306          \core_tests\event\unittest_observer::reset();
 307  
 308          $event1 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
 309          $event1->nest = 1;
 310          $this->assertFalse($event1->is_triggered());
 311          $this->assertFalse($event1->is_dispatched());
 312          $this->assertFalse($event1->is_restored());
 313          $event1->trigger();
 314          $this->assertTrue($event1->is_triggered());
 315          $this->assertTrue($event1->is_dispatched());
 316          $this->assertFalse($event1->is_restored());
 317  
 318          $event1 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>2, 'xx'=>10)));
 319          $event1->trigger();
 320  
 321          $this->assertSame(
 322              array('observe_all-nesting-1', 'observe_one-1', 'observe_all-666', 'observe_one-666', 'observe_all-2', 'observe_one-2'),
 323              \core_tests\event\unittest_observer::$info);
 324      }
 325  
 326      public function test_event_sink() {
 327          $sink = $this->redirectEvents();
 328          $event1 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
 329          $event1->trigger();
 330          $this->assertSame(1, $sink->count());
 331          $retult = $sink->get_events();
 332          $this->assertSame($event1, $retult[0]);
 333  
 334          $event2 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>2, 'xx'=>10)));
 335          $event2->trigger();
 336          $this->assertSame(2, $sink->count());
 337          $retult = $sink->get_events();
 338          $this->assertSame($event1, $retult[0]);
 339          $this->assertSame($event2, $retult[1]);
 340  
 341          $sink->clear();
 342          $this->assertSame(0, $sink->count());
 343          $this->assertSame(array(), $sink->get_events());
 344  
 345          $event3 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>3, 'xx'=>10)));
 346          $event3->trigger();
 347          $this->assertSame(1, $sink->count());
 348          $retult = $sink->get_events();
 349          $this->assertSame($event3, $retult[0]);
 350  
 351          $sink->close();
 352          $event4 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>4, 'xx'=>10)));
 353          $event4->trigger();
 354          $this->assertSame(1, $sink->count());
 355          $retult = $sink->get_events();
 356          $this->assertSame($event3, $retult[0]);
 357      }
 358  
 359      public function test_ignore_exceptions() {
 360          $observers = array(
 361  
 362              array(
 363                  'eventname'   => '\core_tests\event\unittest_executed',
 364                  'callback'    => '\core_tests\event\unittest_observer::observe_one',
 365              ),
 366  
 367              array(
 368                  'eventname'   => '\core_tests\event\unittest_executed',
 369                  'callback'    => '\core_tests\event\unittest_observer::broken_observer',
 370                  'priority'    => 100,
 371              ),
 372          );
 373  
 374          \core\event\manager::phpunit_replace_observers($observers);
 375          \core_tests\event\unittest_observer::reset();
 376  
 377          $event1 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
 378          $event1->trigger();
 379          $this->assertDebuggingCalled();
 380  
 381          $event1 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>2, 'xx'=>10)));
 382          $event1->trigger();
 383          $this->assertDebuggingCalled();
 384  
 385          $this->assertSame(
 386              array('broken_observer-1', 'observe_one-1', 'broken_observer-2', 'observe_one-2'),
 387              \core_tests\event\unittest_observer::$info);
 388      }
 389  
 390      public function test_external_buffer() {
 391          global $DB;
 392  
 393          $this->preventResetByRollback();
 394  
 395          $observers = array(
 396  
 397              array(
 398                  'eventname'   => '\core_tests\event\unittest_executed',
 399                  'callback'    => '\core_tests\event\unittest_observer::observe_one',
 400              ),
 401  
 402              array(
 403                  'eventname'   => '\core_tests\event\unittest_executed',
 404                  'callback'    => '\core_tests\event\unittest_observer::external_observer',
 405                  'priority'    => 200,
 406                  'internal'    => 0,
 407              ),
 408          );
 409  
 410          \core\event\manager::phpunit_replace_observers($observers);
 411          \core_tests\event\unittest_observer::reset();
 412  
 413          $event1 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
 414          $event1->trigger();
 415          $event2 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>2, 'xx'=>10)));
 416          $event2->trigger();
 417  
 418          $this->assertSame(
 419              array('external_observer-1', 'observe_one-1', 'external_observer-2', 'observe_one-2'),
 420              \core_tests\event\unittest_observer::$info);
 421  
 422          \core\event\manager::phpunit_replace_observers($observers);
 423          \core_tests\event\unittest_observer::reset();
 424  
 425          $this->assertSame(array(), \core_tests\event\unittest_observer::$info);
 426  
 427          $trans = $DB->start_delegated_transaction();
 428  
 429          $event1 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
 430          $event1->trigger();
 431          $event2 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>2, 'xx'=>10)));
 432          $event2->trigger();
 433  
 434          $this->assertSame(
 435              array('observe_one-1', 'observe_one-2'),
 436              \core_tests\event\unittest_observer::$info);
 437  
 438          $trans->allow_commit();
 439  
 440          $this->assertSame(
 441              array('observe_one-1', 'observe_one-2', 'external_observer-1', 'external_observer-2'),
 442              \core_tests\event\unittest_observer::$info);
 443  
 444          \core\event\manager::phpunit_replace_observers($observers);
 445          \core_tests\event\unittest_observer::reset();
 446  
 447          $event1 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
 448          $event1->trigger();
 449          $trans = $DB->start_delegated_transaction();
 450          $event2 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>2, 'xx'=>10)));
 451          $event2->trigger();
 452          try {
 453              $trans->rollback(new \moodle_exception('xxx'));
 454              $this->fail('Expecting exception');
 455          } catch (\moodle_exception $e) {
 456              $this->assertInstanceOf(\moodle_exception::class, $e);
 457          }
 458  
 459          $this->assertSame(
 460              array('external_observer-1', 'observe_one-1', 'observe_one-2'),
 461              \core_tests\event\unittest_observer::$info);
 462      }
 463  
 464      public function test_rollback() {
 465          global $DB;
 466  
 467          $this->resetAfterTest();
 468          $this->preventResetByRollback();
 469  
 470          $observers = array(
 471              array(
 472                  'eventname'   => '\core_tests\event\unittest_executed',
 473                  'callback'    => '\core_tests\event\unittest_observer::external_observer',
 474                  'internal'    => 0,
 475              ),
 476          );
 477  
 478          \core\event\manager::phpunit_replace_observers($observers);
 479          \core_tests\event\unittest_observer::reset();
 480  
 481          $this->assertCount(0, \core_tests\event\unittest_observer::$event);
 482  
 483          \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)))->trigger();
 484          $this->assertCount(1, \core_tests\event\unittest_observer::$event);
 485          \core_tests\event\unittest_observer::reset();
 486  
 487          $transaction1 = $DB->start_delegated_transaction();
 488  
 489          \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)))->trigger();
 490          $this->assertCount(0, \core_tests\event\unittest_observer::$event);
 491  
 492          $transaction2 = $DB->start_delegated_transaction();
 493  
 494          \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)))->trigger();
 495          $this->assertCount(0, \core_tests\event\unittest_observer::$event);
 496  
 497          try {
 498              $transaction2->rollback(new \Exception('x'));
 499              $this->fail('Expecting exception');
 500          } catch (\Exception $e) {}
 501          $this->assertCount(0, \core_tests\event\unittest_observer::$event);
 502  
 503          $this->assertTrue($DB->is_transaction_started());
 504  
 505          try {
 506              $transaction1->rollback(new \Exception('x'));
 507              $this->fail('Expecting exception');
 508          } catch (\Exception $e) {}
 509          $this->assertCount(0, \core_tests\event\unittest_observer::$event);
 510  
 511          $this->assertFalse($DB->is_transaction_started());
 512  
 513          \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)))->trigger();
 514          $this->assertCount(1, \core_tests\event\unittest_observer::$event);
 515      }
 516  
 517      public function test_forced_rollback() {
 518          global $DB;
 519  
 520          $this->resetAfterTest();
 521          $this->preventResetByRollback();
 522  
 523          $observers = array(
 524              array(
 525                  'eventname'   => '\core_tests\event\unittest_executed',
 526                  'callback'    => '\core_tests\event\unittest_observer::external_observer',
 527                  'internal'    => 0,
 528              ),
 529          );
 530  
 531          \core\event\manager::phpunit_replace_observers($observers);
 532          \core_tests\event\unittest_observer::reset();
 533  
 534          $this->assertCount(0, \core_tests\event\unittest_observer::$event);
 535  
 536          \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)))->trigger();
 537          $this->assertCount(1, \core_tests\event\unittest_observer::$event);
 538          \core_tests\event\unittest_observer::reset();
 539  
 540          $transaction1 = $DB->start_delegated_transaction();
 541  
 542          \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)))->trigger();
 543          $this->assertCount(0, \core_tests\event\unittest_observer::$event);
 544  
 545          $transaction2 = $DB->start_delegated_transaction();
 546  
 547          \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)))->trigger();
 548          $this->assertCount(0, \core_tests\event\unittest_observer::$event);
 549  
 550          $DB->force_transaction_rollback();
 551          $this->assertCount(0, \core_tests\event\unittest_observer::$event);
 552  
 553          $this->assertFalse($DB->is_transaction_started());
 554  
 555          \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)))->trigger();
 556          $this->assertCount(1, \core_tests\event\unittest_observer::$event);
 557      }
 558  
 559      public function test_deprecated() {
 560          global $DB;
 561  
 562          $this->resetAfterTest(true);
 563  
 564          $event = \core_tests\event\deprecated_event1::create();
 565          $this->assertDebuggingCalled('level property is deprecated, use edulevel property instead');
 566  
 567          $this->assertSame($event::LEVEL_TEACHING, $event->level);
 568          $this->assertDebuggingCalled('level property is deprecated, use edulevel property instead');
 569  
 570          $this->assertTrue(isset($event->level));
 571          $this->assertDebuggingCalled('level property is deprecated, use edulevel property instead');
 572  
 573          $this->assertSame($event::LEVEL_TEACHING, $event->edulevel);
 574      }
 575  
 576      public function test_legacy() {
 577          global $DB, $CFG;
 578  
 579          $this->resetAfterTest(true);
 580  
 581          $observers = array(
 582              array(
 583                  'eventname'   => '\core_tests\event\unittest_executed',
 584                  'callback'    => '\core_tests\event\unittest_observer::observe_one',
 585              ),
 586              array(
 587                  'eventname'   => '*',
 588                  'callback'    => '\core_tests\event\unittest_observer::observe_all',
 589                  'includefile' => null,
 590                  'internal'    => 1,
 591                  'priority'    => 9999,
 592              ),
 593          );
 594  
 595          $DB->delete_records('log', array());
 596          $this->expectException(\coding_exception::class);
 597          events_update_definition('unittest');
 598  
 599          $DB->delete_records_select('events_handlers', "component <> 'unittest'");
 600  
 601          $this->assertDebuggingCalled(self::DEBUGGING_MSG, DEBUG_DEVELOPER);
 602          $this->assertEquals(3, $DB->count_records('events_handlers'));
 603          set_config('loglifetime', 60*60*24*5);
 604  
 605          \core\event\manager::phpunit_replace_observers($observers);
 606          \core_tests\event\unittest_observer::reset();
 607  
 608          $event1 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>5, 'xx'=>10)));
 609          $event1->trigger();
 610  
 611          $event2 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>6, 'xx'=>11)));
 612          $event2->nest = true;
 613          $event2->trigger();
 614  
 615          $this->assertSame(
 616              array('observe_all-5', 'observe_one-5', 'observe_all-nesting-6', 'observe_one-6', 'observe_all-666', 'observe_one-666'),
 617              \core_tests\event\unittest_observer::$info);
 618  
 619          $this->assertSame($event1, \core_tests\event\unittest_observer::$event[0]);
 620          $this->assertSame($event1, \core_tests\event\unittest_observer::$event[1]);
 621  
 622          $logs = $DB->get_records('log', array(), 'id ASC');
 623          $this->assertCount(0, $logs);
 624      }
 625  
 626      public function test_restore_event() {
 627          $event1 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
 628          $data1 = $event1->get_data();
 629  
 630          $event2 = \core\event\base::restore($data1, array('origin'=>'clid'));
 631          $data2 = $event2->get_data();
 632  
 633          $this->assertTrue($event2->is_triggered());
 634          $this->assertTrue($event2->is_restored());
 635          $this->assertEquals($data1, $data2);
 636          $this->assertInstanceOf(\core_tests\event\unittest_executed::class, $event2);
 637  
 638          $this->assertEquals($event1->get_context(), $event2->get_context());
 639  
 640          // Now test problematic data.
 641          $data3 = $data1;
 642          $data3['eventname'] = '\\a\\b\\c';
 643          $event3 = \core\event\base::restore($data3, array());
 644          $this->assertFalse($event3, 'Class name must match');
 645  
 646          $data4 = $data1;
 647          unset($data4['userid']);
 648          $event4 = \core\event\base::restore($data4, array());
 649          $this->assertInstanceOf(\core_tests\event\unittest_executed::class, $event4);
 650          $this->assertDebuggingCalled();
 651  
 652          $data5 = $data1;
 653          $data5['xx'] = 'xx';
 654          $event5 = \core\event\base::restore($data5, array());
 655          $this->assertInstanceOf(\core_tests\event\unittest_executed::class, $event5);
 656          $this->assertDebuggingCalled();
 657  
 658      }
 659  
 660      public function test_trigger_problems() {
 661          $this->resetAfterTest(true);
 662  
 663          $event = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>5, 'xx'=>10)));
 664          $event->trigger();
 665          try {
 666              $event->trigger();
 667              $this->fail('Exception expected on double trigger');
 668          } catch (\moodle_exception $e) {
 669              $this->assertInstanceOf(\coding_exception::class, $e);
 670          }
 671  
 672          $data = $event->get_data();
 673          $restored = \core_tests\event\unittest_executed::restore($data, array());
 674          $this->assertTrue($restored->is_triggered());
 675          $this->assertTrue($restored->is_restored());
 676  
 677          try {
 678              $restored->trigger();
 679              $this->fail('Exception expected on triggering of restored event');
 680          } catch (\moodle_exception $e) {
 681              $this->assertInstanceOf(\coding_exception::class, $e);
 682          }
 683  
 684          $event = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>5, 'xx'=>10)));
 685          try {
 686              \core\event\manager::dispatch($event);
 687              $this->fail('Exception expected on manual event dispatching');
 688          } catch (\moodle_exception $e) {
 689              $this->assertInstanceOf(\coding_exception::class, $e);
 690          }
 691      }
 692  
 693      public function test_bad_events() {
 694          $this->resetAfterTest(true);
 695  
 696          try {
 697              $event = \core_tests\event\unittest_executed::create(array('other'=>array('sample'=>5, 'xx'=>10)));
 698              $this->fail('Exception expected when context and contextid missing');
 699          } catch (\moodle_exception $e) {
 700              $this->assertInstanceOf(\coding_exception::class, $e);
 701          }
 702  
 703          $event = \core_tests\event\bad_event1::create(array('context'=>\context_system::instance()));
 704          try {
 705              $event->trigger();
 706              $this->fail('Exception expected when $data not valid');
 707          } catch (\moodle_exception $e) {
 708              $this->assertInstanceOf(\coding_exception::class, $e);
 709          }
 710  
 711          $event = \core_tests\event\bad_event2::create(array('context'=>\context_system::instance()));
 712          try {
 713              $event->trigger();
 714              $this->fail('Exception expected when $data not valid');
 715          } catch (\moodle_exception $e) {
 716              $this->assertInstanceOf(\coding_exception::class, $e);
 717          }
 718  
 719          $event = \core_tests\event\bad_event2b::create(array('context'=>\context_system::instance()));
 720          @$event->trigger();
 721          $this->assertDebuggingCalled();
 722  
 723          $event = \core_tests\event\bad_event3::create(array('context'=>\context_system::instance()));
 724          @$event->trigger();
 725          $this->assertDebuggingCalled();
 726  
 727          $event = \core_tests\event\bad_event4::create(array('context'=>\context_system::instance()));
 728          @$event->trigger();
 729          $this->assertDebuggingCalled();
 730  
 731          $event = \core_tests\event\bad_event5::create(array('context'=>\context_system::instance()));
 732          @$event->trigger();
 733          $this->assertDebuggingCalled();
 734  
 735          $event = \core_tests\event\bad_event6::create(array('objectid'=>1, 'context'=>\context_system::instance()));
 736          $event->trigger();
 737          $this->assertDebuggingCalled('Unknown table specified in objecttable field');
 738  
 739          $event = \core_tests\event\bad_event7::create(array('objectid'=>1, 'context'=>\context_system::instance()));
 740          try {
 741              $event->trigger();
 742              $this->fail('Exception expected when $data contains objectid but objecttable not specified');
 743          } catch (\moodle_exception $e) {
 744              $this->assertInstanceOf(\coding_exception::class, $e);
 745          }
 746  
 747          $event = \core_tests\event\bad_event8::create(array('context'=>\context_system::instance()));
 748          $event->trigger();
 749          $this->assertDebuggingCalled('Event property objectid must be set when objecttable is defined');
 750      }
 751  
 752      public function test_problematic_events() {
 753          $this->resetAfterTest(true);
 754  
 755          $event1 = \core_tests\event\problematic_event1::create(array('context'=>\context_system::instance()));
 756          $this->assertDebuggingNotCalled();
 757          $this->assertNull($event1->xxx);
 758          $this->assertDebuggingCalled();
 759  
 760          $event2 = \core_tests\event\problematic_event1::create(array('xxx'=>0, 'context'=>\context_system::instance()));
 761          $this->assertDebuggingCalled();
 762  
 763          set_debugging(DEBUG_NONE);
 764          $event3 = \core_tests\event\problematic_event1::create(array('xxx'=>0, 'context'=>\context_system::instance()));
 765          $this->assertDebuggingNotCalled();
 766          set_debugging(DEBUG_DEVELOPER);
 767  
 768          $event4 = \core_tests\event\problematic_event1::create(array('context'=>\context_system::instance(), 'other'=>array('a'=>1)));
 769          $event4->trigger();
 770          $this->assertDebuggingNotCalled();
 771  
 772          $event5 = \core_tests\event\problematic_event1::create(array('context'=>\context_system::instance(), 'other'=>(object)array('a'=>1)));
 773          $this->assertDebuggingNotCalled();
 774          $event5->trigger();
 775          $this->assertDebuggingCalled();
 776  
 777          $url = new \moodle_url('/admin/');
 778          $event6 = \core_tests\event\problematic_event1::create(array('context'=>\context_system::instance(), 'other'=>array('a'=>$url)));
 779          $this->assertDebuggingNotCalled();
 780          $event6->trigger();
 781          $this->assertDebuggingCalled();
 782  
 783          // Check that whole float numbers do not trigger debugging messages.
 784          $event7 = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(),
 785              'other' => array('wholenumber' => 90.0000, 'numberwithdecimals' => 54.7656, 'sample' => 1)));
 786          $event7->trigger();
 787          $this->assertDebuggingNotCalled();
 788  
 789          $event = \core_tests\event\problematic_event2::create(array());
 790          $this->assertDebuggingNotCalled();
 791          $event = \core_tests\event\problematic_event2::create(array('context'=>\context_system::instance()));
 792          $this->assertDebuggingCalled();
 793  
 794          $event = \core_tests\event\problematic_event3::create(array('other'=>1));
 795          $this->assertDebuggingNotCalled();
 796          $event = \core_tests\event\problematic_event3::create(array());
 797          $this->assertDebuggingCalled();
 798      }
 799  
 800      public function test_record_snapshots() {
 801          global $DB;
 802  
 803          $this->resetAfterTest(true);
 804  
 805          $event = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
 806          $course1 = $DB->get_record('course', array('id'=>1));
 807          $this->assertNotEmpty($course1);
 808  
 809          $event->add_record_snapshot('course', $course1);
 810  
 811          $result = $event->get_record_snapshot('course', $course1->id);
 812          // Convert to arrays because record snapshot returns a clone of the object.
 813          $this->assertSame((array)$course1, (array)$result);
 814  
 815          $user = $event->get_record_snapshot('user', 1);
 816          $this->assertEquals(1, $user->id);
 817          $this->assertSame('guest', $user->username);
 818  
 819          $event->add_record_snapshot('course', $course1);
 820          $event->trigger();
 821          try {
 822              $event->add_record_snapshot('course', $course1);
 823              $this->fail('Updating of snapshots after trigger is not ok');;
 824          } catch (\moodle_exception $e) {
 825              $this->assertInstanceOf(\coding_exception::class, $e);
 826          }
 827  
 828          $event2 = \core_tests\event\unittest_executed::restore($event->get_data(), array());
 829          try {
 830              $event2->get_record_snapshot('course', $course1->id);
 831              $this->fail('Reading of snapshots from restored events is not ok');;
 832          } catch (\moodle_exception $e) {
 833              $this->assertInstanceOf(\coding_exception::class, $e);
 834          }
 835      }
 836  
 837      public function test_get_name() {
 838          $event = \core_tests\event\noname_event::create(array('other' => array('sample' => 1, 'xx' => 10)));
 839          $this->assertEquals("core_tests: noname event", $event->get_name());
 840      }
 841  
 842      public function test_iteration() {
 843          $event = \core_tests\event\unittest_executed::create(array('context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
 844  
 845          $data = array();
 846          foreach ($event as $k => $v) {
 847              $data[$k] = $v;
 848          }
 849  
 850          $this->assertSame($event->get_data(), $data);
 851      }
 852  
 853      public function test_context_not_used() {
 854          // TODO: MDL-69688 - This test is far away from my understanding. It throws a
 855          // "Trying to get property 'instanceid' of non-object" notice, so
 856          // it's not clear for me what the test is doing. This was detected
 857          // when preparing tests for PHPUnit 8 (MDL-67673) and, at the end
 858          // all that was done is to move the annotation (deprecated) to
 859          // explicit expectation. Still try commenting it out and you'll see
 860          // the notice.
 861          if (PHP_VERSION_ID >= 80000) {
 862              $this->expectWarning();
 863          } else {
 864              $this->expectNotice();
 865          }
 866          $event = \core_tests\event\context_used_in_event::create(array('other' => array('sample' => 1, 'xx' => 10)));
 867          $this->assertEventContextNotUsed($event);
 868  
 869          $eventcontext = phpunit_event_mock::testable_get_event_context($event);
 870          phpunit_event_mock::testable_set_event_context($event, null);
 871          $this->assertEventContextNotUsed($event);
 872      }
 873  
 874      /**
 875       * Test that all observer information is returned correctly.
 876       */
 877      public function test_get_all_observers() {
 878          // Retrieve all observers.
 879          $observers = \core\event\manager::get_all_observers();
 880  
 881          // Expected information from the workshop allocation scheduled observer.
 882          $expected = new \stdClass();
 883          $expected->callable = '\workshopallocation_scheduled\observer::workshop_viewed';
 884          $expected->priority = 0;
 885          $expected->internal = true;
 886          $expected->includefile = null;
 887          $expected->plugintype = 'workshopallocation';
 888          $expected->plugin = 'scheduled';
 889  
 890          // May be more than one observer for the mod_workshop event.
 891          $found = false;
 892          foreach ($observers['\mod_workshop\event\course_module_viewed'] as $observer) {
 893              if ($expected == $observer) {
 894                  $found = true;
 895                  break;
 896              }
 897          }
 898          $this->assertTrue($found);
 899      }
 900  
 901      /**
 902       * Test formatting of the get_explanation method.
 903       * This formats the information from an events class docblock.
 904       */
 905      public function test_get_explanation() {
 906          $explanation = \core_tests\event\full_docblock::get_explanation();
 907  
 908          $expected = "This is an explanation of the event.
 909       - I'm making a point here.
 910       - I have a second {@link something}  point here.
 911       - whitespace is intentional to test it's removal.
 912  I have something else *Yeah* that.";
 913  
 914          $this->assertEquals($explanation, $expected);
 915  
 916          $explanation = \core_tests\event\docblock_test2::get_explanation();
 917  
 918          $expected = "We have only the description in the docblock
 919  and nothing else.";
 920  
 921          $this->assertEquals($explanation, $expected);
 922  
 923          $explanation = \core_tests\event\docblock_test3::get_explanation();
 924          $expected = "Calendar event created event.";
 925          $this->assertEquals($explanation, $expected);
 926  
 927      }
 928  
 929      /**
 930       * Test that general information about an event is returned
 931       * by the get_static_info() method.
 932       */
 933      public function test_get_static_info() {
 934          $staticinfo = \core_tests\event\static_info_viewing::get_static_info();
 935  
 936          $expected = array(
 937              'eventname'   => '\\core_tests\\event\\static_info_viewing',
 938              'component'   => 'core_tests',
 939              'target'      => 'static_info',
 940              'action'      => 'viewing',
 941              'crud'        => 'r',
 942              'edulevel'    => 0,
 943              'objecttable' => 'mod_unittest'
 944          );
 945          $this->assertEquals($staticinfo, $expected);
 946      }
 947  
 948      /**
 949       * This tests the internal method of \core\event\manager::get_observing_classes.
 950       *
 951       * What we are testing is if we can subscribe to a parent event class, instead of only
 952       * the base event class or the final, implemented event class.  This enables us to subscribe
 953       * to things like all course module view events, all comment created events, etc.
 954       */
 955      public function test_observe_parent_event() {
 956          $this->resetAfterTest();
 957  
 958          // Ensure this has been reset prior to using it.
 959          \core_tests\event\unittest_observer::reset();
 960  
 961          $course  = $this->getDataGenerator()->create_course();
 962          $feed    = $this->getDataGenerator()->create_module('feedback', ['course' => $course->id]);
 963          $context = \context_module::instance($feed->cmid);
 964          $data    = [
 965              'context'  => $context,
 966              'courseid' => $course->id,
 967              'objectid' => $feed->id
 968          ];
 969  
 970          // This assertion ensures that basic observe use case did not break.
 971          \core\event\manager::phpunit_replace_observers([[
 972              'eventname' => '\core_tests\event\course_module_viewed',
 973              'callback'  => ['\core_tests\event\unittest_observer', 'observe_all_alt'],
 974          ]]);
 975  
 976          $pageevent = \core_tests\event\course_module_viewed::create($data);
 977          $pageevent->trigger();
 978  
 979          $this->assertSame(['observe_all_alt'], \core_tests\event\unittest_observer::$info, 'Error observing triggered event');
 980  
 981          \core_tests\event\unittest_observer::reset();
 982  
 983          // This assertion tests that we can observe an abstract (parent) class instead of the implemented class.
 984          \core\event\manager::phpunit_replace_observers([[
 985              'eventname' => '\core\event\course_module_viewed',
 986              'callback'  => ['\core_tests\event\unittest_observer', 'observe_all_alt'],
 987          ]]);
 988  
 989          $pageevent = \core_tests\event\course_module_viewed::create($data);
 990          $pageevent->trigger();
 991  
 992          $this->assertSame(['observe_all_alt'], \core_tests\event\unittest_observer::$info, 'Error observing parent class event');
 993  
 994          \core_tests\event\unittest_observer::reset();
 995      }
 996  }