| 1 | efrain | 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 availability_completion;
 | 
        
           |  |  | 18 |   | 
        
           |  |  | 19 | defined('MOODLE_INTERNAL') || die();
 | 
        
           |  |  | 20 |   | 
        
           |  |  | 21 | global $CFG;
 | 
        
           |  |  | 22 | require_once($CFG->libdir . '/completionlib.php');
 | 
        
           |  |  | 23 |   | 
        
           |  |  | 24 | /**
 | 
        
           |  |  | 25 |  * Unit tests for the completion condition.
 | 
        
           |  |  | 26 |  *
 | 
        
           |  |  | 27 |  * @package availability_completion
 | 
        
           |  |  | 28 |  * @copyright 2014 The Open University
 | 
        
           |  |  | 29 |  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 30 |  */
 | 
        
           | 1441 | ariadna | 31 | final class condition_test extends \advanced_testcase {
 | 
        
           | 1 | efrain | 32 |   | 
        
           |  |  | 33 |     /**
 | 
        
           |  |  | 34 |      * Setup to ensure that fixtures are loaded.
 | 
        
           |  |  | 35 |      */
 | 
        
           |  |  | 36 |     public static function setupBeforeClass(): void {
 | 
        
           |  |  | 37 |         global $CFG;
 | 
        
           |  |  | 38 |         // Load the mock info class so that it can be used.
 | 
        
           |  |  | 39 |         require_once($CFG->dirroot . '/availability/tests/fixtures/mock_info.php');
 | 
        
           |  |  | 40 |         require_once($CFG->dirroot . '/availability/tests/fixtures/mock_info_module.php');
 | 
        
           |  |  | 41 |         require_once($CFG->dirroot . '/availability/tests/fixtures/mock_info_section.php');
 | 
        
           |  |  | 42 |     }
 | 
        
           |  |  | 43 |   | 
        
           |  |  | 44 |     /**
 | 
        
           |  |  | 45 |      * Load required classes.
 | 
        
           |  |  | 46 |      */
 | 
        
           |  |  | 47 |     public function setUp(): void {
 | 
        
           | 1441 | ariadna | 48 |         parent::setUp();
 | 
        
           | 1 | efrain | 49 |         condition::wipe_static_cache();
 | 
        
           |  |  | 50 |     }
 | 
        
           |  |  | 51 |   | 
        
           |  |  | 52 |     /**
 | 
        
           |  |  | 53 |      * Tests constructing and using condition as part of tree.
 | 
        
           |  |  | 54 |      */
 | 
        
           | 11 | efrain | 55 |     public function test_in_tree(): void {
 | 
        
           | 1 | efrain | 56 |         global $USER, $CFG;
 | 
        
           |  |  | 57 |         $this->resetAfterTest();
 | 
        
           |  |  | 58 |   | 
        
           |  |  | 59 |         $this->setAdminUser();
 | 
        
           |  |  | 60 |   | 
        
           |  |  | 61 |         // Create course with completion turned on and a Page.
 | 
        
           |  |  | 62 |         $CFG->enablecompletion = true;
 | 
        
           |  |  | 63 |         $CFG->enableavailability = true;
 | 
        
           |  |  | 64 |         $generator = $this->getDataGenerator();
 | 
        
           |  |  | 65 |         $course = $generator->create_course(['enablecompletion' => 1]);
 | 
        
           |  |  | 66 |         $page = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 67 |                 ['course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 68 |         $selfpage = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 69 |                 ['course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 70 |   | 
        
           |  |  | 71 |         $modinfo = get_fast_modinfo($course);
 | 
        
           |  |  | 72 |         $cm = $modinfo->get_cm($page->cmid);
 | 
        
           |  |  | 73 |         $info = new \core_availability\mock_info($course, $USER->id);
 | 
        
           |  |  | 74 |   | 
        
           |  |  | 75 |         $structure = (object)[
 | 
        
           |  |  | 76 |             'op' => '|',
 | 
        
           |  |  | 77 |             'show' => true,
 | 
        
           |  |  | 78 |             'c' => [
 | 
        
           |  |  | 79 |                 (object)[
 | 
        
           |  |  | 80 |                     'type' => 'completion',
 | 
        
           |  |  | 81 |                     'cm' => (int)$cm->id,
 | 
        
           |  |  | 82 |                     'e' => COMPLETION_COMPLETE
 | 
        
           |  |  | 83 |                 ]
 | 
        
           |  |  | 84 |             ]
 | 
        
           |  |  | 85 |         ];
 | 
        
           |  |  | 86 |         $tree = new \core_availability\tree($structure);
 | 
        
           |  |  | 87 |   | 
        
           |  |  | 88 |         // Initial check (user has not completed activity).
 | 
        
           |  |  | 89 |         $result = $tree->check_available(false, $info, true, $USER->id);
 | 
        
           |  |  | 90 |         $this->assertFalse($result->is_available());
 | 
        
           |  |  | 91 |   | 
        
           |  |  | 92 |         // Mark activity complete.
 | 
        
           |  |  | 93 |         $completion = new \completion_info($course);
 | 
        
           |  |  | 94 |         $completion->update_state($cm, COMPLETION_COMPLETE);
 | 
        
           |  |  | 95 |   | 
        
           |  |  | 96 |         // Now it's true!
 | 
        
           |  |  | 97 |         $result = $tree->check_available(false, $info, true, $USER->id);
 | 
        
           |  |  | 98 |         $this->assertTrue($result->is_available());
 | 
        
           |  |  | 99 |     }
 | 
        
           |  |  | 100 |   | 
        
           |  |  | 101 |     /**
 | 
        
           |  |  | 102 |      * Tests the constructor including error conditions. Also tests the
 | 
        
           |  |  | 103 |      * string conversion feature (intended for debugging only).
 | 
        
           |  |  | 104 |      */
 | 
        
           | 11 | efrain | 105 |     public function test_constructor(): void {
 | 
        
           | 1 | efrain | 106 |         // No parameters.
 | 
        
           |  |  | 107 |         $structure = new \stdClass();
 | 
        
           |  |  | 108 |         try {
 | 
        
           |  |  | 109 |             $cond = new condition($structure);
 | 
        
           |  |  | 110 |             $this->fail();
 | 
        
           |  |  | 111 |         } catch (\coding_exception $e) {
 | 
        
           |  |  | 112 |             $this->assertStringContainsString('Missing or invalid ->cm', $e->getMessage());
 | 
        
           |  |  | 113 |         }
 | 
        
           |  |  | 114 |   | 
        
           |  |  | 115 |         // Invalid $cm.
 | 
        
           |  |  | 116 |         $structure->cm = 'hello';
 | 
        
           |  |  | 117 |         try {
 | 
        
           |  |  | 118 |             $cond = new condition($structure);
 | 
        
           |  |  | 119 |             $this->fail();
 | 
        
           |  |  | 120 |         } catch (\coding_exception $e) {
 | 
        
           |  |  | 121 |             $this->assertStringContainsString('Missing or invalid ->cm', $e->getMessage());
 | 
        
           |  |  | 122 |         }
 | 
        
           |  |  | 123 |   | 
        
           |  |  | 124 |         // Missing $e.
 | 
        
           |  |  | 125 |         $structure->cm = 42;
 | 
        
           |  |  | 126 |         try {
 | 
        
           |  |  | 127 |             $cond = new condition($structure);
 | 
        
           |  |  | 128 |             $this->fail();
 | 
        
           |  |  | 129 |         } catch (\coding_exception $e) {
 | 
        
           |  |  | 130 |             $this->assertStringContainsString('Missing or invalid ->e', $e->getMessage());
 | 
        
           |  |  | 131 |         }
 | 
        
           |  |  | 132 |   | 
        
           |  |  | 133 |         // Invalid $e.
 | 
        
           |  |  | 134 |         $structure->e = 99;
 | 
        
           |  |  | 135 |         try {
 | 
        
           |  |  | 136 |             $cond = new condition($structure);
 | 
        
           |  |  | 137 |             $this->fail();
 | 
        
           |  |  | 138 |         } catch (\coding_exception $e) {
 | 
        
           |  |  | 139 |             $this->assertStringContainsString('Missing or invalid ->e', $e->getMessage());
 | 
        
           |  |  | 140 |         }
 | 
        
           |  |  | 141 |   | 
        
           |  |  | 142 |         // Successful construct & display with all different expected values.
 | 
        
           |  |  | 143 |         $structure->e = COMPLETION_COMPLETE;
 | 
        
           |  |  | 144 |         $cond = new condition($structure);
 | 
        
           |  |  | 145 |         $this->assertEquals('{completion:cm42 COMPLETE}', (string)$cond);
 | 
        
           |  |  | 146 |   | 
        
           |  |  | 147 |         $structure->e = COMPLETION_COMPLETE_PASS;
 | 
        
           |  |  | 148 |         $cond = new condition($structure);
 | 
        
           |  |  | 149 |         $this->assertEquals('{completion:cm42 COMPLETE_PASS}', (string)$cond);
 | 
        
           |  |  | 150 |   | 
        
           |  |  | 151 |         $structure->e = COMPLETION_COMPLETE_FAIL;
 | 
        
           |  |  | 152 |         $cond = new condition($structure);
 | 
        
           |  |  | 153 |         $this->assertEquals('{completion:cm42 COMPLETE_FAIL}', (string)$cond);
 | 
        
           |  |  | 154 |   | 
        
           |  |  | 155 |         $structure->e = COMPLETION_INCOMPLETE;
 | 
        
           |  |  | 156 |         $cond = new condition($structure);
 | 
        
           |  |  | 157 |         $this->assertEquals('{completion:cm42 INCOMPLETE}', (string)$cond);
 | 
        
           |  |  | 158 |   | 
        
           |  |  | 159 |         // Successful contruct with previous activity.
 | 
        
           |  |  | 160 |         $structure->cm = condition::OPTION_PREVIOUS;
 | 
        
           |  |  | 161 |         $cond = new condition($structure);
 | 
        
           |  |  | 162 |         $this->assertEquals('{completion:cmopprevious INCOMPLETE}', (string)$cond);
 | 
        
           |  |  | 163 |   | 
        
           |  |  | 164 |     }
 | 
        
           |  |  | 165 |   | 
        
           |  |  | 166 |     /**
 | 
        
           |  |  | 167 |      * Tests the save() function.
 | 
        
           |  |  | 168 |      */
 | 
        
           | 11 | efrain | 169 |     public function test_save(): void {
 | 
        
           | 1 | efrain | 170 |         $structure = (object)['cm' => 42, 'e' => COMPLETION_COMPLETE];
 | 
        
           |  |  | 171 |         $cond = new condition($structure);
 | 
        
           |  |  | 172 |         $structure->type = 'completion';
 | 
        
           |  |  | 173 |         $this->assertEquals($structure, $cond->save());
 | 
        
           |  |  | 174 |     }
 | 
        
           |  |  | 175 |   | 
        
           |  |  | 176 |     /**
 | 
        
           |  |  | 177 |      * Tests the is_available and get_description functions.
 | 
        
           |  |  | 178 |      */
 | 
        
           | 11 | efrain | 179 |     public function test_usage(): void {
 | 
        
           | 1 | efrain | 180 |         global $CFG, $DB;
 | 
        
           |  |  | 181 |         require_once($CFG->dirroot . '/mod/assign/locallib.php');
 | 
        
           |  |  | 182 |         $this->resetAfterTest();
 | 
        
           |  |  | 183 |   | 
        
           |  |  | 184 |         // Create course with completion turned on.
 | 
        
           |  |  | 185 |         $CFG->enablecompletion = true;
 | 
        
           |  |  | 186 |         $CFG->enableavailability = true;
 | 
        
           |  |  | 187 |         $generator = $this->getDataGenerator();
 | 
        
           |  |  | 188 |         $course = $generator->create_course(['enablecompletion' => 1]);
 | 
        
           |  |  | 189 |         $user = $generator->create_user();
 | 
        
           |  |  | 190 |         $generator->enrol_user($user->id, $course->id);
 | 
        
           |  |  | 191 |         $this->setUser($user);
 | 
        
           |  |  | 192 |   | 
        
           |  |  | 193 |         // Create a Page with manual completion for basic checks.
 | 
        
           |  |  | 194 |         $page = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 195 |                 ['course' => $course->id, 'name' => 'Page!',
 | 
        
           |  |  | 196 |                 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 197 |   | 
        
           |  |  | 198 |         // Create an assignment - we need to have something that can be graded
 | 
        
           |  |  | 199 |         // so as to test the PASS/FAIL states. Set it up to be completed based
 | 
        
           |  |  | 200 |         // on its grade item.
 | 
        
           |  |  | 201 |         $assignrow = $this->getDataGenerator()->create_module('assign', [
 | 
        
           |  |  | 202 |                         'course' => $course->id, 'name' => 'Assign!',
 | 
        
           |  |  | 203 |                         'completion' => COMPLETION_TRACKING_AUTOMATIC]);
 | 
        
           |  |  | 204 |         $DB->set_field('course_modules', 'completiongradeitemnumber', 0,
 | 
        
           |  |  | 205 |                 ['id' => $assignrow->cmid]);
 | 
        
           |  |  | 206 |         // As we manually set the field here, we are going to need to reset the modinfo cache.
 | 
        
           |  |  | 207 |         rebuild_course_cache($course->id, true);
 | 
        
           |  |  | 208 |         $assign = new \assign(\context_module::instance($assignrow->cmid), false, false);
 | 
        
           |  |  | 209 |   | 
        
           |  |  | 210 |         // Get basic details.
 | 
        
           |  |  | 211 |         $modinfo = get_fast_modinfo($course);
 | 
        
           |  |  | 212 |         $pagecm = $modinfo->get_cm($page->cmid);
 | 
        
           |  |  | 213 |         $assigncm = $assign->get_course_module();
 | 
        
           |  |  | 214 |         $info = new \core_availability\mock_info($course, $user->id);
 | 
        
           |  |  | 215 |   | 
        
           |  |  | 216 |         // COMPLETE state (false), positive and NOT.
 | 
        
           |  |  | 217 |         $cond = new condition((object)[
 | 
        
           |  |  | 218 |             'cm' => (int)$pagecm->id, 'e' => COMPLETION_COMPLETE
 | 
        
           |  |  | 219 |         ]);
 | 
        
           |  |  | 220 |         $this->assertFalse($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 221 |         $information = $cond->get_description(false, false, $info);
 | 
        
           |  |  | 222 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 223 |         $this->assertMatchesRegularExpression('~Page!.*is marked complete~', $information);
 | 
        
           |  |  | 224 |         $this->assertTrue($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 225 |   | 
        
           |  |  | 226 |         // INCOMPLETE state (true).
 | 
        
           |  |  | 227 |         $cond = new condition((object)[
 | 
        
           |  |  | 228 |             'cm' => (int)$pagecm->id, 'e' => COMPLETION_INCOMPLETE
 | 
        
           |  |  | 229 |         ]);
 | 
        
           |  |  | 230 |         $this->assertTrue($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 231 |         $this->assertFalse($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 232 |         $information = $cond->get_description(false, true, $info);
 | 
        
           |  |  | 233 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 234 |         $this->assertMatchesRegularExpression('~Page!.*is marked complete~', $information);
 | 
        
           |  |  | 235 |   | 
        
           |  |  | 236 |         // Mark page complete.
 | 
        
           |  |  | 237 |         $completion = new \completion_info($course);
 | 
        
           |  |  | 238 |         $completion->update_state($pagecm, COMPLETION_COMPLETE);
 | 
        
           |  |  | 239 |   | 
        
           |  |  | 240 |         // COMPLETE state (true).
 | 
        
           |  |  | 241 |         $cond = new condition((object)[
 | 
        
           |  |  | 242 |             'cm' => (int)$pagecm->id, 'e' => COMPLETION_COMPLETE
 | 
        
           |  |  | 243 |         ]);
 | 
        
           |  |  | 244 |         $this->assertTrue($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 245 |         $this->assertFalse($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 246 |         $information = $cond->get_description(false, true, $info);
 | 
        
           |  |  | 247 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 248 |         $this->assertMatchesRegularExpression('~Page!.*is incomplete~', $information);
 | 
        
           |  |  | 249 |   | 
        
           |  |  | 250 |         // INCOMPLETE state (false).
 | 
        
           |  |  | 251 |         $cond = new condition((object)[
 | 
        
           |  |  | 252 |             'cm' => (int)$pagecm->id, 'e' => COMPLETION_INCOMPLETE
 | 
        
           |  |  | 253 |         ]);
 | 
        
           |  |  | 254 |         $this->assertFalse($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 255 |         $information = $cond->get_description(false, false, $info);
 | 
        
           |  |  | 256 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 257 |         $this->assertMatchesRegularExpression('~Page!.*is incomplete~', $information);
 | 
        
           |  |  | 258 |         $this->assertTrue($cond->is_available(true, $info,
 | 
        
           |  |  | 259 |                 true, $user->id));
 | 
        
           |  |  | 260 |   | 
        
           |  |  | 261 |         // We are going to need the grade item so that we can get pass/fails.
 | 
        
           |  |  | 262 |         $gradeitem = $assign->get_grade_item();
 | 
        
           |  |  | 263 |         \grade_object::set_properties($gradeitem, ['gradepass' => 50.0]);
 | 
        
           |  |  | 264 |         $gradeitem->update();
 | 
        
           |  |  | 265 |   | 
        
           |  |  | 266 |         // With no grade, it should return true for INCOMPLETE and false for
 | 
        
           |  |  | 267 |         // the other three.
 | 
        
           |  |  | 268 |         $cond = new condition((object)[
 | 
        
           |  |  | 269 |             'cm' => (int)$assigncm->id, 'e' => COMPLETION_INCOMPLETE
 | 
        
           |  |  | 270 |         ]);
 | 
        
           |  |  | 271 |         $this->assertTrue($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 272 |         $this->assertFalse($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 273 |   | 
        
           |  |  | 274 |         $cond = new condition((object)[
 | 
        
           |  |  | 275 |             'cm' => (int)$assigncm->id, 'e' => COMPLETION_COMPLETE
 | 
        
           |  |  | 276 |         ]);
 | 
        
           |  |  | 277 |         $this->assertFalse($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 278 |         $this->assertTrue($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 279 |   | 
        
           |  |  | 280 |         // Check $information for COMPLETE_PASS and _FAIL as we haven't yet.
 | 
        
           |  |  | 281 |         $cond = new condition((object)[
 | 
        
           |  |  | 282 |             'cm' => (int)$assigncm->id, 'e' => COMPLETION_COMPLETE_PASS
 | 
        
           |  |  | 283 |         ]);
 | 
        
           |  |  | 284 |         $this->assertFalse($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 285 |         $information = $cond->get_description(false, false, $info);
 | 
        
           |  |  | 286 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 287 |         $this->assertMatchesRegularExpression('~Assign!.*is complete and passed~', $information);
 | 
        
           |  |  | 288 |         $this->assertTrue($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 289 |   | 
        
           |  |  | 290 |         $cond = new condition((object)[
 | 
        
           |  |  | 291 |             'cm' => (int)$assigncm->id, 'e' => COMPLETION_COMPLETE_FAIL
 | 
        
           |  |  | 292 |         ]);
 | 
        
           |  |  | 293 |         $this->assertFalse($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 294 |         $information = $cond->get_description(false, false, $info);
 | 
        
           |  |  | 295 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 296 |         $this->assertMatchesRegularExpression('~Assign!.*is complete and failed~', $information);
 | 
        
           |  |  | 297 |         $this->assertTrue($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 298 |   | 
        
           |  |  | 299 |         // Change the grade to be complete and failed.
 | 
        
           |  |  | 300 |         self::set_grade($assignrow, $user->id, 40);
 | 
        
           |  |  | 301 |   | 
        
           |  |  | 302 |         $cond = new condition((object)[
 | 
        
           |  |  | 303 |             'cm' => (int)$assigncm->id, 'e' => COMPLETION_INCOMPLETE
 | 
        
           |  |  | 304 |         ]);
 | 
        
           |  |  | 305 |         $this->assertTrue($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 306 |         $this->assertFalse($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 307 |   | 
        
           |  |  | 308 |         $cond = new condition((object)[
 | 
        
           |  |  | 309 |             'cm' => (int)$assigncm->id, 'e' => COMPLETION_COMPLETE
 | 
        
           |  |  | 310 |         ]);
 | 
        
           |  |  | 311 |         $this->assertFalse($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 312 |         $this->assertTrue($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 313 |   | 
        
           |  |  | 314 |         $cond = new condition((object)[
 | 
        
           |  |  | 315 |             'cm' => (int)$assigncm->id, 'e' => COMPLETION_COMPLETE_PASS
 | 
        
           |  |  | 316 |         ]);
 | 
        
           |  |  | 317 |         $this->assertFalse($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 318 |         $information = $cond->get_description(false, false, $info);
 | 
        
           |  |  | 319 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 320 |         $this->assertMatchesRegularExpression('~Assign!.*is complete and passed~', $information);
 | 
        
           |  |  | 321 |         $this->assertTrue($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 322 |   | 
        
           |  |  | 323 |         $cond = new condition((object)[
 | 
        
           |  |  | 324 |             'cm' => (int)$assigncm->id, 'e' => COMPLETION_COMPLETE_FAIL
 | 
        
           |  |  | 325 |         ]);
 | 
        
           |  |  | 326 |         $this->assertTrue($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 327 |         $this->assertFalse($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 328 |         $information = $cond->get_description(false, true, $info);
 | 
        
           |  |  | 329 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 330 |         $this->assertMatchesRegularExpression('~Assign!.*is not complete and failed~', $information);
 | 
        
           |  |  | 331 |   | 
        
           |  |  | 332 |         // Now change it to pass.
 | 
        
           |  |  | 333 |         self::set_grade($assignrow, $user->id, 60);
 | 
        
           |  |  | 334 |   | 
        
           |  |  | 335 |         $cond = new condition((object)[
 | 
        
           |  |  | 336 |             'cm' => (int)$assigncm->id, 'e' => COMPLETION_INCOMPLETE
 | 
        
           |  |  | 337 |         ]);
 | 
        
           |  |  | 338 |         $this->assertFalse($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 339 |         $this->assertTrue($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 340 |   | 
        
           |  |  | 341 |         $cond = new condition((object)[
 | 
        
           |  |  | 342 |             'cm' => (int)$assigncm->id, 'e' => COMPLETION_COMPLETE
 | 
        
           |  |  | 343 |         ]);
 | 
        
           |  |  | 344 |         $this->assertTrue($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 345 |         $this->assertFalse($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 346 |   | 
        
           |  |  | 347 |         $cond = new condition((object)[
 | 
        
           |  |  | 348 |                         'cm' => (int)$assigncm->id, 'e' => COMPLETION_COMPLETE_PASS
 | 
        
           |  |  | 349 |                     ]);
 | 
        
           |  |  | 350 |         $this->assertTrue($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 351 |         $this->assertFalse($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 352 |         $information = $cond->get_description(false, true, $info);
 | 
        
           |  |  | 353 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 354 |         $this->assertMatchesRegularExpression('~Assign!.*is not complete and passed~', $information);
 | 
        
           |  |  | 355 |   | 
        
           |  |  | 356 |         $cond = new condition((object)[
 | 
        
           |  |  | 357 |             'cm' => (int)$assigncm->id, 'e' => COMPLETION_COMPLETE_FAIL
 | 
        
           |  |  | 358 |         ]);
 | 
        
           |  |  | 359 |         $this->assertFalse($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 360 |         $information = $cond->get_description(false, false, $info);
 | 
        
           |  |  | 361 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 362 |         $this->assertMatchesRegularExpression('~Assign!.*is complete and failed~', $information);
 | 
        
           |  |  | 363 |         $this->assertTrue($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 364 |   | 
        
           |  |  | 365 |         // Simulate deletion of an activity by using an invalid cmid. These
 | 
        
           |  |  | 366 |         // conditions always fail, regardless of NOT flag or INCOMPLETE.
 | 
        
           |  |  | 367 |         $cond = new condition((object)[
 | 
        
           |  |  | 368 |             'cm' => ($assigncm->id + 100), 'e' => COMPLETION_COMPLETE
 | 
        
           |  |  | 369 |         ]);
 | 
        
           |  |  | 370 |         $this->assertFalse($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 371 |         $information = $cond->get_description(false, false, $info);
 | 
        
           |  |  | 372 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 373 |         $this->assertMatchesRegularExpression('~(Missing activity).*is marked complete~', $information);
 | 
        
           |  |  | 374 |         $this->assertFalse($cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 375 |         $cond = new condition((object)[
 | 
        
           |  |  | 376 |             'cm' => ($assigncm->id + 100), 'e' => COMPLETION_INCOMPLETE
 | 
        
           |  |  | 377 |         ]);
 | 
        
           |  |  | 378 |         $this->assertFalse($cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 379 |     }
 | 
        
           |  |  | 380 |   | 
        
           |  |  | 381 |     /**
 | 
        
           |  |  | 382 |      * Tests the is_available and get_description functions for previous activity option.
 | 
        
           |  |  | 383 |      *
 | 
        
           |  |  | 384 |      * @dataProvider previous_activity_data
 | 
        
           |  |  | 385 |      * @param int $grade the current assign grade (0 for none)
 | 
        
           |  |  | 386 |      * @param int $condition true for complete, false for incomplete
 | 
        
           |  |  | 387 |      * @param string $mark activity to mark as complete
 | 
        
           |  |  | 388 |      * @param string $activity activity name to test
 | 
        
           |  |  | 389 |      * @param bool $result if it must be available or not
 | 
        
           |  |  | 390 |      * @param bool $resultnot if it must be available when the condition is inverted
 | 
        
           |  |  | 391 |      * @param string $description the availabiklity text to check
 | 
        
           |  |  | 392 |      */
 | 
        
           |  |  | 393 |     public function test_previous_activity(int $grade, int $condition, string $mark, string $activity,
 | 
        
           |  |  | 394 |             bool $result, bool $resultnot, string $description): void {
 | 
        
           |  |  | 395 |         global $CFG, $DB;
 | 
        
           |  |  | 396 |         require_once($CFG->dirroot . '/mod/assign/locallib.php');
 | 
        
           |  |  | 397 |         $this->resetAfterTest();
 | 
        
           |  |  | 398 |   | 
        
           |  |  | 399 |         // Create course with completion turned on.
 | 
        
           |  |  | 400 |         $CFG->enablecompletion = true;
 | 
        
           |  |  | 401 |         $CFG->enableavailability = true;
 | 
        
           |  |  | 402 |         $generator = $this->getDataGenerator();
 | 
        
           |  |  | 403 |         $course = $generator->create_course(['enablecompletion' => 1]);
 | 
        
           |  |  | 404 |         $user = $generator->create_user();
 | 
        
           |  |  | 405 |         $generator->enrol_user($user->id, $course->id);
 | 
        
           |  |  | 406 |         $this->setUser($user);
 | 
        
           |  |  | 407 |   | 
        
           |  |  | 408 |         // Page 1 (manual completion).
 | 
        
           |  |  | 409 |         $page1 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 410 |                 ['course' => $course->id, 'name' => 'Page1!',
 | 
        
           |  |  | 411 |                 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 412 |   | 
        
           |  |  | 413 |         // Page 2 (manual completion).
 | 
        
           |  |  | 414 |         $page2 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 415 |                 ['course' => $course->id, 'name' => 'Page2!',
 | 
        
           |  |  | 416 |                 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 417 |   | 
        
           |  |  | 418 |         // Page ignored (no completion).
 | 
        
           |  |  | 419 |         $pagenocompletion = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 420 |                 ['course' => $course->id, 'name' => 'Page ignored!']);
 | 
        
           |  |  | 421 |   | 
        
           |  |  | 422 |         // Create an assignment - we need to have something that can be graded
 | 
        
           |  |  | 423 |         // so as to test the PASS/FAIL states. Set it up to be completed based
 | 
        
           |  |  | 424 |         // on its grade item.
 | 
        
           |  |  | 425 |         $assignrow = $this->getDataGenerator()->create_module('assign', [
 | 
        
           |  |  | 426 |             'course' => $course->id, 'name' => 'Assign!',
 | 
        
           |  |  | 427 |             'completion' => COMPLETION_TRACKING_AUTOMATIC
 | 
        
           |  |  | 428 |         ]);
 | 
        
           |  |  | 429 |         $DB->set_field('course_modules', 'completiongradeitemnumber', 0,
 | 
        
           |  |  | 430 |                 ['id' => $assignrow->cmid]);
 | 
        
           |  |  | 431 |         $assign = new \assign(\context_module::instance($assignrow->cmid), false, false);
 | 
        
           |  |  | 432 |   | 
        
           |  |  | 433 |         // Page 3 (manual completion).
 | 
        
           |  |  | 434 |         $page3 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 435 |                 ['course' => $course->id, 'name' => 'Page3!',
 | 
        
           |  |  | 436 |                 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 437 |   | 
        
           |  |  | 438 |         // Get basic details.
 | 
        
           |  |  | 439 |         $activities = [];
 | 
        
           |  |  | 440 |         $modinfo = get_fast_modinfo($course);
 | 
        
           |  |  | 441 |         $activities['page1'] = $modinfo->get_cm($page1->cmid);
 | 
        
           |  |  | 442 |         $activities['page2'] = $modinfo->get_cm($page2->cmid);
 | 
        
           |  |  | 443 |         $activities['assign'] = $assign->get_course_module();
 | 
        
           |  |  | 444 |         $activities['page3'] = $modinfo->get_cm($page3->cmid);
 | 
        
           |  |  | 445 |         $prevvalue = condition::OPTION_PREVIOUS;
 | 
        
           |  |  | 446 |   | 
        
           |  |  | 447 |         // Setup gradings and completion.
 | 
        
           |  |  | 448 |         if ($grade) {
 | 
        
           |  |  | 449 |             $gradeitem = $assign->get_grade_item();
 | 
        
           |  |  | 450 |             \grade_object::set_properties($gradeitem, ['gradepass' => 50.0]);
 | 
        
           |  |  | 451 |             $gradeitem->update();
 | 
        
           |  |  | 452 |             self::set_grade($assignrow, $user->id, $grade);
 | 
        
           |  |  | 453 |         }
 | 
        
           |  |  | 454 |         if ($mark) {
 | 
        
           |  |  | 455 |             $completion = new \completion_info($course);
 | 
        
           |  |  | 456 |             $completion->update_state($activities[$mark], COMPLETION_COMPLETE);
 | 
        
           |  |  | 457 |         }
 | 
        
           |  |  | 458 |   | 
        
           |  |  | 459 |         // Set opprevious WITH non existent previous activity.
 | 
        
           |  |  | 460 |         $info = new \core_availability\mock_info_module($user->id, $activities[$activity]);
 | 
        
           |  |  | 461 |         $cond = new condition((object)[
 | 
        
           |  |  | 462 |             'cm' => (int)$prevvalue, 'e' => $condition
 | 
        
           |  |  | 463 |         ]);
 | 
        
           |  |  | 464 |   | 
        
           |  |  | 465 |         // Do the checks.
 | 
        
           |  |  | 466 |         $this->assertEquals($result, $cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 467 |         $this->assertEquals($resultnot, $cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 468 |         $information = $cond->get_description(false, false, $info);
 | 
        
           |  |  | 469 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 470 |         $this->assertMatchesRegularExpression($description, $information);
 | 
        
           |  |  | 471 |     }
 | 
        
           |  |  | 472 |   | 
        
           | 1441 | ariadna | 473 |     public static function previous_activity_data(): array {
 | 
        
           | 1 | efrain | 474 |         // Assign grade, condition, activity to complete, activity to test, result, resultnot, description.
 | 
        
           |  |  | 475 |         return [
 | 
        
           |  |  | 476 |             'Missing previous activity complete' => [
 | 
        
           |  |  | 477 |                 0, COMPLETION_COMPLETE, '', 'page1', false, false, '~Missing activity.*is marked complete~'
 | 
        
           |  |  | 478 |             ],
 | 
        
           |  |  | 479 |             'Missing previous activity incomplete' => [
 | 
        
           |  |  | 480 |                 0, COMPLETION_INCOMPLETE, '', 'page1', false, false, '~Missing activity.*is incomplete~'
 | 
        
           |  |  | 481 |             ],
 | 
        
           |  |  | 482 |             'Previous complete condition with previous activity incompleted' => [
 | 
        
           |  |  | 483 |                 0, COMPLETION_COMPLETE, '', 'page2', false, true, '~Page1!.*is marked complete~'
 | 
        
           |  |  | 484 |             ],
 | 
        
           |  |  | 485 |             'Previous incomplete condition with previous activity incompleted' => [
 | 
        
           |  |  | 486 |                 0, COMPLETION_INCOMPLETE, '', 'page2', true, false, '~Page1!.*is incomplete~'
 | 
        
           |  |  | 487 |             ],
 | 
        
           |  |  | 488 |             'Previous complete condition with previous activity completed' => [
 | 
        
           |  |  | 489 |                 0, COMPLETION_COMPLETE, 'page1', 'page2', true, false, '~Page1!.*is marked complete~'
 | 
        
           |  |  | 490 |             ],
 | 
        
           |  |  | 491 |             'Previous incomplete condition with previous activity completed' => [
 | 
        
           |  |  | 492 |                 0, COMPLETION_INCOMPLETE, 'page1', 'page2', false, true, '~Page1!.*is incomplete~'
 | 
        
           |  |  | 493 |             ],
 | 
        
           |  |  | 494 |             // Depenging on page pass fail (pages are not gradable).
 | 
        
           |  |  | 495 |             'Previous complete pass condition with previous no gradable activity incompleted' => [
 | 
        
           |  |  | 496 |                 0, COMPLETION_COMPLETE_PASS, '', 'page2', false, true, '~Page1!.*is complete and passed~'
 | 
        
           |  |  | 497 |             ],
 | 
        
           |  |  | 498 |             'Previous complete fail condition with previous no gradable activity incompleted' => [
 | 
        
           |  |  | 499 |                 0, COMPLETION_COMPLETE_FAIL, '', 'page2', false, true, '~Page1!.*is complete and failed~'
 | 
        
           |  |  | 500 |             ],
 | 
        
           |  |  | 501 |             'Previous complete pass condition with previous no gradable activity completed' => [
 | 
        
           |  |  | 502 |                 0, COMPLETION_COMPLETE_PASS, 'page1', 'page2', false, true, '~Page1!.*is complete and passed~'
 | 
        
           |  |  | 503 |             ],
 | 
        
           |  |  | 504 |             'Previous complete fail condition with previous no gradable activity completed' => [
 | 
        
           |  |  | 505 |                 0, COMPLETION_COMPLETE_FAIL, 'page1', 'page2', false, true, '~Page1!.*is complete and failed~'
 | 
        
           |  |  | 506 |             ],
 | 
        
           |  |  | 507 |             // There's an page without completion between page2 ans assign.
 | 
        
           |  |  | 508 |             'Previous complete condition with sibling activity incompleted' => [
 | 
        
           |  |  | 509 |                 0, COMPLETION_COMPLETE, '', 'assign', false, true, '~Page2!.*is marked complete~'
 | 
        
           |  |  | 510 |             ],
 | 
        
           |  |  | 511 |             'Previous incomplete condition with sibling activity incompleted' => [
 | 
        
           |  |  | 512 |                 0, COMPLETION_INCOMPLETE, '', 'assign', true, false, '~Page2!.*is incomplete~'
 | 
        
           |  |  | 513 |             ],
 | 
        
           |  |  | 514 |             'Previous complete condition with sibling activity completed' => [
 | 
        
           |  |  | 515 |                 0, COMPLETION_COMPLETE, 'page2', 'assign', true, false, '~Page2!.*is marked complete~'
 | 
        
           |  |  | 516 |             ],
 | 
        
           |  |  | 517 |             'Previous incomplete condition with sibling activity completed' => [
 | 
        
           |  |  | 518 |                 0, COMPLETION_INCOMPLETE, 'page2', 'assign', false, true, '~Page2!.*is incomplete~'
 | 
        
           |  |  | 519 |             ],
 | 
        
           |  |  | 520 |             // Depending on assign without grade.
 | 
        
           |  |  | 521 |             'Previous complete condition with previous without grade' => [
 | 
        
           |  |  | 522 |                 0, COMPLETION_COMPLETE, '', 'page3', false, true, '~Assign!.*is marked complete~'
 | 
        
           |  |  | 523 |             ],
 | 
        
           |  |  | 524 |             'Previous incomplete condition with previous without grade' => [
 | 
        
           |  |  | 525 |                 0, COMPLETION_INCOMPLETE, '', 'page3', true, false, '~Assign!.*is incomplete~'
 | 
        
           |  |  | 526 |             ],
 | 
        
           |  |  | 527 |             'Previous complete pass condition with previous without grade' => [
 | 
        
           |  |  | 528 |                 0, COMPLETION_COMPLETE_PASS, '', 'page3', false, true, '~Assign!.*is complete and passed~'
 | 
        
           |  |  | 529 |             ],
 | 
        
           |  |  | 530 |             'Previous complete fail condition with previous without grade' => [
 | 
        
           |  |  | 531 |                 0, COMPLETION_COMPLETE_FAIL, '', 'page3', false, true, '~Assign!.*is complete and failed~'
 | 
        
           |  |  | 532 |             ],
 | 
        
           |  |  | 533 |             // Depending on assign with grade.
 | 
        
           |  |  | 534 |             'Previous complete condition with previous fail grade' => [
 | 
        
           |  |  | 535 |                 40, COMPLETION_COMPLETE, '', 'page3', false, true, '~Assign!.*is marked complete~',
 | 
        
           |  |  | 536 |             ],
 | 
        
           |  |  | 537 |             'Previous incomplete condition with previous fail grade' => [
 | 
        
           |  |  | 538 |                 40, COMPLETION_INCOMPLETE, '', 'page3', true, false, '~Assign!.*is incomplete~',
 | 
        
           |  |  | 539 |             ],
 | 
        
           |  |  | 540 |             'Previous complete pass condition with previous fail grade' => [
 | 
        
           |  |  | 541 |                 40, COMPLETION_COMPLETE_PASS, '', 'page3', false, true, '~Assign!.*is complete and passed~'
 | 
        
           |  |  | 542 |             ],
 | 
        
           |  |  | 543 |             'Previous complete fail condition with previous fail grade' => [
 | 
        
           |  |  | 544 |                 40, COMPLETION_COMPLETE_FAIL, '', 'page3', true, false, '~Assign!.*is complete and failed~'
 | 
        
           |  |  | 545 |             ],
 | 
        
           |  |  | 546 |             'Previous complete condition with previous pass grade' => [
 | 
        
           |  |  | 547 |                 60, COMPLETION_COMPLETE, '', 'page3', true, false, '~Assign!.*is marked complete~'
 | 
        
           |  |  | 548 |             ],
 | 
        
           |  |  | 549 |             'Previous incomplete condition with previous pass grade' => [
 | 
        
           |  |  | 550 |                 60, COMPLETION_INCOMPLETE, '', 'page3', false, true, '~Assign!.*is incomplete~'
 | 
        
           |  |  | 551 |             ],
 | 
        
           |  |  | 552 |             'Previous complete pass condition with previous pass grade' => [
 | 
        
           |  |  | 553 |                 60, COMPLETION_COMPLETE_PASS, '', 'page3', true, false, '~Assign!.*is complete and passed~'
 | 
        
           |  |  | 554 |             ],
 | 
        
           |  |  | 555 |             'Previous complete fail condition with previous pass grade' => [
 | 
        
           |  |  | 556 |                 60, COMPLETION_COMPLETE_FAIL, '', 'page3', false, true, '~Assign!.*is complete and failed~'
 | 
        
           |  |  | 557 |             ],
 | 
        
           |  |  | 558 |         ];
 | 
        
           |  |  | 559 |     }
 | 
        
           |  |  | 560 |   | 
        
           |  |  | 561 |     /**
 | 
        
           |  |  | 562 |      * Tests the is_available and get_description functions for
 | 
        
           |  |  | 563 |      * previous activity option in course sections.
 | 
        
           |  |  | 564 |      *
 | 
        
           |  |  | 565 |      * @dataProvider section_previous_activity_data
 | 
        
           |  |  | 566 |      * @param int $condition condition value
 | 
        
           |  |  | 567 |      * @param bool $mark if Page 1 must be mark as completed
 | 
        
           |  |  | 568 |      * @param string $section section to add the availability
 | 
        
           |  |  | 569 |      * @param bool $result expected result
 | 
        
           |  |  | 570 |      * @param bool $resultnot expected negated result
 | 
        
           |  |  | 571 |      * @param string $description description to match
 | 
        
           |  |  | 572 |      */
 | 
        
           |  |  | 573 |     public function test_section_previous_activity(int $condition, bool $mark, string $section,
 | 
        
           |  |  | 574 |                 bool $result, bool $resultnot, string $description): void {
 | 
        
           |  |  | 575 |         global $CFG, $DB;
 | 
        
           |  |  | 576 |         require_once($CFG->dirroot . '/mod/assign/locallib.php');
 | 
        
           |  |  | 577 |         $this->resetAfterTest();
 | 
        
           |  |  | 578 |   | 
        
           |  |  | 579 |         // Create course with completion turned on.
 | 
        
           |  |  | 580 |         $CFG->enablecompletion = true;
 | 
        
           |  |  | 581 |         $CFG->enableavailability = true;
 | 
        
           |  |  | 582 |         $generator = $this->getDataGenerator();
 | 
        
           |  |  | 583 |         $course = $generator->create_course(
 | 
        
           |  |  | 584 |                 ['numsections' => 4, 'enablecompletion' => 1],
 | 
        
           |  |  | 585 |                 ['createsections' => true]);
 | 
        
           |  |  | 586 |         $user = $generator->create_user();
 | 
        
           |  |  | 587 |         $generator->enrol_user($user->id, $course->id);
 | 
        
           |  |  | 588 |         $this->setUser($user);
 | 
        
           |  |  | 589 |   | 
        
           |  |  | 590 |         // Section 1 - page1 (manual completion).
 | 
        
           |  |  | 591 |         $page1 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 592 |                 ['course' => $course->id, 'name' => 'Page1!', 'section' => 1,
 | 
        
           |  |  | 593 |                 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 594 |   | 
        
           |  |  | 595 |         // Section 1 - page ignored 1 (no completion).
 | 
        
           |  |  | 596 |         $pagenocompletion1 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 597 |                 ['course' => $course, 'name' => 'Page ignored!', 'section' => 1]);
 | 
        
           |  |  | 598 |   | 
        
           |  |  | 599 |         // Section 2 - page ignored 2 (no completion).
 | 
        
           |  |  | 600 |         $pagenocompletion2 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 601 |                 ['course' => $course, 'name' => 'Page ignored!', 'section' => 2]);
 | 
        
           |  |  | 602 |   | 
        
           |  |  | 603 |         // Section 3 - page2 (manual completion).
 | 
        
           |  |  | 604 |         $page2 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 605 |                 ['course' => $course->id, 'name' => 'Page2!', 'section' => 3,
 | 
        
           |  |  | 606 |                 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 607 |   | 
        
           |  |  | 608 |         // Section 4 is empty.
 | 
        
           |  |  | 609 |   | 
        
           |  |  | 610 |         // Get basic details.
 | 
        
           |  |  | 611 |         get_fast_modinfo(0, 0, true);
 | 
        
           |  |  | 612 |         $modinfo = get_fast_modinfo($course);
 | 
        
           |  |  | 613 |         $sections['section1'] = $modinfo->get_section_info(1);
 | 
        
           |  |  | 614 |         $sections['section2'] = $modinfo->get_section_info(2);
 | 
        
           |  |  | 615 |         $sections['section3'] = $modinfo->get_section_info(3);
 | 
        
           |  |  | 616 |         $sections['section4'] = $modinfo->get_section_info(4);
 | 
        
           |  |  | 617 |         $page1cm = $modinfo->get_cm($page1->cmid);
 | 
        
           |  |  | 618 |         $prevvalue = condition::OPTION_PREVIOUS;
 | 
        
           |  |  | 619 |   | 
        
           |  |  | 620 |         if ($mark) {
 | 
        
           |  |  | 621 |             // Mark page1 complete.
 | 
        
           |  |  | 622 |             $completion = new \completion_info($course);
 | 
        
           |  |  | 623 |             $completion->update_state($page1cm, COMPLETION_COMPLETE);
 | 
        
           |  |  | 624 |         }
 | 
        
           |  |  | 625 |   | 
        
           |  |  | 626 |         $info = new \core_availability\mock_info_section($user->id, $sections[$section]);
 | 
        
           |  |  | 627 |         $cond = new condition((object)[
 | 
        
           |  |  | 628 |             'cm' => (int)$prevvalue, 'e' => $condition
 | 
        
           |  |  | 629 |         ]);
 | 
        
           |  |  | 630 |         $this->assertEquals($result, $cond->is_available(false, $info, true, $user->id));
 | 
        
           |  |  | 631 |         $this->assertEquals($resultnot, $cond->is_available(true, $info, true, $user->id));
 | 
        
           |  |  | 632 |         $information = $cond->get_description(false, false, $info);
 | 
        
           |  |  | 633 |         $information = \core_availability\info::format_info($information, $course);
 | 
        
           |  |  | 634 |         $this->assertMatchesRegularExpression($description, $information);
 | 
        
           |  |  | 635 |   | 
        
           |  |  | 636 |     }
 | 
        
           |  |  | 637 |   | 
        
           | 1441 | ariadna | 638 |     public static function section_previous_activity_data(): array {
 | 
        
           | 1 | efrain | 639 |         return [
 | 
        
           |  |  | 640 |             // Condition, Activity completion, section to test, result, resultnot, description.
 | 
        
           |  |  | 641 |             'Completion complete Section with no previous activity' => [
 | 
        
           |  |  | 642 |                 COMPLETION_COMPLETE, false, 'section1', false, false, '~Missing activity.*is marked complete~'
 | 
        
           |  |  | 643 |             ],
 | 
        
           |  |  | 644 |             'Completion incomplete Section with no previous activity' => [
 | 
        
           |  |  | 645 |                 COMPLETION_INCOMPLETE, false, 'section1', false, false, '~Missing activity.*is incomplete~'
 | 
        
           |  |  | 646 |             ],
 | 
        
           |  |  | 647 |             // Section 2 depending on section 1 -> Page 1 (no grading).
 | 
        
           |  |  | 648 |             'Completion complete Section with previous activity incompleted' => [
 | 
        
           |  |  | 649 |                 COMPLETION_COMPLETE, false, 'section2', false, true, '~Page1!.*is marked complete~'
 | 
        
           |  |  | 650 |             ],
 | 
        
           |  |  | 651 |             'Completion incomplete Section with previous activity incompleted' => [
 | 
        
           |  |  | 652 |                 COMPLETION_INCOMPLETE, false, 'section2', true, false, '~Page1!.*is incomplete~'
 | 
        
           |  |  | 653 |             ],
 | 
        
           |  |  | 654 |             'Completion complete Section with previous activity completed' => [
 | 
        
           |  |  | 655 |                 COMPLETION_COMPLETE, true, 'section2', true, false, '~Page1!.*is marked complete~'
 | 
        
           |  |  | 656 |             ],
 | 
        
           |  |  | 657 |             'Completion incomplete Section with previous activity completed' => [
 | 
        
           |  |  | 658 |                 COMPLETION_INCOMPLETE, true, 'section2', false, true, '~Page1!.*is incomplete~'
 | 
        
           |  |  | 659 |             ],
 | 
        
           |  |  | 660 |             // Section 3 depending on section 1 -> Page 1 (no grading).
 | 
        
           |  |  | 661 |             'Completion complete Section ignoring empty sections and activity incompleted' => [
 | 
        
           |  |  | 662 |                 COMPLETION_COMPLETE, false, 'section3', false, true, '~Page1!.*is marked complete~'
 | 
        
           |  |  | 663 |             ],
 | 
        
           |  |  | 664 |             'Completion incomplete Section ignoring empty sections and activity incompleted' => [
 | 
        
           |  |  | 665 |                 COMPLETION_INCOMPLETE, false, 'section3', true, false, '~Page1!.*is incomplete~'
 | 
        
           |  |  | 666 |             ],
 | 
        
           |  |  | 667 |             'Completion complete Section ignoring empty sections and activity completed' => [
 | 
        
           |  |  | 668 |                 COMPLETION_COMPLETE, true, 'section3', true, false, '~Page1!.*is marked complete~'
 | 
        
           |  |  | 669 |             ],
 | 
        
           |  |  | 670 |             'Completion incomplete Section ignoring empty sections and activity completed' => [
 | 
        
           |  |  | 671 |                 COMPLETION_INCOMPLETE, true, 'section3', false, true, '~Page1!.*is incomplete~'
 | 
        
           |  |  | 672 |             ],
 | 
        
           |  |  | 673 |             // Section 4 depending on section 3 -> Page 2 (no grading).
 | 
        
           |  |  | 674 |             'Completion complete Last section with previous activity incompleted' => [
 | 
        
           |  |  | 675 |                 COMPLETION_COMPLETE, false, 'section4', false, true, '~Page2!.*is marked complete~'
 | 
        
           |  |  | 676 |             ],
 | 
        
           |  |  | 677 |             'Completion incomplete Last section with previous activity incompleted' => [
 | 
        
           |  |  | 678 |                 COMPLETION_INCOMPLETE, false, 'section4', true, false, '~Page2!.*is incomplete~'
 | 
        
           |  |  | 679 |             ],
 | 
        
           |  |  | 680 |             'Completion complete Last section with previous activity completed' => [
 | 
        
           |  |  | 681 |                 COMPLETION_COMPLETE, true, 'section4', false, true, '~Page2!.*is marked complete~'
 | 
        
           |  |  | 682 |             ],
 | 
        
           |  |  | 683 |             'Completion incomplete Last section with previous activity completed' => [
 | 
        
           |  |  | 684 |                 COMPLETION_INCOMPLETE, true, 'section4', true, false, '~Page2!.*is incomplete~'
 | 
        
           |  |  | 685 |             ],
 | 
        
           |  |  | 686 |         ];
 | 
        
           |  |  | 687 |     }
 | 
        
           |  |  | 688 |   | 
        
           |  |  | 689 |     /**
 | 
        
           |  |  | 690 |      * Tests completion_value_used static function.
 | 
        
           |  |  | 691 |      */
 | 
        
           | 11 | efrain | 692 |     public function test_completion_value_used(): void {
 | 
        
           | 1 | efrain | 693 |         global $CFG, $DB;
 | 
        
           |  |  | 694 |         $this->resetAfterTest();
 | 
        
           |  |  | 695 |         $prevvalue = condition::OPTION_PREVIOUS;
 | 
        
           |  |  | 696 |   | 
        
           |  |  | 697 |         // Create course with completion turned on and some sections.
 | 
        
           |  |  | 698 |         $CFG->enablecompletion = true;
 | 
        
           |  |  | 699 |         $CFG->enableavailability = true;
 | 
        
           |  |  | 700 |         $generator = $this->getDataGenerator();
 | 
        
           |  |  | 701 |         $course = $generator->create_course(
 | 
        
           |  |  | 702 |                 ['numsections' => 1, 'enablecompletion' => 1],
 | 
        
           |  |  | 703 |                 ['createsections' => true]);
 | 
        
           |  |  | 704 |   | 
        
           |  |  | 705 |         // Create six pages with manual completion.
 | 
        
           |  |  | 706 |         $page1 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 707 |                 ['course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 708 |         $page2 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 709 |                 ['course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 710 |         $page3 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 711 |                 ['course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 712 |         $page4 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 713 |                 ['course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 714 |         $page5 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 715 |                 ['course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 716 |         $page6 = $generator->get_plugin_generator('mod_page')->create_instance(
 | 
        
           |  |  | 717 |                 ['course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL]);
 | 
        
           |  |  | 718 |   | 
        
           |  |  | 719 |         // Set up page3 to depend on page1, and section1 to depend on page2.
 | 
        
           |  |  | 720 |         $DB->set_field('course_modules', 'availability',
 | 
        
           |  |  | 721 |                 '{"op":"|","show":true,"c":[' .
 | 
        
           |  |  | 722 |                 '{"type":"completion","e":1,"cm":' . $page1->cmid . '}]}',
 | 
        
           |  |  | 723 |                 ['id' => $page3->cmid]);
 | 
        
           |  |  | 724 |         $DB->set_field('course_sections', 'availability',
 | 
        
           |  |  | 725 |                 '{"op":"|","show":true,"c":[' .
 | 
        
           |  |  | 726 |                 '{"type":"completion","e":1,"cm":' . $page2->cmid . '}]}',
 | 
        
           |  |  | 727 |                 ['course' => $course->id, 'section' => 1]);
 | 
        
           |  |  | 728 |         // Set up page5 and page6 to depend on previous activity.
 | 
        
           |  |  | 729 |         $DB->set_field('course_modules', 'availability',
 | 
        
           |  |  | 730 |                 '{"op":"|","show":true,"c":[' .
 | 
        
           |  |  | 731 |                 '{"type":"completion","e":1,"cm":' . $prevvalue . '}]}',
 | 
        
           |  |  | 732 |                 ['id' => $page5->cmid]);
 | 
        
           |  |  | 733 |         $DB->set_field('course_modules', 'availability',
 | 
        
           |  |  | 734 |                 '{"op":"|","show":true,"c":[' .
 | 
        
           |  |  | 735 |                 '{"type":"completion","e":1,"cm":' . $prevvalue . '}]}',
 | 
        
           |  |  | 736 |                 ['id' => $page6->cmid]);
 | 
        
           |  |  | 737 |   | 
        
           |  |  | 738 |         // Check 1: nothing depends on page3 and page6 but something does on the others.
 | 
        
           |  |  | 739 |         $this->assertTrue(condition::completion_value_used(
 | 
        
           |  |  | 740 |                 $course, $page1->cmid));
 | 
        
           |  |  | 741 |         $this->assertTrue(condition::completion_value_used(
 | 
        
           |  |  | 742 |                 $course, $page2->cmid));
 | 
        
           |  |  | 743 |         $this->assertFalse(condition::completion_value_used(
 | 
        
           |  |  | 744 |                 $course, $page3->cmid));
 | 
        
           |  |  | 745 |         $this->assertTrue(condition::completion_value_used(
 | 
        
           |  |  | 746 |                 $course, $page4->cmid));
 | 
        
           |  |  | 747 |         $this->assertTrue(condition::completion_value_used(
 | 
        
           |  |  | 748 |                 $course, $page5->cmid));
 | 
        
           |  |  | 749 |         $this->assertFalse(condition::completion_value_used(
 | 
        
           |  |  | 750 |                 $course, $page6->cmid));
 | 
        
           |  |  | 751 |     }
 | 
        
           |  |  | 752 |   | 
        
           |  |  | 753 |     /**
 | 
        
           |  |  | 754 |      * Updates the grade of a user in the given assign module instance.
 | 
        
           |  |  | 755 |      *
 | 
        
           |  |  | 756 |      * @param \stdClass $assignrow Assignment row from database
 | 
        
           |  |  | 757 |      * @param int $userid User id
 | 
        
           |  |  | 758 |      * @param float $grade Grade
 | 
        
           |  |  | 759 |      */
 | 
        
           |  |  | 760 |     protected static function set_grade($assignrow, $userid, $grade) {
 | 
        
           |  |  | 761 |         $grades = [];
 | 
        
           |  |  | 762 |         $grades[$userid] = (object)[
 | 
        
           |  |  | 763 |             'rawgrade' => $grade, 'userid' => $userid
 | 
        
           |  |  | 764 |         ];
 | 
        
           |  |  | 765 |         $assignrow->cmidnumber = null;
 | 
        
           |  |  | 766 |         assign_grade_item_update($assignrow, $grades);
 | 
        
           |  |  | 767 |     }
 | 
        
           |  |  | 768 |   | 
        
           |  |  | 769 |     /**
 | 
        
           |  |  | 770 |      * Tests the update_dependency_id() function.
 | 
        
           |  |  | 771 |      */
 | 
        
           | 11 | efrain | 772 |     public function test_update_dependency_id(): void {
 | 
        
           | 1 | efrain | 773 |         $cond = new condition((object)[
 | 
        
           |  |  | 774 |             'cm' => 42, 'e' => COMPLETION_COMPLETE, 'selfid' => 43
 | 
        
           |  |  | 775 |         ]);
 | 
        
           |  |  | 776 |         $this->assertFalse($cond->update_dependency_id('frogs', 42, 540));
 | 
        
           |  |  | 777 |         $this->assertFalse($cond->update_dependency_id('course_modules', 12, 34));
 | 
        
           |  |  | 778 |         $this->assertTrue($cond->update_dependency_id('course_modules', 42, 456));
 | 
        
           |  |  | 779 |         $after = $cond->save();
 | 
        
           |  |  | 780 |         $this->assertEquals(456, $after->cm);
 | 
        
           |  |  | 781 |   | 
        
           |  |  | 782 |         // Test selfid updating.
 | 
        
           |  |  | 783 |         $cond = new condition((object)[
 | 
        
           |  |  | 784 |             'cm' => 42, 'e' => COMPLETION_COMPLETE
 | 
        
           |  |  | 785 |         ]);
 | 
        
           |  |  | 786 |         $this->assertFalse($cond->update_dependency_id('frogs', 43, 540));
 | 
        
           |  |  | 787 |         $this->assertFalse($cond->update_dependency_id('course_modules', 12, 34));
 | 
        
           |  |  | 788 |         $after = $cond->save();
 | 
        
           |  |  | 789 |         $this->assertEquals(42, $after->cm);
 | 
        
           |  |  | 790 |   | 
        
           |  |  | 791 |         // Test on previous activity.
 | 
        
           |  |  | 792 |         $cond = new condition((object)[
 | 
        
           |  |  | 793 |             'cm' => condition::OPTION_PREVIOUS,
 | 
        
           |  |  | 794 |             'e' => COMPLETION_COMPLETE
 | 
        
           |  |  | 795 |         ]);
 | 
        
           |  |  | 796 |         $this->assertFalse($cond->update_dependency_id('frogs', 43, 80));
 | 
        
           |  |  | 797 |         $this->assertFalse($cond->update_dependency_id('course_modules', 12, 34));
 | 
        
           |  |  | 798 |         $after = $cond->save();
 | 
        
           |  |  | 799 |         $this->assertEquals(condition::OPTION_PREVIOUS, $after->cm);
 | 
        
           |  |  | 800 |     }
 | 
        
           |  |  | 801 | }
 |