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