Merge branch 'MDL-55944' of https://github.com/mr-russ/moodle
[moodle.git] / mod / assign / tests / locallib_test.php
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/>.
17 /**
18  * Unit tests for (some of) mod/assign/locallib.php.
19  *
20  * @package    mod_assign
21  * @category   phpunit
22  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
30 require_once($CFG->dirroot . '/mod/assign/locallib.php');
31 require_once($CFG->dirroot . '/mod/assign/upgradelib.php');
32 require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
34 /**
35  * Unit tests for (some of) mod/assign/locallib.php.
36  *
37  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
38  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39  */
40 class mod_assign_locallib_testcase extends mod_assign_base_testcase {
42     public function test_return_links() {
43         global $PAGE;
44         $this->setUser($this->editingteachers[0]);
45         $returnaction = 'RETURNACTION';
46         $returnparams = array('param'=>'1');
47         $assign = $this->create_instance();
48         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
49         $assign->register_return_link($returnaction, $returnparams);
50         $this->assertEquals($returnaction, $assign->get_return_action());
51         $this->assertEquals($returnparams, $assign->get_return_params());
52     }
54     public function test_get_feedback_plugins() {
55         $this->setUser($this->editingteachers[0]);
56         $assign = $this->create_instance();
57         $installedplugins = array_keys(core_component::get_plugin_list('assignfeedback'));
59         foreach ($assign->get_feedback_plugins() as $plugin) {
60             $this->assertContains($plugin->get_type(), $installedplugins, 'Feedback plugin not in list of installed plugins');
61         }
62     }
64     public function test_get_submission_plugins() {
65         $this->setUser($this->editingteachers[0]);
66         $assign = $this->create_instance();
67         $installedplugins = array_keys(core_component::get_plugin_list('assignsubmission'));
69         foreach ($assign->get_submission_plugins() as $plugin) {
70             $this->assertContains($plugin->get_type(), $installedplugins, 'Submission plugin not in list of installed plugins');
71         }
72     }
74     public function test_is_blind_marking() {
75         $this->setUser($this->editingteachers[0]);
76         $assign = $this->create_instance(array('blindmarking'=>1));
77         $this->assertEquals(true, $assign->is_blind_marking());
79         // Test cannot see student names.
80         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
81         $output = $assign->get_renderer()->render($gradingtable);
82         $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));
84         // Test students cannot reveal identities.
85         $nopermission = false;
86         $this->students[0]->ignoresesskey = true;
87         $this->setUser($this->students[0]);
88         $this->expectException('required_capability_exception');
89         $assign->reveal_identities();
90         $this->students[0]->ignoresesskey = false;
92         // Test teachers cannot reveal identities.
93         $nopermission = false;
94         $this->teachers[0]->ignoresesskey = true;
95         $this->setUser($this->teachers[0]);
96         $this->expectException('required_capability_exception');
97         $assign->reveal_identities();
98         $this->teachers[0]->ignoresesskey = false;
100         // Test sesskey is required.
101         $this->setUser($this->editingteachers[0]);
102         $this->expectException('moodle_exception');
103         $assign->reveal_identities();
105         // Test editingteacher can reveal identities if sesskey is ignored.
106         $this->editingteachers[0]->ignoresesskey = true;
107         $this->setUser($this->editingteachers[0]);
108         $assign->reveal_identities();
109         $this->assertEquals(false, $assign->is_blind_marking());
110         $this->editingteachers[0]->ignoresesskey = false;
112         // Test student names are visible.
113         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
114         $output = $assign->get_renderer()->render($gradingtable);
115         $this->assertEquals(false, strpos($output, get_string('hiddenuser', 'assign')));
117         // Set this back to default.
118         $this->editingteachers[0]->ignoresesskey = false;
119     }
121     /**
122      * Data provider for test_get_assign_perpage
123      *
124      * @return array Provider data
125      */
126     public function get_assign_perpage_provider() {
127         return array(
128             array(
129                 'maxperpage' => -1,
130                 'userprefs' => array(
131                     -1 => -1,
132                     10 => 10,
133                     20 => 20,
134                     50 => 50,
135                 ),
136             ),
137             array(
138                 'maxperpage' => 15,
139                 'userprefs' => array(
140                     -1 => 15,
141                     10 => 10,
142                     20 => 15,
143                     50 => 15,
144                 ),
145             ),
146         );
147     }
149     /**
150      * Test maxperpage
151      *
152      * @dataProvider get_assign_perpage_provider
153      * @param integer $maxperpage site config value
154      * @param array $userprefs Array of user preferences and expected page sizes
155      */
156     public function test_get_assign_perpage($maxperpage, $userprefs) {
158         $this->setUser($this->editingteachers[0]);
159         $assign = $this->create_instance();
160         set_config('maxperpage', $maxperpage, 'assign');
161         set_user_preference('assign_perpage', null);
162         $this->assertEquals(10, $assign->get_assign_perpage());
163         foreach ($userprefs as $pref => $perpage) {
164             set_user_preference('assign_perpage', $pref);
165             $this->assertEquals($perpage, $assign->get_assign_perpage());
166         }
167     }
169     /**
170      * Test submissions with extension date.
171      */
172     public function test_gradingtable_extension_due_date() {
173         global $PAGE;
175         // Setup the assignment.
176         $this->create_extra_users();
177         $this->setUser($this->editingteachers[0]);
178         $assign = $this->create_instance(array(
179             'assignsubmission_onlinetext_enabled'=>1,
180             'duedate' => time() - 4 * 24 * 60 * 60,
181          ));
182         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
183             'id' => $assign->get_course_module()->id,
184             'action' => 'grading',
185         )));
187         // Check that the assignment is late.
188         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
189         $output = $assign->get_renderer()->render($gradingtable);
190         $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
191         $this->assertContains(get_string('overdue', 'assign', format_time(4*24*60*60)), $output);
193         // Grant an extension.
194         $extendedtime = time() + 2 * 24 * 60 * 60;
195         $assign->testable_save_user_extension($this->students[0]->id, $extendedtime);
196         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
197         $output = $assign->get_renderer()->render($gradingtable);
198         $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
199         $this->assertContains(get_string('userextensiondate', 'assign', userdate($extendedtime)), $output);
201         // Simulate a submission.
202         $this->setUser($this->students[0]);
203         $submission = $assign->get_user_submission($this->students[0]->id, true);
204         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
205         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
206         $data = new stdClass();
207         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
208                                          'text'=>'Submission text',
209                                          'format'=>FORMAT_MOODLE);
210         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
211         $plugin->save($submission, $data);
213         // Verify output.
214         $this->setUser($this->editingteachers[0]);
215         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
216         $output = $assign->get_renderer()->render($gradingtable);
217         $this->assertContains(get_string('submissionstatus_submitted', 'assign'), $output);
218         $this->assertContains(get_string('userextensiondate', 'assign', userdate($extendedtime)), $output);
219     }
221     /**
222      * Test that late submissions with extension date calculate correctly.
223      */
224     public function test_gradingtable_extension_date_calculation_for_lateness() {
225         global $PAGE;
227         // Setup the assignment.
228         $this->create_extra_users();
229         $this->setUser($this->editingteachers[0]);
230         $time = time();
231         $assign = $this->create_instance(array(
232             'assignsubmission_onlinetext_enabled'=>1,
233             'duedate' => $time - 4 * 24 * 60 * 60,
234          ));
235         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
236             'id' => $assign->get_course_module()->id,
237             'action' => 'grading',
238         )));
240         // Check that the assignment is late.
241         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
242         $output = $assign->get_renderer()->render($gradingtable);
243         $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
244         $difftime = time() - $time;
245         $this->assertContains(get_string('overdue', 'assign', format_time(4*24*60*60 + $difftime)), $output);
247         // Grant an extension that is in the past.
248         $assign->testable_save_user_extension($this->students[0]->id, $time - 2 * 24 * 60 * 60);
249         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
250         $output = $assign->get_renderer()->render($gradingtable);
251         $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
252         $this->assertContains(get_string('userextensiondate', 'assign', userdate($time - 2*24*60*60)), $output);
253         $difftime = time() - $time;
254         $this->assertContains(get_string('overdue', 'assign', format_time(2*24*60*60 + $difftime)), $output);
256         // Simulate a submission.
257         $this->setUser($this->students[0]);
258         $submission = $assign->get_user_submission($this->students[0]->id, true);
259         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
260         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
261         $data = new stdClass();
262         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
263                                          'text'=>'Submission text',
264                                          'format'=>FORMAT_MOODLE);
265         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
266         $plugin->save($submission, $data);
267         $submittedtime = time();
269         // Verify output.
270         $this->setUser($this->editingteachers[0]);
271         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
272         $output = $assign->get_renderer()->render($gradingtable);
273         $this->assertContains(get_string('submissionstatus_submitted', 'assign'), $output);
274         $this->assertContains(get_string('userextensiondate', 'assign', userdate($time - 2*24*60*60)), $output);
276         $difftime = $submittedtime - $time;
277         $this->assertContains(get_string('submittedlateshort', 'assign', format_time(2*24*60*60 + $difftime)), $output);
278     }
280     public function test_gradingtable_status_rendering() {
281         global $PAGE;
283         // Setup the assignment.
284         $this->create_extra_users();
285         $this->setUser($this->editingteachers[0]);
286         $time = time();
287         $assign = $this->create_instance(array(
288             'assignsubmission_onlinetext_enabled' => 1,
289             'duedate' => $time - 4 * 24 * 60 * 60,
290          ));
291         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
292             'id' => $assign->get_course_module()->id,
293             'action' => 'grading',
294         )));
296         // Check that the assignment is late.
297         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
298         $output = $assign->get_renderer()->render($gradingtable);
299         $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
300         $difftime = time() - $time;
301         $this->assertContains(get_string('overdue', 'assign', format_time(4 * 24 * 60 * 60 + $difftime)), $output);
303         // Simulate a student viewing the assignment without submitting.
304         $this->setUser($this->students[0]);
305         $submission = $assign->get_user_submission($this->students[0]->id, true);
306         $submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
307         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
308         $submittedtime = time();
310         // Verify output.
311         $this->setUser($this->editingteachers[0]);
312         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
313         $output = $assign->get_renderer()->render($gradingtable);
314         $difftime = $submittedtime - $time;
315         $this->assertContains(get_string('overdue', 'assign', format_time(4 * 24 * 60 * 60 + $difftime)), $output);
317         $document = new DOMDocument();
318         $document->loadHTML($output);
319         $xpath = new DOMXPath($document);
320         $this->assertEquals('', $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c8"])'));
321     }
323     /**
324      * Check that group submission information is rendered correctly in the
325      * grading table.
326      */
327     public function test_gradingtable_group_submissions_rendering() {
328         global $PAGE;
330         $this->create_extra_users();
331         // Now verify group assignments.
332         $this->setUser($this->teachers[0]);
333         $assign = $this->create_instance(array(
334             'teamsubmission' => 1,
335             'assignsubmission_onlinetext_enabled' => 1,
336             'submissiondrafts' => 1,
337             'requireallteammemberssubmit' => 0,
338         ));
339         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
340             'id' => $assign->get_course_module()->id,
341             'action' => 'grading',
342         )));
344         // Add a submission.
345         $this->setUser($this->extrastudents[0]);
346         $data = new stdClass();
347         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
348                                          'text'=>'Submission text',
349                                          'format'=>FORMAT_MOODLE);
350         $notices = array();
351         $assign->save_submission($data, $notices);
353         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
354         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
355         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
357         // Check output.
358         $this->setUser($this->teachers[0]);
359         $gradingtable = new assign_grading_table($assign, 4, '', 0, true);
360         $output = $assign->get_renderer()->render($gradingtable);
361         $document = new DOMDocument();
362         $document->loadHTML($output);
363         $xpath = new DOMXPath($document);
365         // Check status.
366         $this->assertSame(get_string('submissionstatus_submitted', 'assign'), $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c4"]/div[@class="submissionstatussubmitted"])'));
367         $this->assertSame(get_string('submissionstatus_submitted', 'assign'), $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c4"]/div[@class="submissionstatussubmitted"])'));
369         // Check submission last modified date
370         $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c8"])')));
371         $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c8"])')));
373         // Check group.
374         $this->assertSame($this->groups[0]->name, $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c5"])'));
375         $this->assertSame($this->groups[0]->name, $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c5"])'));
377         // Check submission text.
378         $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c9"]/div/div)'));
379         $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c9"]/div/div)'));
381         // Check comments can be made.
382         $this->assertSame(1, (int)$xpath->evaluate('count(//td[@id="mod_assign_grading_r0_c10"]//textarea)'));
383         $this->assertSame(1, (int)$xpath->evaluate('count(//td[@id="mod_assign_grading_r3_c10"]//textarea)'));
384     }
386     public function test_show_intro() {
387         // Test whether we are showing the intro at the correct times.
388         $this->setUser($this->editingteachers[0]);
389         $assign = $this->create_instance(array('alwaysshowdescription'=>1));
391         $this->assertEquals(true, $assign->testable_show_intro());
393         $tomorrow = time() + (24*60*60);
395         $assign = $this->create_instance(array('alwaysshowdescription'=>0,
396                                                'allowsubmissionsfromdate'=>$tomorrow));
397         $this->assertEquals(false, $assign->testable_show_intro());
398         $yesterday = time() - (24*60*60);
399         $assign = $this->create_instance(array('alwaysshowdescription'=>0,
400                                                'allowsubmissionsfromdate'=>$yesterday));
401         $this->assertEquals(true, $assign->testable_show_intro());
402     }
404     public function test_has_submissions_or_grades() {
405         $this->setUser($this->editingteachers[0]);
406         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
408         $instance = $assign->get_instance();
410         // Should start empty.
411         $this->assertEquals(false, $assign->has_submissions_or_grades());
413         // Simulate a submission.
414         $this->setUser($this->students[0]);
415         $submission = $assign->get_user_submission($this->students[0]->id, true);
417         // The submission is still new.
418         $this->assertEquals(false, $assign->has_submissions_or_grades());
420         // Submit the submission.
421         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
422         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
423         $data = new stdClass();
424         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
425                                          'text'=>'Submission text',
426                                          'format'=>FORMAT_MOODLE);
427         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
428         $plugin->save($submission, $data);
430         // Now test again.
431         $this->assertEquals(true, $assign->has_submissions_or_grades());
432         // Set this back to default.
433         $this->students[0]->ignoresesskey = false;
434     }
436     public function test_delete_grades() {
437         $this->setUser($this->editingteachers[0]);
438         $assign = $this->create_instance();
440         // Simulate adding a grade.
441         $this->setUser($this->teachers[0]);
442         $data = new stdClass();
443         $data->grade = '50.0';
444         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
446         // Now see if the data is in the gradebook.
447         $gradinginfo = grade_get_grades($this->course->id,
448                                         'mod',
449                                         'assign',
450                                         $assign->get_instance()->id);
452         $this->assertNotEquals(0, count($gradinginfo->items));
454         $assign->testable_delete_grades();
455         $gradinginfo = grade_get_grades($this->course->id,
456                                         'mod',
457                                         'assign',
458                                         $assign->get_instance()->id);
460         $this->assertEquals(0, count($gradinginfo->items));
461     }
463     public function test_delete_instance() {
464         $this->setUser($this->editingteachers[0]);
465         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
467         // Simulate adding a grade.
468         $this->setUser($this->teachers[0]);
469         $data = new stdClass();
470         $data->grade = '50.0';
471         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
473         // Simulate a submission.
474         $this->setUser($this->students[0]);
475         $submission = $assign->get_user_submission($this->students[0]->id, true);
476         $data = new stdClass();
477         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
478                                          'text'=>'Submission text',
479                                          'format'=>FORMAT_MOODLE);
480         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
481         $plugin->save($submission, $data);
483         // Now try and delete.
484         $this->assertEquals(true, $assign->delete_instance());
485     }
487     public function test_reset_userdata() {
488         global $DB;
490         $now = time();
491         $this->setUser($this->editingteachers[0]);
492         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1,
493                                                'duedate'=>$now));
495         // Simulate adding a grade.
496         $this->setUser($this->teachers[0]);
497         $data = new stdClass();
498         $data->grade = '50.0';
499         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
501         // Simulate a submission.
502         $this->setUser($this->students[0]);
503         $submission = $assign->get_user_submission($this->students[0]->id, true);
504         $data = new stdClass();
505         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
506                                          'text'=>'Submission text',
507                                          'format'=>FORMAT_MOODLE);
508         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
509         $plugin->save($submission, $data);
511         $this->assertEquals(true, $assign->has_submissions_or_grades());
512         // Now try and reset.
513         $data = new stdClass();
514         $data->reset_assign_submissions = 1;
515         $data->reset_gradebook_grades = 1;
516         $data->courseid = $this->course->id;
517         $data->timeshift = 24*60*60;
518         $this->setUser($this->editingteachers[0]);
519         $assign->reset_userdata($data);
520         $this->assertEquals(false, $assign->has_submissions_or_grades());
522         // Reload the instance data.
523         $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
524         $this->assertEquals($now + 24*60*60, $instance->duedate);
526         // Test reset using assign_reset_userdata().
527         $assignduedate = $instance->duedate; // Keep old updated value for comparison.
528         $data->timeshift = 2*24*60*60;
529         assign_reset_userdata($data);
530         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
531         $this->assertEquals($assignduedate + 2*24*60*60, $instance->duedate);
533         // Create one more assignment and reset, make sure time shifted for previous assignment is not changed.
534         $assign2 = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1,
535                                                'duedate' => $now));
536         $assignduedate = $instance->duedate;
537         $data->timeshift = 3*24*60*60;
538         $assign2->reset_userdata($data);
539         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
540         $this->assertEquals($assignduedate, $instance->duedate);
541         $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
542         $this->assertEquals($now + 3*24*60*60, $instance2->duedate);
544         // Reset both assignments using assign_reset_userdata() and make sure both assignments have same date.
545         $assignduedate = $instance->duedate;
546         $assign2duedate = $instance2->duedate;
547         $data->timeshift = 4*24*60*60;
548         assign_reset_userdata($data);
549         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
550         $this->assertEquals($assignduedate + 4*24*60*60, $instance->duedate);
551         $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
552         $this->assertEquals($assign2duedate + 4*24*60*60, $instance2->duedate);
553     }
555     public function test_plugin_settings() {
556         global $DB;
558         $now = time();
559         $this->setUser($this->editingteachers[0]);
560         $assign = $this->create_instance(array('assignsubmission_file_enabled'=>1,
561                                                'assignsubmission_file_maxfiles'=>12,
562                                                'assignsubmission_file_maxsizebytes'=>10));
564         $plugin = $assign->get_submission_plugin_by_type('file');
565         $this->assertEquals('12', $plugin->get_config('maxfilesubmissions'));
566     }
568     public function test_update_calendar() {
569         global $DB;
571         $this->setUser($this->editingteachers[0]);
572         $userctx = context_user::instance($this->editingteachers[0]->id)->id;
574         // Hack to pretend that there was an editor involved. We need both $_POST and $_REQUEST, and a sesskey.
575         $draftid = file_get_unused_draft_itemid();
576         $_REQUEST['introeditor'] = $draftid;
577         $_POST['introeditor'] = $draftid;
578         $_POST['sesskey'] = sesskey();
580         // Write links to a draft area.
581         $fakearealink1 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">link</a>', 'draftfile.php', $userctx,
582             'user', 'draft', $draftid);
583         $fakearealink2 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">new</a>', 'draftfile.php', $userctx,
584             'user', 'draft', $draftid);
586         // Create a new assignment with links to a draft area.
587         $now = time();
588         $assign = $this->create_instance(array(
589             'duedate' => $now,
590             'intro' => $fakearealink1,
591             'introformat' => FORMAT_HTML
592         ));
594         // See if there is an event in the calendar.
595         $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
596         $event = $DB->get_record('event', $params);
597         $this->assertNotEmpty($event);
598         $this->assertSame('link', $event->description);     // The pluginfile links are removed.
600         // Make sure the same works when updating the assignment.
601         $instance = $assign->get_instance();
602         $instance->instance = $instance->id;
603         $instance->intro = $fakearealink2;
604         $instance->introformat = FORMAT_HTML;
605         $assign->update_instance($instance);
606         $params = array('modulename' => 'assign', 'instance' => $assign->get_instance()->id);
607         $event = $DB->get_record('event', $params);
608         $this->assertNotEmpty($event);
609         $this->assertSame('new', $event->description);     // The pluginfile links are removed.
611         // Create an assignment with a description that should be hidden.
612         $assign = $this->create_instance(array('duedate'=>$now + 160,
613                                                'alwaysshowdescription'=>false,
614                                                'allowsubmissionsfromdate'=>$now + 60,
615                                                'intro'=>'Some text'));
617         // Get the event from the calendar.
618         $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
619         $event = $DB->get_record('event', $params);
621         $this->assertEmpty($event->description);
623         // Change the allowsubmissionfromdate to the past - do this directly in the DB
624         // because if we call the assignment update method - it will update the calendar
625         // and we want to test that this works from cron.
626         $DB->set_field('assign', 'allowsubmissionsfromdate', $now - 60, array('id'=>$assign->get_instance()->id));
627         // Run cron to update the event in the calendar.
628         assign::cron();
629         $event = $DB->get_record('event', $params);
631         $this->assertContains('Some text', $event->description);
633     }
635     public function test_update_instance() {
636         global $DB;
638         $this->setUser($this->editingteachers[0]);
639         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
641         $now = time();
642         $instance = $assign->get_instance();
643         $instance->duedate = $now;
644         $instance->instance = $instance->id;
645         $instance->assignsubmission_onlinetext_enabled = 1;
647         $assign->update_instance($instance);
649         $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
650         $this->assertEquals($now, $instance->duedate);
651     }
653     public function test_cannot_submit_empty() {
654         global $PAGE;
656         $this->setUser($this->editingteachers[0]);
657         $assign = $this->create_instance(array('submissiondrafts'=>1));
659         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
661         // Test you cannot see the submit button for an offline assignment regardless.
662         $this->setUser($this->students[0]);
663         $output = $assign->view_student_summary($this->students[0], true);
664         $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Can submit empty offline assignment');
666         // Test you cannot see the submit button for an online text assignment with no submission.
667         $this->setUser($this->editingteachers[0]);
668         $instance = $assign->get_instance();
669         $instance->instance = $instance->id;
670         $instance->assignsubmission_onlinetext_enabled = 1;
672         $assign->update_instance($instance);
673         $this->setUser($this->students[0]);
674         $output = $assign->view_student_summary($this->students[0], true);
675         $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Cannot submit empty onlinetext assignment');
677         // Simulate a submission.
678         $submission = $assign->get_user_submission($this->students[0]->id, true);
679         $data = new stdClass();
680         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
681                                          'text'=>'Submission text',
682                                          'format'=>FORMAT_MOODLE);
683         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
684         $plugin->save($submission, $data);
685         // Test you can see the submit button for an online text assignment with a submission.
686         $output = $assign->view_student_summary($this->students[0], true);
687         $this->assertContains(get_string('submitassignment', 'assign'), $output, 'Can submit non empty onlinetext assignment');
688     }
690     /**
691      * Test new_submission_empty
692      *
693      * We only test combinations of plugins here. Individual plugins are tested
694      * in their respective test files.
695      *
696      * @dataProvider test_new_submission_empty_testcases
697      * @param string $data The file submission data
698      * @param bool $expected The expected return value
699      */
700     public function test_new_submission_empty($data, $expected) {
701         $this->resetAfterTest();
702         $assign = $this->create_instance(['assignsubmission_file_enabled' => 1,
703                                           'assignsubmission_file_maxfiles' => 12,
704                                           'assignsubmission_file_maxsizebytes' => 10,
705                                           'assignsubmission_onlinetext_enabled' => 1]);
706         $this->setUser($this->students[0]);
707         $submission = new stdClass();
709         if ($data['file'] && isset($data['file']['filename'])) {
710             $itemid = file_get_unused_draft_itemid();
711             $submission->files_filemanager = $itemid;
712             $data['file'] += ['contextid' => context_user::instance($this->students[0]->id)->id, 'itemid' => $itemid];
713             $fs = get_file_storage();
714             $fs->create_file_from_string((object)$data['file'], 'Content of ' . $data['file']['filename']);
715         }
717         if ($data['onlinetext']) {
718             $submission->onlinetext_editor = ['text' => $data['onlinetext']];
719         }
721         $result = $assign->new_submission_empty($submission);
722         $this->assertTrue($result === $expected);
723     }
725     /**
726      * Dataprovider for the test_new_submission_empty testcase
727      *
728      * @return array of testcases
729      */
730     public function test_new_submission_empty_testcases() {
731         return [
732             'With file and onlinetext' => [
733                 [
734                     'file' => [
735                         'component' => 'user',
736                         'filearea' => 'draft',
737                         'filepath' => '/',
738                         'filename' => 'not_a_virus.exe'
739                     ],
740                     'onlinetext' => 'Balin Fundinul Uzbadkhazaddumu'
741                 ],
742                 false
743             ]
744         ];
745     }
747     public function test_list_participants() {
748         global $CFG, $DB;
750         $this->create_extra_users();
751         $this->setUser($this->editingteachers[0]);
752         $assign = $this->create_instance(array('grade'=>100));
754         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
756         // Teacher with user preference set should see suspended users as well.
757         set_user_preference('grade_report_showonlyactiveenrol', false);
758         $assign = $this->create_instance(array('grade'=>100));
759         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT,
760                 count($assign->list_participants(null, true)));
762         // Non-editing teacher should not see suspended users, even if user preference is set.
763         $this->setUser($this->teachers[0]);
764         set_user_preference('grade_report_showonlyactiveenrol', false);
765         $assign = $this->create_instance(array('grade'=>100));
766         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
768         // Turn on availability and a group restriction, and check that it doesn't
769         // show users who aren't in the group.
770         $CFG->enableavailability = true;
771         $specialgroup = $this->getDataGenerator()->create_group(
772                 array('courseid' => $this->course->id));
773         $assign = $this->create_instance(array('grade' => 100,
774                 'availability' => json_encode(\core_availability\tree::get_root_json(
775                     array(\availability_group\condition::get_json($specialgroup->id))))));
776         groups_add_member($specialgroup, $this->students[0]);
777         groups_add_member($specialgroup, $this->students[1]);
778         $this->assertEquals(2, count($assign->list_participants(null, true)));
779     }
781     public function test_get_participant_user_not_exist() {
782         $assign = $this->create_instance(array('grade' => 100));
783         $this->assertNull($assign->get_participant('-1'));
784     }
786     public function test_get_participant_not_enrolled() {
787         $assign = $this->create_instance(array('grade' => 100));
788         $user = $this->getDataGenerator()->create_user();
789         $this->assertNull($assign->get_participant($user->id));
790     }
792     public function test_get_participant_no_submission() {
793         $assign = $this->create_instance(array('grade' => 100));
794         $student = $this->students[0];
795         $participant = $assign->get_participant($student->id);
797         $this->assertEquals($student->id, $participant->id);
798         $this->assertFalse($participant->submitted);
799         $this->assertFalse($participant->requiregrading);
800     }
802     public function test_get_participant_with_ungraded_submission() {
803         $assign = $this->create_instance(array('grade' => 100));
804         $student = $this->students[0];
805         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
807         $this->setUser($student);
809         // Simulate a submission.
810         $data = new stdClass();
811         $data->onlinetext_editor = array(
812             'itemid' => file_get_unused_draft_itemid(),
813             'text' => 'Student submission text',
814             'format' => FORMAT_MOODLE
815         );
817         $notices = array();
818         $assign->save_submission($data, $notices);
820         $data = new stdClass;
821         $data->userid = $student->id;
822         $assign->submit_for_grading($data, array());
824         $participant = $assign->get_participant($student->id);
826         $this->assertEquals($student->id, $participant->id);
827         $this->assertTrue($participant->submitted);
828         $this->assertTrue($participant->requiregrading);
829     }
831     public function test_get_participant_with_graded_submission() {
832         $assign = $this->create_instance(array('grade' => 100));
833         $student = $this->students[0];
834         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
836         $this->setUser($student);
838         // Simulate a submission.
839         $data = new stdClass();
840         $data->onlinetext_editor = array(
841             'itemid' => file_get_unused_draft_itemid(),
842             'text' => 'Student submission text',
843             'format' => FORMAT_MOODLE
844         );
846         $notices = array();
847         $assign->save_submission($data, $notices);
849         $data = new stdClass;
850         $data->userid = $student->id;
851         $assign->submit_for_grading($data, array());
853         // This is to make sure the grade happens after the submission because
854         // we have no control over the timemodified values.
855         $this->waitForSecond();
856         // Grade the submission.
857         $this->setUser($this->teachers[0]);
859         $data = new stdClass();
860         $data->grade = '50.0';
861         $assign->testable_apply_grade_to_user($data, $student->id, 0);
863         $participant = $assign->get_participant($student->id);
865         $this->assertEquals($student->id, $participant->id);
866         $this->assertTrue($participant->submitted);
867         $this->assertFalse($participant->requiregrading);
868     }
870     public function test_count_teams() {
871         $this->create_extra_users();
872         $this->setUser($this->editingteachers[0]);
873         $assign1 = $this->create_instance(array('teamsubmission' => 1));
874         $this->assertEquals(self::GROUP_COUNT + 1, $assign1->count_teams());
876         $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id));
877         $this->getDataGenerator()->create_grouping_group(array('groupid' => $this->groups[0]->id, 'groupingid' => $grouping->id));
878         $this->getDataGenerator()->create_grouping_group(array('groupid' => $this->groups[1]->id, 'groupingid' => $grouping->id));
880         // No active group and non group submissions allowed => 2 groups + the default one.
881         $params = array(
882             'teamsubmission' => 1,
883             'teamsubmissiongroupingid' => $grouping->id,
884             'preventsubmissionnotingroup' => false
885         );
886         $assign2 = $this->create_instance($params);
887         $this->assertEquals(3, $assign2->count_teams());
889         // An active group => Just the selected one.
890         $this->assertEquals(1, $assign2->count_teams($this->groups[0]->id));
892         // No active group and non group submissions allowed => 2 groups + no default one.
893         $params = array('teamsubmission' => 1, 'teamsubmissiongroupingid' => $grouping->id, 'preventsubmissionnotingroup' => true);
894         $assign3 = $this->create_instance($params);
895         $this->assertEquals(2, $assign3->count_teams());
897         $assign4 = $this->create_instance(array('teamsubmission' => 1, 'preventsubmissionnotingroup' => true));
898         $this->assertEquals(self::GROUP_COUNT, $assign4->count_teams());
899     }
901     public function test_submit_to_default_group() {
902         global $DB, $SESSION;
904         $this->preventResetByRollback();
905         $sink = $this->redirectMessages();
907         $this->setUser($this->editingteachers[0]);
908         $params = array('teamsubmission' => 1,
909                         'assignsubmission_onlinetext_enabled' => 1,
910                         'submissiondrafts' => 0,
911                         'groupmode' => VISIBLEGROUPS);
912         $assign = $this->create_instance($params);
914         $newstudent = $this->getDataGenerator()->create_user();
915         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
916         $this->getDataGenerator()->enrol_user($newstudent->id,
917                                               $this->course->id,
918                                               $studentrole->id);
919         $this->setUser($newstudent);
920         $data = new stdClass();
921         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
922                                          'text'=>'Submission text',
923                                          'format'=>FORMAT_MOODLE);
924         $notices = array();
926         $group = $assign->get_submission_group($newstudent->id);
927         $this->assertFalse($group, 'New student is in default group');
928         $assign->save_submission($data, $notices);
929         $this->assertEmpty($notices, 'No errors on save submission');
931         // Set active groups to all groups.
932         $this->setUser($this->teachers[0]);
933         $SESSION->activegroup[$this->course->id]['aag'][0] = 0;
934         $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
936         // Set an active group.
937         $anothergroup = $this->groups[0];
938         $SESSION->activegroup[$this->course->id]['aag'][0] = (int)$anothergroup->id;
939         $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
941         $sink->close();
942     }
944     public function test_count_submissions() {
945         global $SESSION;
947         $this->create_extra_users();
948         $this->setUser($this->editingteachers[0]);
949         $assign1 = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1));
951         // Simulate a submission.
952         $this->setUser($this->extrastudents[0]);
953         $submission = $assign1->get_user_submission($this->extrastudents[0]->id, true);
954         $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
955         $assign1->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
956         // Leave this one as DRAFT.
957         $data = new stdClass();
958         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
959                                          'text'=>'Submission text',
960                                          'format'=>FORMAT_MOODLE);
961         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
962         $plugin->save($submission, $data);
964         // Simulate adding a grade.
965         $this->setUser($this->teachers[0]);
966         $data = new stdClass();
967         $data->grade = '50.0';
968         $assign1->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
970         // Simulate a submission.
971         $this->setUser($this->extrastudents[1]);
972         $submission = $assign1->get_user_submission($this->extrastudents[1]->id, true);
973         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
974         $assign1->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
975         $data = new stdClass();
976         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
977                                          'text'=>'Submission text',
978                                          'format'=>FORMAT_MOODLE);
979         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
980         $plugin->save($submission, $data);
982         // Simulate a submission.
983         $this->setUser($this->extrastudents[2]);
984         $submission = $assign1->get_user_submission($this->extrastudents[2]->id, true);
985         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
986         $assign1->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
987         $data = new stdClass();
988         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
989                                          'text'=>'Submission text',
990                                          'format'=>FORMAT_MOODLE);
991         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
992         $plugin->save($submission, $data);
994         // Simulate a submission.
995         $this->setUser($this->extrastudents[3]);
996         $submission = $assign1->get_user_submission($this->extrastudents[3]->id, true);
997         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
998         $assign1->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
999         $data = new stdClass();
1000         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1001                                          'text'=>'Submission text',
1002                                          'format'=>FORMAT_MOODLE);
1003         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
1004         $plugin->save($submission, $data);
1006         // Simulate a submission for suspended user, this will never be counted.
1007         $this->setUser($this->extrastudents[3]);
1008         $submission = $assign1->get_user_submission($this->extrasuspendedstudents[0]->id, true);
1009         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1010         $assign1->testable_update_submission($submission, $this->extrasuspendedstudents[0]->id, true, false);
1011         $data = new stdClass();
1012         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1013                                          'text'=>'Submission text',
1014                                          'format'=>FORMAT_MOODLE);
1015         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
1016         $plugin->save($submission, $data);
1018         // Wait 1 second so the submission and grade do not have the same timemodified.
1019         $this->waitForSecond();
1020         // Simulate adding a grade.
1021         $this->setUser($this->editingteachers[0]);
1022         $data = new stdClass();
1023         $data->grade = '50.0';
1024         $assign1->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
1025         $assign1->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
1027         // Create a new submission with status NEW.
1028         $this->setUser($this->extrastudents[4]);
1029         $submission = $assign1->get_user_submission($this->extrastudents[4]->id, true);
1031         $this->assertEquals(2, $assign1->count_grades());
1032         $this->assertEquals(4, $assign1->count_submissions());
1033         $this->assertEquals(5, $assign1->count_submissions(true));
1034         $this->assertEquals(2, $assign1->count_submissions_need_grading());
1035         $this->assertEquals(3, $assign1->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1036         $this->assertEquals(1, $assign1->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
1038         // Groups.
1039         $assign2 = $this->create_instance(array(
1040             'assignsubmission_onlinetext_enabled' => 1,
1041             'groupmode' => VISIBLEGROUPS
1042         ));
1044         $this->setUser($this->extrastudents[1]);
1045         $submission = $assign2->get_user_submission($this->extrastudents[1]->id, true);
1046         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1047         $assign2->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
1048         $data = new stdClass();
1049         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1050                                          'text' => 'Submission text',
1051                                          'format' => FORMAT_MOODLE);
1052         $plugin = $assign2->get_submission_plugin_by_type('onlinetext');
1053         $plugin->save($submission, $data);
1055         $this->assertEquals(1, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1057         // Set active groups to all groups.
1058         $this->setUser($this->teachers[0]);
1059         $SESSION->activegroup[$this->course->id]['aag'][0] = 0;
1060         $this->assertEquals(1, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1062         // Set the user group.
1063         $studentgroups = groups_get_user_groups($this->course->id, $this->extrastudents[1]->id);
1064         $this->assertEquals(1, count($studentgroups));
1065         $studentgroup = array_pop($studentgroups);
1066         $SESSION->activegroup[$this->course->id]['aag'][0] = $studentgroup[0];
1067         $this->assertEquals(1, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1069         // Set another group.
1070         $anothergroup = $this->groups[0];
1071         $this->assertNotEquals($anothergroup->id, $studentgroup[0]);
1072         $SESSION->activegroup[$this->course->id]['aag'][0] = (int)$anothergroup->id;
1073         $this->assertEquals(0, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1074     }
1076     public function test_count_submissions_for_groups() {
1077         $this->create_extra_users();
1078         $groupid = null;
1079         $this->setUser($this->editingteachers[0]);
1080         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1, 'teamsubmission' => 1));
1082         // Simulate a submission.
1083         $this->setUser($this->extrastudents[0]);
1084         $submission = $assign->get_group_submission($this->extrastudents[0]->id, $groupid, true);
1085         $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
1086         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
1087         // Leave this one as DRAFT.
1088         $data = new stdClass();
1089         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1090                                          'text' => 'Submission text',
1091                                          'format' => FORMAT_MOODLE);
1092         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1093         $plugin->save($submission, $data);
1095         // Simulate adding a grade.
1096         $this->setUser($this->teachers[0]);
1097         $data = new stdClass();
1098         $data->grade = '50.0';
1099         $assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
1101         // Simulate a submission.
1102         $this->setUser($this->extrastudents[1]);
1103         $submission = $assign->get_group_submission($this->extrastudents[1]->id, $groupid, true);
1104         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1105         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
1106         $data = new stdClass();
1107         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1108                                          'text' => 'Submission text',
1109                                          'format' => FORMAT_MOODLE);
1110         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1111         $plugin->save($submission, $data);
1113         // Simulate a submission.
1114         $this->setUser($this->extrastudents[2]);
1115         $submission = $assign->get_group_submission($this->extrastudents[2]->id, $groupid, true);
1116         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1117         $assign->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
1118         $data = new stdClass();
1119         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1120                                          'text' => 'Submission text',
1121                                          'format' => FORMAT_MOODLE);
1122         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1123         $plugin->save($submission, $data);
1125         // Simulate a submission.
1126         $this->setUser($this->extrastudents[3]);
1127         $submission = $assign->get_group_submission($this->extrastudents[3]->id, $groupid, true);
1128         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1129         $assign->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
1130         $data = new stdClass();
1131         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1132                                          'text' => 'Submission text',
1133                                          'format' => FORMAT_MOODLE);
1134         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1135         $plugin->save($submission, $data);
1137         // Simulate adding a grade.
1138         $this->setUser($this->editingteachers[0]);
1139         $data = new stdClass();
1140         $data->grade = '50.0';
1141         $assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
1142         $assign->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
1144         // Create a new submission with status NEW.
1145         $this->setUser($this->extrastudents[4]);
1146         $submission = $assign->get_group_submission($this->extrastudents[4]->id, $groupid, true);
1148         $this->assertEquals(2, $assign->count_grades());
1149         $this->assertEquals(4, $assign->count_submissions());
1150         $this->assertEquals(5, $assign->count_submissions(true));
1151         $this->assertEquals(3, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1152         $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
1153     }
1155     public function test_get_grading_userid_list() {
1156         $this->create_extra_users();
1157         $this->setUser($this->editingteachers[0]);
1158         $assign = $this->create_instance();
1160         $users = $assign->testable_get_grading_userid_list();
1161         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($users));
1163         $this->setUser($this->editingteachers[0]);
1164         set_user_preference('grade_report_showonlyactiveenrol', false);
1165         $assign = $this->create_instance();
1167         $users = $assign->testable_get_grading_userid_list();
1168         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT, count($users));
1169     }
1171     public function test_cron() {
1172         // First run cron so there are no messages waiting to be sent (from other tests).
1173         cron_setup_user();
1174         assign::cron();
1176         // Now create an assignment and add some feedback.
1177         $this->setUser($this->editingteachers[0]);
1178         $assign = $this->create_instance(array('sendstudentnotifications'=>1));
1180         // Simulate adding a grade.
1181         $this->setUser($this->teachers[0]);
1182         $data = new stdClass();
1183         $data->grade = '50.0';
1184         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1185         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
1187         $data->sendstudentnotifications = false;
1188         $assign->testable_apply_grade_to_user($data, $this->students[2]->id, 0);
1190         // Now run cron and see that one message was sent.
1191         $this->preventResetByRollback();
1192         $sink = $this->redirectMessages();
1193         cron_setup_user();
1194         $this->expectOutputRegex('/Done processing 2 assignment submissions/');
1195         assign::cron();
1197         $messages = $sink->get_messages();
1198         // The sent count should be 2, because the 3rd one was marked as do not send notifications.
1199         $this->assertEquals(2, count($messages));
1200         $this->assertEquals(1, $messages[0]->notification);
1201         $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
1203         // Regrading a grade causes a notification to the user.
1204         $data->sendstudentnotifications = true;
1205         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1206         assign::cron();
1207         $messages = $sink->get_messages();
1208         $this->assertEquals(3, count($messages));
1209     }
1211     /**
1212      * Test delivery of grade notifications as controlled by marking workflow.
1213      */
1214     public function test_markingworkflow_cron() {
1215         // First run cron so there are no messages waiting to be sent (from other tests).
1216         cron_setup_user();
1217         assign::cron();
1219         // Now create an assignment with marking workflow enabled.
1220         $this->setUser($this->editingteachers[0]);
1221         $assign = $this->create_instance(array('sendstudentnotifications' => 1, 'markingworkflow' => 1));
1223         // Simulate adding a grade.
1224         $this->setUser($this->teachers[0]);
1225         $data = new stdClass();
1226         $data->grade = '50.0';
1228         // This student will not receive notification.
1229         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
1230         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1232         // This student will receive notification.
1233         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
1234         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
1236         // Now run cron and see that one message was sent.
1237         $this->preventResetByRollback();
1238         $sink = $this->redirectMessages();
1239         cron_setup_user();
1240         $this->expectOutputRegex('/Done processing 1 assignment submissions/');
1241         assign::cron();
1243         $messages = $sink->get_messages();
1244         $this->assertEquals(1, count($messages));
1245         $this->assertEquals($messages[0]->useridto, $this->students[1]->id);
1246         $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
1247     }
1249     public function test_is_graded() {
1250         $this->setUser($this->editingteachers[0]);
1251         $assign = $this->create_instance();
1253         // Simulate adding a grade.
1254         $this->setUser($this->teachers[0]);
1255         $data = new stdClass();
1256         $data->grade = '50.0';
1257         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1259         $this->assertEquals(true, $assign->testable_is_graded($this->students[0]->id));
1260         $this->assertEquals(false, $assign->testable_is_graded($this->students[1]->id));
1261     }
1263     public function test_can_grade() {
1264         global $DB;
1266         $this->setUser($this->editingteachers[0]);
1267         $assign = $this->create_instance();
1269         $this->setUser($this->students[0]);
1270         $this->assertEquals(false, $assign->can_grade());
1271         $this->setUser($this->editingteachers[0]);
1272         $this->assertEquals(true, $assign->can_grade());
1273         $this->setUser($this->teachers[0]);
1274         $this->assertEquals(true, $assign->can_grade());
1276         // Test the viewgrades capability - without mod/assign:grade.
1277         $this->setUser($this->students[0]);
1278         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1279         assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
1280         $this->assertEquals(false, $assign->can_grade());
1281     }
1283     public function test_can_view_submission() {
1284         global $DB;
1286         $this->create_extra_users();
1287         $this->setUser($this->editingteachers[0]);
1288         $assign = $this->create_instance();
1290         $this->setUser($this->students[0]);
1291         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1292         $this->assertEquals(false, $assign->can_view_submission($this->students[1]->id));
1293         $this->assertEquals(false, $assign->can_view_submission($this->teachers[0]->id));
1294         $this->setUser($this->teachers[0]);
1295         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1296         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
1297         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
1298         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
1299         $this->setUser($this->editingteachers[0]);
1300         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1301         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
1302         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
1303         $this->assertEquals(true, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
1305         // Test the viewgrades capability - without mod/assign:grade.
1306         $this->setUser($this->students[0]);
1307         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1308         assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
1309         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1310         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
1311         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
1312         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
1313     }
1316     public function test_update_submission() {
1317         $this->create_extra_users();
1318         $this->setUser($this->editingteachers[0]);
1319         $assign = $this->create_instance();
1321         $this->setUser($this->extrastudents[0]);
1322         $now = time();
1323         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
1324         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
1326         $this->setUser($this->teachers[0]);
1327         // Verify the gradebook update.
1328         $gradinginfo = grade_get_grades($this->course->id,
1329                                         'mod',
1330                                         'assign',
1331                                         $assign->get_instance()->id,
1332                                         $this->extrastudents[0]->id);
1334         $this->assertEquals($this->extrastudents[0]->id,
1335                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
1337         // Now verify group assignments.
1338         $this->setUser($this->editingteachers[0]);
1339         $assign = $this->create_instance(array('teamsubmission'=>1));
1341         $this->setUser($this->extrastudents[0]);
1342         $now = time();
1343         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1344         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1346         // Check that at least 2 active members and 1 suspended member of the submission group had their submission updated.
1348         $this->setUser($this->editingteachers[0]);
1349         $gradinginfo = grade_get_grades($this->course->id,
1350                                         'mod',
1351                                         'assign',
1352                                         $assign->get_instance()->id,
1353                                         $this->extrastudents[0]->id);
1355         $this->assertEquals($this->extrastudents[0]->id,
1356                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
1358         $gradinginfo = grade_get_grades($this->course->id,
1359                                         'mod',
1360                                         'assign',
1361                                         $assign->get_instance()->id,
1362                                         $this->extrastudents[self::GROUP_COUNT]->id);
1364         $this->assertEquals($this->extrastudents[self::GROUP_COUNT]->id,
1365                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT]->id]->usermodified);
1367         $gradinginfo = grade_get_grades($this->course->id,
1368                                         'mod',
1369                                         'assign',
1370                                         $assign->get_instance()->id,
1371                                         $this->extrasuspendedstudents[0]->id);
1372         $this->assertEquals($this->extrasuspendedstudents[0]->id,
1373                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[0]->id]->usermodified);
1375         // Check the same with non-editing teacher and make sure submission is not updated for suspended user.
1376         $this->setUser($this->editingteachers[0]);
1377         $assign = $this->create_instance(array('teamsubmission'=>1));
1379         $this->setUser($this->extrastudents[1]);
1380         $now = time();
1381         $submission = $assign->get_group_submission($this->extrastudents[1]->id, 0, true);
1382         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, true);
1384         $this->setUser($this->teachers[0]);
1385         $gradinginfo = grade_get_grades($this->course->id,
1386                                         'mod',
1387                                         'assign',
1388                                         $assign->get_instance()->id,
1389                                         $this->extrastudents[1]->id);
1391         $this->assertEquals($this->extrastudents[1]->id,
1392                             $gradinginfo->items[0]->grades[$this->extrastudents[1]->id]->usermodified);
1394         $gradinginfo = grade_get_grades($this->course->id,
1395                                         'mod',
1396                                         'assign',
1397                                         $assign->get_instance()->id,
1398                                         $this->extrastudents[self::GROUP_COUNT+1]->id);
1400         $this->assertEquals($this->extrastudents[self::GROUP_COUNT+1]->id,
1401                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT+1]->id]->usermodified);
1403         $gradinginfo = grade_get_grades($this->course->id,
1404                                         'mod',
1405                                         'assign',
1406                                         $assign->get_instance()->id,
1407                                         $this->extrasuspendedstudents[1]->id);
1408         $this->assertEquals($this->extrasuspendedstudents[1]->id,
1409                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[1]->id]->usermodified);
1411         // Now verify blind marking.
1412         $this->setUser($this->editingteachers[0]);
1413         $assign = $this->create_instance(array('blindmarking'=>1));
1415         $this->setUser($this->extrastudents[0]);
1416         $now = time();
1417         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
1418         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
1420         $this->setUser($this->editingteachers[0]);
1421         $gradinginfo = grade_get_grades($this->course->id,
1422                                         'mod',
1423                                         'assign',
1424                                         $assign->get_instance()->id,
1425                                         $this->extrastudents[0]->id);
1427         $this->assertEquals(null, $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->datesubmitted);
1428     }
1430     public function test_group_submissions_submit_for_marking_requireallteammemberssubmit() {
1431         global $PAGE;
1433         $this->create_extra_users();
1434         // Now verify group assignments.
1435         $this->setUser($this->editingteachers[0]);
1436         $assign = $this->create_instance(array('teamsubmission'=>1,
1437                                                'assignsubmission_onlinetext_enabled'=>1,
1438                                                'submissiondrafts'=>1,
1439                                                'requireallteammemberssubmit'=>1));
1440         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1442         // Add a submission.
1443         $this->setUser($this->extrastudents[0]);
1444         $data = new stdClass();
1445         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1446                                          'text'=>'Submission text',
1447                                          'format'=>FORMAT_MOODLE);
1449         $notices = array();
1450         $assign->save_submission($data, $notices);
1452         // Check we can see the submit button.
1453         $output = $assign->view_student_summary($this->extrastudents[0], true);
1454         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1456         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1457         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1458         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1460         // Check that the student does not see "Submit" button.
1461         $output = $assign->view_student_summary($this->extrastudents[0], true);
1462         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1464         // Change to another user in the same group.
1465         $this->setUser($this->extrastudents[self::GROUP_COUNT]);
1466         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1467         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1469         $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
1470         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1471         $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
1472         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1473         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1474     }
1476     public function test_group_submissions_submit_for_marking() {
1477         global $PAGE;
1479         $this->create_extra_users();
1480         // Now verify group assignments.
1481         $this->setUser($this->editingteachers[0]);
1482         $time = time();
1483         $assign = $this->create_instance(array('teamsubmission'=>1,
1484                                                'assignsubmission_onlinetext_enabled'=>1,
1485                                                'submissiondrafts'=>1,
1486                                                'requireallteammemberssubmit'=>0,
1487                                                'duedate' => $time - 2*24*60*60));
1488         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1490         $this->setUser($this->extrastudents[0]);
1491         // Add a submission.
1492         $data = new stdClass();
1493         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1494                                          'text'=>'Submission text',
1495                                          'format'=>FORMAT_MOODLE);
1497         $notices = array();
1498         $assign->save_submission($data, $notices);
1500         // Check we can see the submit button.
1501         $output = $assign->view_student_summary($this->extrastudents[0], true);
1502         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1503         $this->assertContains(get_string('timeremaining', 'assign'), $output);
1504         $difftime = time() - $time;
1505         $this->assertContains(get_string('overdue', 'assign', format_time(2*24*60*60 + $difftime)), $output);
1507         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1508         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1509         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1511         // Check that the student does not see "Submit" button.
1512         $output = $assign->view_student_summary($this->extrastudents[0], true);
1513         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1515         // Change to another user in the same group.
1516         $this->setUser($this->extrastudents[self::GROUP_COUNT]);
1517         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1518         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1520         // Check that time remaining is not overdue.
1521         $this->assertContains(get_string('timeremaining', 'assign'), $output);
1522         $difftime = time() - $time;
1523         $this->assertContains(get_string('submittedlate', 'assign', format_time(2*24*60*60 + $difftime)), $output);
1525         $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
1526         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1527         $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
1528         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1529         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1530     }
1532     public function test_submissions_open() {
1533         $this->setUser($this->editingteachers[0]);
1535         $now = time();
1536         $tomorrow = $now + 24*60*60;
1537         $oneweek = $now + 7*24*60*60;
1538         $yesterday = $now - 24*60*60;
1540         $assign = $this->create_instance();
1541         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1543         $assign = $this->create_instance(array('duedate'=>$tomorrow));
1544         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1546         $assign = $this->create_instance(array('duedate'=>$yesterday));
1547         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1549         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$tomorrow));
1550         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1552         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$yesterday));
1553         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
1555         $assign->testable_save_user_extension($this->students[0]->id, $tomorrow);
1556         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1558         $assign = $this->create_instance(array('submissiondrafts'=>1));
1559         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1561         $this->setUser($this->students[0]);
1562         $now = time();
1563         $submission = $assign->get_user_submission($this->students[0]->id, true);
1564         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1565         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1566         $this->setUser($this->editingteachers[0]);
1567         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
1568     }
1570     public function test_get_graders() {
1571         $this->create_extra_users();
1572         $this->setUser($this->editingteachers[0]);
1574         // Create an assignment with no groups.
1575         $assign = $this->create_instance();
1576         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
1577                            self::DEFAULT_EDITING_TEACHER_COUNT +
1578                            self::EXTRA_TEACHER_COUNT +
1579                            self::EXTRA_EDITING_TEACHER_COUNT,
1580                            $assign->testable_get_graders($this->students[0]->id));
1582         // Force create an assignment with SEPARATEGROUPS.
1583         $data = new stdClass();
1584         $data->courseid = $this->course->id;
1585         $data->name = 'Grouping';
1586         $groupingid = groups_create_grouping($data);
1587         groups_assign_grouping($groupingid, $this->groups[0]->id);
1588         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1590         $this->setUser($this->students[1]);
1591         $this->assertCount(4, $assign->testable_get_graders($this->students[0]->id));
1592         // Note the second student is in a group that is not in the grouping.
1593         // This means that we get all graders that are not in a group in the grouping.
1594         $this->assertCount(10, $assign->testable_get_graders($this->students[1]->id));
1595     }
1597     public function test_get_notified_users() {
1598         global $CFG, $DB;
1600         $capability = 'mod/assign:receivegradernotifications';
1601         $coursecontext = context_course::instance($this->course->id);
1602         $role = $DB->get_record('role', array('shortname' => 'teacher'));
1604         $this->create_extra_users();
1605         $this->setUser($this->editingteachers[0]);
1607         // Create an assignment with no groups.
1608         $assign = $this->create_instance();
1610         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
1611                            self::DEFAULT_EDITING_TEACHER_COUNT +
1612                            self::EXTRA_TEACHER_COUNT +
1613                            self::EXTRA_EDITING_TEACHER_COUNT,
1614                            $assign->testable_get_notifiable_users($this->students[0]->id));
1616         // Change nonediting teachers role to not receive grader notifications.
1617         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
1619         $this->assertCount(self::DEFAULT_EDITING_TEACHER_COUNT +
1620                            self::EXTRA_EDITING_TEACHER_COUNT,
1621                            $assign->testable_get_notifiable_users($this->students[0]->id));
1623         // Reset nonediting teachers role to default.
1624         unassign_capability($capability, $role->id, $coursecontext);
1626         // Force create an assignment with SEPARATEGROUPS.
1627         $data = new stdClass();
1628         $data->courseid = $this->course->id;
1629         $data->name = 'Grouping';
1630         $groupingid = groups_create_grouping($data);
1631         groups_assign_grouping($groupingid, $this->groups[0]->id);
1632         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1634         $this->setUser($this->students[1]);
1635         $this->assertCount(4, $assign->testable_get_notifiable_users($this->students[0]->id));
1636         // Note the second student is in a group that is not in the grouping.
1637         // This means that we get all graders that are not in a group in the grouping.
1638         $this->assertCount(10, $assign->testable_get_notifiable_users($this->students[1]->id));
1640         // Change nonediting teachers role to not receive grader notifications.
1641         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
1643         $this->assertCount(2, $assign->testable_get_notifiable_users($this->students[0]->id));
1644         // Note the second student is in a group that is not in the grouping.
1645         // This means that we get all graders that are not in a group in the grouping.
1646         $this->assertCount(5, $assign->testable_get_notifiable_users($this->students[1]->id));
1647     }
1649     public function test_group_members_only() {
1650         global $CFG;
1652         $this->setAdminUser();
1653         $this->create_extra_users();
1654         $CFG->enableavailability = true;
1655         $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id));
1656         groups_assign_grouping($grouping->id, $this->groups[0]->id);
1658         // Force create an assignment with SEPARATEGROUPS.
1659         $instance = $this->getDataGenerator()->create_module('assign', array('course'=>$this->course->id),
1660                 array('availability' => json_encode(\core_availability\tree::get_root_json(array(
1661                     \availability_grouping\condition::get_json()))),
1662                 'groupingid' => $grouping->id));
1664         $cm = get_coursemodule_from_instance('assign', $instance->id);
1665         $context = context_module::instance($cm->id);
1666         $assign = new testable_assign($context, $cm, $this->course);
1668         $this->setUser($this->teachers[0]);
1669         get_fast_modinfo($this->course, 0, true);
1670         $this->assertCount(5, $assign->list_participants(0, true));
1672     }
1674     public function test_get_uniqueid_for_user() {
1675         $this->setUser($this->editingteachers[0]);
1676         $assign = $this->create_instance();
1678         foreach ($this->students as $student) {
1679             $uniqueid = $assign->get_uniqueid_for_user($student->id);
1680             $this->assertEquals($student->id, $assign->get_user_id_for_uniqueid($uniqueid));
1681         }
1682     }
1684     public function test_show_student_summary() {
1685         global $CFG, $PAGE;
1687         $this->setUser($this->editingteachers[0]);
1688         $assign = $this->create_instance();
1689         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1691         // No feedback should be available because this student has not been graded.
1692         $this->setUser($this->students[0]);
1693         $output = $assign->view_student_summary($this->students[0], true);
1694         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if there is no grade');
1695         // Simulate adding a grade.
1696         $this->setUser($this->teachers[0]);
1697         $data = new stdClass();
1698         $data->grade = '50.0';
1699         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1701         // Now we should see the feedback.
1702         $this->setUser($this->students[0]);
1703         $output = $assign->view_student_summary($this->students[0], true);
1704         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback if there is a grade');
1706         // Now hide the grade in gradebook.
1707         $this->setUser($this->teachers[0]);
1708         require_once($CFG->libdir.'/gradelib.php');
1709         $gradeitem = new grade_item(array(
1710             'itemtype'      => 'mod',
1711             'itemmodule'    => 'assign',
1712             'iteminstance'  => $assign->get_instance()->id,
1713             'courseid'      => $this->course->id));
1715         $gradeitem->set_hidden(1, false);
1717         // No feedback should be available because the grade is hidden.
1718         $this->setUser($this->students[0]);
1719         $output = $assign->view_student_summary($this->students[0], true);
1720         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
1722         // Do the same but add feedback.
1723         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
1725         $this->setUser($this->teachers[0]);
1726         $grade = $assign->get_user_grade($this->students[0]->id, true);
1727         $data = new stdClass();
1728         $data->assignfeedbackcomments_editor = array('text'=>'Tomato sauce',
1729                                          'format'=>FORMAT_MOODLE);
1730         $plugin = $assign->get_feedback_plugin_by_type('comments');
1731         $plugin->save($grade, $data);
1733         // Should have feedback but no grade.
1734         $this->setUser($this->students[0]);
1735         $output = $assign->view_student_summary($this->students[0], true);
1736         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback even if there is no grade');
1737         $this->assertEquals(false, strpos($output, 'Grade'), 'Do not show grade when there is no grade.');
1738         $this->assertEquals(false, strpos($output, 'Graded on'), 'Do not show graded date when there is no grade.');
1740         // Now hide the grade in gradebook.
1741         $this->setUser($this->teachers[0]);
1742         $gradeitem = new grade_item(array(
1743             'itemtype'      => 'mod',
1744             'itemmodule'    => 'assign',
1745             'iteminstance'  => $assign->get_instance()->id,
1746             'courseid'      => $this->course->id));
1748         $gradeitem->set_hidden(1, false);
1750         // No feedback should be available because the grade is hidden.
1751         $this->setUser($this->students[0]);
1752         $output = $assign->view_student_summary($this->students[0], true);
1753         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
1754     }
1756     public function test_attempt_reopen_method_manual() {
1757         global $PAGE;
1759         $this->setUser($this->editingteachers[0]);
1760         $assign = $this->create_instance(array('attemptreopenmethod'=>ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
1761                                                'maxattempts'=>3,
1762                                                'submissiondrafts'=>1,
1763                                                'assignsubmission_onlinetext_enabled'=>1));
1764         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1766         // Student should be able to see an add submission button.
1767         $this->setUser($this->students[0]);
1768         $output = $assign->view_student_summary($this->students[0], true);
1769         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1771         // Add a submission.
1772         $now = time();
1773         $submission = $assign->get_user_submission($this->students[0]->id, true);
1774         $data = new stdClass();
1775         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1776                                          'text'=>'Submission text',
1777                                          'format'=>FORMAT_MOODLE);
1778         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1779         $plugin->save($submission, $data);
1781         // And now submit it for marking.
1782         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1783         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1785         // Verify the student cannot make changes to the submission.
1786         $output = $assign->view_student_summary($this->students[0], true);
1787         $this->assertEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1789         // Mark the submission.
1790         $this->setUser($this->teachers[0]);
1791         $data = new stdClass();
1792         $data->grade = '50.0';
1793         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1795         // Check the student can see the grade.
1796         $this->setUser($this->students[0]);
1797         $output = $assign->view_student_summary($this->students[0], true);
1798         $this->assertNotEquals(false, strpos($output, '50.0'));
1800         // Allow the student another attempt.
1801         $this->teachers[0]->ignoresesskey = true;
1802         $this->setUser($this->teachers[0]);
1803         $result = $assign->testable_process_add_attempt($this->students[0]->id);
1804         $this->assertEquals(true, $result);
1806         // Check that the previous attempt is now in the submission history table.
1807         $this->setUser($this->students[0]);
1808         $output = $assign->view_student_summary($this->students[0], true);
1809         // Need a better check.
1810         $this->assertNotEquals(false, strpos($output, 'Submission text'), 'Contains: Submission text');
1812         // Check that the student now has a button for Add a new attempt".
1813         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1814         // Check that the student now does not have a button for Submit.
1815         $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
1817         // Check that the student now has a submission history.
1818         $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
1820         $this->setUser($this->teachers[0]);
1821         // Check that the grading table loads correctly and contains this user.
1822         // This is also testing that we do not get duplicate rows in the grading table.
1823         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1824         $output = $assign->get_renderer()->render($gradingtable);
1825         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
1827         // Should be 1 not 2.
1828         $this->assertEquals(1, $assign->count_submissions());
1829         $this->assertEquals(1, $assign->count_submissions_with_status('reopened'));
1830         $this->assertEquals(0, $assign->count_submissions_need_grading());
1831         $this->assertEquals(1, $assign->count_grades());
1833         // Change max attempts to unlimited.
1834         $formdata = clone($assign->get_instance());
1835         $formdata->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
1836         $formdata->instance = $formdata->id;
1837         $assign->update_instance($formdata);
1839         // Mark the submission again.
1840         $data = new stdClass();
1841         $data->grade = '60.0';
1842         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1844         // Check the grade exists.
1845         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1846         $this->assertEquals(60, (int)$grades[$this->students[0]->id]->rawgrade);
1848         // Check we can reopen still.
1849         $result = $assign->testable_process_add_attempt($this->students[0]->id);
1850         $this->assertEquals(true, $result);
1852         // Should no longer have a grade because there is no grade for the latest attempt.
1853         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1854         $this->assertEmpty($grades);
1856     }
1858     /**
1859      * Test reopen behavior when in "Reopen until pass" mode.
1860      */
1861     public function test_attempt_reopen_method_untilpass() {
1862         global $PAGE;
1864         $this->setUser($this->editingteachers[0]);
1865         $assign = $this->create_instance(array('attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS,
1866                 'maxattempts' => 3,
1867                 'submissiondrafts' => 1,
1868                 'assignsubmission_onlinetext_enabled' => 1));
1869         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1871         // Set grade to pass to 80.
1872         $gradeitem = $assign->get_grade_item();
1873         $gradeitem->gradepass = '80.0';
1874         $gradeitem->update();
1876         // Student should be able to see an add submission button.
1877         $this->setUser($this->students[0]);
1878         $output = $assign->view_student_summary($this->students[0], true);
1879         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1881         // Add a submission.
1882         $now = time();
1883         $submission = $assign->get_user_submission($this->students[0]->id, true);
1884         $data = new stdClass();
1885         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1886                 'text' => 'Submission text',
1887                 'format' => FORMAT_MOODLE);
1888         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1889         $plugin->save($submission, $data);
1891         // And now submit it for marking.
1892         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1893         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1895         // Verify the student cannot make a new attempt.
1896         $output = $assign->view_student_summary($this->students[0], true);
1897         $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1899         // Mark the submission as non-passing.
1900         $this->setUser($this->teachers[0]);
1901         $data = new stdClass();
1902         $data->grade = '50.0';
1903         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1905         // Check the student can see the grade.
1906         $this->setUser($this->students[0]);
1907         $output = $assign->view_student_summary($this->students[0], true);
1908         $this->assertNotEquals(false, strpos($output, '50.0'));
1910         // Check that the student now has a button for Add a new attempt.
1911         $output = $assign->view_student_summary($this->students[0], true);
1912         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1914         // Check that the student now does not have a button for Submit.
1915         $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
1917         // Check that the student now has a submission history.
1918         $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
1920         // Add a second submission.
1921         $now = time();
1922         $submission = $assign->get_user_submission($this->students[0]->id, true, 1);
1923         $data = new stdClass();
1924         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1925                 'text' => 'Submission text',
1926                 'format' => FORMAT_MOODLE);
1927         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1928         $plugin->save($submission, $data);
1930         // And now submit it for marking.
1931         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1932         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1934         // Mark the submission as passing.
1935         $this->setUser($this->teachers[0]);
1936         $data = new stdClass();
1937         $data->grade = '80.0';
1938         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1940         // Check that the student does not have a button for Add a new attempt.
1941         $this->setUser($this->students[0]);
1942         $output = $assign->view_student_summary($this->students[0], true);
1943         $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1945         // Re-mark the submission as not passing.
1946         $this->setUser($this->teachers[0]);
1947         $data = new stdClass();
1948         $data->grade = '50.0';
1949         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1951         // Check that the student now has a button for Add a new attempt.
1952         $this->setUser($this->students[0]);
1953         $output = $assign->view_student_summary($this->students[0], true);
1954         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1956         // Add a submission as a second student.
1957         $this->setUser($this->students[1]);
1958         $now = time();
1959         $submission = $assign->get_user_submission($this->students[1]->id, true);
1960         $data = new stdClass();
1961         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1962                 'text' => 'Submission text',
1963                 'format' => FORMAT_MOODLE);
1964         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1965         $plugin->save($submission, $data);
1967         // And now submit it for marking.
1968         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1969         $assign->testable_update_submission($submission, $this->students[1]->id, true, false);
1971         // Mark the submission as passing.
1972         $this->setUser($this->teachers[0]);
1973         $data = new stdClass();
1974         $data->grade = '100.0';
1975         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
1977         // Check the student can see the grade.
1978         $this->setUser($this->students[1]);
1979         $output = $assign->view_student_summary($this->students[1], true);
1980         $this->assertNotEquals(false, strpos($output, '100.0'));
1982         // Check that the student does not have a button for Add a new attempt.
1983         $output = $assign->view_student_summary($this->students[1], true);
1984         $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1986         // Set grade to pass to 0, so that no attempts should reopen.
1987         $gradeitem = $assign->get_grade_item();
1988         $gradeitem->gradepass = '0';
1989         $gradeitem->update();
1991         // Add another submission.
1992         $this->setUser($this->students[2]);
1993         $now = time();
1994         $submission = $assign->get_user_submission($this->students[2]->id, true);
1995         $data = new stdClass();
1996         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1997                 'text' => 'Submission text',
1998                 'format' => FORMAT_MOODLE);
1999         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2000         $plugin->save($submission, $data);
2002         // And now submit it for marking.
2003         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2004         $assign->testable_update_submission($submission, $this->students[2]->id, true, false);
2006         // Mark the submission as graded.
2007         $this->setUser($this->teachers[0]);
2008         $data = new stdClass();
2009         $data->grade = '0.0';
2010         $assign->testable_apply_grade_to_user($data, $this->students[2]->id, 0);
2012         // Check the student can see the grade.
2013         $this->setUser($this->students[2]);
2014         $output = $assign->view_student_summary($this->students[2], true);
2015         $this->assertNotEquals(false, strpos($output, '0.0'));
2017         // Check that the student does not have a button for Add a new attempt.
2018         $output = $assign->view_student_summary($this->students[2], true);
2019         $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2020     }
2023     public function test_markingworkflow() {
2024         global $PAGE;
2026         $this->setUser($this->editingteachers[0]);
2027         $assign = $this->create_instance(array('markingworkflow'=>1));
2028         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2030         // Mark the submission and set to notmarked.
2031         $this->setUser($this->teachers[0]);
2032         $data = new stdClass();
2033         $data->grade = '50.0';
2034         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED;
2035         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2037         // Check the student can't see the grade.
2038         $this->setUser($this->students[0]);
2039         $output = $assign->view_student_summary($this->students[0], true);
2040         $this->assertEquals(false, strpos($output, '50.0'));
2042         // Make sure the grade isn't pushed to the gradebook.
2043         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2044         $this->assertEmpty($grades);
2046         // Mark the submission and set to inmarking.
2047         $this->setUser($this->teachers[0]);
2048         $data = new stdClass();
2049         $data->grade = '50.0';
2050         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INMARKING;
2051         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2053         // Check the student can't see the grade.
2054         $this->setUser($this->students[0]);
2055         $output = $assign->view_student_summary($this->students[0], true);
2056         $this->assertEquals(false, strpos($output, '50.0'));
2058         // Make sure the grade isn't pushed to the gradebook.
2059         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2060         $this->assertEmpty($grades);
2062         // Mark the submission and set to readyforreview.
2063         $this->setUser($this->teachers[0]);
2064         $data = new stdClass();
2065         $data->grade = '50.0';
2066         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW;
2067         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2069         // Check the student can't see the grade.
2070         $this->setUser($this->students[0]);
2071         $output = $assign->view_student_summary($this->students[0], true);
2072         $this->assertEquals(false, strpos($output, '50.0'));
2074         // Make sure the grade isn't pushed to the gradebook.
2075         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2076         $this->assertEmpty($grades);
2078         // Mark the submission and set to inreview.
2079         $this->setUser($this->teachers[0]);
2080         $data = new stdClass();
2081         $data->grade = '50.0';
2082         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW;
2083         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2085         // Check the student can't see the grade.
2086         $this->setUser($this->students[0]);
2087         $output = $assign->view_student_summary($this->students[0], true);
2088         $this->assertEquals(false, strpos($output, '50.0'));
2090         // Make sure the grade isn't pushed to the gradebook.
2091         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2092         $this->assertEmpty($grades);
2094         // Mark the submission and set to readyforrelease.
2095         $this->setUser($this->teachers[0]);
2096         $data = new stdClass();
2097         $data->grade = '50.0';
2098         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
2099         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2101         // Check the student can't see the grade.
2102         $this->setUser($this->students[0]);
2103         $output = $assign->view_student_summary($this->students[0], true);
2104         $this->assertEquals(false, strpos($output, '50.0'));
2106         // Make sure the grade isn't pushed to the gradebook.
2107         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2108         $this->assertEmpty($grades);
2110         // Mark the submission and set to released.
2111         $this->setUser($this->teachers[0]);
2112         $data = new stdClass();
2113         $data->grade = '50.0';
2114         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
2115         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2117         // Check the student can see the grade.
2118         $this->setUser($this->students[0]);
2119         $output = $assign->view_student_summary($this->students[0], true);
2120         $this->assertNotEquals(false, strpos($output, '50.0'));
2122         // Make sure the grade is pushed to the gradebook.
2123         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2124         $this->assertEquals(50, (int)$grades[$this->students[0]->id]->rawgrade);
2125     }
2127     public function test_markerallocation() {
2128         global $PAGE;
2130         $this->setUser($this->editingteachers[0]);
2131         $assign = $this->create_instance(array('markingworkflow'=>1, 'markingallocation'=>1));
2132         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2134         // Allocate marker to submission.
2135         $data = new stdClass();
2136         $data->allocatedmarker = $this->teachers[0]->id;
2137         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2139         // Check the allocated marker can view the submission.
2140         $this->setUser($this->teachers[0]);
2141         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
2142         $output = $assign->get_renderer()->render($gradingtable);
2143         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
2145         // Check that other teachers can't view this submission.
2146         $this->setUser($this->teachers[1]);
2147         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
2148         $output = $assign->get_renderer()->render($gradingtable);
2149         $this->assertNotEquals(true, strpos($output, $this->students[0]->lastname));
2150     }
2152     /**
2153      * @expectedException moodle_exception
2154      */
2155     public function test_teacher_submit_for_student() {
2156         global $PAGE;
2158         $this->preventResetByRollback();
2159         $sink = $this->redirectMessages();
2161         $this->setUser($this->editingteachers[0]);
2163         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
2164         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2166         $this->setUser($this->students[0]);
2167         // Simulate a submission.
2168         $data = new stdClass();
2169         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2170                                          'text'=>'Student submission text',
2171                                          'format'=>FORMAT_MOODLE);
2173         $notices = array();
2174         $assign->save_submission($data, $notices);
2176         // Check that the submission text was saved.
2177         $output = $assign->view_student_summary($this->students[0], true);
2178         $this->assertContains('Student submission text', $output, 'Contains student submission text');
2180         // Check that a teacher teacher with the extra capability can edit a students submission.
2181         $this->setUser($this->teachers[0]);
2182         $data = new stdClass();
2183         $data->userid = $this->students[0]->id;
2184         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2185                                          'text'=>'Teacher edited submission text',
2186                                          'format'=>FORMAT_MOODLE);
2188         // Add the required capability.
2189         $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
2190         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
2191         role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
2192         accesslib_clear_all_caches_for_unit_testing();
2194         // Try to save the submission.
2195         $notices = array();
2196         $assign->save_submission($data, $notices);
2198         // Check that the teacher can submit the students work.
2199         $data = new stdClass();
2200         $data->userid = $this->students[0]->id;
2201         $notices = array();
2202         $assign->submit_for_grading($data, $notices);
2204         // Revert to draft so the student can edit it.
2205         $assign->revert_to_draft($this->students[0]->id);
2207         $this->setUser($this->students[0]);
2209         // Check that the submission text was saved.
2210         $output = $assign->view_student_summary($this->students[0], true);
2211         $this->assertContains('Teacher edited submission text', $output, 'Contains student submission text');
2213         // Check that the student can submit their work.
2214         $data = new stdClass();
2215         $assign->submit_for_grading($data, $notices);
2217         $output = $assign->view_student_summary($this->students[0], true);
2218         $this->assertNotContains(get_string('addsubmission', 'assign'), $output);
2220         // Set to a default editing teacher who should not be able to edit this submission.
2221         $this->setUser($this->editingteachers[1]);
2223         // Revert to draft so the submission is editable.
2224         $assign->revert_to_draft($this->students[0]->id);
2226         $data = new stdClass();
2227         $data->userid = $this->students[0]->id;
2228         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2229                                          'text'=>'Teacher 2 edited submission text',
2230                                          'format'=>FORMAT_MOODLE);
2232         $notices = array();
2233         $assign->save_submission($data, $notices);
2235         $sink->close();
2236     }
2238     public function test_disable_submit_after_cutoff_date() {
2239         global $PAGE;
2241         $this->setUser($this->editingteachers[0]);
2242         $now = time();
2243         $tomorrow = $now + 24*60*60;
2244         $lastweek = $now - 7*24*60*60;
2245         $yesterday = $now - 24*60*60;
2247         $assign = $this->create_instance(array('duedate'=>$yesterday,
2248                                                'cutoffdate'=>$tomorrow,
2249                                                'assignsubmission_onlinetext_enabled'=>1));
2250         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2252         // Student should be able to see an add submission button.
2253         $this->setUser($this->students[0]);
2254         $output = $assign->view_student_summary($this->students[0], true);
2255         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2257         // Add a submission but don't submit now.
2258         $submission = $assign->get_user_submission($this->students[0]->id, true);
2259         $data = new stdClass();
2260         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2261                                          'text'=>'Submission text',
2262                                          'format'=>FORMAT_MOODLE);
2263         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2264         $plugin->save($submission, $data);
2266         // Create another instance with cut-off and due-date already passed.
2267         $this->setUser($this->editingteachers[0]);
2268         $now = time();
2269         $assign = $this->create_instance(array('duedate'=>$lastweek,
2270                                                'cutoffdate'=>$yesterday,
2271                                                'assignsubmission_onlinetext_enabled'=>1));
2273         $this->setUser($this->students[0]);
2274         $output = $assign->view_student_summary($this->students[0], true);
2275         $this->assertNotContains($output, get_string('editsubmission', 'assign'),
2276                                  'Should not be able to edit after cutoff date.');
2277         $this->assertNotContains($output, get_string('submitassignment', 'assign'),
2278                                  'Should not be able to submit after cutoff date.');
2279     }
2280     /**
2281      * Testing for submission comment plugin settings
2282      */
2283     public function test_submission_comment_plugin_settings() {
2284         global $CFG;
2286         $commentconfig = false;
2287         if (!empty($CFG->usecomments)) {
2288             $commentconfig = $CFG->usecomments;
2289         }
2291         $CFG->usecomments = true;
2292         $assign = $this->create_instance();
2293         $plugin = $assign->get_submission_plugin_by_type('comments');
2294         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2296         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
2297         $plugin = $assign->get_submission_plugin_by_type('comments');
2298         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2300         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
2301         $plugin = $assign->get_submission_plugin_by_type('comments');
2302         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2304         $CFG->usecomments = false;
2305         $assign = $this->create_instance();
2306         $plugin = $assign->get_submission_plugin_by_type('comments');
2307         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2309         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
2310         $plugin = $assign->get_submission_plugin_by_type('comments');
2311         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2313         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
2314         $plugin = $assign->get_submission_plugin_by_type('comments');
2315         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2317         $CFG->usecomments = $commentconfig;
2318     }
2320     /**
2321      * Testing for comment inline settings
2322      */
2323     public function test_feedback_comment_commentinline() {
2324         global $CFG;
2326         $sourcetext = "Hello!
2328 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
2330 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
2331 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
2333 External link 1:<img src='https://moodle.org/logo/logo-240x60.gif' alt='Moodle'/>
2334 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\"/>
2335 Internal link 1:<img src='@@PLUGINFILE@@/logo-240x60.gif' alt='Moodle'/>
2336 Internal link 2:<img alt=\"Moodle\" src=\"@@PLUGINFILE@@logo-240x60.gif\"/>
2337 Anchor link 1:<a href=\"@@PLUGINFILE@@logo-240x60.gif\" alt=\"bananas\">Link text</a>
2338 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
2339 ";
2341         // Note the internal images have been stripped and the html is purified (quotes fixed in this case).
2342         $filteredtext = "Hello!
2344 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
2346 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
2347 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
2349 External link 1:<img src=\"https://moodle.org/logo/logo-240x60.gif\" alt=\"Moodle\" />
2350 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\" />
2351 Internal link 1:
2352 Internal link 2:
2353 Anchor link 1:Link text
2354 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
2355 ";
2357         $this->setUser($this->editingteachers[0]);
2358         $params = array('assignsubmission_onlinetext_enabled' => 1,
2359                         'assignfeedback_comments_enabled' => 1,
2360                         'assignfeedback_comments_commentinline' => 1);
2361         $assign = $this->create_instance($params);
2363         $this->setUser($this->students[0]);
2364         // Add a submission but don't submit now.
2365         $submission = $assign->get_user_submission($this->students[0]->id, true);
2366         $data = new stdClass();
2368         // Test the internal link is stripped, but the external one is not.
2369         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2370                                          'text'=>$sourcetext,
2371                                          'format'=>FORMAT_MOODLE);
2373         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2374         $plugin->save($submission, $data);
2376         $this->setUser($this->editingteachers[0]);
2378         $data = new stdClass();
2379         require_once($CFG->dirroot . '/mod/assign/gradeform.php');
2380         $pagination = array('userid'=>$this->students[0]->id,
2381                             'rownum'=>0,
2382                             'last'=>true,
2383                             'useridlistid' => $assign->get_useridlist_key_id(),
2384                             'attemptnumber'=>0);
2385         $formparams = array($assign, $data, $pagination);
2386         $mform = new mod_assign_grade_form(null, $formparams);
2388         $this->assertEquals($filteredtext, $data->assignfeedbackcomments_editor['text']);
2389     }
2391     /**
2392      * Testing for feedback comment plugin settings
2393      */
2394     public function test_feedback_plugin_settings() {
2396         $assign = $this->create_instance();
2397         $plugin = $assign->get_feedback_plugin_by_type('comments');
2398         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2400         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 0));
2401         $plugin = $assign->get_feedback_plugin_by_type('comments');
2402         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2404         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
2405         $plugin = $assign->get_feedback_plugin_by_type('comments');
2406         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2407     }
2409     /**
2410      * Testing if gradebook feedback plugin is enabled.
2411      */
2412     public function test_is_gradebook_feedback_enabled() {
2413         $adminconfig = get_config('assign');
2414         $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
2416         // Create assignment with gradebook feedback enabled and grade = 0.
2417         $assign = $this->create_instance(array($gradebookplugin . '_enabled' => 1, 'grades' => 0));
2419         // Get gradebook feedback plugin.
2420         $gradebookplugintype = str_replace('assignfeedback_', '', $gradebookplugin);
2421         $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
2422         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2423         $this->assertEquals(1, $assign->is_gradebook_feedback_enabled());
2425         // Create assignment with gradebook feedback disabled and grade = 0.
2426         $assign = $this->create_instance(array($gradebookplugin . '_enabled' => 0, 'grades' => 0));
2427         $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
2428         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2429     }
2431     /**
2432      * Testing can_edit_submission
2433      */
2434     public function test_can_edit_submission() {
2435         global $PAGE, $DB;
2436         $this->create_extra_users();
2438         $this->setAdminUser();
2439         // Create assignment (onlinetext).
2440         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
2441         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2443         // Check student can edit their own submission.
2444         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->students[0]->id));
2445         // Check student cannot edit others submission.
2446         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->students[1]->id));
2448         // Check teacher cannot (by default) edit a students submission.
2449         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
2451         // Add the required capability to edit a student submission.
2452         $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
2453         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
2454         role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
2455         accesslib_clear_all_caches_for_unit_testing();
2456         // Retest - should now have access.
2457         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
2459         // Force create an assignment with SEPARATEGROUPS.
2460         $data = new stdClass();
2461         $data->courseid = $this->course->id;
2462         $data->name = 'Grouping';
2463         $groupingid = groups_create_grouping($data);
2464         groups_assign_grouping($groupingid, $this->groups[0]->id);
2465         groups_assign_grouping($groupingid, $this->groups[1]->id);
2466         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
2468         // Add the capability to the new assignment for extra students 0 and 1.
2469         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
2470         role_assign($roleid, $this->extrastudents[0]->id, $assign->get_context()->id);
2471         role_assign($roleid, $this->extrastudents[1]->id, $assign->get_context()->id);
2472         accesslib_clear_all_caches_for_unit_testing();
2474         // Verify the extra student does not have the capability to edit a submission not in their group.
2475         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[1]->id));
2476         // Verify the extra student does have the capability to edit a submission in their group.
2477         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[0]->id));
2479     }
2481     /**
2482      * Test if the view blind details capability works
2483      */
2484     public function test_can_view_blind_details() {
2485         global $DB;
2486         // Note: The shared setUp leads to terrible tests. Please don't use it.
2487         $roles = $DB->get_records('role', null, '', 'shortname, id');
2488         $course = $this->getDataGenerator()->create_course([]);
2490         $student = $this->students[0];// Get a student user.
2491         $this->getDataGenerator()->enrol_user($student->id,
2492                                               $course->id,
2493                                               $roles['student']->id);
2495         // Create a teacher. Shouldn't be able to view blind marking ID.
2496         $teacher = $this->getDataGenerator()->create_user();
2498         $this->getDataGenerator()->enrol_user($teacher->id,
2499                                               $course->id,
2500                                               $roles['teacher']->id);
2502         // Create a manager.. Should be able to view blind marking ID.
2503         $manager = $this->getDataGenerator()->create_user();
2504         $this->getDataGenerator()->enrol_user($manager->id,
2505                                               $course->id,
2506                                               $roles['manager']->id);
2508         // Generate blind marking assignment.
2509         $assign = $this->create_instance(array('course' => $course->id, 'blindmarking' => 1));
2510         $this->assertEquals(true, $assign->is_blind_marking());
2512         // Test student names are hidden to teacher.
2513         $this->setUser($teacher);
2514         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
2515         $output = $assign->get_renderer()->render($gradingtable);
2516         $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));    // "Participant" is somewhere on the page.
2517         $this->assertEquals(false, strpos($output, fullname($student)));    // Students full name doesn't appear.
2519         // Test student names are visible to manager.
2520         $this->setUser($manager);
2521         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
2522         $output = $assign->get_renderer()->render($gradingtable);
2523         $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));
2524         $this->assertEquals(true, strpos($output, fullname($student)));
2525     }
2527     /**
2528      * Testing get_shared_group_members
2529      */
2530     public function test_get_shared_group_members() {
2531         $this->create_extra_users();
2532         $this->setAdminUser();
2534         // Force create an assignment with SEPARATEGROUPS.
2535         $data = new stdClass();
2536         $data->courseid = $this->course->id;
2537         $data->name = 'Grouping';
2538         $groupingid = groups_create_grouping($data);
2539         groups_assign_grouping($groupingid, $this->groups[0]->id);
2540         groups_assign_grouping($groupingid, $this->groups[1]->id);
2541         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
2542         $cm = $assign->get_course_module();
2544         // Add the capability to access allgroups.
2545         $roleid = create_role('Access all groups role', 'accessallgroupsrole', '');
2546         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $roleid, $assign->get_context()->id);
2547         role_assign($roleid, $this->extrastudents[3]->id, $assign->get_context()->id);
2548         accesslib_clear_all_caches_for_unit_testing();
2550         // Get shared group members for students 0 and 1.
2551         $groupmembers = array();
2552         $groupmembers[0] = $assign->get_shared_group_members($cm, $this->students[0]->id);
2553         $groupmembers[1] = $assign->get_shared_group_members($cm, $this->students[1]->id);
2555         // They should share groups with extrastudents 0 and 1.
2556         $this->assertTrue(in_array($this->extrastudents[0]->id, $groupmembers[0]));
2557         $this->assertFalse(in_array($this->extrastudents[0]->id, $groupmembers[1]));
2558         $this->assertTrue(in_array($this->extrastudents[1]->id, $groupmembers[1]));
2559         $this->assertFalse(in_array($this->extrastudents[1]->id, $groupmembers[0]));
2561         // Lists of group members for students and extrastudents should be the same.
2562         $this->assertEquals($groupmembers[0], $assign->get_shared_group_members($cm, $this->extrastudents[0]->id));
2563         $this->assertEquals($groupmembers[1], $assign->get_shared_group_members($cm, $this->extrastudents[1]->id));
2565         // Get all group members for extrastudent 3 wich can access all groups.
2566         $allgroupmembers = $assign->get_shared_group_members($cm, $this->extrastudents[3]->id);
2568         // Extrastudent 3 should see students 0 and 1, extrastudent 0 and 1.
2569         $this->assertTrue(in_array($this->students[0]->id, $allgroupmembers));
2570         $this->assertTrue(in_array($this->students[1]->id, $allgroupmembers));
2571         $this->assertTrue(in_array($this->extrastudents[0]->id, $allgroupmembers));
2572         $this->assertTrue(in_array($this->extrastudents[1]->id , $allgroupmembers));
2573     }
2575     /**
2576      * Test get plugins file areas
2577      */
2578     public function test_get_plugins_file_areas() {
2579         $this->setUser($this->editingteachers[0]);
2580         $assign = $this->create_instance();
2582         // Test that all the submission and feedback plugins are returning the expected file aras.
2583         $usingfilearea = 0;
2584         $coreplugins = core_plugin_manager::standard_plugins_list('assignsubmission');
2585         foreach ($assign->get_submission_plugins() as $plugin) {
2586             $type = $plugin->get_type();
2587             if (!in_array($type, $coreplugins)) {
2588                 continue;
2589             }
2590             $fileareas = $plugin->get_file_areas();
2592             if ($type == 'onlinetext') {
2593                 $this->assertEquals(array('submissions_onlinetext' => 'Online text'), $fileareas);
2594                 $usingfilearea++;
2595             } else if ($type == 'file') {
2596                 $this->assertEquals(array('submission_files' => 'File submissions'), $fileareas);
2597                 $usingfilearea++;
2598             } else {
2599                 $this->assertEmpty($fileareas);
2600             }
2601         }
2602         $this->assertEquals(2, $usingfilearea);
2604         $usingfilearea = 0;
2605         $coreplugins = core_plugin_manager::standard_plugins_list('assignfeedback');
2606         foreach ($assign->get_feedback_plugins() as $plugin) {
2607             $type = $plugin->get_type();
2608             if (!in_array($type, $coreplugins)) {
2609                 continue;
2610             }
2611             $fileareas = $plugin->get_file_areas();
2613             if ($type == 'editpdf') {
2614                 $this->assertEquals(array('download' => 'Annotate PDF'), $fileareas);
2615                 $usingfilearea++;
2616             } else if ($type == 'file') {
2617                 $this->assertEquals(array('feedback_files' => 'Feedback files'), $fileareas);
2618                 $usingfilearea++;
2619             } else {
2620                 $this->assertEmpty($fileareas);
2621             }
2622         }
2623         $this->assertEquals(2, $usingfilearea);
2624     }
2626     /**
2627      * Test the quicksave grades processor
2628      */
2629     public function test_process_save_quick_grades() {
2630         $this->editingteachers[0]->ignoresesskey = true;
2631         $this->setUser($this->editingteachers[0]);
2633         $assign = $this->create_instance(array('attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL));
2635         // Initially grade the user.
2636         $grade = $assign->get_user_grade($this->students[0]->id, false);
2637         if (!$grade) {
2638             $grade = new stdClass();
2639             $grade->attemptnumber = '';
2640             $grade->timemodified = '';
2641         }
2642         $data = array(
2643             'grademodified_' . $this->students[0]->id => $grade->timemodified,
2644             'gradeattempt_' . $this->students[0]->id => $grade->attemptnumber,
2645             'quickgrade_' . $this->students[0]->id => '60.0'
2646         );
2647         $result = $assign->testable_process_save_quick_grades($data);
2648         $this->assertContains(get_string('quickgradingchangessaved', 'assign'), $result);
2649         $grade = $assign->get_user_grade($this->students[0]->id, false);
2650         $this->assertEquals('60.0', $grade->grade);
2652         // Attempt to grade with a past attempts grade info.
2653         $assign->testable_process_add_attempt($this->students[0]->id);
2654         $data = array(
2655             'grademodified_' . $this->students[0]->id => $grade->timemodified,
2656             'gradeattempt_' . $this->students[0]->id => $grade->attemptnumber,
2657             'quickgrade_' . $this->students[0]->id => '50.0'
2658         );
2659         $result = $assign->testable_process_save_quick_grades($data);
2660         $this->assertContains(get_string('errorrecordmodified', 'assign'), $result);
2661         $grade = $assign->get_user_grade($this->students[0]->id, false);
2662         $this->assertFalse($grade);
2664         // Attempt to grade a the attempt.
2665         $submission = $assign->get_user_submission($this->students[0]->id, false);
2666         $data = array(
2667             'grademodified_' . $this->students[0]->id => '',
2668             'gradeattempt_' . $this->students[0]->id => $submission->attemptnumber,
2669             'quickgrade_' . $this->students[0]->id => '40.0'
2670         );
2671         $result = $assign->testable_process_save_quick_grades($data);
2672         $this->assertContains(get_string('quickgradingchangessaved', 'assign'), $result);
2673         $grade = $assign->get_user_grade($this->students[0]->id, false);
2674         $this->assertEquals('40.0', $grade->grade);
2676         // Catch grade update conflicts.
2677         // Save old data for later.
2678         $pastdata = $data;
2679         // Update the grade the 'good' way.
2680         $data = array(
2681             'grademodified_' . $this->students[0]->id => $grade->timemodified,
2682             'gradeattempt_' . $this->students[0]->id => $grade->attemptnumber,
2683             'quickgrade_' . $this->students[0]->id => '30.0'
2684         );
2685         $result = $assign->testable_process_save_quick_grades($data);
2686         $this->assertContains(get_string('quickgradingchangessaved', 'assign'), $result);
2687         $grade = $assign->get_user_grade($this->students[0]->id, false);
2688         $this->assertEquals('30.0', $grade->grade);
2690         // Now update using 'old' data. Should fail.
2691         $result = $assign->testable_process_save_quick_grades($pastdata);
2692         $this->assertContains(get_string('errorrecordmodified', 'assign'), $result);
2693         $grade = $assign->get_user_grade($this->students[0]->id, false);
2694         $this->assertEquals('30.0', $grade->grade);
2695     }
2697     /**
2698      * Test updating activity completion when submitting an assessment.
2699      */
2700     public function test_update_activity_completion_records_solitary_submission() {
2701         $assign = $this->create_instance(array('grade' => 100,
2702                 'completion' => COMPLETION_TRACKING_AUTOMATIC,
2703                 'requireallteammemberssubmit' => 0));
2705         $cm = $assign->get_course_module();
2707         $student = $this->students[0];
2708         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
2710         $this->setUser($student);
2712         // Simulate a submission.
2713         $data = new stdClass();
2714         $data->onlinetext_editor = array(
2715             'itemid' => file_get_unused_draft_itemid(),
2716             'text' => 'Student submission text',
2717             'format' => FORMAT_MOODLE
2718         );
2719         $completion = new completion_info($this->course);
2721         $notices = array();
2722         $assign->save_submission($data, $notices);
2724         $submission = $assign->get_user_submission($student->id, true);
2726         // Check that completion is not met yet.
2727         $completiondata = $completion->get_data($cm, false, $student->id);
2728         $this->assertEquals(0, $completiondata->completionstate);
2729         $assign->testable_update_activity_completion_records(0, 0, $submission,
2730                 $student->id, COMPLETION_COMPLETE, $completion);
2731         // Completion should now be met.
2732         $completiondata = $completion->get_data($cm, false, $student->id);
2733         $this->assertEquals(1, $completiondata->completionstate);
2734     }
2736     /**
2737      * Test updating activity completion when submitting an assessment.
2738      */
2739     public function test_update_activity_completion_records_team_submission() {
2740         $assign = $this->create_instance(array('grade' => 100,
2741                 'completion' => COMPLETION_TRACKING_AUTOMATIC,
2742                  'teamsubmission' => 1));
2744         $cm = $assign->get_course_module();
2746         $student1 = $this->students[0];
2747         $student2 = $this->students[1];
2748         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
2750         // Put both users into a group.
2751         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
2752         $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $student1->id));
2753         $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $student2->id));
2755         $this->setUser($student1);
2757         // Simulate a submission.
2758         $data = new stdClass();
2759         $data->onlinetext_editor = array(
2760             'itemid' => file_get_unused_draft_itemid(),
2761             'text' => 'Student submission text',
2762             'format' => FORMAT_MOODLE
2763         );
2764         $completion = new completion_info($this->course);
2766         $notices = array();
2767         $assign->save_submission($data, $notices);
2769         $submission = $assign->get_user_submission($student1->id, true);
2770         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2771         $submission->groupid = $group1->id;
2773         // Check that completion is not met yet.
2774         $completiondata = $completion->get_data($cm, false, $student1->id);
2775         $this->assertEquals(0, $completiondata->completionstate);
2776         $completiondata = $completion->get_data($cm, false, $student2->id);
2777         $this->assertEquals(0, $completiondata->completionstate);
2778         $assign->testable_update_activity_completion_records(1, 0, $submission, $student1->id,
2779                 COMPLETION_COMPLETE, $completion);
2780         // Completion should now be met.
2781         $completiondata = $completion->get_data($cm, false, $student1->id);
2782         $this->assertEquals(1, $completiondata->completionstate);
2783         $completiondata = $completion->get_data($cm, false, $student2->id);
2784         $this->assertEquals(1, $completiondata->completionstate);
2785     }