Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * mod_h5pactivity attempt tests 19 * 20 * @package mod_h5pactivity 21 * @category test 22 * @copyright 2020 Ferran Recio <ferran@moodle.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 namespace mod_h5pactivity\local; 27 28 use \core_xapi\local\statement; 29 use \core_xapi\local\statement\item; 30 use \core_xapi\local\statement\item_agent; 31 use \core_xapi\local\statement\item_activity; 32 use \core_xapi\local\statement\item_definition; 33 use \core_xapi\local\statement\item_verb; 34 use \core_xapi\local\statement\item_result; 35 use core_xapi\test_helper; 36 use stdClass; 37 38 /** 39 * Attempt tests class for mod_h5pactivity. 40 * 41 * @package mod_h5pactivity 42 * @category test 43 * @copyright 2020 Ferran Recio <ferran@moodle.com> 44 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 45 */ 46 class attempt_test extends \advanced_testcase { 47 48 /** 49 * Generate a scenario to run all tests. 50 * @return array course_modules, user record, course record 51 */ 52 private function generate_testing_scenario(): array { 53 $this->resetAfterTest(); 54 $this->setAdminUser(); 55 56 $course = $this->getDataGenerator()->create_course(); 57 $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 58 $cm = get_coursemodule_from_id('h5pactivity', $activity->cmid, 0, false, MUST_EXIST); 59 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 60 61 return [$cm, $student, $course]; 62 } 63 64 /** 65 * Test for create_attempt method. 66 */ 67 public function test_create_attempt() { 68 global $CFG, $DB; 69 require_once($CFG->dirroot.'/lib/xapi/tests/helper.php'); 70 71 [$cm, $student, $course] = $this->generate_testing_scenario(); 72 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 73 74 // Save the current state for this activity for student1 and student2 (before creating the first attempt). 75 $manager = manager::create_from_coursemodule($cm); 76 $this->setUser($student2); 77 test_helper::create_state([ 78 'activity' => item_activity::create_from_id($manager->get_context()->id), 79 'component' => 'mod_h5pactivity', 80 ], true); 81 $this->setUser($student); 82 test_helper::create_state([ 83 'activity' => item_activity::create_from_id($manager->get_context()->id), 84 'component' => 'mod_h5pactivity', 85 ], true); 86 87 $this->assertEquals(2, $DB->count_records('xapi_states')); 88 89 // Create first attempt. 90 $attempt = attempt::new_attempt($student, $cm); 91 $this->assertEquals($student->id, $attempt->get_userid()); 92 $this->assertEquals($cm->instance, $attempt->get_h5pactivityid()); 93 $this->assertEquals(1, $attempt->get_attempt()); 94 $this->assertEquals(1, $DB->count_records('xapi_states')); 95 $this->assertEquals(0, $DB->count_records('xapi_states', ['userid' => $student->id])); 96 97 // Create a second attempt. 98 $attempt = attempt::new_attempt($student, $cm); 99 $this->assertEquals($student->id, $attempt->get_userid()); 100 $this->assertEquals($cm->instance, $attempt->get_h5pactivityid()); 101 $this->assertEquals(2, $attempt->get_attempt()); 102 } 103 104 /** 105 * Test for last_attempt method 106 */ 107 public function test_last_attempt() { 108 109 list($cm, $student) = $this->generate_testing_scenario(); 110 111 // Create first attempt. 112 $attempt = attempt::last_attempt($student, $cm); 113 $this->assertEquals($student->id, $attempt->get_userid()); 114 $this->assertEquals($cm->instance, $attempt->get_h5pactivityid()); 115 $this->assertEquals(1, $attempt->get_attempt()); 116 $lastid = $attempt->get_id(); 117 118 // Get last attempt. 119 $attempt = attempt::last_attempt($student, $cm); 120 $this->assertEquals($student->id, $attempt->get_userid()); 121 $this->assertEquals($cm->instance, $attempt->get_h5pactivityid()); 122 $this->assertEquals(1, $attempt->get_attempt()); 123 $this->assertEquals($lastid, $attempt->get_id()); 124 125 // Now force a new attempt. 126 $attempt = attempt::new_attempt($student, $cm); 127 $this->assertEquals($student->id, $attempt->get_userid()); 128 $this->assertEquals($cm->instance, $attempt->get_h5pactivityid()); 129 $this->assertEquals(2, $attempt->get_attempt()); 130 $lastid = $attempt->get_id(); 131 132 // Get last attempt. 133 $attempt = attempt::last_attempt($student, $cm); 134 $this->assertEquals($student->id, $attempt->get_userid()); 135 $this->assertEquals($cm->instance, $attempt->get_h5pactivityid()); 136 $this->assertEquals(2, $attempt->get_attempt()); 137 $this->assertEquals($lastid, $attempt->get_id()); 138 } 139 140 /** 141 * Test saving statements. 142 * 143 * @dataProvider save_statement_data 144 * @param string $subcontent subcontent identifier 145 * @param bool $hasdefinition generate definition 146 * @param bool $hasresult generate result 147 * @param array $results 0 => insert ok, 1 => maxscore, 2 => rawscore, 3 => count 148 */ 149 public function test_save_statement(string $subcontent, bool $hasdefinition, bool $hasresult, array $results) { 150 151 list($cm, $student) = $this->generate_testing_scenario(); 152 153 $attempt = attempt::new_attempt($student, $cm); 154 $this->assertEquals(0, $attempt->get_maxscore()); 155 $this->assertEquals(0, $attempt->get_rawscore()); 156 $this->assertEquals(0, $attempt->count_results()); 157 $this->assertEquals(0, $attempt->get_duration()); 158 $this->assertNull($attempt->get_completion()); 159 $this->assertNull($attempt->get_success()); 160 $this->assertFalse($attempt->get_scoreupdated()); 161 162 $statement = $this->generate_statement($hasdefinition, $hasresult); 163 $result = $attempt->save_statement($statement, $subcontent); 164 $this->assertEquals($results[0], $result); 165 $this->assertEquals($results[1], $attempt->get_maxscore()); 166 $this->assertEquals($results[2], $attempt->get_rawscore()); 167 $this->assertEquals($results[3], $attempt->count_results()); 168 $this->assertEquals($results[4], $attempt->get_duration()); 169 $this->assertEquals($results[5], $attempt->get_completion()); 170 $this->assertEquals($results[6], $attempt->get_success()); 171 if ($results[5]) { 172 $this->assertTrue($attempt->get_scoreupdated()); 173 } else { 174 $this->assertFalse($attempt->get_scoreupdated()); 175 } 176 } 177 178 /** 179 * Data provider for data request creation tests. 180 * 181 * @return array 182 */ 183 public function save_statement_data(): array { 184 return [ 185 'Statement without definition and result' => [ 186 '', false, false, [false, 0, 0, 0, 0, null, null] 187 ], 188 'Statement with definition but no result' => [ 189 '', true, false, [false, 0, 0, 0, 0, null, null] 190 ], 191 'Statement with result but no definition' => [ 192 '', true, false, [false, 0, 0, 0, 0, null, null] 193 ], 194 'Statement subcontent without definition and result' => [ 195 '111-222-333', false, false, [false, 0, 0, 0, 0, null, null] 196 ], 197 'Statement subcontent with definition but no result' => [ 198 '111-222-333', true, false, [false, 0, 0, 0, 0, null, null] 199 ], 200 'Statement subcontent with result but no definition' => [ 201 '111-222-333', true, false, [false, 0, 0, 0, 0, null, null] 202 ], 203 'Statement with definition, result but no subcontent' => [ 204 '', true, true, [true, 2, 2, 1, 25, 1, 1] 205 ], 206 'Statement with definition, result and subcontent' => [ 207 '111-222-333', true, true, [true, 0, 0, 1, 0, null, null] 208 ], 209 ]; 210 } 211 212 /** 213 * Test delete results from attempt. 214 */ 215 public function test_delete_results() { 216 217 list($cm, $student) = $this->generate_testing_scenario(); 218 219 $attempt = $this->generate_full_attempt($student, $cm); 220 $attempt->delete_results(); 221 $this->assertEquals(0, $attempt->count_results()); 222 } 223 224 /** 225 * Test delete attempt. 226 */ 227 public function test_delete_attempt() { 228 global $DB; 229 230 list($cm, $student) = $this->generate_testing_scenario(); 231 232 // Check no previous attempts are created. 233 $count = $DB->count_records('h5pactivity_attempts'); 234 $this->assertEquals(0, $count); 235 $count = $DB->count_records('h5pactivity_attempts_results'); 236 $this->assertEquals(0, $count); 237 238 // Generate one attempt. 239 $attempt1 = $this->generate_full_attempt($student, $cm); 240 $count = $DB->count_records('h5pactivity_attempts'); 241 $this->assertEquals(1, $count); 242 $count = $DB->count_records('h5pactivity_attempts_results'); 243 $this->assertEquals(2, $count); 244 245 // Generate a second attempt. 246 $attempt2 = $this->generate_full_attempt($student, $cm); 247 $count = $DB->count_records('h5pactivity_attempts'); 248 $this->assertEquals(2, $count); 249 $count = $DB->count_records('h5pactivity_attempts_results'); 250 $this->assertEquals(4, $count); 251 252 // Delete the first attempt. 253 attempt::delete_attempt($attempt1); 254 $count = $DB->count_records('h5pactivity_attempts'); 255 $this->assertEquals(1, $count); 256 $count = $DB->count_records('h5pactivity_attempts_results'); 257 $this->assertEquals(2, $count); 258 $this->assertEquals(2, $attempt2->count_results()); 259 } 260 261 /** 262 * Test delete all attempts. 263 * 264 * @dataProvider delete_all_attempts_data 265 * @param bool $hasstudent if user is specificed 266 * @param int[] 0-3 => statements count results, 4-5 => totals 267 */ 268 public function test_delete_all_attempts(bool $hasstudent, array $results) { 269 global $DB; 270 271 list($cm, $student, $course) = $this->generate_testing_scenario(); 272 273 // For this test we need extra activity and student. 274 $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 275 $cm2 = get_coursemodule_from_id('h5pactivity', $activity->cmid, 0, false, MUST_EXIST); 276 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 277 278 // Check no previous attempts are created. 279 $count = $DB->count_records('h5pactivity_attempts'); 280 $this->assertEquals(0, $count); 281 $count = $DB->count_records('h5pactivity_attempts_results'); 282 $this->assertEquals(0, $count); 283 284 // Generate some attempts attempt on both activities and students. 285 $attempts = []; 286 $attempts[] = $this->generate_full_attempt($student, $cm); 287 $attempts[] = $this->generate_full_attempt($student2, $cm); 288 $attempts[] = $this->generate_full_attempt($student, $cm2); 289 $attempts[] = $this->generate_full_attempt($student2, $cm2); 290 $count = $DB->count_records('h5pactivity_attempts'); 291 $this->assertEquals(4, $count); 292 $count = $DB->count_records('h5pactivity_attempts_results'); 293 $this->assertEquals(8, $count); 294 295 // Delete all specified attempts. 296 $user = ($hasstudent) ? $student : null; 297 attempt::delete_all_attempts($cm, $user); 298 299 // Check data. 300 for ($assert = 0; $assert < 4; $assert++) { 301 $count = $attempts[$assert]->count_results(); 302 $this->assertEquals($results[$assert], $count); 303 } 304 $count = $DB->count_records('h5pactivity_attempts'); 305 $this->assertEquals($results[4], $count); 306 $count = $DB->count_records('h5pactivity_attempts_results'); 307 $this->assertEquals($results[5], $count); 308 } 309 310 /** 311 * Data provider for data request creation tests. 312 * 313 * @return array 314 */ 315 public function delete_all_attempts_data(): array { 316 return [ 317 'Delete all attempts from activity' => [ 318 false, [0, 0, 2, 2, 2, 4] 319 ], 320 'Delete all attempts from user' => [ 321 true, [0, 2, 2, 2, 3, 6] 322 ], 323 ]; 324 } 325 326 /** 327 * Test set_score method. 328 * 329 */ 330 public function test_set_score(): void { 331 global $DB; 332 333 list($cm, $student, $course) = $this->generate_testing_scenario(); 334 335 // Generate one attempt. 336 $attempt = $this->generate_full_attempt($student, $cm); 337 338 $dbattempt = $DB->get_record('h5pactivity_attempts', ['id' => $attempt->get_id()]); 339 $this->assertEquals($dbattempt->rawscore, $attempt->get_rawscore()); 340 $this->assertEquals(2, $dbattempt->rawscore); 341 $this->assertEquals($dbattempt->maxscore, $attempt->get_maxscore()); 342 $this->assertEquals(2, $dbattempt->maxscore); 343 $this->assertEquals(1, $dbattempt->scaled); 344 345 // Set attempt score. 346 $attempt->set_score(5, 10); 347 348 $this->assertEquals(5, $attempt->get_rawscore()); 349 $this->assertEquals(10, $attempt->get_maxscore()); 350 $this->assertTrue($attempt->get_scoreupdated()); 351 352 // Save new score into DB. 353 $attempt->save(); 354 355 $dbattempt = $DB->get_record('h5pactivity_attempts', ['id' => $attempt->get_id()]); 356 $this->assertEquals($dbattempt->rawscore, $attempt->get_rawscore()); 357 $this->assertEquals(5, $dbattempt->rawscore); 358 $this->assertEquals($dbattempt->maxscore, $attempt->get_maxscore()); 359 $this->assertEquals(10, $dbattempt->maxscore); 360 $this->assertEquals(0.5, $dbattempt->scaled); 361 } 362 363 /** 364 * Test set_duration method. 365 * 366 * @dataProvider basic_setters_data 367 * @param string $attribute the stribute to test 368 * @param int $oldvalue attribute old value 369 * @param int $newvalue attribute new expected value 370 */ 371 public function test_basic_setters(string $attribute, int $oldvalue, int $newvalue): void { 372 global $DB; 373 374 list($cm, $student, $course) = $this->generate_testing_scenario(); 375 376 // Generate one attempt. 377 $attempt = $this->generate_full_attempt($student, $cm); 378 379 $setmethod = 'set_'.$attribute; 380 $getmethod = 'get_'.$attribute; 381 382 $dbattempt = $DB->get_record('h5pactivity_attempts', ['id' => $attempt->get_id()]); 383 $this->assertEquals($dbattempt->$attribute, $attempt->$getmethod()); 384 $this->assertEquals($oldvalue, $dbattempt->$attribute); 385 386 // Set attempt attribute. 387 $attempt->$setmethod($newvalue); 388 389 $this->assertEquals($newvalue, $attempt->$getmethod()); 390 391 // Save new score into DB. 392 $attempt->save(); 393 394 $dbattempt = $DB->get_record('h5pactivity_attempts', ['id' => $attempt->get_id()]); 395 $this->assertEquals($dbattempt->$attribute, $attempt->$getmethod()); 396 $this->assertEquals($newvalue, $dbattempt->$attribute); 397 398 // Set null $attribute. 399 $attempt->$setmethod(null); 400 401 $this->assertNull($attempt->$getmethod()); 402 403 // Save new score into DB. 404 $attempt->save(); 405 406 $dbattempt = $DB->get_record('h5pactivity_attempts', ['id' => $attempt->get_id()]); 407 $this->assertEquals($dbattempt->$attribute, $attempt->$getmethod()); 408 $this->assertNull($dbattempt->$attribute); 409 } 410 411 /** 412 * Data provider for testing basic setters. 413 * 414 * @return array 415 */ 416 public function basic_setters_data(): array { 417 return [ 418 'Set attempt duration' => [ 419 'duration', 25, 35 420 ], 421 'Set attempt completion' => [ 422 'completion', 1, 0 423 ], 424 'Set attempt success' => [ 425 'success', 1, 0 426 ], 427 ]; 428 } 429 430 /** 431 * Generate a fake attempt with two results. 432 * 433 * @param stdClass $student a user record 434 * @param stdClass $cm a course_module record 435 * @return attempt 436 */ 437 private function generate_full_attempt($student, $cm): attempt { 438 $attempt = attempt::new_attempt($student, $cm); 439 $this->assertEquals(0, $attempt->get_maxscore()); 440 $this->assertEquals(0, $attempt->get_rawscore()); 441 $this->assertEquals(0, $attempt->count_results()); 442 443 $statement = $this->generate_statement(true, true); 444 $saveok = $attempt->save_statement($statement, ''); 445 $this->assertTrue($saveok); 446 $saveok = $attempt->save_statement($statement, '111-222-333'); 447 $this->assertTrue($saveok); 448 $this->assertEquals(2, $attempt->count_results()); 449 450 return $attempt; 451 } 452 453 /** 454 * Return a xAPI partial statement with object defined. 455 * @param bool $hasdefinition if has to include definition 456 * @param bool $hasresult if has to include results 457 * @return statement 458 */ 459 private function generate_statement(bool $hasdefinition, bool $hasresult): statement { 460 global $USER; 461 462 $statement = new statement(); 463 $statement->set_actor(item_agent::create_from_user($USER)); 464 $statement->set_verb(item_verb::create_from_id('http://adlnet.gov/expapi/verbs/completed')); 465 $definition = null; 466 if ($hasdefinition) { 467 $definition = item_definition::create_from_data((object)[ 468 'interactionType' => 'compound', 469 'correctResponsesPattern' => '1', 470 ]); 471 } 472 $statement->set_object(item_activity::create_from_id('something', $definition)); 473 if ($hasresult) { 474 $statement->set_result(item_result::create_from_data((object)[ 475 'completion' => true, 476 'success' => true, 477 'score' => (object) ['min' => 0, 'max' => 2, 'raw' => 2, 'scaled' => 1], 478 'duration' => 'PT25S', 479 ])); 480 } 481 return $statement; 482 } 483 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body