See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
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 core_course; 18 19 defined('MOODLE_INTERNAL') || die(); 20 global $CFG; 21 22 require_once($CFG->dirroot . '/completion/criteria/completion_criteria.php'); 23 require_once($CFG->dirroot . '/completion/criteria/completion_criteria_activity.php'); 24 require_once($CFG->dirroot . '/lib/grade/grade_item.php'); 25 require_once($CFG->dirroot . '/lib/grade/grade_grade.php'); 26 require_once($CFG->dirroot . '/lib/grade/grade_category.php'); 27 require_once($CFG->dirroot . '/lib/grade/constants.php'); 28 29 /** 30 * Unit tests for core targets. 31 * 32 * @package core_course 33 * @copyright 2019 Victor Deniz <victor@moodle.com> 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 class targets_test extends \advanced_testcase { 37 38 /** 39 * Provides course params for the {@link self::test_core_target_course_completion_analysable()} method. 40 * 41 * @return array 42 */ 43 public function analysable_provider() { 44 45 $now = new \DateTime("now", \core_date::get_server_timezone_object()); 46 $year = $now->format('Y'); 47 $month = $now->format('m'); 48 49 return [ 50 'coursenotyetstarted' => [ 51 'params' => [ 52 'enablecompletion' => 1, 53 'startdate' => mktime(0, 0, 0, 10, 24, $year + 1) 54 ], 55 'isvalid' => get_string('coursenotyetstarted', 'course') 56 ], 57 'coursenostudents' => [ 58 'params' => [ 59 'enablecompletion' => 1, 60 'startdate' => mktime(0, 0, 0, 10, 24, $year - 2), 61 'enddate' => mktime(0, 0, 0, 10, 24, $year - 1) 62 ], 63 'isvalid' => get_string('nocoursestudents', 'course') 64 ], 65 'coursenosections' => [ 66 'params' => [ 67 'enablecompletion' => 1, 68 'format' => 'social', 69 'students' => true 70 ], 71 'isvalid' => get_string('nocoursesections', 'course') 72 ], 73 'coursenoendtime' => [ 74 'params' => [ 75 'enablecompletion' => 1, 76 'format' => 'topics', 77 'enddate' => 0, 78 'students' => true 79 ], 80 'isvalid' => get_string('nocourseendtime', 'course') 81 ], 82 'courseendbeforestart' => [ 83 'params' => [ 84 'enablecompletion' => 1, 85 'enddate' => mktime(0, 0, 0, 10, 23, $year - 2), 86 'students' => true 87 ], 88 'isvalid' => get_string('errorendbeforestart', 'course') 89 ], 90 'coursetoolong' => [ 91 'params' => [ 92 'enablecompletion' => 1, 93 'startdate' => mktime(0, 0, 0, 10, 24, $year - 2), 94 'enddate' => mktime(0, 0, 0, 10, 23, $year), 95 'students' => true 96 ], 97 'isvalid' => get_string('coursetoolong', 'course') 98 ], 99 'coursealreadyfinished' => [ 100 'params' => [ 101 'enablecompletion' => 1, 102 'startdate' => mktime(0, 0, 0, 10, 24, $year - 2), 103 'enddate' => mktime(0, 0, 0, 10, 23, $year - 1), 104 'students' => true 105 ], 106 'isvalid' => get_string('coursealreadyfinished', 'course'), 107 'fortraining' => false 108 ], 109 'coursenotyetfinished' => [ 110 'params' => [ 111 'enablecompletion' => 1, 112 'startdate' => mktime(0, 0, 0, $month - 1, 24, $year), 113 'enddate' => mktime(0, 0, 0, $month + 2, 23, $year), 114 'students' => true 115 ], 116 'isvalid' => get_string('coursenotyetfinished', 'course') 117 ], 118 'coursenocompletion' => [ 119 'params' => [ 120 'enablecompletion' => 0, 121 'startdate' => mktime(0, 0, 0, $month - 2, 24, $year), 122 'enddate' => mktime(0, 0, 0, $month - 1, 23, $year), 123 'students' => true 124 ], 125 'isvalid' => get_string('completionnotenabledforcourse', 'completion') 126 ], 127 'coursehiddentraining' => [ 128 'params' => [ 129 'enablecompletion' => 1, 130 'startdate' => mktime(0, 0, 0, $month - 1, 24, $year - 1), 131 'enddate' => mktime(0, 0, 0, $month - 1, 23, $year), 132 'students' => true, 133 'visible' => '0', 134 ], 135 'isvalid' => true, 136 ], 137 'coursehiddenprediction' => [ 138 'params' => [ 139 'enablecompletion' => 1, 140 'startdate' => mktime(0, 0, 0, $month - 1, 24, $year), 141 'enddate' => mktime(0, 0, 0, $month - 1, 23, $year + 1), 142 'students' => true, 143 'visible' => '0', 144 ], 145 'isvalid' => get_string('hiddenfromstudents'), 146 'fortraining' => false 147 ], 148 ]; 149 } 150 151 /** 152 * Provides enrolment params for the {@link self::test_core_target_course_completion_samples()} method. 153 * 154 * @return array 155 */ 156 public function sample_provider() { 157 $now = time(); 158 return [ 159 'enrolmentendbeforecourse' => [ 160 'coursestart' => $now, 161 'courseend' => $now + (WEEKSECS * 8), 162 'timestart' => $now, 163 'timeend' => $now - DAYSECS, 164 'isvalidfortraining' => false, 165 'isvalidforprediction' => false 166 ], 167 'enrolmenttoolong' => [ 168 'coursestart' => $now, 169 'courseend' => $now + (WEEKSECS * 8), 170 'timestart' => $now - (YEARSECS + (WEEKSECS * 8)), 171 'timeend' => $now + (WEEKSECS * 8), 172 'isvalidfortraining' => false, 173 'isvalidforprediction' => false 174 ], 175 'enrolmentstartaftercourse' => [ 176 'coursestart' => $now, 177 'courseend' => $now + (WEEKSECS * 8), 178 'timestart' => $now + (WEEKSECS * 9), 179 'timeend' => $now + (WEEKSECS * 10), 180 'isvalidfortraining' => false, 181 'isvalidforprediction' => false 182 ], 183 'enrolmentstartsafternow' => [ 184 'coursestart' => $now, 185 'courseend' => $now + (WEEKSECS * 8), 186 'timestart' => $now + (WEEKSECS * 2), 187 'timeend' => $now + (WEEKSECS * 7), 188 'isvalidfortraining' => false, 189 'isvalidforprediction' => false 190 ], 191 'enrolmentfinishedbeforenow' => [ 192 'coursestart' => $now - (WEEKSECS * 4), 193 'courseend' => $now - (WEEKSECS * 1), 194 'timestart' => $now - (WEEKSECS * 3), 195 'timeend' => $now - (WEEKSECS * 2), 196 'isvalidfortraining' => true, 197 'isvalidforprediction' => false 198 ], 199 ]; 200 } 201 202 /** 203 * Provides enrolment params for the {@link self::test_core_target_course_completion_samples()} method. 204 * 205 * @return array 206 */ 207 public function active_during_analysis_time_provider() { 208 $now = time(); 209 210 return [ 211 'enrol-after-end' => [ 212 'starttime' => $now, 213 'endtime' => $now + WEEKSECS, 214 'timestart' => $now + (WEEKSECS * 2), 215 'timeend' => $now + (WEEKSECS * 3), 216 'nullcalculation' => true, 217 ], 218 'enrol-before-start' => [ 219 'starttime' => $now + (WEEKSECS * 2), 220 'endtime' => $now + (WEEKSECS * 3), 221 'timestart' => $now, 222 'timeend' => $now + WEEKSECS, 223 'nullcalculation' => true, 224 ], 225 'enrol-active-exact-match' => [ 226 'starttime' => $now, 227 'endtime' => $now + (WEEKSECS * 1), 228 'timestart' => $now, 229 'timeend' => $now + (WEEKSECS * 1), 230 'nullcalculation' => false, 231 ], 232 'enrol-active' => [ 233 'starttime' => $now + WEEKSECS, 234 'endtime' => $now + (WEEKSECS * 2), 235 'timestart' => $now, 236 'timeend' => $now + (WEEKSECS * 3), 237 'nullcalculation' => false, 238 ], 239 'enrol-during-analysis-active-just-for-a-while' => [ 240 'starttime' => $now, 241 'endtime' => $now + (WEEKSECS * 10), 242 'timestart' => $now + WEEKSECS, 243 'timeend' => $now + (WEEKSECS * 2), 244 'nullcalculation' => true, 245 ], 246 'enrol-during-analysis-mostly-active' => [ 247 'starttime' => $now, 248 'endtime' => $now + (WEEKSECS * 20), 249 'timestart' => $now + WEEKSECS, 250 'timeend' => $now + (WEEKSECS * 19), 251 'nullcalculation' => false, 252 ], 253 'enrol-partly-active-starts-before' => [ 254 'starttime' => $now + WEEKSECS, 255 'endtime' => $now + (WEEKSECS * 10), 256 'timestart' => $now, 257 'timeend' => $now + (WEEKSECS * 2), 258 'nullcalculation' => true, 259 ], 260 'enrol-mostly-active-starts-before' => [ 261 'starttime' => $now + WEEKSECS, 262 'endtime' => $now + (WEEKSECS * 10), 263 'timestart' => $now, 264 'timeend' => $now + (WEEKSECS * 9), 265 'nullcalculation' => false, 266 ], 267 'enrol-partly-active-ends-afterwards' => [ 268 'starttime' => $now, 269 'endtime' => $now + (WEEKSECS * 10), 270 'timestart' => $now + (WEEKSECS * 9), 271 'timeend' => $now + (WEEKSECS * 11), 272 'nullcalculation' => true, 273 ], 274 'enrol-mostly-active-ends-afterwards' => [ 275 'starttime' => $now, 276 'endtime' => $now + (WEEKSECS * 10), 277 'timestart' => $now + WEEKSECS, 278 'timeend' => $now + (WEEKSECS * 11), 279 'nullcalculation' => false, 280 ], 281 'enrol-partly-active-no-enrolment-end' => [ 282 'starttime' => $now, 283 'endtime' => $now + (WEEKSECS * 10), 284 'timestart' => $now + (WEEKSECS * 9), 285 'timeend' => false, 286 'nullcalculation' => true, 287 ], 288 'enrol-mostly-active-no-enrolment-end-false' => [ 289 'starttime' => $now, 290 'endtime' => $now + (WEEKSECS * 10), 291 'timestart' => $now + WEEKSECS, 292 'timeend' => false, 293 'nullcalculation' => false, 294 ], 295 'enrol-mostly-active-no-enrolment-end-zero' => [ 296 'starttime' => $now, 297 'endtime' => $now + (WEEKSECS * 10), 298 'timestart' => $now + WEEKSECS, 299 'timeend' => 0, 300 'nullcalculation' => false, 301 ], 302 'enrol-no-enrolment-start-false' => [ 303 'starttime' => $now, 304 'endtime' => $now + (WEEKSECS * 10), 305 'timestart' => false, 306 'timeend' => $now + (WEEKSECS * 9), 307 'nullcalculation' => false, 308 ], 309 'enrol-no-enrolment-start-zero' => [ 310 'starttime' => $now, 311 'endtime' => $now + (WEEKSECS * 10), 312 'timestart' => 0, 313 'timeend' => $now + (WEEKSECS * 9), 314 'nullcalculation' => false, 315 ], 316 'no-start' => [ 317 'starttime' => 0, 318 'endtime' => $now + (WEEKSECS * 2), 319 'timestart' => $now + WEEKSECS, 320 'timeend' => $now + (WEEKSECS * 3), 321 'nullcalculation' => false, 322 ], 323 'no-end' => [ 324 'starttime' => $now, 325 'endtime' => 0, 326 'timestart' => $now + (WEEKSECS * 2), 327 'timeend' => $now + (WEEKSECS * 3), 328 'nullcalculation' => false, 329 ] 330 ]; 331 } 332 333 /** 334 * Test the conditions of a valid analysable, both common and specific to this target (course_completion). 335 * 336 * @dataProvider analysable_provider 337 * @param mixed $courseparams Course data 338 * @param true|string $isvalid True when analysable is valid, string when it is not 339 * @param boolean $fortraining True if the course is for training the model 340 */ 341 public function test_core_target_course_completion_analysable($courseparams, $isvalid, $fortraining = true) { 342 global $DB; 343 344 $this->resetAfterTest(true); 345 346 try { 347 $course = $this->getDataGenerator()->create_course($courseparams); 348 } catch (\moodle_exception $e) { 349 $course = $this->getDataGenerator()->create_course(); 350 $courserecord = $courseparams; 351 $courserecord['id'] = $course->id; 352 unset($courserecord['students']); 353 354 $DB->update_record_raw('course', $courserecord); 355 $course = get_course($course->id); 356 } 357 $user = $this->getDataGenerator()->create_user(); 358 359 if (!empty($courseparams['enablecompletion'])) { 360 $assign = $this->getDataGenerator()->create_module('assign', ['course' => $course->id, 'completion' => 1]); 361 $cm = get_coursemodule_from_id('assign', $assign->cmid); 362 363 $criteriadata = (object) [ 364 'id' => $course->id, 365 'criteria_activity' => [ 366 $cm->id => 1 367 ] 368 ]; 369 $criterion = new \completion_criteria_activity(); 370 $criterion->update_config($criteriadata); 371 } 372 373 $target = new \core_course\analytics\target\course_completion(); 374 375 // Test valid analysables. 376 377 if (!empty($courseparams['students'])) { 378 // Enroll user in course. 379 $this->getDataGenerator()->enrol_user($user->id, $course->id); 380 } 381 382 $analysable = new \core_analytics\course($course); 383 $this->assertEquals($isvalid, $target->is_valid_analysable($analysable, $fortraining)); 384 } 385 386 /** 387 * Test the conditions of a valid sample, both common and specific to this target (course_completion). 388 * 389 * @dataProvider sample_provider 390 * @param int $coursestart Course start date 391 * @param int $courseend Course end date 392 * @param int $timestart Enrol start date 393 * @param int $timeend Enrol end date 394 * @param boolean $isvalidfortraining True when sample is valid for training, false when it is not 395 * @param boolean $isvalidforprediction True when sample is valid for prediction, false when it is not 396 */ 397 public function test_core_target_course_completion_samples($coursestart, $courseend, $timestart, $timeend, 398 $isvalidfortraining, $isvalidforprediction) { 399 400 $this->resetAfterTest(true); 401 402 $courserecord = new \stdClass(); 403 $courserecord->startdate = $coursestart; 404 $courserecord->enddate = $courseend; 405 406 $user = $this->getDataGenerator()->create_user(); 407 $course = $this->getDataGenerator()->create_course($courserecord); 408 $this->getDataGenerator()->enrol_user($user->id, $course->id, null, 'manual', $timestart, $timeend); 409 410 $target = new \core_course\analytics\target\course_completion(); 411 $analyser = new \core\analytics\analyser\student_enrolments(1, $target, [], [], []); 412 $analysable = new \core_analytics\course($course); 413 414 $class = new \ReflectionClass('\core\analytics\analyser\student_enrolments'); 415 $method = $class->getMethod('get_all_samples'); 416 $method->setAccessible(true); 417 418 list($sampleids, $samplesdata) = $method->invoke($analyser, $analysable); 419 $target->add_sample_data($samplesdata); 420 $sampleid = reset($sampleids); 421 422 $this->assertEquals($isvalidfortraining, $target->is_valid_sample($sampleid, $analysable, true)); 423 $this->assertEquals($isvalidforprediction, $target->is_valid_sample($sampleid, $analysable, false)); 424 } 425 426 /** 427 * Test the conditions of a valid calculation (course_completion). 428 * 429 * @dataProvider active_during_analysis_time_provider 430 * @param int $starttime Analysis start time 431 * @param int $endtime Analysis end time 432 * @param int $timestart Enrol start date 433 * @param int $timeend Enrol end date 434 * @param boolean $nullcalculation Whether the calculation should be null or not 435 */ 436 public function test_core_target_course_completion_active_during_analysis_time($starttime, $endtime, $timestart, $timeend, 437 $nullcalculation) { 438 439 $this->resetAfterTest(true); 440 441 $user = $this->getDataGenerator()->create_user(); 442 $course = $this->getDataGenerator()->create_course(); 443 $this->getDataGenerator()->enrol_user($user->id, $course->id, null, 'manual', $timestart, $timeend); 444 445 $target = new \core_course\analytics\target\course_completion(); 446 $analyser = new \core\analytics\analyser\student_enrolments(1, $target, [], [], []); 447 $analysable = new \core_analytics\course($course); 448 449 $class = new \ReflectionClass('\core\analytics\analyser\student_enrolments'); 450 $method = $class->getMethod('get_all_samples'); 451 $method->setAccessible(true); 452 453 list($sampleids, $samplesdata) = $method->invoke($analyser, $analysable); 454 $target->add_sample_data($samplesdata); 455 $sampleid = reset($sampleids); 456 457 $reftarget = new \ReflectionObject($target); 458 $refmethod = $reftarget->getMethod('calculate_sample'); 459 $refmethod->setAccessible(true); 460 461 if ($nullcalculation) { 462 $this->assertNull($refmethod->invoke($target, $sampleid, $analysable, $starttime, $endtime)); 463 } else { 464 $this->assertNotNull($refmethod->invoke($target, $sampleid, $analysable, $starttime, $endtime)); 465 } 466 } 467 468 /** 469 * Setup user, framework, competencies and course competencies. 470 */ 471 protected function setup_competencies_environment() { 472 $this->resetAfterTest(true); 473 $now = time(); 474 $this->setAdminUser(); 475 $dg = $this->getDataGenerator(); 476 $lpg = $dg->get_plugin_generator('core_competency'); 477 478 $course = $dg->create_course(array('startdate' => $now - WEEKSECS, 'enddate' => $now - DAYSECS)); 479 $coursenocompetencies = $dg->create_course(array('startdate' => $now - WEEKSECS, 'enddate' => $now - DAYSECS)); 480 481 $u1 = $dg->create_user(); 482 $this->getDataGenerator()->enrol_user($u1->id, $course->id); 483 $this->getDataGenerator()->enrol_user($u1->id, $coursenocompetencies->id); 484 $f1 = $lpg->create_framework(); 485 $c1 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 486 $c2 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 487 $c3 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 488 $c4 = $lpg->create_competency(array('competencyframeworkid' => $f1->get('id'))); 489 $cc1 = $lpg->create_course_competency(array('competencyid' => $c1->get('id'), 'courseid' => $course->id, 490 'ruleoutcome' => \core_competency\course_competency::OUTCOME_NONE)); 491 $cc2 = $lpg->create_course_competency(array('competencyid' => $c2->get('id'), 'courseid' => $course->id, 492 'ruleoutcome' => \core_competency\course_competency::OUTCOME_EVIDENCE)); 493 $cc3 = $lpg->create_course_competency(array('competencyid' => $c3->get('id'), 'courseid' => $course->id, 494 'ruleoutcome' => \core_competency\course_competency::OUTCOME_RECOMMEND)); 495 $cc4 = $lpg->create_course_competency(array('competencyid' => $c4->get('id'), 'courseid' => $course->id, 496 'ruleoutcome' => \core_competency\course_competency::OUTCOME_COMPLETE)); 497 498 return array( 499 'course' => $course, 500 'coursenocompetencies' => $coursenocompetencies, 501 'user' => $u1, 502 'course_competencies' => array($cc1, $cc2, $cc3, $cc4) 503 ); 504 } 505 506 /** 507 * Test the specific conditions of a valid analysable for the course_competencies target. 508 */ 509 public function test_core_target_course_competencies_analysable() { 510 511 $data = $this->setup_competencies_environment(); 512 513 $analysable = new \core_analytics\course($data['course']); 514 $target = new \core_course\analytics\target\course_competencies(); 515 516 $this->assertTrue($target->is_valid_analysable($analysable)); 517 518 $analysable = new \core_analytics\course($data['coursenocompetencies']); 519 $this->assertEquals(get_string('nocompetenciesincourse', 'tool_lp'), $target->is_valid_analysable($analysable)); 520 } 521 522 /** 523 * Test the target value calculation. 524 */ 525 public function test_core_target_course_competencies_calculate() { 526 527 $data = $this->setup_competencies_environment(); 528 529 $target = new \core_course\analytics\target\course_competencies(); 530 $analyser = new \core\analytics\analyser\student_enrolments(1, $target, [], [], []); 531 $analysable = new \core_analytics\course($data['course']); 532 533 $class = new \ReflectionClass('\core\analytics\analyser\student_enrolments'); 534 $method = $class->getMethod('get_all_samples'); 535 $method->setAccessible(true); 536 537 list($sampleids, $samplesdata) = $method->invoke($analyser, $analysable); 538 $target->add_sample_data($samplesdata); 539 $sampleid = reset($sampleids); 540 541 $class = new \ReflectionClass('\core_course\analytics\target\course_competencies'); 542 $method = $class->getMethod('calculate_sample'); 543 $method->setAccessible(true); 544 545 // Method calculate_sample() returns 1 when the user has not achieved all the competencies assigned to the course. 546 $this->assertEquals(1, $method->invoke($target, $sampleid, $analysable)); 547 548 // Grading of all the competences assigned to the course, in such way that the user achieves them all. 549 foreach ($data['course_competencies'] as $competency) { 550 \core_competency\api::grade_competency_in_course($data['course']->id, $data['user']->id, 551 $competency->get('competencyid'), 3, 'Unit test'); 552 } 553 // Method calculate_sample() returns 0 when the user has achieved all the competencies assigned to the course. 554 $this->assertEquals(0, $method->invoke($target, $sampleid, $analysable)); 555 } 556 557 /** 558 * Test the specific conditions of a valid analysable for the course_gradetopass target. 559 */ 560 public function test_core_target_course_gradetopass_analysable() { 561 global $DB; 562 563 $this->resetAfterTest(true); 564 $now = time(); 565 566 $dg = $this->getDataGenerator(); 567 568 // Course without grade to pass set. 569 $course1 = $dg->create_course(array('startdate' => $now - WEEKSECS, 'enddate' => $now - DAYSECS)); 570 $student1 = $dg->create_user(); 571 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 572 $dg->enrol_user($student1->id, $course1->id, $studentrole->id); 573 574 $analysable = new \core_analytics\course($course1); 575 $target = new \core_course\analytics\target\course_gradetopass(); 576 $this->assertEquals(get_string('gradetopassnotset', 'course'), $target->is_valid_analysable($analysable)); 577 578 // Set grade to pass. 579 $courseitem = \grade_item::fetch_course_item($course1->id); 580 $courseitem->gradepass = 50; 581 $DB->update_record('grade_items', $courseitem); 582 // Since the grade to pass value is cached in the target, a new one it is instanciated. 583 $target = new \core_course\analytics\target\course_gradetopass(); 584 $this->assertTrue($target->is_valid_analysable($analysable)); 585 586 } 587 588 /** 589 * Test the target value calculation of the course_gradetopass target. 590 */ 591 public function test_core_target_course_gradetopass_calculate() { 592 global $DB; 593 594 $this->resetAfterTest(true); 595 596 $dg = $this->getDataGenerator(); 597 $course1 = $dg->create_course(); 598 // Set grade to pass. 599 $student1 = $dg->create_user(); 600 $student2 = $dg->create_user(); 601 $student3 = $dg->create_user(); 602 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 603 $dg->enrol_user($student1->id, $course1->id, $studentrole->id); 604 $dg->enrol_user($student2->id, $course1->id, $studentrole->id); 605 $dg->enrol_user($student3->id, $course1->id, $studentrole->id); 606 607 // get_all_samples() does not guarantee any order, so let's 608 // explicitly define the expectations here for later comparing. 609 // Expectations format being array($userid => expectation, ...) 610 $expectations = []; 611 612 $courseitem = \grade_item::fetch_course_item($course1->id); 613 // Student1 (< gradepass) fails, so it's non achieved sample. 614 $courseitem->update_final_grade($student1->id, 30); 615 $expectations[$student1->id] = 1; 616 617 // Student2 (> gradepass) passes, so it's achieved sample. 618 $courseitem->update_final_grade($student2->id, 60); 619 $expectations[$student2->id] = 0; 620 621 // Student 3 (has no grade) fails, so it's non achieved sample. 622 $expectations[$student3->id] = 1; 623 624 $courseitem->gradepass = 50; 625 $DB->update_record('grade_items', $courseitem); 626 627 $target = new \core_course\analytics\target\course_gradetopass(); 628 $analyser = new \core\analytics\analyser\student_enrolments(1, $target, [], [], []); 629 $analysable = new \core_analytics\course($course1); 630 631 $class = new \ReflectionClass('\core\analytics\analyser\student_enrolments'); 632 $method = $class->getMethod('get_all_samples'); 633 $method->setAccessible(true); 634 635 list($sampleids, $samplesdata) = $method->invoke($analyser, $analysable); 636 $target->add_sample_data($samplesdata); 637 638 $class = new \ReflectionClass('\core_course\analytics\target\course_gradetopass'); 639 $method = $class->getMethod('calculate_sample'); 640 $method->setAccessible(true); 641 642 // Verify all the expectations are fulfilled. 643 foreach ($sampleids as $sampleid => $key) { 644 $this->assertEquals($expectations[$samplesdata[$key]['user']->id], $method->invoke($target, $sampleid, $analysable)); 645 } 646 } 647 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body