diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fde9f95..c6952b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,16 +8,14 @@ jobs: fail-fast: false matrix: include: - - php: '7.4' - moodle-branch: 'master' - database: 'pgsql' - - php: '7.4' - moodle-branch: 'MOODLE_311_STABLE' - database: 'mariadb' + - { php: '8.1', moodle-branch: MOODLE_404_STABLE, database: pgsql } + - { php: '8.2', moodle-branch: MOODLE_405_STABLE, database: mariadb } + - { php: '8.3', moodle-branch: MOODLE_500_STABLE, database: pgsql } + - { php: '8.3', moodle-branch: main, database: pgsql } services: postgres: - image: postgres + image: postgres:14 env: POSTGRES_USER: 'postgres' POSTGRES_HOST_AUTH_METHOD: 'trust' @@ -30,7 +28,7 @@ jobs: - 5432:5432 mariadb: - image: mariadb + image: mariadb:10 env: MYSQL_USER: 'root' MYSQL_ALLOW_EMPTY_PASSWORD: "true" @@ -49,25 +47,25 @@ jobs: with: node-version: '14.15.0' - - name: Setup PHP + - name: Setup PHP ${{ matrix.php }} uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - extensions: mbstring, pgsql, mysqli -# tools: phpunit -# coverage: none + extensions: ${{ matrix.extensions }} + ini-values: max_input_vars=5000 + coverage: none - - name: Deploy moodle-plugin-ci + - name: Initialise moodle-plugin-ci run: | - composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3 - # Add dirs to $PATH + composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^4 echo $(cd ci/bin; pwd) >> $GITHUB_PATH echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH - # PHPUnit depends on en_AU.UTF-8 locale sudo locale-gen en_AU.UTF-8 + echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV - - name: Install Moodle - run: moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 + - name: Install moodle-plugin-ci + run: | + moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 env: DB: ${{ matrix.database }} MOODLE_BRANCH: ${{ matrix.moodle-branch }} diff --git a/backup/moodle2/backup_qtype_oumultiresponse_plugin.class.php b/backup/moodle2/backup_qtype_oumultiresponse_plugin.class.php index bfa6d8d..2dc21e5 100644 --- a/backup/moodle2/backup_qtype_oumultiresponse_plugin.class.php +++ b/backup/moodle2/backup_qtype_oumultiresponse_plugin.class.php @@ -15,6 +15,8 @@ // along with Moodle. If not, see . /** + * Backup plugin for the OU multiple response question type. + * * @package qtype_oumultiresponse * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -47,18 +49,19 @@ protected function define_question_plugin_structure() { $this->add_question_question_answers($pluginwrapper); // Now create the qtype own structures. - $oumultiresponse = new backup_nested_element('oumultiresponse', array('id'), array( + $oumultiresponse = new backup_nested_element('oumultiresponse', ['id'], [ 'shuffleanswers', 'correctfeedback', 'correctfeedbackformat', 'partiallycorrectfeedback', 'partiallycorrectfeedbackformat', 'incorrectfeedback', 'incorrectfeedbackformat', 'answernumbering', - 'shownumcorrect', 'showstandardinstruction')); + 'shownumcorrect', 'showstandardinstruction', + ]); // Now the own qtype tree. $pluginwrapper->add_child($oumultiresponse); // Set source to populate the data. $oumultiresponse->set_source_table('question_oumultiresponse', - array('questionid' => backup::VAR_PARENTID)); + ['questionid' => backup::VAR_PARENTID]); // Don't need to annotate ids nor files. @@ -72,9 +75,10 @@ protected function define_question_plugin_structure() { * files to be processed both in backup and restore. */ public static function get_qtype_fileareas() { - return array( + return [ 'correctfeedback' => 'question_created', 'partiallycorrectfeedback' => 'question_created', - 'incorrectfeedback' => 'question_created'); + 'incorrectfeedback' => 'question_created', + ]; } } diff --git a/backup/moodle2/restore_qtype_oumultiresponse_plugin.class.php b/backup/moodle2/restore_qtype_oumultiresponse_plugin.class.php index 586f513..0b0fcb9 100644 --- a/backup/moodle2/restore_qtype_oumultiresponse_plugin.class.php +++ b/backup/moodle2/restore_qtype_oumultiresponse_plugin.class.php @@ -15,6 +15,8 @@ // along with Moodle. If not, see . /** + * Restore plugin class for the OU multiple response question type. + * * @package qtype_oumultiresponse * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -34,7 +36,7 @@ class restore_qtype_oumultiresponse_plugin extends restore_qtype_plugin { */ protected function define_question_plugin_structure() { - $paths = array(); + $paths = []; // This qtype uses question_answers, add them. $this->add_question_question_answers($paths); @@ -74,6 +76,7 @@ public function process_oumultiresponse($data) { } } + #[\Override] public function recode_response($questionid, $sequencenumber, array $response) { if (array_key_exists('_order', $response)) { $response['_order'] = $this->recode_choice_order($response['_order']); @@ -87,7 +90,7 @@ public function recode_response($questionid, $sequencenumber, array $response) { * @return string the recoded order. */ protected function recode_choice_order($order) { - $neworder = array(); + $neworder = []; foreach (explode(',', $order) as $id) { if ($newid = $this->get_mappingid('question_answer', $id)) { $neworder[] = $newid; @@ -101,9 +104,9 @@ protected function recode_choice_order($order) { */ public static function define_decode_contents() { - $contents = array(); + $contents = []; - $fields = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'); + $fields = ['correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback']; $contents[] = new restore_decode_content('question_oumultiresponse', $fields, 'question_oumultiresponse'); diff --git a/changes.md b/changes.md index 0f1530d..cfa01e7 100644 --- a/changes.md +++ b/changes.md @@ -1,5 +1,15 @@ # Change log for the OU Multi-response question type +## Changes in 2.5 +* This version works with Moodle 5.0. +* Automation test failures are fixed. +* Cherry-picked commits since february 2024 till now: + * Update btn to "Save preview options and start again" + * Fix backup and restore tests to run synchronously M4.4 + * Choice Tiny Editor disrupts the theme column layout on edit question page + * Add required for answer field when user not submit an answer +* Upgrade the CI to support Moodle 5.0 (PHP 8.3), and update the branch to support branch MOODLE_405_STABLE, and MOODLE_500_STABLE. + ## Changes in 2.4 * This version works with Moodle 4.0. diff --git a/classes/output/mobile.php b/classes/output/mobile.php index bd28671..0faa57e 100644 --- a/classes/output/mobile.php +++ b/classes/output/mobile.php @@ -25,6 +25,9 @@ */ class mobile { + /** + * Returns the mobile output for the oumultiresponse question type. + */ public static function oumr_view() { global $CFG; // General notes: @@ -35,9 +38,9 @@ public static function oumr_view() { return [ 'templates' => [[ 'id' => 'main', - 'html' => file_get_contents($CFG->dirroot . '/question/type/oumultiresponse/mobile/oumr.html') + 'html' => file_get_contents($CFG->dirroot . '/question/type/oumultiresponse/mobile/oumr.html'), ]], - 'javascript' => file_get_contents($CFG->dirroot . '/question/type/oumultiresponse/mobile/oumr.js') + 'javascript' => file_get_contents($CFG->dirroot . '/question/type/oumultiresponse/mobile/oumr.js'), ]; } } diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 3a8b0ed..ad8e578 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -13,6 +13,7 @@ // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . + /** * Privacy Subsystem implementation for qtype_oumultiresponse. * @@ -22,10 +23,10 @@ */ namespace qtype_oumultiresponse\privacy; -use \core_privacy\local\metadata\collection; -use \core_privacy\local\request\transform; -use \core_privacy\local\request\user_preference_provider; -use \core_privacy\local\request\writer; +use core_privacy\local\metadata\collection; +use core_privacy\local\request\transform; +use core_privacy\local\request\user_preference_provider; +use core_privacy\local\request\writer; /** * Privacy Subsystem for qtype_oumultiresponse implementing user_preference_provider. diff --git a/classes/utils.php b/classes/utils.php new file mode 100644 index 0000000..fdb66af --- /dev/null +++ b/classes/utils.php @@ -0,0 +1,104 @@ +. + +/** + * Utility functions for the oumultiresponse question type. + * + * @package qtype_oumultiresponse + * @copyright 2025 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace qtype_oumultiresponse; + +/** + * Class that holds utility functions used by the oumultiresponse question type. + * + * @package qtype_oumultiresponse + * @copyright 2025 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class utils { + + /** @var string - Less than operator */ + const OP_LT = "<"; + /** @var string - equal operator */ + const OP_E = "="; + /** @var string - greater than operator */ + const OP_GT = ">"; + + /** + * Conveniently compare the current moodle version to a provided version in branch format. This function will + * inflate version numbers to a three digit number before comparing them. This way moodle minor versions greater + * than 9 can be correctly and easily compared. + * + * Examples: + * utils::moodle_version_is("<", "39"); + * utils::moodle_version_is("<=", "310"); + * utils::moodle_version_is(">", "39"); + * utils::moodle_version_is(">=", "38"); + * utils::moodle_version_is("=", "41"); + * + * CFG reference: + * $CFG->branch = "311", "310", "39", "38", ... + * $CFG->release = "3.11+ (Build: 20210604)", ... + * $CFG->version = "2021051700.04", ... + * + * @param string $operator for the comparison + * @param string $version to compare to + * @return boolean + */ + public static function moodle_version_is(string $operator, string $version): bool { + global $CFG; + + if (strlen($version) == 2) { + $version = $version[0]."0".$version[1]; + } + + $current = $CFG->branch; + if (strlen($current) == 2) { + $current = $current[0]."0".$current[1]; + } + + $from = intval($current); + $to = intval($version); + $ops = str_split($operator); + + foreach ($ops as $op) { + switch ($op) { + case self::OP_LT: + if ($from < $to) { + return true; + } + break; + case self::OP_E: + if ($from == $to) { + return true; + } + break; + case self::OP_GT: + if ($from > $to) { + return true; + } + break; + default: + throw new \coding_exception('invalid operator '.$op); + } + } + + return false; + } +} diff --git a/combinable/combinable.php b/combinable/combinable.php index 7c872ec..5630109 100644 --- a/combinable/combinable.php +++ b/combinable/combinable.php @@ -25,18 +25,29 @@ defined('MOODLE_INTERNAL') || die(); +/** + * Combined question type for oumultiresponse. + */ class qtype_combined_combinable_type_oumultiresponse extends qtype_combined_combinable_type_base { + /** + * The question type identifier. + * + * @var string + */ protected $identifier = 'multiresponse'; + #[\Override] protected function extra_question_properties() { return $this->combined_feedback_properties(); } + #[\Override] protected function extra_answer_properties() { - return array('feedback' => array('text' => '', 'format' => FORMAT_PLAIN)); + return ['feedback' => ['text' => '', 'format' => FORMAT_PLAIN]]; } + #[\Override] public function subq_form_fragment_question_option_fields() { return [ 'shuffleanswers' => (bool) get_config('qtype_combined', 'shuffleanswers_multiresponse'), @@ -44,26 +55,27 @@ public function subq_form_fragment_question_option_fields() { ]; } + #[\Override] protected function transform_subq_form_data_to_full($subqdata) { $data = parent::transform_subq_form_data_to_full($subqdata); foreach ($data->answer as $anskey => $answer) { - $data->answer[$anskey] = array('text' => $answer['text'], 'format' => $answer['format']); + $data->answer[$anskey] = ['text' => $answer['text'], 'format' => $answer['format']]; } return $this->add_per_answer_properties($data); } + #[\Override] protected function third_param_for_default_question_text() { return 'v'; } } +/** + * Class question type for combined combinable oumultiresponse. + */ class qtype_combined_combinable_oumultiresponse extends qtype_combined_combinable_accepts_vertical_or_horizontal_layout_param { - /** - * @param moodleform $combinedform - * @param MoodleQuickForm $mform - * @param $repeatenabled - */ + #[\Override] public function add_form_fragment(moodleform $combinedform, MoodleQuickForm $mform, $repeatenabled) { $mform->addElement('advcheckbox', $this->form_field_name('shuffleanswers'), get_string('shuffle', 'qtype_combined')); @@ -75,12 +87,12 @@ public function add_form_fragment(moodleform $combinedform, MoodleQuickForm $mfo $mform->setDefault($this->form_field_name('answernumbering'), get_config('qtype_combined', 'answernumbering_multiresponse')); - $answerels = array(); + $answerels = []; $answerels[] = $mform->createElement('editor', $this->form_field_name('answer'), get_string('choiceno', 'qtype_multichoice', '{no}'), ['rows' => 2]); $mform->setType($this->form_field_name('answer'), PARAM_RAW); $answerels[] = $mform->createElement('advcheckbox', $this->form_field_name('correctanswer'), - get_string('correct', 'question'), get_string('correct', 'question')); + '', get_string('correct', 'question')); $answergroupel = $mform->createElement('group', $this->form_field_name('answergroup'), @@ -93,9 +105,9 @@ public function add_form_fragment(moodleform $combinedform, MoodleQuickForm $mfo $repeatsatstart = max(5, QUESTION_NUMANS_START); } - $combinedform->repeat_elements(array($answergroupel), + $combinedform->repeat_elements([$answergroupel], $repeatsatstart, - array(), + [], $this->form_field_name('noofchoices'), $this->form_field_name('morechoices'), QUESTION_NUMANS_ADD, @@ -103,8 +115,9 @@ public function add_form_fragment(moodleform $combinedform, MoodleQuickForm $mfo true); } + #[\Override] public function data_to_form($context, $fileoptions) { - $mroptions = array('answer' => array(), 'correctanswer' => array()); + $mroptions = ['answer' => [], 'correctanswer' => []]; if ($this->questionrec !== null) { foreach ($this->questionrec->options->answers as $questionrecanswer) { $mroptions['answer'][] = [ @@ -117,9 +130,10 @@ public function data_to_form($context, $fileoptions) { return parent::data_to_form($context, $fileoptions) + $mroptions; } + #[\Override] public function validate() { - $errors = array(); - $nonemptyanswerblanks = array(); + $errors = []; + $nonemptyanswerblanks = []; foreach ($this->formdata->answer as $anskey => $answer) { $answer = $answer['text']; if ('' !== trim($answer)) { @@ -138,6 +152,7 @@ public function validate() { return $errors; } + #[\Override] public function has_submitted_data() { return $this->submitted_data_array_not_empty('correctanswer') || $this->html_field_has_submitted_data($this->form_field_name('answer')) || diff --git a/combinable/renderer.php b/combinable/renderer.php index 6a07327..4a4f0d5 100644 --- a/combinable/renderer.php +++ b/combinable/renderer.php @@ -22,10 +22,10 @@ * @author Jamie Pratt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - class qtype_oumultiresponse_embedded_renderer extends qtype_renderer implements qtype_combined_subquestion_renderer_interface { + #[\Override] public function subquestion(question_attempt $qa, question_display_options $options, qtype_combined_combinable_base $subq, @@ -34,21 +34,22 @@ public function subquestion(question_attempt $qa, $fullresponse = new qtype_combined_response_array_param($qa->get_last_qt_data()); $response = $fullresponse->for_subq($subq); - $commonattributes = array( - 'type' => 'checkbox' - ); + $commonattributes = [ + 'type' => 'checkbox', + 'class' => empty($response) ? 'required' : '', + ]; if ($options->readonly) { $commonattributes['disabled'] = 'disabled'; } - $checkboxes = array(); - $feedbackimg = array(); - $classes = array(); + $checkboxes = []; + $feedbackimg = []; + $classes = []; foreach ($question->get_order($qa) as $value => $ansid) { $inputname = $qa->get_qt_field_name($subq->step_data_name('choice'.$value)); $ans = $question->answers[$ansid]; - $inputattributes = array(); + $inputattributes = []; $inputattributes['name'] = $inputname; $inputattributes['value'] = 1; $inputattributes['id'] = $inputname; @@ -59,11 +60,11 @@ public function subquestion(question_attempt $qa, } $hidden = ''; if (!$options->readonly) { - $hidden = html_writer::empty_tag('input', array( + $hidden = html_writer::empty_tag('input', [ 'type' => 'hidden', 'name' => $inputattributes['name'], 'value' => 0, - )); + ]); } $choice = html_writer::div($question->format_text($ans->answer, $ans->answerformat, $qa, @@ -95,10 +96,10 @@ public function subquestion(question_attempt $qa, foreach ($checkboxes as $key => $checkbox) { $cbhtml .= html_writer::tag($inputwraptag, $checkbox . ' ' . $feedbackimg[$key], - array('class' => $classes[$key])) . "\n"; + ['class' => $classes[$key]]) . "\n"; } - $result = html_writer::tag($inputwraptag, $cbhtml, array('class' => 'answer')); + $result = html_writer::tag($inputwraptag, $cbhtml, ['class' => 'answer']); $result = html_writer::div($result, $classname); // Load JS module for the question answers. diff --git a/db/mobile.php b/db/mobile.php index 543e557..950a032 100644 --- a/db/mobile.php +++ b/db/mobile.php @@ -29,8 +29,8 @@ 'handlers' => [ 'oumultiresponse' => [ 'delegate' => 'CoreQuestionDelegate', - 'method' => 'oumr_view' - ] - ] - ] + 'method' => 'oumr_view', + ], + ], + ], ]; diff --git a/edit_oumultiresponse_form.php b/edit_oumultiresponse_form.php index f32ef8b..875f26c 100644 --- a/edit_oumultiresponse_form.php +++ b/edit_oumultiresponse_form.php @@ -30,6 +30,7 @@ */ class qtype_oumultiresponse_edit_form extends question_edit_form { + #[\Override] protected function definition_inner($mform) { $mform->addElement('advcheckbox', 'shuffleanswers', get_string('shuffleanswers', 'qtype_multichoice'), null, null, [0, 1]); @@ -57,9 +58,10 @@ protected function definition_inner($mform) { $this->add_interactive_settings(true, true); } + #[\Override] protected function get_per_answer_fields($mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) { - $repeated = array(); + $repeated = []; $repeated[] = $mform->createElement('editor', 'answer', $label, ['rows' => 2], $this->editoroptions); $repeated[] = $mform->createElement('checkbox', 'correctanswer', @@ -74,6 +76,7 @@ protected function get_per_answer_fields($mform, $label, $gradeoptions, return $repeated; } + #[\Override] protected function get_hint_fields($withclearwrong = false, $withshownumpartscorrect = false) { list($repeated, $repeatedoptions) = parent::get_hint_fields( $withclearwrong, $withshownumpartscorrect); @@ -95,9 +98,10 @@ protected function get_hint_fields($withclearwrong = false, $withshownumpartscor $repeated[] = $showchoicefeedback; } - return array($repeated, $repeatedoptions); + return [$repeated, $repeatedoptions]; } + #[\Override] protected function data_preprocessing($question) { $question = parent::data_preprocessing($question); $question = $this->data_preprocessing_answers($question, true); @@ -129,6 +133,7 @@ protected function data_preprocessing($question) { return $question; } + #[\Override] public function validation($data, $files) { $errors = parent::validation($data, $files); @@ -163,6 +168,11 @@ public function validation($data, $files) { return $errors; } + /** + * Returns the question type. + * + * @return string The question type. + */ public function qtype() { return 'oumultiresponse'; } diff --git a/lang/en/qtype_oumultiresponse.php b/lang/en/qtype_oumultiresponse.php index 799af2f..86ccc17 100644 --- a/lang/en/qtype_oumultiresponse.php +++ b/lang/en/qtype_oumultiresponse.php @@ -37,14 +37,14 @@ $string['pluginnamesummary'] = '

