MDL-49852 mod_assign: Unit tests for activity completion.
[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->setExpectedException('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->setExpectedException('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->setExpectedException('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     public function test_list_participants() {
691         global $CFG, $DB;
693         $this->create_extra_users();
694         $this->setUser($this->editingteachers[0]);
695         $assign = $this->create_instance(array('grade'=>100));
697         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
699         // Teacher with user preference set should see suspended users as well.
700         set_user_preference('grade_report_showonlyactiveenrol', false);
701         $assign = $this->create_instance(array('grade'=>100));
702         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT,
703                 count($assign->list_participants(null, true)));
705         // Non-editing teacher should not see suspended users, even if user preference is set.
706         $this->setUser($this->teachers[0]);
707         set_user_preference('grade_report_showonlyactiveenrol', false);
708         $assign = $this->create_instance(array('grade'=>100));
709         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
711         // Turn on availability and a group restriction, and check that it doesn't
712         // show users who aren't in the group.
713         $CFG->enableavailability = true;
714         $specialgroup = $this->getDataGenerator()->create_group(
715                 array('courseid' => $this->course->id));
716         $assign = $this->create_instance(array('grade' => 100,
717                 'availability' => json_encode(\core_availability\tree::get_root_json(
718                     array(\availability_group\condition::get_json($specialgroup->id))))));
719         groups_add_member($specialgroup, $this->students[0]);
720         groups_add_member($specialgroup, $this->students[1]);
721         $this->assertEquals(2, count($assign->list_participants(null, true)));
722     }
724     public function test_get_participant_user_not_exist() {
725         $assign = $this->create_instance(array('grade' => 100));
726         $this->assertNull($assign->get_participant('-1'));
727     }
729     public function test_get_participant_not_enrolled() {
730         $assign = $this->create_instance(array('grade' => 100));
731         $user = $this->getDataGenerator()->create_user();
732         $this->assertNull($assign->get_participant($user->id));
733     }
735     public function test_get_participant_no_submission() {
736         $assign = $this->create_instance(array('grade' => 100));
737         $student = $this->students[0];
738         $participant = $assign->get_participant($student->id);
740         $this->assertEquals($student->id, $participant->id);
741         $this->assertFalse($participant->submitted);
742         $this->assertFalse($participant->requiregrading);
743     }
745     public function test_get_participant_with_ungraded_submission() {
746         $assign = $this->create_instance(array('grade' => 100));
747         $student = $this->students[0];
748         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
750         $this->setUser($student);
752         // Simulate a submission.
753         $data = new stdClass();
754         $data->onlinetext_editor = array(
755             'itemid' => file_get_unused_draft_itemid(),
756             'text' => 'Student submission text',
757             'format' => FORMAT_MOODLE
758         );
760         $notices = array();
761         $assign->save_submission($data, $notices);
763         $data = new stdClass;
764         $data->userid = $student->id;
765         $assign->submit_for_grading($data, array());
767         $participant = $assign->get_participant($student->id);
769         $this->assertEquals($student->id, $participant->id);
770         $this->assertTrue($participant->submitted);
771         $this->assertTrue($participant->requiregrading);
772     }
774     public function test_get_participant_with_graded_submission() {
775         $assign = $this->create_instance(array('grade' => 100));
776         $student = $this->students[0];
777         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
779         $this->setUser($student);
781         // Simulate a submission.
782         $data = new stdClass();
783         $data->onlinetext_editor = array(
784             'itemid' => file_get_unused_draft_itemid(),
785             'text' => 'Student submission text',
786             'format' => FORMAT_MOODLE
787         );
789         $notices = array();
790         $assign->save_submission($data, $notices);
792         $data = new stdClass;
793         $data->userid = $student->id;
794         $assign->submit_for_grading($data, array());
796         // This is to make sure the grade happens after the submission because
797         // we have no control over the timemodified values.
798         sleep(1);
799         // Grade the submission.
800         $this->setUser($this->teachers[0]);
802         $data = new stdClass();
803         $data->grade = '50.0';
804         $assign->testable_apply_grade_to_user($data, $student->id, 0);
806         $participant = $assign->get_participant($student->id);
808         $this->assertEquals($student->id, $participant->id);
809         $this->assertTrue($participant->submitted);
810         $this->assertFalse($participant->requiregrading);
811     }
813     public function test_count_teams() {
814         $this->create_extra_users();
815         $this->setUser($this->editingteachers[0]);
816         $assign1 = $this->create_instance(array('teamsubmission' => 1));
817         $this->assertEquals(self::GROUP_COUNT + 1, $assign1->count_teams());
819         $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id));
820         $this->getDataGenerator()->create_grouping_group(array('groupid' => $this->groups[0]->id, 'groupingid' => $grouping->id));
821         $this->getDataGenerator()->create_grouping_group(array('groupid' => $this->groups[1]->id, 'groupingid' => $grouping->id));
823         // No active group and non group submissions allowed => 2 groups + the default one.
824         $params = array(
825             'teamsubmission' => 1,
826             'teamsubmissiongroupingid' => $grouping->id,
827             'preventsubmissionnotingroup' => false
828         );
829         $assign2 = $this->create_instance($params);
830         $this->assertEquals(3, $assign2->count_teams());
832         // An active group => Just the selected one.
833         $this->assertEquals(1, $assign2->count_teams($this->groups[0]->id));
835         // No active group and non group submissions allowed => 2 groups + no default one.
836         $params = array('teamsubmission' => 1, 'teamsubmissiongroupingid' => $grouping->id, 'preventsubmissionnotingroup' => true);
837         $assign3 = $this->create_instance($params);
838         $this->assertEquals(2, $assign3->count_teams());
840         $assign4 = $this->create_instance(array('teamsubmission' => 1, 'preventsubmissionnotingroup' => true));
841         $this->assertEquals(self::GROUP_COUNT, $assign4->count_teams());
842     }
844     public function test_submit_to_default_group() {
845         global $DB, $SESSION;
847         $this->preventResetByRollback();
848         $sink = $this->redirectMessages();
850         $this->setUser($this->editingteachers[0]);
851         $params = array('teamsubmission' => 1,
852                         'assignsubmission_onlinetext_enabled' => 1,
853                         'submissiondrafts' => 0,
854                         'groupmode' => VISIBLEGROUPS);
855         $assign = $this->create_instance($params);
857         $newstudent = $this->getDataGenerator()->create_user();
858         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
859         $this->getDataGenerator()->enrol_user($newstudent->id,
860                                               $this->course->id,
861                                               $studentrole->id);
862         $this->setUser($newstudent);
863         $data = new stdClass();
864         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
865                                          'text'=>'Submission text',
866                                          'format'=>FORMAT_MOODLE);
867         $notices = array();
869         $group = $assign->get_submission_group($newstudent->id);
870         $this->assertFalse($group, 'New student is in default group');
871         $assign->save_submission($data, $notices);
872         $this->assertEmpty($notices, 'No errors on save submission');
874         // Set active groups to all groups.
875         $this->setUser($this->teachers[0]);
876         $SESSION->activegroup[$this->course->id]['aag'][0] = 0;
877         $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
879         // Set an active group.
880         $anothergroup = $this->groups[0];
881         $SESSION->activegroup[$this->course->id]['aag'][0] = (int)$anothergroup->id;
882         $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
884         $sink->close();
885     }
887     public function test_count_submissions() {
888         global $SESSION;
890         $this->create_extra_users();
891         $this->setUser($this->editingteachers[0]);
892         $assign1 = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1));
894         // Simulate a submission.
895         $this->setUser($this->extrastudents[0]);
896         $submission = $assign1->get_user_submission($this->extrastudents[0]->id, true);
897         $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
898         $assign1->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
899         // Leave this one as DRAFT.
900         $data = new stdClass();
901         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
902                                          'text'=>'Submission text',
903                                          'format'=>FORMAT_MOODLE);
904         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
905         $plugin->save($submission, $data);
907         // Simulate adding a grade.
908         $this->setUser($this->teachers[0]);
909         $data = new stdClass();
910         $data->grade = '50.0';
911         $assign1->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
913         // Simulate a submission.
914         $this->setUser($this->extrastudents[1]);
915         $submission = $assign1->get_user_submission($this->extrastudents[1]->id, true);
916         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
917         $assign1->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
918         $data = new stdClass();
919         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
920                                          'text'=>'Submission text',
921                                          'format'=>FORMAT_MOODLE);
922         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
923         $plugin->save($submission, $data);
925         // Simulate a submission.
926         $this->setUser($this->extrastudents[2]);
927         $submission = $assign1->get_user_submission($this->extrastudents[2]->id, true);
928         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
929         $assign1->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
930         $data = new stdClass();
931         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
932                                          'text'=>'Submission text',
933                                          'format'=>FORMAT_MOODLE);
934         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
935         $plugin->save($submission, $data);
937         // Simulate a submission.
938         $this->setUser($this->extrastudents[3]);
939         $submission = $assign1->get_user_submission($this->extrastudents[3]->id, true);
940         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
941         $assign1->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
942         $data = new stdClass();
943         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
944                                          'text'=>'Submission text',
945                                          'format'=>FORMAT_MOODLE);
946         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
947         $plugin->save($submission, $data);
949         // Simulate a submission for suspended user, this will never be counted.
950         $this->setUser($this->extrastudents[3]);
951         $submission = $assign1->get_user_submission($this->extrasuspendedstudents[0]->id, true);
952         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
953         $assign1->testable_update_submission($submission, $this->extrasuspendedstudents[0]->id, true, false);
954         $data = new stdClass();
955         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
956                                          'text'=>'Submission text',
957                                          'format'=>FORMAT_MOODLE);
958         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
959         $plugin->save($submission, $data);
961         // Wait 1 second so the submission and grade do not have the same timemodified.
962         sleep(1);
963         // Simulate adding a grade.
964         $this->setUser($this->editingteachers[0]);
965         $data = new stdClass();
966         $data->grade = '50.0';
967         $assign1->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
968         $assign1->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
970         // Create a new submission with status NEW.
971         $this->setUser($this->extrastudents[4]);
972         $submission = $assign1->get_user_submission($this->extrastudents[4]->id, true);
974         $this->assertEquals(2, $assign1->count_grades());
975         $this->assertEquals(4, $assign1->count_submissions());
976         $this->assertEquals(5, $assign1->count_submissions(true));
977         $this->assertEquals(2, $assign1->count_submissions_need_grading());
978         $this->assertEquals(3, $assign1->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
979         $this->assertEquals(1, $assign1->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
981         // Groups.
982         $assign2 = $this->create_instance(array(
983             'assignsubmission_onlinetext_enabled' => 1,
984             'groupmode' => VISIBLEGROUPS
985         ));
987         $this->setUser($this->extrastudents[1]);
988         $submission = $assign2->get_user_submission($this->extrastudents[1]->id, true);
989         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
990         $assign2->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
991         $data = new stdClass();
992         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
993                                          'text' => 'Submission text',
994                                          'format' => FORMAT_MOODLE);
995         $plugin = $assign2->get_submission_plugin_by_type('onlinetext');
996         $plugin->save($submission, $data);
998         $this->assertEquals(1, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1000         // Set active groups to all groups.
1001         $this->setUser($this->teachers[0]);
1002         $SESSION->activegroup[$this->course->id]['aag'][0] = 0;
1003         $this->assertEquals(1, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1005         // Set the user group.
1006         $studentgroups = groups_get_user_groups($this->course->id, $this->extrastudents[1]->id);
1007         $this->assertEquals(1, count($studentgroups));
1008         $studentgroup = array_pop($studentgroups);
1009         $SESSION->activegroup[$this->course->id]['aag'][0] = $studentgroup[0];
1010         $this->assertEquals(1, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1012         // Set another group.
1013         $anothergroup = $this->groups[0];
1014         $this->assertNotEquals($anothergroup->id, $studentgroup[0]);
1015         $SESSION->activegroup[$this->course->id]['aag'][0] = (int)$anothergroup->id;
1016         $this->assertEquals(0, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1017     }
1019     public function test_count_submissions_for_groups() {
1020         $this->create_extra_users();
1021         $groupid = null;
1022         $this->setUser($this->editingteachers[0]);
1023         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1, 'teamsubmission' => 1));
1025         // Simulate a submission.
1026         $this->setUser($this->extrastudents[0]);
1027         $submission = $assign->get_group_submission($this->extrastudents[0]->id, $groupid, true);
1028         $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
1029         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
1030         // Leave this one as DRAFT.
1031         $data = new stdClass();
1032         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1033                                          'text' => 'Submission text',
1034                                          'format' => FORMAT_MOODLE);
1035         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1036         $plugin->save($submission, $data);
1038         // Simulate adding a grade.
1039         $this->setUser($this->teachers[0]);
1040         $data = new stdClass();
1041         $data->grade = '50.0';
1042         $assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
1044         // Simulate a submission.
1045         $this->setUser($this->extrastudents[1]);
1046         $submission = $assign->get_group_submission($this->extrastudents[1]->id, $groupid, true);
1047         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1048         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
1049         $data = new stdClass();
1050         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1051                                          'text' => 'Submission text',
1052                                          'format' => FORMAT_MOODLE);
1053         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1054         $plugin->save($submission, $data);
1056         // Simulate a submission.
1057         $this->setUser($this->extrastudents[2]);
1058         $submission = $assign->get_group_submission($this->extrastudents[2]->id, $groupid, true);
1059         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1060         $assign->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
1061         $data = new stdClass();
1062         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1063                                          'text' => 'Submission text',
1064                                          'format' => FORMAT_MOODLE);
1065         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1066         $plugin->save($submission, $data);
1068         // Simulate a submission.
1069         $this->setUser($this->extrastudents[3]);
1070         $submission = $assign->get_group_submission($this->extrastudents[3]->id, $groupid, true);
1071         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1072         $assign->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
1073         $data = new stdClass();
1074         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1075                                          'text' => 'Submission text',
1076                                          'format' => FORMAT_MOODLE);
1077         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1078         $plugin->save($submission, $data);
1080         // Simulate adding a grade.
1081         $this->setUser($this->editingteachers[0]);
1082         $data = new stdClass();
1083         $data->grade = '50.0';
1084         $assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
1085         $assign->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
1087         // Create a new submission with status NEW.
1088         $this->setUser($this->extrastudents[4]);
1089         $submission = $assign->get_group_submission($this->extrastudents[4]->id, $groupid, true);
1091         $this->assertEquals(2, $assign->count_grades());
1092         $this->assertEquals(4, $assign->count_submissions());
1093         $this->assertEquals(5, $assign->count_submissions(true));
1094         $this->assertEquals(3, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1095         $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
1096     }
1098     public function test_get_grading_userid_list() {
1099         $this->create_extra_users();
1100         $this->setUser($this->editingteachers[0]);
1101         $assign = $this->create_instance();
1103         $users = $assign->testable_get_grading_userid_list();
1104         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($users));
1106         $this->setUser($this->editingteachers[0]);
1107         set_user_preference('grade_report_showonlyactiveenrol', false);
1108         $assign = $this->create_instance();
1110         $users = $assign->testable_get_grading_userid_list();
1111         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT, count($users));
1112     }
1114     public function test_cron() {
1115         // First run cron so there are no messages waiting to be sent (from other tests).
1116         cron_setup_user();
1117         assign::cron();
1119         // Now create an assignment and add some feedback.
1120         $this->setUser($this->editingteachers[0]);
1121         $assign = $this->create_instance(array('sendstudentnotifications'=>1));
1123         // Simulate adding a grade.
1124         $this->setUser($this->teachers[0]);
1125         $data = new stdClass();
1126         $data->grade = '50.0';
1127         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1128         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
1130         $data->sendstudentnotifications = false;
1131         $assign->testable_apply_grade_to_user($data, $this->students[2]->id, 0);
1133         // Now run cron and see that one message was sent.
1134         $this->preventResetByRollback();
1135         $sink = $this->redirectMessages();
1136         cron_setup_user();
1137         $this->expectOutputRegex('/Done processing 2 assignment submissions/');
1138         assign::cron();
1140         $messages = $sink->get_messages();
1141         // The sent count should be 2, because the 3rd one was marked as do not send notifications.
1142         $this->assertEquals(2, count($messages));
1143         $this->assertEquals(1, $messages[0]->notification);
1144         $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
1146         // Regrading a grade causes a notification to the user.
1147         $data->sendstudentnotifications = true;
1148         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1149         assign::cron();
1150         $messages = $sink->get_messages();
1151         $this->assertEquals(3, count($messages));
1152     }
1154     /**
1155      * Test delivery of grade notifications as controlled by marking workflow.
1156      */
1157     public function test_markingworkflow_cron() {
1158         // First run cron so there are no messages waiting to be sent (from other tests).
1159         cron_setup_user();
1160         assign::cron();
1162         // Now create an assignment with marking workflow enabled.
1163         $this->setUser($this->editingteachers[0]);
1164         $assign = $this->create_instance(array('sendstudentnotifications' => 1, 'markingworkflow' => 1));
1166         // Simulate adding a grade.
1167         $this->setUser($this->teachers[0]);
1168         $data = new stdClass();
1169         $data->grade = '50.0';
1171         // This student will not receive notification.
1172         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
1173         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1175         // This student will receive notification.
1176         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
1177         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
1179         // Now run cron and see that one message was sent.
1180         $this->preventResetByRollback();
1181         $sink = $this->redirectMessages();
1182         cron_setup_user();
1183         $this->expectOutputRegex('/Done processing 1 assignment submissions/');
1184         assign::cron();
1186         $messages = $sink->get_messages();
1187         $this->assertEquals(1, count($messages));
1188         $this->assertEquals($messages[0]->useridto, $this->students[1]->id);
1189         $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
1190     }
1192     public function test_is_graded() {
1193         $this->setUser($this->editingteachers[0]);
1194         $assign = $this->create_instance();
1196         // Simulate adding a grade.
1197         $this->setUser($this->teachers[0]);
1198         $data = new stdClass();
1199         $data->grade = '50.0';
1200         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1202         $this->assertEquals(true, $assign->testable_is_graded($this->students[0]->id));
1203         $this->assertEquals(false, $assign->testable_is_graded($this->students[1]->id));
1204     }
1206     public function test_can_grade() {
1207         global $DB;
1209         $this->setUser($this->editingteachers[0]);
1210         $assign = $this->create_instance();
1212         $this->setUser($this->students[0]);
1213         $this->assertEquals(false, $assign->can_grade());
1214         $this->setUser($this->editingteachers[0]);
1215         $this->assertEquals(true, $assign->can_grade());
1216         $this->setUser($this->teachers[0]);
1217         $this->assertEquals(true, $assign->can_grade());
1219         // Test the viewgrades capability - without mod/assign:grade.
1220         $this->setUser($this->students[0]);
1221         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1222         assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
1223         $this->assertEquals(false, $assign->can_grade());
1224     }
1226     public function test_can_view_submission() {
1227         global $DB;
1229         $this->create_extra_users();
1230         $this->setUser($this->editingteachers[0]);
1231         $assign = $this->create_instance();
1233         $this->setUser($this->students[0]);
1234         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1235         $this->assertEquals(false, $assign->can_view_submission($this->students[1]->id));
1236         $this->assertEquals(false, $assign->can_view_submission($this->teachers[0]->id));
1237         $this->setUser($this->teachers[0]);
1238         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1239         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
1240         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
1241         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
1242         $this->setUser($this->editingteachers[0]);
1243         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1244         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
1245         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
1246         $this->assertEquals(true, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
1248         // Test the viewgrades capability - without mod/assign:grade.
1249         $this->setUser($this->students[0]);
1250         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1251         assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
1252         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1253         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
1254         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
1255         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
1256     }
1259     public function test_update_submission() {
1260         $this->create_extra_users();
1261         $this->setUser($this->editingteachers[0]);
1262         $assign = $this->create_instance();
1264         $this->setUser($this->extrastudents[0]);
1265         $now = time();
1266         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
1267         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
1269         $this->setUser($this->teachers[0]);
1270         // Verify the gradebook update.
1271         $gradinginfo = grade_get_grades($this->course->id,
1272                                         'mod',
1273                                         'assign',
1274                                         $assign->get_instance()->id,
1275                                         $this->extrastudents[0]->id);
1277         $this->assertEquals($this->extrastudents[0]->id,
1278                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
1280         // Now verify group assignments.
1281         $this->setUser($this->editingteachers[0]);
1282         $assign = $this->create_instance(array('teamsubmission'=>1));
1284         $this->setUser($this->extrastudents[0]);
1285         $now = time();
1286         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1287         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1289         // Check that at least 2 active members and 1 suspended member of the submission group had their submission updated.
1291         $this->setUser($this->editingteachers[0]);
1292         $gradinginfo = grade_get_grades($this->course->id,
1293                                         'mod',
1294                                         'assign',
1295                                         $assign->get_instance()->id,
1296                                         $this->extrastudents[0]->id);
1298         $this->assertEquals($this->extrastudents[0]->id,
1299                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
1301         $gradinginfo = grade_get_grades($this->course->id,
1302                                         'mod',
1303                                         'assign',
1304                                         $assign->get_instance()->id,
1305                                         $this->extrastudents[self::GROUP_COUNT]->id);
1307         $this->assertEquals($this->extrastudents[self::GROUP_COUNT]->id,
1308                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT]->id]->usermodified);
1310         $gradinginfo = grade_get_grades($this->course->id,
1311                                         'mod',
1312                                         'assign',
1313                                         $assign->get_instance()->id,
1314                                         $this->extrasuspendedstudents[0]->id);
1315         $this->assertEquals($this->extrasuspendedstudents[0]->id,
1316                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[0]->id]->usermodified);
1318         // Check the same with non-editing teacher and make sure submission is not updated for suspended user.
1319         $this->setUser($this->editingteachers[0]);
1320         $assign = $this->create_instance(array('teamsubmission'=>1));
1322         $this->setUser($this->extrastudents[1]);
1323         $now = time();
1324         $submission = $assign->get_group_submission($this->extrastudents[1]->id, 0, true);
1325         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, true);
1327         $this->setUser($this->teachers[0]);
1328         $gradinginfo = grade_get_grades($this->course->id,
1329                                         'mod',
1330                                         'assign',
1331                                         $assign->get_instance()->id,
1332                                         $this->extrastudents[1]->id);
1334         $this->assertEquals($this->extrastudents[1]->id,
1335                             $gradinginfo->items[0]->grades[$this->extrastudents[1]->id]->usermodified);
1337         $gradinginfo = grade_get_grades($this->course->id,
1338                                         'mod',
1339                                         'assign',
1340                                         $assign->get_instance()->id,
1341                                         $this->extrastudents[self::GROUP_COUNT+1]->id);
1343         $this->assertEquals($this->extrastudents[self::GROUP_COUNT+1]->id,
1344                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT+1]->id]->usermodified);
1346         $gradinginfo = grade_get_grades($this->course->id,
1347                                         'mod',
1348                                         'assign',
1349                                         $assign->get_instance()->id,
1350                                         $this->extrasuspendedstudents[1]->id);
1351         $this->assertEquals($this->extrasuspendedstudents[1]->id,
1352                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[1]->id]->usermodified);
1354         // Now verify blind marking.
1355         $this->setUser($this->editingteachers[0]);
1356         $assign = $this->create_instance(array('blindmarking'=>1));
1358         $this->setUser($this->extrastudents[0]);
1359         $now = time();
1360         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
1361         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
1363         $this->setUser($this->editingteachers[0]);
1364         $gradinginfo = grade_get_grades($this->course->id,
1365                                         'mod',
1366                                         'assign',
1367                                         $assign->get_instance()->id,
1368                                         $this->extrastudents[0]->id);
1370         $this->assertEquals(null, $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->datesubmitted);
1371     }
1373     public function test_group_submissions_submit_for_marking_requireallteammemberssubmit() {
1374         global $PAGE;
1376         $this->create_extra_users();
1377         // Now verify group assignments.
1378         $this->setUser($this->editingteachers[0]);
1379         $assign = $this->create_instance(array('teamsubmission'=>1,
1380                                                'assignsubmission_onlinetext_enabled'=>1,
1381                                                'submissiondrafts'=>1,
1382                                                'requireallteammemberssubmit'=>1));
1383         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1385         // Add a submission.
1386         $this->setUser($this->extrastudents[0]);
1387         $data = new stdClass();
1388         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1389                                          'text'=>'Submission text',
1390                                          'format'=>FORMAT_MOODLE);
1392         $notices = array();
1393         $assign->save_submission($data, $notices);
1395         // Check we can see the submit button.
1396         $output = $assign->view_student_summary($this->extrastudents[0], true);
1397         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1399         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1400         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1401         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1403         // Check that the student does not see "Submit" button.
1404         $output = $assign->view_student_summary($this->extrastudents[0], true);
1405         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1407         // Change to another user in the same group.
1408         $this->setUser($this->extrastudents[self::GROUP_COUNT]);
1409         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1410         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1412         $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
1413         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1414         $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
1415         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1416         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1417     }
1419     public function test_group_submissions_submit_for_marking() {
1420         global $PAGE;
1422         $this->create_extra_users();
1423         // Now verify group assignments.
1424         $this->setUser($this->editingteachers[0]);
1425         $time = time();
1426         $assign = $this->create_instance(array('teamsubmission'=>1,
1427                                                'assignsubmission_onlinetext_enabled'=>1,
1428                                                'submissiondrafts'=>1,
1429                                                'requireallteammemberssubmit'=>0,
1430                                                'duedate' => $time - 2*24*60*60));
1431         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1433         $this->setUser($this->extrastudents[0]);
1434         // Add a submission.
1435         $data = new stdClass();
1436         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1437                                          'text'=>'Submission text',
1438                                          'format'=>FORMAT_MOODLE);
1440         $notices = array();
1441         $assign->save_submission($data, $notices);
1443         // Check we can see the submit button.
1444         $output = $assign->view_student_summary($this->extrastudents[0], true);
1445         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1446         $this->assertContains(get_string('timeremaining', 'assign'), $output);
1447         $difftime = time() - $time;
1448         $this->assertContains(get_string('overdue', 'assign', format_time(2*24*60*60 + $difftime)), $output);
1450         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1451         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1452         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1454         // Check that the student does not see "Submit" button.
1455         $output = $assign->view_student_summary($this->extrastudents[0], true);
1456         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1458         // Change to another user in the same group.
1459         $this->setUser($this->extrastudents[self::GROUP_COUNT]);
1460         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1461         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1463         // Check that time remaining is not overdue.
1464         $this->assertContains(get_string('timeremaining', 'assign'), $output);
1465         $difftime = time() - $time;
1466         $this->assertContains(get_string('submittedlate', 'assign', format_time(2*24*60*60 + $difftime)), $output);
1468         $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
1469         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1470         $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
1471         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1472         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1473     }
1475     public function test_submissions_open() {
1476         $this->setUser($this->editingteachers[0]);
1478         $now = time();
1479         $tomorrow = $now + 24*60*60;
1480         $oneweek = $now + 7*24*60*60;
1481         $yesterday = $now - 24*60*60;
1483         $assign = $this->create_instance();
1484         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1486         $assign = $this->create_instance(array('duedate'=>$tomorrow));
1487         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1489         $assign = $this->create_instance(array('duedate'=>$yesterday));
1490         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1492         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$tomorrow));
1493         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1495         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$yesterday));
1496         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
1498         $assign->testable_save_user_extension($this->students[0]->id, $tomorrow);
1499         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1501         $assign = $this->create_instance(array('submissiondrafts'=>1));
1502         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1504         $this->setUser($this->students[0]);
1505         $now = time();
1506         $submission = $assign->get_user_submission($this->students[0]->id, true);
1507         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1508         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1509         $this->setUser($this->editingteachers[0]);
1510         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
1511     }
1513     public function test_get_graders() {
1514         $this->create_extra_users();
1515         $this->setUser($this->editingteachers[0]);
1517         // Create an assignment with no groups.
1518         $assign = $this->create_instance();
1519         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
1520                            self::DEFAULT_EDITING_TEACHER_COUNT +
1521                            self::EXTRA_TEACHER_COUNT +
1522                            self::EXTRA_EDITING_TEACHER_COUNT,
1523                            $assign->testable_get_graders($this->students[0]->id));
1525         // Force create an assignment with SEPARATEGROUPS.
1526         $data = new stdClass();
1527         $data->courseid = $this->course->id;
1528         $data->name = 'Grouping';
1529         $groupingid = groups_create_grouping($data);
1530         groups_assign_grouping($groupingid, $this->groups[0]->id);
1531         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1533         $this->setUser($this->students[1]);
1534         $this->assertCount(4, $assign->testable_get_graders($this->students[0]->id));
1535         // Note the second student is in a group that is not in the grouping.
1536         // This means that we get all graders that are not in a group in the grouping.
1537         $this->assertCount(10, $assign->testable_get_graders($this->students[1]->id));
1538     }
1540     public function test_get_notified_users() {
1541         global $CFG, $DB;
1543         $capability = 'mod/assign:receivegradernotifications';
1544         $coursecontext = context_course::instance($this->course->id);
1545         $role = $DB->get_record('role', array('shortname' => 'teacher'));
1547         $this->create_extra_users();
1548         $this->setUser($this->editingteachers[0]);
1550         // Create an assignment with no groups.
1551         $assign = $this->create_instance();
1553         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
1554                            self::DEFAULT_EDITING_TEACHER_COUNT +
1555                            self::EXTRA_TEACHER_COUNT +
1556                            self::EXTRA_EDITING_TEACHER_COUNT,
1557                            $assign->testable_get_notifiable_users($this->students[0]->id));
1559         // Change nonediting teachers role to not receive grader notifications.
1560         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
1562         $this->assertCount(self::DEFAULT_EDITING_TEACHER_COUNT +
1563                            self::EXTRA_EDITING_TEACHER_COUNT,
1564                            $assign->testable_get_notifiable_users($this->students[0]->id));
1566         // Reset nonediting teachers role to default.
1567         unassign_capability($capability, $role->id, $coursecontext);
1569         // Force create an assignment with SEPARATEGROUPS.
1570         $data = new stdClass();
1571         $data->courseid = $this->course->id;
1572         $data->name = 'Grouping';
1573         $groupingid = groups_create_grouping($data);
1574         groups_assign_grouping($groupingid, $this->groups[0]->id);
1575         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1577         $this->setUser($this->students[1]);
1578         $this->assertCount(4, $assign->testable_get_notifiable_users($this->students[0]->id));
1579         // Note the second student is in a group that is not in the grouping.
1580         // This means that we get all graders that are not in a group in the grouping.
1581         $this->assertCount(10, $assign->testable_get_notifiable_users($this->students[1]->id));
1583         // Change nonediting teachers role to not receive grader notifications.
1584         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
1586         $this->assertCount(2, $assign->testable_get_notifiable_users($this->students[0]->id));
1587         // Note the second student is in a group that is not in the grouping.
1588         // This means that we get all graders that are not in a group in the grouping.
1589         $this->assertCount(5, $assign->testable_get_notifiable_users($this->students[1]->id));
1590     }
1592     public function test_group_members_only() {
1593         global $CFG;
1595         $this->setAdminUser();
1596         $this->create_extra_users();
1597         $CFG->enableavailability = true;
1598         $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id));
1599         groups_assign_grouping($grouping->id, $this->groups[0]->id);
1601         // Force create an assignment with SEPARATEGROUPS.
1602         $instance = $this->getDataGenerator()->create_module('assign', array('course'=>$this->course->id),
1603                 array('availability' => json_encode(\core_availability\tree::get_root_json(array(
1604                     \availability_grouping\condition::get_json()))),
1605                 'groupingid' => $grouping->id));
1607         $cm = get_coursemodule_from_instance('assign', $instance->id);
1608         $context = context_module::instance($cm->id);
1609         $assign = new testable_assign($context, $cm, $this->course);
1611         $this->setUser($this->teachers[0]);
1612         get_fast_modinfo($this->course, 0, true);
1613         $this->assertCount(5, $assign->list_participants(0, true));
1615     }
1617     public function test_get_uniqueid_for_user() {
1618         $this->setUser($this->editingteachers[0]);
1619         $assign = $this->create_instance();
1621         foreach ($this->students as $student) {
1622             $uniqueid = $assign->get_uniqueid_for_user($student->id);
1623             $this->assertEquals($student->id, $assign->get_user_id_for_uniqueid($uniqueid));
1624         }
1625     }
1627     public function test_show_student_summary() {
1628         global $CFG, $PAGE;
1630         $this->setUser($this->editingteachers[0]);
1631         $assign = $this->create_instance();
1632         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1634         // No feedback should be available because this student has not been graded.
1635         $this->setUser($this->students[0]);
1636         $output = $assign->view_student_summary($this->students[0], true);
1637         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if there is no grade');
1638         // Simulate adding a grade.
1639         $this->setUser($this->teachers[0]);
1640         $data = new stdClass();
1641         $data->grade = '50.0';
1642         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1644         // Now we should see the feedback.
1645         $this->setUser($this->students[0]);
1646         $output = $assign->view_student_summary($this->students[0], true);
1647         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback if there is a grade');
1649         // Now hide the grade in gradebook.
1650         $this->setUser($this->teachers[0]);
1651         require_once($CFG->libdir.'/gradelib.php');
1652         $gradeitem = new grade_item(array(
1653             'itemtype'      => 'mod',
1654             'itemmodule'    => 'assign',
1655             'iteminstance'  => $assign->get_instance()->id,
1656             'courseid'      => $this->course->id));
1658         $gradeitem->set_hidden(1, false);
1660         // No feedback should be available because the grade is hidden.
1661         $this->setUser($this->students[0]);
1662         $output = $assign->view_student_summary($this->students[0], true);
1663         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
1665         // Do the same but add feedback.
1666         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
1668         $this->setUser($this->teachers[0]);
1669         $grade = $assign->get_user_grade($this->students[0]->id, true);
1670         $data = new stdClass();
1671         $data->assignfeedbackcomments_editor = array('text'=>'Tomato sauce',
1672                                          'format'=>FORMAT_MOODLE);
1673         $plugin = $assign->get_feedback_plugin_by_type('comments');
1674         $plugin->save($grade, $data);
1676         // Should have feedback but no grade.
1677         $this->setUser($this->students[0]);
1678         $output = $assign->view_student_summary($this->students[0], true);
1679         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback even if there is no grade');
1680         $this->assertEquals(false, strpos($output, 'Grade'), 'Do not show grade when there is no grade.');
1681         $this->assertEquals(false, strpos($output, 'Graded on'), 'Do not show graded date when there is no grade.');
1683         // Now hide the grade in gradebook.
1684         $this->setUser($this->teachers[0]);
1685         $gradeitem = new grade_item(array(
1686             'itemtype'      => 'mod',
1687             'itemmodule'    => 'assign',
1688             'iteminstance'  => $assign->get_instance()->id,
1689             'courseid'      => $this->course->id));
1691         $gradeitem->set_hidden(1, false);
1693         // No feedback should be available because the grade is hidden.
1694         $this->setUser($this->students[0]);
1695         $output = $assign->view_student_summary($this->students[0], true);
1696         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
1697     }
1699     public function test_attempt_reopen_method_manual() {
1700         global $PAGE;
1702         $this->setUser($this->editingteachers[0]);
1703         $assign = $this->create_instance(array('attemptreopenmethod'=>ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
1704                                                'maxattempts'=>3,
1705                                                'submissiondrafts'=>1,
1706                                                'assignsubmission_onlinetext_enabled'=>1));
1707         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1709         // Student should be able to see an add submission button.
1710         $this->setUser($this->students[0]);
1711         $output = $assign->view_student_summary($this->students[0], true);
1712         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1714         // Add a submission.
1715         $now = time();
1716         $submission = $assign->get_user_submission($this->students[0]->id, true);
1717         $data = new stdClass();
1718         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1719                                          'text'=>'Submission text',
1720                                          'format'=>FORMAT_MOODLE);
1721         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1722         $plugin->save($submission, $data);
1724         // And now submit it for marking.
1725         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1726         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1728         // Verify the student cannot make changes to the submission.
1729         $output = $assign->view_student_summary($this->students[0], true);
1730         $this->assertEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1732         // Mark the submission.
1733         $this->setUser($this->teachers[0]);
1734         $data = new stdClass();
1735         $data->grade = '50.0';
1736         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1738         // Check the student can see the grade.
1739         $this->setUser($this->students[0]);
1740         $output = $assign->view_student_summary($this->students[0], true);
1741         $this->assertNotEquals(false, strpos($output, '50.0'));
1743         // Allow the student another attempt.
1744         $this->teachers[0]->ignoresesskey = true;
1745         $this->setUser($this->teachers[0]);
1746         $result = $assign->testable_process_add_attempt($this->students[0]->id);
1747         $this->assertEquals(true, $result);
1749         // Check that the previous attempt is now in the submission history table.
1750         $this->setUser($this->students[0]);
1751         $output = $assign->view_student_summary($this->students[0], true);
1752         // Need a better check.
1753         $this->assertNotEquals(false, strpos($output, 'Submission text'), 'Contains: Submission text');
1755         // Check that the student now has a button for Add a new attempt".
1756         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1757         // Check that the student now does not have a button for Submit.
1758         $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
1760         // Check that the student now has a submission history.
1761         $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
1763         $this->setUser($this->teachers[0]);
1764         // Check that the grading table loads correctly and contains this user.
1765         // This is also testing that we do not get duplicate rows in the grading table.
1766         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1767         $output = $assign->get_renderer()->render($gradingtable);
1768         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
1770         // Should be 1 not 2.
1771         $this->assertEquals(1, $assign->count_submissions());
1772         $this->assertEquals(1, $assign->count_submissions_with_status('reopened'));
1773         $this->assertEquals(0, $assign->count_submissions_need_grading());
1774         $this->assertEquals(1, $assign->count_grades());
1776         // Change max attempts to unlimited.
1777         $formdata = clone($assign->get_instance());
1778         $formdata->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
1779         $formdata->instance = $formdata->id;
1780         $assign->update_instance($formdata);
1782         // Mark the submission again.
1783         $data = new stdClass();
1784         $data->grade = '60.0';
1785         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1787         // Check the grade exists.
1788         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1789         $this->assertEquals(60, (int)$grades[$this->students[0]->id]->rawgrade);
1791         // Check we can reopen still.
1792         $result = $assign->testable_process_add_attempt($this->students[0]->id);
1793         $this->assertEquals(true, $result);
1795         // Should no longer have a grade because there is no grade for the latest attempt.
1796         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1797         $this->assertEmpty($grades);
1799     }
1801     /**
1802      * Test reopen behavior when in "Reopen until pass" mode.
1803      */
1804     public function test_attempt_reopen_method_untilpass() {
1805         global $PAGE;
1807         $this->setUser($this->editingteachers[0]);
1808         $assign = $this->create_instance(array('attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS,
1809                 'maxattempts' => 3,
1810                 'submissiondrafts' => 1,
1811                 'assignsubmission_onlinetext_enabled' => 1));
1812         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1814         // Set grade to pass to 80.
1815         $gradeitem = $assign->get_grade_item();
1816         $gradeitem->gradepass = '80.0';
1817         $gradeitem->update();
1819         // Student should be able to see an add submission button.
1820         $this->setUser($this->students[0]);
1821         $output = $assign->view_student_summary($this->students[0], true);
1822         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1824         // Add a submission.
1825         $now = time();
1826         $submission = $assign->get_user_submission($this->students[0]->id, true);
1827         $data = new stdClass();
1828         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1829                 'text' => 'Submission text',
1830                 'format' => FORMAT_MOODLE);
1831         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1832         $plugin->save($submission, $data);
1834         // And now submit it for marking.
1835         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1836         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1838         // Verify the student cannot make a new attempt.
1839         $output = $assign->view_student_summary($this->students[0], true);
1840         $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1842         // Mark the submission as non-passing.
1843         $this->setUser($this->teachers[0]);
1844         $data = new stdClass();
1845         $data->grade = '50.0';
1846         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1848         // Check the student can see the grade.
1849         $this->setUser($this->students[0]);
1850         $output = $assign->view_student_summary($this->students[0], true);
1851         $this->assertNotEquals(false, strpos($output, '50.0'));
1853         // Check that the student now has a button for Add a new attempt.
1854         $output = $assign->view_student_summary($this->students[0], true);
1855         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1857         // Check that the student now does not have a button for Submit.
1858         $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
1860         // Check that the student now has a submission history.
1861         $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
1863         // Add a second submission.
1864         $now = time();
1865         $submission = $assign->get_user_submission($this->students[0]->id, true, 1);
1866         $data = new stdClass();
1867         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1868                 'text' => 'Submission text',
1869                 'format' => FORMAT_MOODLE);
1870         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1871         $plugin->save($submission, $data);
1873         // And now submit it for marking.
1874         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1875         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1877         // Mark the submission as passing.
1878         $this->setUser($this->teachers[0]);
1879         $data = new stdClass();
1880         $data->grade = '80.0';
1881         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1883         // Check that the student does not have a button for Add a new attempt.
1884         $this->setUser($this->students[0]);
1885         $output = $assign->view_student_summary($this->students[0], true);
1886         $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1888         // Re-mark the submission as not passing.
1889         $this->setUser($this->teachers[0]);
1890         $data = new stdClass();
1891         $data->grade = '50.0';
1892         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1894         // Check that the student now has a button for Add a new attempt.
1895         $this->setUser($this->students[0]);
1896         $output = $assign->view_student_summary($this->students[0], true);
1897         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1899         // Add a submission as a second student.
1900         $this->setUser($this->students[1]);
1901         $now = time();
1902         $submission = $assign->get_user_submission($this->students[1]->id, true);
1903         $data = new stdClass();
1904         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1905                 'text' => 'Submission text',
1906                 'format' => FORMAT_MOODLE);
1907         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1908         $plugin->save($submission, $data);
1910         // And now submit it for marking.
1911         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1912         $assign->testable_update_submission($submission, $this->students[1]->id, true, false);
1914         // Mark the submission as passing.
1915         $this->setUser($this->teachers[0]);
1916         $data = new stdClass();
1917         $data->grade = '100.0';
1918         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
1920         // Check the student can see the grade.
1921         $this->setUser($this->students[1]);
1922         $output = $assign->view_student_summary($this->students[1], true);
1923         $this->assertNotEquals(false, strpos($output, '100.0'));
1925         // Check that the student does not have a button for Add a new attempt.
1926         $output = $assign->view_student_summary($this->students[1], true);
1927         $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1929         // Set grade to pass to 0, so that no attempts should reopen.
1930         $gradeitem = $assign->get_grade_item();
1931         $gradeitem->gradepass = '0';
1932         $gradeitem->update();
1934         // Add another submission.
1935         $this->setUser($this->students[2]);
1936         $now = time();
1937         $submission = $assign->get_user_submission($this->students[2]->id, true);
1938         $data = new stdClass();
1939         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1940                 'text' => 'Submission text',
1941                 'format' => FORMAT_MOODLE);
1942         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1943         $plugin->save($submission, $data);
1945         // And now submit it for marking.
1946         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1947         $assign->testable_update_submission($submission, $this->students[2]->id, true, false);
1949         // Mark the submission as graded.
1950         $this->setUser($this->teachers[0]);
1951         $data = new stdClass();
1952         $data->grade = '0.0';
1953         $assign->testable_apply_grade_to_user($data, $this->students[2]->id, 0);
1955         // Check the student can see the grade.
1956         $this->setUser($this->students[2]);
1957         $output = $assign->view_student_summary($this->students[2], true);
1958         $this->assertNotEquals(false, strpos($output, '0.0'));
1960         // Check that the student does not have a button for Add a new attempt.
1961         $output = $assign->view_student_summary($this->students[2], true);
1962         $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1963     }
1966     public function test_markingworkflow() {
1967         global $PAGE;
1969         $this->setUser($this->editingteachers[0]);
1970         $assign = $this->create_instance(array('markingworkflow'=>1));
1971         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1973         // Mark the submission and set to notmarked.
1974         $this->setUser($this->teachers[0]);
1975         $data = new stdClass();
1976         $data->grade = '50.0';
1977         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED;
1978         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1980         // Check the student can't see the grade.
1981         $this->setUser($this->students[0]);
1982         $output = $assign->view_student_summary($this->students[0], true);
1983         $this->assertEquals(false, strpos($output, '50.0'));
1985         // Make sure the grade isn't pushed to the gradebook.
1986         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1987         $this->assertEmpty($grades);
1989         // Mark the submission and set to inmarking.
1990         $this->setUser($this->teachers[0]);
1991         $data = new stdClass();
1992         $data->grade = '50.0';
1993         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INMARKING;
1994         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1996         // Check the student can't see the grade.
1997         $this->setUser($this->students[0]);
1998         $output = $assign->view_student_summary($this->students[0], true);
1999         $this->assertEquals(false, strpos($output, '50.0'));
2001         // Make sure the grade isn't pushed to the gradebook.
2002         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2003         $this->assertEmpty($grades);
2005         // Mark the submission and set to readyforreview.
2006         $this->setUser($this->teachers[0]);
2007         $data = new stdClass();
2008         $data->grade = '50.0';
2009         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW;
2010         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2012         // Check the student can't see the grade.
2013         $this->setUser($this->students[0]);
2014         $output = $assign->view_student_summary($this->students[0], true);
2015         $this->assertEquals(false, strpos($output, '50.0'));
2017         // Make sure the grade isn't pushed to the gradebook.
2018         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2019         $this->assertEmpty($grades);
2021         // Mark the submission and set to inreview.
2022         $this->setUser($this->teachers[0]);
2023         $data = new stdClass();
2024         $data->grade = '50.0';
2025         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW;
2026         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2028         // Check the student can't see the grade.
2029         $this->setUser($this->students[0]);
2030         $output = $assign->view_student_summary($this->students[0], true);
2031         $this->assertEquals(false, strpos($output, '50.0'));
2033         // Make sure the grade isn't pushed to the gradebook.
2034         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2035         $this->assertEmpty($grades);
2037         // Mark the submission and set to readyforrelease.
2038         $this->setUser($this->teachers[0]);
2039         $data = new stdClass();
2040         $data->grade = '50.0';
2041         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
2042         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2044         // Check the student can't see the grade.
2045         $this->setUser($this->students[0]);
2046         $output = $assign->view_student_summary($this->students[0], true);
2047         $this->assertEquals(false, strpos($output, '50.0'));
2049         // Make sure the grade isn't pushed to the gradebook.
2050         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2051         $this->assertEmpty($grades);
2053         // Mark the submission and set to released.
2054         $this->setUser($this->teachers[0]);
2055         $data = new stdClass();
2056         $data->grade = '50.0';
2057         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
2058         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2060         // Check the student can see the grade.
2061         $this->setUser($this->students[0]);
2062         $output = $assign->view_student_summary($this->students[0], true);
2063         $this->assertNotEquals(false, strpos($output, '50.0'));
2065         // Make sure the grade is pushed to the gradebook.
2066         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2067         $this->assertEquals(50, (int)$grades[$this->students[0]->id]->rawgrade);
2068     }
2070     public function test_markerallocation() {
2071         global $PAGE;
2073         $this->setUser($this->editingteachers[0]);
2074         $assign = $this->create_instance(array('markingworkflow'=>1, 'markingallocation'=>1));
2075         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2077         // Allocate marker to submission.
2078         $data = new stdClass();
2079         $data->allocatedmarker = $this->teachers[0]->id;
2080         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2082         // Check the allocated marker can view the submission.
2083         $this->setUser($this->teachers[0]);
2084         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
2085         $output = $assign->get_renderer()->render($gradingtable);
2086         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
2088         // Check that other teachers can't view this submission.
2089         $this->setUser($this->teachers[1]);
2090         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
2091         $output = $assign->get_renderer()->render($gradingtable);
2092         $this->assertNotEquals(true, strpos($output, $this->students[0]->lastname));
2093     }
2097     public function test_teacher_submit_for_student() {
2098         global $PAGE;
2100         $this->preventResetByRollback();
2101         $sink = $this->redirectMessages();
2103         $this->setUser($this->editingteachers[0]);
2105         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
2106         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2108         $this->setUser($this->students[0]);
2109         // Simulate a submission.
2110         $data = new stdClass();
2111         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2112                                          'text'=>'Student submission text',
2113                                          'format'=>FORMAT_MOODLE);
2115         $notices = array();
2116         $assign->save_submission($data, $notices);
2118         // Check that the submission text was saved.
2119         $output = $assign->view_student_summary($this->students[0], true);
2120         $this->assertContains('Student submission text', $output, 'Contains student submission text');
2122         // Check that a teacher teacher with the extra capability can edit a students submission.
2123         $this->setUser($this->teachers[0]);
2124         $data = new stdClass();
2125         $data->userid = $this->students[0]->id;
2126         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2127                                          'text'=>'Teacher edited submission text',
2128                                          'format'=>FORMAT_MOODLE);
2130         // Add the required capability.
2131         $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
2132         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
2133         role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
2134         accesslib_clear_all_caches_for_unit_testing();
2136         // Try to save the submission.
2137         $notices = array();
2138         $assign->save_submission($data, $notices);
2140         // Check that the teacher can submit the students work.
2141         $data = new stdClass();
2142         $data->userid = $this->students[0]->id;
2143         $notices = array();
2144         $assign->submit_for_grading($data, $notices);
2146         // Revert to draft so the student can edit it.
2147         $assign->revert_to_draft($this->students[0]->id);
2149         $this->setUser($this->students[0]);
2151         // Check that the submission text was saved.
2152         $output = $assign->view_student_summary($this->students[0], true);
2153         $this->assertContains('Teacher edited submission text', $output, 'Contains student submission text');
2155         // Check that the student can submit their work.
2156         $data = new stdClass();
2157         $assign->submit_for_grading($data, $notices);
2159         $output = $assign->view_student_summary($this->students[0], true);
2160         $this->assertNotContains(get_string('addsubmission', 'assign'), $output);
2162         // Set to a default editing teacher who should not be able to edit this submission.
2163         $this->setUser($this->editingteachers[1]);
2165         // Revert to draft so the submission is editable.
2166         $assign->revert_to_draft($this->students[0]->id);
2168         $data = new stdClass();
2169         $data->userid = $this->students[0]->id;
2170         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2171                                          'text'=>'Teacher 2 edited submission text',
2172                                          'format'=>FORMAT_MOODLE);
2174         $notices = array();
2175         $this->setExpectedException('moodle_exception');
2176         $assign->save_submission($data, $notices);
2178         $sink->close();
2179     }
2181     public function test_disable_submit_after_cutoff_date() {
2182         global $PAGE;
2184         $this->setUser($this->editingteachers[0]);
2185         $now = time();
2186         $tomorrow = $now + 24*60*60;
2187         $lastweek = $now - 7*24*60*60;
2188         $yesterday = $now - 24*60*60;
2190         $assign = $this->create_instance(array('duedate'=>$yesterday,
2191                                                'cutoffdate'=>$tomorrow,
2192                                                'assignsubmission_onlinetext_enabled'=>1));
2193         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2195         // Student should be able to see an add submission button.
2196         $this->setUser($this->students[0]);
2197         $output = $assign->view_student_summary($this->students[0], true);
2198         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2200         // Add a submission but don't submit now.
2201         $submission = $assign->get_user_submission($this->students[0]->id, true);
2202         $data = new stdClass();
2203         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2204                                          'text'=>'Submission text',
2205                                          'format'=>FORMAT_MOODLE);
2206         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2207         $plugin->save($submission, $data);
2209         // Create another instance with cut-off and due-date already passed.
2210         $this->setUser($this->editingteachers[0]);
2211         $now = time();
2212         $assign = $this->create_instance(array('duedate'=>$lastweek,
2213                                                'cutoffdate'=>$yesterday,
2214                                                'assignsubmission_onlinetext_enabled'=>1));
2216         $this->setUser($this->students[0]);
2217         $output = $assign->view_student_summary($this->students[0], true);
2218         $this->assertNotContains($output, get_string('editsubmission', 'assign'),
2219                                  'Should not be able to edit after cutoff date.');
2220         $this->assertNotContains($output, get_string('submitassignment', 'assign'),
2221                                  'Should not be able to submit after cutoff date.');
2222     }
2223     /**
2224      * Testing for submission comment plugin settings
2225      */
2226     public function test_submission_comment_plugin_settings() {
2227         global $CFG;
2229         $commentconfig = false;
2230         if (!empty($CFG->usecomments)) {
2231             $commentconfig = $CFG->usecomments;
2232         }
2234         $CFG->usecomments = true;
2235         $assign = $this->create_instance();
2236         $plugin = $assign->get_submission_plugin_by_type('comments');
2237         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2239         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
2240         $plugin = $assign->get_submission_plugin_by_type('comments');
2241         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2243         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
2244         $plugin = $assign->get_submission_plugin_by_type('comments');
2245         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2247         $CFG->usecomments = false;
2248         $assign = $this->create_instance();
2249         $plugin = $assign->get_submission_plugin_by_type('comments');
2250         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2252         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
2253         $plugin = $assign->get_submission_plugin_by_type('comments');
2254         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2256         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
2257         $plugin = $assign->get_submission_plugin_by_type('comments');
2258         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2260         $CFG->usecomments = $commentconfig;
2261     }
2263     /**
2264      * Testing for comment inline settings
2265      */
2266     public function test_feedback_comment_commentinline() {
2267         global $CFG;
2269         $sourcetext = "Hello!
2271 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
2273 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
2274 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
2276 External link 1:<img src='https://moodle.org/logo/logo-240x60.gif' alt='Moodle'/>
2277 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\"/>
2278 Internal link 1:<img src='@@PLUGINFILE@@/logo-240x60.gif' alt='Moodle'/>
2279 Internal link 2:<img alt=\"Moodle\" src=\"@@PLUGINFILE@@logo-240x60.gif\"/>
2280 Anchor link 1:<a href=\"@@PLUGINFILE@@logo-240x60.gif\" alt=\"bananas\">Link text</a>
2281 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
2282 ";
2284         // Note the internal images have been stripped and the html is purified (quotes fixed in this case).
2285         $filteredtext = "Hello!
2287 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
2289 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
2290 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
2292 External link 1:<img src=\"https://moodle.org/logo/logo-240x60.gif\" alt=\"Moodle\" />
2293 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\" />
2294 Internal link 1:
2295 Internal link 2:
2296 Anchor link 1:Link text
2297 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
2298 ";
2300         $this->setUser($this->editingteachers[0]);
2301         $params = array('assignsubmission_onlinetext_enabled' => 1,
2302                         'assignfeedback_comments_enabled' => 1,
2303                         'assignfeedback_comments_commentinline' => 1);
2304         $assign = $this->create_instance($params);
2306         $this->setUser($this->students[0]);
2307         // Add a submission but don't submit now.
2308         $submission = $assign->get_user_submission($this->students[0]->id, true);
2309         $data = new stdClass();
2311         // Test the internal link is stripped, but the external one is not.
2312         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2313                                          'text'=>$sourcetext,
2314                                          'format'=>FORMAT_MOODLE);
2316         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2317         $plugin->save($submission, $data);
2319         $this->setUser($this->editingteachers[0]);
2321         $data = new stdClass();
2322         require_once($CFG->dirroot . '/mod/assign/gradeform.php');
2323         $pagination = array('userid'=>$this->students[0]->id,
2324                             'rownum'=>0,
2325                             'last'=>true,
2326                             'useridlistid' => $assign->get_useridlist_key_id(),
2327                             'attemptnumber'=>0);
2328         $formparams = array($assign, $data, $pagination);
2329         $mform = new mod_assign_grade_form(null, $formparams);
2331         $this->assertEquals($filteredtext, $data->assignfeedbackcomments_editor['text']);
2332     }
2334     /**
2335      * Testing for feedback comment plugin settings
2336      */
2337     public function test_feedback_plugin_settings() {
2339         $assign = $this->create_instance();
2340         $plugin = $assign->get_feedback_plugin_by_type('comments');
2341         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2343         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 0));
2344         $plugin = $assign->get_feedback_plugin_by_type('comments');
2345         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2347         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
2348         $plugin = $assign->get_feedback_plugin_by_type('comments');
2349         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2350     }
2352     /**
2353      * Testing if gradebook feedback plugin is enabled.
2354      */
2355     public function test_is_gradebook_feedback_enabled() {
2356         $adminconfig = get_config('assign');
2357         $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
2359         // Create assignment with gradebook feedback enabled and grade = 0.
2360         $assign = $this->create_instance(array($gradebookplugin . '_enabled' => 1, 'grades' => 0));
2362         // Get gradebook feedback plugin.
2363         $gradebookplugintype = str_replace('assignfeedback_', '', $gradebookplugin);
2364         $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
2365         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2366         $this->assertEquals(1, $assign->is_gradebook_feedback_enabled());
2368         // Create assignment with gradebook feedback disabled and grade = 0.
2369         $assign = $this->create_instance(array($gradebookplugin . '_enabled' => 0, 'grades' => 0));
2370         $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
2371         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2372     }
2374     /**
2375      * Testing can_edit_submission
2376      */
2377     public function test_can_edit_submission() {
2378         global $PAGE, $DB;
2379         $this->create_extra_users();
2381         $this->setAdminUser();
2382         // Create assignment (onlinetext).
2383         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
2384         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2386         // Check student can edit their own submission.
2387         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->students[0]->id));
2388         // Check student cannot edit others submission.
2389         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->students[1]->id));
2391         // Check teacher cannot (by default) edit a students submission.
2392         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
2394         // Add the required capability to edit a student submission.
2395         $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
2396         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
2397         role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
2398         accesslib_clear_all_caches_for_unit_testing();
2399         // Retest - should now have access.
2400         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
2402         // Force create an assignment with SEPARATEGROUPS.
2403         $data = new stdClass();
2404         $data->courseid = $this->course->id;
2405         $data->name = 'Grouping';
2406         $groupingid = groups_create_grouping($data);
2407         groups_assign_grouping($groupingid, $this->groups[0]->id);
2408         groups_assign_grouping($groupingid, $this->groups[1]->id);
2409         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
2411         // Add the capability to the new assignment for extra students 0 and 1.
2412         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
2413         role_assign($roleid, $this->extrastudents[0]->id, $assign->get_context()->id);
2414         role_assign($roleid, $this->extrastudents[1]->id, $assign->get_context()->id);
2415         accesslib_clear_all_caches_for_unit_testing();
2417         // Verify the extra student does not have the capability to edit a submission not in their group.
2418         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[1]->id));
2419         // Verify the extra student does have the capability to edit a submission in their group.
2420         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[0]->id));
2422     }
2424     /**
2425      * Test if the view blind details capability works
2426      */
2427     public function test_can_view_blind_details() {
2428         global $PAGE, $DB;
2429         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2430         $managerrole = $DB->get_record('role', array('shortname' => 'manager'));
2432         $student = $this->students[0];// Get a student user.
2433         // Create a teacher. Shouldn't be able to view blind marking ID.
2434         $teacher = $this->getDataGenerator()->create_user();
2436         $this->getDataGenerator()->enrol_user($teacher->id,
2437                                               $this->course->id,
2438                                               $teacherrole->id);
2440         // Create a manager.. Should be able to view blind marking ID.
2441         $manager = $this->getDataGenerator()->create_user();
2442         $this->getDataGenerator()->enrol_user($manager->id,
2443                 $this->course->id,
2444                 $managerrole->id);
2446         // Generate blind marking assignment.
2447         $assign = $this->create_instance(array('blindmarking' => 1));
2448         $this->assertEquals(true, $assign->is_blind_marking());
2450         // Test student names are hidden to teacher.
2451         $this->setUser($teacher);
2452         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
2453         $output = $assign->get_renderer()->render($gradingtable);
2454         $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));    // "Participant" is somewhere on the page.
2455         $this->assertEquals(false, strpos($output, fullname($student)));    // Students full name doesn't appear.
2457         // Test student names are visible to manager.
2458         $this->setUser($manager);
2459         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
2460         $output = $assign->get_renderer()->render($gradingtable);
2461         $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));
2462         $this->assertEquals(true, strpos($output, fullname($student)));
2463     }
2465     /**
2466      * Testing get_shared_group_members
2467      */
2468     public function test_get_shared_group_members() {
2469         $this->create_extra_users();
2470         $this->setAdminUser();
2472         // Force create an assignment with SEPARATEGROUPS.
2473         $data = new stdClass();
2474         $data->courseid = $this->course->id;
2475         $data->name = 'Grouping';
2476         $groupingid = groups_create_grouping($data);
2477         groups_assign_grouping($groupingid, $this->groups[0]->id);
2478         groups_assign_grouping($groupingid, $this->groups[1]->id);
2479         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
2480         $cm = $assign->get_course_module();
2482         // Add the capability to access allgroups.
2483         $roleid = create_role('Access all groups role', 'accessallgroupsrole', '');
2484         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $roleid, $assign->get_context()->id);
2485         role_assign($roleid, $this->extrastudents[3]->id, $assign->get_context()->id);
2486         accesslib_clear_all_caches_for_unit_testing();
2488         // Get shared group members for students 0 and 1.
2489         $groupmembers = array();
2490         $groupmembers[0] = $assign->get_shared_group_members($cm, $this->students[0]->id);
2491         $groupmembers[1] = $assign->get_shared_group_members($cm, $this->students[1]->id);
2493         // They should share groups with extrastudents 0 and 1.
2494         $this->assertTrue(in_array($this->extrastudents[0]->id, $groupmembers[0]));
2495         $this->assertFalse(in_array($this->extrastudents[0]->id, $groupmembers[1]));
2496         $this->assertTrue(in_array($this->extrastudents[1]->id, $groupmembers[1]));
2497         $this->assertFalse(in_array($this->extrastudents[1]->id, $groupmembers[0]));
2499         // Lists of group members for students and extrastudents should be the same.
2500         $this->assertEquals($groupmembers[0], $assign->get_shared_group_members($cm, $this->extrastudents[0]->id));
2501         $this->assertEquals($groupmembers[1], $assign->get_shared_group_members($cm, $this->extrastudents[1]->id));
2503         // Get all group members for extrastudent 3 wich can access all groups.
2504         $allgroupmembers = $assign->get_shared_group_members($cm, $this->extrastudents[3]->id);
2506         // Extrastudent 3 should see students 0 and 1, extrastudent 0 and 1.
2507         $this->assertTrue(in_array($this->students[0]->id, $allgroupmembers));
2508         $this->assertTrue(in_array($this->students[1]->id, $allgroupmembers));
2509         $this->assertTrue(in_array($this->extrastudents[0]->id, $allgroupmembers));
2510         $this->assertTrue(in_array($this->extrastudents[1]->id , $allgroupmembers));
2511     }
2513     /**
2514      * Test get plugins file areas
2515      */
2516     public function test_get_plugins_file_areas() {
2517         $this->setUser($this->editingteachers[0]);
2518         $assign = $this->create_instance();
2520         // Test that all the submission and feedback plugins are returning the expected file aras.
2521         $usingfilearea = 0;
2522         $coreplugins = core_plugin_manager::standard_plugins_list('assignsubmission');
2523         foreach ($assign->get_submission_plugins() as $plugin) {
2524             $type = $plugin->get_type();
2525             if (!in_array($type, $coreplugins)) {
2526                 continue;
2527             }
2528             $fileareas = $plugin->get_file_areas();
2530             if ($type == 'onlinetext') {
2531                 $this->assertEquals(array('submissions_onlinetext' => 'Online text'), $fileareas);
2532                 $usingfilearea++;
2533             } else if ($type == 'file') {
2534                 $this->assertEquals(array('submission_files' => 'File submissions'), $fileareas);
2535                 $usingfilearea++;
2536             } else {
2537                 $this->assertEmpty($fileareas);
2538             }
2539         }
2540         $this->assertEquals(2, $usingfilearea);
2542         $usingfilearea = 0;
2543         $coreplugins = core_plugin_manager::standard_plugins_list('assignfeedback');
2544         foreach ($assign->get_feedback_plugins() as $plugin) {
2545             $type = $plugin->get_type();
2546             if (!in_array($type, $coreplugins)) {
2547                 continue;
2548             }
2549             $fileareas = $plugin->get_file_areas();
2551             if ($type == 'editpdf') {
2552                 $this->assertEquals(array('download' => 'Annotate PDF'), $fileareas);
2553                 $usingfilearea++;
2554             } else if ($type == 'file') {
2555                 $this->assertEquals(array('feedback_files' => 'Feedback files'), $fileareas);
2556                 $usingfilearea++;
2557             } else {
2558                 $this->assertEmpty($fileareas);
2559             }
2560         }
2561         $this->assertEquals(2, $usingfilearea);
2562     }
2564     /**
2565      * Test the quicksave grades processor
2566      */
2567     public function test_process_save_quick_grades() {
2568         $this->editingteachers[0]->ignoresesskey = true;
2569         $this->setUser($this->editingteachers[0]);
2571         $assign = $this->create_instance(array('attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL));
2573         // Initially grade the user.
2574         $grade = $assign->get_user_grade($this->students[0]->id, false);
2575         if (!$grade) {
2576             $grade = new stdClass();
2577             $grade->attemptnumber = '';
2578             $grade->timemodified = '';
2579         }
2580         $data = array(
2581             'grademodified_' . $this->students[0]->id => $grade->timemodified,
2582             'gradeattempt_' . $this->students[0]->id => $grade->attemptnumber,
2583             'quickgrade_' . $this->students[0]->id => '60.0'
2584         );
2585         $result = $assign->testable_process_save_quick_grades($data);
2586         $this->assertContains(get_string('quickgradingchangessaved', 'assign'), $result);
2587         $grade = $assign->get_user_grade($this->students[0]->id, false);
2588         $this->assertEquals('60.0', $grade->grade);
2590         // Attempt to grade with a past attempts grade info.
2591         $assign->testable_process_add_attempt($this->students[0]->id);
2592         $data = array(
2593             'grademodified_' . $this->students[0]->id => $grade->timemodified,
2594             'gradeattempt_' . $this->students[0]->id => $grade->attemptnumber,
2595             'quickgrade_' . $this->students[0]->id => '50.0'
2596         );
2597         $result = $assign->testable_process_save_quick_grades($data);
2598         $this->assertContains(get_string('errorrecordmodified', 'assign'), $result);
2599         $grade = $assign->get_user_grade($this->students[0]->id, false);
2600         $this->assertFalse($grade);
2602         // Attempt to grade a the attempt.
2603         $submission = $assign->get_user_submission($this->students[0]->id, false);
2604         $data = array(
2605             'grademodified_' . $this->students[0]->id => '',
2606             'gradeattempt_' . $this->students[0]->id => $submission->attemptnumber,
2607             'quickgrade_' . $this->students[0]->id => '40.0'
2608         );
2609         $result = $assign->testable_process_save_quick_grades($data);
2610         $this->assertContains(get_string('quickgradingchangessaved', 'assign'), $result);
2611         $grade = $assign->get_user_grade($this->students[0]->id, false);
2612         $this->assertEquals('40.0', $grade->grade);
2614         // Catch grade update conflicts.
2615         // Save old data for later.
2616         $pastdata = $data;
2617         // Update the grade the 'good' way.
2618         $data = array(
2619             'grademodified_' . $this->students[0]->id => $grade->timemodified,
2620             'gradeattempt_' . $this->students[0]->id => $grade->attemptnumber,
2621             'quickgrade_' . $this->students[0]->id => '30.0'
2622         );
2623         $result = $assign->testable_process_save_quick_grades($data);
2624         $this->assertContains(get_string('quickgradingchangessaved', 'assign'), $result);
2625         $grade = $assign->get_user_grade($this->students[0]->id, false);
2626         $this->assertEquals('30.0', $grade->grade);
2628         // Now update using 'old' data. Should fail.
2629         $result = $assign->testable_process_save_quick_grades($pastdata);
2630         $this->assertContains(get_string('errorrecordmodified', 'assign'), $result);
2631         $grade = $assign->get_user_grade($this->students[0]->id, false);
2632         $this->assertEquals('30.0', $grade->grade);
2633     }
2635     /**
2636      * Test updating activity completion when submitting an assessment.
2637      */
2638     public function test_update_activity_completion_records_solitary_submission() {
2639         $assign = $this->create_instance(array('grade' => 100,
2640                 'completion' => COMPLETION_TRACKING_AUTOMATIC,
2641                 'requireallteammemberssubmit' => 0));
2643         $cm = $assign->get_course_module();
2645         $student = $this->students[0];
2646         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
2648         $this->setUser($student);
2650         // Simulate a submission.
2651         $data = new stdClass();
2652         $data->onlinetext_editor = array(
2653             'itemid' => file_get_unused_draft_itemid(),
2654             'text' => 'Student submission text',
2655             'format' => FORMAT_MOODLE
2656         );
2657         $completion = new completion_info($this->course);
2659         $notices = array();
2660         $assign->save_submission($data, $notices);
2662         $submission = $assign->get_user_submission($student->id, true);
2664         // Check that completion is not met yet.
2665         $completiondata = $completion->get_data($cm, false, $student->id);
2666         $this->assertEquals(0, $completiondata->completionstate);
2667         $assign->testable_update_activity_completion_records(0, 0, $submission,
2668                 $student->id, COMPLETION_COMPLETE, $completion);
2669         // Completion should now be met.
2670         $completiondata = $completion->get_data($cm, false, $student->id);
2671         $this->assertEquals(1, $completiondata->completionstate);
2672     }
2674     /**
2675      * Test updating activity completion when submitting an assessment.
2676      */
2677     public function test_update_activity_completion_records_team_submission() {
2678         $assign = $this->create_instance(array('grade' => 100,
2679                 'completion' => COMPLETION_TRACKING_AUTOMATIC,
2680                  'teamsubmission' => 1));
2682         $cm = $assign->get_course_module();
2684         $student1 = $this->students[0];
2685         $student2 = $this->students[1];
2686         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
2688         // Put both users into a group.
2689         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
2690         $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $student1->id));
2691         $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $student2->id));
2693         $this->setUser($student1);
2695         // Simulate a submission.
2696         $data = new stdClass();
2697         $data->onlinetext_editor = array(
2698             'itemid' => file_get_unused_draft_itemid(),
2699             'text' => 'Student submission text',
2700             'format' => FORMAT_MOODLE
2701         );
2702         $completion = new completion_info($this->course);
2704         $notices = array();
2705         $assign->save_submission($data, $notices);
2707         $submission = $assign->get_user_submission($student1->id, true);
2708         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2709         $submission->groupid = $group1->id;
2711         // Check that completion is not met yet.
2712         $completiondata = $completion->get_data($cm, false, $student1->id);
2713         $this->assertEquals(0, $completiondata->completionstate);
2714         $completiondata = $completion->get_data($cm, false, $student2->id);
2715         $this->assertEquals(0, $completiondata->completionstate);
2716         $assign->testable_update_activity_completion_records(1, 0, $submission, $student1->id,
2717                 COMPLETION_COMPLETE, $completion);
2718         // Completion should now be met.
2719         $completiondata = $completion->get_data($cm, false, $student1->id);
2720         $this->assertEquals(1, $completiondata->completionstate);
2721         $completiondata = $completion->get_data($cm, false, $student2->id);
2722         $this->assertEquals(1, $completiondata->completionstate);
2723     }