From 69ab44644c6f1cfbd427c7783b477e88f4412775 Mon Sep 17 00:00:00 2001 From: mkassaei Date: Thu, 7 Sep 2023 15:15:48 +0100 Subject: [PATCH] phunit and herper class --- classes/column.php | 24 ++-- classes/row.php | 18 +-- edit_oumatrix_form.php | 8 +- questiontype.php | 57 ++++----- renderer.php | 4 +- tests/helper.php | 203 ++++++++++++++++++++++++++---- tests/questiontype_test.php | 243 ++++++++++++++++++++++++++++++++++++ 7 files changed, 476 insertions(+), 81 deletions(-) create mode 100644 tests/questiontype_test.php diff --git a/classes/column.php b/classes/column.php index b933de6..2f4b407 100644 --- a/classes/column.php +++ b/classes/column.php @@ -57,10 +57,10 @@ public function __construct(int $id = 0, int $questionid = 0, int $number = 0, s $this->number = $number; $this->name = $name; $this->id = $id; - //$this->column = $this->populate(); + $this->column = $this->populate(); } - private function populate(): ?stdClass { + public function populate(): ?stdClass { if ($this->questionid && $this->number && $this->name) { $column = new stdClass(); $column->questionid = $this->questionid; @@ -101,33 +101,29 @@ public function create_a_column(int $questionid, int $number, string $name): int } /** + * Return column id + * * @return int */ - public function getQuestionid(): int { - return $this->questionid; - } - - /** - * @return int - */ - public function getId(): int { + public function get_id(): int { return $this->id; } /** + * Return column number + * * @return int */ - public function getNumber(): int { + public function get_number(): int { return $this->number; } /** - * Return name + * Return column name * - * @param int $id * @return string */ - public function getName(): string { + public function get_name(): string { return $this->name; } diff --git a/classes/row.php b/classes/row.php index 5576602..f72f890 100644 --- a/classes/row.php +++ b/classes/row.php @@ -44,7 +44,7 @@ class row { /** @var string The row name. */ public $name; - /** @var array The list of correct answers A json-encoded list of correct answerids for a given row. */ + /** @var array The list of correct answers, A json-encoded list of correct answerids for a given row. */ public $correctanswers = []; /** @var string The row specific feedback. */ @@ -80,8 +80,8 @@ public function __construct(int $id = 0, int $questionid = 0, int $number = 0, s */ public function get_a_row_by_id(int $id): ?stdClass { global $DB; - if ($column = $DB->get_record('qtype_oumatrix_rows', ['id' => $id])) { - return $column; + if ($row = $DB->get_record('qtype_oumatrix_rows', ['id' => $id])) { + return $row; } return null; } @@ -148,42 +148,42 @@ public function delete_a_row(int $rownumber) { /** * @return int */ - public function getId(): int { + public function get_id(): int { return $this->id; } /** * @return int */ - public function getNumber(): int { + public function get_number(): int { return $this->number; } /** * @return string */ - public function getName(): string { + public function get_name(): string { return $this->name; } /** * @return array */ - public function getCorrectanswers(): array { + public function get_correctanswers(): array { return $this->correctanswers; } /** * @return string */ - public function getFeedback(): string { + public function get_feedback(): string { return $this->feedback; } /** * @return int */ - public function getFeedbackformat(): int { + public function get_feedbackformat(): int { return $this->feedbackformat; } diff --git a/edit_oumatrix_form.php b/edit_oumatrix_form.php index 37e99a4..6eb5b97 100644 --- a/edit_oumatrix_form.php +++ b/edit_oumatrix_form.php @@ -147,10 +147,10 @@ public function data_preprocessing($question) { $question = $this->data_preprocessing_combined_feedback($question, true); $question = $this->data_preprocessing_hints($question, true, true); $question = $this->data_preprocessing_options($question,); - print_object('data_preprocessing() 1111111111111111111111'); - print_object('data_preprocessing() 222222222222222222222');return $question; - print_object($question); - print_object('data_preprocessing() 222222222222222222222');return $question; + //print_object('data_preprocessing() 1111111111111111111111'); + //print_object('data_preprocessing() 222222222222222222222');return $question; + //print_object($question); + //print_object('data_preprocessing() 222222222222222222222');return $question; } function data_preprocessing_options($question) { diff --git a/questiontype.php b/questiontype.php index b8b482c..f9d9c61 100644 --- a/questiontype.php +++ b/questiontype.php @@ -22,7 +22,8 @@ * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -use \qtype_oumatrix\row; +use qtype_oumatrix\column; +use qtype_oumatrix\row; defined('MOODLE_INTERNAL') || die(); @@ -200,8 +201,11 @@ public function save_rows($question) { // Prepare correct answers. if($question->inputtype == 'multiple') { for ($c = 0; $c < count($question->columnname); $c++) { + if ($question->columnname[$c] === '') { + continue; + } $anslabel = get_string('a', 'qtype_oumatrix', $c + 1); - $rowanswerslabel = "rowanswers". $anslabel; + $rowanswerslabel = "rowanswers" . $anslabel; if (!array_key_exists($i, $question->$rowanswerslabel)) { $answerslist[$question->columnname[$c]] = "0"; @@ -265,12 +269,14 @@ public function delete_question($questionid, $contextid) { protected function get_num_correct_choices($questiondata) { $numright = 0; - // TODO: To be done correctly - //foreach ($questiondata->options->answers as $answer) { - // if (!question_state::graded_state_for_fraction($answer->fraction)->is_incorrect()) { - // $numright += 1; - // } - //} + foreach ($questiondata->options->rows as $row) { + $rowanwers = json_decode($row->correctanswers); + foreach ($rowanwers as $key => $value) { + if ((int) $value === 1) { + $numright += 1; + } + } + } return $numright; } @@ -281,20 +287,20 @@ public function get_random_guess_score($questiondata) { // Amazingly, the forumla for this works out to be // # correct choices / total # choices in all cases. - //TODO: improve this is a correct way if we are not using the answers table, etc. + //TODO: improve this. return $this->get_num_correct_choices($questiondata) / - count($questiondata->options->answers); - } + count($questiondata->options->rows); + } public function get_possible_responses($questiondata) { $numright = $this->get_num_correct_choices($questiondata); $parts = []; // TODO: To be done correctly - //foreach ($questiondata->options->answers as $aid => $answer) { - // $parts[$aid] = array($aid => - // new question_possible_response($answer->answer, $answer->fraction / $numright)); - //} + foreach ($questiondata->options->answers as $aid => $answer) { + $parts[$aid] = array($aid => + new question_possible_response($answer->answer, $answer->fraction / $numright)); + } return $parts; } @@ -304,23 +310,17 @@ public function get_possible_responses($questiondata) { * @param question_definition $question the question_definition we are creating. * @param object $questiondata the question data loaded from the database. */ - protected function initialise_question_rows(question_definition $question, - $questiondata) { + protected function initialise_question_rows(question_definition $question, $questiondata) { if (!empty($questiondata->options->rows)) { - print_object("222222222222222222222222222"); - print_object($questiondata); foreach ($questiondata->options->rows as $row) { $newrow = $this->make_row($row); - if ($newrow->getCorrectanswers() != '') { - $correctAnswers = $newrow->getCorrectanswers(); + if ($newrow->get_correctanswers() != '') { + $correctAnswers = $newrow->get_correctanswers(); //$decodedanswers = []; if( $questiondata->options->inputtype == 'multiple') { $correctAnswers = []; - $todecode = implode(",", $newrow->getCorrectanswers()); - //foreach ($newrow->getCorrectanswers() as $value) { - // $decodedanswers = json_decode(implode("", [$value]), true); - //} + $todecode = implode(",", $newrow->get_correctanswers()); $decodedanswers = json_decode($todecode, true); foreach($questiondata->options->columns as $key => $column) { if ($decodedanswers != null && array_key_exists($column->name, $decodedanswers)) { @@ -330,9 +330,6 @@ protected function initialise_question_rows(question_definition $question, } $newrow->setCorrectanswers($correctAnswers); } - //print_object("****************************"); - //print_object($newrow); - $question->rows[] = $newrow; } } @@ -352,7 +349,7 @@ protected function initialise_question_columns(question_definition $question, $q } protected function make_column($columndata) { - return new \qtype_oumatrix\column($columndata->id, $columndata->questionid, $columndata->number, $columndata->name); + return new column($columndata->id, $columndata->questionid, $columndata->number, $columndata->name); } public function make_row($rowdata) { @@ -409,7 +406,7 @@ public function import_from_xml($data, $question, qformat_xml $format, $extra=nu } public function export_to_xml($question, qformat_xml $format, $extra = null) { - print_object($question); + //print_object($question); $output = ''; $output .= " " . $format->get_single( diff --git a/renderer.php b/renderer.php index 4aec7d1..7f097f9 100644 --- a/renderer.php +++ b/renderer.php @@ -245,14 +245,14 @@ public function get_matrix(question_attempt $qa) { "; foreach ($question->columns as $key => $value) { - $colname[$key] = $value->getName(); + $colname[$key] = $value->get_name(); $table .= "$colname[$key]"; } $table .= " "; $i = 0; foreach ($question->rows as $key => $value) { - $rowname = $value->getName(); + $rowname = $value->get_name(); $rowid = 'row_'. $key; $table .= "$rowname"; for ($j = 0; $j < count($colname); $j++) { diff --git a/tests/helper.php b/tests/helper.php index 66b5bf6..09c48e3 100644 --- a/tests/helper.php +++ b/tests/helper.php @@ -26,8 +26,8 @@ class qtype_oumatrix_test_helper extends question_test_helper { public function get_test_questions() { return [ - 'three_by_three_single', - 'three_by_three_multople' + 'four_by_four_single', + 'three_by_three_multiple' ]; } @@ -73,57 +73,57 @@ public function get_oumatrix_question_data_four_by_four_single() { $qdata->options->columns = [ 11 => (object) [ 'id' => 11, - 'numbdr' => 0, + 'number' => 0, 'name' => 'Insects', ], 12 => (object) [ 'id' => 12, - 'numbdr' => 1, + 'number' => 1, 'name' => 'Fish', ], 13 => (object) [ 'id' => 13, - 'numbdr' => 2, + 'number' => 2, 'name' => 'Birdas', ], 14 => (object) [ 'id' => 13, - 'numbdr' => 2, + 'number' => 2, 'name' => 'Mammals', ], ]; $qdata->options->rows = [ 11 => (object) [ 'id' => 11, - 'numbdr' => 0, + 'number' => 0, 'name' => 'Fly, bee, spider', 'feedback' => 'Flies, bees and spiders are insects.', 'feedbackformat' => FORMAT_HTML, - 'correctanswers' => '{"Insects":"1","Fish":"0","Birds":"0", "$mammals":"0"}', + 'correctanswers' => '{"Insects":"1","Fish":"0","Birds":"0", "mammals":"0"}', ], 12 => (object) [ 'id' => 12, - 'numbdr' => 0, + 'number' => 1, 'name' => 'Cod, Salmon, Trout', 'feedback' => 'Cod, Salmon and Trout are fish.', 'feedbackformat' => FORMAT_HTML, - 'correctanswers' => '{"Insects":"0","Fish":"1","Birds":"0", "$mammals":"0"}', + 'correctanswers' => '{"Insects":"0","Fish":"1","Birds":"0", "mammals":"0"}', ], 13 => (object) [ 'id' => 13, - 'numbdr' => 0, + 'number' => 2, 'name' => 'Gull, Owl', 'feedback' => 'Gull and Owl are birds.', 'feedbackformat' => FORMAT_HTML, - 'correctanswers' => '{"Insects":"0","Fish":"0","Birds":"1", "$mammals":"0"}', + 'correctanswers' => '{"Insects":"0","Fish":"0","Birds":"1", "mammals":"0"}', ], 14 => (object) [ 'id' => 14, - 'numbdr' => 0, + 'number' => 3, 'name' => 'Cow, Dog, Horse', - 'feedback' => 'Cow, Dog and Horse are mammals',, + 'feedback' => 'Cow, Dog and Horse are mammals', 'feedbackformat' => FORMAT_HTML, - 'correctanswers' => '{"Insects":"0","Fish":"0","Birds":"0", "$mammals":"1"}', + 'correctanswers' => '{"Insects":"0","Fish":"0","Birds":"0", "mammals":"1"}', ], ]; @@ -146,6 +146,90 @@ public function get_oumatrix_question_data_four_by_four_single() { return $qdata; } + /** + * Get the question data, as it would be loaded by get_question_options. + * @return object + */ + public static function get_oumatrix_question_form_data_four_by_four_single() { + $qfdata = new stdClass(); + + $qfdata->name = 'Matrix_4by4_single01'; + $qfdata->questiontext = ['text' => 'Please answer the sub questions in all 4 rows', 'format' => FORMAT_HTML]; + $qfdata->generalfeedback = ['text' => 'We are recognising different type of animals.', 'format' => FORMAT_HTML]; + $qfdata->defaultmark = 1; + $qfdata->length = 1; + $qfdata->penalty = 0.3333333; + $qfdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY; + $qfdata->versionid = 0; + $qfdata->version = 1; + $qfdata->questionbankentryid = 0; + $qfdata->inputtype = 'single'; + $qfdata->grademethod = 'partial'; + $qfdata->shuffleanswers = 1; + $qfdata->correctfeedback = [ + 'text' => test_question_maker::STANDARD_OVERALL_CORRECT_FEEDBACK, + 'format' => FORMAT_HTML]; + $qfdata->partiallycorrectfeedback = [ + 'text' => test_question_maker::STANDARD_OVERALL_PARTIALLYCORRECT_FEEDBACK, + 'format' => FORMAT_HTML]; + $qfdata->shownumcorrect = 1; + $qfdata->incorrectfeedback = [ + 'text' => test_question_maker::STANDARD_OVERALL_INCORRECT_FEEDBACK, + 'format' => FORMAT_HTML]; + $qfdata->columns = [ + 11 => [ + 'number' => 0, + 'name' => 'Insects' + ], + 12 => [ + 'number' => 1, + 'name' => 'Fish' + ], + 13 => [ + 'number' => 2, + 'name' => 'Birdas' + ], + 14 => [ + 'number' => 2, + 'name' => 'Mammals' + ], + ]; + $qfdata->rows = [ + 11 => [ + 'number' => 0, + 'name' => 'Fly, bee, spider', + 'correctanswers' => '{"Insects":"1","Fish":"0","Birds":"0", "mammals":"0"}', + 'feedback' => ['text' => 'Flies, bees and spiders are insects.', 'format' => FORMAT_HTML] + ], + 12 => [ + 'number' => 0, + 'name' => 'Cod, Salmon, Trout', + 'correctanswers' => '{"Insects":"0","Fish":"1","Birds":"0", "mammals":"0"}', + 'feedback' => ['text' => 'Cod, Salmon and Trout are fish.', 'format' => FORMAT_HTML] + ], + 13 => [ + 'number' => 0, + 'name' => 'Gull, Owl', + 'correctanswers' => '{"Insects":"0","Fish":"0","Birds":"1", "mammals":"0"}', + 'feedback' => ['text' => 'Gull and Owl are birds.', 'format' => FORMAT_HTML] + ], + 14 => (object) [ + 'number' => 0, + 'name' => 'Cow, Dog, Horse', + 'correctanswers' => '{"Insects":"0","Fish":"0","Birds":"0", "mammals":"1"}', + 'feedback' => ['text' => 'Cow, Dog and Horse are mammals', 'format' => FORMAT_HTML] + ], + ]; + + $qfdata->hints = [ + 0 => ['text' => 'Hint 1.', 'format' => FORMAT_HTML], + 1 => ['text' => 'Hint 2.', 'format' => FORMAT_HTML] + ]; + $qfdata->hintclearwrong = [0, 1]; + $qfdata->hintshownumbcorrect = [1, 1]; + return $qfdata; + } + /** * Get the question data, as it would be loaded by get_question_options. * @return object @@ -207,25 +291,25 @@ public function get_oumatrix_question_data_three_by_three_multiple() { 'id' => 21, 'numbdr' => 0, 'name' => 'Even numbers', + 'correctanswers' => '{"one":"0","two":"1","three":"0"}', 'feedback' => 'Even numbers are divisible by 2 without remainders.', 'feedbackformat' => FORMAT_HTML, - 'correctanswers' => '{"one":"0","two":"1","three":"0"}', ], 22 => (object) [ 'id' => 22, - 'numbdr' => 0, + 'numbdr' => 1, 'name' => 'Odd numbers', + 'correctanswers' => '{"one":"1","two":"0","three":"1"}', 'feedback' => 'Odd numbers are not evenly divisible by 2 and end in 1, 3, 5, 7, or 9.', 'feedbackformat' => FORMAT_HTML, - 'correctanswers' => '{"one":"1","two":"0","three":"1"}', ], 23 => (object) [ 'id' => 23, - 'numbdr' => 0, + 'numbdr' => 2, 'name' => 'Number less than 3', + 'correctanswers' => '{"one":"1","two":"1","three":"0"}', 'feedback' => 'All numbers smaller than 3.', 'feedbackformat' => FORMAT_HTML, - 'correctanswers' => '{"one":"1","two":"1","three":"0"}', ], ]; @@ -248,6 +332,81 @@ public function get_oumatrix_question_data_three_by_three_multiple() { return $qdata; } + /** + * Get the question data, as it would be loaded by get_question_options. + * @return object + */ + public static function get_oumatrix_question_form_data_three_by_three_pultiple() { + $qfdata = new stdClass(); + + $qfdata->name = 'Matrix_3by3_multiple01'; + $qfdata->questiontext = ['text' => 'Please answer the sub questions in each row', 'format' => FORMAT_HTML]; + $qfdata->generalfeedback = ['text' => 'We are dealing with even and odd numbers', 'format' => FORMAT_HTML]; + $qfdata->defaultmark = 1; + $qfdata->length = 1; + $qfdata->penalty = 0.3333333; + $qfdata->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY; + $qfdata->versionid = 0; + $qfdata->version = 1; + $qfdata->questionbankentryid = 0; + $qfdata->inputtype = 'multiple'; + $qfdata->grademethod = 'partial'; + $qfdata->shuffleanswers = 1; + $qfdata->correctfeedback = [ + 'text' => test_question_maker::STANDARD_OVERALL_CORRECT_FEEDBACK, + 'format' => FORMAT_HTML]; + $qfdata->partiallycorrectfeedback = [ + 'text' => test_question_maker::STANDARD_OVERALL_PARTIALLYCORRECT_FEEDBACK, + 'format' => FORMAT_HTML]; + $qfdata->shownumcorrect = 1; + $qfdata->incorrectfeedback = [ + 'text' => test_question_maker::STANDARD_OVERALL_INCORRECT_FEEDBACK, + 'format' => FORMAT_HTML]; + $qfdata->columns = [ + 21 => [ + 'number' => 0, + 'name' => 'one' + ], + 22 => [ + 'number' => 1, + 'name' => 'two' + ], + 23 => [ + 'number' => 2, + 'name' => 'three' + ], + ]; + $qfdata->rows = [ + 21 => [ + 'number' => 0, + 'name' => 'Even numbers', + 'correctanswers' => '{"one":"0","two":"1","three":"0"}', + 'feedback' => ['text' => 'Even numbers are divisible by 2 without remainders.', 'format' => FORMAT_HTML] + ], + 22 => [ + 'number' => 1, + 'name' => 'Odd numbers', + 'correctanswers' => '{"one":"1","two":"0","three":"1"}', + 'feedback' => ['text' => 'Odd numbers are not evenly divisible by 2 and end in 1, 3, 5, 7, or 9.', + 'format' => FORMAT_HTML] + ], + 23 => [ + 'number' => 2, + 'name' => 'Number less than 3', + 'correctanswers' => '{"one":"1","two":"1","three":"0"}', + 'feedback' => ['text' => 'All numbers smaller than 3.', 'format' => FORMAT_HTML] + ], + ]; + + $qfdata->hints = [ + 0 => ['text' => 'Hint 1.', 'format' => FORMAT_HTML], + 1 => ['text' => 'Hint 2.', 'format' => FORMAT_HTML] + ]; + $qfdata->hintclearwrong = [0, 1]; + $qfdata->hintshownumbcorrect = [1, 1]; + return $qfdata; + } + // TODO: following methods are copied from the old marix and may need some chnages. /** * @@ -365,8 +524,8 @@ public function init_oumatrix_question(): qtype_oumatrix_question { $result->penalty = 0.3333333; $result->qtype = question_bank::get_qtype('oumatrix'); - $result->rows = array(); - $result->cols = array(); + $result->rows = []; + $result->cols = []; return $result; } } diff --git a/tests/questiontype_test.php b/tests/questiontype_test.php new file mode 100644 index 0000000..6e0e458 --- /dev/null +++ b/tests/questiontype_test.php @@ -0,0 +1,243 @@ +. + +namespace qtype_oumatrix; + +use qtype_oumatrix; +use qtype_oumatrix_edit_form; +use question_possible_response; +use qtype_oumatrix_test_helper; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); +require_once($CFG->dirroot . '/question/type/oumatrix/tests/helper.php'); +require_once($CFG->dirroot . '/question/type/oumatrix/questiontype.php'); +require_once($CFG->dirroot . '/question/type/edit_question_form.php'); +require_once($CFG->dirroot . '/question/type/oumatrix/edit_oumatrix_form.php'); + +/** + * Unit tests for the OU matrix question definition class. + * + * @package qtype_oumatrix + * @copyright 2023 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_oumatrix + */ +class questiontype_test extends \advanced_testcase { + protected $qtype; + + protected function setUp(): void { + $this->qtype = new qtype_oumatrix(); + } + + protected function tearDown(): void { + $this->qtype = null; + } + + public function test_name() { + $this->assertEquals($this->qtype->name(), 'oumatrix'); + } + + protected function get_test_question_data() { + return \test_question_maker::get_question_data('oumatrix', 'four_by_four_single'); + } + + public function test_initialise_question_instance() { + $h = new qtype_oumatrix_test_helper(); + //$qsingle = $h->get_oumatrix_question_data_four_by_four_single(); + //$this->assertEquals(0.5, $this->qtype->get_random_guess_score($qsingle)); + + $qmultiple = $h->get_oumatrix_question_data_three_by_three_multiple(); + //print_object($qmultiple); + $qdata = $this->get_test_question_data(); + + $expected = \test_question_maker::make_question('ddwtos'); + $expected->stamp = $qdata->stamp; + $expected->idnumber = null; + + $q = $this->qtype->make_question($qdata); + + $this->assertEquals($expected, $q); + } + + public function test_get_random_guess_score() { + $h = new qtype_oumatrix_test_helper(); + + $qsingle = $h->get_oumatrix_question_data_four_by_four_single(); + $this->assertEquals(0.5, $this->qtype->get_random_guess_score($qsingle)); + + $qmultiple = $h->get_oumatrix_question_data_three_by_three_multiple(); + print_object('$qsingle ------------------------------------------------'); + print_object($qmultiple); + return; + $this->assertEquals(0.5, $this->qtype->get_random_guess_score($qmultiple)); + } + + public function test_get_random_guess_score_broken_question() { + $q = $this->get_test_question_data(); + $q->options->rows= []; + $this->assertNull($this->qtype->get_random_guess_score($q)); + } + + public function test_get_possible_responses_multi() { + $q = $this->get_test_question_data(); + $q->options->single = false; + + $this->assertEquals(array( + 1 => array(1 => new question_possible_response('frog', 1)), + 2 => array(2 => new question_possible_response('toad', 0)), + ), $this->qtype->get_possible_responses($q)); + } + + public function get_question_saving_which() { + return [['four_by_four_songle'], ['three_by_three_multiple']]; + } + + /** + * @dataProvider get_question_saving + */ + public function test_question_saving($which) { + $this->resetAfterTest(true); + $this->setAdminUser(); + + $questiondata = \test_question_maker::get_question_data('oumatrix', $which); + $formdata = \test_question_maker::get_question_form_data('oumatrix', $which); + + $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); + $cat = $generator->create_question_category(array()); + + $formdata->category = "{$cat->id},{$cat->contextid}"; + qtype_oumatrix_edit_form::mock_submit((array)$formdata); + + $form = \qtype_oumatrix_test_helper::get_question_editing_form($cat, $questiondata); + + $this->assertTrue($form->is_validated()); + + $fromform = $form->get_data(); + + $returnedfromsave = $this->qtype->save_question($questiondata, $fromform); + $actualquestionsdata = question_load_questions([$returnedfromsave->id], 'qbe.idnumber'); + $actualquestiondata = end($actualquestionsdata); + + foreach ($questiondata as $property => $value) { + if (!in_array($property, ['id', 'timemodified', 'timecreated', 'options', 'hints', 'stamp', + 'versionid', 'questionbankentryid'])) { + $this->assertEquals($value, $actualquestiondata->$property); + } + } + + foreach ($questiondata->options as $optionname => $value) { + if ($optionname === 'columns' || $optionname === 'rows') { + continue; + } + $this->assertEquals($value, $actualquestiondata->options->$optionname); + } + + foreach ($questiondata->options->columns as $id => $column) { + $actualcolumn = array_shift($actualquestiondata->options->columns); + // TODO: finsih this. + } + + foreach ($questiondata->options->rows as $id => $row) { + $actualrow = array_shift($actualquestiondata->options->rows); + // TODO: finsih this. + } + + foreach ($questiondata->hints as $hint) { + $actualhint = array_shift($actualquestiondata->hints); + foreach ($hint as $property => $value) { + if (!in_array($property, array('id', 'questionid', 'options'))) { + $this->assertEquals($value, $actualhint->$property); + } + } + } + } + + /** + * Test to make sure that loading of question options works, including in an error case. + */ + public function test_get_question_options() { + global $DB; + + $this->resetAfterTest(true); + $this->setAdminUser(); + + // Create a complete, in DB question to use. + $questiondata = \test_question_maker::get_question_data('oumatrix', 'four_by_four_single'); + $formdata = \test_question_maker::get_question_form_data('oumatrix', 'four_by_four_single'); + + $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); + $cat = $generator->create_question_category(array()); + + $formdata->category = "{$cat->id},{$cat->contextid}"; + qtype_oumatrix_edit_form::mock_submit((array)$formdata); + + $form = \qtype_oumatrix_test_helper::get_question_editing_form($cat, $questiondata); + + $this->assertTrue($form->is_validated()); + + $fromform = $form->get_data(); + + $returnedfromsave = $this->qtype->save_question($questiondata, $fromform); + + // Now get just the raw DB record. + $question = $DB->get_record('question', ['id' => $returnedfromsave->id], '*', MUST_EXIST); + + // Load it. + $this->qtype->get_question_options($question); + $this->assertDebuggingNotCalled(); + $this->assertInstanceOf(\stdClass::class, $question->options); + + $options = $question->options; + $this->assertEquals($question->id, $options->questionid); + $this->assertEquals(0, $options->single); + + $this->assertCount(4, $options->answers); + + // Now we are going to delete the options record. + $DB->delete_records('qtype_oumatrix_options', ['questionid' => $question->id]); + + // Now see what happens. + $question = $DB->get_record('question', ['id' => $returnedfromsave->id], '*', MUST_EXIST); + $this->qtype->get_question_options($question); + + $this->assertDebuggingCalled('Question ID '.$question->id.' was missing an options record. Using default.'); + $this->assertInstanceOf(\stdClass::class, $question->options); + $options = $question->options; + $this->assertEquals($question->id, $options->questionid); + $this->assertCount(4, $options->answers); + + $this->assertEquals(get_string('correctfeedbackdefault', 'question'), $options->correctfeedback); + $this->assertEquals(FORMAT_HTML, $options->correctfeedbackformat); + + // We no longer know how many answers, so it just has to guess with the default value. + $this->assertEquals(get_config('qtype_oumatrix', 'answerhowmany'), $options->single); + + // And finally we try again with no answer either. + $DB->delete_records('question_answers', ['question' => $question->id]); + + $question = $DB->get_record('question', ['id' => $returnedfromsave->id], '*', MUST_EXIST); + $this->qtype->get_question_options($question); + + $this->assertDebuggingCalled('Question ID '.$question->id.' was missing an options record. Using default.'); + $this->assertInstanceOf(\stdClass::class, $question->options); + $options = $question->options; + $this->assertEquals($question->id, $options->questionid); + $this->assertCount(0, $options->answers); + } +}