A multiple-choice, multiple-response question type with particular scoring rules.

Recommended if your question has more than one correct answer.

'; $string['privacy:metadata'] = 'Multiple response question type plugin allows question authors to set default options as user preferences.'; +$string['privacy:preference:answernumbering'] = 'Which numbering stye should be used (1., 2., 3., .../a., b., c., ... etc.)'; $string['privacy:preference:defaultmark'] = 'The default mark set for a given question.'; $string['privacy:preference:penalty'] = 'The penalty for each incorrect try when questions are run using the \'Interactive with multiple tries\' or \'Adaptive mode\' behaviour.'; -$string['privacy:preference:shuffleanswers'] = 'Whether the answers should be automatically shuffled.'; -$string['privacy:preference:answernumbering'] = 'Which numbering stye should be used (1., 2., 3., .../a., b., c., ... etc.)'; $string['privacy:preference:showstandardinstruction'] = 'Whether showing standard instruction.'; -$string['toomanyoptions'] = 'You have selected too many options.'; +$string['privacy:preference:shuffleanswers'] = 'Whether the answers should be automatically shuffled.'; $string['showeachanswerfeedback'] = 'Show the feedback for the selected responses.'; -$string['yougotnright'] = 'You have correctly selected {$a->num} options.'; -$string['yougot1right'] = 'You have correctly selected one option.'; $string['showstandardinstruction'] = 'Show standard instruction'; $string['showstandardinstruction_help'] = 'With this setting enabled, standard instruction will be supplied as part of the selection area (e.g. "Select one or more:"). If disabled, question authors can instead included instructions in the question content, if required.'; +$string['toomanyoptions'] = 'You have selected too many options.'; +$string['yougot1right'] = 'You have correctly selected one option.'; +$string['yougotnright'] = 'You have correctly selected {$a->num} options.'; diff --git a/lib.php b/lib.php index a9adc7a..bbbdded 100644 --- a/lib.php +++ b/lib.php @@ -25,7 +25,7 @@ /** * Checks file access for oumultiresponse questions. */ -function qtype_oumultiresponse_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) { +function qtype_oumultiresponse_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = []) { global $CFG; require_once($CFG->libdir . '/questionlib.php'); question_pluginfile($course, $context, 'qtype_oumultiresponse', $filearea, $args, $forcedownload, $options); diff --git a/question.php b/question.php index e9dc0aa..385dc4d 100644 --- a/question.php +++ b/question.php @@ -52,6 +52,7 @@ public function get_renderer(moodle_page $page) { return $page->get_renderer('qtype_oumultiresponse'); } + #[\Override] public function make_behaviour(question_attempt $qa, $preferredbehaviour) { if ($preferredbehaviour == 'interactive') { return question_engine::make_behaviour( @@ -60,6 +61,7 @@ public function make_behaviour(question_attempt $qa, $preferredbehaviour) { return question_engine::make_archetypal_behaviour($preferredbehaviour, $qa); } + #[\Override] public function classify_response(array $response) { $choices = parent::classify_response($response); $numright = $this->get_num_correct_choices(); @@ -69,6 +71,9 @@ public function classify_response(array $response) { return $choices; } + /** + * Grade the response. + */ public function grade_response(array $response) { list($numright, $total) = $this->get_num_parts_right($response); $numwrong = $this->get_num_selected_choices($response) - $numright; @@ -81,17 +86,21 @@ public function grade_response(array $response) { $state = question_state::$gradedpartial; } - return array($fraction, $state); + return [$fraction, $state]; } + #[\Override] protected function disable_hint_settings_when_too_many_selected( question_hint_with_parts $hint) { parent::disable_hint_settings_when_too_many_selected($hint); $hint->showchoicefeedback = false; } + /** + * Compute the final grade. + */ public function compute_final_grade($responses, $totaltries) { - $responsehistories = array(); + $responsehistories = []; foreach ($this->order as $key => $ansid) { $fieldname = $this->field($key); $responsehistories[$ansid] = ''; @@ -123,7 +132,7 @@ public static function grade_computation($responsehistory, $answers, $penalty, $questionnumtries) { // First we reverse the strings to get the most recent responses to the start, then // distinguish right and wrong by replacing 1 with 2 for right answers. - $workspace = array(); + $workspace = []; $numright = 0; foreach ($responsehistory as $id => $string) { $workspace[$id] = strrev($string); @@ -151,7 +160,7 @@ public static function grade_computation($responsehistory, $answers, } if ($numselected > $numright) { $numtoclear = $numselected - $numright; - $newworkspace = array(); + $newworkspace = []; foreach ($workspace as $string) { if (substr($string, $try, 1) == '2' && $numtoclear > 0) { $string = self::replace_char_at($string, $try, '0'); @@ -183,6 +192,13 @@ public static function grade_computation($responsehistory, $answers, return array_sum($scores); } + /** + * Replace a character at a given position. + * + * @param string $string The string to modify. + * @param int $pos The position of the character to replace (0-based). + * @param string $newchar The new character to insert. + */ public static function replace_char_at($string, $pos, $newchar) { return substr($string, 0, $pos) . $newchar . substr($string, $pos + 1); } diff --git a/questiontype.php b/questiontype.php index 5f76bef..42380ac 100644 --- a/questiontype.php +++ b/questiontype.php @@ -43,21 +43,28 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qtype_oumultiresponse extends question_type { + + #[\Override] public function has_html_answers() { return true; } + /** + * Get the name of this question type. + */ public function requires_qtypes() { - return array('multichoice'); + return ['multichoice']; } + #[\Override] public function get_question_options($question) { global $DB; $question->options = $DB->get_record('question_oumultiresponse', - array('questionid' => $question->id), '*', MUST_EXIST); + ['questionid' => $question->id], '*', MUST_EXIST); parent::get_question_options($question); } + #[\Override] public function save_defaults_for_new_questions(stdClass $fromform): void { parent::save_defaults_for_new_questions($fromform); $this->set_default_value('shuffleanswers', $fromform->shuffleanswers); @@ -65,13 +72,14 @@ public function save_defaults_for_new_questions(stdClass $fromform): void { $this->set_default_value('showstandardinstruction', $fromform->showstandardinstruction); } + #[\Override] public function save_question_options($question) { global $DB; $context = $question->context; $result = new stdClass(); $oldanswers = $DB->get_records('question_answers', - array('question' => $question->id), 'id ASC'); + ['question' => $question->id], 'id ASC'); // The following hack to checks that at least two answers exist. $answercount = 0; @@ -86,7 +94,7 @@ public function save_question_options($question) { } // Insert all the new answers. - $answers = array(); + $answers = []; foreach ($question->answer as $key => $answerdata) { if (trim($answerdata['text']) == '') { continue; @@ -118,11 +126,11 @@ public function save_question_options($question) { $fs = get_file_storage(); foreach ($oldanswers as $oldanswer) { $fs->delete_area_files($context->id, 'question', 'answerfeedback', $oldanswer->id); - $DB->delete_records('question_answers', array('id' => $oldanswer->id)); + $DB->delete_records('question_answers', ['id' => $oldanswer->id]); } $options = $DB->get_record('question_oumultiresponse', - array('questionid' => $question->id)); + ['questionid' => $question->id]); if (!$options) { $options = new stdClass(); $options->questionid = $question->id; @@ -142,12 +150,13 @@ public function save_question_options($question) { $this->save_hints($question, true); } + #[\Override] public function save_hints($formdata, $withparts = false) { global $DB; $context = $formdata->context; $oldhints = $DB->get_records('question_hints', - array('questionid' => $formdata->id), 'id ASC'); + ['questionid' => $formdata->id], 'id ASC'); if (!empty($formdata->hint)) { $numhints = max(array_keys($formdata->hint)) + 1; @@ -217,20 +226,23 @@ public function save_hints($formdata, $withparts = false) { $fs = get_file_storage(); foreach ($oldhints as $oldhint) { $fs->delete_area_files($context->id, 'question', 'hint', $oldhint->id); - $DB->delete_records('question_hints', array('id' => $oldhint->id)); + $DB->delete_records('question_hints', ['id' => $oldhint->id]); } } + #[\Override] protected function make_hint($hint) { return qtype_oumultiresponse_hint::load_from_record($hint); } + #[\Override] // phpcs:ignore Generic.CodeAnalysis.UselessOverridingMethod.Found public function make_answer($answer) { // Overridden just so we can make it public for use by question.php. return parent::make_answer($answer); } + #[\Override] protected function initialise_question_instance(question_definition $question, $questiondata) { parent::initialise_question_instance($question, $questiondata); $question->shuffleanswers = $questiondata->options->shuffleanswers; @@ -240,12 +252,18 @@ protected function initialise_question_instance(question_definition $question, $ $this->initialise_question_answers($question, $questiondata, false); } + #[\Override] public function delete_question($questionid, $contextid) { global $DB; - $DB->delete_records('question_oumultiresponse', array('questionid' => $questionid)); + $DB->delete_records('question_oumultiresponse', ['questionid' => $questionid]); return parent::delete_question($questionid, $contextid); } + /** + * Get the number of correct choices in the question. + * + * @param question_definition $questiondata The question data. + */ protected function get_num_correct_choices($questiondata) { $numright = 0; foreach ($questiondata->options->answers as $answer) { @@ -256,6 +274,7 @@ protected function get_num_correct_choices($questiondata) { return $numright; } + #[\Override] public function get_random_guess_score($questiondata) { // We compute the randome guess score here on the assumption we are using // the deferred feedback behaviour, and the question text tells the @@ -266,18 +285,20 @@ public function get_random_guess_score($questiondata) { count($questiondata->options->answers); } + #[\Override] public function get_possible_responses($questiondata) { $numright = $this->get_num_correct_choices($questiondata); - $parts = array(); + $parts = []; foreach ($questiondata->options->answers as $aid => $answer) { - $parts[$aid] = array($aid => - new question_possible_response($answer->answer, $answer->fraction / $numright)); + $parts[$aid] = [$aid => + new question_possible_response($answer->answer, $answer->fraction / $numright)]; } return $parts; } + #[\Override] public function import_from_xml($data, $question, qformat_xml $format, $extra=null) { if (!isset($data['@']['type']) || $data['@']['type'] != 'oumultiresponse') { return false; @@ -287,11 +308,11 @@ public function import_from_xml($data, $question, qformat_xml $format, $extra=nu $question->qtype = 'oumultiresponse'; $question->shuffleanswers = $format->trans_single( - $format->getpath($data, array('#', 'shuffleanswers', 0, '#'), 1)); + $format->getpath($data, ['#', 'shuffleanswers', 0, '#'], 1)); $question->answernumbering = $format->getpath($data, - array('#', 'answernumbering', 0, '#'), 'abc'); + ['#', 'answernumbering', 0, '#'], 'abc'); $question->showstandardinstruction = $format->getpath($data, - array('#', 'showstandardinstruction', 0, '#'), 1); + ['#', 'showstandardinstruction', 0, '#'], 1); $format->import_combined_feedback($question, $data, true); @@ -308,7 +329,7 @@ public function import_from_xml($data, $question, qformat_xml $format, $extra=nu if (array_key_exists('correctanswer', $answer['#'])) { $keys = array_keys($question->correctanswer); $question->correctanswer[end($keys)] = $format->getpath($answer, - array('#', 'correctanswer', 0, '#'), 0); + ['#', 'correctanswer', 0, '#'], 0); } } @@ -325,6 +346,7 @@ public function import_from_xml($data, $question, qformat_xml $format, $extra=nu return $question; } + #[\Override] public function export_to_xml($question, qformat_xml $format, $extra = null) { $output = ''; @@ -341,6 +363,7 @@ public function export_to_xml($question, qformat_xml $format, $extra = null) { return $output; } + #[\Override] public function move_files($questionid, $oldcontextid, $newcontextid) { $fs = get_file_storage(); @@ -356,6 +379,7 @@ public function move_files($questionid, $oldcontextid, $newcontextid) { $newcontextid, 'question', 'incorrectfeedback', $questionid); } + #[\Override] protected function delete_files($questionid, $contextid) { $fs = get_file_storage(); @@ -377,7 +401,7 @@ protected function delete_files($questionid, $contextid) { * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qtype_oumultiresponse_hint extends question_hint_with_parts { - /** @var boolean whether to show the feedback for each choice. */ + /** @var bool whether to show the feedback for each choice. */ public $showchoicefeedback; /** @@ -403,10 +427,11 @@ public static function load_from_record($row) { $row->shownumcorrect, $row->clearwrong, !empty($row->options)); } + #[\Override] public function adjust_display_options(question_display_options $options) { parent::adjust_display_options($options); if (defined('qtype_multichoice::COMBINED_BUT_NOT_CHOICE_FEEDBACK')) { - // Newer Moodle versions/ + // Newer Moodle versions. if ($options->feedback && !$this->showchoicefeedback) { $options->feedback = qtype_multichoice::COMBINED_BUT_NOT_CHOICE_FEEDBACK; } diff --git a/renderer.php b/renderer.php index 8be7161..54587e3 100644 --- a/renderer.php +++ b/renderer.php @@ -28,6 +28,9 @@ require_once($CFG->dirroot . '/question/type/multichoice/renderer.php'); require_once($CFG->dirroot . '/question/type/oumultiresponse/lib.php'); +/** + * Class renderer for the OU multiple response question type. + */ class qtype_oumultiresponse_renderer extends qtype_multichoice_multi_renderer { /** diff --git a/tests/behat/backup_and_restore.feature b/tests/behat/backup_and_restore.feature index 1910466..c05f279 100644 --- a/tests/behat/backup_and_restore.feature +++ b/tests/behat/backup_and_restore.feature @@ -19,6 +19,8 @@ Feature: Test duplicating a quiz containing an OU multiple response question | quiz | Test quiz | C1 | quiz1 | And quiz "Test quiz" contains the following questions: | oumultiresponse 001 | 1 | + And the following config values are set as admin: + | enableasyncbackup | 0 | @javascript Scenario: Backup and restore a course containing an OU multiple response question diff --git a/tests/behat/preview.feature b/tests/behat/preview.feature index 34adc3b..ea0537e 100644 --- a/tests/behat/preview.feature +++ b/tests/behat/preview.feature @@ -25,7 +25,7 @@ Feature: Preview an OU multiple response question When I am on the "oumultiresponse 001" "core_question > preview" page logged in as teacher And I expand all fieldsets And I set the field "How questions behave" to "Immediate feedback" - And I press "Start again with these options" + And I press "id_saverestart" And I click on "One" "qtype_multichoice > Answer" And I click on "Two" "qtype_multichoice > Answer" And I press "Check" @@ -38,7 +38,7 @@ Feature: Preview an OU multiple response question When I am on the "oumultiresponse 001" "core_question > preview" page logged in as teacher And I expand all fieldsets And I set the field "How questions behave" to "Immediate feedback" - And I press "Start again with these options" + And I press "id_saverestart" And I click on "One" "qtype_multichoice > Answer" And I click on "Three" "qtype_multichoice > Answer" And I press "Check" @@ -60,7 +60,7 @@ Feature: Preview an OU multiple response question And I am on the "oumultiresponse 002" "core_question > preview" page And I expand all fieldsets And I set the field "How questions behave" to "Immediate feedback" - And I press "Start again with these options" + And I press "id_saverestart" And I click on "One" "qtype_multichoice > Answer" And I click on "Two" "qtype_multichoice > Answer" And I press "Check" diff --git a/tests/helper.php b/tests/helper.php index 6601397..f14b8ae 100644 --- a/tests/helper.php +++ b/tests/helper.php @@ -29,14 +29,22 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qtype_oumultiresponse_test_helper { + + /** + * Get test question function. + * + * @return array + */ public function get_test_questions() { - return array('two_of_four', 'two_of_five'); + return ['two_of_four', 'two_of_five']; } /** + * Create a question of type oumultiresponse with two correct answers out of four. + * * @return qtype_oumultiresponse_question */ - public static function make_oumultiresponse_question_two_of_four() { + public static function make_oumultiresponse_question_two_of_four(): qtype_oumultiresponse_question { question_bank::load_question_definition_classes('oumultiresponse'); $mc = new qtype_oumultiresponse_question(); @@ -54,17 +62,17 @@ public static function make_oumultiresponse_question_two_of_four() { test_question_maker::set_standard_combined_feedback_fields($mc); - $mc->answers = array( + $mc->answers = [ 13 => new question_answer(13, 'One', 1, 'One is odd.', FORMAT_HTML), 14 => new question_answer(14, 'Two', 0, 'Two is even.', FORMAT_HTML), 15 => new question_answer(15, 'Three', 1, 'Three is odd.', FORMAT_HTML), 16 => new question_answer(16, 'Four', 0, 'Four is even.', FORMAT_HTML), - ); + ]; - $mc->hints = array( + $mc->hints = [ new qtype_oumultiresponse_hint(1, 'Hint 1.', FORMAT_HTML, true, false, false), new qtype_oumultiresponse_hint(2, 'Hint 2.', FORMAT_HTML, true, true, true), - ); + ]; return $mc; } @@ -115,67 +123,69 @@ public static function get_oumultiresponse_question_data_two_of_four() { test_question_maker::STANDARD_OVERALL_INCORRECT_FEEDBACK; $qdata->options->incorrectfeedbackformat = FORMAT_HTML; - $qdata->options->answers = array( - 13 => (object) array( + $qdata->options->answers = [ + 13 => (object) [ 'id' => 13, 'answer' => 'One', 'answerformat' => FORMAT_PLAIN, 'fraction' => 1, 'feedback' => 'One is odd.', 'feedbackformat' => FORMAT_HTML, - ), - 14 => (object) array( + ], + 14 => (object) [ 'id' => 14, 'answer' => 'Two', 'answerformat' => FORMAT_PLAIN, 'fraction' => 0, 'feedback' => 'Two is even.', 'feedbackformat' => FORMAT_HTML, - ), - 15 => (object) array( + ], + 15 => (object) [ 'id' => 15, 'answer' => 'Three', 'answerformat' => FORMAT_PLAIN, 'fraction' => 1, 'feedback' => 'Three is odd.', 'feedbackformat' => FORMAT_HTML, - ), - 16 => (object) array( + ], + 16 => (object) [ 'id' => 16, 'answer' => 'Four', 'answerformat' => FORMAT_PLAIN, 'fraction' => 0, 'feedback' => 'Four is even.', 'feedbackformat' => FORMAT_HTML, - ), - ); + ], + ]; - $qdata->hints = array( - 1 => (object) array( + $qdata->hints = [ + 1 => (object) [ 'id' => 1, 'hint' => 'Hint 1.', 'hintformat' => FORMAT_HTML, 'shownumcorrect' => 1, 'clearwrong' => 0, 'options' => 0, - ), - 2 => (object) array( + ], + 2 => (object) [ 'id' => 2, 'hint' => 'Hint 2.', 'hintformat' => FORMAT_HTML, 'shownumcorrect' => 1, 'clearwrong' => 1, 'options' => 1, - ), - ); + ], + ]; return $qdata; } /** + * Make oumultiresponse question with two correct answers out of five. + * * @return qtype_oumultiresponse_question */ - public static function make_oumultiresponse_question_two_of_five() { + public static function make_oumultiresponse_question_two_of_five(): qtype_oumultiresponse_question { question_bank::load_question_definition_classes('oumultiresponse'); $mc = new qtype_oumultiresponse_question(); @@ -192,53 +202,55 @@ public static function make_oumultiresponse_question_two_of_five() { test_question_maker::set_standard_combined_feedback_fields($mc); - $mc->answers = array( + $mc->answers = [ 13 => new question_answer(13, 'A', 1, '', FORMAT_HTML), 14 => new question_answer(14, 'B', 1, '', FORMAT_HTML), 15 => new question_answer(15, 'C', 0, '', FORMAT_HTML), 16 => new question_answer(16, 'D', 0, '', FORMAT_HTML), 17 => new question_answer(17, 'E', 0, '', FORMAT_HTML), - ); + ]; - $mc->hints = array( + $mc->hints = [ 1 => new qtype_oumultiresponse_hint(1, 'Hint 1.', FORMAT_HTML, true, false, false), 2 => new qtype_oumultiresponse_hint(2, 'Hint 2.', FORMAT_HTML, true, true, true), - ); + ]; return $mc; } /** + * Get oumultiresponse question form data for a question with two correct answers out of four. + * * @return stdClass date to create an oumultiresponse question. */ - public function get_oumultiresponse_question_form_data_two_of_four() { + public function get_oumultiresponse_question_form_data_two_of_four(): stdClass { $fromform = new stdClass(); $fromform->name = 'OU multiple response question'; - $fromform->questiontext = array('text' => 'Which are the odd numbers?', 'format' => FORMAT_HTML); + $fromform->questiontext = ['text' => 'Which are the odd numbers?', 'format' => FORMAT_HTML]; $fromform->defaultmark = 1.0; - $fromform->generalfeedback = array('text' => 'The odd numbers are One and Three.', 'format' => FORMAT_HTML); + $fromform->generalfeedback = ['text' => 'The odd numbers are One and Three.', 'format' => FORMAT_HTML]; $fromform->shuffleanswers = 0; $fromform->answernumbering = 'abc'; $fromform->showstandardinstruction = 0; - $fromform->answer = array( - 0 => array('text' => 'One', 'format' => FORMAT_PLAIN), - 1 => array('text' => 'Two', 'format' => FORMAT_PLAIN), - 2 => array('text' => 'Three', 'format' => FORMAT_PLAIN), - 3 => array('text' => 'Four', 'format' => FORMAT_PLAIN) - ); - $fromform->correctanswer = array( - 0 => 1, - 1 => 0, - 2 => 1, - 3 => 0 - ); - $fromform->feedback = array( - 0 => array('text' => 'One is odd.', 'format' => FORMAT_HTML), - 1 => array('text' => 'Two is even.', 'format' => FORMAT_HTML), - 2 => array('text' => 'Three is odd.', 'format' => FORMAT_HTML), - 3 => array('text' => 'Four is odd.', 'format' => FORMAT_HTML) - ); + $fromform->answer = [ + 0 => ['text' => 'One', 'format' => FORMAT_PLAIN], + 1 => ['text' => 'Two', 'format' => FORMAT_PLAIN], + 2 => ['text' => 'Three', 'format' => FORMAT_PLAIN], + 3 => ['text' => 'Four', 'format' => FORMAT_PLAIN], + ]; + $fromform->correctanswer = [ + 0 => 1, + 1 => 0, + 2 => 1, + 3 => 0, + ]; + $fromform->feedback = [ + 0 => ['text' => 'One is odd.', 'format' => FORMAT_HTML], + 1 => ['text' => 'Two is even.', 'format' => FORMAT_HTML], + 2 => ['text' => 'Three is odd.', 'format' => FORMAT_HTML], + 3 => ['text' => 'Four is odd.', 'format' => FORMAT_HTML], + ]; test_question_maker::set_standard_combined_feedback_form_data($fromform); $fromform->shownumcorrect = 0; $fromform->penalty = 0.3333333; diff --git a/tests/privacy_provider_test.php b/tests/privacy_provider_test.php index 55d8770..6790fb3 100644 --- a/tests/privacy_provider_test.php +++ b/tests/privacy_provider_test.php @@ -24,7 +24,7 @@ namespace qtype_oumultiresponse; use core_privacy\local\metadata\collection; -use \core_privacy\local\request\user_preference_provider; +use core_privacy\local\request\user_preference_provider; use qtype_oumultiresponse\privacy\provider; use core_privacy\local\request\writer; use core_privacy\local\request\transform; @@ -40,17 +40,18 @@ * @package qtype_oumultiresponse * @copyright 2021 The Open university * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_oumultiresponse\privacy\provider */ -class privacy_provider_test extends \core_privacy\tests\provider_testcase { +final class privacy_provider_test extends \core_privacy\tests\provider_testcase { // Include the privacy helper which has assertions on it. - public function test_get_metadata() { + public function test_get_metadata(): void { $collection = new \core_privacy\local\metadata\collection('qtype_oumultiresponse'); $actual = \qtype_oumultiresponse\privacy\provider::get_metadata($collection); $this->assertEquals($collection, $actual); } - public function test_export_user_preferences_no_pref() { + public function test_export_user_preferences_no_pref(): void { $this->resetAfterTest(); $user = $this->getDataGenerator()->create_user(); @@ -67,7 +68,7 @@ public function test_export_user_preferences_no_pref() { * @param string $value The value stored in the database * @param string $expected The expected transformed value */ - public function test_export_user_preferences($name, $value, $expected) { + public function test_export_user_preferences($name, $value, $expected): void { $this->resetAfterTest(); $user = $this->getDataGenerator()->create_user(); set_user_preference("qtype_oumultiresponse_$name", $value, $user); @@ -91,7 +92,7 @@ public function test_export_user_preferences($name, $value, $expected) { * * @return array Array of valid user preferences. */ - public function user_preference_provider() { + public static function user_preference_provider(): array { return [ 'default mark 2' => ['defaultmark', 2, 2], 'penalty 33.33333%' => ['penalty', 0.3333333, '33.33333%'], @@ -103,7 +104,7 @@ public function user_preference_provider() { 'answernumbering iii' => ['answernumbering', 'iii', 'i., ii., iii., ...'], 'answernumbering III' => ['answernumbering', 'IIII', 'I., II., III., ...'], 'show standard instruction yes' => ['showstandardinstruction', 1, 'Yes'], - 'show standard instruction no' => ['showstandardinstruction', 0, 'No'] + 'show standard instruction no' => ['showstandardinstruction', 0, 'No'], ]; } } diff --git a/tests/question_test.php b/tests/question_test.php index f6733b5..3340068 100644 --- a/tests/question_test.php +++ b/tests/question_test.php @@ -41,207 +41,215 @@ * * @copyright 2008 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers ::replace_char_at + * @covers ::grade_response + * @covers ::grade_computation */ +final class question_test extends \basic_testcase { -class question_test extends \basic_testcase { + /** + * Tolerance for floating point comparisons. + * + * @var float + */ private $tolerance = 0.000001; - public function test_replace_char_at() { + public function test_replace_char_at(): void { $this->assertEquals(qtype_oumultiresponse_question::replace_char_at('220', 0, '0'), '020'); } - public function test_grade_responses_right_right() { + public function test_grade_responses_right_right(): void { $mc = test_question_maker::make_question('oumultiresponse', 'two_of_four'); $mc->shuffleanswers = false; $mc->start_attempt(new question_attempt_step(), 1); - list($fraction, $state) = $mc->grade_response(array('choice0' => '1', 'choice2' => '1')); + list($fraction, $state) = $mc->grade_response(['choice0' => '1', 'choice2' => '1']); $this->assertEquals(1, $fraction, '', $this->tolerance); $this->assertEquals($state, question_state::$gradedright); } - public function test_grade_responses_right() { + public function test_grade_responses_right(): void { $mc = test_question_maker::make_question('oumultiresponse', 'two_of_four'); $mc->shuffleanswers = false; $mc->start_attempt(new question_attempt_step(), 1); - list($fraction, $state) = $mc->grade_response(array('choice0' => '1')); + list($fraction, $state) = $mc->grade_response(['choice0' => '1']); $this->assertEquals(0.5, $fraction, '', $this->tolerance); $this->assertEquals($state, question_state::$gradedpartial); } - public function test_grade_responses_wrong_wrong() { + public function test_grade_responses_wrong_wrong(): void { $mc = test_question_maker::make_question('oumultiresponse', 'two_of_four'); $mc->shuffleanswers = false; $mc->start_attempt(new question_attempt_step(), 1); - list($fraction, $state) = $mc->grade_response(array('choice1' => '1', 'choice3' => '1')); + list($fraction, $state) = $mc->grade_response(['choice1' => '1', 'choice3' => '1']); $this->assertEquals(0, $fraction, '', $this->tolerance); $this->assertEquals($state, question_state::$gradedwrong); } - public function test_grade_responses_right_wrong_wrong() { + public function test_grade_responses_right_wrong_wrong(): void { $mc = test_question_maker::make_question('oumultiresponse', 'two_of_four'); $mc->shuffleanswers = false; $mc->start_attempt(new question_attempt_step(), 1); list($fraction, $state) = $mc->grade_response( - array('choice0' => '1', 'choice1' => '1', 'choice3' => '1')); + ['choice0' => '1', 'choice1' => '1', 'choice3' => '1']); $this->assertEquals(0, $fraction, '', $this->tolerance); $this->assertEquals($state, question_state::$gradedpartial); } - public function test_grade_responses_right_wrong() { + public function test_grade_responses_right_wrong(): void { $mc = test_question_maker::make_question('oumultiresponse', 'two_of_four'); $mc->shuffleanswers = false; $mc->start_attempt(new question_attempt_step(), 1); - list($fraction, $state) = $mc->grade_response(array('choice0' => '1', 'choice1' => '1')); + list($fraction, $state) = $mc->grade_response(['choice0' => '1', 'choice1' => '1']); $this->assertEquals(0.5, $fraction, '', $this->tolerance); $this->assertEquals($state, question_state::$gradedpartial); } - public function test_grade_responses_right_right_wrong() { + public function test_grade_responses_right_right_wrong(): void { $mc = test_question_maker::make_question('oumultiresponse', 'two_of_four'); $mc->shuffleanswers = false; $mc->start_attempt(new question_attempt_step(), 1); - list($fraction, $state) = $mc->grade_response(array( - 'choice0' => '1', 'choice2' => '1', 'choice3' => '1')); + list($fraction, $state) = $mc->grade_response([ + 'choice0' => '1', 'choice2' => '1', 'choice3' => '1']); $this->assertEquals(0.5, $fraction, '', $this->tolerance); $this->assertEquals($state, question_state::$gradedpartial); } - public function test_grade_responses_right_right_wrong_wrong() { + public function test_grade_responses_right_right_wrong_wrong(): void { $mc = test_question_maker::make_question('oumultiresponse', 'two_of_four'); $mc->shuffleanswers = false; $mc->start_attempt(new question_attempt_step(), 1); - list($fraction, $state) = $mc->grade_response(array( - 'choice0' => '1', 'choice1' => '1', 'choice2' => '1', 'choice3' => '1')); + list($fraction, $state) = $mc->grade_response([ + 'choice0' => '1', 'choice1' => '1', 'choice2' => '1', 'choice3' => '1']); $this->assertEquals(0, $fraction, '', $this->tolerance); $this->assertEquals($state, question_state::$gradedpartial); } - public function test_grade_computation() { + public function test_grade_computation(): void { $right = new \stdClass(); $right->fraction = 1.0; $wrong = new \stdClass(); $wrong->fraction = 0.0; $penalty = 0.3333333; - $answers = array($right, $right, $right, $wrong, $wrong, $wrong); + $answers = [$right, $right, $right, $wrong, $wrong, $wrong]; - $responsehistory = array('111', '000', '000', '000', '000', '000'); + $responsehistory = ['111', '000', '000', '000', '000', '000']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.3333333, $this->tolerance, ''); - $responsehistory = array('111', '111', '000', '000', '000', '000'); + $responsehistory = ['111', '111', '000', '000', '000', '000']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.6666667, $this->tolerance, ''); - $responsehistory = array('1', '1', '1', '0', '0', '0'); + $responsehistory = ['1', '1', '1', '0', '0', '0']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 1.0, $this->tolerance, ''); - $responsehistory = array('111', '111', '111', '111', '000', '000'); + $responsehistory = ['111', '111', '111', '111', '000', '000']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.6666667, $this->tolerance, ''); - $responsehistory = array('111', '111', '111', '111', '111', '000'); + $responsehistory = ['111', '111', '111', '111', '111', '000']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.3333333, $this->tolerance, ''); - $responsehistory = array('111', '111', '111', '111', '111', '111'); + $responsehistory = ['111', '111', '111', '111', '111', '111']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.0, $this->tolerance, ''); - $responsehistory = array('011', '000', '000', '100', '111', '111'); + $responsehistory = ['011', '000', '000', '100', '111', '111']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.2222222, $this->tolerance, ''); - $responsehistory = array('001', '000', '000', '110', '111', '111'); + $responsehistory = ['001', '000', '000', '110', '111', '111']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.1111111, $this->tolerance, ''); - $responsehistory = array('111', '111', '001', '100', '010', '000'); + $responsehistory = ['111', '111', '001', '100', '010', '000']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.7777778, $this->tolerance, ''); - $responsehistory = array('100', '100', '001', '100', '011', '001'); + $responsehistory = ['100', '100', '001', '100', '011', '001']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.1111111, $this->tolerance, ''); - $responsehistory = array('101', '101', '001', '110', '011', '111'); + $responsehistory = ['101', '101', '001', '110', '011', '111']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.1111111, $this->tolerance, ''); - $responsehistory = array('011', '001', '001', '100', '110', '111'); + $responsehistory = ['011', '001', '001', '100', '110', '111']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.3333333, $this->tolerance, ''); - $responsehistory = array('111', '111', '111', '110', '110', '100'); + $responsehistory = ['111', '111', '111', '110', '110', '100']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.4444444, $this->tolerance, ''); - $responsehistory = array('111', '111', '111', '110', '100', '100'); + $responsehistory = ['111', '111', '111', '110', '100', '100']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.5555556, $this->tolerance, ''); - $responsehistory = array('110', '101', '101', '111', '110', '100'); + $responsehistory = ['110', '101', '101', '111', '110', '100']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.2222222, $this->tolerance, ''); - $responsehistory = array('111', '110', '110', '111', '111', '100'); + $responsehistory = ['111', '110', '110', '111', '111', '100']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.2222222, $this->tolerance, ''); - $responsehistory = array('011', '111', '110', '111', '111', '100'); + $responsehistory = ['011', '111', '110', '111', '111', '100']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.2222222, $this->tolerance, ''); - $responsehistory = array('110', '111', '110', '111', '111', '100'); + $responsehistory = ['110', '111', '110', '111', '111', '100']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.2222222, $this->tolerance, ''); - $responsehistory = array('111', '111', '111', '110', '110', '100'); + $responsehistory = ['111', '111', '111', '110', '110', '100']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.4444444, $this->tolerance, ''); - $responsehistory = array('110', '111', '110', '111', '111', '100'); + $responsehistory = ['110', '111', '110', '111', '111', '100']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.2222222, $this->tolerance, ''); - $responsehistory = array('011', '111', '110', '111', '111', '100'); + $responsehistory = ['011', '111', '110', '111', '111', '100']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.2222222, $this->tolerance, ''); - $responsehistory = array('011', '111', '110', '110', '111', '001'); + $responsehistory = ['011', '111', '110', '110', '111', '001']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.3333333, $this->tolerance, ''); - $responsehistory = array('11', '01', '01', '10', '10', '00'); + $responsehistory = ['11', '01', '01', '10', '10', '00']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 3), 0.7777778, $this->tolerance, ''); $penalty = 0.2; - $answers = array($right, $right, $right, $right, $wrong, $wrong, $wrong, $wrong); - $responsehistory = array( - '11111', '10111', '11100', '11011', '10011', '01010', '01000', '00100'); + $answers = [$right, $right, $right, $right, $wrong, $wrong, $wrong, $wrong]; + $responsehistory = [ + '11111', '10111', '11100', '11011', '10011', '01010', '01000', '00100']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 5), 0.45, $this->tolerance, ''); $penalty = 0.33334; - $answers = array($right, $right, $wrong, $wrong, $wrong); - $responsehistory = array('0', '0', '1', '1', '0'); + $answers = [$right, $right, $wrong, $wrong, $wrong]; + $responsehistory = ['0', '0', '1', '1', '0']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 1), 0.0, $this->tolerance, ''); - $responsehistory = array('0', '1', '1', '0', '0'); + $responsehistory = ['0', '1', '1', '0', '0']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 1), 0.5, $this->tolerance, ''); - $responsehistory = array('1', '1', '0', '0', '0'); + $responsehistory = ['1', '1', '0', '0', '0']; $this->assertEqualsWithDelta(qtype_oumultiresponse_question::grade_computation( $responsehistory, $answers, $penalty, 1), 1.0, $this->tolerance, ''); } diff --git a/tests/questiontype_test.php b/tests/questiontype_test.php index ae03f0c..cc8c7a3 100644 --- a/tests/questiontype_test.php +++ b/tests/questiontype_test.php @@ -42,27 +42,30 @@ * * @copyright 2008 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_oumultiresponse */ -class questiontype_test extends \question_testcase { +final class questiontype_test extends \question_testcase { /** * @var qtype_oumultiresponse */ private $qtype; public function setUp(): void { + parent::setUp(); $this->qtype = new qtype_oumultiresponse(); } - public function assert_same_xml($expectedxml, $xml) { + #[\Override] + public function assert_same_xml($expectedxml, $xml): void { $this->assertEquals(str_replace("\r\n", "\n", $expectedxml), str_replace("\r\n", "\n", $xml)); } - public function test_name() { + public function test_name(): void { $this->assertEquals($this->qtype->name(), 'oumultiresponse'); } - public function test_initialise_question_instance() { + public function test_initialise_question_instance(): void { $qdata = test_question_maker::get_question_data('oumultiresponse', 'two_of_four'); $expectedq = test_question_maker::make_question('oumultiresponse', 'two_of_four'); $qdata->stamp = $expectedq->stamp; @@ -75,36 +78,36 @@ public function test_initialise_question_instance() { $this->assertEquals($expectedq, $question); } - public function test_can_analyse_responses() { + public function test_can_analyse_responses(): void { $this->assertTrue($this->qtype->can_analyse_responses()); } - public function test_get_possible_responses() { + public function test_get_possible_responses(): void { $q = new \stdClass(); $q->id = 1; $q->options = new \stdClass(); $q->options->answers = [ - 1 => (object) array('answer' => 'frog', 'fraction' => 1), - 2 => (object) array('answer' => 'toad', 'fraction' => 1), - 3 => (object) array('answer' => 'newt', 'fraction' => 0), + 1 => (object) ['answer' => 'frog', 'fraction' => 1], + 2 => (object) ['answer' => 'toad', 'fraction' => 1], + 3 => (object) ['answer' => 'newt', 'fraction' => 0], ]; $responses = $this->qtype->get_possible_responses($q); - $this->assertEquals(array( - 1 => array(1 => new question_possible_response('frog', 0.5)), - 2 => array(2 => new question_possible_response('toad', 0.5)), - 3 => array(3 => new question_possible_response('newt', 0)), - ), $this->qtype->get_possible_responses($q)); + $this->assertEquals([ + 1 => [1 => new question_possible_response('frog', 0.5)], + 2 => [2 => new question_possible_response('toad', 0.5)], + 3 => [3 => new question_possible_response('newt', 0)], + ], $this->qtype->get_possible_responses($q)); } - public function test_get_random_guess_score() { + public function test_get_random_guess_score(): void { $questiondata = new \stdClass(); $questiondata->options = new \stdClass(); - $questiondata->options->answers = array( + $questiondata->options->answers = [ 1 => new question_answer(1, 'A', 1, '', FORMAT_HTML), 2 => new question_answer(2, 'B', 0, '', FORMAT_HTML), 3 => new question_answer(3, 'C', 0, '', FORMAT_HTML), - ); + ]; $this->assertEquals(1 / 3, $this->qtype->get_random_guess_score($questiondata), '', 0.000001); @@ -117,7 +120,7 @@ public function test_get_random_guess_score() { $this->qtype->get_random_guess_score($questiondata), '', 0.000001); } - public function test_xml_import() { + public function test_xml_import(): void { $xml = ' OU multiple response question @@ -196,44 +199,51 @@ public function test_xml_import() { $expectedq->penalty = 0.3333333; $expectedq->shuffleanswers = 1; - $expectedq->correctfeedback = array('text' => 'Well done.', - 'format' => FORMAT_HTML); - $expectedq->partiallycorrectfeedback = array('text' => 'Not entirely.', - 'format' => FORMAT_HTML); + $expectedq->correctfeedback = [ + 'text' => 'Well done.', + 'format' => FORMAT_HTML, + ]; + $expectedq->partiallycorrectfeedback = [ + 'text' => 'Not entirely.', + 'format' => FORMAT_HTML, + ]; $expectedq->shownumcorrect = false; - $expectedq->incorrectfeedback = array('text' => 'Completely wrong!', - 'format' => FORMAT_HTML); - - $expectedq->answer = array( - array('text' => 'One', 'format' => FORMAT_HTML), - array('text' => 'Two', 'format' => FORMAT_HTML), - array('text' => 'Three', 'format' => FORMAT_HTML), - array('text' => 'Four', 'format' => FORMAT_HTML), - ); - $expectedq->correctanswer = array(1, 0, 1, 0); - $expectedq->feedback = array( - array('text' => 'Specific feedback to correct answer.', - 'format' => FORMAT_HTML), - array('text' => 'Specific feedback to wrong answer.', - 'format' => FORMAT_HTML), - array('text' => 'Specific feedback to correct answer.', - 'format' => FORMAT_HTML), - array('text' => 'Specific feedback to wrong answer.', - 'format' => FORMAT_HTML), - ); - - $expectedq->hint = array( - array('text' => 'Try again.', 'format' => FORMAT_HTML), - array('text' => 'Hint 2.', 'format' => FORMAT_HTML)); - $expectedq->hintshownumcorrect = array(true, true); - $expectedq->hintclearwrong = array(false, true); - $expectedq->hintshowchoicefeedback = array(false, true); + $expectedq->incorrectfeedback = [ + 'text' => 'Completely wrong!', + 'format' => FORMAT_HTML, + ]; + + $expectedq->answer = [ + ['text' => 'One', 'format' => FORMAT_HTML], + ['text' => 'Two', 'format' => FORMAT_HTML], + ['text' => 'Three', 'format' => FORMAT_HTML], + ['text' => 'Four', 'format' => FORMAT_HTML], + ]; + $expectedq->correctanswer = [1, 0, 1, 0]; + $expectedq->feedback = [ + ['text' => 'Specific feedback to correct answer.', + 'format' => FORMAT_HTML], + ['text' => 'Specific feedback to wrong answer.', + 'format' => FORMAT_HTML], + ['text' => 'Specific feedback to correct answer.', + 'format' => FORMAT_HTML], + ['text' => 'Specific feedback to wrong answer.', + 'format' => FORMAT_HTML], + ]; + + $expectedq->hint = [ + ['text' => 'Try again.', 'format' => FORMAT_HTML], + ['text' => 'Hint 2.', 'format' => FORMAT_HTML], + ]; + $expectedq->hintshownumcorrect = [true, true]; + $expectedq->hintclearwrong = [false, true]; + $expectedq->hintshowchoicefeedback = [false, true]; $this->assert(new question_check_specified_fields_expectation($expectedq), $q); $this->assertEquals($expectedq->answer, $q->answer); } - public function test_xml_import_legacy() { + public function test_xml_import_legacy(): void { $xml = ' 008 OUMR feedback test @@ -328,44 +338,51 @@ public function test_xml_import_legacy() { $expectedq->shuffleanswers = 1; $expectedq->answernumbering = 'abc'; - $expectedq->correctfeedback = array('text' => 'Correct overall feedback', - 'format' => FORMAT_HTML); - $expectedq->partiallycorrectfeedback = array( - 'text' => 'Partially correct overall feedback.', - 'format' => FORMAT_HTML); + $expectedq->correctfeedback = [ + 'text' => 'Correct overall feedback', + 'format' => FORMAT_HTML, + ]; + $expectedq->partiallycorrectfeedback = [ + 'text' => 'Partially correct overall feedback.', + 'format' => FORMAT_HTML, + ]; $expectedq->shownumcorrect = false; - $expectedq->incorrectfeedback = array('text' => 'Incorrect overall feedback.', - 'format' => FORMAT_HTML); - - $expectedq->answer = array( - array('text' => 'eighta', 'format' => FORMAT_HTML), - array('text' => 'eightb', 'format' => FORMAT_HTML), - array('text' => 'one', 'format' => FORMAT_HTML), - array('text' => 'two', 'format' => FORMAT_HTML)); - $expectedq->correctanswer = array(1, 1, 0, 0); - $expectedq->feedback = array( - array('text' => '

Specific feedback to correct answer.

', - 'format' => FORMAT_HTML), - array('text' => '

Specific feedback to correct answer.

', - 'format' => FORMAT_HTML), - array('text' => '

Specific feedback to wrong answer.

', - 'format' => FORMAT_HTML), - array('text' => '

Specific feedback to wrong answer.

', - 'format' => FORMAT_HTML), - ); - - $expectedq->hint = array( - array('text' => 'Hint 1.', 'format' => FORMAT_HTML), - array('text' => 'Hint 2.', 'format' => FORMAT_HTML)); - $expectedq->hintshownumcorrect = array(false, false); - $expectedq->hintclearwrong = array(false, false); - $expectedq->hintshowchoicefeedback = array(true, true); + $expectedq->incorrectfeedback = [ + 'text' => 'Incorrect overall feedback.', + 'format' => FORMAT_HTML, + ]; + + $expectedq->answer = [ + ['text' => 'eighta', 'format' => FORMAT_HTML], + ['text' => 'eightb', 'format' => FORMAT_HTML], + ['text' => 'one', 'format' => FORMAT_HTML], + ['text' => 'two', 'format' => FORMAT_HTML], + ]; + $expectedq->correctanswer = [1, 1, 0, 0]; + $expectedq->feedback = [ + ['text' => '

Specific feedback to correct answer.

', + 'format' => FORMAT_HTML], + ['text' => '

Specific feedback to correct answer.

', + 'format' => FORMAT_HTML], + ['text' => '

Specific feedback to wrong answer.

', + 'format' => FORMAT_HTML], + ['text' => '

Specific feedback to wrong answer.

', + 'format' => FORMAT_HTML], + ]; + + $expectedq->hint = [ + ['text' => 'Hint 1.', 'format' => FORMAT_HTML], + ['text' => 'Hint 2.', 'format' => FORMAT_HTML], + ]; + $expectedq->hintshownumcorrect = [false, false]; + $expectedq->hintclearwrong = [false, false]; + $expectedq->hintshowchoicefeedback = [true, true]; $this->assertEquals($expectedq->answer, $q->answer); $this->assert(new question_check_specified_fields_expectation($expectedq), $q); } - public function test_xml_export() { + public function test_xml_export(): void { $qdata = test_question_maker::get_question_data('oumultiresponse', 'two_of_four'); $qdata->defaultmark = 6; diff --git a/tests/walkthrough_test.php b/tests/walkthrough_test.php index 371ba10..b368d27 100644 --- a/tests/walkthrough_test.php +++ b/tests/walkthrough_test.php @@ -44,10 +44,11 @@ * * @copyright 2010 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qtype_oumultiresponse_question */ -class walkthrough_test extends \qbehaviour_walkthrough_test_base { +final class walkthrough_test extends \qbehaviour_walkthrough_test_base { - public function test_shows_standrd_instruction_yes() { + public function test_shows_standrd_instruction_yes(): void { // Create a multichoice single question. $mc = test_question_maker::make_question('oumultiresponse', 'two_of_four'); @@ -61,7 +62,7 @@ public function test_shows_standrd_instruction_yes() { $this->assertStringContainsString($standardinstruction, $this->currentoutput); } - public function test_shows_standrd_instruction_no() { + public function test_shows_standrd_instruction_no(): void { // Create a multichoice single question. $mc = test_question_maker::make_question('oumultiresponse', 'two_of_four'); @@ -70,14 +71,18 @@ public function test_shows_standrd_instruction_no() { $this->start_attempt_at_question($mc, 'interactive', 3); $this->render(); + $standardinstructionclass = 'prompt h6 fw-normal visually-hidden'; + if (utils::moodle_version_is("<=" , "45")) { + $standardinstructionclass = 'prompt h6 font-weight-normal sr-only'; + } // Check for 'Show standard instruction'. $standardinstruction = \html_writer::tag('legend', get_string('answer'), [ - 'class' => 'prompt h6 font-weight-normal sr-only' + 'class' => $standardinstructionclass, ]); $this->assertStringContainsString($standardinstruction, $this->currentoutput); } - public function test_interactive_behaviour() { + public function test_interactive_behaviour(): void { // Create a multichoice single question. $mc = test_question_maker::make_question('oumultiresponse', 'two_of_four'); @@ -101,7 +106,7 @@ public function test_interactive_behaviour() { $this->get_no_hint_visible_expectation()); // Save the wrong answer. - $this->process_submission(array('choice1' => '1', 'choice3' => '1')); + $this->process_submission(['choice1' => '1', 'choice3' => '1']); // Verify. $this->check_current_state(question_state::$todo); @@ -119,7 +124,7 @@ public function test_interactive_behaviour() { $this->get_no_hint_visible_expectation()); // Submit the wrong answer. - $this->process_submission(array('choice1' => '1', 'choice3' => '1', '-submit' => '1')); + $this->process_submission(['choice1' => '1', 'choice3' => '1', '-submit' => '1']); // Verify. $this->check_current_state(question_state::$todo); @@ -144,7 +149,7 @@ public function test_interactive_behaviour() { $this->quba->get_field_prefix($this->slot) . 'choice3')); // Do try again. - $this->process_submission(array('-tryagain' => 1)); + $this->process_submission(['-tryagain' => 1]); // Verify. $this->check_current_state(question_state::$todo); @@ -162,7 +167,7 @@ public function test_interactive_behaviour() { $this->get_no_hint_visible_expectation()); // Submit a partially right answer. - $this->process_submission(array('choice0' => '1', 'choice3' => '1', '-submit' => '1')); + $this->process_submission(['choice0' => '1', 'choice3' => '1', '-submit' => '1']); // Verify. $this->check_current_state(question_state::$todo); @@ -187,7 +192,7 @@ public function test_interactive_behaviour() { $this->quba->get_field_prefix($this->slot) . 'choice3', '0')); // Do try again. - $this->process_submission(array('choice0' => '1', '-tryagain' => 1)); + $this->process_submission(['choice0' => '1', '-tryagain' => 1]); // Verify. $this->check_current_state(question_state::$todo); @@ -205,7 +210,7 @@ public function test_interactive_behaviour() { $this->get_no_hint_visible_expectation()); // Submit the right answer. - $this->process_submission(array('choice0' => '1', 'choice2' => '1', '-submit' => '1')); + $this->process_submission(['choice0' => '1', 'choice2' => '1', '-submit' => '1']); // Verify. $this->check_current_state(question_state::$gradedright); @@ -220,15 +225,15 @@ public function test_interactive_behaviour() { $this->get_contains_standard_correct_combined_feedback_expectation()); } - public function test_interactive_behaviour2() { + public function test_interactive_behaviour2(): void { // Create a multichoice single question. $mc = test_question_maker::make_question('oumultiresponse', 'two_of_four'); $mc->showstandardinstruction = 1; - $mc->hints = array( + $mc->hints = [ new qtype_oumultiresponse_hint(1, 'Hint 1', FORMAT_HTML, true, true, true), new qtype_oumultiresponse_hint(2, 'Hint 2', FORMAT_HTML, true, true, true), - ); + ]; $mc->shuffleanswers = false; $mc->showstandardinstruction = true; $this->start_attempt_at_question($mc, 'interactive', 3); @@ -251,8 +256,8 @@ public function test_interactive_behaviour2() { preg_quote(get_string('selectmulti', 'qtype_multichoice'), '/') . '/')); // Submit the wrong answer with too manu options selected. - $this->process_submission(array( - 'choice1' => '1', 'choice2' => '1', 'choice3' => '1', '-submit' => '1')); + $this->process_submission([ + 'choice1' => '1', 'choice2' => '1', 'choice3' => '1', '-submit' => '1']); // Verify. $this->check_current_state(question_state::$todo); @@ -281,14 +286,14 @@ public function test_interactive_behaviour2() { preg_quote(get_string('selectmulti', 'qtype_multichoice'), '/') . '/')); } - public function test_interactive_clear_wrong() { + public function test_interactive_clear_wrong(): void { // Create a multichoice single question. $mc = test_question_maker::make_question('oumultiresponse', 'two_of_four'); - $mc->hints = array( + $mc->hints = [ new qtype_oumultiresponse_hint(1, 'Hint 1', FORMAT_HTML, true, true, true), new qtype_oumultiresponse_hint(2, 'Hint 2', FORMAT_HTML, true, true, true), - ); + ]; $mc->shuffleanswers = false; $mc->showstandardinstruction = true; $this->start_attempt_at_question($mc, 'interactive', 3); @@ -310,7 +315,7 @@ public function test_interactive_clear_wrong() { $this->get_no_hint_visible_expectation()); // Submit a wrong answer. - $this->process_submission(array('choice1' => '1', 'choice3' => '1', '-submit' => '1')); + $this->process_submission(['choice1' => '1', 'choice3' => '1', '-submit' => '1']); // Verify. $this->check_current_state(question_state::$todo); @@ -332,7 +337,7 @@ public function test_interactive_clear_wrong() { $this->quba->get_field_prefix($this->slot) . 'choice3', '0')); // Try again. - $this->process_submission(array('choice1' => '0', 'choice3' => '0', '-tryagain' => '1')); + $this->process_submission(['choice1' => '0', 'choice3' => '0', '-tryagain' => '1']); // Vreify. $this->check_current_state(question_state::$todo); @@ -351,7 +356,7 @@ public function test_interactive_clear_wrong() { $this->get_no_hint_visible_expectation()); // Submit a partially right answer. - $this->process_submission(array('choice0' => '1', 'choice3' => '1', '-submit' => '1')); + $this->process_submission(['choice0' => '1', 'choice3' => '1', '-submit' => '1']); // Verify. $this->check_current_state(question_state::$todo); @@ -373,7 +378,7 @@ public function test_interactive_clear_wrong() { $this->quba->get_field_prefix($this->slot) . 'choice3', '0')); // Try again. - $this->process_submission(array('choice0' => '1', 'choice3' => '0', '-tryagain' => '1')); + $this->process_submission(['choice0' => '1', 'choice3' => '0', '-tryagain' => '1']); // Check the initial state. $this->check_current_state(question_state::$todo); @@ -392,7 +397,7 @@ public function test_interactive_clear_wrong() { $this->get_no_hint_visible_expectation()); } - public function test_interactive_bug_11263() { + public function test_interactive_bug_11263(): void { // Create a multichoice single question. $mc = test_question_maker::make_question('oumultiresponse', 'two_of_five'); @@ -406,28 +411,28 @@ public function test_interactive_bug_11263() { $this->get_tries_remaining_expectation(3)); // Submit a wrong answer. - $this->process_submission(array( + $this->process_submission([ 'choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '1', 'choice4' => '1', - '-submit' => '1' - )); + '-submit' => '1', + ]); // Verify. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); // Try again. - $this->process_submission(array( + $this->process_submission([ 'choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '1', 'choice4' => '1', - '-tryagain' => '1' - )); + '-tryagain' => '1', + ]); // Verify. $this->check_current_state(question_state::$todo); @@ -436,28 +441,28 @@ public function test_interactive_bug_11263() { $this->get_tries_remaining_expectation(2)); // Submit a wrong answer again. - $this->process_submission(array( + $this->process_submission([ 'choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '1', 'choice4' => '1', - '-submit' => '1' - )); + '-submit' => '1', + ]); // Verify. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); // Try again - clears wrong. - $this->process_submission(array( + $this->process_submission([ 'choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0', 'choice4' => '0', - '-tryagain' => '1' - )); + '-tryagain' => '1', + ]); // Verify. $this->check_current_state(question_state::$todo); @@ -466,21 +471,21 @@ public function test_interactive_bug_11263() { $this->get_tries_remaining_expectation(1)); // Submit one right choice. - $this->process_submission(array( + $this->process_submission([ 'choice0' => '1', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0', 'choice4' => '0', - '-submit' => '1' - )); + '-submit' => '1', + ]); // Verify. $this->check_current_state(question_state::$gradedpartial); $this->check_current_mark(0); } - public function test_interactive_regrade_changing_num_tries_leaving_open() { + public function test_interactive_regrade_changing_num_tries_leaving_open(): void { // Create a multichoice multiple question. $q = test_question_maker::make_question('oumultiresponse', 'two_of_five'); $this->start_attempt_at_question($q, 'interactive', 3); @@ -492,14 +497,14 @@ public function test_interactive_regrade_changing_num_tries_leaving_open() { $this->get_tries_remaining_expectation(3)); // Submit the right answer. - $this->process_submission(array( + $this->process_submission([ 'choice0' => '1', 'choice1' => '1', 'choice2' => '0', 'choice3' => '0', 'choice4' => '0', - '-submit' => '1' - )); + '-submit' => '1', + ]); // Verify. $this->check_current_state(question_state::$gradedright); @@ -515,7 +520,7 @@ public function test_interactive_regrade_changing_num_tries_leaving_open() { $this->check_current_mark(null); } - public function test_interactive_regrade_changing_num_tries_finished() { + public function test_interactive_regrade_changing_num_tries_finished(): void { // Create a multichoice multiple question. $q = test_question_maker::make_question('oumultiresponse', 'two_of_five'); $this->start_attempt_at_question($q, 'interactive', 3); @@ -527,14 +532,14 @@ public function test_interactive_regrade_changing_num_tries_finished() { $this->get_tries_remaining_expectation(3)); // Submit the right answer. - $this->process_submission(array( + $this->process_submission([ 'choice0' => '1', 'choice1' => '1', 'choice2' => '0', 'choice3' => '0', 'choice4' => '0', - '-submit' => '1' - )); + '-submit' => '1', + ]); // Verify. $this->check_current_state(question_state::$gradedright); @@ -550,6 +555,7 @@ public function test_interactive_regrade_changing_num_tries_finished() { $this->check_current_mark(2); } + #[\Override] protected function get_contains_num_parts_correct($num) { $a = new \stdClass(); if ($num == 1) { diff --git a/version.php b/version.php index 021d7f7..d4133ee 100644 --- a/version.php +++ b/version.php @@ -29,10 +29,10 @@ $plugin->requires = 2021051700; $plugin->component = 'qtype_oumultiresponse'; $plugin->maturity = MATURITY_STABLE; -$plugin->release = '2.4 for Moodle 3.11+'; +$plugin->release = '2.5 for Moodle 5.0+'; -$plugin->dependencies = array( +$plugin->dependencies = [ 'qtype_multichoice' => 2020061500, -); +]; $plugin->outestssufficient = true;