Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Contains unit tests for core_completion/activity_custom_completion.
  19   *
  20   * @package   mod_forum
  21   * @copyright Simey Lameze <simey@moodle.com>
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  declare(strict_types=1);
  26  
  27  namespace mod_forum;
  28  
  29  use advanced_testcase;
  30  use cm_info;
  31  use coding_exception;
  32  use mod_forum\completion\custom_completion;
  33  use moodle_exception;
  34  
  35  defined('MOODLE_INTERNAL') || die();
  36  
  37  global $CFG;
  38  require_once($CFG->libdir . '/completionlib.php');
  39  require_once($CFG->dirroot . '/mod/forum/tests/generator/lib.php');
  40  require_once($CFG->dirroot . '/mod/forum/tests/generator_trait.php');
  41  
  42  /**
  43   * Class for unit testing mod_forum/activity_custom_completion.
  44   *
  45   * @package   mod_forum
  46   * @copyright Simey Lameze <simey@moodle.com>
  47   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  48   */
  49  class custom_completion_test extends advanced_testcase {
  50  
  51      use \mod_forum_tests_generator_trait;
  52  
  53      /**
  54       * Data provider for get_state().
  55       *
  56       * @return array[]
  57       */
  58      public function get_state_provider(): array {
  59          return [
  60              'Undefined rule' => [
  61                  'somenonexistentrule', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, coding_exception::class
  62              ],
  63              'Completion discussions rule not available'  => [
  64                  'completiondiscussions', 0,  COMPLETION_TRACKING_NONE, 0, 0, 0, null, moodle_exception::class
  65              ],
  66              'Completion discussions rule available, user has not created discussion' => [
  67                  'completiondiscussions', 0, COMPLETION_TRACKING_AUTOMATIC, 5, 0, 0, COMPLETION_INCOMPLETE, null
  68              ],
  69              'Rule available, user has created discussions' => [
  70                  'completiondiscussions', 5, COMPLETION_TRACKING_AUTOMATIC, 5, 0, 0, COMPLETION_COMPLETE, null
  71              ],
  72              'Completion replies rule not available' => [
  73                  'completionreplies', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, moodle_exception::class
  74              ],
  75              'Rule available, user has not replied' => [
  76                  'completionreplies', 0, COMPLETION_TRACKING_AUTOMATIC, 0, 5, 0, COMPLETION_INCOMPLETE, null
  77              ],
  78              'Rule available, user has created replied' => [
  79                  'completionreplies', 5, COMPLETION_TRACKING_AUTOMATIC, 0, 5, 0, COMPLETION_COMPLETE, null
  80              ],
  81              'Completion posts rule not available' => [
  82                  'completionposts', 0, COMPLETION_TRACKING_NONE, 0, 0, 0, null, moodle_exception::class
  83              ],
  84              'Rule available, user has not posted' => [
  85                  'completionposts', 0, COMPLETION_TRACKING_AUTOMATIC, 0, 0, 5, COMPLETION_INCOMPLETE, null
  86              ],
  87              'Rule available, user has posted' => [
  88                  'completionposts', 5, COMPLETION_TRACKING_AUTOMATIC, 0, 0, 5, COMPLETION_COMPLETE, null
  89              ],
  90          ];
  91      }
  92  
  93      /**
  94       * Test for get_state().
  95       *
  96       * @dataProvider get_state_provider
  97       * @param string $rule The custom completion rule.
  98       * @param int $rulecount Quantity of discussions, replies or posts to be created.
  99       * @param int $available Whether this rule is available.
 100       * @param int|null $discussions The number of discussions.
 101       * @param int|null $replies The number of replies.
 102       * @param int|null $posts The number of posts.
 103       * @param int|null $status Expected status.
 104       * @param string|null $exception Expected exception.
 105       */
 106      public function test_get_state(string $rule, int $rulecount, int $available, ?int $discussions, ?int $replies,
 107                                     ?int $posts, ?int $status, ?string $exception) {
 108  
 109          if (!is_null($exception)) {
 110              $this->expectException($exception);
 111          }
 112  
 113          $this->resetAfterTest();
 114  
 115          $course = $this->getDataGenerator()->create_course(['enablecompletion' => COMPLETION_ENABLED]);
 116          $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
 117  
 118          $forumgenerator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
 119  
 120          $params = [
 121              'course' => $course->id,
 122              'completion' => $available,
 123              'completiondiscussions' => $discussions,
 124              'completionreplies' => $replies,
 125              'completionposts' => $posts
 126          ];
 127          $forum = $this->getDataGenerator()->create_module('forum', $params);
 128  
 129          $cm = get_coursemodule_from_instance('forum', $forum->id);
 130  
 131          if ($rulecount > 0) {
 132              if ($rule == 'completiondiscussions') {
 133                  // Create x number of discussions.
 134                  for ($i = 0; $i < $rulecount; $i++) {
 135                      $forumgenerator->create_discussion((object) [
 136                          'course' => $forum->course,
 137                          'userid' => $student->id,
 138                          'forum' => $forum->id,
 139                      ]);
 140                  }
 141              } else if ($rule == 'completionreplies') {
 142                  [$discussion1, $post1] = $this->helper_post_to_forum($forum, $student);
 143                  for ($i = 0; $i < $rulecount; $i++) {
 144                      $this->helper_reply_to_post($post1, $student);
 145                  }
 146              } else if ($rule == 'completionposts') {
 147                  for ($i = 0; $i < $rulecount; $i++) {
 148                      $this->helper_post_to_forum($forum, $student);
 149                  }
 150              }
 151          }
 152  
 153          // Make sure we're using a cm_info object.
 154          $cm = cm_info::create($cm);
 155  
 156          $customcompletion = new custom_completion($cm, (int)$student->id);
 157          $this->assertEquals($status, $customcompletion->get_state($rule));
 158      }
 159  
 160      /**
 161       * Test for get_defined_custom_rules().
 162       */
 163      public function test_get_defined_custom_rules() {
 164          $rules = custom_completion::get_defined_custom_rules();
 165          $this->assertCount(3, $rules);
 166          $this->assertEquals('completiondiscussions', reset($rules));
 167      }
 168  
 169      /**
 170       * Test for get_defined_custom_rule_descriptions().
 171       */
 172      public function test_get_custom_rule_descriptions() {
 173          // Get defined custom rules.
 174          $rules = custom_completion::get_defined_custom_rules();
 175  
 176          // Build a mock cm_info instance.
 177          $mockcminfo = $this->getMockBuilder(cm_info::class)
 178              ->disableOriginalConstructor()
 179              ->onlyMethods(['__get'])
 180              ->getMock();
 181          // Instantiate a custom_completion object using the mocked cm_info.
 182          $customcompletion = new custom_completion($mockcminfo, 1);
 183  
 184          // Get custom rule descriptions.
 185          $ruledescriptions = $customcompletion->get_custom_rule_descriptions();
 186  
 187          // Confirm that defined rules and rule descriptions are consistent with each other.
 188          $this->assertEquals(count($rules), count($ruledescriptions));
 189          foreach ($rules as $rule) {
 190              $this->assertArrayHasKey($rule, $ruledescriptions);
 191          }
 192      }
 193  
 194      /**
 195       * Test for is_defined().
 196       */
 197      public function test_is_defined() {
 198          // Build a mock cm_info instance.
 199          $mockcminfo = $this->getMockBuilder(cm_info::class)
 200              ->disableOriginalConstructor()
 201              ->getMock();
 202  
 203          $customcompletion = new custom_completion($mockcminfo, 1);
 204  
 205          // Rule is defined.
 206          $this->assertTrue($customcompletion->is_defined('completiondiscussions'));
 207  
 208          // Undefined rule.
 209          $this->assertFalse($customcompletion->is_defined('somerandomrule'));
 210      }
 211  
 212      /**
 213       * Data provider for test_get_available_custom_rules().
 214       *
 215       * @return array[]
 216       */
 217      public function get_available_custom_rules_provider(): array {
 218          return [
 219              'Completion discussions available' => [
 220                  COMPLETION_ENABLED, ['completiondiscussions']
 221              ],
 222              'Completion discussions not available' => [
 223                  COMPLETION_DISABLED, []
 224              ],
 225              'Completion replies available' => [
 226                  COMPLETION_ENABLED, ['completionreplies']
 227              ],
 228              'Completion replies not available' => [
 229                  COMPLETION_DISABLED, []
 230              ],
 231              'Completion posts available' => [
 232                  COMPLETION_ENABLED, ['completionposts']
 233              ],
 234              'Completion posts not available' => [
 235                  COMPLETION_DISABLED, []
 236              ],
 237          ];
 238      }
 239  
 240      /**
 241       * Test for get_available_custom_rules().
 242       *
 243       * @dataProvider get_available_custom_rules_provider
 244       * @param int $status
 245       * @param array $expected
 246       */
 247      public function test_get_available_custom_rules(int $status, array $expected) {
 248          $customdataval = [
 249              'customcompletionrules' => []
 250          ];
 251          if ($status == COMPLETION_ENABLED) {
 252              $rule = $expected[0];
 253              $customdataval = [
 254                  'customcompletionrules' => [$rule => $status]
 255              ];
 256          }
 257  
 258          // Build a mock cm_info instance.
 259          $mockcminfo = $this->getMockBuilder(cm_info::class)
 260              ->disableOriginalConstructor()
 261              ->onlyMethods(['__get'])
 262              ->getMock();
 263  
 264          // Mock the return of magic getter for the customdata attribute.
 265          $mockcminfo->expects($this->any())
 266              ->method('__get')
 267              ->with('customdata')
 268              ->willReturn($customdataval);
 269  
 270          $customcompletion = new custom_completion($mockcminfo, 1);
 271          $this->assertEquals($expected, $customcompletion->get_available_custom_rules());
 272      }
 273  }