Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace mod_bigbluebuttonbn\completion;
  18  
  19  use completion_info;
  20  use context_module;
  21  use mod_bigbluebuttonbn\instance;
  22  use mod_bigbluebuttonbn\local\config;
  23  use mod_bigbluebuttonbn\logger;
  24  use mod_bigbluebuttonbn\meeting;
  25  use mod_bigbluebuttonbn\test\testcase_helper_trait;
  26  
  27  /**
  28   * Tests for Big Blue Button Completion.
  29   *
  30   * @package   mod_bigbluebuttonbn
  31   * @copyright 2021 - present, Blindside Networks Inc
  32   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  33   * @author    Laurent David (laurent@call-learning.fr)
  34   * @covers \mod_bigbluebuttonbn\completion\custom_completion
  35   */
  36  class completion_test extends \advanced_testcase {
  37      use testcase_helper_trait;
  38  
  39      /**
  40       * Setup basic
  41       */
  42      public function setUp(): void {
  43          parent::setUp();
  44          $this->initialise_mock_server();
  45          set_config('enablecompletion', true); // Enable completion for all tests.
  46      }
  47  
  48      /**
  49       * Completion with no rules: the completion is completed as soons as we view the course.
  50       */
  51      public function test_get_completion_state_no_rules() {
  52          $this->resetAfterTest();
  53          list($bbactivitycontext, $bbactivitycm, $bbactivity) = $this->create_instance();
  54  
  55          $user = $this->getDataGenerator()->create_user();
  56          $this->setUser($user);
  57  
  58          $completion = new custom_completion($bbactivitycm, $user->id);
  59          $result = $completion->get_overall_completion_state();
  60          // No custom rules so complete by default.
  61          $this->assertEquals(COMPLETION_COMPLETE, $result);
  62      }
  63  
  64      /**
  65       * Completion with no rules and join meeting
  66       */
  67      public function test_get_completion_state_no_rules_and_join_meeting() {
  68          $this->resetAfterTest();
  69          list($bbactivitycontext, $bbactivitycm, $bbactivity) = $this->create_instance();
  70  
  71          $user = $this->getDataGenerator()->create_user();
  72          $this->setUser($user);
  73  
  74          // Now create a couple of logs.
  75          $instance = instance::get_from_instanceid($bbactivity->id);
  76          logger::log_meeting_joined_event($instance, 0);
  77          // No custom rules and we joined once so complete.
  78          $completion = new custom_completion($bbactivitycm, $user->id);
  79          $result = $completion->get_overall_completion_state();
  80          $this->assertEquals(COMPLETION_COMPLETE, $result);
  81      }
  82  
  83      /**
  84       * With state incomplete
  85       */
  86      public function test_get_completion_state_incomplete() {
  87          $this->resetAfterTest();
  88  
  89          list($bbactivitycontext, $bbactivitycm, $bbactivity) = $this->create_instance();
  90  
  91          $bbactivitycm->override_customdata('customcompletionrules', [
  92              'completionengagementchats' => '1',
  93              'completionattendance' => '1'
  94          ]);
  95  
  96          $user = $this->getDataGenerator()->create_user();
  97          $this->setUser($user);
  98  
  99          $completion = new custom_completion($bbactivitycm, $user->id);
 100          $result = $completion->get_overall_completion_state();
 101          $this->assertEquals(COMPLETION_INCOMPLETE, $result);
 102      }
 103  
 104      /**
 105       * With state complete
 106       */
 107      public function test_get_completion_state_complete() {
 108          $this->resetAfterTest();
 109  
 110          list($bbactivitycontext, $bbactivitycm, $bbactivity) = $this->create_instance(
 111              $this->get_course(),
 112              [
 113                  'completion' => '2',
 114                  'completionengagementtalks' => 2,
 115                  'completionengagementchats' => 2,
 116                  'completionattendance' => 15
 117              ]
 118          );
 119          $instance = instance::get_from_instanceid($bbactivity->id);
 120  
 121          $user = $this->getDataGenerator()->create_user();
 122          $this->setUser($user);
 123  
 124          // Add a couple of fake logs.
 125          $overrides = ['meetingid' => $bbactivity->meetingid];
 126          $meta = [
 127              'origin' => 0,
 128              'data' => [
 129                  'duration' => 300, // 300 seconds, i.e 5 mins.
 130                  'engagement' => [
 131                      'chats' => 2,
 132                      'talks' => 2,
 133                  ],
 134              ],
 135          ];
 136  
 137          // We setup a couple of logs as per engagement and duration.
 138          logger::log_event_summary($instance, $overrides, $meta);
 139          logger::log_event_summary($instance, $overrides, $meta);
 140          $completion = new custom_completion($bbactivitycm, $user->id);
 141          $result = $completion->get_overall_completion_state();
 142          $this->assertEquals(COMPLETION_INCOMPLETE, $result);
 143  
 144          // Now we have 15 mins.
 145          logger::log_event_summary($instance, $overrides, $meta);
 146          // Now that the meeting was joined, it should be complete.
 147          $completion = new custom_completion($bbactivitycm, $user->id);
 148          $result = $completion->get_overall_completion_state();
 149          $this->assertEquals(COMPLETION_COMPLETE, $result);
 150      }
 151  
 152      /**
 153       * No rule description but active
 154       */
 155      public function test_mod_bigbluebuttonbn_get_completion_active_rule_descriptions() {
 156          $this->resetAfterTest();
 157          $user = $this->getDataGenerator()->create_user();
 158          $this->setUser($user);
 159          // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't.
 160          // Inspired from the same test in forum.
 161          list($bbactivitycontext, $cm1, $bbactivity) = $this->create_instance($this->get_course(),
 162              ['completion' => '2']);
 163          $cm1->override_customdata('customcompletionrules', [
 164              'completionattendance' => '1'
 165          ]);
 166          list($bbactivitycontext, $cm2, $bbactivity) = $this->create_instance($this->get_course(),
 167              ['completion' => '2']);
 168          $cm2->override_customdata('customcompletionrules', [
 169              'completionattendance' => '0'
 170          ]);
 171  
 172          $completioncm1 = new custom_completion($cm1, $user->id);
 173          // TODO: check the return value here as there might be an issue with the function compared to the forum for example.
 174          $this->assertEquals(
 175              [
 176                  'completionengagementchats' => get_string('completionengagementchats_desc', 'mod_bigbluebuttonbn', 1),
 177                  'completionengagementtalks' => get_string('completionengagementtalks_desc', 'mod_bigbluebuttonbn', 1),
 178                  'completionattendance' => get_string('completionattendance_desc', 'mod_bigbluebuttonbn', 1),
 179                  'completionengagementraisehand' => get_string('completionengagementraisehand_desc', 'mod_bigbluebuttonbn', 1),
 180                  'completionengagementpollvotes' => get_string('completionengagementpollvotes_desc', 'mod_bigbluebuttonbn', 1),
 181                  'completionengagementemojis' => get_string('completionengagementemojis_desc', 'mod_bigbluebuttonbn', 1)
 182              ],
 183              $completioncm1->get_custom_rule_descriptions());
 184          $completioncm2 = new custom_completion($cm2, $user->id);
 185          $this->assertEquals(
 186              [
 187                  'completionengagementchats' => get_string('completionengagementchats_desc', 'mod_bigbluebuttonbn', 1),
 188                  'completionengagementtalks' => get_string('completionengagementtalks_desc', 'mod_bigbluebuttonbn', 1),
 189                  'completionattendance' => get_string('completionattendance_desc', 'mod_bigbluebuttonbn', 0),
 190                  'completionengagementraisehand' => get_string('completionengagementraisehand_desc', 'mod_bigbluebuttonbn', 1),
 191                  'completionengagementpollvotes' => get_string('completionengagementpollvotes_desc', 'mod_bigbluebuttonbn', 1),
 192                  'completionengagementemojis' => get_string('completionengagementemojis_desc', 'mod_bigbluebuttonbn', 1)
 193              ], $completioncm2->get_custom_rule_descriptions());
 194      }
 195  
 196      /**
 197       * Completion View
 198       */
 199      public function test_view() {
 200          $this->resetAfterTest();
 201          $this->setAdminUser();
 202          list($bbactivitycontext, $bbactivitycm, $bbactivity) = $this->create_instance(
 203              null, ['completion' => 2, 'completionview' => 1]
 204          );
 205  
 206          // Trigger and capture the event.
 207          $sink = $this->redirectEvents();
 208  
 209          // Check completion before viewing.
 210          $completion = new completion_info($this->get_course());
 211          $completiondata = $completion->get_data($bbactivitycm);
 212          $this->assertEquals(0, $completiondata->viewed);
 213          $this->assertEquals(COMPLETION_NOT_VIEWED, $completiondata->completionstate);
 214  
 215          bigbluebuttonbn_view($bbactivity, $this->get_course(), $bbactivitycm, context_module::instance($bbactivitycm->id));
 216  
 217          $events = $sink->get_events();
 218          $this->assertTrue(count($events) > 1); // TODO : Here we have the module completion event triggered twice.
 219          // this might be a bug from 4.0 core and will need some further investigation.
 220          $event = reset($events);
 221  
 222          // Checking that the event contains the expected values.
 223          $this->assertInstanceOf('\mod_bigbluebuttonbn\event\course_module_viewed', $event);
 224          $this->assertEquals($bbactivitycontext, $event->get_context());
 225          $url = new \moodle_url('/mod/bigbluebuttonbn/view.php', ['id' => $bbactivitycontext->instanceid]);
 226          $this->assertEquals($url, $event->get_url());
 227          $this->assertEventContextNotUsed($event);
 228          $this->assertNotEmpty($event->get_name());
 229  
 230          // Check completion status.
 231          $completion = new completion_info($this->get_course());
 232          $completiondata = $completion->get_data($bbactivitycm);
 233          $this->assertEquals(1, $completiondata->viewed);
 234          $this->assertEquals(COMPLETION_COMPLETE, $completiondata->completionstate);
 235      }
 236  
 237      /**
 238       * Completion with no rules and join meeting
 239       *
 240       * @param array $customcompletionrules
 241       * @param array $events
 242       * @param int $expectedstate
 243       * @dataProvider custom_completion_data_provider
 244       */
 245      public function test_get_completion_with_events(array $customcompletionrules, array $events, int $expectedstate) {
 246          $this->resetAfterTest();
 247          list($bbactivitycontext, $bbactivitycm, $bbactivity) = $this->create_instance(
 248              $this->get_course(),
 249              [
 250                  'completion' => '2',
 251              ]
 252          );
 253          $bbactivitycm->override_customdata('customcompletionrules', $customcompletionrules);
 254          $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn');
 255  
 256          $user = $this->getDataGenerator()->create_user();
 257          $this->setUser($user);
 258  
 259          // Now create a couple of events.
 260          $instance = instance::get_from_instanceid($bbactivity->id);
 261          set_config('bigbluebuttonbn_meetingevents_enabled', true);
 262          $meeting = $plugingenerator->create_meeting([
 263              'instanceid' => $instance->get_instance_id(),
 264              'groupid' => $instance->get_group_id(),
 265              'participants' => json_encode([$user->id])
 266          ]);
 267          foreach ($events as $edesc) {
 268              $plugingenerator->add_meeting_event($user, $instance, $edesc->name, $edesc->data ?? '');
 269          }
 270          $result = $plugingenerator->send_all_events($instance);
 271          $this->assertNotEmpty($result->data);
 272          $data = json_decode(json_encode($result->data));
 273          meeting::meeting_events($instance, $data);
 274          $completion = new custom_completion($bbactivitycm, $user->id);
 275          $result = $completion->get_overall_completion_state();
 276          $this->assertEquals($expectedstate, $result);
 277      }
 278  
 279      /**
 280       * Data generator
 281       *
 282       * @return array[]
 283       */
 284      public function custom_completion_data_provider() {
 285          return [
 286              'simple' => [
 287                  'customcompletionrules' => [
 288                      'completionengagementtalks' => 1,
 289                      'completionengagementchats' => 1,
 290                  ],
 291                  'events' => [
 292                      (object) ['name' => 'talks'],
 293                      (object) ['name' => 'chats']
 294                  ],
 295                  'expectedstate' => COMPLETION_COMPLETE
 296              ],
 297              'not right events' => [
 298                  'customcompletionrules' => [
 299                      'completionengagementchats' => 1,
 300                  ],
 301                  'events' => [
 302                      (object) ['name' => 'talks']
 303                  ],
 304                  'expectedstate' => COMPLETION_INCOMPLETE
 305              ],
 306              'attendance' => [
 307                  'customcompletionrules' => [
 308                      'completionattendance' => 1,
 309                  ],
 310                  'events' => [
 311                      (object) ['name' => 'talks'],
 312                      (object) ['name' => 'attendance', 'data' => '70']
 313                  ],
 314                  'expectedstate' => COMPLETION_COMPLETE
 315              ]
 316          ];
 317      }
 318  }
 319