Differences Between: [Versions 310 and 311] [Versions 39 and 311]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace qformat_gift; 18 19 use qformat_gift; 20 use question_bank; 21 use question_check_specified_fields_expectation; 22 23 defined('MOODLE_INTERNAL') || die(); 24 25 global $CFG; 26 require_once($CFG->libdir . '/questionlib.php'); 27 require_once($CFG->dirroot . '/question/format.php'); 28 require_once($CFG->dirroot . '/question/format/gift/format.php'); 29 require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); 30 31 /** 32 * Unit tests for the GIFT import/export format. 33 * 34 * @package qformat_gift 35 * @copyright 2010 The Open University 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class giftformat_test extends \question_testcase { 39 public function assert_same_gift($expectedtext, $text) { 40 $this->assertEquals(str_replace("\r\n", "\n", $expectedtext), 41 str_replace("\r\n", "\n", $text)); 42 } 43 44 public function test_import_essay() { 45 $gift = ' 46 // essay 47 ::Q8:: How are you? {}'; 48 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 49 50 $importer = new qformat_gift(); 51 $q = $importer->readquestion($lines); 52 53 $expectedq = (object) array( 54 'name' => 'Q8', 55 'questiontext' => 'How are you?', 56 'questiontextformat' => FORMAT_MOODLE, 57 'generalfeedback' => '', 58 'generalfeedbackformat' => FORMAT_MOODLE, 59 'qtype' => 'essay', 60 'defaultmark' => 1, 61 'penalty' => 0.3333333, 62 'length' => 1, 63 'responseformat' => 'editor', 64 'responsefieldlines' => 15, 65 'attachments' => 0, 66 'graderinfo' => array( 67 'text' => '', 68 'format' => FORMAT_HTML, 69 'files' => array()), 70 ); 71 72 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 73 } 74 75 public function test_export_essay() { 76 $qdata = (object) array( 77 'id' => 666 , 78 'name' => 'Q8', 79 'questiontext' => 'How are you?', 80 'questiontextformat' => FORMAT_MOODLE, 81 'generalfeedback' => '', 82 'generalfeedbackformat' => FORMAT_MOODLE, 83 'defaultmark' => 1, 84 'penalty' => 0.3333333, 85 'length' => 1, 86 'qtype' => 'essay', 87 'options' => (object) array( 88 'responseformat' => 'editor', 89 'responsefieldlines' => 15, 90 'attachments' => 0, 91 'graderinfo' => '', 92 'graderinfoformat' => FORMAT_HTML, 93 ), 94 ); 95 96 $exporter = new qformat_gift(); 97 $gift = $exporter->writequestion($qdata); 98 99 $expectedgift = "// question: 666 name: Q8 100 ::Q8::How are you?{} 101 102 "; 103 104 $this->assert_same_gift($expectedgift, $gift); 105 } 106 107 public function test_import_match() { 108 $gift = ' 109 // question: 2 name: Moodle activities 110 ::Moodle activities::[html]Match the <b>activity</b> to the description.{ 111 =[html]An activity supporting asynchronous discussions. -> Forum 112 =[moodle]A teacher asks a question and specifies a choice of multiple responses. -> Choice 113 =[plain]A bank of record entries which participants can add to. -> Database 114 =[markdown]A collection of web pages that anyone can add to or edit. -> Wiki 115 = -> Chat 116 }'; 117 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 118 119 $importer = new qformat_gift(); 120 $q = $importer->readquestion($lines); 121 122 $expectedq = (object) array( 123 'name' => 'Moodle activities', 124 'questiontext' => 'Match the <b>activity</b> to the description.', 125 'questiontextformat' => FORMAT_HTML, 126 'generalfeedback' => '', 127 'generalfeedbackformat' => FORMAT_HTML, 128 'qtype' => 'match', 129 'defaultmark' => 1, 130 'penalty' => 0.3333333, 131 'length' => 1, 132 'shuffleanswers' => '1', 133 'correctfeedback' => array( 134 'text' => '', 135 'format' => FORMAT_HTML, 136 'files' => array(), 137 ), 138 'partiallycorrectfeedback' => array( 139 'text' => '', 140 'format' => FORMAT_HTML, 141 'files' => array(), 142 ), 143 'incorrectfeedback' => array( 144 'text' => '', 145 'format' => FORMAT_HTML, 146 'files' => array(), 147 ), 148 'subquestions' => array( 149 0 => array( 150 'text' => 'An activity supporting asynchronous discussions.', 151 'format' => FORMAT_HTML, 152 'files' => array(), 153 ), 154 1 => array( 155 'text' => 'A teacher asks a question and specifies a choice of multiple responses.', 156 'format' => FORMAT_MOODLE, 157 'files' => array(), 158 ), 159 2 => array( 160 'text' => 'A bank of record entries which participants can add to.', 161 'format' => FORMAT_PLAIN, 162 'files' => array(), 163 ), 164 3 => array( 165 'text' => 'A collection of web pages that anyone can add to or edit.', 166 'format' => FORMAT_MARKDOWN, 167 'files' => array(), 168 ), 169 4 => array( 170 'text' => '', 171 'format' => FORMAT_HTML, 172 'files' => array(), 173 ), 174 ), 175 'subanswers' => array( 176 0 => 'Forum', 177 1 => 'Choice', 178 2 => 'Database', 179 3 => 'Wiki', 180 4 => 'Chat', 181 ), 182 ); 183 184 // Repeated test for better failure messages. 185 $this->assertEquals($expectedq->subquestions, $q->subquestions); 186 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 187 } 188 189 public function test_export_match() { 190 $qdata = (object) array( 191 'id' => 666 , 192 'name' => 'Moodle activities', 193 'questiontext' => 'Match the <b>activity</b> to the description.', 194 'questiontextformat' => FORMAT_HTML, 195 'generalfeedback' => '', 196 'generalfeedbackformat' => FORMAT_HTML, 197 'defaultmark' => 1, 198 'penalty' => 0.3333333, 199 'length' => 1, 200 'qtype' => 'match', 201 'options' => (object) array( 202 'id' => 123, 203 'question' => 666, 204 'shuffleanswers' => 1, 205 'subquestions' => array( 206 42 => (object) array( 207 'id' => 1234, 208 'code' => 12341234, 209 'question' => 666, 210 'questiontext' => '<div class="frog">An activity supporting asynchronous discussions.</div>', 211 'questiontextformat' => FORMAT_HTML, 212 'answertext' => 'Forum', 213 ), 214 43 => (object) array( 215 'id' => 1234, 216 'code' => 12341234, 217 'question' => 666, 218 'questiontext' => 'A teacher asks a question and specifies a choice of multiple responses.', 219 'questiontextformat' => FORMAT_MOODLE, 220 'answertext' => 'Choice', 221 ), 222 44 => (object) array( 223 'id' => 1234, 224 'code' => 12341234, 225 'question' => 666, 226 'questiontext' => 'A bank of record entries which participants can add to.', 227 'questiontextformat' => FORMAT_PLAIN, 228 'answertext' => 'Database', 229 ), 230 45 => (object) array( 231 'id' => 1234, 232 'code' => 12341234, 233 'question' => 666, 234 'questiontext' => 'A collection of web pages that anyone can add to or edit.', 235 'questiontextformat' => FORMAT_MARKDOWN, 236 'answertext' => 'Wiki', 237 ), 238 46 => (object) array( 239 'id' => 1234, 240 'code' => 12341234, 241 'question' => 666, 242 'questiontext' => '', 243 'questiontextformat' => FORMAT_MARKDOWN, 244 'answertext' => 'Chat', 245 ), 246 ), 247 ), 248 ); 249 250 $exporter = new qformat_gift(); 251 $gift = $exporter->writequestion($qdata); 252 253 $expectedgift = "// question: 666 name: Moodle activities 254 ::Moodle activities::[html]Match the <b>activity</b> to the description.{ 255 \t=<div class\\=\"frog\">An activity supporting asynchronous discussions.</div> -> Forum 256 \t=[moodle]A teacher asks a question and specifies a choice of multiple responses. -> Choice 257 \t=[plain]A bank of record entries which participants can add to. -> Database 258 \t=[markdown]A collection of web pages that anyone can add to or edit. -> Wiki 259 \t= -> Chat 260 } 261 262 "; 263 264 $this->assert_same_gift($expectedgift, $gift); 265 } 266 267 /** 268 * Test import of multichoice question in GIFT format 269 * 270 * @dataProvider numberingstyle_provider 271 * 272 * @param string $numberingstyle multichoice numbering style to set for qtype_multichoice 273 * 274 */ 275 public function test_import_multichoice($numberingstyle) { 276 $this->resetAfterTest(true); 277 278 set_config('answernumbering', $numberingstyle, 'qtype_multichoice'); 279 $gift = " 280 // multiple choice with specified feedback for right and wrong answers 281 ::Q2:: What's between orange and green in the spectrum? 282 { 283 =yellow # right; good! 284 ~red # [html]wrong, it's yellow 285 ~[plain]blue # wrong, it's yellow 286 }"; 287 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 288 289 $importer = new qformat_gift(); 290 $q = $importer->readquestion($lines); 291 292 $expectedq = (object) array( 293 'name' => 'Q2', 294 'questiontext' => "What's between orange and green in the spectrum?", 295 'questiontextformat' => FORMAT_MOODLE, 296 'generalfeedback' => '', 297 'generalfeedbackformat' => FORMAT_MOODLE, 298 'qtype' => 'multichoice', 299 'defaultmark' => 1, 300 'penalty' => 0.3333333, 301 'length' => 1, 302 'single' => 1, 303 'shuffleanswers' => '1', 304 'answernumbering' => $numberingstyle, 305 'correctfeedback' => array( 306 'text' => '', 307 'format' => FORMAT_MOODLE, 308 'files' => array(), 309 ), 310 'partiallycorrectfeedback' => array( 311 'text' => '', 312 'format' => FORMAT_MOODLE, 313 'files' => array(), 314 ), 315 'incorrectfeedback' => array( 316 'text' => '', 317 'format' => FORMAT_MOODLE, 318 'files' => array(), 319 ), 320 'answer' => array( 321 0 => array( 322 'text' => 'yellow', 323 'format' => FORMAT_MOODLE, 324 'files' => array(), 325 ), 326 1 => array( 327 'text' => 'red', 328 'format' => FORMAT_MOODLE, 329 'files' => array(), 330 ), 331 2 => array( 332 'text' => 'blue', 333 'format' => FORMAT_PLAIN, 334 'files' => array(), 335 ), 336 ), 337 'fraction' => array(1, 0, 0), 338 'feedback' => array( 339 0 => array( 340 'text' => 'right; good!', 341 'format' => FORMAT_MOODLE, 342 'files' => array(), 343 ), 344 1 => array( 345 'text' => "wrong, it's yellow", 346 'format' => FORMAT_HTML, 347 'files' => array(), 348 ), 349 2 => array( 350 'text' => "wrong, it's yellow", 351 'format' => FORMAT_MOODLE, 352 'files' => array(), 353 ), 354 ), 355 ); 356 357 // Repeated test for better failure messages. 358 $this->assertEquals($expectedq->answer, $q->answer); 359 $this->assertEquals($expectedq->feedback, $q->feedback); 360 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 361 } 362 363 /** 364 * Return a list of numbering styles (see question/type/multichoice/questiontype.php 365 * for valid choices) 366 * 367 * @return array Array of 1-element arrays of qtype_multichoice numbering styles 368 */ 369 public function numberingstyle_provider() { 370 return [ 371 ['abc'], 372 ['ABCD'], 373 ['123'], 374 ['iii'], 375 ['IIII'], 376 ['none'] 377 ]; 378 } 379 380 public function test_import_multichoice_multi() { 381 $gift = " 382 // multiple choice, multiple response with specified feedback for right and wrong answers 383 ::colours:: What's between orange and green in the spectrum? 384 { 385 ~%50%yellow # right; good! 386 ~%-100%red # [html]wrong 387 ~%50%off-beige # right; good! 388 ~%-100%[plain]blue # wrong 389 }"; 390 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 391 392 $importer = new qformat_gift(); 393 $q = $importer->readquestion($lines); 394 395 $expectedq = (object) array( 396 'name' => 'colours', 397 'questiontext' => "What's between orange and green in the spectrum?", 398 'questiontextformat' => FORMAT_MOODLE, 399 'generalfeedback' => '', 400 'generalfeedbackformat' => FORMAT_MOODLE, 401 'qtype' => 'multichoice', 402 'defaultmark' => 1, 403 'penalty' => 0.3333333, 404 'length' => 1, 405 'single' => 0, 406 'shuffleanswers' => '1', 407 'answernumbering' => 'abc', 408 'correctfeedback' => array( 409 'text' => '', 410 'format' => FORMAT_MOODLE, 411 'files' => array(), 412 ), 413 'partiallycorrectfeedback' => array( 414 'text' => '', 415 'format' => FORMAT_MOODLE, 416 'files' => array(), 417 ), 418 'incorrectfeedback' => array( 419 'text' => '', 420 'format' => FORMAT_MOODLE, 421 'files' => array(), 422 ), 423 'answer' => array( 424 0 => array( 425 'text' => 'yellow', 426 'format' => FORMAT_MOODLE, 427 'files' => array(), 428 ), 429 1 => array( 430 'text' => 'red', 431 'format' => FORMAT_MOODLE, 432 'files' => array(), 433 ), 434 2 => array( 435 'text' => 'off-beige', 436 'format' => FORMAT_MOODLE, 437 'files' => array(), 438 ), 439 3 => array( 440 'text' => 'blue', 441 'format' => FORMAT_PLAIN, 442 'files' => array(), 443 ), 444 ), 445 'fraction' => array(0.5, -1, 0.5, -1), 446 'feedback' => array( 447 0 => array( 448 'text' => 'right; good!', 449 'format' => FORMAT_MOODLE, 450 'files' => array(), 451 ), 452 1 => array( 453 'text' => "wrong", 454 'format' => FORMAT_HTML, 455 'files' => array(), 456 ), 457 2 => array( 458 'text' => "right; good!", 459 'format' => FORMAT_MOODLE, 460 'files' => array(), 461 ), 462 3 => array( 463 'text' => "wrong", 464 'format' => FORMAT_MOODLE, 465 'files' => array(), 466 ), 467 ), 468 ); 469 470 // Repeated test for better failure messages. 471 $this->assertEquals($expectedq->answer, $q->answer); 472 $this->assertEquals($expectedq->feedback, $q->feedback); 473 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 474 } 475 476 public function test_import_multichoice_multi_tricky() { 477 $gift = " 478 // multiple choice, multiple response with specified feedback for right and wrong answers 479 ::colours:: What's between orange and green in the spectrum? 480 { 481 ~%100%yellow # right; good! 482 ~%-50%red # wrong 483 ~%-50%blue # wrong 484 }"; 485 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 486 487 $importer = new qformat_gift(); 488 $q = $importer->readquestion($lines); 489 490 $expectedq = (object) array( 491 'name' => 'colours', 492 'questiontext' => "What's between orange and green in the spectrum?", 493 'questiontextformat' => FORMAT_MOODLE, 494 'generalfeedback' => '', 495 'generalfeedbackformat' => FORMAT_MOODLE, 496 'qtype' => 'multichoice', 497 'defaultmark' => 1, 498 'penalty' => 0.3333333, 499 'length' => 1, 500 'single' => 0, 501 'shuffleanswers' => '1', 502 'answernumbering' => 'abc', 503 'correctfeedback' => array( 504 'text' => '', 505 'format' => FORMAT_MOODLE, 506 'files' => array(), 507 ), 508 'partiallycorrectfeedback' => array( 509 'text' => '', 510 'format' => FORMAT_MOODLE, 511 'files' => array(), 512 ), 513 'incorrectfeedback' => array( 514 'text' => '', 515 'format' => FORMAT_MOODLE, 516 'files' => array(), 517 ), 518 'answer' => array( 519 0 => array( 520 'text' => 'yellow', 521 'format' => FORMAT_MOODLE, 522 'files' => array(), 523 ), 524 1 => array( 525 'text' => 'red', 526 'format' => FORMAT_MOODLE, 527 'files' => array(), 528 ), 529 2 => array( 530 'text' => 'blue', 531 'format' => FORMAT_MOODLE, 532 'files' => array(), 533 ), 534 ), 535 'fraction' => array(1, -0.5, -0.5), 536 'feedback' => array( 537 0 => array( 538 'text' => 'right; good!', 539 'format' => FORMAT_MOODLE, 540 'files' => array(), 541 ), 542 1 => array( 543 'text' => "wrong", 544 'format' => FORMAT_MOODLE, 545 'files' => array(), 546 ), 547 2 => array( 548 'text' => "wrong", 549 'format' => FORMAT_MOODLE, 550 'files' => array(), 551 ), 552 ), 553 ); 554 555 // Repeated test for better failure messages. 556 $this->assertEquals($expectedq->answer, $q->answer); 557 $this->assertEquals($expectedq->feedback, $q->feedback); 558 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 559 } 560 561 public function test_export_multichoice() { 562 $qdata = (object) array( 563 'id' => 666 , 564 'name' => 'Q8', 565 'questiontext' => "What's between orange and green in the spectrum?", 566 'questiontextformat' => FORMAT_MOODLE, 567 'generalfeedback' => '', 568 'generalfeedbackformat' => FORMAT_MOODLE, 569 'defaultmark' => 1, 570 'penalty' => 0.3333333, 571 'length' => 1, 572 'qtype' => 'multichoice', 573 'options' => (object) array( 574 'single' => 1, 575 'shuffleanswers' => '1', 576 'answernumbering' => 'abc', 577 'correctfeedback' => '', 578 'correctfeedbackformat' => FORMAT_MOODLE, 579 'partiallycorrectfeedback' => '', 580 'partiallycorrectfeedbackformat' => FORMAT_MOODLE, 581 'incorrectfeedback' => '', 582 'incorrectfeedbackformat' => FORMAT_MOODLE, 583 'answers' => array( 584 123 => (object) array( 585 'id' => 123, 586 'answer' => 'yellow', 587 'answerformat' => FORMAT_MOODLE, 588 'fraction' => 1, 589 'feedback' => 'right; good!', 590 'feedbackformat' => FORMAT_MOODLE, 591 ), 592 124 => (object) array( 593 'id' => 124, 594 'answer' => 'red', 595 'answerformat' => FORMAT_MOODLE, 596 'fraction' => 0, 597 'feedback' => "wrong, it's yellow", 598 'feedbackformat' => FORMAT_HTML, 599 ), 600 125 => (object) array( 601 'id' => 125, 602 'answer' => 'blue', 603 'answerformat' => FORMAT_PLAIN, 604 'fraction' => 0, 605 'feedback' => "wrong, it's yellow", 606 'feedbackformat' => FORMAT_MOODLE, 607 ), 608 ), 609 ), 610 ); 611 612 $exporter = new qformat_gift(); 613 $gift = $exporter->writequestion($qdata); 614 615 $expectedgift = "// question: 666 name: Q8 616 ::Q8::What's between orange and green in the spectrum?{ 617 \t=yellow#right; good! 618 \t~red#[html]wrong, it's yellow 619 \t~[plain]blue#wrong, it's yellow 620 } 621 622 "; 623 624 $this->assert_same_gift($expectedgift, $gift); 625 } 626 627 public function test_export_multichoice_multi_tricky() { 628 $qdata = (object) array( 629 'id' => 666 , 630 'name' => 'Q8', 631 'questiontext' => "What's between orange and green in the spectrum?", 632 'questiontextformat' => FORMAT_MOODLE, 633 'generalfeedback' => '', 634 'generalfeedbackformat' => FORMAT_MOODLE, 635 'defaultmark' => 1, 636 'penalty' => 0.3333333, 637 'length' => 1, 638 'qtype' => 'multichoice', 639 'options' => (object) array( 640 'single' => 0, 641 'shuffleanswers' => '1', 642 'answernumbering' => 'abc', 643 'correctfeedback' => '', 644 'correctfeedbackformat' => FORMAT_MOODLE, 645 'partiallycorrectfeedback' => '', 646 'partiallycorrectfeedbackformat' => FORMAT_MOODLE, 647 'incorrectfeedback' => '', 648 'incorrectfeedbackformat' => FORMAT_MOODLE, 649 'answers' => array( 650 123 => (object) array( 651 'id' => 123, 652 'answer' => 'yellow', 653 'answerformat' => FORMAT_MOODLE, 654 'fraction' => 1, 655 'feedback' => 'right; good!', 656 'feedbackformat' => FORMAT_MOODLE, 657 ), 658 124 => (object) array( 659 'id' => 124, 660 'answer' => 'red', 661 'answerformat' => FORMAT_MOODLE, 662 'fraction' => -0.5, 663 'feedback' => "wrong, it's yellow", 664 'feedbackformat' => FORMAT_MOODLE, 665 ), 666 125 => (object) array( 667 'id' => 125, 668 'answer' => 'blue', 669 'answerformat' => FORMAT_MOODLE, 670 'fraction' => -0.5, 671 'feedback' => "wrong, it's yellow", 672 'feedbackformat' => FORMAT_MOODLE, 673 ), 674 ), 675 ), 676 ); 677 678 $exporter = new qformat_gift(); 679 $gift = $exporter->writequestion($qdata); 680 681 $expectedgift = "// question: 666 name: Q8 682 ::Q8::What's between orange and green in the spectrum?{ 683 \t~%100%yellow#right; good! 684 \t~%-50%red#wrong, it's yellow 685 \t~%-50%blue#wrong, it's yellow 686 } 687 688 "; 689 690 $this->assert_same_gift($expectedgift, $gift); 691 } 692 693 public function test_import_numerical() { 694 $gift = " 695 // math range question 696 ::Q5:: What is a number from 1 to 5? {#3:2~#Completely wrong}"; 697 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 698 699 $importer = new qformat_gift(); 700 $q = $importer->readquestion($lines); 701 702 $expectedq = (object) array( 703 'name' => 'Q5', 704 'questiontext' => "What is a number from 1 to 5?", 705 'questiontextformat' => FORMAT_MOODLE, 706 'generalfeedback' => '', 707 'generalfeedbackformat' => FORMAT_MOODLE, 708 'qtype' => 'numerical', 709 'defaultmark' => 1, 710 'penalty' => 0.3333333, 711 'length' => 1, 712 'answer' => array( 713 '3', 714 '*', 715 ), 716 'fraction' => array(1, 0), 717 'feedback' => array( 718 0 => array( 719 'text' => '', 720 'format' => FORMAT_MOODLE, 721 'files' => array(), 722 ), 723 1 => array( 724 'text' => "Completely wrong", 725 'format' => FORMAT_MOODLE, 726 'files' => array(), 727 ), 728 ), 729 'tolerance' => array(2, ''), 730 ); 731 732 // Repeated test for better failure messages. 733 $this->assertEquals($expectedq->answer, $q->answer); 734 $this->assertEquals($expectedq->fraction, $q->fraction); 735 $this->assertEquals($expectedq->feedback, $q->feedback); 736 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 737 } 738 739 public function test_export_numerical() { 740 $qdata = (object) array( 741 'id' => 666 , 742 'name' => 'Q5', 743 'questiontext' => "What is a number from 1 to 5?", 744 'questiontextformat' => FORMAT_MOODLE, 745 'generalfeedback' => '', 746 'generalfeedbackformat' => FORMAT_MOODLE, 747 'defaultmark' => 1, 748 'penalty' => 1, 749 'length' => 1, 750 'qtype' => 'numerical', 751 'options' => (object) array( 752 'id' => 123, 753 'question' => 666, 754 'unitsleft' => 0, 755 'showunits' => 2, 756 'unitgradingtype' => 0, 757 'unitpenalty' => 0, 758 'answers' => array( 759 1 => (object) array( 760 'id' => 123, 761 'answer' => '3', 762 'answerformat' => 0, 763 'fraction' => 1, 764 'tolerance' => 2, 765 'feedback' => '', 766 'feedbackformat' => FORMAT_MOODLE, 767 ), 768 2 => (object) array( 769 'id' => 124, 770 'answer' => '*', 771 'answerformat' => 0, 772 'fraction' => 0, 773 'tolerance' => 0, 774 'feedback' => "Completely wrong", 775 'feedbackformat' => FORMAT_MOODLE, 776 ), 777 ), 778 ), 779 ); 780 781 $exporter = new qformat_gift(); 782 $gift = $exporter->writequestion($qdata); 783 784 $expectedgift = "// question: 666 name: Q5 785 ::Q5::What is a number from 1 to 5?{# 786 \t=%100%3:2# 787 \t~#Completely wrong 788 } 789 790 "; 791 792 $this->assert_same_gift($expectedgift, $gift); 793 } 794 795 public function test_import_shortanswer() { 796 $gift = " 797 // question: 666 name: Shortanswer 798 ::Shortanswer::Which is the best animal?{ 799 =Frog#Good! 800 =%50%Cat#What is it with Moodlers and cats? 801 =%0%*#Completely wrong 802 }"; 803 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 804 805 $importer = new qformat_gift(); 806 $q = $importer->readquestion($lines); 807 808 $expectedq = (object) array( 809 'name' => 'Shortanswer', 810 'questiontext' => "Which is the best animal?", 811 'questiontextformat' => FORMAT_MOODLE, 812 'generalfeedback' => '', 813 'generalfeedbackformat' => FORMAT_MOODLE, 814 'qtype' => 'shortanswer', 815 'defaultmark' => 1, 816 'penalty' => 0.3333333, 817 'length' => 1, 818 'answer' => array( 819 'Frog', 820 'Cat', 821 '*', 822 ), 823 'fraction' => array(1, 0.5, 0), 824 'feedback' => array( 825 0 => array( 826 'text' => 'Good!', 827 'format' => FORMAT_MOODLE, 828 'files' => array(), 829 ), 830 1 => array( 831 'text' => "What is it with Moodlers and cats?", 832 'format' => FORMAT_MOODLE, 833 'files' => array(), 834 ), 835 2 => array( 836 'text' => "Completely wrong", 837 'format' => FORMAT_MOODLE, 838 'files' => array(), 839 ), 840 ), 841 ); 842 843 // Repeated test for better failure messages. 844 $this->assertEquals($expectedq->answer, $q->answer); 845 $this->assertEquals($expectedq->fraction, $q->fraction); 846 $this->assertEquals($expectedq->feedback, $q->feedback); 847 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 848 } 849 850 public function test_import_shortanswer_with_general_feedback() { 851 $gift = " 852 // question: 666 name: Shortanswer 853 ::Shortanswer::Which is the best animal?{ 854 =Frog#Good! 855 =%50%Cat#What is it with Moodlers and cats? 856 =%0%*#Completely wrong 857 ####[html]Here is some general feedback! 858 }"; 859 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 860 861 $importer = new qformat_gift(); 862 $q = $importer->readquestion($lines); 863 864 $expectedq = (object) array( 865 'name' => 'Shortanswer', 866 'questiontext' => "Which is the best animal?", 867 'questiontextformat' => FORMAT_MOODLE, 868 'generalfeedback' => 'Here is some general feedback!', 869 'generalfeedbackformat' => FORMAT_HTML, 870 'qtype' => 'shortanswer', 871 'defaultmark' => 1, 872 'penalty' => 0.3333333, 873 'length' => 1, 874 'answer' => array( 875 'Frog', 876 'Cat', 877 '*', 878 ), 879 'fraction' => array(1, 0.5, 0), 880 'feedback' => array( 881 0 => array( 882 'text' => 'Good!', 883 'format' => FORMAT_MOODLE, 884 'files' => array(), 885 ), 886 1 => array( 887 'text' => "What is it with Moodlers and cats?", 888 'format' => FORMAT_MOODLE, 889 'files' => array(), 890 ), 891 2 => array( 892 'text' => "Completely wrong", 893 'format' => FORMAT_MOODLE, 894 'files' => array(), 895 ), 896 ), 897 ); 898 899 // Repeated test for better failure messages. 900 $this->assertEquals($expectedq->answer, $q->answer); 901 $this->assertEquals($expectedq->fraction, $q->fraction); 902 $this->assertEquals($expectedq->feedback, $q->feedback); 903 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 904 } 905 906 public function test_export_shortanswer() { 907 $qdata = (object) array( 908 'id' => 666 , 909 'name' => 'Shortanswer', 910 'questiontext' => "Which is the best animal?", 911 'questiontextformat' => FORMAT_MOODLE, 912 'generalfeedback' => '', 913 'generalfeedbackformat' => FORMAT_MOODLE, 914 'defaultmark' => 1, 915 'penalty' => 1, 916 'length' => 1, 917 'qtype' => 'shortanswer', 918 'options' => (object) array( 919 'id' => 123, 920 'questionid' => 666, 921 'usecase' => 1, 922 'answers' => array( 923 1 => (object) array( 924 'id' => 1, 925 'answer' => 'Frog', 926 'answerformat' => 0, 927 'fraction' => 1, 928 'feedback' => 'Good!', 929 'feedbackformat' => FORMAT_MOODLE, 930 ), 931 2 => (object) array( 932 'id' => 2, 933 'answer' => 'Cat', 934 'answerformat' => 0, 935 'fraction' => 0.5, 936 'feedback' => "What is it with Moodlers and cats?", 937 'feedbackformat' => FORMAT_MOODLE, 938 ), 939 3 => (object) array( 940 'id' => 3, 941 'answer' => '*', 942 'answerformat' => 0, 943 'fraction' => 0, 944 'feedback' => "Completely wrong", 945 'feedbackformat' => FORMAT_MOODLE, 946 ), 947 ), 948 ), 949 ); 950 951 $exporter = new qformat_gift(); 952 $gift = $exporter->writequestion($qdata); 953 954 $expectedgift = "// question: 666 name: Shortanswer 955 ::Shortanswer::Which is the best animal?{ 956 \t=%100%Frog#Good! 957 \t=%50%Cat#What is it with Moodlers and cats? 958 \t=%0%*#Completely wrong 959 } 960 961 "; 962 963 $this->assert_same_gift($expectedgift, $gift); 964 } 965 966 public function test_export_shortanswer_with_general_feedback() { 967 $qdata = (object) array( 968 'id' => 666 , 969 'name' => 'Shortanswer', 970 'questiontext' => "Which is the best animal?", 971 'questiontextformat' => FORMAT_MOODLE, 972 'generalfeedback' => 'Here is some general feedback!', 973 'generalfeedbackformat' => FORMAT_HTML, 974 'defaultmark' => 1, 975 'penalty' => 1, 976 'length' => 1, 977 'qtype' => 'shortanswer', 978 'options' => (object) array( 979 'id' => 123, 980 'questionid' => 666, 981 'usecase' => 1, 982 'answers' => array( 983 1 => (object) array( 984 'id' => 1, 985 'answer' => 'Frog', 986 'answerformat' => 0, 987 'fraction' => 1, 988 'feedback' => 'Good!', 989 'feedbackformat' => FORMAT_MOODLE, 990 ), 991 2 => (object) array( 992 'id' => 2, 993 'answer' => 'Cat', 994 'answerformat' => 0, 995 'fraction' => 0.5, 996 'feedback' => "What is it with Moodlers and cats?", 997 'feedbackformat' => FORMAT_MOODLE, 998 ), 999 3 => (object) array( 1000 'id' => 3, 1001 'answer' => '*', 1002 'answerformat' => 0, 1003 'fraction' => 0, 1004 'feedback' => "Completely wrong", 1005 'feedbackformat' => FORMAT_MOODLE, 1006 ), 1007 ), 1008 ), 1009 ); 1010 1011 $exporter = new qformat_gift(); 1012 $gift = $exporter->writequestion($qdata); 1013 1014 $expectedgift = "// question: 666 name: Shortanswer 1015 ::Shortanswer::Which is the best animal?{ 1016 \t=%100%Frog#Good! 1017 \t=%50%Cat#What is it with Moodlers and cats? 1018 \t=%0%*#Completely wrong 1019 \t####[html]Here is some general feedback! 1020 } 1021 1022 "; 1023 1024 $this->assert_same_gift($expectedgift, $gift); 1025 } 1026 1027 public function test_import_truefalse() { 1028 $gift = " 1029 // true/false 1030 ::Q1:: 42 is the Absolute Answer to everything.{ 1031 FALSE#42 is the Ultimate Answer.#You gave the right answer.}"; 1032 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 1033 1034 $importer = new qformat_gift(); 1035 $q = $importer->readquestion($lines); 1036 1037 $expectedq = (object) array( 1038 'name' => 'Q1', 1039 'questiontext' => "42 is the Absolute Answer to everything.", 1040 'questiontextformat' => FORMAT_MOODLE, 1041 'generalfeedback' => '', 1042 'generalfeedbackformat' => FORMAT_MOODLE, 1043 'qtype' => 'truefalse', 1044 'defaultmark' => 1, 1045 'penalty' => 1, 1046 'length' => 1, 1047 'correctanswer' => 0, 1048 'feedbacktrue' => array( 1049 'text' => '42 is the Ultimate Answer.', 1050 'format' => FORMAT_MOODLE, 1051 'files' => array(), 1052 ), 1053 'feedbackfalse' => array( 1054 'text' => 'You gave the right answer.', 1055 'format' => FORMAT_MOODLE, 1056 'files' => array(), 1057 ), 1058 ); 1059 1060 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 1061 } 1062 1063 public function test_import_truefalse_true_answer1() { 1064 $gift = "// name 0-11 1065 ::2-08 TSL::TSL is blablabla.{T}"; 1066 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 1067 1068 $importer = new qformat_gift(); 1069 $q = $importer->readquestion($lines); 1070 1071 $expectedq = (object) array( 1072 'name' => '2-08 TSL', 1073 'questiontext' => "TSL is blablabla.", 1074 'questiontextformat' => FORMAT_MOODLE, 1075 'generalfeedback' => '', 1076 'generalfeedbackformat' => FORMAT_MOODLE, 1077 'qtype' => 'truefalse', 1078 'defaultmark' => 1, 1079 'penalty' => 1, 1080 'length' => 1, 1081 'correctanswer' => 1, 1082 'feedbacktrue' => array( 1083 'text' => '', 1084 'format' => FORMAT_MOODLE, 1085 'files' => array(), 1086 ), 1087 'feedbackfalse' => array( 1088 'text' => '', 1089 'format' => FORMAT_MOODLE, 1090 'files' => array(), 1091 ), 1092 ); 1093 1094 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 1095 } 1096 1097 public function test_import_truefalse_true_answer2() { 1098 $gift = "// name 0-11 1099 ::2-08 TSL::TSL is blablabla.{TRUE}"; 1100 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 1101 1102 $importer = new qformat_gift(); 1103 $q = $importer->readquestion($lines); 1104 1105 $expectedq = (object) array( 1106 'name' => '2-08 TSL', 1107 'questiontext' => "TSL is blablabla.", 1108 'questiontextformat' => FORMAT_MOODLE, 1109 'generalfeedback' => '', 1110 'generalfeedbackformat' => FORMAT_MOODLE, 1111 'qtype' => 'truefalse', 1112 'defaultmark' => 1, 1113 'penalty' => 1, 1114 'length' => 1, 1115 'correctanswer' => 1, 1116 'feedbacktrue' => array( 1117 'text' => '', 1118 'format' => FORMAT_MOODLE, 1119 'files' => array(), 1120 ), 1121 'feedbackfalse' => array( 1122 'text' => '', 1123 'format' => FORMAT_MOODLE, 1124 'files' => array(), 1125 ), 1126 ); 1127 1128 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 1129 } 1130 1131 public function test_export_truefalse() { 1132 $qdata = (object) array( 1133 'id' => 666 , 1134 'name' => 'Q1', 1135 'questiontext' => "42 is the Absolute Answer to everything.", 1136 'questiontextformat' => FORMAT_MOODLE, 1137 'generalfeedback' => '', 1138 'generalfeedbackformat' => FORMAT_MOODLE, 1139 'defaultmark' => 1, 1140 'penalty' => 1, 1141 'length' => 1, 1142 'qtype' => 'truefalse', 1143 'options' => (object) array( 1144 'id' => 123, 1145 'question' => 666, 1146 'trueanswer' => 1, 1147 'falseanswer' => 2, 1148 'answers' => array( 1149 1 => (object) array( 1150 'id' => 123, 1151 'answer' => 'True', 1152 'answerformat' => 0, 1153 'fraction' => 1, 1154 'feedback' => 'You gave the right answer.', 1155 'feedbackformat' => FORMAT_MOODLE, 1156 ), 1157 2 => (object) array( 1158 'id' => 124, 1159 'answer' => 'False', 1160 'answerformat' => 0, 1161 'fraction' => 0, 1162 'feedback' => "42 is the Ultimate Answer.", 1163 'feedbackformat' => FORMAT_HTML, 1164 ), 1165 ), 1166 ), 1167 ); 1168 1169 $exporter = new qformat_gift(); 1170 $gift = $exporter->writequestion($qdata); 1171 1172 $expectedgift = "// question: 666 name: Q1 1173 ::Q1::42 is the Absolute Answer to everything.{TRUE#[html]42 is the Ultimate Answer.#You gave the right answer.} 1174 1175 "; 1176 1177 $this->assert_same_gift($expectedgift, $gift); 1178 } 1179 1180 public function test_export_backslash() { 1181 // There was a bug (MDL-34171) where \\ was getting exported as \\, not 1182 // \\\\, and on import, \\ in converted to \. 1183 // We need \\\\ in the test code, because of PHPs string escaping rules. 1184 $qdata = (object) array( 1185 'id' => 666 , 1186 'name' => 'backslash', 1187 'questiontext' => 'A \\ B \\\\ C', 1188 'questiontextformat' => FORMAT_MOODLE, 1189 'generalfeedback' => '', 1190 'generalfeedbackformat' => FORMAT_MOODLE, 1191 'defaultmark' => 1, 1192 'penalty' => 0.3333333, 1193 'length' => 1, 1194 'qtype' => 'essay', 1195 'options' => (object) array( 1196 'responseformat' => 'editor', 1197 'responsefieldlines' => 15, 1198 'attachments' => 0, 1199 'graderinfo' => '', 1200 'graderinfoformat' => FORMAT_HTML, 1201 ), 1202 ); 1203 1204 $exporter = new qformat_gift(); 1205 $gift = $exporter->writequestion($qdata); 1206 1207 $expectedgift = "// question: 666 name: backslash 1208 ::backslash::A \\\\ B \\\\\\\\ C{} 1209 1210 "; 1211 1212 $this->assert_same_gift($expectedgift, $gift); 1213 } 1214 1215 public function test_import_backslash() { 1216 // There was a bug (MDL-34171) where \\ in the import was getting changed 1217 // to \. This test checks for that. 1218 // We need \\\\ in the test code, because of PHPs string escaping rules. 1219 $gift = ' 1220 // essay 1221 ::double backslash:: A \\\\ B \\\\\\\\ C{}'; 1222 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 1223 1224 $importer = new qformat_gift(); 1225 $q = $importer->readquestion($lines); 1226 1227 $expectedq = (object) array( 1228 'name' => 'double backslash', 1229 'questiontext' => 'A \\ B \\\\ C', 1230 'questiontextformat' => FORMAT_MOODLE, 1231 'generalfeedback' => '', 1232 'generalfeedbackformat' => FORMAT_MOODLE, 1233 'qtype' => 'essay', 1234 'defaultmark' => 1, 1235 'penalty' => 0.3333333, 1236 'length' => 1, 1237 'responseformat' => 'editor', 1238 'responsefieldlines' => 15, 1239 'attachments' => 0, 1240 'graderinfo' => array( 1241 'text' => '', 1242 'format' => FORMAT_HTML, 1243 'files' => array()), 1244 ); 1245 1246 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 1247 } 1248 1249 public function test_import_pre_content() { 1250 $gift = ' 1251 ::Q001::[html]<p>What would running the test method print?</p> 1252 <pre> 1253 public void test() \{ 1254 method1(); 1255 method2(); 1256 method3(); 1257 \} 1258 </pre> 1259 {}'; 1260 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 1261 1262 $importer = new qformat_gift(); 1263 $q = $importer->readquestion($lines); 1264 1265 $expectedq = (object) array( 1266 'name' => 'Q001', 1267 'questiontext' => '<p>What would running the test method print?</p> 1268 <pre> 1269 public void test() { 1270 method1(); 1271 method2(); 1272 method3(); 1273 } 1274 </pre>', 1275 'questiontextformat' => FORMAT_HTML, 1276 'generalfeedback' => '', 1277 'generalfeedbackformat' => FORMAT_HTML, 1278 'qtype' => 'essay', 1279 'defaultmark' => 1, 1280 'penalty' => 0.3333333, 1281 'length' => 1, 1282 'responseformat' => 'editor', 1283 'responsefieldlines' => 15, 1284 'attachments' => 0, 1285 'graderinfo' => array( 1286 'text' => '', 1287 'format' => FORMAT_HTML, 1288 'files' => array()), 1289 ); 1290 1291 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 1292 } 1293 1294 public function test_import_question_with_tags() { 1295 $gift = ' 1296 // This question is to test importing tags: [tag:tag] [tag:other-tag]. 1297 // And an idnumber: [id:myid]. 1298 ::Question name:: How are you? {}'; 1299 $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift)); 1300 1301 $importer = new qformat_gift(); 1302 $q = $importer->readquestion($lines); 1303 1304 $expectedq = (object) array( 1305 'name' => 'Question name', 1306 'questiontext' => 'How are you?', 1307 'questiontextformat' => FORMAT_MOODLE, 1308 'generalfeedback' => '', 1309 'generalfeedbackformat' => FORMAT_MOODLE, 1310 'qtype' => 'essay', 1311 'defaultmark' => 1, 1312 'penalty' => 0.3333333, 1313 'length' => 1, 1314 'responseformat' => 'editor', 1315 'responsefieldlines' => 15, 1316 'attachments' => 0, 1317 'graderinfo' => array( 1318 'text' => '', 1319 'format' => FORMAT_HTML, 1320 'files' => array()), 1321 'tags' => ['tag', 'other-tag'], 1322 'idnumber' => 'myid', 1323 ); 1324 1325 $this->assert(new question_check_specified_fields_expectation($expectedq), $q); 1326 } 1327 1328 /** 1329 * Data provider for test_extract_idnumber_and_tags_from_comment. 1330 * 1331 * @return array the test cases. 1332 */ 1333 public function extract_idnumber_and_tags_from_comment_testcases() { 1334 return [ 1335 'blank comment' => ['', [], ''], 1336 'nothing in comment' => ['', [], '// A basic comment.'], 1337 'idnumber only' => ['frog', [], '// A comment with [id:frog] <-- an idnumber.'], 1338 'tags only' => ['', ['frog', 'toad'], '// Look tags: [tag:frog] [tag:toad].'], 1339 'everything' => ['four', ['add', 'basic'], '// [tag:add] [tag:basic] [id:four]'], 1340 'everything mixed up' => ['four', ['basic', 'add'], 1341 "// [tag: basic] Here is \n// a [id: four ] que[tag:add ]stion."], 1342 'split over line' => ['', [], "// Ceci n\'est pas une [tag:\n\\ frog]."], 1343 'escape ] idnumber' => ['i]d', [], '// [id:i\]d].'], 1344 'escape ] tag' => ['', ['t]ag'], '// [tag:t\]ag].'], 1345 ]; 1346 } 1347 1348 /** 1349 * Test extract_idnumber_and_tags_from_comment. 1350 * 1351 * @dataProvider extract_idnumber_and_tags_from_comment_testcases 1352 * @param string $expectedidnumber the expected idnumber. 1353 * @param array $expectedtags the expected tags. 1354 * @param string $comment the comment to parse. 1355 */ 1356 public function test_extract_idnumber_and_tags_from_comment( 1357 string $expectedidnumber, array $expectedtags, string $comment) { 1358 $importer = new qformat_gift(); 1359 1360 list($idnumber, $tags) = $importer->extract_idnumber_and_tags_from_comment($comment); 1361 $this->assertSame($expectedidnumber, $idnumber); 1362 $this->assertSame($expectedtags, $tags); 1363 } 1364 1365 public function test_export_question_with_tags_and_idnumber() { 1366 $this->resetAfterTest(); 1367 1368 // Create a question with tags. 1369 $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); 1370 $category = $generator->create_question_category(); 1371 $question = $generator->create_question('truefalse', null, 1372 ['category' => $category->id, 'idnumber' => 'myid']); 1373 \core_tag_tag::set_item_tags('core_question', 'question', $question->id, 1374 \context::instance_by_id($category->contextid), ['tag1', 'tag2'], 0); 1375 1376 // Export it. 1377 $questiondata = question_bank::load_question_data($question->id); 1378 $exporter = new qformat_gift(); 1379 $exporter->course = get_course(SITEID); 1380 $gift = $exporter->writequestion($questiondata); 1381 1382 // Verify. 1383 $expectedgift = "// question: {$question->id} name: True/false question 1384 // [id:myid] [tag:tag1] [tag:tag2] 1385 ::True/false question::[html]The answer is true.{TRUE#This is the wrong answer.#This is the right answer.####You should have selected true.} 1386 1387 "; 1388 1389 $this->assert_same_gift($expectedgift, $gift); 1390 } 1391 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body