MDL-80416 qtype_ordering: Tests for reuse functionality

Part of: MDL-79863
This commit is contained in:
Mathew May 2024-01-18 14:41:30 +08:00
parent 6631b37eca
commit aa2ef1fe9c
12 changed files with 519 additions and 8 deletions

View file

@ -27,6 +27,7 @@
*
* @copyright 2013 Gordon Bateson (gordon.bateson@gmail.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore For old Moodle sites in which upgrades are always risky.
*/
class moodle1_qtype_ordering_handler extends moodle1_qtype_handler {

View file

@ -85,6 +85,7 @@ class restore_qtype_ordering_plugin extends restore_qtype_plugin {
*
* @param object $state
* @return string|false
* @codeCoverageIgnore Restoring from 2.0 is risky business and hopefully not needed.
*/
public function recode_legacy_state_answer($state): string|false {
$answer = $state->answer;

View file

@ -31,6 +31,7 @@ class provider implements \core_privacy\local\metadata\null_provider {
* file to explain why this plugin stores no data.
*
* @return string
* @codeCoverageIgnore A null provider so no special handling for us.
*/
public static function get_reason(): string {
return 'privacy:metadata';

View file

@ -398,12 +398,12 @@ class qtype_ordering extends question_type {
* Import question from GIFT format
*
* @param array $lines
* @param stdClass $question
* @param stdClass|null $question
* @param qformat_gift $format
* @param string|null $extra (optional, default=null)
* @return stdClass|bool Question instance
*/
public function import_from_gift(array $lines, stdClass $question, qformat_gift $format, string $extra = null): bool|stdClass {
public function import_from_gift(array $lines, ?stdClass $question, qformat_gift $format, string $extra = null): bool|stdClass {
global $CFG;
require_once($CFG->dirroot.'/question/type/ordering/question.php');

View file

@ -0,0 +1,96 @@
<?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 qtype_ordering;
use question_bank;
use test_question_maker;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
require_once($CFG->dirroot . '/course/externallib.php');
/**
* Tests for the orderinging question type backup and restore logic.
*
* @package qtype_ordering
* @copyright 2020 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class backup_test extends \advanced_testcase {
/**
* Duplicate quiz with a orderinging question, and check it worked.
*/
public function test_duplicate_ordering_question(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$coregenerator = $this->getDataGenerator();
$questiongenerator = $coregenerator->get_plugin_generator('core_question');
// Create a course with a page that embeds a question.
$course = $coregenerator->create_course();
$quiz = $coregenerator->create_module('quiz', ['course' => $course->id]);
$quizcontext = \context_module::instance($quiz->cmid);
$cat = $questiongenerator->create_question_category(['contextid' => $quizcontext->id]);
$question = $questiongenerator->create_question('ordering', 'moodle', ['category' => $cat->id]);
// Store some counts.
$numquizzes = count(get_fast_modinfo($course)->instances['quiz']);
$numorderingquestions = $DB->count_records('question', ['qtype' => 'ordering']);
// Duplicate the page.
duplicate_module($course, get_fast_modinfo($course)->get_cm($quiz->cmid));
// Verify the copied quiz exists.
$this->assertCount($numquizzes + 1, get_fast_modinfo($course)->instances['quiz']);
// Verify the copied question.
$this->assertEquals($numorderingquestions + 1, $DB->count_records('question', ['qtype' => 'ordering']));
$neworderingid = $DB->get_field_sql("
SELECT MAX(id)
FROM {question}
WHERE qtype = ?
", ['ordering']);
// Declare some parts of the question to be compared.
$existingorderingdata = question_bank::load_question_data($question->id);
$orderingdata = question_bank::load_question_data($neworderingid);
$existinganswers = array_values((array) $existingorderingdata->options->answers);
$answers = array_values((array) $orderingdata->options->answers);
// Verify the copied question has the same values without being too verbose.
foreach ((array) $existingorderingdata as $key => $value) {
if (in_array($key, ['questiontext', 'generalfeedback', 'partiallycorrectfeedback'])) {
$this->assertEquals($value, $orderingdata->$key);
}
}
// Verify some parts of the new question we'll know that will be different.
$this->assertNotEquals($existingorderingdata->id, $orderingdata->id);
for ($i = 0; $i < count($existinganswers); $i++) {
$this->assertEquals($existinganswers[$i]->answer, $answers[$i]->answer);
$this->assertEquals($existinganswers[$i]->fraction, $answers[$i]->fraction);
$this->assertEquals($existinganswers[$i]->feedback, $answers[$i]->feedback);
}
}
}

View file

@ -0,0 +1,10 @@
// question: 409000 name: Moodle
// [id:myid]
::Moodle::[html]Put these words in order.{>0 none
Modular
Object
Oriented
Dynamic
Learning
Environment
}

View file

@ -0,0 +1,68 @@
<!-- question: 409000 -->
<question type="ordering">
<name>
<text>Moodle</text>
</name>
<questiontext format="html">
<text>Put these words in order.</text>
</questiontext>
<generalfeedback format="html">
<text><![CDATA[The correct answer is "Modular Object Oriented Dynamic Learning Environment".]]></text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0.3333333</penalty>
<hidden>0</hidden>
<idnumber>myid</idnumber>
<layouttype></layouttype>
<selecttype></selecttype>
<selectcount>2</selectcount>
<gradingtype></gradingtype>
<showgrading></showgrading>
<numberingstyle>none</numberingstyle>
<correctfeedback format="html">
<text>Well done!</text>
</correctfeedback>
<partiallycorrectfeedback format="html">
<text>Parts, but only parts, of your response are correct.</text>
</partiallycorrectfeedback>
<incorrectfeedback format="html">
<text>That is not right at all.</text>
</incorrectfeedback>
<shownumcorrect>1</shownumcorrect>
<answer fraction="1.0000000" format="html">
<text>Modular</text>
<feedback format="html">
<text>Modular is correct.</text>
</feedback>
</answer>
<answer fraction="2.0000000" format="html">
<text>Object</text>
<feedback format="html">
<text>Object is correct.</text>
</feedback>
</answer>
<answer fraction="3.0000000" format="html">
<text>Oriented</text>
<feedback format="html">
<text>Oriented is correct.</text>
</feedback>
</answer>
<answer fraction="4.0000000" format="html">
<text>Dynamic</text>
<feedback format="html">
<text>Dynamic is correct.</text>
</feedback>
</answer>
<answer fraction="5.0000000" format="html">
<text>Learning</text>
<feedback format="html">
<text>Learning is correct.</text>
</feedback>
</answer>
<answer fraction="6.0000000" format="html">
<text>Environment</text>
<feedback format="html">
<text>Environment is correct.</text>
</feedback>
</answer>
</question>

View file

@ -0,0 +1,12 @@
// question: 25 name: Moodle
// [id:myid]
::Moodle::[html]Put these words in order.{>4 CONTIGUOUS VERT ABSOLUTE TRUE ABCD
Modular
Object
Oriented
Dynamic
Learning
Environment
#### The correct answer is "Modular Object Oriented Dynamic Learning Environment".
}

View file

@ -0,0 +1,64 @@
<question type="ordering">
<name>
<text>Moodle</text>
</name>
<questiontext format="html">
<text>Put these words in order.</text>
</questiontext>
<questiontext format="html">
<text><![CDATA[Put these words in order.]]></text>
</questiontext>
<generalfeedback format="html">
<text><![CDATA[The correct answer is "Modular Object Oriented Dynamic Learning Environment".]]></text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0.3333333</penalty>
<hidden>0</hidden>
<idnumber>myid</idnumber>
<numberingstyle>none</numberingstyle>
<correctfeedback format="html">
<text><![CDATA[<p>Your answer is correct.</p>]]></text>
</correctfeedback>
<partiallycorrectfeedback format="html">
<text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
</partiallycorrectfeedback>
<incorrectfeedback format="html">
<text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
</incorrectfeedback>
<answer fraction="1.0000000" format="html">
<text>Modular</text>
<feedback format="html">
<text><![CDATA[Modular is correct.]]></text>
</feedback>
</answer>
<answer fraction="2.0000000" format="html">
<text>Object</text>
<feedback format="html">
<text><![CDATA[Object is correct.]]></text>
</feedback>
</answer>
<answer fraction="3.0000000" format="html">
<text>Oriented</text>
<feedback format="html">
<text><![CDATA[Oriented is correct.]]></text>
</feedback>
</answer>
<answer fraction="4.0000000" format="html">
<text>Dynamic</text>
<feedback format="html">
<text><![CDATA[Dynamic is correct.]]></text>
</feedback>
</answer>
<answer fraction="5.0000000" format="html">
<text>Learning</text>
<feedback format="html">
<text><![CDATA[Learning is correct.]]></text>
</feedback>
</answer>
<answer fraction="6.0000000" format="html">
<text>Environment</text>
<feedback format="html">
<text><![CDATA[Environment is correct.]]></text>
</feedback>
</answer>
</question>

View file

@ -0,0 +1,64 @@
<question type="ordering">
<name>
<text></text>
</name>
<questiontext format="html">
<text>Put these words in order.</text>
</questiontext>
<questiontext format="html">
<text><![CDATA[Put these words in order.]]></text>
</questiontext>
<generalfeedback format="html">
<text><![CDATA[The correct answer is "Modular Object Oriented Dynamic Learning Environment".]]></text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0.3333333</penalty>
<hidden>0</hidden>
<idnumber>myid</idnumber>
<numberingstyle>none</numberingstyle>
<correctfeedback format="html">
<text><![CDATA[<p>Your answer is correct.</p>]]></text>
</correctfeedback>
<partiallycorrectfeedback format="html">
<text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
</partiallycorrectfeedback>
<incorrectfeedback format="html">
<text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
</incorrectfeedback>
<answer fraction="1.0000000" format="html">
<text>Modular</text>
<feedback format="html">
<text><![CDATA[Modular is correct.]]></text>
</feedback>
</answer>
<answer fraction="2.0000000" format="html">
<text>Object</text>
<feedback format="html">
<text><![CDATA[Object is correct.]]></text>
</feedback>
</answer>
<answer fraction="3.0000000" format="html">
<text>Oriented</text>
<feedback format="html">
<text><![CDATA[Oriented is correct.]]></text>
</feedback>
</answer>
<answer fraction="4.0000000" format="html">
<text>Dynamic</text>
<feedback format="html">
<text><![CDATA[Dynamic is correct.]]></text>
</feedback>
</answer>
<answer fraction="5.0000000" format="html">
<text>Learning</text>
<feedback format="html">
<text><![CDATA[Learning is correct.]]></text>
</feedback>
</answer>
<answer fraction="6.0000000" format="html">
<text>Environment</text>
<feedback format="html">
<text><![CDATA[Environment is correct.]]></text>
</feedback>
</answer>
</question>

View file

@ -0,0 +1,64 @@
<question type="ordering">
<name>
<text>Moodle Moodle Moodle Moodle Moodle Moodle Moodle</text>
</name>
<questiontext format="html">
<text>Put these words in order.</text>
</questiontext>
<questiontext format="html">
<text><![CDATA[Put these words in order.]]></text>
</questiontext>
<generalfeedback format="html">
<text><![CDATA[The correct answer is "Modular Object Oriented Dynamic Learning Environment".]]></text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0.3333333</penalty>
<hidden>0</hidden>
<idnumber>myid</idnumber>
<numberingstyle>none</numberingstyle>
<correctfeedback format="html">
<text><![CDATA[<p>Your answer is correct.</p>]]></text>
</correctfeedback>
<partiallycorrectfeedback format="html">
<text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
</partiallycorrectfeedback>
<incorrectfeedback format="html">
<text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
</incorrectfeedback>
<answer fraction="1.0000000" format="html">
<text>Modular</text>
<feedback format="html">
<text><![CDATA[Modular is correct.]]></text>
</feedback>
</answer>
<answer fraction="2.0000000" format="html">
<text>Object</text>
<feedback format="html">
<text><![CDATA[Object is correct.]]></text>
</feedback>
</answer>
<answer fraction="3.0000000" format="html">
<text>Oriented</text>
<feedback format="html">
<text><![CDATA[Oriented is correct.]]></text>
</feedback>
</answer>
<answer fraction="4.0000000" format="html">
<text>Dynamic</text>
<feedback format="html">
<text><![CDATA[Dynamic is correct.]]></text>
</feedback>
</answer>
<answer fraction="5.0000000" format="html">
<text>Learning</text>
<feedback format="html">
<text><![CDATA[Learning is correct.]]></text>
</feedback>
</answer>
<answer fraction="6.0000000" format="html">
<text>Environment</text>
<feedback format="html">
<text><![CDATA[Environment is correct.]]></text>
</feedback>
</answer>
</question>

View file

@ -24,23 +24,31 @@
namespace qtype_ordering;
use core_question_generator;
use qtype_ordering;
use test_question_maker;
use qtype_ordering_edit_form;
use qtype_ordering_test_helper;
use qtype_ordering_edit_form;
use qtype_ordering_question;
use test_question_maker;
use question_bank;
use question_possible_response;
use qtype_ordering_question;
use core_question_generator;
use qformat_gift;
use question_check_specified_fields_expectation;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
require_once($CFG->dirroot . '/question/type/ordering/questiontype.php');
require_once($CFG->dirroot . '/question/type/edit_question_form.php');
require_once($CFG->dirroot . '/question/type/ordering/questiontype.php');
require_once($CFG->dirroot . '/question/type/ordering/edit_ordering_form.php');
require_once($CFG->libdir . '/questionlib.php');
require_once($CFG->dirroot . '/question/format.php');
require_once($CFG->dirroot . '/question/format/gift/format.php');
require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
/**
* Unit tests for the ordering question type class.
*
@ -48,16 +56,43 @@ require_once($CFG->dirroot . '/question/type/ordering/edit_ordering_form.php');
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \qtype_ordering
*/
class questiontype_test extends \advanced_testcase {
class questiontype_test extends \question_testcase {
/** @var qtype_ordering instance of the question type class to test. */
protected $qtype;
/** @var object Default import object to compare against. */
protected $expectedimportobj;
protected function setUp(): void {
$this->qtype = new qtype_ordering();
$this->expectedimportobj = (object) [
'qtype' => 'ordering',
'idnumber' => 'myid',
'name' => 'Moodle',
'length' => 1,
'penalty' => 0.3333333,
'questiontext' => 'Put these words in order.',
'questiontextformat' => 1,
'generalfeedback' => 'The correct answer is "Modular Object Oriented Dynamic Learning Environment".',
'generalfeedbackformat' => 1,
'defaultmark' => 1,
];
}
protected function tearDown(): void {
$this->qtype = null;
$this->expectedimportobj = null;
}
/**
* Asserts that two XML strings are the same, ignoring differences in line endings.
*
* @param string $expectedxml
* @param string $xml
*/
public function assert_same_xml(string $expectedxml, string $xml): void {
$this->assertEquals(str_replace("\r\n", "\n", $expectedxml),
str_replace("\r\n", "\n", $xml));
}
public function test_name(): void {
@ -215,4 +250,99 @@ class questiontype_test extends \advanced_testcase {
$actual = $this->qtype->get_numberingstyle($questiondata);
$this->assertEquals($expected, $actual);
}
public function test_xml_import(): void {
$this->resetAfterTest();
// Import a question from XML
$xml = file_get_contents(__DIR__ . '/fixtures/testimport.moodle.xml');
$xmldata = xmlize($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);
}
public function test_xml_import_long(): void {
$this->resetAfterTest();
// Import a question from XML
$xml = file_get_contents(__DIR__ . '/fixtures/testimportlong.moodle.xml');
$xmldata = xmlize($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 ...';
$this->assert(new question_check_specified_fields_expectation($this->expectedimportobj), $imported);
}
public function test_xml_export(): void {
$this->resetAfterTest();
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
$category = $generator->create_question_category();
$question = $generator->create_question('ordering', 'moodle',
['category' => $category->id, 'idnumber' => 'myid']);
// Export it.
$questiondata = question_bank::load_question_data($question->id);
// Add some feedback to ensure it comes through the export.
foreach ($questiondata->options->answers as $answer) {
$answer->feedback = $answer->answer . ' is correct.';
$answer->feedbackformat = FORMAT_HTML;
$answer->feedbackfiles = 0;
}
$exporter = new \qformat_xml();
$xml = $exporter->writequestion($questiondata);
$expectedxml = file_get_contents(__DIR__ . '/fixtures/testexport.moodle.xml');
$this->assert_same_xml($expectedxml, $xml);
}
public function test_gift_import(): void {
$this->resetAfterTest();
// Import a question from GIFT
$gift = file_get_contents(__DIR__ . '/fixtures/testimport.gift.txt');
$format = new qformat_gift();
$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);
}
public function test_gift_export(): void {
$this->resetAfterTest();
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
$category = $generator->create_question_category();
$question = $generator->create_question('ordering', 'moodle',
['category' => $category->id, 'idnumber' => 'myid']);
// Export it.
$questiondata = question_bank::load_question_data($question->id);
$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);
}
}