Search moodle.org's
Developer Documentation

  • 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  /**
      18   * This file contains the class that handles testing of the calendar event vault.
      19   *
      20   * @package core_calendar
      21   * @copyright 2017 Ryan Wyllie <ryan@moodle.com>
      22   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      23   */
      24  
      25  defined('MOODLE_INTERNAL') || die();
      26  
      27  global $CFG;
      28  require_once($CFG->dirroot . '/calendar/tests/helpers.php');
      29  
      30  use core_calendar\local\event\data_access\event_vault;
      31  use core_calendar\local\event\strategies\raw_event_retrieval_strategy;
      32  
      33  /**
      34   * This file contains the class that handles testing of the calendar event vault.
      35   *
      36   * @package core_calendar
      37   * @copyright 2017 Ryan Wyllie <ryan@moodle.com>
      38   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      39   */
      40  class core_calendar_event_vault_testcase extends advanced_testcase {
      41  
      42      /**
      43       * Test that get_action_events_by_timesort returns events after the
      44       * provided timesort value.
      45       */
      46      public function test_get_action_events_by_timesort_after_time() {
      47          $this->resetAfterTest(true);
      48  
      49          $user = $this->getDataGenerator()->create_user();
      50          $factory = new action_event_test_factory();
      51          $strategy = new raw_event_retrieval_strategy();
      52          $vault = new event_vault($factory, $strategy);
      53  
      54          $this->setUser($user);
      55  
      56          for ($i = 1; $i < 6; $i++) {
      57              create_event([
      58                  'name' => sprintf('Event %d', $i),
      59                  'eventtype' => 'user',
      60                  'userid' => $user->id,
      61                  'timesort' => $i,
      62                  'type' => CALENDAR_EVENT_TYPE_ACTION
      63              ]);
      64          }
      65  
      66          $events = $vault->get_action_events_by_timesort($user, 3);
      67  
      68          $this->assertCount(3, $events);
      69          $this->assertEquals('Event 3', $events[0]->get_name());
      70          $this->assertEquals('Event 4', $events[1]->get_name());
      71          $this->assertEquals('Event 5', $events[2]->get_name());
      72  
      73          $events = $vault->get_action_events_by_timesort($user, 3, null, null, 1);
      74  
      75          $this->assertCount(1, $events);
      76          $this->assertEquals('Event 3', $events[0]->get_name());
      77  
      78          $events = $vault->get_action_events_by_timesort($user, 6);
      79  
      80          $this->assertCount(0, $events);
      81      }
      82  
      83      /**
      84       * Test that get_action_events_by_timesort returns events before the
      85       * provided timesort value.
      86       */
      87      public function test_get_action_events_by_timesort_before_time() {
      88          $this->resetAfterTest(true);
      89          $this->setAdminuser();
      90  
      91          $user = $this->getDataGenerator()->create_user();
      92          $factory = new action_event_test_factory();
      93          $strategy = new raw_event_retrieval_strategy();
      94          $vault = new event_vault($factory, $strategy);
      95  
      96          for ($i = 1; $i < 6; $i++) {
      97              create_event([
      98                  'name' => sprintf('Event %d', $i),
      99                  'eventtype' => 'user',
     100                  'userid' => $user->id,
     101                  'timesort' => $i,
     102                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     103                  'courseid' => 1
     104              ]);
     105          }
     106  
     107          $events = $vault->get_action_events_by_timesort($user, null, 3);
     108  
     109          $this->assertCount(3, $events);
     110          $this->assertEquals('Event 1', $events[0]->get_name());
     111          $this->assertEquals('Event 2', $events[1]->get_name());
     112          $this->assertEquals('Event 3', $events[2]->get_name());
     113  
     114          $events = $vault->get_action_events_by_timesort($user, null, 3, null, 1);
     115  
     116          $this->assertCount(1, $events);
     117          $this->assertEquals('Event 1', $events[0]->get_name());
     118  
     119          $events = $vault->get_action_events_by_timesort($user, 6);
     120  
     121          $this->assertCount(0, $events);
     122      }
     123  
     124      /**
     125       * Test that get_action_events_by_timesort returns events between the
     126       * provided timesort values.
     127       */
     128      public function test_get_action_events_by_timesort_between_time() {
     129          $this->resetAfterTest(true);
     130          $this->setAdminuser();
     131  
     132          $user = $this->getDataGenerator()->create_user();
     133          $factory = new action_event_test_factory();
     134          $strategy = new raw_event_retrieval_strategy();
     135          $vault = new event_vault($factory, $strategy);
     136  
     137          for ($i = 1; $i < 6; $i++) {
     138              create_event([
     139                  'name' => sprintf('Event %d', $i),
     140                  'eventtype' => 'user',
     141                  'userid' => $user->id,
     142                  'timesort' => $i,
     143                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     144                  'courseid' => 1
     145              ]);
     146          }
     147  
     148          $events = $vault->get_action_events_by_timesort($user, 2, 4);
     149  
     150          $this->assertCount(3, $events);
     151          $this->assertEquals('Event 2', $events[0]->get_name());
     152          $this->assertEquals('Event 3', $events[1]->get_name());
     153          $this->assertEquals('Event 4', $events[2]->get_name());
     154  
     155          $events = $vault->get_action_events_by_timesort($user, 2, 4, null, 1);
     156  
     157          $this->assertCount(1, $events);
     158          $this->assertEquals('Event 2', $events[0]->get_name());
     159      }
     160  
     161      /**
     162       * Test that get_action_events_by_timesort returns events between the
     163       * provided timesort values and after the last seen event when one is
     164       * provided.
     165       */
     166      public function test_get_action_events_by_timesort_between_time_after_event() {
     167          $this->resetAfterTest(true);
     168          $this->setAdminuser();
     169  
     170          $user = $this->getDataGenerator()->create_user();
     171          $factory = new action_event_test_factory();
     172          $strategy = new raw_event_retrieval_strategy();
     173          $vault = new event_vault($factory, $strategy);
     174  
     175          $records = [];
     176          for ($i = 1; $i < 21; $i++) {
     177              $records[] = create_event([
     178                  'name' => sprintf('Event %d', $i),
     179                  'eventtype' => 'user',
     180                  'userid' => $user->id,
     181                  'timesort' => $i,
     182                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     183                  'courseid' => 1
     184              ]);
     185          }
     186  
     187          $aftereventid = $records[6]->id;
     188          $afterevent = $vault->get_event_by_id($aftereventid);
     189          $events = $vault->get_action_events_by_timesort($user, 3, 15, $afterevent);
     190  
     191          $this->assertCount(8, $events);
     192          $this->assertEquals('Event 8', $events[0]->get_name());
     193  
     194          $events = $vault->get_action_events_by_timesort($user, 3, 15, $afterevent, 3);
     195  
     196          $this->assertCount(3, $events);
     197      }
     198  
     199      /**
     200       * Test that get_action_events_by_timesort returns events between the
     201       * provided timesort values and the last seen event can be provided to
     202       * get paginated results.
     203       */
     204      public function test_get_action_events_by_timesort_between_time_skip_even_records() {
     205          $this->resetAfterTest(true);
     206          $this->setAdminuser();
     207  
     208          $user = $this->getDataGenerator()->create_user();
     209          // The factory will return every event that is divisible by 2.
     210          $factory = new action_event_test_factory(function($actionevent) {
     211              static $count = 0;
     212              $count++;
     213              return ($count % 2) ? true : false;
     214          });
     215          $strategy = new raw_event_retrieval_strategy();
     216          $vault = new event_vault($factory, $strategy);
     217  
     218          for ($i = 1; $i < 41; $i++) {
     219              create_event([
     220                  'name' => sprintf('Event %d', $i),
     221                  'eventtype' => 'user',
     222                  'userid' => $user->id,
     223                  'timesort' => $i,
     224                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     225                  'courseid' => 1
     226              ]);
     227          }
     228  
     229          $events = $vault->get_action_events_by_timesort($user, 3, 35, null, 5);
     230  
     231          $this->assertCount(5, $events);
     232          $this->assertEquals('Event 3', $events[0]->get_name());
     233          $this->assertEquals('Event 5', $events[1]->get_name());
     234          $this->assertEquals('Event 7', $events[2]->get_name());
     235          $this->assertEquals('Event 9', $events[3]->get_name());
     236          $this->assertEquals('Event 11', $events[4]->get_name());
     237  
     238          $afterevent = $events[4];
     239          $events = $vault->get_action_events_by_timesort($user, 3, 35, $afterevent, 5);
     240  
     241          $this->assertCount(5, $events);
     242          $this->assertEquals('Event 13', $events[0]->get_name());
     243          $this->assertEquals('Event 15', $events[1]->get_name());
     244          $this->assertEquals('Event 17', $events[2]->get_name());
     245          $this->assertEquals('Event 19', $events[3]->get_name());
     246          $this->assertEquals('Event 21', $events[4]->get_name());
     247      }
     248  
     249      /**
     250       * Test that get_action_events_by_timesort returns events between the
     251       * provided timesort values. The database will continue to be read until the
     252       * number of events requested has been satisfied. In this case the first
     253       * five events are rejected so it should require two database requests.
     254       */
     255      public function test_get_action_events_by_timesort_between_time_skip_first_records() {
     256          $this->resetAfterTest(true);
     257          $this->setAdminuser();
     258  
     259          $user = $this->getDataGenerator()->create_user();
     260          $limit = 5;
     261          $seen = 0;
     262          // The factory will skip the first $limit events.
     263          $factory = new action_event_test_factory(function($actionevent) use (&$seen, $limit) {
     264              if ($seen < $limit) {
     265                  $seen++;
     266                  return false;
     267              } else {
     268                  return true;
     269              }
     270          });
     271          $strategy = new raw_event_retrieval_strategy();
     272          $vault = new event_vault($factory, $strategy);
     273  
     274          for ($i = 1; $i < 21; $i++) {
     275              create_event([
     276                  'name' => sprintf('Event %d', $i),
     277                  'eventtype' => 'user',
     278                  'userid' => $user->id,
     279                  'timesort' => $i,
     280                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     281                  'courseid' => 1
     282              ]);
     283          }
     284  
     285          $events = $vault->get_action_events_by_timesort($user, 1, 20, null, $limit);
     286  
     287          $this->assertCount($limit, $events);
     288          $this->assertEquals(sprintf('Event %d', $limit + 1), $events[0]->get_name());
     289          $this->assertEquals(sprintf('Event %d', $limit + 2), $events[1]->get_name());
     290          $this->assertEquals(sprintf('Event %d', $limit + 3), $events[2]->get_name());
     291          $this->assertEquals(sprintf('Event %d', $limit + 4), $events[3]->get_name());
     292          $this->assertEquals(sprintf('Event %d', $limit + 5), $events[4]->get_name());
     293      }
     294  
     295      /**
     296       * Test that get_action_events_by_timesort returns events between the
     297       * provided timesort values and after the last seen event when one is
     298       * provided. This should work even when the event ids aren't ordered the
     299       * same as the timesort order.
     300       */
     301      public function test_get_action_events_by_timesort_non_consecutive_ids() {
     302          $this->resetAfterTest(true);
     303          $this->setAdminuser();
     304  
     305          $user = $this->getDataGenerator()->create_user();
     306          $factory = new action_event_test_factory();
     307          $strategy = new raw_event_retrieval_strategy();
     308          $vault = new event_vault($factory, $strategy);
     309  
     310          /*
     311           * The events should be ordered by timesort as follows:
     312           *
     313           * 1 event 1
     314           * 2 event 1
     315           * 1 event 2
     316           * 2 event 2
     317           * 1 event 3
     318           * 2 event 3
     319           * 1 event 4
     320           * 2 event 4
     321           * 1 event 5
     322           * 2 event 5
     323           * 1 event 6
     324           * 2 event 6
     325           * 1 event 7
     326           * 2 event 7
     327           * 1 event 8
     328           * 2 event 8
     329           * 1 event 9
     330           * 2 event 9
     331           * 1 event 10
     332           * 2 event 10
     333           */
     334          $records = [];
     335          for ($i = 1; $i < 11; $i++) {
     336              $records[] = create_event([
     337                  'name' => sprintf('1 event %d', $i),
     338                  'eventtype' => 'user',
     339                  'userid' => $user->id,
     340                  'timesort' => $i,
     341                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     342                  'courseid' => 1
     343              ]);
     344          }
     345  
     346          for ($i = 1; $i < 11; $i++) {
     347              $records[] = create_event([
     348                  'name' => sprintf('2 event %d', $i),
     349                  'eventtype' => 'user',
     350                  'userid' => $user->id,
     351                  'timesort' => $i,
     352                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     353                  'courseid' => 1
     354              ]);
     355          }
     356  
     357          /*
     358           * Expected result set:
     359           *
     360           * 2 event 4
     361           * 1 event 5
     362           * 2 event 5
     363           * 1 event 6
     364           * 2 event 6
     365           * 1 event 7
     366           * 2 event 7
     367           * 1 event 8
     368           * 2 event 8
     369           */
     370          $aftereventid = $records[3]->id;
     371          $afterevent = $vault->get_event_by_id($aftereventid);
     372          // Offset results by event with name "1 event 4" which has the same timesort
     373          // value as the lower boundary of this query (3). Confirm that the given
     374          // $afterevent is used to ignore events with the same timesortfrom values.
     375          $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent);
     376  
     377          $this->assertCount(9, $events);
     378          $this->assertEquals('2 event 4', $events[0]->get_name());
     379          $this->assertEquals('2 event 8', $events[8]->get_name());
     380  
     381          /*
     382           * Expected result set:
     383           *
     384           * 2 event 4
     385           * 1 event 5
     386           */
     387          $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent, 2);
     388  
     389          $this->assertCount(2, $events);
     390          $this->assertEquals('2 event 4', $events[0]->get_name());
     391          $this->assertEquals('1 event 5', $events[1]->get_name());
     392  
     393          /*
     394           * Expected result set:
     395           *
     396           * 2 event 8
     397           */
     398          $aftereventid = $records[7]->id;
     399          $afterevent = $vault->get_event_by_id($aftereventid);
     400          // Offset results by event with name "1 event 8" which has the same timesort
     401          // value as the upper boundary of this query (8). Confirm that the given
     402          // $afterevent is used to ignore events with the same timesortto values.
     403          $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent);
     404  
     405          $this->assertCount(1, $events);
     406          $this->assertEquals('2 event 8', $events[0]->get_name());
     407  
     408          /*
     409           * Expected empty result set.
     410           */
     411          $aftereventid = $records[18]->id;
     412          $afterevent = $vault->get_event_by_id($aftereventid);
     413          // Offset results by event with name "2 event 9" which has a timesort
     414          // value larger than the upper boundary of this query (9 > 8). Confirm
     415          // that the given $afterevent is used for filtering events.
     416          $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent);
     417          $this->assertEmpty($events);
     418      }
     419  
     420      /**
     421       * There are subtle cases where the priority of an event override may be identical to another.
     422       * For example, if you duplicate a group override, but make it apply to a different group. Now
     423       * there are two overrides with exactly the same overridden dates. In this case the priority of
     424       * both is 1.
     425       *
     426       * In this situation:
     427       * - A user in group A should see only the A override
     428       * - A user in group B should see only the B override
     429       * - A user in both A and B should see both
     430       */
     431      public function test_get_action_events_by_timesort_with_identical_group_override_priorities() {
     432          $this->resetAfterTest();
     433          $this->setAdminuser();
     434  
     435          $course = $this->getDataGenerator()->create_course();
     436  
     437          // Create an assign instance.
     438          $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
     439          $assigninstance = $assigngenerator->create_instance(['course' => $course->id]);
     440  
     441          // Create users.
     442          $users = [
     443              'Only in group A'  => $this->getDataGenerator()->create_user(),
     444              'Only in group B'  => $this->getDataGenerator()->create_user(),
     445              'In group A and B' => $this->getDataGenerator()->create_user(),
     446              'In no groups'     => $this->getDataGenerator()->create_user()
     447          ];
     448  
     449          // Enrol users.
     450          foreach ($users as $user) {
     451              $this->getDataGenerator()->enrol_user($user->id, $course->id);
     452          }
     453  
     454          // Create groups.
     455          $groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
     456          $groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
     457  
     458          // Add members to groups.
     459          // Group A.
     460          $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['Only in group A']->id]);
     461          $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['In group A and B']->id]);
     462  
     463          // Group B.
     464          $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['Only in group B']->id]);
     465          $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['In group A and B']->id]);
     466  
     467          // Events with the same module name, instance and event type.
     468          $events = [
     469              [
     470                  'name' => 'Assignment 1 due date - Group A override',
     471                  'description' => '',
     472                  'format' => 1,
     473                  'courseid' => $course->id,
     474                  'groupid' => $groupa->id,
     475                  'userid' => 2,
     476                  'modulename' => 'assign',
     477                  'instance' => $assigninstance->id,
     478                  'eventtype' => 'due',
     479                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     480                  'timestart' => 1,
     481                  'timeduration' => 0,
     482                  'visible' => 1,
     483                  'priority' => 1
     484              ],
     485              [
     486                  'name' => 'Assignment 1 due date - Group B override',
     487                  'description' => '',
     488                  'format' => 1,
     489                  'courseid' => $course->id,
     490                  'groupid' => $groupb->id,
     491                  'userid' => 2,
     492                  'modulename' => 'assign',
     493                  'instance' => $assigninstance->id,
     494                  'eventtype' => 'due',
     495                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     496                  'timestart' => 1,
     497                  'timeduration' => 0,
     498                  'visible' => 1,
     499                  'priority' => 1
     500              ],
     501              [
     502                  'name' => 'Assignment 1 due date',
     503                  'description' => '',
     504                  'format' => 1,
     505                  'courseid' => $course->id,
     506                  'groupid' => 0,
     507                  'userid' => 2,
     508                  'modulename' => 'assign',
     509                  'instance' => $assigninstance->id,
     510                  'eventtype' => 'due',
     511                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     512                  'timestart' => 1,
     513                  'timeduration' => 0,
     514                  'visible' => 1,
     515                  'priority' => null,
     516              ]
     517          ];
     518  
     519          foreach ($events as $event) {
     520              calendar_event::create($event, false);
     521          }
     522  
     523          $factory = new action_event_test_factory();
     524          $strategy = new raw_event_retrieval_strategy();
     525          $vault = new event_vault($factory, $strategy);
     526  
     527          $usersevents = array_reduce(array_keys($users), function($carry, $description) use ($users, $vault) {
     528              // NB: This is currently needed to make get_action_events_by_timesort return the right thing.
     529              // It needs to be fixed, see MDL-58736.
     530              $this->setUser($users[$description]);
     531              return $carry + ['For user ' . lcfirst($description) => $vault->get_action_events_by_timesort($users[$description])];
     532          }, []);
     533  
     534          foreach ($usersevents as $description => $userevents) {
     535              if ($description == 'For user in group A and B') {
     536                  // User is in both A and B, so they should see the override for both
     537                  // given that the priority is the same.
     538                  $this->assertCount(2, $userevents);
     539                  continue;
     540              }
     541  
     542              // Otherwise there should be only one assign event for each user.
     543              $this->assertCount(1, $userevents);
     544          }
     545  
     546          // User in only group A should see the group A override.
     547          $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user only in group A'][0]->get_name());
     548  
     549          // User in only group B should see the group B override.
     550          $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user only in group B'][0]->get_name());
     551  
     552          // User in group A and B should see see both overrides since the priorities are the same.
     553          $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user in group A and B'][0]->get_name());
     554          $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user in group A and B'][1]->get_name());
     555  
     556          // User in no groups should see the plain assignment event.
     557          $this->assertEquals('Assignment 1 due date', $usersevents['For user in no groups'][0]->get_name());
     558      }
     559  
     560      /**
     561       * Test that if a user is suspended that events related to that course are not shown.
     562       * User 1 is suspended. User 2 is active.
     563       */
     564      public function test_get_action_events_by_timesort_with_suspended_user() {
     565          $this->resetAfterTest();
     566          $user1 = $this->getDataGenerator()->create_user();
     567          $user2 = $this->getDataGenerator()->create_user();
     568          $course = $this->getDataGenerator()->create_course();
     569          $this->setAdminuser();
     570          $lesson = $this->getDataGenerator()->create_module('lesson', [
     571                  'name' => 'Lesson 1',
     572                  'course' => $course->id,
     573                  'available' => time(),
     574                  'deadline' => (time() + (60 * 60 * 24 * 5))
     575              ]
     576          );
     577          $this->getDataGenerator()->enrol_user($user1->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
     578          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
     579  
     580          $factory = new action_event_test_factory();
     581          $strategy = new raw_event_retrieval_strategy();
     582          $vault = new event_vault($factory, $strategy);
     583  
     584          $user1events = $vault->get_action_events_by_timesort($user1, null, null, null, 20, true);
     585          $this->assertEmpty($user1events);
     586          $user2events = $vault->get_action_events_by_timesort($user2, null, null, null, 20, true);
     587          $this->assertCount(1, $user2events);
     588          $this->assertEquals('Lesson 1 closes', $user2events[0]->get_name());
     589      }
     590  
     591      /**
     592       * Test that get_action_events_by_course returns events after the
     593       * provided timesort value.
     594       */
     595      public function test_get_action_events_by_course_after_time() {
     596          $user = $this->getDataGenerator()->create_user();
     597          $course1 = $this->getDataGenerator()->create_course();
     598          $course2 = $this->getDataGenerator()->create_course();
     599          $factory = new action_event_test_factory();
     600          $strategy = new raw_event_retrieval_strategy();
     601          $vault = new event_vault($factory, $strategy);
     602  
     603          $this->resetAfterTest(true);
     604          $this->setAdminuser();
     605          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
     606          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
     607  
     608          for ($i = 1; $i < 6; $i++) {
     609              create_event([
     610                  'name' => sprintf('Event %d', $i),
     611                  'eventtype' => 'user',
     612                  'userid' => $user->id,
     613                  'timesort' => $i,
     614                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     615                  'courseid' => $course1->id,
     616              ]);
     617          }
     618  
     619          for ($i = 6; $i < 12; $i++) {
     620              create_event([
     621                  'name' => sprintf('Event %d', $i),
     622                  'eventtype' => 'user',
     623                  'userid' => $user->id,
     624                  'timesort' => $i,
     625                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     626                  'courseid' => $course2->id,
     627              ]);
     628          }
     629  
     630          $events = $vault->get_action_events_by_course($user, $course1, 3);
     631          $this->assertCount(3, $events);
     632          $this->assertEquals('Event 3', $events[0]->get_name());
     633          $this->assertEquals('Event 4', $events[1]->get_name());
     634          $this->assertEquals('Event 5', $events[2]->get_name());
     635  
     636          $events = $vault->get_action_events_by_course($user, $course1, 3, null, null, 1);
     637  
     638          $this->assertCount(1, $events);
     639          $this->assertEquals('Event 3', $events[0]->get_name());
     640  
     641          $events = $vault->get_action_events_by_course($user, $course1, 6);
     642  
     643          $this->assertCount(0, $events);
     644      }
     645  
     646      /**
     647       * Test that get_action_events_by_course returns events before the
     648       * provided timesort value.
     649       */
     650      public function test_get_action_events_by_course_before_time() {
     651          $user = $this->getDataGenerator()->create_user();
     652          $course1 = $this->getDataGenerator()->create_course();
     653          $course2 = $this->getDataGenerator()->create_course();
     654          $factory = new action_event_test_factory();
     655          $strategy = new raw_event_retrieval_strategy();
     656          $vault = new event_vault($factory, $strategy);
     657  
     658          $this->resetAfterTest(true);
     659          $this->setAdminuser();
     660          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
     661          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
     662  
     663          for ($i = 1; $i < 6; $i++) {
     664              create_event([
     665                  'name' => sprintf('Event %d', $i),
     666                  'eventtype' => 'user',
     667                  'userid' => $user->id,
     668                  'timesort' => $i,
     669                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     670                  'courseid' => $course1->id,
     671              ]);
     672          }
     673  
     674          for ($i = 6; $i < 12; $i++) {
     675              create_event([
     676                  'name' => sprintf('Event %d', $i),
     677                  'eventtype' => 'user',
     678                  'userid' => $user->id,
     679                  'timesort' => $i,
     680                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     681                  'courseid' => $course2->id,
     682              ]);
     683          }
     684  
     685          $events = $vault->get_action_events_by_course($user, $course1, null, 3);
     686  
     687          $this->assertCount(3, $events);
     688          $this->assertEquals('Event 1', $events[0]->get_name());
     689          $this->assertEquals('Event 2', $events[1]->get_name());
     690          $this->assertEquals('Event 3', $events[2]->get_name());
     691  
     692          $events = $vault->get_action_events_by_course($user, $course1, null, 3, null, 1);
     693  
     694          $this->assertCount(1, $events);
     695          $this->assertEquals('Event 1', $events[0]->get_name());
     696  
     697          $events = $vault->get_action_events_by_course($user, $course1, 6);
     698  
     699          $this->assertCount(0, $events);
     700      }
     701  
     702      /**
     703       * Test that get_action_events_by_course returns events between the
     704       * provided timesort values.
     705       */
     706      public function test_get_action_events_by_course_between_time() {
     707          $user = $this->getDataGenerator()->create_user();
     708          $course1 = $this->getDataGenerator()->create_course();
     709          $course2 = $this->getDataGenerator()->create_course();
     710          $factory = new action_event_test_factory();
     711          $strategy = new raw_event_retrieval_strategy();
     712          $vault = new event_vault($factory, $strategy);
     713  
     714          $this->resetAfterTest(true);
     715          $this->setAdminuser();
     716          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
     717          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
     718  
     719          for ($i = 1; $i < 6; $i++) {
     720              create_event([
     721                  'name' => sprintf('Event %d', $i),
     722                  'eventtype' => 'user',
     723                  'userid' => $user->id,
     724                  'timesort' => $i,
     725                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     726                  'courseid' => $course1->id,
     727              ]);
     728          }
     729  
     730          for ($i = 6; $i < 12; $i++) {
     731              create_event([
     732                  'name' => sprintf('Event %d', $i),
     733                  'eventtype' => 'user',
     734                  'userid' => $user->id,
     735                  'timesort' => $i,
     736                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     737                  'courseid' => $course2->id,
     738              ]);
     739          }
     740  
     741          $events = $vault->get_action_events_by_course($user, $course1, 2, 4);
     742  
     743          $this->assertCount(3, $events);
     744          $this->assertEquals('Event 2', $events[0]->get_name());
     745          $this->assertEquals('Event 3', $events[1]->get_name());
     746          $this->assertEquals('Event 4', $events[2]->get_name());
     747  
     748          $events = $vault->get_action_events_by_course($user, $course1, 2, 4, null, 1);
     749  
     750          $this->assertCount(1, $events);
     751          $this->assertEquals('Event 2', $events[0]->get_name());
     752      }
     753  
     754      /**
     755       * Test that get_action_events_by_course returns events between the
     756       * provided timesort values and after the last seen event when one is
     757       * provided.
     758       */
     759      public function test_get_action_events_by_course_between_time_after_event() {
     760          $user = $this->getDataGenerator()->create_user();
     761          $course1 = $this->getDataGenerator()->create_course();
     762          $course2 = $this->getDataGenerator()->create_course();
     763          $factory = new action_event_test_factory();
     764          $strategy = new raw_event_retrieval_strategy();
     765          $vault = new event_vault($factory, $strategy);
     766          $records = [];
     767  
     768          $this->resetAfterTest(true);
     769          $this->setAdminuser();
     770          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
     771          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
     772  
     773          for ($i = 1; $i < 21; $i++) {
     774              $records[] = create_event([
     775                  'name' => sprintf('Event %d', $i),
     776                  'eventtype' => 'user',
     777                  'userid' => $user->id,
     778                  'timesort' => $i,
     779                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     780                  'courseid' => $course1->id,
     781              ]);
     782          }
     783  
     784          for ($i = 21; $i < 41; $i++) {
     785              $records[] = create_event([
     786                  'name' => sprintf('Event %d', $i),
     787                  'eventtype' => 'user',
     788                  'userid' => $user->id,
     789                  'timesort' => $i,
     790                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     791                  'courseid' => $course2->id,
     792              ]);
     793          }
     794  
     795          $aftereventid = $records[6]->id;
     796          $afterevent = $vault->get_event_by_id($aftereventid);
     797          $events = $vault->get_action_events_by_course($user, $course1, 3, 15, $afterevent);
     798  
     799          $this->assertCount(8, $events);
     800          $this->assertEquals('Event 8', $events[0]->get_name());
     801  
     802          $events = $vault->get_action_events_by_course($user, $course1, 3, 15, $afterevent, 3);
     803  
     804          $this->assertCount(3, $events);
     805      }
     806  
     807      /**
     808       * Test that get_action_events_by_course returns events between the
     809       * provided timesort values and the last seen event can be provided to
     810       * get paginated results.
     811       */
     812      public function test_get_action_events_by_course_between_time_skip_even_records() {
     813          $user = $this->getDataGenerator()->create_user();
     814          $course1 = $this->getDataGenerator()->create_course();
     815          $course2 = $this->getDataGenerator()->create_course();
     816          // The factory will return every event that is divisible by 2.
     817          $factory = new action_event_test_factory(function($actionevent) {
     818              static $count = 0;
     819              $count++;
     820              return ($count % 2) ? true : false;
     821          });
     822          $strategy = new raw_event_retrieval_strategy();
     823          $vault = new event_vault($factory, $strategy);
     824  
     825          $this->resetAfterTest(true);
     826          $this->setAdminuser();
     827          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
     828          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
     829  
     830          for ($i = 1; $i < 41; $i++) {
     831              create_event([
     832                  'name' => sprintf('Event %d', $i),
     833                  'eventtype' => 'user',
     834                  'userid' => $user->id,
     835                  'timesort' => $i,
     836                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     837                  'courseid' => $course1->id,
     838              ]);
     839          }
     840  
     841          for ($i = 41; $i < 81; $i++) {
     842              create_event([
     843                  'name' => sprintf('Event %d', $i),
     844                  'eventtype' => 'user',
     845                  'userid' => $user->id,
     846                  'timesort' => $i,
     847                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     848                  'courseid' => $course2->id,
     849              ]);
     850          }
     851  
     852          $events = $vault->get_action_events_by_course($user, $course1, 3, 35, null, 5);
     853  
     854          $this->assertCount(5, $events);
     855          $this->assertEquals('Event 3', $events[0]->get_name());
     856          $this->assertEquals('Event 5', $events[1]->get_name());
     857          $this->assertEquals('Event 7', $events[2]->get_name());
     858          $this->assertEquals('Event 9', $events[3]->get_name());
     859          $this->assertEquals('Event 11', $events[4]->get_name());
     860  
     861          $afterevent = $events[4];
     862          $events = $vault->get_action_events_by_course($user, $course1, 3, 35, $afterevent, 5);
     863  
     864          $this->assertCount(5, $events);
     865          $this->assertEquals('Event 13', $events[0]->get_name());
     866          $this->assertEquals('Event 15', $events[1]->get_name());
     867          $this->assertEquals('Event 17', $events[2]->get_name());
     868          $this->assertEquals('Event 19', $events[3]->get_name());
     869          $this->assertEquals('Event 21', $events[4]->get_name());
     870      }
     871  
     872      /**
     873       * Test that get_action_events_by_course returns events between the
     874       * provided timesort values. The database will continue to be read until the
     875       * number of events requested has been satisfied. In this case the first
     876       * five events are rejected so it should require two database requests.
     877       */
     878      public function test_get_action_events_by_course_between_time_skip_first_records() {
     879          $user = $this->getDataGenerator()->create_user();
     880          $course1 = $this->getDataGenerator()->create_course();
     881          $course2 = $this->getDataGenerator()->create_course();
     882          $limit = 5;
     883          $seen = 0;
     884          // The factory will skip the first $limit events.
     885          $factory = new action_event_test_factory(function($actionevent) use (&$seen, $limit) {
     886              if ($seen < $limit) {
     887                  $seen++;
     888                  return false;
     889              } else {
     890                  return true;
     891              }
     892          });
     893          $strategy = new raw_event_retrieval_strategy();
     894          $vault = new event_vault($factory, $strategy);
     895  
     896          $this->resetAfterTest(true);
     897          $this->setAdminuser();
     898          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
     899          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
     900  
     901          for ($i = 1; $i < 21; $i++) {
     902              create_event([
     903                  'name' => sprintf('Event %d', $i),
     904                  'eventtype' => 'user',
     905                  'userid' => $user->id,
     906                  'timesort' => $i,
     907                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     908                  'courseid' => $course1->id,
     909              ]);
     910          }
     911  
     912          for ($i = 21; $i < 41; $i++) {
     913              create_event([
     914                  'name' => sprintf('Event %d', $i),
     915                  'eventtype' => 'user',
     916                  'userid' => $user->id,
     917                  'timesort' => $i,
     918                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     919                  'courseid' => $course2->id,
     920              ]);
     921          }
     922  
     923          $events = $vault->get_action_events_by_course($user, $course1, 1, 20, null, $limit);
     924  
     925          $this->assertCount($limit, $events);
     926          $this->assertEquals(sprintf('Event %d', $limit + 1), $events[0]->get_name());
     927          $this->assertEquals(sprintf('Event %d', $limit + 2), $events[1]->get_name());
     928          $this->assertEquals(sprintf('Event %d', $limit + 3), $events[2]->get_name());
     929          $this->assertEquals(sprintf('Event %d', $limit + 4), $events[3]->get_name());
     930          $this->assertEquals(sprintf('Event %d', $limit + 5), $events[4]->get_name());
     931      }
     932  
     933      /**
     934       * Test that get_action_events_by_course returns events between the
     935       * provided timesort values and after the last seen event when one is
     936       * provided. This should work even when the event ids aren't ordered the
     937       * same as the timesort order.
     938       */
     939      public function test_get_action_events_by_course_non_consecutive_ids() {
     940          $this->resetAfterTest(true);
     941          $this->setAdminuser();
     942  
     943          $user = $this->getDataGenerator()->create_user();
     944          $course1 = $this->getDataGenerator()->create_course();
     945          $course2 = $this->getDataGenerator()->create_course();
     946          $factory = new action_event_test_factory();
     947          $strategy = new raw_event_retrieval_strategy();
     948          $vault = new event_vault($factory, $strategy);
     949  
     950          $this->setAdminuser();
     951          $this->getDataGenerator()->enrol_user($user->id, $course1->id);
     952          $this->getDataGenerator()->enrol_user($user->id, $course2->id);
     953  
     954          /*
     955           * The events should be ordered by timesort as follows:
     956           *
     957           * 1 event 1
     958           * 2 event 1
     959           * 1 event 2
     960           * 2 event 2
     961           * 1 event 3
     962           * 2 event 3
     963           * 1 event 4
     964           * 2 event 4
     965           * 1 event 5
     966           * 2 event 5
     967           * 1 event 6
     968           * 2 event 6
     969           * 1 event 7
     970           * 2 event 7
     971           * 1 event 8
     972           * 2 event 8
     973           * 1 event 9
     974           * 2 event 9
     975           * 1 event 10
     976           * 2 event 10
     977           */
     978          $records = [];
     979          for ($i = 1; $i < 11; $i++) {
     980              $records[] = create_event([
     981                  'name' => sprintf('1 event %d', $i),
     982                  'eventtype' => 'user',
     983                  'userid' => $user->id,
     984                  'timesort' => $i,
     985                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     986                  'courseid' => $course1->id,
     987              ]);
     988          }
     989  
     990          for ($i = 1; $i < 11; $i++) {
     991              $records[] = create_event([
     992                  'name' => sprintf('2 event %d', $i),
     993                  'eventtype' => 'user',
     994                  'userid' => $user->id,
     995                  'timesort' => $i,
     996                  'type' => CALENDAR_EVENT_TYPE_ACTION,
     997                  'courseid' => $course1->id,
     998              ]);
     999          }
    1000  
    1001          // Create events for the other course.
    1002          for ($i = 1; $i < 11; $i++) {
    1003              $records[] = create_event([
    1004                  'name' => sprintf('3 event %d', $i),
    1005                  'eventtype' => 'user',
    1006                  'userid' => $user->id,
    1007                  'timesort' => $i,
    1008                  'type' => CALENDAR_EVENT_TYPE_ACTION,
    1009                  'courseid' => $course2->id,
    1010              ]);
    1011          }
    1012  
    1013          /*
    1014           * Expected result set:
    1015           *
    1016           * 2 event 4
    1017           * 1 event 5
    1018           * 2 event 5
    1019           * 1 event 6
    1020           * 2 event 6
    1021           * 1 event 7
    1022           * 2 event 7
    1023           * 1 event 8
    1024           * 2 event 8
    1025           */
    1026          $aftereventid = $records[3]->id;
    1027          $afterevent = $vault->get_event_by_id($aftereventid);
    1028          // Offset results by event with name "1 event 4" which has the same timesort
    1029          // value as the lower boundary of this query (3). Confirm that the given
    1030          // $afterevent is used to ignore events with the same timesortfrom values.
    1031          $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent);
    1032  
    1033          $this->assertCount(9, $events);
    1034          $this->assertEquals('2 event 4', $events[0]->get_name());
    1035          $this->assertEquals('2 event 8', $events[8]->get_name());
    1036  
    1037          /*
    1038           * Expected result set:
    1039           *
    1040           * 2 event 4
    1041           * 1 event 5
    1042           */
    1043          $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent, 2);
    1044  
    1045          $this->assertCount(2, $events);
    1046          $this->assertEquals('2 event 4', $events[0]->get_name());
    1047          $this->assertEquals('1 event 5', $events[1]->get_name());
    1048  
    1049          /*
    1050           * Expected result set:
    1051           *
    1052           * 2 event 8
    1053           */
    1054          $aftereventid = $records[7]->id;
    1055          $afterevent = $vault->get_event_by_id($aftereventid);
    1056          // Offset results by event with name "1 event 8" which has the same timesort
    1057          // value as the upper boundary of this query (8). Confirm that the given
    1058          // $afterevent is used to ignore events with the same timesortto values.
    1059          $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent);
    1060  
    1061          $this->assertCount(1, $events);
    1062          $this->assertEquals('2 event 8', $events[0]->get_name());
    1063  
    1064          /*
    1065           * Expected empty result set.
    1066           */
    1067          $aftereventid = $records[18]->id;
    1068          $afterevent = $vault->get_event_by_id($aftereventid);
    1069          // Offset results by event with name "2 event 9" which has a timesort
    1070          // value larger than the upper boundary of this query (9 > 8). Confirm
    1071          // that the given $afterevent is used for filtering events.
    1072          $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent);
    1073  
    1074          $this->assertEmpty($events);
    1075      }
    1076  
    1077      /**
    1078       * There are subtle cases where the priority of an event override may be identical to another.
    1079       * For example, if you duplicate a group override, but make it apply to a different group. Now
    1080       * there are two overrides with exactly the same overridden dates. In this case the priority of
    1081       * both is 1.
    1082       *
    1083       * In this situation:
    1084       * - A user in group A should see only the A override
    1085       * - A user in group B should see only the B override
    1086       * - A user in both A and B should see both
    1087       */
    1088      public function test_get_action_events_by_course_with_identical_group_override_priorities() {
    1089          $this->resetAfterTest();
    1090          $this->setAdminuser();
    1091  
    1092          $course = $this->getDataGenerator()->create_course();
    1093  
    1094          // Create an assign instance.
    1095          $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
    1096          $assigninstance = $assigngenerator->create_instance(['course' => $course->id]);
    1097  
    1098          // Create users.
    1099          $users = [
    1100              'Only in group A'  => $this->getDataGenerator()->create_user(),
    1101              'Only in group B'  => $this->getDataGenerator()->create_user(),
    1102              'In group A and B' => $this->getDataGenerator()->create_user(),
    1103              'In no groups'     => $this->getDataGenerator()->create_user()
    1104          ];
    1105  
    1106          // Enrol users.
    1107          foreach ($users as $user) {
    1108              $this->getDataGenerator()->enrol_user($user->id, $course->id);
    1109          }
    1110  
    1111          // Create groups.
    1112          $groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
    1113          $groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
    1114  
    1115          // Add members to groups.
    1116          // Group A.
    1117          $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['Only in group A']->id]);
    1118          $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['In group A and B']->id]);
    1119  
    1120          // Group B.
    1121          $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['Only in group B']->id]);
    1122          $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['In group A and B']->id]);
    1123  
    1124          // Events with the same module name, instance and event type.
    1125          $events = [
    1126              [
    1127                  'name' => 'Assignment 1 due date - Group A override',
    1128                  'description' => '',
    1129                  'format' => 1,
    1130                  'courseid' => $course->id,
    1131                  'groupid' => $groupa->id,
    1132                  'userid' => 2,
    1133                  'modulename' => 'assign',
    1134                  'instance' => $assigninstance->id,
    1135                  'eventtype' => 'due',
    1136                  'type' => CALENDAR_EVENT_TYPE_ACTION,
    1137                  'timestart' => 1,
    1138                  'timeduration' => 0,
    1139                  'visible' => 1,
    1140                  'priority' => 1
    1141              ],
    1142              [
    1143                  'name' => 'Assignment 1 due date - Group B override',
    1144                  'description' => '',
    1145                  'format' => 1,
    1146                  'courseid' => $course->id,
    1147                  'groupid' => $groupb->id,
    1148                  'userid' => 2,
    1149                  'modulename' => 'assign',
    1150                  'instance' => $assigninstance->id,
    1151                  'eventtype' => 'due',
    1152                  'type' => CALENDAR_EVENT_TYPE_ACTION,
    1153                  'timestart' => 1,
    1154                  'timeduration' => 0,
    1155                  'visible' => 1,
    1156                  'priority' => 1
    1157              ],
    1158              [
    1159                  'name' => 'Assignment 1 due date',
    1160                  'description' => '',
    1161                  'format' => 1,
    1162                  'courseid' => $course->id,
    1163                  'groupid' => 0,
    1164                  'userid' => 2,
    1165                  'modulename' => 'assign',
    1166                  'instance' => $assigninstance->id,
    1167                  'eventtype' => 'due',
    1168                  'type' => CALENDAR_EVENT_TYPE_ACTION,
    1169                  'timestart' => 1,
    1170                  'timeduration' => 0,
    1171                  'visible' => 1,
    1172                  'priority' => null,
    1173              ]
    1174          ];
    1175  
    1176          foreach ($events as $event) {
    1177              calendar_event::create($event, false);
    1178          }
    1179  
    1180          $factory = new action_event_test_factory();
    1181          $strategy = new raw_event_retrieval_strategy();
    1182          $vault = new event_vault($factory, $strategy);
    1183  
    1184          $usersevents = array_reduce(array_keys($users), function($carry, $description) use ($users, $course, $vault) {
    1185              // NB: This is currently needed to make get_action_events_by_timesort return the right thing.
    1186              // It needs to be fixed, see MDL-58736.
    1187              $this->setUser($users[$description]);
    1188              return $carry + [
    1189                  'For user ' . lcfirst($description) => $vault->get_action_events_by_course($users[$description], $course)
    1190              ];
    1191          }, []);
    1192  
    1193          foreach ($usersevents as $description => $userevents) {
    1194              if ($description == 'For user in group A and B') {
    1195                  // User is in both A and B, so they should see the override for both
    1196                  // given that the priority is the same.
    1197                  $this->assertCount(2, $userevents);
    1198                  continue;
    1199              }
    1200  
    1201              // Otherwise there should be only one assign event for each user.
    1202              $this->assertCount(1, $userevents);
    1203          }
    1204  
    1205          // User in only group A should see the group A override.
    1206          $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user only in group A'][0]->get_name());
    1207  
    1208          // User in only group B should see the group B override.
    1209          $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user only in group B'][0]->get_name());
    1210  
    1211          // User in group A and B should see see both overrides since the priorities are the same.
    1212          $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user in group A and B'][0]->get_name());
    1213          $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user in group A and B'][1]->get_name());
    1214  
    1215          // User in no groups should see the plain assignment event.
    1216          $this->assertEquals('Assignment 1 due date', $usersevents['For user in no groups'][0]->get_name());
    1217      }
    1218  }