diff --git a/changehistory.md b/changehistory.md index 61a2f3bcc..3aeb6e662 100644 --- a/changehistory.md +++ b/changehistory.md @@ -1,5 +1,18 @@ # CHANGE HISTORY +### 11 July 2017. Version 3.2.0 + +* Add allow_multiple_stdins option for advanced use of combinator templates. + This option disables the usual behaviour of running combinator templates + once for each test when any tests have standard input defined. When enabled + the combinator is given all testcases (as when standard input is not present) + and must itself manage the switching of standard inputs between tests. +* Incorporate style changes from Open University (thanks Mahmoud Kassaei) for + improved accessibility of the Ace editor. +* Disable the suppression of a regrade when the same answer is + submitted twice in succession. The suppression was introduced in + version 3.1.5 but has proved confusing for question authors. + ### 5 July 2017. Version 3.1.5+ * Fix bug in display of "For example" table when question has customised diff --git a/classes/jobrunner.php b/classes/jobrunner.php index ce985b44f..b3617dbbd 100644 --- a/classes/jobrunner.php +++ b/classes/jobrunner.php @@ -82,7 +82,8 @@ public function run_tests($question, $code, $testcases, $isprecheck) { 'STUDENT' => new qtype_coderunner_student($USER) ); - if ($question->get_is_combinator() and $this->has_no_stdins()) { + if ($question->get_is_combinator() and + ($this->has_no_stdins() || $question->allow_multiple_stdins())) { $outcome = $this->run_combinator($isprecheck); } else { $outcome = null; @@ -95,7 +96,6 @@ public function run_tests($question, $code, $testcases, $isprecheck) { // a test result for each test case. if ($outcome == null) { - assert (!($question->get_is_combinator() && $this->grader->name() == 'TemplateGrader')); $outcome = $this->run_tests_singly($isprecheck); } diff --git a/db/install.xml b/db/install.xml index a26cd7c5d..19dfa8610 100644 --- a/db/install.xml +++ b/db/install.xml @@ -1,5 +1,5 @@ - @@ -25,6 +25,7 @@ + diff --git a/db/upgrade.php b/db/upgrade.php index 61bb3ef91..63d1b1ee3 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -174,6 +174,21 @@ function xmldb_qtype_coderunner_upgrade($oldversion) { upgrade_plugin_savepoint(true, 2016123001, 'qtype', 'coderunner'); } + if ($oldversion < 2017071100) { + + // Define field allowmultiplestdins to be added to question_coderunner_options. + $table = new xmldb_table('question_coderunner_options'); + $field = new xmldb_field('allowmultiplestdins', XMLDB_TYPE_INTEGER, '1', null, null, null, null, 'combinatortemplate'); + + // Conditionally launch add field allowmultiplestdins. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Coderunner savepoint reached. + upgrade_plugin_savepoint(true, 2017071100, 'qtype', 'coderunner'); + } + require_once(__DIR__ . '/upgradelib.php'); update_question_types(); diff --git a/edit_coderunner_form.php b/edit_coderunner_form.php index 9c617e514..67137a1d9 100644 --- a/edit_coderunner_form.php +++ b/edit_coderunner_form.php @@ -552,6 +552,8 @@ private function make_customisation_panel($mform) { $templatecontrols = array(); $templatecontrols[] = $mform->createElement('advcheckbox', 'iscombinatortemplate', null, get_string('iscombinatortemplate', 'qtype_coderunner')); + $templatecontrols[] = $mform->createElement('advcheckbox', 'allowmultiplestdins', null, + get_string('allowmultiplestdins', 'qtype_coderunner')); $templatecontrols[] = $mform->createElement('text', 'testsplitterre', get_string('testsplitterre', 'qtype_coderunner'), @@ -647,6 +649,7 @@ private function make_advanced_customisation_panel($mform) { $mform->disabledIf('typename', 'prototypetype', 'neq', '2'); $mform->disabledIf('testsplitterre', 'iscombinatortemplate', 'eq', 0); + $mform->disabledIf('allowmultiplestdins', 'iscombinatortemplate', 'eq', 0); } // UTILITY FUNCTIONS. diff --git a/lang/en/qtype_coderunner.php b/lang/en/qtype_coderunner.php index a92ba7f6c..a80220537 100644 --- a/lang/en/qtype_coderunner.php +++ b/lang/en/qtype_coderunner.php @@ -33,6 +33,7 @@ The per-test-case marks can be specified only if the all-or-nothing checkbox is unchecked. If using a template grader that awards part marks to test cases, \'All-or-nothing\' should generally be unchecked.'; +$string['allowmultiplestdins'] = 'Allow multiple stdins'; $string['answer'] = 'Sample answer'; $string['answerprompt'] = 'Answer:'; $string['answer_help'] = 'A sample answer can be entered here and used for checking by the question author and optionally shown to students during review. It is also used by the bulk tester script. The correctness of a non-empty answer is checked when saving unless \'Validate on save\' is unchecked'; @@ -487,13 +488,16 @@ However, if testcases have standard input defined, combinator templates become problematic. If the template constructs a single program, what should the standard -input be? There is no simple answer to that question so in this case coderunner -runs the test cases one at a time, using the combinator template to build +input be? The simplest (and default) solution is to +run the test cases one at a time, using the combinator template to build each program, passing it a TESTCASES variable containing just a single test. This \'trick\' allows the combinator template to serve a dual role: it behaves as a per-test-case template (with a 1-element TESTCASES array) when the question author supplies standard input but as a proper combinator (with an n-element -TESTCASES array) otherwise. +TESTCASES array) otherwise. To change this behaviour so that the combinator +receives all testcases, even when stdin is present, check the \'Allow multiple +stdins\' checkbox. + If a run of the combinator program results in any output to stderr, that is interpreted as a run error. To ensure the student gets credit for as many diff --git a/question.php b/question.php index eaa7e2185..56ab932e0 100644 --- a/question.php +++ b/question.php @@ -308,6 +308,12 @@ public function get_is_combinator() { return $this->iscombinatortemplate; } + + // Return whether or not multiple stdins are allowed when using combiantor + public function allow_multiple_stdins() { + return $this->allowmultiplestdins; + } + // Return an instance of the sandbox to be used to run code for this question. public function get_sandbox() { global $CFG; diff --git a/questiontype.php b/questiontype.php index 455160247..5dff5cb8d 100644 --- a/questiontype.php +++ b/questiontype.php @@ -95,6 +95,7 @@ public function extra_question_fields() { 'resultcolumns', 'template', 'iscombinatortemplate', + 'allowmultiplestdins', 'answer', 'validateonsave', 'testsplitterre', diff --git a/tests/walkthrough_extras.php b/tests/walkthrough_extras.php index 82db6885a..b5e1890ab 100644 --- a/tests/walkthrough_extras.php +++ b/tests/walkthrough_extras.php @@ -86,7 +86,7 @@ public function test_result_column_selection() { * or misconfigured" exception. * * @expectedException qtype_coderunner_exception - * @expectedExceptionMessageRegExp |.*jobesandbox is down or misconfigured.*| + * @expectedExceptionMessageRegExp |.*Error from the sandbox.*may be down or overloaded.*| * @retrun void */ public function test_misconfigured_jobe() { @@ -96,6 +96,29 @@ public function test_misconfigured_jobe() { set_config('jobe_host', 'localhostxxx', 'qtype_coderunner'); // Broken jobe_host url. $q = test_question_maker::make_question('coderunner', 'sqr'); $this->start_attempt_at_question($q, 'adaptive', 1, 1); + $this->process_submission(array('-submit' => 1, 'answer' => "def sqr(n): return n * n\n")); } + + /** Check that a combinator template is run once per test case when stdin + * is present and allowmultiplestdins is false, but run with all test + * cases when allowmutliplestdins is true. + */ + public function test_multiplestdins() { + $q = test_question_maker::make_question('coderunner', 'sqr'); + $q->testcases[0]->stdin = 'A bit of standard input to trigger one-at-a-time mode'; + $q->showsource = true; + + // Submit a right answer. + $this->start_attempt_at_question($q, 'adaptive', 1, 1); + $this->process_submission(array('-submit' => 1, 'answer' => "def sqr(n): return n * n\n")); + $this->check_output_contains('Run 4'); + + // Now turn on allowmultiplestdins and try again + $q->allowmultiplestdins = true; + $this->start_attempt_at_question($q, 'adaptive', 1, 1); + $this->process_submission(array('-submit' => 1, 'answer' => "def sqr(n): return n * n\n")); + $this->check_output_does_not_contain('Run 4'); + } + } diff --git a/version.php b/version.php index 7dbeb4c64..298a1cc08 100644 --- a/version.php +++ b/version.php @@ -22,12 +22,12 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2017052600; +$plugin->version = 2017071100; $plugin->requires = 2015051100; $plugin->cron = 0; $plugin->component = 'qtype_coderunner'; $plugin->maturity = MATURITY_STABLE; -$plugin->release = '3.1.5'; +$plugin->release = '3.1.6'; $plugin->dependencies = array( 'qbehaviour_adaptive_adapted_for_coderunner' => 2017052600