Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   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  /**
  18   * Unit tests for session manager class.
  19   *
  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
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  /**
  29   * Unit tests for session manager class.
  30   *
  31   * @package    core
  32   * @category   phpunit
  33   * @copyright  2013 Petr Skoda {@link http://skodak.org}
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class core_session_manager_testcase extends advanced_testcase {
  37      public function test_start() {
  38          $this->resetAfterTest();
  39          // Session must be started only once...
  40          \core\session\manager::start();
  41          $this->assertDebuggingCalled('Session was already started!', DEBUG_DEVELOPER);
  42      }
  43  
  44      public function test_init_empty_session() {
  45          global $SESSION, $USER;
  46          $this->resetAfterTest();
  47  
  48          $user = $this->getDataGenerator()->create_user();
  49  
  50          $SESSION->test = true;
  51          $this->assertTrue($GLOBALS['SESSION']->test);
  52          $this->assertTrue($_SESSION['SESSION']->test);
  53  
  54          \core\session\manager::set_user($user);
  55          $this->assertSame($user, $USER);
  56          $this->assertSame($user, $GLOBALS['USER']);
  57          $this->assertSame($user, $_SESSION['USER']);
  58  
  59          \core\session\manager::init_empty_session();
  60  
  61          $this->assertInstanceOf('stdClass', $SESSION);
  62          $this->assertEmpty((array)$SESSION);
  63          $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
  64          $this->assertSame($GLOBALS['SESSION'], $SESSION);
  65  
  66          $this->assertInstanceOf('stdClass', $USER);
  67          $this->assertEqualsCanonicalizing(array('id' => 0, 'mnethostid' => 1), (array)$USER);
  68          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
  69          $this->assertSame($GLOBALS['USER'], $USER);
  70  
  71          // Now test how references work.
  72  
  73          $GLOBALS['SESSION'] = new \stdClass();
  74          $GLOBALS['SESSION']->test = true;
  75          $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
  76          $this->assertSame($GLOBALS['SESSION'], $SESSION);
  77  
  78          $SESSION = new \stdClass();
  79          $SESSION->test2 = true;
  80          $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
  81          $this->assertSame($GLOBALS['SESSION'], $SESSION);
  82  
  83          $_SESSION['SESSION'] = new stdClass();
  84          $_SESSION['SESSION']->test3 = true;
  85          $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
  86          $this->assertSame($GLOBALS['SESSION'], $SESSION);
  87  
  88          $GLOBALS['USER'] = new \stdClass();
  89          $GLOBALS['USER']->test = true;
  90          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
  91          $this->assertSame($GLOBALS['USER'], $USER);
  92  
  93          $USER = new \stdClass();
  94          $USER->test2 = true;
  95          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
  96          $this->assertSame($GLOBALS['USER'], $USER);
  97  
  98          $_SESSION['USER'] = new stdClass();
  99          $_SESSION['USER']->test3 = true;
 100          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
 101          $this->assertSame($GLOBALS['USER'], $USER);
 102      }
 103  
 104      public function test_set_user() {
 105          global $USER;
 106          $this->resetAfterTest();
 107  
 108          $this->assertEquals(0, $USER->id);
 109  
 110          $user = $this->getDataGenerator()->create_user();
 111          $this->assertObjectHasAttribute('description', $user);
 112          $this->assertObjectHasAttribute('password', $user);
 113  
 114          \core\session\manager::set_user($user);
 115  
 116          $this->assertEquals($user->id, $USER->id);
 117          $this->assertObjectNotHasAttribute('description', $user);
 118          $this->assertObjectNotHasAttribute('password', $user);
 119          $this->assertObjectHasAttribute('sesskey', $user);
 120          $this->assertSame($user, $GLOBALS['USER']);
 121          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
 122          $this->assertSame($GLOBALS['USER'], $USER);
 123      }
 124  
 125      public function test_login_user() {
 126          global $USER;
 127          $this->resetAfterTest();
 128  
 129          $this->assertEquals(0, $USER->id);
 130  
 131          $user = $this->getDataGenerator()->create_user();
 132  
 133          @\core\session\manager::login_user($user); // Ignore header error messages.
 134          $this->assertEquals($user->id, $USER->id);
 135  
 136          $this->assertObjectNotHasAttribute('description', $user);
 137          $this->assertObjectNotHasAttribute('password', $user);
 138          $this->assertSame($user, $GLOBALS['USER']);
 139          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
 140          $this->assertSame($GLOBALS['USER'], $USER);
 141      }
 142  
 143      public function test_terminate_current() {
 144          global $USER, $SESSION;
 145          $this->resetAfterTest();
 146  
 147          $this->setAdminUser();
 148          \core\session\manager::terminate_current();
 149          $this->assertEquals(0, $USER->id);
 150  
 151          $this->assertInstanceOf('stdClass', $SESSION);
 152          $this->assertEmpty((array)$SESSION);
 153          $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
 154          $this->assertSame($GLOBALS['SESSION'], $SESSION);
 155  
 156          $this->assertInstanceOf('stdClass', $USER);
 157          $this->assertEqualsCanonicalizing(array('id' => 0, 'mnethostid' => 1), (array)$USER);
 158          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
 159          $this->assertSame($GLOBALS['USER'], $USER);
 160      }
 161  
 162      public function test_write_close() {
 163          global $USER;
 164          $this->resetAfterTest();
 165  
 166          // Just make sure no errors and $USER->id is kept
 167          $this->setAdminUser();
 168          $userid = $USER->id;
 169          \core\session\manager::write_close();
 170          $this->assertSame($userid, $USER->id);
 171  
 172          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
 173          $this->assertSame($GLOBALS['USER'], $USER);
 174      }
 175  
 176      public function test_session_exists() {
 177          global $CFG, $DB;
 178          $this->resetAfterTest();
 179  
 180          $this->assertFalse(\core\session\manager::session_exists('abc'));
 181  
 182          $user = $this->getDataGenerator()->create_user();
 183          $guest = guest_user();
 184  
 185          // The file handler is used by default, so let's fake the data somehow.
 186          $sid = md5('hokus');
 187          mkdir("$CFG->dataroot/sessions/", $CFG->directorypermissions, true);
 188          touch("$CFG->dataroot/sessions/sess_$sid");
 189  
 190          $this->assertFalse(\core\session\manager::session_exists($sid));
 191  
 192          $record = new stdClass();
 193          $record->userid = 0;
 194          $record->sid = $sid;
 195          $record->timecreated = time();
 196          $record->timemodified = $record->timecreated;
 197          $record->id = $DB->insert_record('sessions', $record);
 198  
 199          $this->assertTrue(\core\session\manager::session_exists($sid));
 200  
 201          $record->timecreated = time() - $CFG->sessiontimeout - 100;
 202          $record->timemodified = $record->timecreated + 10;
 203          $DB->update_record('sessions', $record);
 204  
 205          $this->assertTrue(\core\session\manager::session_exists($sid));
 206  
 207          $record->userid = $guest->id;
 208          $DB->update_record('sessions', $record);
 209  
 210          $this->assertTrue(\core\session\manager::session_exists($sid));
 211  
 212          $record->userid = $user->id;
 213          $DB->update_record('sessions', $record);
 214  
 215          $this->assertFalse(\core\session\manager::session_exists($sid));
 216  
 217          $CFG->sessiontimeout = $CFG->sessiontimeout + 3000;
 218  
 219          $this->assertTrue(\core\session\manager::session_exists($sid));
 220      }
 221  
 222      public function test_touch_session() {
 223          global $DB;
 224          $this->resetAfterTest();
 225  
 226          $sid = md5('hokus');
 227          $record = new \stdClass();
 228          $record->state        = 0;
 229          $record->sid          = $sid;
 230          $record->sessdata     = null;
 231          $record->userid       = 2;
 232          $record->timecreated  = time() - 60*60;
 233          $record->timemodified = time() - 30;
 234          $record->firstip      = $record->lastip = '10.0.0.1';
 235          $record->id = $DB->insert_record('sessions', $record);
 236  
 237          $now = time();
 238          \core\session\manager::touch_session($sid);
 239          $updated = $DB->get_field('sessions', 'timemodified', array('id'=>$record->id));
 240  
 241          $this->assertGreaterThanOrEqual($now, $updated);
 242          $this->assertLessThanOrEqual(time(), $updated);
 243      }
 244  
 245      public function test_kill_session() {
 246          global $DB, $USER;
 247          $this->resetAfterTest();
 248  
 249          $this->setAdminUser();
 250          $userid = $USER->id;
 251  
 252          $sid = md5('hokus');
 253          $record = new \stdClass();
 254          $record->state        = 0;
 255          $record->sid          = $sid;
 256          $record->sessdata     = null;
 257          $record->userid       = $userid;
 258          $record->timecreated  = time() - 60*60;
 259          $record->timemodified = time() - 30;
 260          $record->firstip      = $record->lastip = '10.0.0.1';
 261          $DB->insert_record('sessions', $record);
 262  
 263          $record->userid       = 0;
 264          $record->sid          = md5('pokus');
 265          $DB->insert_record('sessions', $record);
 266  
 267          $this->assertEquals(2, $DB->count_records('sessions'));
 268  
 269          \core\session\manager::kill_session($sid);
 270  
 271          $this->assertEquals(1, $DB->count_records('sessions'));
 272          $this->assertFalse($DB->record_exists('sessions', array('sid'=>$sid)));
 273  
 274          $this->assertSame($userid, $USER->id);
 275      }
 276  
 277      public function test_kill_user_sessions() {
 278          global $DB, $USER;
 279          $this->resetAfterTest();
 280  
 281          $this->setAdminUser();
 282          $userid = $USER->id;
 283  
 284          $sid = md5('hokus');
 285          $record = new \stdClass();
 286          $record->state        = 0;
 287          $record->sid          = $sid;
 288          $record->sessdata     = null;
 289          $record->userid       = $userid;
 290          $record->timecreated  = time() - 60*60;
 291          $record->timemodified = time() - 30;
 292          $record->firstip      = $record->lastip = '10.0.0.1';
 293          $DB->insert_record('sessions', $record);
 294  
 295          $record->sid          = md5('hokus2');
 296          $DB->insert_record('sessions', $record);
 297  
 298          $record->userid       = 0;
 299          $record->sid          = md5('pokus');
 300          $DB->insert_record('sessions', $record);
 301  
 302          $this->assertEquals(3, $DB->count_records('sessions'));
 303  
 304          \core\session\manager::kill_user_sessions($userid);
 305  
 306          $this->assertEquals(1, $DB->count_records('sessions'));
 307          $this->assertFalse($DB->record_exists('sessions', array('userid' => $userid)));
 308  
 309          $record->userid       = $userid;
 310          $record->sid          = md5('pokus3');
 311          $DB->insert_record('sessions', $record);
 312  
 313          $record->userid       = $userid;
 314          $record->sid          = md5('pokus4');
 315          $DB->insert_record('sessions', $record);
 316  
 317          $record->userid       = $userid;
 318          $record->sid          = md5('pokus5');
 319          $DB->insert_record('sessions', $record);
 320  
 321          $this->assertEquals(3, $DB->count_records('sessions', array('userid' => $userid)));
 322  
 323          \core\session\manager::kill_user_sessions($userid, md5('pokus5'));
 324  
 325          $this->assertEquals(1, $DB->count_records('sessions', array('userid' => $userid)));
 326          $this->assertEquals(1, $DB->count_records('sessions', array('userid' => $userid, 'sid' => md5('pokus5'))));
 327      }
 328  
 329      public function test_apply_concurrent_login_limit() {
 330          global $DB;
 331          $this->resetAfterTest();
 332  
 333          $user1 = $this->getDataGenerator()->create_user();
 334          $user2 = $this->getDataGenerator()->create_user();
 335          $guest = guest_user();
 336  
 337          $record = new \stdClass();
 338          $record->state        = 0;
 339          $record->sessdata     = null;
 340          $record->userid       = $user1->id;
 341          $record->timemodified = time();
 342          $record->firstip      = $record->lastip = '10.0.0.1';
 343  
 344          $record->sid = md5('hokus1');
 345          $record->timecreated = 20;
 346          $DB->insert_record('sessions', $record);
 347          $record->sid = md5('hokus2');
 348          $record->timecreated = 10;
 349          $DB->insert_record('sessions', $record);
 350          $record->sid = md5('hokus3');
 351          $record->timecreated = 30;
 352          $DB->insert_record('sessions', $record);
 353  
 354          $record->userid = $user2->id;
 355          $record->sid = md5('pokus1');
 356          $record->timecreated = 20;
 357          $DB->insert_record('sessions', $record);
 358          $record->sid = md5('pokus2');
 359          $record->timecreated = 10;
 360          $DB->insert_record('sessions', $record);
 361          $record->sid = md5('pokus3');
 362          $record->timecreated = 30;
 363          $DB->insert_record('sessions', $record);
 364  
 365          $record->timecreated = 10;
 366          $record->userid = $guest->id;
 367          $record->sid = md5('g1');
 368          $DB->insert_record('sessions', $record);
 369          $record->sid = md5('g2');
 370          $DB->insert_record('sessions', $record);
 371          $record->sid = md5('g3');
 372          $DB->insert_record('sessions', $record);
 373  
 374          $record->userid = 0;
 375          $record->sid = md5('nl1');
 376          $DB->insert_record('sessions', $record);
 377          $record->sid = md5('nl2');
 378          $DB->insert_record('sessions', $record);
 379          $record->sid = md5('nl3');
 380          $DB->insert_record('sessions', $record);
 381  
 382          set_config('limitconcurrentlogins', 0);
 383          $this->assertCount(12, $DB->get_records('sessions'));
 384  
 385          \core\session\manager::apply_concurrent_login_limit($user1->id);
 386          \core\session\manager::apply_concurrent_login_limit($user2->id);
 387          \core\session\manager::apply_concurrent_login_limit($guest->id);
 388          \core\session\manager::apply_concurrent_login_limit(0);
 389          $this->assertCount(12, $DB->get_records('sessions'));
 390  
 391          set_config('limitconcurrentlogins', -1);
 392  
 393          \core\session\manager::apply_concurrent_login_limit($user1->id);
 394          \core\session\manager::apply_concurrent_login_limit($user2->id);
 395          \core\session\manager::apply_concurrent_login_limit($guest->id);
 396          \core\session\manager::apply_concurrent_login_limit(0);
 397          $this->assertCount(12, $DB->get_records('sessions'));
 398  
 399          set_config('limitconcurrentlogins', 2);
 400  
 401          \core\session\manager::apply_concurrent_login_limit($user1->id);
 402          $this->assertCount(11, $DB->get_records('sessions'));
 403          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 20)));
 404          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 30)));
 405          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 10)));
 406  
 407          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 20)));
 408          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 30)));
 409          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 10)));
 410          set_config('limitconcurrentlogins', 2);
 411          \core\session\manager::apply_concurrent_login_limit($user2->id, md5('pokus2'));
 412          $this->assertCount(10, $DB->get_records('sessions'));
 413          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 20)));
 414          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 30)));
 415          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 10)));
 416  
 417          \core\session\manager::apply_concurrent_login_limit($guest->id);
 418          \core\session\manager::apply_concurrent_login_limit(0);
 419          $this->assertCount(10, $DB->get_records('sessions'));
 420  
 421          set_config('limitconcurrentlogins', 1);
 422  
 423          \core\session\manager::apply_concurrent_login_limit($user1->id, md5('grrr'));
 424          $this->assertCount(9, $DB->get_records('sessions'));
 425          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 20)));
 426          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 30)));
 427          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 10)));
 428  
 429          \core\session\manager::apply_concurrent_login_limit($user1->id);
 430          $this->assertCount(9, $DB->get_records('sessions'));
 431          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 20)));
 432          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 30)));
 433          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 10)));
 434  
 435          \core\session\manager::apply_concurrent_login_limit($user2->id, md5('pokus2'));
 436          $this->assertCount(8, $DB->get_records('sessions'));
 437          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 20)));
 438          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 30)));
 439          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 10)));
 440  
 441          \core\session\manager::apply_concurrent_login_limit($user2->id);
 442          $this->assertCount(8, $DB->get_records('sessions'));
 443          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 20)));
 444          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 30)));
 445          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 10)));
 446  
 447          \core\session\manager::apply_concurrent_login_limit($guest->id);
 448          \core\session\manager::apply_concurrent_login_limit(0);
 449          $this->assertCount(8, $DB->get_records('sessions'));
 450      }
 451  
 452      public function test_kill_all_sessions() {
 453          global $DB, $USER;
 454          $this->resetAfterTest();
 455  
 456          $this->setAdminUser();
 457          $userid = $USER->id;
 458  
 459          $sid = md5('hokus');
 460          $record = new \stdClass();
 461          $record->state        = 0;
 462          $record->sid          = $sid;
 463          $record->sessdata     = null;
 464          $record->userid       = $userid;
 465          $record->timecreated  = time() - 60*60;
 466          $record->timemodified = time() - 30;
 467          $record->firstip      = $record->lastip = '10.0.0.1';
 468          $DB->insert_record('sessions', $record);
 469  
 470          $record->sid          = md5('hokus2');
 471          $DB->insert_record('sessions', $record);
 472  
 473          $record->userid       = 0;
 474          $record->sid          = md5('pokus');
 475          $DB->insert_record('sessions', $record);
 476  
 477          $this->assertEquals(3, $DB->count_records('sessions'));
 478  
 479          \core\session\manager::kill_all_sessions();
 480  
 481          $this->assertEquals(0, $DB->count_records('sessions'));
 482          $this->assertSame(0, $USER->id);
 483      }
 484  
 485      public function test_gc() {
 486          global $CFG, $DB, $USER;
 487          $this->resetAfterTest();
 488  
 489          $this->setAdminUser();
 490          $adminid = $USER->id;
 491          $this->setGuestUser();
 492          $guestid = $USER->id;
 493          $this->setUser(0);
 494  
 495          $CFG->sessiontimeout = 60*10;
 496  
 497          $record = new \stdClass();
 498          $record->state        = 0;
 499          $record->sid          = md5('hokus1');
 500          $record->sessdata     = null;
 501          $record->userid       = $adminid;
 502          $record->timecreated  = time() - 60*60;
 503          $record->timemodified = time() - 30;
 504          $record->firstip      = $record->lastip = '10.0.0.1';
 505          $r1 = $DB->insert_record('sessions', $record);
 506  
 507          $record->sid          = md5('hokus2');
 508          $record->userid       = $adminid;
 509          $record->timecreated  = time() - 60*60;
 510          $record->timemodified = time() - 60*20;
 511          $r2 = $DB->insert_record('sessions', $record);
 512  
 513          $record->sid          = md5('hokus3');
 514          $record->userid       = $guestid;
 515          $record->timecreated  = time() - 60*60*60;
 516          $record->timemodified = time() - 60*20;
 517          $r3 = $DB->insert_record('sessions', $record);
 518  
 519          $record->sid          = md5('hokus4');
 520          $record->userid       = $guestid;
 521          $record->timecreated  = time() - 60*60*60;
 522          $record->timemodified = time() - 60*10*5 - 60;
 523          $r4 = $DB->insert_record('sessions', $record);
 524  
 525          $record->sid          = md5('hokus5');
 526          $record->userid       = 0;
 527          $record->timecreated  = time() - 60*5;
 528          $record->timemodified = time() - 60*5;
 529          $r5 = $DB->insert_record('sessions', $record);
 530  
 531          $record->sid          = md5('hokus6');
 532          $record->userid       = 0;
 533          $record->timecreated  = time() - 60*60;
 534          $record->timemodified = time() - 60*10 -10;
 535          $r6 = $DB->insert_record('sessions', $record);
 536  
 537          $record->sid          = md5('hokus7');
 538          $record->userid       = 0;
 539          $record->timecreated  = time() - 60*60;
 540          $record->timemodified = time() - 60*9;
 541          $r7 = $DB->insert_record('sessions', $record);
 542  
 543          \core\session\manager::gc();
 544  
 545          $this->assertTrue($DB->record_exists('sessions', array('id'=>$r1)));
 546          $this->assertFalse($DB->record_exists('sessions', array('id'=>$r2)));
 547          $this->assertTrue($DB->record_exists('sessions', array('id'=>$r3)));
 548          $this->assertFalse($DB->record_exists('sessions', array('id'=>$r4)));
 549          $this->assertFalse($DB->record_exists('sessions', array('id'=>$r5)));
 550          $this->assertFalse($DB->record_exists('sessions', array('id'=>$r6)));
 551          $this->assertTrue($DB->record_exists('sessions', array('id'=>$r7)));
 552      }
 553  
 554      /**
 555       * Test loginas.
 556       * @copyright  2103 Rajesh Taneja <rajesh@moodle.com>
 557       */
 558      public function test_loginas() {
 559          global $USER, $SESSION;
 560          $this->resetAfterTest();
 561  
 562          // Set current user as Admin user and save it for later use.
 563          $this->setAdminUser();
 564          $adminuser = $USER;
 565          $adminsession = $SESSION;
 566          $user = $this->getDataGenerator()->create_user();
 567          $_SESSION['extra'] = true;
 568  
 569          // Try admin loginas this user in system context.
 570          $this->assertObjectNotHasAttribute('realuser', $USER);
 571          \core\session\manager::loginas($user->id, context_system::instance());
 572  
 573          $this->assertSame($user->id, $USER->id);
 574          $this->assertEquals(context_system::instance(), $USER->loginascontext);
 575          $this->assertSame($adminuser->id, $USER->realuser);
 576          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
 577          $this->assertSame($GLOBALS['USER'], $USER);
 578          $this->assertNotSame($adminuser, $_SESSION['REALUSER']);
 579          $this->assertEquals($adminuser, $_SESSION['REALUSER']);
 580  
 581          $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
 582          $this->assertSame($GLOBALS['SESSION'], $SESSION);
 583          $this->assertNotSame($adminsession, $_SESSION['REALSESSION']);
 584          $this->assertEquals($adminsession, $_SESSION['REALSESSION']);
 585  
 586          $this->assertArrayNotHasKey('extra', $_SESSION);
 587  
 588          // Set user as current user and login as admin user in course context.
 589          \core\session\manager::init_empty_session();
 590          $this->setUser($user);
 591          $this->assertNotEquals($adminuser->id, $USER->id);
 592          $course = $this->getDataGenerator()->create_course();
 593          $coursecontext = context_course::instance($course->id);
 594  
 595          // Catch event triggered.
 596          $sink = $this->redirectEvents();
 597          \core\session\manager::loginas($adminuser->id, $coursecontext);
 598          $events = $sink->get_events();
 599          $sink->close();
 600          $event = array_pop($events);
 601  
 602          $this->assertSame($adminuser->id, $USER->id);
 603          $this->assertSame($coursecontext, $USER->loginascontext);
 604          $this->assertSame($user->id, $USER->realuser);
 605  
 606          // Test event captured has proper information.
 607          $this->assertInstanceOf('\core\event\user_loggedinas', $event);
 608          $this->assertSame($user->id, $event->objectid);
 609          $this->assertSame($adminuser->id, $event->relateduserid);
 610          $this->assertSame($course->id, $event->courseid);
 611          $this->assertEquals($coursecontext, $event->get_context());
 612          $oldfullname = fullname($user, true);
 613          $newfullname = fullname($adminuser, true);
 614          $expectedlogdata = array($course->id, "course", "loginas", "../user/view.php?id=$course->id&amp;user=$user->id", "$oldfullname -> $newfullname");
 615          $this->assertEventLegacyLogData($expectedlogdata, $event);
 616      }
 617  
 618      public function test_is_loggedinas() {
 619          $this->resetAfterTest();
 620  
 621          $user1 = $this->getDataGenerator()->create_user();
 622          $user2 = $this->getDataGenerator()->create_user();
 623  
 624          $this->assertFalse(\core\session\manager::is_loggedinas());
 625  
 626          $this->setUser($user1);
 627          \core\session\manager::loginas($user2->id, context_system::instance());
 628  
 629          $this->assertTrue(\core\session\manager::is_loggedinas());
 630      }
 631  
 632      public function test_get_realuser() {
 633          $this->resetAfterTest();
 634  
 635          $user1 = $this->getDataGenerator()->create_user();
 636          $user2 = $this->getDataGenerator()->create_user();
 637  
 638          $this->setUser($user1);
 639          $normal = \core\session\manager::get_realuser();
 640          $this->assertSame($GLOBALS['USER'], $normal);
 641  
 642          \core\session\manager::loginas($user2->id, context_system::instance());
 643  
 644          $real = \core\session\manager::get_realuser();
 645  
 646          unset($real->password);
 647          unset($real->description);
 648          unset($real->sesskey);
 649          unset($user1->password);
 650          unset($user1->description);
 651          unset($user1->sesskey);
 652  
 653          $this->assertEquals($real, $user1);
 654          $this->assertSame($_SESSION['REALUSER'], $real);
 655      }
 656  
 657      /**
 658       * Session lock info on pages.
 659       *
 660       * @return array
 661       */
 662      public function pages_sessionlocks() {
 663          return [
 664              [
 665                  'url'      => '/good.php',
 666                  'start'    => 1500000001.000,
 667                  'gained'   => 1500000002.000,
 668                  'released' => 1500000003.000,
 669                  'wait'     => 1.0,
 670                  'held'     => 1.0
 671              ],
 672              [
 673                  'url'      => '/bad.php?wait=5',
 674                  'start'    => 1500000003.000,
 675                  'gained'   => 1500000005.000,
 676                  'released' => 1500000007.000,
 677                  'held'     => 2.0,
 678                  'wait'     => 2.0
 679              ]
 680          ];
 681      }
 682  
 683      /**
 684       * Test to get recent session locks.
 685       */
 686      public function test_get_recent_session_locks() {
 687          global $CFG;
 688  
 689          $this->resetAfterTest();
 690          $CFG->debugsessionlock = 5;
 691          $pages = $this->pages_sessionlocks();
 692          // Recent session locks must be empty at first.
 693          $recentsessionlocks = \core\session\manager::get_recent_session_locks();
 694          $this->assertEmpty($recentsessionlocks);
 695  
 696          // Add page to the recentsessionlocks array.
 697          \core\session\manager::update_recent_session_locks($pages[0]);
 698          $recentsessionlocks = \core\session\manager::get_recent_session_locks();
 699          // Make sure we are getting the first page we added.
 700          $this->assertEquals($pages[0], $recentsessionlocks[0]);
 701          // There should be 1 page in the array.
 702          $this->assertCount(1, $recentsessionlocks);
 703  
 704          // Add second page to the recentsessionlocks array.
 705          \core\session\manager::update_recent_session_locks($pages[1]);
 706          $recentsessionlocks = \core\session\manager::get_recent_session_locks();
 707          // Make sure we are getting the second page we added.
 708          $this->assertEquals($pages[1], $recentsessionlocks[1]);
 709          // There should be 2 pages in the array.
 710          $this->assertCount(2, $recentsessionlocks);
 711      }
 712  
 713      /**
 714       * Test to update recent session locks.
 715       */
 716      public function test_update_recent_session_locks() {
 717          global $CFG;
 718  
 719          $this->resetAfterTest();
 720          $CFG->debugsessionlock = 5;
 721          $pages = $this->pages_sessionlocks();
 722  
 723          \core\session\manager::update_recent_session_locks($pages[0]);
 724          \core\session\manager::update_recent_session_locks($pages[1]);
 725          $recentsessionlocks = \core\session\manager::get_recent_session_locks();
 726          // There should be 2 pages in the array.
 727          $this->assertCount(2, $recentsessionlocks);
 728          // Make sure the last page is added at the end of the array.
 729          $this->assertEquals($pages[1], end($recentsessionlocks));
 730  
 731      }
 732  
 733      /**
 734       * Test to get session lock info.
 735       */
 736      public function test_get_session_lock_info() {
 737          global $PERF;
 738  
 739          $this->resetAfterTest();
 740  
 741          $pages = $this->pages_sessionlocks();
 742          $PERF->sessionlock = $pages[0];
 743          $sessionlock = \core\session\manager::get_session_lock_info();
 744          $this->assertEquals($pages[0], $sessionlock);
 745      }
 746  
 747      /**
 748       * Session lock info on some pages to serve as history.
 749       *
 750       * @return array
 751       */
 752      public function sessionlock_history() {
 753          return [
 754              [
 755                  'url'      => '/good.php',
 756                  'start'    => 1500000001.000,
 757                  'gained'   => 1500000001.100,
 758                  'released' => 1500000001.500,
 759                  'wait'     => 0.1
 760              ],
 761              [
 762                  // This bad request doesn't release the session for 10 seconds.
 763                  'url'      => '/bad.php',
 764                  'start'    => 1500000012.000,
 765                  'gained'   => 1500000012.200,
 766                  'released' => 1500000020.200,
 767                  'wait'     => 0.2
 768              ],
 769              [
 770                  // All subsequent requests are blocked and need to wait.
 771                  'url'      => '/good.php?id=1',
 772                  'start'    => 1500000012.900,
 773                  'gained'   => 1500000020.200,
 774                  'released' => 1500000022.000,
 775                  'wait'     => 7.29
 776              ],
 777              [
 778                  'url'      => '/good.php?id=2',
 779                  'start'    => 1500000014.000,
 780                  'gained'   => 1500000022.000,
 781                  'released' => 1500000025.000,
 782                  'wait'     => 8.0
 783              ],
 784              [
 785                  'url'      => '/good.php?id=3',
 786                  'start'    => 1500000015.000,
 787                  'gained'   => 1500000025.000,
 788                  'released' => 1500000026.000,
 789                  'wait'     => 10.0
 790              ],
 791              [
 792                  'url'      => '/good.php?id=4',
 793                  'start'    => 1500000016.000,
 794                  'gained'   => 1500000026.000,
 795                  'released' => 1500000027.000,
 796                  'wait'     => 10.0
 797              ]
 798          ];
 799      }
 800  
 801      /**
 802       * Data provider for test_get_locked_page_at function.
 803       *
 804       * @return array
 805       */
 806      public function sessionlocks_info_provider() : array {
 807          return [
 808              [
 809                  'url'      => null,
 810                  'time'    => 1500000001.000
 811              ],
 812              [
 813                  'url'      => '/bad.php',
 814                  'time'    => 1500000014.000
 815              ],
 816              [
 817                  'url'      => '/good.php?id=2',
 818                  'time'    => 1500000022.500
 819              ],
 820          ];
 821      }
 822  
 823      /**
 824       * Test to get locked page at a speficic timestamp.
 825       *
 826       * @dataProvider sessionlocks_info_provider
 827       * @param array $url Session lock page url.
 828       * @param array $time Session lock time.
 829       */
 830      public function test_get_locked_page_at($url, $time) {
 831          global $CFG, $SESSION;
 832  
 833          $this->resetAfterTest();
 834          $CFG->debugsessionlock = 5;
 835          $SESSION->recentsessionlocks = $this->sessionlock_history();
 836  
 837          $page = \core\session\manager::get_locked_page_at($time);
 838          $this->assertEquals($url, is_array($page) ? $page['url'] : null);
 839      }
 840  
 841      /**
 842       * Test cleanup recent session locks.
 843       */
 844      public function test_cleanup_recent_session_locks() {
 845          global $CFG, $SESSION;
 846  
 847          $this->resetAfterTest();
 848          $CFG->debugsessionlock = 5;
 849  
 850          $SESSION->recentsessionlocks = $this->sessionlock_history();
 851          $this->assertCount(6, $SESSION->recentsessionlocks);
 852          \core\session\manager::cleanup_recent_session_locks();
 853          // Make sure the session history has been cleaned up and only has the latest page.
 854          $this->assertCount(1, $SESSION->recentsessionlocks);
 855          $this->assertEquals('/good.php?id=4', $SESSION->recentsessionlocks[0]['url']);
 856      }
 857  
 858      public function test_array_session_diff_same_array() {
 859          $a = [];
 860          $a['c'] = new stdClass();
 861          $a['c']->o = new stdClass();
 862          $a['c']->o->o = new stdClass();
 863          $a['c']->o->o->l = 'cool';
 864  
 865          $class = new ReflectionClass('\core\session\manager');
 866          $method = $class->getMethod('array_session_diff');
 867          $method->setAccessible(true);
 868  
 869          $result = $method->invokeArgs(null, [$a, $a]);
 870  
 871          $this->assertEmpty($result);
 872      }
 873  
 874      public function test_array_session_diff_first_array_larger() {
 875          $a = [];
 876          $a['stdClass'] = new stdClass();
 877          $a['stdClass']->attribute = 'This is an attribute';
 878          $a['array'] = ['array', 'contents'];
 879  
 880          $b = [];
 881          $b['array'] = ['array', 'contents'];
 882  
 883          $class = new ReflectionClass('\core\session\manager');
 884          $method = $class->getMethod('array_session_diff');
 885          $method->setAccessible(true);
 886  
 887          $result = $method->invokeArgs(null, [$a, $b]);
 888  
 889          $expected = [];
 890          $expected['stdClass'] = new stdClass();
 891          $expected['stdClass']->attribute = 'This is an attribute';
 892          $this->assertEquals($expected, $result);
 893      }
 894  
 895      public function test_array_session_diff_second_array_larger() {
 896          $a = [];
 897          $a['array'] = ['array', 'contents'];
 898  
 899          $b = [];
 900          $b['stdClass'] = new stdClass();
 901          $b['stdClass']->attribute = 'This is an attribute';
 902          $b['array'] = ['array', 'contents'];
 903  
 904          $class = new ReflectionClass('\core\session\manager');
 905          $method = $class->getMethod('array_session_diff');
 906          $method->setAccessible(true);
 907  
 908          $result = $method->invokeArgs(null, [$a, $b]);
 909  
 910          // It's empty because the first array contains all the contents of the second.
 911          $expected = [];
 912          $this->assertEquals($expected, $result);
 913      }
 914  }