From 881107a977be116bf7597b4650e2a3c83bcacb9f Mon Sep 17 00:00:00 2001 From: Mathew May Date: Wed, 21 Feb 2024 14:46:53 +0800 Subject: [PATCH] MDL-79863 qtype_ordering: Unit test updates --- .../output/formulation_and_controls.php | 4 +- .../output/specific_grade_detail_feedback.php | 2 +- question/type/ordering/edit_ordering_form.php | 10 +- question/type/ordering/question.php | 4 +- question/type/ordering/questiontype.php | 8 +- question/type/ordering/tests/backup_test.php | 10 +- .../tests/behat/behat_qtype_ordering.php | 13 +- question/type/ordering/tests/coverage.php | 1 - .../tests/fixtures/testexport.gift.txt | 4 +- .../tests/fixtures/testexport.moodle.xml | 8 +- .../tests/fixtures/testimport.moodle.xml | 6 + .../tests/fixtures/testimportempty.moodle.xml | 64 ---------- question/type/ordering/tests/helper.php | 35 +++++- .../tests/output/correct_response_test.php | 25 ++-- .../ordering/tests/output/feedback_test.php | 26 ++-- .../output/formulation_and_controls_test.php | 80 +++++++++++-- .../tests/output/num_parts_correct_test.php | 16 ++- .../specific_grade_detail_feedback_test.php | 44 +++++-- .../type/ordering/tests/question_test.php | 40 +++---- .../type/ordering/tests/questionhint_test.php | 111 ++++++++++++++++++ .../type/ordering/tests/questiontype_test.php | 102 +++++++--------- .../type/ordering/tests/walkthrough_test.php | 14 +-- 22 files changed, 386 insertions(+), 241 deletions(-) delete mode 100644 question/type/ordering/tests/fixtures/testimportempty.moodle.xml create mode 100644 question/type/ordering/tests/questionhint_test.php diff --git a/question/type/ordering/classes/output/formulation_and_controls.php b/question/type/ordering/classes/output/formulation_and_controls.php index 195fdaa5fb3..51f5383bc1c 100644 --- a/question/type/ordering/classes/output/formulation_and_controls.php +++ b/question/type/ordering/classes/output/formulation_and_controls.php @@ -99,7 +99,9 @@ class formulation_and_controls extends renderable_base { foreach ($currentresponse as $position => $answerid) { if (!array_key_exists($answerid, $question->answers) || !array_key_exists($position, $correctresponse)) { - continue; // Shouldn't happen !! + // @codeCoverageIgnoreStart + continue; // This shouldn't happen. + // @codeCoverageIgnoreEnd } // Format the answer text. diff --git a/question/type/ordering/classes/output/specific_grade_detail_feedback.php b/question/type/ordering/classes/output/specific_grade_detail_feedback.php index 7de9472d911..81f27d0641b 100644 --- a/question/type/ordering/classes/output/specific_grade_detail_feedback.php +++ b/question/type/ordering/classes/output/specific_grade_detail_feedback.php @@ -88,7 +88,7 @@ class specific_grade_detail_feedback extends renderable_base { } } - if ($totalmaxscore == 0) { + if ($question->gradingtype === qtype_ordering_question::GRADING_ALL_OR_NOTHING || $totalmaxscore == 0) { unset($data['scoredetails']); // All or nothing. } else { // Format gradedetails, e.g. 4/6 = 67%. diff --git a/question/type/ordering/edit_ordering_form.php b/question/type/ordering/edit_ordering_form.php index 4313d17e387..1d16b7e39df 100644 --- a/question/type/ordering/edit_ordering_form.php +++ b/question/type/ordering/edit_ordering_form.php @@ -14,14 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -/** - * Defines the editing form for the ordering question type. - * - * @package qtype_ordering - * @copyright 2013 Gordon Bateson (gordon.bateson@gmail.com) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - // Prevent direct access to this script. defined('MOODLE_INTERNAL') || die(); @@ -31,8 +23,10 @@ require_once($CFG->dirroot.'/question/type/ordering/question.php'); /** * Ordering editing form definition * + * @package qtype_ordering * @copyright 2013 Gordon Bateson (gordon.bateson@gmail.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @codeCoverageIgnore Edit form is covered via behat and mocked in units. */ class qtype_ordering_edit_form extends question_edit_form { diff --git a/question/type/ordering/question.php b/question/type/ordering/question.php index 5d8df10995f..5eb22ff2887 100644 --- a/question/type/ordering/question.php +++ b/question/type/ordering/question.php @@ -285,10 +285,9 @@ class qtype_ordering_question extends question_graded_automatically { */ public function summarise_response(array $response) { $name = $this->get_response_fieldname(); + $items = []; if (array_key_exists($name, $response)) { $items = explode(',', $response[$name]); - } else { - $items = []; // Shouldn't happen! } $answerids = []; foreach ($this->answers as $answer) { @@ -782,6 +781,7 @@ class qtype_ordering_question extends question_graded_automatically { * @param int $type * @return array|string array if $type is not specified and single string if $type is specified * @throws coding_exception + * @codeCoverageIgnore */ public static function get_types(array $types, $type): array|string { if ($type === null) { diff --git a/question/type/ordering/questiontype.php b/question/type/ordering/questiontype.php index 32a6a06c777..512b1619a6e 100644 --- a/question/type/ordering/questiontype.php +++ b/question/type/ordering/questiontype.php @@ -34,6 +34,7 @@ class qtype_ordering extends question_type { /** * @return bool whether the question_answers.answer field needs to have * restore_decode_content_links_worker called on it. + * @CodeCoverageIgnore */ public function has_html_answers(): bool { return true; @@ -787,11 +788,12 @@ class qtype_ordering extends question_type { * Fix empty or long question name * * @param string $name The name of the question - * @param string $defaultname (optional, default='') The default name of the question - * @param integer $maxnamelength (optional, default=42) The maximum length of the name + * @param string|null $defaultname (optional, default='') The default name of the question + * @param int|null $maxnamelength (optional, default=42) The maximum length of the name * @return string Fixed name + * @throws coding_exception */ - public function fix_questionname(string $name, string $defaultname = '', int $maxnamelength = 42): string { + public function fix_questionname(string $name, ?string $defaultname = '', ?int $maxnamelength = 42): string { if (trim($name) == '') { if ($defaultname) { $name = $defaultname; diff --git a/question/type/ordering/tests/backup_test.php b/question/type/ordering/tests/backup_test.php index 26c43ff023e..d730465d1fa 100644 --- a/question/type/ordering/tests/backup_test.php +++ b/question/type/ordering/tests/backup_test.php @@ -17,7 +17,6 @@ namespace qtype_ordering; use question_bank; -use test_question_maker; defined('MOODLE_INTERNAL') || die(); @@ -32,11 +31,10 @@ require_once($CFG->dirroot . '/course/externallib.php'); * @package qtype_ordering * @copyright 2020 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later. - * @covers \backup_qtype_ordering_plugin - * @covers \restore_qtype_ordering_plugin + * @covers \backup_qtype_ordering_plugin + * @covers \restore_qtype_ordering_plugin */ -class backup_test extends \advanced_testcase { - +final class backup_test extends \advanced_testcase { /** * Duplicate quiz with a orderinging question, and check it worked. */ @@ -54,7 +52,7 @@ class backup_test extends \advanced_testcase { $quizcontext = \context_module::instance($quiz->cmid); $cat = $questiongenerator->create_question_category(['contextid' => $quizcontext->id]); - $question = $questiongenerator->create_question('ordering', 'moodle', ['category' => $cat->id]); + $question = $questiongenerator->create_question('ordering', 'moodle', ['category' => $cat->id, 'shownumcorrect' => null]); // Store some counts. $numquizzes = count(get_fast_modinfo($course)->instances['quiz']); diff --git a/question/type/ordering/tests/behat/behat_qtype_ordering.php b/question/type/ordering/tests/behat/behat_qtype_ordering.php index 9953589ea5b..4347e2179fd 100644 --- a/question/type/ordering/tests/behat/behat_qtype_ordering.php +++ b/question/type/ordering/tests/behat/behat_qtype_ordering.php @@ -14,22 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -/** - * Behat steps definitions for the ordering question type. - * - * @package qtype_ordering - * @category test - * @copyright 2018 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. require_once(__DIR__ . '/../../../../../lib/behat/behat_base.php'); /** - * Steps definitions related with the ordering question type. + * Behat steps definitions for the ordering question type. * + * @package qtype_ordering + * @category test * @copyright 2018 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ diff --git a/question/type/ordering/tests/coverage.php b/question/type/ordering/tests/coverage.php index ac3bc97e5a5..9eec85ca75c 100644 --- a/question/type/ordering/tests/coverage.php +++ b/question/type/ordering/tests/coverage.php @@ -31,7 +31,6 @@ return new class extends phpunit_coverage_info { /** @var array The list of files relative to the plugin root to include in coverage generation. */ protected $includelistfiles = [ - 'edit_ordering_form.php', 'question.php', 'questiontype.php', ]; diff --git a/question/type/ordering/tests/fixtures/testexport.gift.txt b/question/type/ordering/tests/fixtures/testexport.gift.txt index cb01abcf087..3dd297b3ebd 100644 --- a/question/type/ordering/tests/fixtures/testexport.gift.txt +++ b/question/type/ordering/tests/fixtures/testexport.gift.txt @@ -1,6 +1,6 @@ -// question: 409000 name: Moodle +// question: 123 name: Moodle // [id:myid] -::Moodle::[html]Put these words in order.{>0 none +::Moodle::[html]Put these words in order.{>2 none Modular Object Oriented diff --git a/question/type/ordering/tests/fixtures/testexport.moodle.xml b/question/type/ordering/tests/fixtures/testexport.moodle.xml index cb3158fd57b..8a35b313307 100644 --- a/question/type/ordering/tests/fixtures/testexport.moodle.xml +++ b/question/type/ordering/tests/fixtures/testexport.moodle.xml @@ -1,4 +1,4 @@ - + Moodle @@ -65,4 +65,10 @@ Environment is correct. + + Hint 1 + + + Hint 2 + diff --git a/question/type/ordering/tests/fixtures/testimport.moodle.xml b/question/type/ordering/tests/fixtures/testimport.moodle.xml index 821cbf99210..e797ac0b378 100644 --- a/question/type/ordering/tests/fixtures/testimport.moodle.xml +++ b/question/type/ordering/tests/fixtures/testimport.moodle.xml @@ -61,4 +61,10 @@ + + Hint 1 + + + Hint 2 + diff --git a/question/type/ordering/tests/fixtures/testimportempty.moodle.xml b/question/type/ordering/tests/fixtures/testimportempty.moodle.xml deleted file mode 100644 index df84d708588..00000000000 --- a/question/type/ordering/tests/fixtures/testimportempty.moodle.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - Put these words in order. - - - - - - - - 1 - 0.3333333 - 0 - myid - none - - Your answer is correct.

]]>
-
- - Your answer is partially correct.

]]>
-
- - Your answer is incorrect.

]]>
-
- - Modular - - - - - - Object - - - - - - Oriented - - - - - - Dynamic - - - - - - Learning - - - - - - Environment - - - - -
diff --git a/question/type/ordering/tests/helper.php b/question/type/ordering/tests/helper.php index a942075d90a..661dab87412 100644 --- a/question/type/ordering/tests/helper.php +++ b/question/type/ordering/tests/helper.php @@ -17,9 +17,9 @@ /** * Test helper for the ordering question type. * - * @package qtype_ordering - * @copyright 2018 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package qtype_ordering + * @copyright 2018 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); @@ -36,6 +36,11 @@ require_once($CFG->dirroot . '/question/type/ordering/question.php'); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qtype_ordering_test_helper extends question_test_helper { + /** + * Get the question types that this helper can handle. + * + * @return array the question types. + */ public function get_test_questions(): array { return ['moodle']; } @@ -48,6 +53,16 @@ class qtype_ordering_test_helper extends question_test_helper { public function make_ordering_question_moodle(): qtype_ordering_question { question_bank::load_question_definition_classes('ordering'); $q = new qtype_ordering_question(); + $q->hints = [ + [ + 'text' => 'Hint 1', + 'format' => FORMAT_HTML, + ], + [ + 'text' => 'Hint 2', + 'format' => FORMAT_HTML, + ], + ]; test_question_maker::initialise_a_question($q); $q->qtype = question_bank::get_qtype('ordering'); $q->name = 'Moodle'; @@ -144,8 +159,18 @@ class qtype_ordering_test_helper extends question_test_helper { test_question_maker::set_standard_combined_feedback_form_data($form); $form->penalty = '0.3333333'; - $form->numhints = 0; - $form->hint = []; + // Build the expected hint base. + $form->numhints = 2; + $form->hint = [ + [ + 'text' => 'Hint 1', + 'format' => FORMAT_HTML, + ], + [ + 'text' => 'Hint 2', + 'format' => FORMAT_HTML, + ], + ];; $form->qtype = 'ordering'; return $form; diff --git a/question/type/ordering/tests/output/correct_response_test.php b/question/type/ordering/tests/output/correct_response_test.php index fc9de85742a..cc49d428144 100644 --- a/question/type/ordering/tests/output/correct_response_test.php +++ b/question/type/ordering/tests/output/correct_response_test.php @@ -29,12 +29,13 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); /** * A test class used to test correct_response. * - * @package qtype_ordering - * @copyright 2023 Mihail Geshoski - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @covers \qtype_ordering\output\correct_response + * @package qtype_ordering + * @copyright 2023 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_ordering\output\renderable_base + * @covers \qtype_ordering\output\correct_response */ -class correct_response_test extends advanced_testcase { +final class correct_response_test extends advanced_testcase { /** * Test the exported data for the template that renders the correct response to a given question attempt. * @@ -43,7 +44,6 @@ class correct_response_test extends advanced_testcase { * @param string $layouttype The type of the layout. * @param array $expected The expected exported data. * @return void - * @covers ::export_for_template */ public function test_export_for_template(array $currentresponse, string $layouttype, array $expected): void { global $PAGE; @@ -53,6 +53,7 @@ class correct_response_test extends advanced_testcase { $question->gradingtype = qtype_ordering_question::GRADING_ABSOLUTE_POSITION; $question->layouttype = $layouttype === 'horizontal' ? qtype_ordering_question::LAYOUT_HORIZONTAL : qtype_ordering_question::LAYOUT_VERTICAL; + // Create a question attempt. $qa = new \testable_question_attempt($question, 0); // Create a question attempt step and add it to the question attempt. @@ -63,6 +64,9 @@ class correct_response_test extends advanced_testcase { // attempt step. [$fraction, $state] = $question->grade_response(qtype_ordering_test_helper::get_response($question, $currentresponse)); $qa->get_last_step()->set_state($state); + if ($expected['hascorrectresponse'] === false) { + $qa->get_question()->correctresponse = 0; + } $renderer = $PAGE->get_renderer('core'); $correctresponse = new correct_response($qa); @@ -75,7 +79,7 @@ class correct_response_test extends advanced_testcase { * * @return array */ - public function export_for_template_provider(): array { + public static function export_for_template_provider(): array { return [ 'Correct question attempt.' => [ @@ -137,6 +141,13 @@ class correct_response_test extends advanced_testcase { ], ], ], + 'Correct state not set somehow' => [ + ['Modular', 'Object', 'Oriented', 'Dynamic', 'Learning', 'Environment'], + 'horizontal', + [ + 'hascorrectresponse' => false, + ], + ], ]; } } diff --git a/question/type/ordering/tests/output/feedback_test.php b/question/type/ordering/tests/output/feedback_test.php index 951c4f9d4d4..0ec705e7bab 100644 --- a/question/type/ordering/tests/output/feedback_test.php +++ b/question/type/ordering/tests/output/feedback_test.php @@ -30,14 +30,15 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); /** * Test the feedback exporter. * - * @package qtype_ordering - * @copyright 2023 Mathew May - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package qtype_ordering + * @copyright 2023 Mathew May + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_ordering\output\renderable_base + * @covers \qtype_ordering\output\feedback */ -class feedback_test extends qbehaviour_walkthrough_test_base { - - /** @var array $correctanswers The correct answers for the question, added to quickly reference. */ - private $correctanswers = [ +final class feedback_test extends qbehaviour_walkthrough_test_base { + /** @var array $CORRECTANSWERS The correct answers for the question, added to quickly reference. */ + const CORRECTANSWERS = [ 0 => [ 'answertext' => 'Modular', ], @@ -61,7 +62,6 @@ class feedback_test extends qbehaviour_walkthrough_test_base { /** * Test the exported data for the template that renders the feedback test to a given question attempt. * - * @covers \qtype_ordering\output\feedback::export_for_template() * @dataProvider export_for_template_provider * @param array $answeritems The array of ordered answers. * @param int $gradingtype Grading type. @@ -119,7 +119,7 @@ class feedback_test extends qbehaviour_walkthrough_test_base { * * @return array */ - public function export_for_template_provider(): array { + public static function export_for_template_provider(): array { global $CFG; require_once($CFG->dirroot . '/question/type/ordering/question.php'); @@ -201,7 +201,7 @@ class feedback_test extends qbehaviour_walkthrough_test_base { 'hascorrectresponse' => true, 'showcorrect' => true, 'orderinglayoutclass' => 'horizontal', - 'correctanswers' => $this->correctanswers, + 'correctanswers' => self::CORRECTANSWERS, ], ], ], @@ -233,7 +233,7 @@ class feedback_test extends qbehaviour_walkthrough_test_base { 'hascorrectresponse' => true, 'showcorrect' => true, 'orderinglayoutclass' => 'horizontal', - 'correctanswers' => $this->correctanswers, + 'correctanswers' => self::CORRECTANSWERS, ], 'numpartscorrect' => [ 'numcorrect' => 4, @@ -298,7 +298,7 @@ class feedback_test extends qbehaviour_walkthrough_test_base { 'hascorrectresponse' => true, 'showcorrect' => true, 'orderinglayoutclass' => 'horizontal', - 'correctanswers' => $this->correctanswers, + 'correctanswers' => self::CORRECTANSWERS, ], ], ], @@ -358,7 +358,7 @@ class feedback_test extends qbehaviour_walkthrough_test_base { 'hascorrectresponse' => true, 'showcorrect' => true, 'orderinglayoutclass' => 'vertical', - 'correctanswers' => $this->correctanswers, + 'correctanswers' => self::CORRECTANSWERS, ], ], ], diff --git a/question/type/ordering/tests/output/formulation_and_controls_test.php b/question/type/ordering/tests/output/formulation_and_controls_test.php index bc738b89ce6..33c88af6601 100644 --- a/question/type/ordering/tests/output/formulation_and_controls_test.php +++ b/question/type/ordering/tests/output/formulation_and_controls_test.php @@ -30,13 +30,14 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); /** * A test class used to test formulation_and_controls. * - * @package qtype_ordering - * @copyright 2023 Ilya Tregubov - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @covers \qtype_ordering\output\specific_grade_detail_feedback + * @package qtype_ordering + * @copyright 2023 Ilya Tregubov + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_ordering\output\renderable_base + * @covers \qtype_ordering\output\formulation_and_controls + * @covers \qtype_ordering_renderer::feedback_image */ -class formulation_and_controls_test extends advanced_testcase { - +final class formulation_and_controls_test extends advanced_testcase { /** * Test the exported data for the template that renders the formulation and controls for a given question. * @@ -46,7 +47,6 @@ class formulation_and_controls_test extends advanced_testcase { * @param string $layouttype The type of the layout. * @param array $expected The expected exported data. * @return void - * @covers ::export_for_template */ public function test_export_for_template(array $answeritems, int $gradingtype, string $layouttype, array $expected): void { global $PAGE; @@ -74,6 +74,10 @@ class formulation_and_controls_test extends advanced_testcase { $step->set_qt_var('_currentresponse', $keys); [$fraction, $state] = $question->grade_response(qtype_ordering_test_helper::get_response($question, $values)); + // Force the state to be complete if it is graded correct for testing purposes. + if ($state === \question_state::$gradedright) { + $state = \question_state::$complete; + } $qa->get_last_step()->set_state($state); $renderer = $PAGE->get_renderer('core'); @@ -88,7 +92,7 @@ class formulation_and_controls_test extends advanced_testcase { * * @return array */ - public function export_for_template_provider(): array { + public static function export_for_template_provider(): array { global $CFG, $OUTPUT; require_once($CFG->dirroot . '/question/type/ordering/question.php'); @@ -217,6 +221,66 @@ class formulation_and_controls_test extends advanced_testcase { ], ], ], + 'Horizontal, correct and complete' => [ + [13 => 'Modular', 14 => 'Object', 15 => 'Oriented', 16 => 'Dynamic', 17 => 'Learning', 18 => 'Environment'], + qtype_ordering_question::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT, + 'horizontal', + [ + 'readonly' => false, + 'questiontext' => 'Put these words in order', + 'responsename' => 'q0:_response_0', + 'responseid' => 'id_q0_response_0', + 'value' => 'ordering_item_ac5fc041de63c8c5b34d0aabb96cf33d,' . + 'ordering_item_497031794414a552435f90151ac3b54b,' . + 'ordering_item_5a35edab0f2bf86dfa3901baa8c235dc,' . + 'ordering_item_971fd8cc345d8bd9f92e9f7d88fdf20c,' . + 'ordering_item_8af0f5c3edad8d8e158ff27b9f03afac,' . + 'ordering_item_0ba29c6a1afacf586b03a26162c72274', + 'ablockid' => 'id_ablock_0', + 'layoutclass' => 'horizontal', + 'numberingstyle' => 'none', + 'active' => true, + 'sortableid' => 'id_sortable_0', + 'answers' => [ + [ + 'scoreclass' => 'correct', + 'id' => 'ordering_item_' . md5('Modular'), + 'answertext' => "Modular", + 'feedbackimage' => $correct, + ], + [ + 'scoreclass' => 'correct', + 'id' => 'ordering_item_' . md5('Object'), + 'answertext' => "Object", + 'feedbackimage' => $correct, + ], + [ + 'scoreclass' => 'correct', + 'id' => 'ordering_item_' . md5('Oriented'), + 'answertext' => "Oriented", + 'feedbackimage' => $correct, + ], + [ + 'scoreclass' => 'correct', + 'id' => 'ordering_item_' . md5('Dynamic'), + 'answertext' => "Dynamic", + 'feedbackimage' => $correct, + ], + [ + 'scoreclass' => 'correct', + 'id' => 'ordering_item_' . md5('Learning'), + 'answertext' => "Learning", + 'feedbackimage' => $correct, + ], + [ + 'scoreclass' => 'correct', + 'id' => 'ordering_item_' . md5('Environment'), + 'answertext' => "Environment", + 'feedbackimage' => $correct, + ], + ], + ], + ], ]; } } diff --git a/question/type/ordering/tests/output/num_parts_correct_test.php b/question/type/ordering/tests/output/num_parts_correct_test.php index 3dd86428a22..672dbbf89a2 100644 --- a/question/type/ordering/tests/output/num_parts_correct_test.php +++ b/question/type/ordering/tests/output/num_parts_correct_test.php @@ -19,7 +19,6 @@ namespace qtype_ordering\output; use advanced_testcase; use test_question_maker; use qtype_ordering_question; -use qtype_ordering_test_helper; defined('MOODLE_INTERNAL') || die(); @@ -29,13 +28,13 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); /** * A test class used to test specific_grade_detail_feedback. * - * @package qtype_ordering - * @copyright 2023 Ilya Tregubov - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @covers \qtype_ordering\output\specific_grade_detail_feedback + * @package qtype_ordering + * @copyright 2023 Ilya Tregubov + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_ordering\output\renderable_base + * @covers \qtype_ordering\output\num_parts_correct */ -class num_parts_correct_test extends advanced_testcase { - +final class num_parts_correct_test extends advanced_testcase { /** * Test the exported data for the template that renders the specific grade detail feedback test to a given question attempt. * @@ -44,7 +43,6 @@ class num_parts_correct_test extends advanced_testcase { * @param int $gradingtype Grading type. * @param array $expected The expected exported data. * @return void - * @covers ::export_for_template */ public function test_export_for_template(array $answeritems, int $gradingtype, array $expected): void { global $PAGE; @@ -71,7 +69,7 @@ class num_parts_correct_test extends advanced_testcase { * * @return array */ - public function export_for_template_provider(): array { + public static function export_for_template_provider(): array { global $CFG; require_once($CFG->dirroot . '/question/type/ordering/question.php'); diff --git a/question/type/ordering/tests/output/specific_grade_detail_feedback_test.php b/question/type/ordering/tests/output/specific_grade_detail_feedback_test.php index df8d522cfff..dfb094e9233 100644 --- a/question/type/ordering/tests/output/specific_grade_detail_feedback_test.php +++ b/question/type/ordering/tests/output/specific_grade_detail_feedback_test.php @@ -29,13 +29,13 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); /** * A test class used to test specific_grade_detail_feedback. * - * @package qtype_ordering - * @copyright 2023 Ilya Tregubov - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @covers \qtype_ordering\output\specific_grade_detail_feedback + * @package qtype_ordering + * @copyright 2023 Ilya Tregubov + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_ordering\output\renderable_base + * @covers \qtype_ordering\output\specific_grade_detail_feedback */ -class specific_grade_detail_feedback_test extends advanced_testcase { - +final class specific_grade_detail_feedback_test extends advanced_testcase { /** * Test the exported data for the template that renders the specific grade detail feedback test to a given question attempt. * @@ -46,7 +46,6 @@ class specific_grade_detail_feedback_test extends advanced_testcase { * @param array $expected The expected exported data. * @param int $selecttype The type of the select. * @return void - * @covers ::export_for_template */ public function test_export_for_template(array $answeritems, int $gradingtype, string $layouttype, array $expected, int $selecttype): void { @@ -82,8 +81,11 @@ class specific_grade_detail_feedback_test extends advanced_testcase { $this->assertEquals($expected['showpartialwrong'], $actual['showpartialwrong']); $this->assertEquals($expected['gradingtype'], $actual['gradingtype']); $this->assertEquals($expected['orderinglayoutclass'], $actual['orderinglayoutclass']); - $this->assertEquals($expected['totalmaxscore'], $actual['totalmaxscore']); - $this->assertArrayHasKey('scoredetails', $actual); + // All or nothing grading type does not have score details. + if ($gradingtype !== qtype_ordering_question::GRADING_ALL_OR_NOTHING) { + $this->assertEquals($expected['totalmaxscore'], $actual['totalmaxscore']); + $this->assertArrayHasKey('scoredetails', $actual); + } } } @@ -92,7 +94,7 @@ class specific_grade_detail_feedback_test extends advanced_testcase { * * @return array */ - public function export_for_template_provider(): array { + public static function export_for_template_provider(): array { global $CFG; require_once($CFG->dirroot . '/question/type/ordering/question.php'); @@ -238,6 +240,28 @@ class specific_grade_detail_feedback_test extends advanced_testcase { ], qtype_ordering_question::SELECT_CONTIGUOUS, ], + 'Incorrect question attempt (SELECT_CONTIGUOUS). Grading type: GRADING_ALL_OR_NOTHING' => [ + [14 => 'Object', 16 => 'Dynamic', 13 => 'Modular', 17 => 'Learning', 18 => 'Environment', 15 => 'Oriented'], + qtype_ordering_question::GRADING_ALL_OR_NOTHING, + 'vertical', + [ + 'showpartialwrong' => 1, + 'gradingtype' => 'Grading type: All or nothing', + 'orderinglayoutclass' => 'vertical', + 'gradedetails' => 0, + 'totalscore' => 0, + 'totalmaxscore' => 2, + 'scoredetails' => [ + ['score' => 0, 'maxscore' => 1, 'percent' => 0], + ['score' => 1, 'maxscore' => 1, 'percent' => 100.0], + ['score' => 0, 'maxscore' => 1, 'percent' => 0], + ['score' => 'No score', 'maxscore' => null, 'percent' => 0], + ['score' => 'No score', 'maxscore' => null, 'percent' => 0], + ['score' => 'No score', 'maxscore' => null, 'percent' => 0], + ], + ], + qtype_ordering_question::SELECT_CONTIGUOUS, + ], ]; } } diff --git a/question/type/ordering/tests/question_test.php b/question/type/ordering/tests/question_test.php index 7b81bdea853..597932a7a87 100644 --- a/question/type/ordering/tests/question_test.php +++ b/question/type/ordering/tests/question_test.php @@ -14,15 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . - -/** - * Unit tests for the ordering question definition class. - * - * @package qtype_ordering - * @copyright 2018 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - namespace qtype_ordering; use test_question_maker; @@ -41,11 +32,13 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); /** * Unit tests for the ordering question definition class. * + * @package qtype_ordering * @copyright 2018 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @covers \qtype_ordering_question + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_ordering + * @covers \qtype_ordering_question */ -class question_test extends \advanced_testcase { +final class question_test extends \advanced_testcase { /** * Array of draggable items in correct order. */ @@ -92,6 +85,7 @@ class question_test extends \advanced_testcase { ) ); } + public function test_grading_absolute_position(): void { // Create an Ordering question. /** @var qtype_ordering_question $question */ @@ -158,6 +152,7 @@ class question_test extends \advanced_testcase { ) ); } + public function test_grading_relative_next_exclude_last(): void { // Create an Ordering question. /** @var qtype_ordering_question $question */ @@ -207,6 +202,7 @@ class question_test extends \advanced_testcase { ) ); } + public function test_grading_relative_next_include_last(): void { // Create an Ordering question. /** @var qtype_ordering_question $question */ @@ -245,6 +241,7 @@ class question_test extends \advanced_testcase { ) ); } + public function test_grading_relative_one_previous_and_next(): void { // Create an Ordering question. /** @var qtype_ordering_question $question */ @@ -292,6 +289,7 @@ class question_test extends \advanced_testcase { ) ); } + public function test_grading_relative_all_previous_and_next(): void { // Create an Ordering question. /** @var qtype_ordering_question $question */ @@ -348,6 +346,7 @@ class question_test extends \advanced_testcase { ) ); } + public function test_grading_longest_ordered_subset(): void { // Create an Ordering question. /** @var qtype_ordering_question $question */ @@ -395,6 +394,7 @@ class question_test extends \advanced_testcase { ) ); } + public function test_grading_longest_contiguous_subset(): void { // Create an Ordering question. /** @var qtype_ordering_question $question */ @@ -442,6 +442,7 @@ class question_test extends \advanced_testcase { ) ); } + public function test_grading_relative_to_correct(): void { // Create an Ordering question. /** @var qtype_ordering_question $question */ @@ -512,6 +513,7 @@ class question_test extends \advanced_testcase { $question->get_correct_response() ); } + public function test_is_same_response(): void { // Create an Ordering question. /** @var qtype_ordering_question $question */ @@ -579,11 +581,12 @@ class question_test extends \advanced_testcase { /** @var qtype_ordering_question $question */ $question = test_question_maker::make_question('ordering'); - if ($question->layouttype === 0) { - $this->assertEquals('vertical', $question->get_ordering_layoutclass()); - } else if ($question->layouttype === 1) { - $this->assertEquals('horizontal', $question->get_ordering_layoutclass()); - } + $question->layouttype = 0; + $this->assertEquals('vertical', $question->get_ordering_layoutclass()); + + $question->layouttype = 1; + $this->assertEquals('horizontal', $question->get_ordering_layoutclass()); + // Confirm that if an invalid layouttype is set, an empty string is returned. $question->layouttype = 3; $error = $question->get_ordering_layoutclass(); @@ -814,8 +817,5 @@ class question_test extends \advanced_testcase { $this->assertEquals(true, $question->is_complete_response([])); $this->assertEquals(true, $question->is_gradable_response([])); $this->assertEquals('', $question->get_validation_error([])); - - $this->expectException(\coding_exception::class); - qtype_ordering_question::get_types(['foo', 'bar', 'baz'], 'notexist'); } } diff --git a/question/type/ordering/tests/questionhint_test.php b/question/type/ordering/tests/questionhint_test.php new file mode 100644 index 00000000000..f55b41283cf --- /dev/null +++ b/question/type/ordering/tests/questionhint_test.php @@ -0,0 +1,111 @@ +. + +namespace qtype_ordering; + +use advanced_testcase; +use qtype_ordering; +use question_bank; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); + +/** + * A test class used to test question_hint_ordering. + * + * @package qtype_ordering + * @copyright 2024 Mathew May + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_ordering\question_hint_ordering + * @covers \qtype_ordering + */ +final class questionhint_test extends advanced_testcase { + /** @var qtype_ordering instance of the question type class to test. */ + protected $qtype; + + protected function setUp(): void { + $this->qtype = new qtype_ordering(); + } + + protected function tearDown(): void { + $this->qtype = null; + } + /** + * Test that hints can be fetched from the DB. + * @return void + */ + public function test_load_from_record(): void { + $this->resetAfterTest(); + + $obj = (object) [ + 'id' => 13, + 'hint' => 'Hint 1', + 'hintformat' => FORMAT_HTML, + 'shownumcorrect' => 0, + 'clearwrong' => 0, + 'options' => null, + ]; + $hint = question_hint_ordering::load_from_record($obj); + $this->assertInstanceOf(question_hint_ordering::class, $hint); + } + + public function test_make_hint(): void { + $this->resetAfterTest(); + + $syscontext = \context_system::instance(); + $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); + $category = $generator->create_question_category(['contextid' => $syscontext->id]); + + $fromdata = \test_question_maker::get_question_form_data('ordering'); + $fromdata->category = $category->id . ',' . $syscontext->id; + + $question = new \stdClass(); + $question->category = $category->id; + $question->qtype = 'ordering'; + $question->createdby = 0; + + $question = $this->qtype->save_question($question, $fromdata); + $questiondata = question_bank::load_question_data($question->id); + + // Build the expected hint base. + $hintbase = [ + 'questionid' => $questiondata->id, + 'shownumcorrect' => '0', + 'clearwrong' => '0', + 'options' => '0', + ]; + + $expectedhints = []; + foreach ($fromdata->hint as $key => $value) { + $hint = $hintbase + [ + 'hint' => $value['text'], + 'hintformat' => $value['format'], + ]; + $expectedhints[] = (object)$hint; + } + + // Need to get rid of ids. + $gothints = array_map(function($hint) { + unset($hint->id); + return $hint; + }, $questiondata->hints); + + // Compare hints. + $this->assertEquals($expectedhints, array_values($gothints)); + } +} diff --git a/question/type/ordering/tests/questiontype_test.php b/question/type/ordering/tests/questiontype_test.php index ee2930611be..83df17c241f 100644 --- a/question/type/ordering/tests/questiontype_test.php +++ b/question/type/ordering/tests/questiontype_test.php @@ -14,14 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -/** - * Unit tests for the ordering question type class. - * - * @package qtype_ordering - * @copyright 2018 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - namespace qtype_ordering; use core_question_generator; @@ -32,7 +24,7 @@ use qtype_ordering_question; use test_question_maker; use question_bank; use question_possible_response; - +use qformat_xml; use qformat_gift; use question_check_specified_fields_expectation; @@ -52,20 +44,21 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); /** * Unit tests for the ordering question type class. * - * @copyright 20018 The Open University + * @package qtype_ordering + * @copyright 2018 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @covers \qtype_ordering + * @covers \qtype_ordering_question */ -class questiontype_test extends \question_testcase { - /** @var qtype_ordering instance of the question type class to test. */ - protected $qtype; +final class questiontype_test extends \question_testcase { - /** @var object Default import object to compare against. */ - protected $expectedimportobj; - - protected function setUp(): void { - $this->qtype = new qtype_ordering(); - $this->expectedimportobj = (object) [ + /** + * Define the import object to compare against. + * + * @return object The expected import object. + */ + private static function expectedimport(): object { + return (object) [ 'qtype' => 'ordering', 'idnumber' => 'myid', 'name' => 'Moodle', @@ -79,11 +72,6 @@ class questiontype_test extends \question_testcase { ]; } - protected function tearDown(): void { - $this->qtype = null; - $this->expectedimportobj = null; - } - /** * Asserts that two XML strings are the same, ignoring differences in line endings. * @@ -96,11 +84,13 @@ class questiontype_test extends \question_testcase { } public function test_name(): void { - $this->assertEquals('ordering', $this->qtype->name()); + $ordering = new qtype_ordering(); + $this->assertEquals('ordering', $ordering->name()); } public function test_can_analyse_responses(): void { - $this->assertTrue($this->qtype->can_analyse_responses()); + $ordering = new qtype_ordering(); + $this->assertTrue($ordering->can_analyse_responses()); } public function test_question_saving(): void { @@ -122,8 +112,9 @@ class questiontype_test extends \question_testcase { $this->assertTrue($form->is_validated()); $fromform = $form->get_data(); + $ordering = new qtype_ordering(); - $returnedfromsave = $this->qtype->save_question($questiondata, $fromform); + $returnedfromsave = $ordering->save_question($questiondata, $fromform); $actualquestiondata = question_bank::load_question_data($returnedfromsave->id); foreach ($questiondata as $property => $value) { @@ -156,7 +147,8 @@ class questiontype_test extends \question_testcase { public function test_get_possible_responses(): void { $questiondata = test_question_maker::get_question_data('ordering'); - $possibleresponses = $this->qtype->get_possible_responses($questiondata); + $ordering = new qtype_ordering(); + $possibleresponses = $ordering->get_possible_responses($questiondata); $expectedresponseclasses = [ 'Modular' => [ 1 => new question_possible_response('Position 1', 0.1666667), @@ -212,42 +204,44 @@ class questiontype_test extends \question_testcase { public function test_get_possible_responses_very_long(): void { $questiondata = test_question_maker::get_question_data('ordering'); + $ordering = new qtype_ordering(); $onehundredchars = str_repeat('1234567890', 9) . '123456789碁'; // Set one of the answers to over 100 chars, with a multi-byte UTF-8 character at position 100. $questiondata->options->answers[13]->answer = $onehundredchars . 'and some more'; - $possibleresponses = $this->qtype->get_possible_responses($questiondata); + $possibleresponses = $ordering->get_possible_responses($questiondata); $this->assertArrayHasKey($onehundredchars, $possibleresponses); } public function test_get_numberingstyle(): void { $questiondata = test_question_maker::get_question_data('ordering'); + $ordering = new qtype_ordering(); $expected = qtype_ordering_question::NUMBERING_STYLE_DEFAULT; - $actual = $this->qtype->get_numberingstyle($questiondata); + $actual = $ordering->get_numberingstyle($questiondata); $this->assertEquals($expected, $actual); $questiondata->options->numberingstyle = 'abc'; $expected = 'abc'; - $actual = $this->qtype->get_numberingstyle($questiondata); + $actual = $ordering->get_numberingstyle($questiondata); $this->assertEquals($expected, $actual); $questiondata->options->numberingstyle = 'ABCD'; $expected = 'ABCD'; - $actual = $this->qtype->get_numberingstyle($questiondata); + $actual = $ordering->get_numberingstyle($questiondata); $this->assertEquals($expected, $actual); $questiondata->options->numberingstyle = '123'; $expected = '123'; - $actual = $this->qtype->get_numberingstyle($questiondata); + $actual = $ordering->get_numberingstyle($questiondata); $this->assertEquals($expected, $actual); $questiondata->options->numberingstyle = 'iii'; $expected = 'iii'; - $actual = $this->qtype->get_numberingstyle($questiondata); + $actual = $ordering->get_numberingstyle($questiondata); $this->assertEquals($expected, $actual); $questiondata->options->numberingstyle = 'III'; $expected = 'III'; - $actual = $this->qtype->get_numberingstyle($questiondata); + $actual = $ordering->get_numberingstyle($questiondata); $this->assertEquals($expected, $actual); } @@ -256,25 +250,11 @@ class questiontype_test extends \question_testcase { // Import a question from XML. $xml = file_get_contents(__DIR__ . '/fixtures/testimport.moodle.xml'); $xmldata = xmlize($xml); - $format = new \qformat_xml(); + $format = new qformat_xml(); $imported = $format->try_importing_using_qtypes( $xmldata['question'], null, null, 'ordering'); - $this->assert(new question_check_specified_fields_expectation($this->expectedimportobj), $imported); - } - - public function test_xml_import_empty(): void { - $this->resetAfterTest(); - // Import a question from XML. - $xml = file_get_contents(__DIR__ . '/fixtures/testimportempty.moodle.xml'); - $xmldata = xmlize($xml); - $format = new \qformat_xml(); - $imported = $format->try_importing_using_qtypes( - $xmldata['question'], null, null, 'ordering'); - - $this->expectedimportobj->name = 'Put these words in order.'; - - $this->assert(new question_check_specified_fields_expectation($this->expectedimportobj), $imported); + $this->assert(new question_check_specified_fields_expectation(self::expectedimport()), $imported); } public function test_xml_import_long(): void { @@ -282,13 +262,14 @@ class questiontype_test extends \question_testcase { // Import a question from XML. $xml = file_get_contents(__DIR__ . '/fixtures/testimportlong.moodle.xml'); $xmldata = xmlize($xml); - $format = new \qformat_xml(); + $format = new qformat_xml(); $imported = $format->try_importing_using_qtypes( $xmldata['question'], null, null, 'ordering'); - $this->expectedimportobj->name = 'Moodle Moodle Moodle Moodle Moodle Moodle ...'; + $expected = self::expectedimport(); + $expected->name = 'Moodle Moodle Moodle Moodle Moodle Moodle Moodle'; - $this->assert(new question_check_specified_fields_expectation($this->expectedimportobj), $imported); + $this->assert(new question_check_specified_fields_expectation($expected), $imported); } public function test_xml_export(): void { @@ -300,6 +281,9 @@ class questiontype_test extends \question_testcase { // Export it. $questiondata = question_bank::load_question_data($question->id); + // Force the question id to be 123, to ensure it comes through the export. + $questiondata->id = 123; + $questiondata->options->numberingstyle = null; // Add some feedback to ensure it comes through the export. foreach ($questiondata->options->answers as $answer) { $answer->feedback = $answer->answer . ' is correct.'; @@ -307,7 +291,7 @@ class questiontype_test extends \question_testcase { $answer->feedbackfiles = 0; } - $exporter = new \qformat_xml(); + $exporter = new qformat_xml(); $xml = $exporter->writequestion($questiondata); $expectedxml = file_get_contents(__DIR__ . '/fixtures/testexport.moodle.xml'); @@ -323,8 +307,7 @@ class questiontype_test extends \question_testcase { $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); $imported = $format->readquestion($lines); - // TODO - MDL-XXXXX format_gift: Set ID & tags from comment for third parties. - // $this->assert(new question_check_specified_fields_expectation($this->expectedimportobj), $imported); + $this->assert(new question_check_specified_fields_expectation(self::expectedimport()), $imported); } public function test_gift_export(): void { @@ -336,13 +319,14 @@ class questiontype_test extends \question_testcase { // Export it. $questiondata = question_bank::load_question_data($question->id); + // Force the question id to be 123, to ensure it comes through the export. + $questiondata->id = 123; $exporter = new qformat_gift(); $gift = $exporter->writequestion($questiondata); $expectedgift = file_get_contents(__DIR__ . '/fixtures/testexport.gift.txt'); - // TODO - MDL-XXXXX format_gift: Set ID & tags from comment for third parties. - // $this->assertEquals($expectedgift, $gift); + $this->assertEquals($expectedgift, $gift); } } diff --git a/question/type/ordering/tests/walkthrough_test.php b/question/type/ordering/tests/walkthrough_test.php index e05be468aef..3a4e0ceaf10 100644 --- a/question/type/ordering/tests/walkthrough_test.php +++ b/question/type/ordering/tests/walkthrough_test.php @@ -14,20 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -/** - * Unit tests for the ordering question type. - * - * @package qtype_ordering - * @copyright 2018 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - namespace qtype_ordering; use test_question_maker; use question_state; -use question_pattern_expectation; -use stdClass; use qtype_ordering_test_helper; defined('MOODLE_INTERNAL') || die(); @@ -41,11 +31,13 @@ require_once($CFG->dirroot . '/question/type/ddwtos/tests/helper.php'); * * These tests simulate a student's complete interaction with a question. * + * @package qtype_ordering * @copyright 2018 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @covers \qtype_ordering + * @covers \qtype_ordering_question */ -class walkthrough_test extends \qbehaviour_walkthrough_test_base { +final class walkthrough_test extends \qbehaviour_walkthrough_test_base { /** * Get the array of post data that will . *