2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Unit tests for workshop api class defined in mod/workshop/locallib.php
20 * @package mod_workshop
22 * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') || die();
29 require_once($CFG->dirroot . '/mod/workshop/locallib.php'); // Include the code to test
30 require_once(__DIR__ . '/fixtures/testable.php');
34 * Test cases for the internal workshop api
36 class mod_workshop_internal_api_testcase extends advanced_testcase {
44 /** setup testing environment */
45 protected function setUp() {
47 $this->setAdminUser();
48 $this->course = $this->getDataGenerator()->create_course();
49 $workshop = $this->getDataGenerator()->create_module('workshop', array('course' => $this->course));
50 $cm = get_coursemodule_from_instance('workshop', $workshop->id, $this->course->id, false, MUST_EXIST);
51 $this->workshop = new testable_workshop($workshop, $cm, $this->course);
54 protected function tearDown() {
55 $this->workshop = null;
59 public function test_aggregate_submission_grades_process_notgraded() {
60 $this->resetAfterTest(true);
63 $batch = array(); // batch of a submission's assessments
64 $batch[] = (object)array('submissionid' => 12, 'submissiongrade' => null, 'weight' => 1, 'grade' => null);
65 //$DB->expectNever('update_record');
67 $this->workshop->aggregate_submission_grades_process($batch);
70 public function test_aggregate_submission_grades_process_single() {
71 $this->resetAfterTest(true);
74 $batch = array(); // batch of a submission's assessments
75 $batch[] = (object)array('submissionid' => 12, 'submissiongrade' => null, 'weight' => 1, 'grade' => 10.12345);
77 //$DB->expectOnce('update_record');
79 $this->workshop->aggregate_submission_grades_process($batch);
82 public function test_aggregate_submission_grades_process_null_doesnt_influence() {
83 $this->resetAfterTest(true);
86 $batch = array(); // batch of a submission's assessments
87 $batch[] = (object)array('submissionid' => 12, 'submissiongrade' => null, 'weight' => 1, 'grade' => 45.54321);
88 $batch[] = (object)array('submissionid' => 12, 'submissiongrade' => null, 'weight' => 1, 'grade' => null);
90 //$DB->expectOnce('update_record');
92 $this->workshop->aggregate_submission_grades_process($batch);
95 public function test_aggregate_submission_grades_process_weighted_single() {
96 $this->resetAfterTest(true);
99 $batch = array(); // batch of a submission's assessments
100 $batch[] = (object)array('submissionid' => 12, 'submissiongrade' => null, 'weight' => 4, 'grade' => 14.00012);
101 $expected = 14.00012;
102 //$DB->expectOnce('update_record');
104 $this->workshop->aggregate_submission_grades_process($batch);
107 public function test_aggregate_submission_grades_process_mean() {
108 $this->resetAfterTest(true);
111 $batch = array(); // batch of a submission's assessments
112 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 56.12000);
113 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 12.59000);
114 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 10.00000);
115 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 0.00000);
116 $expected = 19.67750;
117 //$DB->expectOnce('update_record');
119 $this->workshop->aggregate_submission_grades_process($batch);
122 public function test_aggregate_submission_grades_process_mean_changed() {
123 $this->resetAfterTest(true);
126 $batch = array(); // batch of a submission's assessments
127 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 12.57750, 'weight' => 1, 'grade' => 56.12000);
128 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 12.57750, 'weight' => 1, 'grade' => 12.59000);
129 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 12.57750, 'weight' => 1, 'grade' => 10.00000);
130 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 12.57750, 'weight' => 1, 'grade' => 0.00000);
131 $expected = 19.67750;
132 //$DB->expectOnce('update_record');
134 $this->workshop->aggregate_submission_grades_process($batch);
137 public function test_aggregate_submission_grades_process_mean_nochange() {
138 $this->resetAfterTest(true);
141 $batch = array(); // batch of a submission's assessments
142 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 19.67750, 'weight' => 1, 'grade' => 56.12000);
143 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 19.67750, 'weight' => 1, 'grade' => 12.59000);
144 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 19.67750, 'weight' => 1, 'grade' => 10.00000);
145 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => 19.67750, 'weight' => 1, 'grade' => 0.00000);
146 //$DB->expectNever('update_record');
148 $this->workshop->aggregate_submission_grades_process($batch);
151 public function test_aggregate_submission_grades_process_rounding() {
152 $this->resetAfterTest(true);
155 $batch = array(); // batch of a submission's assessments
156 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 4.00000);
157 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 2.00000);
158 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 1.00000);
160 //$DB->expectOnce('update_record');
162 $this->workshop->aggregate_submission_grades_process($batch);
165 public function test_aggregate_submission_grades_process_weighted_mean() {
166 $this->resetAfterTest(true);
169 $batch = array(); // batch of a submission's assessments
170 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 3, 'grade' => 12.00000);
171 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 2, 'grade' => 30.00000);
172 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 1, 'grade' => 10.00000);
173 $batch[] = (object)array('submissionid' => 45, 'submissiongrade' => null, 'weight' => 0, 'grade' => 1000.00000);
174 $expected = 17.66667;
175 //$DB->expectOnce('update_record');
177 $this->workshop->aggregate_submission_grades_process($batch);
180 public function test_aggregate_grading_grades_process_nograding() {
181 $this->resetAfterTest(true);
184 $batch[] = (object)array('reviewerid'=>2, 'gradinggrade'=>null, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null);
186 //$DB->expectNever('update_record');
188 $this->workshop->aggregate_grading_grades_process($batch);
191 public function test_aggregate_grading_grades_process_single_grade_new() {
192 $this->resetAfterTest(true);
195 $batch[] = (object)array('reviewerid'=>3, 'gradinggrade'=>82.87670, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null);
198 $expected = new stdclass();
199 $expected->workshopid = $this->workshop->id;
200 $expected->userid = 3;
201 $expected->gradinggrade = 82.87670;
202 $expected->timegraded = $now;
203 //$DB->expectOnce('insert_record', array('workshop_aggregations', $expected));
205 $this->workshop->aggregate_grading_grades_process($batch, $now);
208 public function test_aggregate_grading_grades_process_single_grade_update() {
209 $this->resetAfterTest(true);
212 $batch[] = (object)array('reviewerid'=>3, 'gradinggrade'=>90.00000, 'gradinggradeover'=>null, 'aggregationid'=>1, 'aggregatedgrade'=>82.87670);
214 //$DB->expectOnce('update_record');
216 $this->workshop->aggregate_grading_grades_process($batch);
219 public function test_aggregate_grading_grades_process_single_grade_uptodate() {
220 $this->resetAfterTest(true);
223 $batch[] = (object)array('reviewerid'=>3, 'gradinggrade'=>90.00000, 'gradinggradeover'=>null, 'aggregationid'=>1, 'aggregatedgrade'=>90.00000);
225 //$DB->expectNever('update_record');
227 $this->workshop->aggregate_grading_grades_process($batch);
230 public function test_aggregate_grading_grades_process_single_grade_overridden() {
231 $this->resetAfterTest(true);
234 $batch[] = (object)array('reviewerid'=>4, 'gradinggrade'=>91.56700, 'gradinggradeover'=>82.32105, 'aggregationid'=>2, 'aggregatedgrade'=>91.56700);
236 //$DB->expectOnce('update_record');
238 $this->workshop->aggregate_grading_grades_process($batch);
241 public function test_aggregate_grading_grades_process_multiple_grades_new() {
242 $this->resetAfterTest(true);
245 $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>99.45670, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null);
246 $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>87.34311, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null);
247 $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>51.12000, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null);
250 $expected = new stdclass();
251 $expected->workshopid = $this->workshop->id;
252 $expected->userid = 5;
253 $expected->gradinggrade = 79.3066;
254 $expected->timegraded = $now;
255 //$DB->expectOnce('insert_record', array('workshop_aggregations', $expected));
257 $this->workshop->aggregate_grading_grades_process($batch, $now);
260 public function test_aggregate_grading_grades_process_multiple_grades_update() {
261 $this->resetAfterTest(true);
264 $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>56.23400, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>79.30660);
265 $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>87.34311, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>79.30660);
266 $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>51.12000, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>79.30660);
268 //$DB->expectOnce('update_record');
270 $this->workshop->aggregate_grading_grades_process($batch);
273 public function test_aggregate_grading_grades_process_multiple_grades_overriden() {
274 $this->resetAfterTest(true);
277 $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>56.23400, 'gradinggradeover'=>99.45670, 'aggregationid'=>2, 'aggregatedgrade'=>64.89904);
278 $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>87.34311, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>64.89904);
279 $batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>51.12000, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>64.89904);
281 //$DB->expectOnce('update_record');
283 $this->workshop->aggregate_grading_grades_process($batch);
286 public function test_aggregate_grading_grades_process_multiple_grades_one_missing() {
287 $this->resetAfterTest(true);
290 $batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>50.00000, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000);
291 $batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>null, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000);
292 $batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>52.20000, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000);
294 //$DB->expectOnce('update_record');
296 $this->workshop->aggregate_grading_grades_process($batch);
299 public function test_aggregate_grading_grades_process_multiple_grades_missing_overridden() {
300 $this->resetAfterTest(true);
303 $batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>50.00000, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000);
304 $batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>null, 'gradinggradeover'=>69.00000, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000);
305 $batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>52.20000, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000);
307 //$DB->expectOnce('update_record');
309 $this->workshop->aggregate_grading_grades_process($batch);
312 public function test_percent_to_value() {
313 $this->resetAfterTest(true);
318 $part = workshop::percent_to_value($percent, $total);
320 $this->assertEquals($part, $total * $percent / 100);
324 * @expectedException coding_exception
326 public function test_percent_to_value_negative() {
327 $this->resetAfterTest(true);
333 $part = workshop::percent_to_value($percent, $total);
337 * @expectedException coding_exception
339 public function test_percent_to_value_over_hundred() {
340 $this->resetAfterTest(true);
346 $part = workshop::percent_to_value($percent, $total);
349 public function test_lcm() {
350 $this->resetAfterTest(true);
351 // fixture setup + exercise SUT + verify in one step
352 $this->assertEquals(workshop::lcm(1,4), 4);
353 $this->assertEquals(workshop::lcm(2,4), 4);
354 $this->assertEquals(workshop::lcm(4,2), 4);
355 $this->assertEquals(workshop::lcm(2,3), 6);
356 $this->assertEquals(workshop::lcm(6,4), 12);
359 public function test_lcm_array() {
360 $this->resetAfterTest(true);
362 $numbers = array(5,3,15);
364 $lcm = array_reduce($numbers, 'workshop::lcm', 1);
366 $this->assertEquals($lcm, 15);
369 public function test_prepare_example_assessment() {
370 $this->resetAfterTest(true);
372 $fakerawrecord = (object)array(
374 'submissionid' => 56,
376 'timecreated' => time() - 10,
377 'timemodified' => time() - 5,
379 'gradinggrade' => null,
380 'gradinggradeover' => null,
381 'feedbackauthor' => null,
382 'feedbackauthorformat' => 0,
383 'feedbackauthorattachment' => 0,
386 $a = $this->workshop->prepare_example_assessment($fakerawrecord);
388 $this->assertTrue($a instanceof workshop_example_assessment);
389 $this->assertTrue($a->url instanceof moodle_url);
392 $fakerawrecord->weight = 1;
393 $this->expectException('coding_exception');
395 $a = $this->workshop->prepare_example_assessment($fakerawrecord);
398 public function test_prepare_example_reference_assessment() {
400 $this->resetAfterTest(true);
402 $fakerawrecord = (object)array(
404 'submissionid' => 56,
406 'timecreated' => time() - 100,
407 'timemodified' => time() - 50,
409 'gradinggrade' => 1.00000,
410 'gradinggradeover' => null,
411 'feedbackauthor' => null,
412 'feedbackauthorformat' => 0,
413 'feedbackauthorattachment' => 0,
416 $a = $this->workshop->prepare_example_reference_assessment($fakerawrecord);
418 $this->assertTrue($a instanceof workshop_example_reference_assessment);
421 $fakerawrecord->weight = 0;
422 $this->expectException('coding_exception');
424 $a = $this->workshop->prepare_example_reference_assessment($fakerawrecord);
428 * Tests user restrictions, as they affect lists of users returned by
429 * core API functions.
431 * This includes the groupingid option (when group mode is in use), and
432 * standard activity restrictions using the availability API.
434 public function test_user_restrictions() {
437 $this->resetAfterTest();
439 // Use existing sample course from setUp.
440 $courseid = $this->workshop->course->id;
442 // Make a test grouping and two groups.
443 $generator = $this->getDataGenerator();
444 $grouping = $generator->create_grouping(array('courseid' => $courseid));
445 $group1 = $generator->create_group(array('courseid' => $courseid));
446 groups_assign_grouping($grouping->id, $group1->id);
447 $group2 = $generator->create_group(array('courseid' => $courseid));
448 groups_assign_grouping($grouping->id, $group2->id);
450 // Group 3 is not in the grouping.
451 $group3 = $generator->create_group(array('courseid' => $courseid));
453 // Enrol some students.
454 $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
455 $student1 = $generator->create_user();
456 $student2 = $generator->create_user();
457 $student3 = $generator->create_user();
458 $generator->enrol_user($student1->id, $courseid, $roleids['student']);
459 $generator->enrol_user($student2->id, $courseid, $roleids['student']);
460 $generator->enrol_user($student3->id, $courseid, $roleids['student']);
462 // Place students in groups (except student 3).
463 groups_add_member($group1, $student1);
464 groups_add_member($group2, $student2);
465 groups_add_member($group3, $student3);
467 // The existing workshop doesn't have any restrictions, so user lists
468 // should include all three users.
469 $allusers = get_enrolled_users(context_course::instance($courseid));
470 $result = $this->workshop->get_grouped($allusers);
471 $this->assertCount(4, $result);
472 $users = array_keys($result[0]);
474 $this->assertEquals(array($student1->id, $student2->id, $student3->id), $users);
475 $this->assertEquals(array($student1->id), array_keys($result[$group1->id]));
476 $this->assertEquals(array($student2->id), array_keys($result[$group2->id]));
477 $this->assertEquals(array($student3->id), array_keys($result[$group3->id]));
479 // Test get_users_with_capability_sql (via get_potential_authors).
480 $users = $this->workshop->get_potential_authors(false);
481 $this->assertCount(3, $users);
482 $users = $this->workshop->get_potential_authors(false, $group2->id);
483 $this->assertEquals(array($student2->id), array_keys($users));
485 // Create another test workshop with grouping set.
486 $workshopitem = $this->getDataGenerator()->create_module('workshop',
487 array('course' => $courseid, 'groupmode' => SEPARATEGROUPS,
488 'groupingid' => $grouping->id));
489 $cm = get_coursemodule_from_instance('workshop', $workshopitem->id,
490 $courseid, false, MUST_EXIST);
491 $workshopgrouping = new testable_workshop($workshopitem, $cm, $this->workshop->course);
493 // This time the result should only include users and groups in the
494 // selected grouping.
495 $result = $workshopgrouping->get_grouped($allusers);
496 $this->assertCount(3, $result);
497 $users = array_keys($result[0]);
499 $this->assertEquals(array($student1->id, $student2->id), $users);
500 $this->assertEquals(array($student1->id), array_keys($result[$group1->id]));
501 $this->assertEquals(array($student2->id), array_keys($result[$group2->id]));
503 // Test get_users_with_capability_sql (via get_potential_authors).
504 $users = $workshopgrouping->get_potential_authors(false);
505 $userids = array_keys($users);
507 $this->assertEquals(array($student1->id, $student2->id), $userids);
508 $users = $workshopgrouping->get_potential_authors(false, $group2->id);
509 $this->assertEquals(array($student2->id), array_keys($users));
511 // Enable the availability system and create another test workshop with
512 // availability restriction on grouping.
513 $CFG->enableavailability = true;
514 $workshopitem = $this->getDataGenerator()->create_module('workshop',
515 array('course' => $courseid, 'availability' => json_encode(
516 \core_availability\tree::get_root_json(array(
517 \availability_grouping\condition::get_json($grouping->id)),
518 \core_availability\tree::OP_AND, false))));
519 $cm = get_coursemodule_from_instance('workshop', $workshopitem->id,
520 $courseid, false, MUST_EXIST);
521 $workshoprestricted = new testable_workshop($workshopitem, $cm, $this->workshop->course);
523 // The get_grouped function isn't intended to apply this restriction,
524 // so it should be the same as the base workshop. (Note: in reality,
525 // get_grouped is always run with the parameter being the result of
526 // one of the get_potential_xxx functions, so it works.)
527 $result = $workshoprestricted->get_grouped($allusers);
528 $this->assertCount(4, $result);
529 $this->assertCount(3, $result[0]);
531 // The get_users_with_capability_sql-based functions should apply it.
532 $users = $workshoprestricted->get_potential_authors(false);
533 $userids = array_keys($users);
535 $this->assertEquals(array($student1->id, $student2->id), $userids);
536 $users = $workshoprestricted->get_potential_authors(false, $group2->id);
537 $this->assertEquals(array($student2->id), array_keys($users));
541 * Test the workshop reset feature.
543 public function test_reset_phase() {
544 $this->resetAfterTest(true);
546 $this->workshop->switch_phase(workshop::PHASE_CLOSED);
547 $this->assertEquals(workshop::PHASE_CLOSED, $this->workshop->phase);
549 $settings = (object)array(
550 'reset_workshop_phase' => 0,
552 $status = $this->workshop->reset_userdata($settings);
553 $this->assertEquals(workshop::PHASE_CLOSED, $this->workshop->phase);
555 $settings = (object)array(
556 'reset_workshop_phase' => 1,
558 $status = $this->workshop->reset_userdata($settings);
559 $this->assertEquals(workshop::PHASE_SETUP, $this->workshop->phase);
560 foreach ($status as $result) {
561 $this->assertFalse($result['error']);
566 * Test deleting assessments related data on workshop reset.
568 public function test_reset_userdata_assessments() {
570 $this->resetAfterTest(true);
572 $student1 = $this->getDataGenerator()->create_user();
573 $student2 = $this->getDataGenerator()->create_user();
575 $this->getDataGenerator()->enrol_user($student1->id, $this->workshop->course->id);
576 $this->getDataGenerator()->enrol_user($student2->id, $this->workshop->course->id);
578 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
580 $subid1 = $workshopgenerator->create_submission($this->workshop->id, $student1->id);
581 $subid2 = $workshopgenerator->create_submission($this->workshop->id, $student2->id);
583 $asid1 = $workshopgenerator->create_assessment($subid1, $student2->id);
584 $asid2 = $workshopgenerator->create_assessment($subid2, $student1->id);
586 $settings = (object)array(
587 'reset_workshop_assessments' => 1,
589 $status = $this->workshop->reset_userdata($settings);
591 foreach ($status as $result) {
592 $this->assertFalse($result['error']);
595 $this->assertEquals(2, $DB->count_records('workshop_submissions', array('workshopid' => $this->workshop->id)));
596 $this->assertEquals(0, $DB->count_records('workshop_assessments'));
600 * Test deleting submissions related data on workshop reset.
602 public function test_reset_userdata_submissions() {
604 $this->resetAfterTest(true);
606 $student1 = $this->getDataGenerator()->create_user();
607 $student2 = $this->getDataGenerator()->create_user();
609 $this->getDataGenerator()->enrol_user($student1->id, $this->workshop->course->id);
610 $this->getDataGenerator()->enrol_user($student2->id, $this->workshop->course->id);
612 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
614 $subid1 = $workshopgenerator->create_submission($this->workshop->id, $student1->id);
615 $subid2 = $workshopgenerator->create_submission($this->workshop->id, $student2->id);
617 $asid1 = $workshopgenerator->create_assessment($subid1, $student2->id);
618 $asid2 = $workshopgenerator->create_assessment($subid2, $student1->id);
620 $settings = (object)array(
621 'reset_workshop_submissions' => 1,
623 $status = $this->workshop->reset_userdata($settings);
625 foreach ($status as $result) {
626 $this->assertFalse($result['error']);
629 $this->assertEquals(0, $DB->count_records('workshop_submissions', array('workshopid' => $this->workshop->id)));
630 $this->assertEquals(0, $DB->count_records('workshop_assessments'));
634 * Test normalizing list of extensions.
636 public function test_normalize_file_extensions() {
637 $this->resetAfterTest(true);
639 $this->assertSame(['.odt'], workshop::normalize_file_extensions('odt'));
640 $this->assertSame(['.odt'], workshop::normalize_file_extensions('.odt'));
641 $this->assertSame(['.odt'], workshop::normalize_file_extensions('.ODT'));
642 $this->assertSame(['.doc', '.jpg', '.mp3'], workshop::normalize_file_extensions('doc, jpg, mp3'));
643 $this->assertSame(['.doc', '.jpg', '.mp3'], workshop::normalize_file_extensions(['.doc', '.jpg', '.mp3']));
644 $this->assertSame(['.doc', '.jpg', '.mp3'], workshop::normalize_file_extensions('doc, *.jpg, mp3'));
645 $this->assertSame(['.doc', '.jpg', '.mp3'], workshop::normalize_file_extensions(['doc ', ' JPG ', '.mp3']));
646 $this->assertSame(['.rtf', '.pdf', '.docx'], workshop::normalize_file_extensions("RTF,.pdf\n...DocX,,,;\rPDF\trtf ...Rtf"));
647 $this->assertSame(['.tgz', '.tar.gz'], workshop::normalize_file_extensions('tgz,TAR.GZ tar.gz .tar.gz tgz TGZ'));
648 $this->assertSame(['.notebook'], workshop::normalize_file_extensions('"Notebook":notebook;NOTEBOOK;,\'NoTeBook\''));
649 $this->assertSame([], workshop::normalize_file_extensions(''));
650 $this->assertSame([], workshop::normalize_file_extensions([]));
651 $this->assertSame(['.0'], workshop::normalize_file_extensions(0));
652 $this->assertSame(['.0'], workshop::normalize_file_extensions('0'));
653 $this->assertSame(['.odt'], workshop::normalize_file_extensions('*.odt'));
654 $this->assertSame([], workshop::normalize_file_extensions('.'));
655 $this->assertSame(['.foo'], workshop::normalize_file_extensions('. foo'));
656 $this->assertSame([], workshop::normalize_file_extensions('*'));
657 $this->assertSame([], workshop::normalize_file_extensions('*~'));
658 $this->assertSame(['.pdf', '.ps'], workshop::normalize_file_extensions('* pdf *.ps foo* *bar .r??'));
662 * Test cleaning list of extensions.
664 public function test_clean_file_extensions() {
665 $this->resetAfterTest(true);
667 $this->assertSame('', workshop::clean_file_extensions(''));
668 $this->assertSame('', workshop::clean_file_extensions(null));
669 $this->assertSame('', workshop::clean_file_extensions(' '));
670 $this->assertSame('0', workshop::clean_file_extensions(0));
671 $this->assertSame('0', workshop::clean_file_extensions('0'));
672 $this->assertSame('doc, rtf, pdf', workshop::clean_file_extensions('*.Doc, RTF, PDF, .rtf'.PHP_EOL.'PDF '));
673 $this->assertSame('doc, rtf, pdf', 'doc, rtf, pdf');
677 * Test validation of the list of file extensions.
679 public function test_invalid_file_extensions() {
680 $this->resetAfterTest(true);
682 $this->assertSame([], workshop::invalid_file_extensions('', ''));
683 $this->assertSame([], workshop::invalid_file_extensions('', '.doc'));
684 $this->assertSame([], workshop::invalid_file_extensions('odt', ''));
685 $this->assertSame([], workshop::invalid_file_extensions('odt', '*'));
686 $this->assertSame([], workshop::invalid_file_extensions('odt', 'odt'));
687 $this->assertSame([], workshop::invalid_file_extensions('doc, odt, pdf', ['pdf', 'doc', 'odt']));
688 $this->assertSame([], workshop::invalid_file_extensions(['doc', 'odt', 'PDF'], ['.doc', '.pdf', '.odt']));
689 $this->assertSame([], workshop::invalid_file_extensions('*~ .docx, Odt PDF :doc .pdf', '*.docx *.odt *.pdf *.doc'));
690 $this->assertSame(['.00001-wtf-is-this'], workshop::invalid_file_extensions('docx tgz .00001-wtf-is-this', 'tgz docx'));
691 $this->assertSame(['.foobar', '.wtfisthis'], workshop::invalid_file_extensions(['.pdf', '.foobar', 'wtfisthis'], 'pdf'));
692 $this->assertSame([], workshop::invalid_file_extensions('', ''));
693 $this->assertSame(['.odt'], workshop::invalid_file_extensions(['.PDF', 'PDF', '.ODT'], 'jpg pdf png gif'));
694 $this->assertSame(['.odt'], workshop::invalid_file_extensions(['.PDF', 'PDF', '.ODT'], '.jpg,.pdf, .png .gif'));
695 $this->assertSame(['.exe', '.bat'], workshop::invalid_file_extensions(['.exe', '.odt', '.bat', ''], 'odt'));
699 * Test checking file name against the list of allowed extensions.
701 public function test_is_allowed_file_type() {
702 $this->resetAfterTest(true);
704 $this->assertTrue(workshop::is_allowed_file_type('README.txt', ''));
705 $this->assertTrue(workshop::is_allowed_file_type('README.txt', ['']));
706 $this->assertFalse(workshop::is_allowed_file_type('README.txt', '0'));
708 $this->assertFalse(workshop::is_allowed_file_type('README.txt', 'xt'));
709 $this->assertFalse(workshop::is_allowed_file_type('README.txt', 'old.txt'));
711 $this->assertTrue(workshop::is_allowed_file_type('README.txt', 'txt'));
712 $this->assertTrue(workshop::is_allowed_file_type('README.txt', '.TXT'));
713 $this->assertTrue(workshop::is_allowed_file_type('README.TXT', 'txt'));
714 $this->assertTrue(workshop::is_allowed_file_type('README.txt', '.txt .md'));
715 $this->assertTrue(workshop::is_allowed_file_type('README.txt', 'HTML TXT DOC RTF'));
716 $this->assertTrue(workshop::is_allowed_file_type('README.txt', ['HTML', '...TXT', 'DOC', 'RTF']));
718 $this->assertTrue(workshop::is_allowed_file_type('C:\Moodle\course-data.tar.gz', 'gzip zip 7z tar.gz'));
719 $this->assertFalse(workshop::is_allowed_file_type('C:\Moodle\course-data.tar.gz', 'gzip zip 7z tar'));
720 $this->assertTrue(workshop::is_allowed_file_type('~/course-data.tar.gz', 'gzip zip 7z gz'));
721 $this->assertFalse(workshop::is_allowed_file_type('~/course-data.tar.gz', 'gzip zip 7z'));
723 $this->assertFalse(workshop::is_allowed_file_type('Alice on the beach.jpg.exe', 'png gif jpg bmp'));
724 $this->assertFalse(workshop::is_allowed_file_type('xfiles.exe.jpg', 'exe com bat sh'));
725 $this->assertFalse(workshop::is_allowed_file_type('solution.odt~', 'odt, xls'));
726 $this->assertTrue(workshop::is_allowed_file_type('solution.odt~', 'odt, odt~'));
730 * Test workshop::check_group_membership() functionality.
732 public function test_check_group_membership() {
735 $this->resetAfterTest();
737 $courseid = $this->course->id;
738 $generator = $this->getDataGenerator();
741 $group1 = $generator->create_group(array('courseid' => $courseid));
742 $group2 = $generator->create_group(array('courseid' => $courseid));
743 $group3 = $generator->create_group(array('courseid' => $courseid));
745 // Revoke the accessallgroups from non-editing teachers (tutors).
746 $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
747 unassign_capability('moodle/site:accessallgroups', $roleids['teacher']);
749 // Create test use accounts.
750 $teacher1 = $generator->create_user();
751 $tutor1 = $generator->create_user();
752 $tutor2 = $generator->create_user();
753 $student1 = $generator->create_user();
754 $student2 = $generator->create_user();
755 $student3 = $generator->create_user();
757 // Enrol the teacher (has the access all groups permission).
758 $generator->enrol_user($teacher1->id, $courseid, $roleids['editingteacher']);
760 // Enrol tutors (can not access all groups).
761 $generator->enrol_user($tutor1->id, $courseid, $roleids['teacher']);
762 $generator->enrol_user($tutor2->id, $courseid, $roleids['teacher']);
765 $generator->enrol_user($student1->id, $courseid, $roleids['student']);
766 $generator->enrol_user($student2->id, $courseid, $roleids['student']);
767 $generator->enrol_user($student3->id, $courseid, $roleids['student']);
769 // Add users in groups.
770 groups_add_member($group1, $tutor1);
771 groups_add_member($group2, $tutor2);
772 groups_add_member($group1, $student1);
773 groups_add_member($group2, $student2);
774 groups_add_member($group3, $student3);
776 // Workshop with no groups.
777 $workshopitem1 = $this->getDataGenerator()->create_module('workshop', [
778 'course' => $courseid,
779 'groupmode' => NOGROUPS,
781 $cm = get_coursemodule_from_instance('workshop', $workshopitem1->id, $courseid, false, MUST_EXIST);
782 $workshop1 = new testable_workshop($workshopitem1, $cm, $this->course);
784 $this->setUser($teacher1);
785 $this->assertTrue($workshop1->check_group_membership($student1->id));
786 $this->assertTrue($workshop1->check_group_membership($student2->id));
787 $this->assertTrue($workshop1->check_group_membership($student3->id));
789 $this->setUser($tutor1);
790 $this->assertTrue($workshop1->check_group_membership($student1->id));
791 $this->assertTrue($workshop1->check_group_membership($student2->id));
792 $this->assertTrue($workshop1->check_group_membership($student3->id));
794 // Workshop in visible groups mode.
795 $workshopitem2 = $this->getDataGenerator()->create_module('workshop', [
796 'course' => $courseid,
797 'groupmode' => VISIBLEGROUPS,
799 $cm = get_coursemodule_from_instance('workshop', $workshopitem2->id, $courseid, false, MUST_EXIST);
800 $workshop2 = new testable_workshop($workshopitem2, $cm, $this->course);
802 $this->setUser($teacher1);
803 $this->assertTrue($workshop2->check_group_membership($student1->id));
804 $this->assertTrue($workshop2->check_group_membership($student2->id));
805 $this->assertTrue($workshop2->check_group_membership($student3->id));
807 $this->setUser($tutor1);
808 $this->assertTrue($workshop2->check_group_membership($student1->id));
809 $this->assertTrue($workshop2->check_group_membership($student2->id));
810 $this->assertTrue($workshop2->check_group_membership($student3->id));
812 // Workshop in separate groups mode.
813 $workshopitem3 = $this->getDataGenerator()->create_module('workshop', [
814 'course' => $courseid,
815 'groupmode' => SEPARATEGROUPS,
817 $cm = get_coursemodule_from_instance('workshop', $workshopitem3->id, $courseid, false, MUST_EXIST);
818 $workshop3 = new testable_workshop($workshopitem3, $cm, $this->course);
820 $this->setUser($teacher1);
821 $this->assertTrue($workshop3->check_group_membership($student1->id));
822 $this->assertTrue($workshop3->check_group_membership($student2->id));
823 $this->assertTrue($workshop3->check_group_membership($student3->id));
825 $this->setUser($tutor1);
826 $this->assertTrue($workshop3->check_group_membership($student1->id));
827 $this->assertFalse($workshop3->check_group_membership($student2->id));
828 $this->assertFalse($workshop3->check_group_membership($student3->id));
830 $this->setUser($tutor2);
831 $this->assertFalse($workshop3->check_group_membership($student1->id));
832 $this->assertTrue($workshop3->check_group_membership($student2->id));
833 $this->assertFalse($workshop3->check_group_membership($student3->id));