MDL-39846 rename 'extra' event property to 'other'
[moodle.git] / lib / tests / event_test.php
blobf651f4ef5d266500eb5d71c90da9c6155892b57c
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.
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/>.
17 /**
18 * Tests for event manager, base event and observers.
20 * @package core
21 * @category phpunit
22 * @copyright 2013 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') || die();
28 require_once(__DIR__.'/fixtures/event_fixtures.php');
30 class core_event_testcase extends advanced_testcase {
32 public function test_event_properties() {
33 global $USER;
35 $system = \context_system::instance();
36 $event = \core_tests\event\unittest_executed::create(array('courseid'=>1, 'context'=>$system, 'objectid'=>5, 'other'=>array('sample'=>null, 'xx'=>10)));
38 $this->assertSame('\core_tests\event\unittest_executed', $event->eventname);
39 $this->assertSame('core_tests', $event->component);
40 $this->assertSame('executed', $event->action);
41 $this->assertSame('unittest', $event->object);
42 $this->assertSame(5, $event->objectid);
43 $this->assertSame('u', $event->crud);
44 $this->assertSame(10, $event->level);
46 $this->assertSame($system, $event->get_context());
47 $this->assertSame($system->id, $event->contextid);
48 $this->assertSame($system->contextlevel, $event->contextlevel);
49 $this->assertSame($system->instanceid, $event->contextinstanceid);
51 $this->assertSame($USER->id, $event->userid);
52 $this->assertSame(1, $event->courseid);
54 $this->assertNull($event->relateduserid);
55 $this->assertFalse(isset($event->relateduserid));
57 $this->assertSame(array('sample'=>null, 'xx'=>10), $event->other);
58 $this->assertTrue(isset($event->other['xx']));
59 $this->assertFalse(isset($event->other['sample']));
61 $this->assertLessThanOrEqual(time(), $event->timecreated);
63 try {
64 $event->courseid = 2;
65 $this->fail('Exception expected on event modification');
66 } catch (\moodle_exception $e) {
67 $this->assertInstanceOf('coding_exception', $e);
70 try {
71 $event->xxxx = 1;
72 $this->fail('Exception expected on event modification');
73 } catch (\moodle_exception $e) {
74 $this->assertInstanceOf('coding_exception', $e);
78 public function test_observers_parsing() {
80 $observers = array(
81 array(
82 'eventname' => '\core_tests\event\unittest_executed',
83 'callback' => '\core_tests\event\unittest_observer::observe_one',
84 'includefile' => 'lib/tests/fixtures/event_fixtures.php',
86 array(
87 'eventname' => '*',
88 'callback' => array('\core_tests\event\unittest_observer', 'observe_all'),
89 'includefile' => null,
90 'internal' => 1,
91 'priority' => 9999,
93 array(
94 'eventname' => '\core\event\unknown_executed',
95 'callback' => '\core_tests\event\unittest_observer::broken_observer',
96 'priority' => 100,
98 array(
99 'eventname' => '\core_tests\event\unittest_executed',
100 'callback' => '\core_tests\event\unittest_observer::external_observer',
101 'priority' => 200,
102 'internal' => 0,
106 $result = \core\event\manager::phpunit_replace_observers($observers);
108 $this->assertCount(3, $result);
109 end($result);
110 $this->assertSame('*', key($result));
112 $expected = array();
113 $observer = new stdClass();
114 $observer->callable = array('\core_tests\event\unittest_observer', 'observe_all');
115 $observer->priority = 9999;
116 $observer->internal = true;
117 $observer->includefile = null;
118 $expected[0] = $observer;
119 $observer = new stdClass();
120 $observer->callable = '\core_tests\event\unittest_observer::external_observer';
121 $observer->priority = 200;
122 $observer->internal = false;
123 $observer->includefile = null;
124 $expected[1] = $observer;
125 $observer = new stdClass();
126 $observer->callable = '\core_tests\event\unittest_observer::observe_one';
127 $observer->priority = 0;
128 $observer->internal = true;
129 $observer->includefile = 'lib/tests/fixtures/event_fixtures.php';
130 $expected[2] = $observer;
132 $this->assertEquals($expected, $result['\core_tests\event\unittest_executed']);
134 $expected = array();
135 $observer = new stdClass();
136 $observer->callable = array('\core_tests\event\unittest_observer', 'observe_all');
137 $observer->priority = 9999;
138 $observer->internal = true;
139 $observer->includefile = null;
140 $expected[0] = $observer;
141 $observer = new stdClass();
142 $observer->callable = '\core_tests\event\unittest_observer::broken_observer';
143 $observer->priority = 100;
144 $observer->internal = true;
145 $observer->includefile = null;
146 $expected[1] = $observer;
148 $this->assertEquals($expected, $result['\core\event\unknown_executed']);
150 $expected = array();
151 $observer = new stdClass();
152 $observer->callable = array('\core_tests\event\unittest_observer', 'observe_all');
153 $observer->priority = 9999;
154 $observer->internal = true;
155 $observer->includefile = null;
156 $expected[0] = $observer;
158 $this->assertEquals($expected, $result['*']);
161 // Now test broken stuff...
163 $observers = array(
164 array(
165 'eventname' => 'core_tests\event\unittest_executed', // Fix leading backslash.
166 'callback' => '\core_tests\event\unittest_observer::observe_one',
167 'includefile' => 'lib/tests/fixtures/event_fixtures.php',
168 'internal' => 1, // Cast to bool.
171 $result = \core\event\manager::phpunit_replace_observers($observers);
172 $this->assertCount(1, $result);
173 $expected = array();
174 $observer = new stdClass();
175 $observer->callable = '\core_tests\event\unittest_observer::observe_one';
176 $observer->priority = 0;
177 $observer->internal = true;
178 $observer->includefile = 'lib/tests/fixtures/event_fixtures.php';
179 $expected[0] = $observer;
180 $this->assertEquals($expected, $result['\core_tests\event\unittest_executed']);
182 $observers = array(
183 array(
184 // Missing eventclass.
185 'callback' => '\core_tests\event\unittest_observer::observe_one',
186 'includefile' => 'lib/tests/fixtures/event_fixtures.php',
189 $result = \core\event\manager::phpunit_replace_observers($observers);
190 $this->assertCount(0, $result);
191 $this->assertDebuggingCalled();
193 $observers = array(
194 array(
195 'eventname' => '', // Empty eventclass.
196 'callback' => '\core_tests\event\unittest_observer::observe_one',
197 'includefile' => 'lib/tests/fixtures/event_fixtures.php',
200 $result = \core\event\manager::phpunit_replace_observers($observers);
201 $this->assertCount(0, $result);
202 $this->assertDebuggingCalled();
204 $observers = array(
205 array(
206 'eventname' => '\core_tests\event\unittest_executed',
207 // Missing callable.
208 'includefile' => 'lib/tests/fixtures/event_fixtures.php',
211 $result = \core\event\manager::phpunit_replace_observers($observers);
212 $this->assertCount(0, $result);
213 $this->assertDebuggingCalled();
215 $observers = array(
216 array(
217 'eventname' => '\core_tests\event\unittest_executed',
218 'callback' => '', // empty callable
219 'includefile' => 'lib/tests/fixtures/event_fixtures.php',
222 $result = \core\event\manager::phpunit_replace_observers($observers);
223 $this->assertCount(0, $result);
224 $this->assertDebuggingCalled();
226 $observers = array(
227 array(
228 'eventname' => '\core_tests\event\unittest_executed',
229 'callback' => '\core_tests\event\unittest_observer::observe_one',
230 'includefile' => 'lib/tests/fixtures/event_fixtures.php_xxx', // Missing file.
233 $result = \core\event\manager::phpunit_replace_observers($observers);
234 $this->assertCount(0, $result);
235 $this->assertDebuggingCalled();
238 public function test_normal_dispatching() {
239 $observers = array(
240 array(
241 'eventname' => '\core_tests\event\unittest_executed',
242 'callback' => '\core_tests\event\unittest_observer::observe_one',
244 array(
245 'eventname' => '*',
246 'callback' => '\core_tests\event\unittest_observer::observe_all',
247 'includefile' => null,
248 'internal' => 1,
249 'priority' => 9999,
253 \core\event\manager::phpunit_replace_observers($observers);
254 \core_tests\event\unittest_observer::reset();
256 $event1 = \core_tests\event\unittest_executed::create(array('courseid'=>1, 'context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
257 $event1->nest = 1;
258 $this->assertFalse($event1->is_triggered());
259 $this->assertFalse($event1->is_dispatched());
260 $this->assertFalse($event1->is_restored());
261 $event1->trigger();
262 $this->assertTrue($event1->is_triggered());
263 $this->assertTrue($event1->is_dispatched());
264 $this->assertFalse($event1->is_restored());
266 $event1 = \core_tests\event\unittest_executed::create(array('courseid'=>2, 'context'=>\context_system::instance(), 'other'=>array('sample'=>2, 'xx'=>10)));
267 $event1->trigger();
269 $this->assertSame(
270 array('observe_all-nesting-1', 'observe_one-1', 'observe_all-3', 'observe_one-3', 'observe_all-2', 'observe_one-2'),
271 \core_tests\event\unittest_observer::$info);
274 public function test_ignore_exceptions() {
275 $observers = array(
277 array(
278 'eventname' => '\core_tests\event\unittest_executed',
279 'callback' => '\core_tests\event\unittest_observer::observe_one',
282 array(
283 'eventname' => '\core_tests\event\unittest_executed',
284 'callback' => '\core_tests\event\unittest_observer::broken_observer',
285 'priority' => 100,
289 \core\event\manager::phpunit_replace_observers($observers);
290 \core_tests\event\unittest_observer::reset();
292 $event1 = \core_tests\event\unittest_executed::create(array('courseid'=>1, 'context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
293 $event1->trigger();
294 $this->assertDebuggingCalled();
296 $event1 = \core_tests\event\unittest_executed::create(array('courseid'=>2, 'context'=>\context_system::instance(), 'other'=>array('sample'=>2, 'xx'=>10)));
297 $event1->trigger();
298 $this->assertDebuggingCalled();
300 $this->assertSame(
301 array('broken_observer-1', 'observe_one-1', 'broken_observer-2', 'observe_one-2'),
302 \core_tests\event\unittest_observer::$info);
305 public function test_external_buffer() {
306 global $DB;
308 $this->preventResetByRollback();
310 $observers = array(
312 array(
313 'eventname' => '\core_tests\event\unittest_executed',
314 'callback' => '\core_tests\event\unittest_observer::observe_one',
317 array(
318 'eventname' => '\core_tests\event\unittest_executed',
319 'callback' => '\core_tests\event\unittest_observer::external_observer',
320 'priority' => 200,
321 'internal' => 0,
325 \core\event\manager::phpunit_replace_observers($observers);
326 \core_tests\event\unittest_observer::reset();
328 $event1 = \core_tests\event\unittest_executed::create(array('courseid'=>1, 'context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
329 $event1->trigger();
330 $event2 = \core_tests\event\unittest_executed::create(array('courseid'=>2, 'context'=>\context_system::instance(), 'other'=>array('sample'=>2, 'xx'=>10)));
331 $event2->trigger();
333 $this->assertSame(
334 array('external_observer-1', 'observe_one-1', 'external_observer-2', 'observe_one-2'),
335 \core_tests\event\unittest_observer::$info);
337 \core\event\manager::phpunit_replace_observers($observers);
338 \core_tests\event\unittest_observer::reset();
340 $this->assertSame(array(), \core_tests\event\unittest_observer::$info);
342 $trans = $DB->start_delegated_transaction();
344 $event1 = \core_tests\event\unittest_executed::create(array('courseid'=>1, 'context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
345 $event1->trigger();
346 $event2 = \core_tests\event\unittest_executed::create(array('courseid'=>2, 'context'=>\context_system::instance(), 'other'=>array('sample'=>2, 'xx'=>10)));
347 $event2->trigger();
349 $this->assertSame(
350 array('observe_one-1', 'observe_one-2'),
351 \core_tests\event\unittest_observer::$info);
353 $trans->allow_commit();
355 $this->assertSame(
356 array('observe_one-1', 'observe_one-2', 'external_observer-1', 'external_observer-2'),
357 \core_tests\event\unittest_observer::$info);
359 \core\event\manager::phpunit_replace_observers($observers);
360 \core_tests\event\unittest_observer::reset();
362 $event1 = \core_tests\event\unittest_executed::create(array('courseid'=>1, 'context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
363 $event1->trigger();
364 $trans = $DB->start_delegated_transaction();
365 $event2 = \core_tests\event\unittest_executed::create(array('courseid'=>2, 'context'=>\context_system::instance(), 'other'=>array('sample'=>2, 'xx'=>10)));
366 $event2->trigger();
367 try {
368 $trans->rollback(new \moodle_exception('xxx'));
369 $this->fail('Expecting exception');
370 } catch (\moodle_exception $e) {
373 $this->assertSame(
374 array('external_observer-1', 'observe_one-1', 'observe_one-2'),
375 \core_tests\event\unittest_observer::$info);
378 public function test_legacy() {
379 global $DB;
381 $this->resetAfterTest(true);
383 $observers = array(
384 array(
385 'eventname' => '\core_tests\event\unittest_executed',
386 'callback' => '\core_tests\event\unittest_observer::observe_one',
388 array(
389 'eventname' => '*',
390 'callback' => '\core_tests\event\unittest_observer::observe_all',
391 'includefile' => null,
392 'internal' => 1,
393 'priority' => 9999,
397 $DB->delete_records('log', array());
398 events_update_definition('unittest');
399 $DB->delete_records_select('events_handlers', "component <> 'unittest'");
400 events_get_handlers('reset');
401 $this->assertEquals(3, $DB->count_records('events_handlers'));
402 set_config('loglifetime', 60*60*24*5);
404 \core\event\manager::phpunit_replace_observers($observers);
405 \core_tests\event\unittest_observer::reset();
407 $event1 = \core_tests\event\unittest_executed::create(array('courseid'=>1, 'context'=>\context_system::instance(), 'other'=>array('sample'=>5, 'xx'=>10)));
408 $event1->trigger();
410 $event2 = \core_tests\event\unittest_executed::create(array('courseid'=>2, 'context'=>\context_system::instance(), 'other'=>array('sample'=>6, 'xx'=>11)));
411 $event2->nest = true;
412 $event2->trigger();
415 $this->assertSame(
416 array('observe_all-1', 'observe_one-1', 'legacy_handler-1', 'observe_all-nesting-2', 'legacy_handler-3', 'observe_one-2', 'observe_all-3', 'observe_one-3', 'legacy_handler-2'),
417 \core_tests\event\unittest_observer::$info);
419 $this->assertSame($event1, \core_tests\event\unittest_observer::$event[0]);
420 $this->assertSame($event1, \core_tests\event\unittest_observer::$event[1]);
421 $this->assertSame(array(1, 5), \core_tests\event\unittest_observer::$event[2]);
424 $logs = $DB->get_records('log', array(), 'id ASC');
425 $this->assertCount(3, $logs);
427 $log = array_shift($logs);
428 $this->assertEquals(1, $log->course);
429 $this->assertSame('core_unittest', $log->module);
430 $this->assertSame('view', $log->action);
432 $log = array_shift($logs);
433 $this->assertEquals(2, $log->course);
434 $this->assertSame('core_unittest', $log->module);
435 $this->assertSame('view', $log->action);
437 $log = array_shift($logs);
438 $this->assertEquals(3, $log->course);
439 $this->assertSame('core_unittest', $log->module);
440 $this->assertSame('view', $log->action);
443 public function test_restore_event() {
444 $event1 = \core_tests\event\unittest_executed::create(array('courseid'=>1, 'context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
445 $data1 = $event1->get_data();
447 $event2 = \core\event\base::restore($data1, array('origin'=>'clid'));
448 $data2 = $event2->get_data();
450 $this->assertTrue($event2->is_triggered());
451 $this->assertTrue($event2->is_restored());
452 $this->assertEquals($data1, $data2);
453 $this->assertInstanceOf('core_tests\event\unittest_executed', $event2);
455 $this->assertEquals($event1->get_context(), $event2->get_context());
457 // Now test problematic data.
458 $data3 = $data1;
459 $data3['eventname'] = '\\a\\b\\c';
460 $event3 = \core\event\base::restore($data3, array());
461 $this->assertFalse($event3, 'Class name must match');
463 $data4 = $data1;
464 unset($data4['userid']);
465 $event4 = \core\event\base::restore($data4, array());
466 $this->assertInstanceOf('core_tests\event\unittest_executed', $event4);
467 $this->assertDebuggingCalled();
469 $data5 = $data1;
470 $data5['xx'] = 'xx';
471 $event5 = \core\event\base::restore($data5, array());
472 $this->assertInstanceOf('core_tests\event\unittest_executed', $event5);
473 $this->assertDebuggingCalled();
477 public function test_trigger_problems() {
478 $event = \core_tests\event\unittest_executed::create(array('courseid'=>1, 'context'=>\context_system::instance(), 'other'=>array('sample'=>5, 'xx'=>10)));
479 $event->trigger();
480 try {
481 $event->trigger();
482 $this->fail('Exception expected on double trigger');
483 } catch (Exception $e) {
484 $this->assertInstanceOf('coding_exception', $e);
487 $data = $event->get_data();
488 $restored = \core_tests\event\unittest_executed::restore($data, array());
489 $this->assertTrue($restored->is_triggered());
490 $this->assertTrue($restored->is_restored());
492 try {
493 $restored->trigger();
494 $this->fail('Exception expected on triggering of restored event');
495 } catch (\moodle_exception $e) {
496 $this->assertInstanceOf('coding_exception', $e);
499 $event = \core_tests\event\unittest_executed::create(array('courseid'=>1, 'context'=>\context_system::instance(), 'other'=>array('sample'=>5, 'xx'=>10)));
500 try {
501 \core\event\manager::dispatch($event);
502 $this->fail('Exception expected on manual event dispatching');
503 } catch (\moodle_exception $e) {
504 $this->assertInstanceOf('coding_exception', $e);
508 public function test_bad_events() {
509 $event = \core_tests\event\bad_event1::create();
510 try {
511 $event->trigger();
512 $this->fail('Exception expected when $data not valid');
513 } catch (\moodle_exception $e) {
514 $this->assertInstanceOf('\coding_exception', $e);
517 $event = \core_tests\event\bad_event2::create();
518 try {
519 $event->trigger();
520 $this->fail('Exception expected when $data not valid');
521 } catch (\moodle_exception $e) {
522 $this->assertInstanceOf('\coding_exception', $e);
525 $event = \core_tests\event\bad_event3::create();
526 @$event->trigger();
527 $this->assertDebuggingCalled();
529 $event = \core_tests\event\bad_event4::create();
530 @$event->trigger();
531 $this->assertDebuggingCalled();
533 $event = \core_tests\event\bad_event5::create();
534 @$event->trigger();
535 $this->assertDebuggingCalled();
538 public function test_problematic_events() {
539 global $CFG;
540 $event1 = \core_tests\event\problematic_event1::create();
541 $this->assertDebuggingNotCalled();
542 $this->assertNull($event1->xxx);
543 $this->assertDebuggingCalled();
545 $event2 = \core_tests\event\problematic_event1::create(array('xxx'=>0));
546 $this->assertDebuggingCalled();
548 $CFG->debug = 0;
549 $event3 = \core_tests\event\problematic_event1::create(array('xxx'=>0));
550 $this->assertDebuggingNotCalled();
551 $CFG->debug = E_ALL | E_STRICT;
553 $event4 = \core_tests\event\problematic_event1::create(array('other'=>array('a'=>1)));
554 $event4->trigger();
555 $this->assertDebuggingNotCalled();
557 $event5 = \core_tests\event\problematic_event1::create(array('other'=>(object)array('a'=>1)));
558 $this->assertDebuggingNotCalled();
559 $event5->trigger();
560 $this->assertDebuggingCalled();
562 $url = new moodle_url('/admin/');
563 $event6 = \core_tests\event\problematic_event1::create(array('other'=>array('a'=>$url)));
564 $this->assertDebuggingNotCalled();
565 $event6->trigger();
566 $this->assertDebuggingCalled();
569 public function test_record_cache() {
570 global $DB;
572 $event = \core_tests\event\unittest_executed::create(array('courseid'=>1, 'context'=>\context_system::instance(), 'other'=>array('sample'=>1, 'xx'=>10)));
573 $course1 = $DB->get_record('course', array('id'=>1));
574 $this->assertNotEmpty($course1);
576 $event->add_cached_record('course', $course1);
578 $result = $event->get_cached_record('course', 1, $course1);
579 $this->assertSame($course1, $result);
581 $user = $event->get_cached_record('user', 1);
582 $this->assertEquals(1, $user->id);
583 $this->assertSame('guest', $user->username);