mirror of
https://github.com/moodle/moodle.git
synced 2025-02-22 10:57:20 +01:00
Duplicate data provider keys were overwriting and/or duplicating one another, leading to some cases being skipped. Other "duplicate array key" errors were picked up by `phpcs` in this dragnet across all tests, which have also been fixed.
431 lines
18 KiB
PHP
431 lines
18 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
namespace mod_h5pactivity;
|
|
|
|
use advanced_testcase;
|
|
use mod_h5pactivity\local\manager;
|
|
|
|
/**
|
|
* Unit tests for (some of) mod/h5pactivity/lib.php.
|
|
*
|
|
* @package mod_h5pactivity
|
|
* @copyright 2021 Ilya Tregubov <ilya@moodle.com>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
final class lib_test extends advanced_testcase {
|
|
|
|
/**
|
|
* Load required test libraries
|
|
*/
|
|
public static function setUpBeforeClass(): void {
|
|
global $CFG;
|
|
require_once("{$CFG->dirroot}/mod/h5pactivity/lib.php");
|
|
parent::setUpBeforeClass();
|
|
}
|
|
|
|
/**
|
|
* Test that h5pactivity_delete_instance removes data.
|
|
*
|
|
* @covers ::h5pactivity_delete_instance
|
|
*/
|
|
public function test_h5pactivity_delete_instance(): void {
|
|
global $DB;
|
|
$this->resetAfterTest();
|
|
$this->setAdminUser();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$user = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
|
$activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]);
|
|
$this->setUser($user);
|
|
|
|
/** @var \mod_h5pactivity_generator $generator */
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_h5pactivity');
|
|
|
|
/** @var \core_h5p_generator $h5pgenerator */
|
|
$h5pgenerator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
|
|
|
|
// Add an attempt to the H5P activity.
|
|
$attemptinfo = [
|
|
'userid' => $user->id,
|
|
'h5pactivityid' => $activity->id,
|
|
'attempt' => 1,
|
|
'interactiontype' => 'compound',
|
|
'rawscore' => 2,
|
|
'maxscore' => 2,
|
|
'duration' => 1,
|
|
'completion' => 1,
|
|
'success' => 0,
|
|
];
|
|
$generator->create_attempt($attemptinfo);
|
|
|
|
// Add also a xAPI state to the H5P activity.
|
|
$filerecord = [
|
|
'contextid' => \context_module::instance($activity->cmid)->id,
|
|
'component' => 'mod_h5pactivity',
|
|
'filearea' => 'package',
|
|
'itemid' => 0,
|
|
'filepath' => '/',
|
|
'filename' => 'dummy.h5p',
|
|
'addxapistate' => true,
|
|
];
|
|
$h5pgenerator->generate_h5p_data(false, $filerecord);
|
|
|
|
// Check the H5P activity exists and the attempt has been created.
|
|
$this->assertNotEmpty($DB->get_record('h5pactivity', ['id' => $activity->id]));
|
|
$this->assertEquals(2, $DB->count_records('grade_items'));
|
|
$this->assertEquals(2, $DB->count_records('grade_grades'));
|
|
$this->assertEquals(1, $DB->count_records('xapi_states'));
|
|
|
|
// Check nothing happens when given activity id doesn't exist.
|
|
h5pactivity_delete_instance($activity->id + 1);
|
|
$this->assertNotEmpty($DB->get_record('h5pactivity', ['id' => $activity->id]));
|
|
$this->assertEquals(2, $DB->count_records('grade_items'));
|
|
$this->assertEquals(2, $DB->count_records('grade_grades'));
|
|
$this->assertEquals(1, $DB->count_records('xapi_states'));
|
|
|
|
// Check the H5P instance and its associated data is removed.
|
|
h5pactivity_delete_instance($activity->id);
|
|
$this->assertEmpty($DB->get_record('h5pactivity', ['id' => $activity->id]));
|
|
$this->assertEquals(1, $DB->count_records('grade_items'));
|
|
$this->assertEquals(1, $DB->count_records('grade_grades'));
|
|
$this->assertEquals(0, $DB->count_records('xapi_states'));
|
|
}
|
|
|
|
/**
|
|
* Test that assign_print_recent_activity shows ungraded submitted assignments.
|
|
*
|
|
* @covers ::h5pactivity_print_recent_activity
|
|
*/
|
|
public function test_print_recent_activity(): void {
|
|
$this->resetAfterTest();
|
|
$this->setAdminUser();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
|
$activity = $this->getDataGenerator()->create_module('h5pactivity',
|
|
['course' => $course, 'enabletracking' => 1, 'grademethod' => manager::GRADEHIGHESTATTEMPT]);
|
|
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_h5pactivity');
|
|
|
|
$manager = manager::create_from_instance($activity);
|
|
$cm = $manager->get_coursemodule();
|
|
|
|
$user = $student;
|
|
$params = ['cmid' => $cm->id, 'userid' => $user->id];
|
|
$generator->create_content($activity, $params);
|
|
$this->setUser($student);
|
|
$this->expectOutputRegex('/submitted:/');
|
|
h5pactivity_print_recent_activity($course, true, time() - 3600);
|
|
}
|
|
|
|
/**
|
|
* Test that h5pactivity_print_recent_activity does not display any warnings when a custom fullname has been configured.
|
|
*
|
|
* @covers ::h5pactivity_print_recent_activity
|
|
*/
|
|
public function test_print_recent_activity_fullname(): void {
|
|
$this->resetAfterTest();
|
|
$this->setAdminUser();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
|
|
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
|
$activity = $this->getDataGenerator()->create_module('h5pactivity',
|
|
['course' => $course, 'enabletracking' => 1, 'grademethod' => manager::GRADEHIGHESTATTEMPT]);
|
|
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_h5pactivity');
|
|
|
|
$manager = manager::create_from_instance($activity);
|
|
$cm = $manager->get_coursemodule();
|
|
|
|
$user = $student;
|
|
$params = ['cmid' => $cm->id, 'userid' => $user->id];
|
|
$generator->create_content($activity, $params);
|
|
|
|
$this->setUser($teacher);
|
|
|
|
$this->expectOutputRegex('/submitted:/');
|
|
set_config('fullnamedisplay', 'firstname, lastnamephonetic');
|
|
h5pactivity_print_recent_activity($course, false, time() - 3600);
|
|
}
|
|
|
|
/**
|
|
* Test that h5pactivity_get_recent_mod_activity fetches the h5pactivity correctly.
|
|
*
|
|
* @covers ::h5pactivity_get_recent_mod_activity
|
|
*/
|
|
public function test_h5pactivity_get_recent_mod_activity(): void {
|
|
$this->resetAfterTest();
|
|
$this->setAdminUser();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
|
|
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
|
$activity = $this->getDataGenerator()->create_module('h5pactivity',
|
|
['course' => $course, 'enabletracking' => 1, 'grademethod' => manager::GRADEHIGHESTATTEMPT]);
|
|
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_h5pactivity');
|
|
|
|
$manager = manager::create_from_instance($activity);
|
|
$cm = $manager->get_coursemodule();
|
|
|
|
$user = $student;
|
|
$params = ['cmid' => $cm->id, 'userid' => $user->id];
|
|
$generator->create_content($activity, $params);
|
|
|
|
$index = 1;
|
|
$activities = [
|
|
$index => (object) [
|
|
'type' => 'h5pactivity',
|
|
'cmid' => $cm->id,
|
|
],
|
|
];
|
|
|
|
$this->setUser($teacher);
|
|
h5pactivity_get_recent_mod_activity($activities, $index, time() - HOURSECS, $course->id, $cm->id);
|
|
|
|
$activity = $activities[1];
|
|
$this->assertEquals("h5pactivity", $activity->type);
|
|
$this->assertEquals($student->id, $activity->user->id);
|
|
}
|
|
|
|
/**
|
|
* Test that h5pactivity_get_recent_mod_activity fetches activity correctly.
|
|
*
|
|
* @covers ::h5pactivity_fetch_recent_activity
|
|
*/
|
|
public function test_h5pactivity_fetch_recent_activity(): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
$this->setAdminUser();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
|
|
// Create users and groups.
|
|
$students = array();
|
|
$groups = array();
|
|
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
|
|
|
|
for ($i = 1; $i < 6; $i++) {
|
|
$students[$i] = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
|
$groups[$i] = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
|
}
|
|
$groups[$i] = $this->getDataGenerator()->create_group(array('courseid' => $course->id, 'participation' => 0));
|
|
|
|
// Update the course set the groupmode SEPARATEGROUPS and forced.
|
|
update_course((object)array('id' => $course->id, 'groupmode' => SEPARATEGROUPS, 'groupmodeforce' => true));
|
|
|
|
// Student 1 is in groups 1 and 3.
|
|
groups_add_member($groups[1], $students[1]);
|
|
groups_add_member($groups[3], $students[1]);
|
|
|
|
// Student 2 is in groups 1 and 2.
|
|
groups_add_member($groups[1], $students[2]);
|
|
groups_add_member($groups[2], $students[2]);
|
|
|
|
// Student 3 is only in group 3.
|
|
groups_add_member($groups[3], $students[3]);
|
|
|
|
// Student 4 is only in group 5 (non-participation).
|
|
groups_add_member($groups[6], $students[4]);
|
|
|
|
// Student 5 is not in any groups.
|
|
|
|
// Grader is only in group 3.
|
|
groups_add_member($groups[3], $teacher);
|
|
|
|
$timestart = time() - 1;
|
|
// Create h5pactivity.
|
|
$activity = $this->getDataGenerator()->create_module('h5pactivity',
|
|
['course' => $course->id, 'enabletracking' => 1, 'grademethod' => manager::GRADEHIGHESTATTEMPT,
|
|
'groupmode' => SEPARATEGROUPS]);
|
|
|
|
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
|
|
$cmcontext = \context_module::instance($activity->cmid);
|
|
assign_capability('moodle/site:accessallgroups', CAP_PROHIBIT, $teacherrole->id, $cmcontext, true);
|
|
|
|
$manager = manager::create_from_instance($activity);
|
|
$cm = $manager->get_coursemodule();
|
|
|
|
// Create attempts.
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_h5pactivity');
|
|
foreach ($students as $student) {
|
|
$params = ['cmid' => $cm->id, 'userid' => $student->id];
|
|
$generator->create_content($activity, $params);
|
|
}
|
|
|
|
// Get all attempts.
|
|
$dbparams = [$timestart, $course->id, 'h5pactivity'];
|
|
$userfieldsapi = \core_user\fields::for_userpic();
|
|
$namefields = $userfieldsapi->get_sql('u', false, '', 'userid', false)->selects;;
|
|
|
|
$sql = "SELECT h5pa.id, h5pa.timemodified, cm.id as cmid, $namefields
|
|
FROM {h5pactivity_attempts} h5pa
|
|
JOIN {h5pactivity} h5p ON h5p.id = h5pa.h5pactivityid
|
|
JOIN {course_modules} cm ON cm.instance = h5p.id
|
|
JOIN {modules} md ON md.id = cm.module
|
|
JOIN {user} u ON u.id = h5pa.userid
|
|
WHERE h5pa.timemodified > ?
|
|
AND h5p.course = ?
|
|
AND md.name = ?
|
|
ORDER BY h5pa.timemodified ASC";
|
|
|
|
$submissions = $DB->get_records_sql($sql, $dbparams);
|
|
$this->assertCount(count($students), $submissions);
|
|
|
|
// Fetch activity for student (only his own).
|
|
$this->setUser($students[1]);
|
|
$recentactivity = h5pactivity_fetch_recent_activity($submissions, $course->id);
|
|
$this->assertCount(1, $recentactivity);
|
|
$this->assertEquals($students[1]->id, $recentactivity[$students[1]->id]->userid);
|
|
|
|
// Fetch users group info for grader.
|
|
$this->setUser($teacher);
|
|
$recentactivity = h5pactivity_fetch_recent_activity($submissions, $course->id);
|
|
$this->assertCount(2, $recentactivity);
|
|
// Grader, Student 1 and 3 are in Group 3.
|
|
$this->assertEquals($students[1]->id, $recentactivity[$students[1]->id]->userid);
|
|
$this->assertEquals($students[3]->id, $recentactivity[$students[3]->id]->userid);
|
|
|
|
// Grader is in Group 2.
|
|
groups_remove_member($groups[3], $teacher);
|
|
groups_add_member($groups[2], $teacher);
|
|
get_fast_modinfo($course->id, 0, true);
|
|
$recentactivity = h5pactivity_fetch_recent_activity($submissions, $course->id);
|
|
$this->assertCount(1, $recentactivity);
|
|
// Grader, Student 2 are in Group 2.
|
|
$this->assertEquals($students[2]->id, $recentactivity[$students[2]->id]->userid);
|
|
|
|
// Grader is in Group 1.
|
|
groups_remove_member($groups[2], $teacher);
|
|
groups_add_member($groups[1], $teacher);
|
|
get_fast_modinfo($course->id, 0, true);
|
|
$recentactivity = h5pactivity_fetch_recent_activity($submissions, $course->id);
|
|
$this->assertCount(2, $recentactivity);
|
|
// Grader, Student 1 and 2 are in Group 1.
|
|
$this->assertEquals($students[1]->id, $recentactivity[$students[1]->id]->userid);
|
|
$this->assertEquals($students[2]->id, $recentactivity[$students[2]->id]->userid);
|
|
|
|
// Grader is in no group.
|
|
groups_remove_member($groups[1], $teacher);
|
|
get_fast_modinfo($course->id, 0, true);
|
|
$recentactivity = h5pactivity_fetch_recent_activity($submissions, $course->id);
|
|
// Student 4 and Student 5 have submissions, but they are not in a participation group, so they do not show up in recent
|
|
// activity for separate groups mode.
|
|
$this->assertCount(0, $recentactivity);
|
|
}
|
|
|
|
/**
|
|
* Test that h5pactivity_reset_userdata reset user data.
|
|
*
|
|
* @covers ::h5pactivity_reset_userdata
|
|
*/
|
|
public function test_h5pactivity_reset_userdata(): void {
|
|
global $DB;
|
|
$this->resetAfterTest();
|
|
$this->setAdminUser();
|
|
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$user = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
|
$activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]);
|
|
$this->setUser($user);
|
|
|
|
/** @var \mod_h5pactivity_generator $generator */
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('mod_h5pactivity');
|
|
|
|
/** @var \core_h5p_generator $h5pgenerator */
|
|
$h5pgenerator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
|
|
|
|
// Add an attempt to the H5P activity.
|
|
$attemptinfo = [
|
|
'userid' => $user->id,
|
|
'h5pactivityid' => $activity->id,
|
|
'attempt' => 1,
|
|
'interactiontype' => 'compound',
|
|
'rawscore' => 2,
|
|
'maxscore' => 2,
|
|
'duration' => 1,
|
|
'completion' => 1,
|
|
'success' => 0,
|
|
];
|
|
$generator->create_attempt($attemptinfo);
|
|
|
|
// Add also a xAPI state to the H5P activity.
|
|
$filerecord = [
|
|
'contextid' => \context_module::instance($activity->cmid)->id,
|
|
'component' => 'mod_h5pactivity',
|
|
'filearea' => 'package',
|
|
'itemid' => 0,
|
|
'filepath' => '/',
|
|
'filename' => 'dummy.h5p',
|
|
'addxapistate' => true,
|
|
];
|
|
$h5pgenerator->generate_h5p_data(false, $filerecord);
|
|
|
|
// Check the H5P activity exists and the attempt has been created with the expected data.
|
|
$this->assertNotEmpty($DB->get_record('h5pactivity', ['id' => $activity->id]));
|
|
$this->assertEquals(2, $DB->count_records('grade_items'));
|
|
$this->assertEquals(2, $DB->count_records('grade_grades'));
|
|
$this->assertEquals(1, $DB->count_records('xapi_states'));
|
|
|
|
// Check nothing happens when reset_h5pactivity is not set.
|
|
$data = new \stdClass();
|
|
h5pactivity_reset_userdata($data);
|
|
$this->assertNotEmpty($DB->get_record('h5pactivity', ['id' => $activity->id]));
|
|
$this->assertEquals(2, $DB->count_records('grade_items'));
|
|
$this->assertEquals(2, $DB->count_records('grade_grades'));
|
|
$this->assertEquals(1, $DB->count_records('xapi_states'));
|
|
$this->assertEquals(1, $DB->count_records('xapi_states'));
|
|
|
|
// Check nothing happens when reset_h5pactivity is not set.
|
|
$data = (object) [
|
|
'courseid' => $course->id,
|
|
];
|
|
h5pactivity_reset_userdata($data);
|
|
$this->assertNotEmpty($DB->get_record('h5pactivity', ['id' => $activity->id]));
|
|
$this->assertEquals(2, $DB->count_records('grade_items'));
|
|
$this->assertEquals(2, $DB->count_records('grade_grades'));
|
|
$this->assertEquals(1, $DB->count_records('xapi_states'));
|
|
$this->assertEquals(1, $DB->count_records('xapi_states'));
|
|
|
|
// Check nothing happens when the given course doesn't exist.
|
|
$data = (object) [
|
|
'reset_h5pactivity' => true,
|
|
'courseid' => $course->id + 1,
|
|
];
|
|
h5pactivity_reset_userdata($data);
|
|
$this->assertNotEmpty($DB->get_record('h5pactivity', ['id' => $activity->id]));
|
|
$this->assertEquals(2, $DB->count_records('grade_items'));
|
|
$this->assertEquals(2, $DB->count_records('grade_grades'));
|
|
$this->assertEquals(1, $DB->count_records('xapi_states'));
|
|
$this->assertEquals(1, $DB->count_records('xapi_states'));
|
|
|
|
// Check the H5P instance and its associated data is reset.
|
|
$data = (object) [
|
|
'reset_h5pactivity' => true,
|
|
'courseid' => $course->id,
|
|
];
|
|
h5pactivity_reset_userdata($data);
|
|
$this->assertNotEmpty($DB->get_record('h5pactivity', ['id' => $activity->id]));
|
|
$this->assertEquals(2, $DB->count_records('grade_items'));
|
|
$this->assertEquals(1, $DB->count_records('grade_grades'));
|
|
$this->assertEquals(0, $DB->count_records('xapi_states'));
|
|
}
|
|
}
|