Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
<?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 assignfeedback_editpdf;

use mod_assign_test_generator;

defined('MOODLE_INTERNAL') || die();

global $CFG;
require_once($CFG->dirroot . '/mod/assign/tests/generator.php');

/**
 * Unit tests for assignfeedback_editpdf\comments_quick_list
 *
 * @package    assignfeedback_editpdf
 * @category   test
 * @copyright  2013 Damyon Wiese
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class feedback_test extends \advanced_testcase {

    // Use the generator helper.
    use mod_assign_test_generator;

    /**
     * Ensure that GS is available.
     */
    protected function require_ghostscript() {
        // Skip this test if ghostscript is not supported.
        $result = pdf::test_gs_path(false);
        if ($result->status !== pdf::GSPATH_OK) {
            $this->markTestSkipped('Ghostscript not setup');
        }
    }

    /**
     * Helper method to add a file to a submission.
     *
< * @param stdClass $student Student submitting. < * @param assign $assign Assignment being submitted.
> * @param \stdClass $student Student submitting. > * @param \assign $assign Assignment being submitted.
* @param bool $textfile Use textfile fixture instead of pdf. */ protected function add_file_submission($student, $assign, $textfile = false) { global $CFG; $this->setUser($student); // Create a file submission with the test pdf. $submission = $assign->get_user_submission($student->id, true); $fs = get_file_storage(); $filerecord = (object) array( 'contextid' => $assign->get_context()->id, 'component' => 'assignsubmission_file', 'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA, 'itemid' => $submission->id, 'filepath' => '/', 'filename' => $textfile ? 'submission.txt' : 'submission.pdf' ); $sourcefile = $CFG->dirroot . '/mod/assign/feedback/editpdf/tests/fixtures/submission.' . ($textfile ? 'txt' : 'pdf'); $fs->create_file_from_pathname($filerecord, $sourcefile); $data = new \stdClass(); $plugin = $assign->get_submission_plugin_by_type('file'); $plugin->save($submission, $data); } public function test_comments_quick_list() { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); $this->setUser($teacher); $this->assertEmpty(comments_quick_list::get_comments()); $comment = comments_quick_list::add_comment('test', 45, 'red'); $comments = comments_quick_list::get_comments(); $this->assertEquals(count($comments), 1); $first = reset($comments); $this->assertEquals($comment, $first); $commentbyid = comments_quick_list::get_comment($comment->id); $this->assertEquals($comment, $commentbyid); $this->assertTrue(comments_quick_list::remove_comment($comment->id)); $comments = comments_quick_list::get_comments(); $this->assertEmpty($comments); } public function test_page_editor() { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $assign = $this->create_instance($course, [ 'assignsubmission_onlinetext_enabled' => 1, 'assignsubmission_file_enabled' => 1, 'assignsubmission_file_maxfiles' => 1, 'assignfeedback_editpdf_enabled' => 1, 'assignsubmission_file_maxsizebytes' => 1000000, ]); // Add the standard submission. $this->add_file_submission($student, $assign); $this->setUser($teacher); $grade = $assign->get_user_grade($student->id, true); $notempty = page_editor::has_annotations_or_comments($grade->id, false); $this->assertFalse($notempty); $comment = new comment(); $comment->rawtext = 'Comment text'; $comment->width = 100; $comment->x = 100; $comment->y = 100; $comment->colour = 'red'; $comment2 = new comment(); $comment2->rawtext = 'Comment text 2'; $comment2->width = 100; $comment2->x = 200; $comment2->y = 100; $comment2->colour = 'clear'; page_editor::set_comments($grade->id, 0, array($comment, $comment2)); $annotation = new annotation(); $annotation->path = ''; $annotation->x = 100; $annotation->y = 100; $annotation->endx = 200; $annotation->endy = 200; $annotation->type = 'line'; $annotation->colour = 'red'; $annotation2 = new annotation(); $annotation2->path = ''; $annotation2->x = 100; $annotation2->y = 100; $annotation2->endx = 200; $annotation2->endy = 200; $annotation2->type = 'rectangle'; $annotation2->colour = 'yellow'; page_editor::set_annotations($grade->id, 0, array($annotation, $annotation2)); // Still empty because all edits are still drafts. $this->assertFalse(page_editor::has_annotations_or_comments($grade->id, false)); $comments = page_editor::get_comments($grade->id, 0, false); $this->assertEmpty($comments); $comments = page_editor::get_comments($grade->id, 0, true); $this->assertEquals(count($comments), 2); $annotations = page_editor::get_annotations($grade->id, 0, false); $this->assertEmpty($annotations); $annotations = page_editor::get_annotations($grade->id, 0, true); $this->assertEquals(count($annotations), 2); $comment = reset($comments); $annotation = reset($annotations); page_editor::remove_comment($comment->id); page_editor::remove_annotation($annotation->id); $comments = page_editor::get_comments($grade->id, 0, true); $this->assertEquals(count($comments), 1); $annotations = page_editor::get_annotations($grade->id, 0, true); $this->assertEquals(count($annotations), 1); // Release the drafts. page_editor::release_drafts($grade->id); $notempty = page_editor::has_annotations_or_comments($grade->id, false); $this->assertTrue($notempty); // Unrelease the drafts. page_editor::unrelease_drafts($grade->id); $notempty = page_editor::has_annotations_or_comments($grade->id, false); $this->assertFalse($notempty); } public function test_document_services() { $this->require_ghostscript(); $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $assign = $this->create_instance($course, [ 'assignsubmission_onlinetext_enabled' => 1, 'assignsubmission_file_enabled' => 1, 'assignsubmission_file_maxfiles' => 1, 'assignfeedback_editpdf_enabled' => 1, 'assignsubmission_file_maxsizebytes' => 1000000, ]); // Add the standard submission. $this->add_file_submission($student, $assign); $this->setUser($teacher); $grade = $assign->get_user_grade($student->id, true); $contextid = $assign->get_context()->id; $component = 'assignfeedback_editpdf'; $filearea = document_services::COMBINED_PDF_FILEAREA; $itemid = $grade->id; $filepath = '/'; $filename = document_services::COMBINED_PDF_FILENAME; $fs = \get_file_storage(); // Generate a blank combined pdf. $record = new \stdClass(); $record->contextid = $contextid; $record->component = $component; $record->filearea = $filearea; $record->itemid = $itemid; $record->filepath = $filepath; $record->filename = $filename; $fs->create_file_from_string($record, base64_decode(document_services::BLANK_PDF_BASE64)); // Verify that the blank combined pdf has the expected hash. $combinedpdf = $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename); $this->assertEquals($combinedpdf->get_contenthash(), document_services::BLANK_PDF_HASH); // Generate page images and verify that the combined pdf has been replaced. document_services::get_page_images_for_attempt($assign, $student->id, -1); $combinedpdf = $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename); $this->assertNotEquals($combinedpdf->get_contenthash(), document_services::BLANK_PDF_HASH); $notempty = page_editor::has_annotations_or_comments($grade->id, false); $this->assertFalse($notempty); $comment = new comment(); // Use some different charset in the comment text. $comment->rawtext = 'Testing example: בקלות ואמנות'; $comment->width = 100; $comment->x = 100; $comment->y = 100; $comment->colour = 'red'; page_editor::set_comments($grade->id, 0, array($comment)); $annotations = array(); $annotation = new annotation(); $annotation->path = ''; $annotation->x = 100; $annotation->y = 100; $annotation->endx = 200; $annotation->endy = 200; $annotation->type = 'line'; $annotation->colour = 'red'; array_push($annotations, $annotation); $annotation = new annotation(); $annotation->path = ''; $annotation->x = 100; $annotation->y = 100; $annotation->endx = 200; $annotation->endy = 200; $annotation->type = 'rectangle'; $annotation->colour = 'yellow'; array_push($annotations, $annotation); $annotation = new annotation(); $annotation->path = ''; $annotation->x = 100; $annotation->y = 100; $annotation->endx = 200; $annotation->endy = 200; $annotation->type = 'oval'; $annotation->colour = 'green'; array_push($annotations, $annotation); $annotation = new annotation(); $annotation->path = ''; $annotation->x = 100; $annotation->y = 100; $annotation->endx = 200; $annotation->endy = 116; $annotation->type = 'highlight'; $annotation->colour = 'blue'; array_push($annotations, $annotation); $annotation = new annotation(); $annotation->path = '100,100:105,105:110,100'; $annotation->x = 100; $annotation->y = 100; $annotation->endx = 110; $annotation->endy = 105; $annotation->type = 'pen'; $annotation->colour = 'black'; array_push($annotations, $annotation); page_editor::set_annotations($grade->id, 0, $annotations); page_editor::release_drafts($grade->id); $notempty = page_editor::has_annotations_or_comments($grade->id, false); $this->assertTrue($notempty); $file = document_services::generate_feedback_document($assign->get_instance()->id, $grade->userid, $grade->attemptnumber); $this->assertNotEmpty($file); $file2 = document_services::get_feedback_document($assign->get_instance()->id, $grade->userid, $grade->attemptnumber); $this->assertEquals($file, $file2); document_services::delete_feedback_document($assign->get_instance()->id, $grade->userid, $grade->attemptnumber); $file3 = document_services::get_feedback_document($assign->get_instance()->id, $grade->userid, $grade->attemptnumber); $this->assertEmpty($file3); }
> /** public function test_conversion_task() { > * Test Convert submission ad-hoc task. global $DB; > * $this->require_ghostscript(); > * @covers \assignfeedback_editpdf\task\convert_submission $this->resetAfterTest(); > */
< global $DB;
< cron_setup_user(); < < $task = new \assignfeedback_editpdf\task\convert_submissions;
> \core\cron::setup_user();
$course = $this->getDataGenerator()->create_course(); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $assignopts = [ 'assignsubmission_file_enabled' => 1, 'assignsubmission_file_maxfiles' => 1, 'assignfeedback_editpdf_enabled' => 1, 'assignsubmission_file_maxsizebytes' => 1000000, ]; $assign = $this->create_instance($course, $assignopts); // Add the standard submission. $this->add_file_submission($student, $assign); // Run the conversion task.
> $task = \core\task\manager::get_next_adhoc_task(time());
ob_start(); $task->execute();
> \core\task\manager::adhoc_task_complete($task);
$output = ob_get_clean();
< // Verify it acted on both submissions in the queue. < $this->assertStringContainsString("Convert 1 submission attempt(s) for assignment {$assign->get_instance()->id}", $output); < $this->assertEquals(0, $DB->count_records('assignfeedback_editpdf_queue')); < < // Set a known limit. < set_config('conversionattemptlimit', 3);
> // Confirm, that submission has been converted and the task queue is now empty. > $this->assertStringContainsString('Converting submission for user id ' . $student->id, $output); > $this->assertStringContainsString('The document has been successfully converted', $output); > $this->assertNull(\core\task\manager::get_next_adhoc_task(time()));
// Trigger a re-queue by 'updating' a submission. $submission = $assign->get_user_submission($student->id, true); $plugin = $assign->get_submission_plugin_by_type('file'); $plugin->save($submission, (new \stdClass));
> $task = \core\task\manager::get_next_adhoc_task(time());
// Verify that queued a conversion task.
< $this->assertEquals(1, $DB->count_records('assignfeedback_editpdf_queue')); < < // Fake some failed attempts for it. < $queuerecord = $DB->get_record('assignfeedback_editpdf_queue', ['submissionid' => $submission->id]); < $queuerecord->attemptedconversions = 3; < $DB->update_record('assignfeedback_editpdf_queue', $queuerecord);
> $this->assertNotNull($task);
ob_start(); $task->execute();
> \core\task\manager::adhoc_task_complete($task);
$output = ob_get_clean();
< // Verify that the cron task skipped the submission. < $this->assertStringNotContainsString("Convert 1 submission attempt(s) for assignment {$assign->get_instance()->id}", $output); < // And it removed it from the queue. < $this->assertEquals(0, $DB->count_records('assignfeedback_editpdf_queue')); <
> // Confirm, that submission has been converted and the task queue is now empty. > $this->assertStringContainsString('Converting submission for user id ' . $student->id, $output); > $this->assertStringContainsString('The document has been successfully converted', $output); > $this->assertNull(\core\task\manager::get_next_adhoc_task(time()));
} /** * Test that modifying the annotated pdf form return true when modified * and false when not modified. */ public function test_is_feedback_modified() { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $assign = $this->create_instance($course, [ 'assignsubmission_onlinetext_enabled' => 1, 'assignsubmission_file_enabled' => 1, 'assignsubmission_file_maxfiles' => 1, 'assignfeedback_editpdf_enabled' => 1, 'assignsubmission_file_maxsizebytes' => 1000000, ]); // Add the standard submission. $this->add_file_submission($student, $assign); $this->setUser($teacher); $grade = $assign->get_user_grade($student->id, true); $notempty = page_editor::has_annotations_or_comments($grade->id, false); $this->assertFalse($notempty); $comment = new comment(); $comment->rawtext = 'Comment text'; $comment->width = 100; $comment->x = 100; $comment->y = 100; $comment->colour = 'red'; page_editor::set_comments($grade->id, 0, array($comment)); $annotations = array(); $annotation = new annotation(); $annotation->path = ''; $annotation->x = 100; $annotation->y = 100; $annotation->endx = 200; $annotation->endy = 200; $annotation->type = 'line'; $annotation->colour = 'red'; array_push($annotations, $annotation); page_editor::set_annotations($grade->id, 0, $annotations); $plugin = $assign->get_feedback_plugin_by_type('editpdf'); $data = new \stdClass(); $data->editpdf_source_userid = $student->id; $this->assertTrue($plugin->is_feedback_modified($grade, $data)); $plugin->save($grade, $data); $annotation = new annotation(); $annotation->gradeid = $grade->id; $annotation->pageno = 0; $annotation->path = ''; $annotation->x = 100; $annotation->y = 100; $annotation->endx = 200; $annotation->endy = 200; $annotation->type = 'rectangle'; $annotation->colour = 'yellow'; $yellowannotationid = page_editor::add_annotation($annotation); // Add a comment as well. $comment = new comment(); $comment->gradeid = $grade->id; $comment->pageno = 0; $comment->rawtext = 'Second Comment text'; $comment->width = 100; $comment->x = 100; $comment->y = 100; $comment->colour = 'red'; page_editor::add_comment($comment); $this->assertTrue($plugin->is_feedback_modified($grade, $data)); $plugin->save($grade, $data); // We should have two annotations. $this->assertCount(2, page_editor::get_annotations($grade->id, 0, false)); // And two comments. $this->assertCount(2, page_editor::get_comments($grade->id, 0, false)); // Add one annotation and delete another. $annotation = new annotation(); $annotation->gradeid = $grade->id; $annotation->pageno = 0; $annotation->path = '100,100:105,105:110,100'; $annotation->x = 100; $annotation->y = 100; $annotation->endx = 110; $annotation->endy = 105; $annotation->type = 'pen'; $annotation->colour = 'black'; page_editor::add_annotation($annotation); $annotations = page_editor::get_annotations($grade->id, 0, true); page_editor::remove_annotation($yellowannotationid); $this->assertTrue($plugin->is_feedback_modified($grade, $data)); $plugin->save($grade, $data); // We should have two annotations. $this->assertCount(2, page_editor::get_annotations($grade->id, 0, false)); // And two comments. $this->assertCount(2, page_editor::get_comments($grade->id, 0, false)); // Add a comment and then remove it. Should not be considered as modified. $comment = new comment(); $comment->gradeid = $grade->id; $comment->pageno = 0; $comment->rawtext = 'Third Comment text'; $comment->width = 400; $comment->x = 57; $comment->y = 205; $comment->colour = 'black'; $comment->id = page_editor::add_comment($comment); // We should now have three comments. $this->assertCount(3, page_editor::get_comments($grade->id, 0, true)); // Now delete the newest record. page_editor::remove_comment($comment->id); // Back to two comments. $this->assertCount(2, page_editor::get_comments($grade->id, 0, true)); // No modification. $this->assertFalse($plugin->is_feedback_modified($grade, $data)); } /**
< * Test Convert submissions scheduled task limit. < * < * @covers \assignfeedback_editpdf\task\convert_submissions < */ < public function test_conversion_task_limit() { < global $DB; < $this->require_ghostscript(); < $this->resetAfterTest(); < cron_setup_user(); < < $course = $this->getDataGenerator()->create_course(); < $assignopts = [ < 'assignsubmission_file_enabled' => 1, < 'assignsubmission_file_maxfiles' => 1, < 'assignfeedback_editpdf_enabled' => 1, < 'assignsubmission_file_maxsizebytes' => 1000000, < ]; < $assign = $this->create_instance($course, $assignopts); < < // Generate 110 submissions. < for ($i = 0; $i < 110; $i++) { < $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); < $this->add_file_submission($student, $assign); < } < $this->assertEquals(110, $DB->count_records('assignfeedback_editpdf_queue')); < < // Run the conversion task. < $task = new \assignfeedback_editpdf\task\convert_submissions; < ob_start(); < $task->execute(); < ob_end_clean(); < < // Confirm, that 100 records were processed and 10 were left for the next task run. < $this->assertEquals(10, $DB->count_records('assignfeedback_editpdf_queue')); < } < < /**
* Test that overwriting a submission file deletes any associated conversions.
> * */ > * @covers \core_files\conversion::get_conversions_for_file
public function test_submission_file_overridden() { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $assign = $this->create_instance($course, [ 'assignsubmission_onlinetext_enabled' => 1, 'assignsubmission_file_enabled' => 1, 'assignsubmission_file_maxfiles' => 1, 'assignfeedback_editpdf_enabled' => 1, 'assignsubmission_file_maxsizebytes' => 1000000, ]); $this->add_file_submission($student, $assign, true); $submission = $assign->get_user_submission($student->id, true); $fs = get_file_storage(); $sourcefile = $fs->get_file( $assign->get_context()->id, 'assignsubmission_file', ASSIGNSUBMISSION_FILE_FILEAREA, $submission->id, '/', 'submission.txt' ); $conversion = new \core_files\conversion(0, (object)[ 'sourcefileid' => $sourcefile->get_id(), 'targetformat' => 'pdf' ]); $conversion->create(); $conversions = \core_files\conversion::get_conversions_for_file($sourcefile, 'pdf'); $this->assertCount(1, $conversions); $filerecord = (object)[ 'contextid' => $assign->get_context()->id, 'component' => 'core', 'filearea' => 'unittest', 'itemid' => $submission->id, 'filepath' => '/', 'filename' => 'submission.txt' ]; $fs = get_file_storage(); $newfile = $fs->create_file_from_string($filerecord, 'something totally different'); $sourcefile->replace_file_with($newfile); $conversions = \core_files\conversion::get_conversions_for_file($sourcefile, 'pdf'); $this->assertCount(0, $conversions);
> } } > } > /** > * Tests that when the plugin is not enabled for an assignment it does not create conversion tasks. > * > * @covers \assignfeedback_editpdf\event\observer > */ > public function test_submission_not_enabled() { > $this->require_ghostscript(); > $this->resetAfterTest(); > \core\cron::setup_user(); > > $course = $this->getDataGenerator()->create_course(); > $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); > $assignopts = [ > 'assignsubmission_file_enabled' => 1, > 'assignsubmission_file_maxfiles' => 1, > 'assignfeedback_editpdf_enabled' => 0, > 'assignsubmission_file_maxsizebytes' => 1000000, > ]; > $assign = $this->create_instance($course, $assignopts); > > // Add the standard submission. > $this->add_file_submission($student, $assign); > > $task = \core\task\manager::get_next_adhoc_task(time()); > > // No task was created. > $this->assertNull($task);