diff --git a/lang/en/question.php b/lang/en/question.php index 1d82d2323e2..48a68b7a39a 100644 --- a/lang/en/question.php +++ b/lang/en/question.php @@ -99,6 +99,7 @@ $string['deletequestioncheck'] = 'Are you absolutely sure you want to delete \'{ $string['deletequestionscheck'] = 'Are you absolutely sure you want to delete the following questions?

{$a}'; $string['deletingbehaviour'] = 'Deleting question behaviour \'{$a}\''; $string['deletingqtype'] = 'Deleting question type \'{$a}\''; +$string['didnotmatchanyanswer'] = '[Did not match any answer]'; $string['disabled'] = 'Disabled'; $string['disterror'] = 'The distribution {$a} caused problems'; $string['donothing'] = 'Don\'t copy or move files or change links.'; diff --git a/question/behaviour/adaptive/simpletest/testwalkthrough.php b/question/behaviour/adaptive/simpletest/testwalkthrough.php index 5d5a49806b4..273507abf79 100644 --- a/question/behaviour/adaptive/simpletest/testwalkthrough.php +++ b/question/behaviour/adaptive/simpletest/testwalkthrough.php @@ -238,7 +238,7 @@ class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_b public function test_adaptive_shortanswer_partially_right() { // Create a short answer question - $sa = test_question_maker::make_a_shortanswer_question(); + $sa = test_question_maker::make_question('shortanswer'); $this->start_attempt_at_question($sa, 'adaptive'); // Check the initial state. @@ -307,7 +307,7 @@ class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_b public function test_adaptive_shortanswer_wrong_right_wrong() { // Create a short answer question - $sa = test_question_maker::make_a_shortanswer_question(); + $sa = test_question_maker::make_question('shortanswer'); $this->start_attempt_at_question($sa, 'adaptive', 6); // Check the initial state. @@ -390,7 +390,7 @@ class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_b public function test_adaptive_shortanswer_invalid_after_complete() { // Create a short answer question - $sa = test_question_maker::make_a_shortanswer_question(); + $sa = test_question_maker::make_question('shortanswer'); $this->start_attempt_at_question($sa, 'adaptive'); // Check the initial state. @@ -472,7 +472,7 @@ class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_b public function test_adaptive_shortanswer_zero_penalty() { // Create a short answer question - $sa = test_question_maker::make_a_shortanswer_question(); + $sa = test_question_maker::make_question('shortanswer'); // Disable penalties for this question $sa->penalty = 0; $this->start_attempt_at_question($sa, 'adaptive'); @@ -529,7 +529,7 @@ class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_b public function test_adaptive_shortanswer_try_to_submit_blank() { // Create a short answer question with correct answer true. - $sa = test_question_maker::make_a_shortanswer_question(); + $sa = test_question_maker::make_question('shortanswer'); $this->start_attempt_at_question($sa, 'adaptive'); // Check the initial state. diff --git a/question/behaviour/immediatecbm/simpletest/testwalkthrough.php b/question/behaviour/immediatecbm/simpletest/testwalkthrough.php index 7724a84de80..0cf3d5e480c 100644 --- a/question/behaviour/immediatecbm/simpletest/testwalkthrough.php +++ b/question/behaviour/immediatecbm/simpletest/testwalkthrough.php @@ -188,7 +188,7 @@ class qbehaviour_immediatecbm_walkthrough_test extends qbehaviour_walkthrough_te public function test_immediatecbm_feedback_shortanswer_try_to_submit_no_certainty() { // Create a short answer question with correct answer true. - $sa = test_question_maker::make_a_shortanswer_question(); + $sa = test_question_maker::make_question('shortanswer'); $this->start_attempt_at_question($sa, 'immediatecbm'); // Check the initial state. diff --git a/question/behaviour/interactive/simpletest/testwalkthrough.php b/question/behaviour/interactive/simpletest/testwalkthrough.php index 7f7f2e14bcb..4a4bea560b6 100644 --- a/question/behaviour/interactive/simpletest/testwalkthrough.php +++ b/question/behaviour/interactive/simpletest/testwalkthrough.php @@ -244,7 +244,7 @@ class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_tes public function test_interactive_shortanswer_try_to_submit_blank() { // Create a short answer question. - $sa = test_question_maker::make_a_shortanswer_question(); + $sa = test_question_maker::make_question('shortanswer'); $sa->hints = array( new question_hint(0, 'This is the first hint.', FORMAT_HTML), new question_hint(0, 'This is the second hint.', FORMAT_HTML), @@ -420,7 +420,7 @@ class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_tes public function test_interactive_regrade_changing_num_tries_leaving_open() { // Create a multichoice multiple question. - $q = test_question_maker::make_a_shortanswer_question(); + $q = test_question_maker::make_question('shortanswer'); $q->hints = array( new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true), new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true), @@ -453,7 +453,7 @@ class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_tes public function test_interactive_regrade_changing_num_tries_finished() { // Create a multichoice multiple question. - $q = test_question_maker::make_a_shortanswer_question(); + $q = test_question_maker::make_question('shortanswer'); $q->hints = array( new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true), new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true), diff --git a/question/engine/simpletest/helpers.php b/question/engine/simpletest/helpers.php index 86b744425be..6b43ba35574 100644 --- a/question/engine/simpletest/helpers.php +++ b/question/engine/simpletest/helpers.php @@ -340,29 +340,6 @@ class test_question_maker { return $match; } - /** - * Makes a shortanswer question with correct ansewer 'frog', partially - * correct answer 'toad' and defaultmark 1. - * @return qtype_shortanswer_question - */ - public static function make_a_shortanswer_question() { - question_bank::load_question_definition_classes('shortanswer'); - $sa = new qtype_shortanswer_question(); - self::initialise_a_question($sa); - $sa->name = 'Short answer question'; - $sa->questiontext = 'Name an amphibian: __________'; - $sa->generalfeedback = 'Generalfeedback: frog or toad would have been OK.'; - $sa->usecase = false; - $sa->answers = array( - 13 => new question_answer(13, 'frog', 1.0, 'Frog is a very good answer.', FORMAT_HTML), - 14 => new question_answer(14, 'toad', 0.8, 'Toad is an OK good answer.', FORMAT_HTML), - 15 => new question_answer(15, '*', 0.0, 'That is a bad answer.', FORMAT_HTML), - ); - $sa->qtype = question_bank::get_qtype('shortanswer'); - - return $sa; - } - /** * Makes a truefalse question with correct ansewer true, defaultmark 1. * @return qtype_essay_question diff --git a/question/type/calculated/lang/en/qtype_calculated.php b/question/type/calculated/lang/en/qtype_calculated.php index ffa61b9b3a4..36e2d1d25e3 100644 --- a/question/type/calculated/lang/en/qtype_calculated.php +++ b/question/type/calculated/lang/en/qtype_calculated.php @@ -29,6 +29,7 @@ $string['addmoreunitblanks'] = 'Blanks for {$a} more units'; $string['addsets'] = 'Add set(s)'; $string['answerhdr'] = 'Answer'; $string['answerstoleranceparam'] = 'Answers tolerance parameters'; +$string['answerwithtolerance'] = '{$a->answer} (±{$a->tolerance} {$a->tolerancetype})'; $string['anyvalue'] = 'Any value'; $string['atleastoneanswer'] = 'You need to provide at least one answer.'; $string['atleastonerealdataset']='There should be at least one real dataset in question text'; diff --git a/question/type/calculated/questiontype.php b/question/type/calculated/questiontype.php index 16a87855340..99f4b7b25d0 100644 --- a/question/type/calculated/questiontype.php +++ b/question/type/calculated/questiontype.php @@ -686,6 +686,15 @@ class qtype_calculated extends question_type { parent::delete_question($questionid, $contextid); } + public function get_random_guess_score($questiondata) { + foreach ($questiondata->options->answers as $aid => $answer) { + if ('*' == trim($answer->answer)) { + return max($answer->fraction - $questiondata->options->unitpenalty, 0); + } + } + return 0; + } + public function supports_dataset_item_generation() { // Calcualted support generation of randomly distributed number data return true; @@ -1200,7 +1209,7 @@ class qtype_calculated extends question_type { public function substitute_variables($str, $dataset) { global $OUTPUT; - // testing for wrong numerical values + // testing for wrong numerical values // all calculations used this function so testing here should be OK foreach ($dataset as $name => $value) { @@ -1220,6 +1229,7 @@ class qtype_calculated extends question_type { } return $str; } + public function evaluate_equations($str, $dataset) { $formula = $this->substitute_variables($str, $dataset); if ($error = qtype_calculated_find_formula_errors($formula)) { @@ -1228,7 +1238,6 @@ class qtype_calculated extends question_type { return $str; } - public function substitute_variables_and_eval($str, $dataset) { $formula = $this->substitute_variables($str, $dataset); if ($error = qtype_calculated_find_formula_errors($formula)) { @@ -1793,21 +1802,32 @@ class qtype_calculated extends question_type { $virtualqtype = $this->get_virtual_qtype(); $unit = $virtualqtype->get_default_numerical_unit($questiondata); + $tolerancetypes = $this->tolerance_types(); + + $starfound = false; foreach ($questiondata->options->answers as $aid => $answer) { $responseclass = $answer->answer; - if ($responseclass != '*') { - $responseclass = $virtualqtype->add_unit($questiondata, $responseclass, $unit); + if ($responseclass === '*') { + $starfound = true; + } else { + $a = new stdClass(); + $a->answer = $virtualqtype->add_unit($questiondata, $responseclass, $unit); + $a->tolerance = $answer->tolerance; + $a->tolerancetype = $tolerancetypes[$answer->tolerancetype]; - $ans = new qtype_numerical_answer($answer->id, $answer->answer, $answer->fraction, - $answer->feedback, $answer->feedbackformat, $answer->tolerance); - list($min, $max) = $ans->get_tolerance_interval(); - $responseclass .= " ($min..$max)"; + $responseclass = get_string('answerwithtolerance', 'qtype_calculated', $a); } $responses[$aid] = new question_possible_response($responseclass, $answer->fraction); } + + if (!$starfound) { + $responses[0] = new question_possible_response( + get_string('didnotmatchanyanswer', 'question'), 0); + } + $responses[null] = question_possible_response::no_response(); return array($questiondata->id => $responses); diff --git a/question/type/calculated/simpletest/helper.php b/question/type/calculated/simpletest/helper.php index 6b93bd17452..b91ae1619ad 100644 --- a/question/type/calculated/simpletest/helper.php +++ b/question/type/calculated/simpletest/helper.php @@ -77,6 +77,43 @@ class qtype_calculated_test_helper extends question_test_helper { return $q; } + + /** + * Makes a calculated question about summing two numbers. + * @return qtype_calculated_question + */ + public function get_calculated_question_data_sum() { + question_bank::load_question_definition_classes('calculated'); + $qdata = new stdClass(); + test_question_maker::initialise_question_data($qdata); + + $qdata->qtype = 'calculated'; + $qdata->name = 'Simple sum'; + $qdata->questiontext = 'What is {a} + {b}?'; + $qdata->generalfeedback = 'Generalfeedback: {={a} + {b}} is the right answer.'; + + $qdata->options = new stdClass(); + $qdata->options->unitgradingtype = 0; + $qdata->options->unitpenalty = 0.0; + $qdata->options->showunits = qtype_numerical::UNITNONE; + $qdata->options->unitsleft = 0; + $qdata->options->synchronize = 0; + + $qdata->options->answers = array( + 13 => new qtype_numerical_answer(13, '{a} + {b}', 1.0, 'Very good.', FORMAT_HTML, 0.001), + 14 => new qtype_numerical_answer(14, '{a} - {b}', 0.0, 'Add. not subtract!.', + FORMAT_HTML, 0.001), + 17 => new qtype_numerical_answer(17, '*', 0.0, 'Completely wrong.', FORMAT_HTML, 0), + ); + foreach ($qdata->options->answers as $answer) { + $answer->correctanswerlength = 2; + $answer->correctanswerformat = 1; + } + + $qdata->options->units = array(); + + return $qdata; + } } diff --git a/question/type/calculated/simpletest/testquestion.php b/question/type/calculated/simpletest/testquestion.php index 5678e9bc241..de5405c6c09 100644 --- a/question/type/calculated/simpletest/testquestion.php +++ b/question/type/calculated/simpletest/testquestion.php @@ -109,6 +109,26 @@ class qtype_calculated_question_test extends UnitTestCase { $question->classify_response(array('answer' => ''))); } + public function test_classify_response_no_star() { + $question = test_question_maker::make_question('calculated'); + unset($question->answers[17]); + $question->start_attempt(new question_attempt_step(), 1); + $values = $question->vs->get_values(); + + $this->assertEqual(array( + new question_classified_response(13, $values['a'] + $values['b'], 1.0)), + $question->classify_response(array('answer' => $values['a'] + $values['b']))); + $this->assertEqual(array( + new question_classified_response(14, $values['a'] - $values['b'], 0.0)), + $question->classify_response(array('answer' => $values['a'] - $values['b']))); + $this->assertEqual(array( + new question_classified_response(0, 7 * $values['a'], 0.0)), + $question->classify_response(array('answer' => 7 * $values['a']))); + $this->assertEqual(array( + question_classified_response::no_response()), + $question->classify_response(array('answer' => ''))); + } + public function test_get_variants_selection_seed_q_not_synchronised() { $question = test_question_maker::make_question('calculated'); $this->assertEqual($question->stamp, $question->get_variants_selection_seed()); diff --git a/question/type/calculated/simpletest/testquestiontype.php b/question/type/calculated/simpletest/testquestiontype.php new file mode 100644 index 00000000000..9a7b1059211 --- /dev/null +++ b/question/type/calculated/simpletest/testquestiontype.php @@ -0,0 +1,107 @@ +. + +/** + * Unit tests for (some of) question/type/calculated/questiontype.php. + * + * @package qtype_calculated + * @copyright 2012 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/question/type/calculated/questiontype.php'); + + +/** + * Unit tests for question/type/calculated/questiontype.php. + * + * @copyright 2012 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_calculated_test extends UnitTestCase { + public static $includecoverage = array( + 'question/type/questiontypebase.php', + 'question/type/calculated/questiontype.php' + ); + + protected $tolerance = 0.00000001; + protected $qtype; + + public function setUp() { + $this->qtype = new qtype_calculated(); + } + + public function tearDown() { + $this->qtype = null; + } + + public function test_name() { + $this->assertEqual($this->qtype->name(), 'calculated'); + } + + public function test_can_analyse_responses() { + $this->assertTrue($this->qtype->can_analyse_responses()); + } + + public function test_get_random_guess_score() { + $q = test_question_maker::get_question_data('calculated'); + $q->options->answers[17]->fraction = 0.1; + $this->assertEqual(0.1, $this->qtype->get_random_guess_score($q)); + } + + protected function get_possible_response($ans, $tolerance, $type) { + $a = new stdClass(); + $a->answer = $ans; + $a->tolerance = $tolerance; + $a->tolerancetype = get_string($type, 'qtype_numerical'); + return get_string('answerwithtolerance', 'qtype_calculated', $a); + } + + public function test_get_possible_responses() { + $q = test_question_maker::get_question_data('calculated'); + + $this->assertEqual(array( + $q->id => array( + 13 => new question_possible_response( + $this->get_possible_response('{a} + {b}', 0.001, 'nominal'), 1.0), + 14 => new question_possible_response( + $this->get_possible_response('{a} - {b}', 0.001, 'nominal'), 0.0), + 17 => new question_possible_response('*', 0.0), + null => question_possible_response::no_response() + ), + ), $this->qtype->get_possible_responses($q)); + } + + public function test_get_possible_responses_no_star() { + $q = test_question_maker::get_question_data('calculated'); + unset($q->options->answers[17]); + + $this->assertEqual(array( + $q->id => array( + 13 => new question_possible_response( + $this->get_possible_response('{a} + {b}', 0.001, 'nominal'), 1), + 14 => new question_possible_response( + $this->get_possible_response('{a} - {b}', 0.001, 'nominal'), 0), + 0 => new question_possible_response( + get_string('didnotmatchanyanswer', 'question'), 0), + null => question_possible_response::no_response() + ), + ), $this->qtype->get_possible_responses($q)); + } +} diff --git a/question/type/numerical/question.php b/question/type/numerical/question.php index ea1b026fd1f..017af6bcf7e 100644 --- a/question/type/numerical/question.php +++ b/question/type/numerical/question.php @@ -258,15 +258,16 @@ class qtype_numerical_question extends question_graded_automatically { } list($value, $unit, $multiplier) = $this->ap->apply_units($response['answer'], $selectedunit); $ans = $this->get_matching_answer($value, $multiplier); - if (!$ans) { - return array($this->id => question_classified_response::no_response()); - } $resp = $response['answer']; if ($this->has_separate_unit_field()) { $resp = $this->ap->add_unit($resp, $unit); } + if (!$ans) { + return array($this->id => new question_classified_response(0, $resp, 0)); + } + return array($this->id => new question_classified_response($ans->id, $resp, $this->apply_unit_penalty($ans->fraction, $ans->unitisright))); diff --git a/question/type/numerical/questiontype.php b/question/type/numerical/questiontype.php index 07433189c02..b275ffa195d 100644 --- a/question/type/numerical/questiontype.php +++ b/question/type/numerical/questiontype.php @@ -418,10 +418,13 @@ class qtype_numerical extends question_type { $unit = $this->get_default_numerical_unit($questiondata); + $starfound = false; foreach ($questiondata->options->answers as $aid => $answer) { $responseclass = $answer->answer; - if ($responseclass != '*') { + if ($responseclass === '*') { + $starfound = true; + } else { $responseclass = $this->add_unit($questiondata, $responseclass, $unit); $ans = new qtype_numerical_answer($answer->id, $answer->answer, $answer->fraction, @@ -433,6 +436,12 @@ class qtype_numerical extends question_type { $responses[$aid] = new question_possible_response($responseclass, $answer->fraction); } + + if (!$starfound) { + $responses[0] = new question_possible_response( + get_string('didnotmatchanyanswer', 'question'), 0); + } + $responses[null] = question_possible_response::no_response(); return array($questiondata->id => $responses); diff --git a/question/type/numerical/simpletest/helper.php b/question/type/numerical/simpletest/helper.php index fa463defd6c..ffd15bb2329 100644 --- a/question/type/numerical/simpletest/helper.php +++ b/question/type/numerical/simpletest/helper.php @@ -72,8 +72,7 @@ class qtype_numerical_test_helper extends question_test_helper { } /** - * Makes a numerical question with correct ansewer 3.14, and various incorrect - * answers with different feedback. + * Makes a numerical question with a choice (select menu) of units. * @return qtype_numerical_question */ public function make_numerical_question_unit() { diff --git a/question/type/numerical/simpletest/testquestion.php b/question/type/numerical/simpletest/testquestion.php index 090f85d4ea0..445f09061f1 100644 --- a/question/type/numerical/simpletest/testquestion.php +++ b/question/type/numerical/simpletest/testquestion.php @@ -213,6 +213,22 @@ class qtype_numerical_question_test extends UnitTestCase { $num->classify_response(array('answer' => ''))); } + public function test_classify_response_no_star() { + $num = test_question_maker::make_question('numerical'); + unset($num->answers[17]); + $num->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual(array( + new question_classified_response(15, '3.1', 0.0)), + $num->classify_response(array('answer' => '3.1'))); + $this->assertEqual(array( + new question_classified_response(0, '42', 0.0)), + $num->classify_response(array('answer' => '42'))); + $this->assertEqual(array( + question_classified_response::no_response()), + $num->classify_response(array('answer' => ''))); + } + public function test_classify_response_unit() { $num = test_question_maker::make_question('numerical', 'unit'); $num->start_attempt(new question_attempt_step(), 1); @@ -240,6 +256,25 @@ class qtype_numerical_question_test extends UnitTestCase { $num->classify_response(array('answer' => ''))); } + public function test_classify_response_unit_no_star() { + $num = test_question_maker::make_question('numerical', 'unit'); + unset($num->answers[17]); + $num->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual(array( + new question_classified_response(0, '42 cm', 0)), + $num->classify_response(array('answer' => '42', 'unit' => 'cm'))); + $this->assertEqual(array( + new question_classified_response(0, '3.0', 0)), + $num->classify_response(array('answer' => '3.0', 'unit' => ''))); + $this->assertEqual(array( + new question_classified_response(0, '3.0 m', 0)), + $num->classify_response(array('answer' => '3.0', 'unit' => 'm'))); + $this->assertEqual(array( + question_classified_response::no_response()), + $num->classify_response(array('answer' => '', 'unit' => ''))); + } + public function test_classify_response_currency() { $num = test_question_maker::make_question('numerical', 'currency'); $num->start_attempt(new question_attempt_step(), 1); diff --git a/question/type/numerical/simpletest/testquestiontype.php b/question/type/numerical/simpletest/testquestiontype.php index 1e40a950988..1f4581e7f0a 100644 --- a/question/type/numerical/simpletest/testquestiontype.php +++ b/question/type/numerical/simpletest/testquestiontype.php @@ -101,7 +101,22 @@ class qtype_numerical_test extends UnitTestCase { $q->id => array( 13 => new question_possible_response('42 m (41.5..42.5)', 1), 14 => new question_possible_response('*', 0.1), - null => question_possible_response::no_response()), + null => question_possible_response::no_response() + ), + ), $this->qtype->get_possible_responses($q)); + } + + public function test_get_possible_responses_no_star() { + $q = $this->get_test_question_data(); + unset($q->options->answers[14]); + + $this->assertEqual(array( + $q->id => array( + 13 => new question_possible_response('42 m (41.5..42.5)', 1), + 0 => new question_possible_response( + get_string('didnotmatchanyanswer', 'question'), 0), + null => question_possible_response::no_response() + ), ), $this->qtype->get_possible_responses($q)); } } diff --git a/question/type/questionbase.php b/question/type/questionbase.php index edd9266468f..062c9dd68f6 100644 --- a/question/type/questionbase.php +++ b/question/type/questionbase.php @@ -709,8 +709,10 @@ abstract class question_graded_by_strategy extends question_graded_automatically $ans = $this->get_matching_answer($response); if (!$ans) { - return array($this->id => question_classified_response::no_response()); + return array($this->id => new question_classified_response( + 0, $response['answer'], 0)); } + return array($this->id => new question_classified_response( $ans->id, $response['answer'], $ans->fraction)); } diff --git a/question/type/shortanswer/questiontype.php b/question/type/shortanswer/questiontype.php index ff9df64f763..7c78d389098 100644 --- a/question/type/shortanswer/questiontype.php +++ b/question/type/shortanswer/questiontype.php @@ -139,10 +139,20 @@ class qtype_shortanswer extends question_type { public function get_possible_responses($questiondata) { $responses = array(); + $starfound = false; foreach ($questiondata->options->answers as $aid => $answer) { $responses[$aid] = new question_possible_response($answer->answer, $answer->fraction); + if ($answer->answer === '*') { + $starfound = true; + } } + + if (!$starfound) { + $responses[0] = new question_possible_response( + get_string('didnotmatchanyanswer', 'question'), 0); + } + $responses[null] = question_possible_response::no_response(); return array($questiondata->id => $responses); diff --git a/question/type/shortanswer/simpletest/helper.php b/question/type/shortanswer/simpletest/helper.php new file mode 100644 index 00000000000..71a0f409111 --- /dev/null +++ b/question/type/shortanswer/simpletest/helper.php @@ -0,0 +1,133 @@ +. + +/** + * Test helpers for the shortanswer question type. + * + * @package qtype_shortanswer + * @copyright 2012 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Test helper class for the shortanswer question type. + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_shortanswer_test_helper extends question_test_helper { + public function get_test_questions() { + return array('frogtoad', 'frogonly'); + } + + /** + * Makes a shortanswer question with correct ansewer 'frog', partially + * correct answer 'toad' and defaultmark 1. This question also has a + * '*' match anything answer. + * @return qtype_shortanswer_question + */ + public function make_shortanswer_question_frogtoad() { + question_bank::load_question_definition_classes('shortanswer'); + $sa = new qtype_shortanswer_question(); + test_question_maker::initialise_a_question($sa); + $sa->name = 'Short answer question'; + $sa->questiontext = 'Name an amphibian: __________'; + $sa->generalfeedback = 'Generalfeedback: frog or toad would have been OK.'; + $sa->usecase = false; + $sa->answers = array( + 13 => new question_answer(13, 'frog', 1.0, 'Frog is a very good answer.', FORMAT_HTML), + 14 => new question_answer(14, 'toad', 0.8, 'Toad is an OK good answer.', FORMAT_HTML), + 15 => new question_answer(15, '*', 0.0, 'That is a bad answer.', FORMAT_HTML), + ); + $sa->qtype = question_bank::get_qtype('shortanswer'); + + return $sa; + } + + /** + * Gets the question data for a shortanswer question with with correct + * ansewer 'frog', partially correct answer 'toad' and defaultmark 1. + * This question also has a '*' match anything answer. + * @return stdClass + */ + public function get_shortanswer_question_data_frogtoad() { + $qdata = new stdClass(); + test_question_maker::initialise_question_data($qdata); + + $qdata->qtype = 'shortanswer'; + $qdata->name = 'Short answer question'; + $qdata->questiontext = 'Name an amphibian: __________'; + $qdata->generalfeedback = 'Generalfeedback: frog or toad would have been OK.'; + + $qdata->options = new stdClass(); + $qdata->options->usecase = false; + $qdata->options->answers = array( + 13 => new question_answer(13, 'frog', 1.0, 'Frog is a very good answer.', FORMAT_HTML), + 14 => new question_answer(14, 'toad', 0.8, 'Toad is an OK good answer.', FORMAT_HTML), + 15 => new question_answer(15, '*', 0.0, 'That is a bad answer.', FORMAT_HTML), + ); + + return $qdata; + } + + /** + * Makes a shortanswer question with just the correct ansewer 'frog', and + * no other answer matching. + * @return qtype_shortanswer_question + */ + public function make_shortanswer_question_frogonly() { + question_bank::load_question_definition_classes('shortanswer'); + $sa = new qtype_shortanswer_question(); + test_question_maker::initialise_a_question($sa); + $sa->name = 'Short answer question'; + $sa->questiontext = 'Name the best amphibian: __________'; + $sa->generalfeedback = 'Generalfeedback: you should have said frog.'; + $sa->usecase = false; + $sa->answers = array( + 13 => new question_answer(13, 'frog', 1.0, 'Frog is right.', FORMAT_HTML), + ); + $sa->qtype = question_bank::get_qtype('shortanswer'); + + return $sa; + } + + /** + * Gets the question data for a shortanswer questionwith just the correct + * ansewer 'frog', and no other answer matching. + * @return stdClass + */ + public function get_shortanswer_question_data_frogonly() { + $qdata = new stdClass(); + test_question_maker::initialise_question_data($qdata); + + $qdata->qtype = 'shortanswer'; + $qdata->name = 'Short answer question'; + $qdata->questiontext = 'Name the best amphibian: __________'; + $qdata->generalfeedback = 'Generalfeedback: you should have said frog.'; + + $qdata->options = new stdClass(); + $qdata->options->usecase = false; + $qdata->options->answers = array( + 13 => new question_answer(13, 'frog', 1.0, 'Frog is right.', FORMAT_HTML), + ); + + return $qdata; + } +} diff --git a/question/type/shortanswer/simpletest/testquestion.php b/question/type/shortanswer/simpletest/testquestion.php index afd66efb5fa..4db7d6b4b79 100644 --- a/question/type/shortanswer/simpletest/testquestion.php +++ b/question/type/shortanswer/simpletest/testquestion.php @@ -26,8 +26,8 @@ defined('MOODLE_INTERNAL') || die(); -require_once($CFG->dirroot . '/question/type/shortanswer/question.php'); require_once($CFG->dirroot . '/question/engine/simpletest/helpers.php'); +require_once($CFG->dirroot . '/question/type/shortanswer/question.php'); /** @@ -108,7 +108,7 @@ class qtype_shortanswer_question_test extends UnitTestCase { } public function test_is_complete_response() { - $question = test_question_maker::make_a_shortanswer_question(); + $question = test_question_maker::make_question('shortanswer'); $this->assertFalse($question->is_complete_response(array())); $this->assertFalse($question->is_complete_response(array('answer' => ''))); @@ -118,7 +118,7 @@ class qtype_shortanswer_question_test extends UnitTestCase { } public function test_is_gradable_response() { - $question = test_question_maker::make_a_shortanswer_question(); + $question = test_question_maker::make_question('shortanswer'); $this->assertFalse($question->is_gradable_response(array())); $this->assertFalse($question->is_gradable_response(array('answer' => ''))); @@ -128,7 +128,7 @@ class qtype_shortanswer_question_test extends UnitTestCase { } public function test_grading() { - $question = test_question_maker::make_a_shortanswer_question(); + $question = test_question_maker::make_question('shortanswer'); $this->assertEqual(array(0, question_state::$gradedwrong), $question->grade_response(array('answer' => 'x'))); @@ -139,26 +139,26 @@ class qtype_shortanswer_question_test extends UnitTestCase { } public function test_get_correct_response() { - $question = test_question_maker::make_a_shortanswer_question(); + $question = test_question_maker::make_question('shortanswer'); $this->assertEqual(array('answer' => 'frog'), $question->get_correct_response()); } public function test_get_question_summary() { - $sa = test_question_maker::make_a_shortanswer_question(); + $sa = test_question_maker::make_question('shortanswer'); $qsummary = $sa->get_question_summary(); $this->assertEqual('Name an amphibian: __________', $qsummary); } public function test_summarise_response() { - $sa = test_question_maker::make_a_shortanswer_question(); + $sa = test_question_maker::make_question('shortanswer'); $summary = $sa->summarise_response(array('answer' => 'dog')); $this->assertEqual('dog', $summary); } public function test_classify_response() { - $sa = test_question_maker::make_a_shortanswer_question(); + $sa = test_question_maker::make_question('shortanswer'); $sa->start_attempt(new question_attempt_step(), 1); $this->assertEqual(array( @@ -174,4 +174,19 @@ class qtype_shortanswer_question_test extends UnitTestCase { question_classified_response::no_response()), $sa->classify_response(array('answer' => ''))); } + + public function test_classify_response_no_star() { + $sa = test_question_maker::make_question('shortanswer', 'frogonly'); + $sa->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual(array( + new question_classified_response(13, 'frog', 1.0)), + $sa->classify_response(array('answer' => 'frog'))); + $this->assertEqual(array( + new question_classified_response(0, 'toad', 0.0)), + $sa->classify_response(array('answer' => 'toad'))); + $this->assertEqual(array( + question_classified_response::no_response()), + $sa->classify_response(array('answer' => ''))); + } } diff --git a/question/type/shortanswer/simpletest/testquestiontype.php b/question/type/shortanswer/simpletest/testquestiontype.php index 711dc751afd..340397b8b29 100644 --- a/question/type/shortanswer/simpletest/testquestiontype.php +++ b/question/type/shortanswer/simpletest/testquestiontype.php @@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . '/question/type/shortanswer/questiontype.php'); +require_once($CFG->dirroot . '/question/engine/simpletest/helpers.php'); /** @@ -52,12 +53,7 @@ class qtype_shortanswer_test extends UnitTestCase { } protected function get_test_question_data() { - $q = new stdClass(); - $q->id = 1; - $q->options->answers[1] = (object) array('answer' => 'frog', 'fraction' => 1); - $q->options->answers[2] = (object) array('answer' => '*', 'fraction' => 0.1); - - return $q; + return test_question_maker::get_question_data('shortanswer'); } public function test_name() { @@ -69,18 +65,33 @@ class qtype_shortanswer_test extends UnitTestCase { } public function test_get_random_guess_score() { - $q = $this->get_test_question_data(); + $q = test_question_maker::get_question_data('shortanswer'); + $q->options->answers[15]->fraction = 0.1; $this->assertEqual(0.1, $this->qtype->get_random_guess_score($q)); } public function test_get_possible_responses() { - $q = $this->get_test_question_data(); + $q = test_question_maker::get_question_data('shortanswer'); $this->assertEqual(array( $q->id => array( - 1 => new question_possible_response('frog', 1), - 2 => new question_possible_response('*', 0.1), - null => question_possible_response::no_response()), + 13 => new question_possible_response('frog', 1), + 14 => new question_possible_response('toad', 0.8), + 15 => new question_possible_response('*', 0), + null => question_possible_response::no_response() + ), + ), $this->qtype->get_possible_responses($q)); + } + + public function test_get_possible_responses_no_star() { + $q = test_question_maker::get_question_data('shortanswer', 'frogonly'); + + $this->assertEqual(array( + $q->id => array( + 13 => new question_possible_response('frog', 1), + 0 => new question_possible_response(get_string('didnotmatchanyanswer', 'question'), 0), + null => question_possible_response::no_response() + ), ), $this->qtype->get_possible_responses($q)); } } diff --git a/question/type/truefalse/simpletest/testquestiontype.php b/question/type/truefalse/simpletest/testquestiontype.php index bfc493aa742..d71aced0400 100644 --- a/question/type/truefalse/simpletest/testquestiontype.php +++ b/question/type/truefalse/simpletest/testquestiontype.php @@ -26,7 +26,7 @@ defined('MOODLE_INTERNAL') || die(); -require_once($CFG->dirroot . '/question/type//truefalse/questiontype.php'); +require_once($CFG->dirroot . '/question/type/truefalse/questiontype.php'); /**