48a4688322f5dde7676504759197ea2ea96d8bd7
[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     public function test_show_intro() {
122         // Test whether we are showing the intro at the correct times.
123         $this->setUser($this->editingteachers[0]);
124         $assign = $this->create_instance(array('alwaysshowdescription'=>1));
126         $this->assertEquals(true, $assign->testable_show_intro());
128         $tomorrow = time() + (24*60*60);
130         $assign = $this->create_instance(array('alwaysshowdescription'=>0,
131                                                'allowsubmissionsfromdate'=>$tomorrow));
132         $this->assertEquals(false, $assign->testable_show_intro());
133         $yesterday = time() - (24*60*60);
134         $assign = $this->create_instance(array('alwaysshowdescription'=>0,
135                                                'allowsubmissionsfromdate'=>$yesterday));
136         $this->assertEquals(true, $assign->testable_show_intro());
137     }
139     public function test_has_submissions_or_grades() {
140         $this->setUser($this->editingteachers[0]);
141         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
143         $instance = $assign->get_instance();
145         // Should start empty.
146         $this->assertEquals(false, $assign->has_submissions_or_grades());
148         // Simulate a submission.
149         $this->setUser($this->students[0]);
150         $submission = $assign->get_user_submission($this->students[0]->id, true);
151         $data = new stdClass();
152         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
153                                          'text'=>'Submission text',
154                                          'format'=>FORMAT_MOODLE);
155         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
156         $plugin->save($submission, $data);
158         // Now test again.
159         $this->assertEquals(true, $assign->has_submissions_or_grades());
160         // Set this back to default.
161         $this->students[0]->ignoresesskey = false;
162     }
164     public function test_delete_grades() {
165         $this->setUser($this->editingteachers[0]);
166         $assign = $this->create_instance();
168         // Simulate adding a grade.
169         $this->setUser($this->teachers[0]);
170         $data = new stdClass();
171         $data->grade = '50.0';
172         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
174         // Now see if the data is in the gradebook.
175         $gradinginfo = grade_get_grades($this->course->id,
176                                         'mod',
177                                         'assign',
178                                         $assign->get_instance()->id);
180         $this->assertNotEquals(0, count($gradinginfo->items));
182         $assign->testable_delete_grades();
183         $gradinginfo = grade_get_grades($this->course->id,
184                                         'mod',
185                                         'assign',
186                                         $assign->get_instance()->id);
188         $this->assertEquals(0, count($gradinginfo->items));
189     }
191     public function test_delete_instance() {
192         $this->setUser($this->editingteachers[0]);
193         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
195         // Simulate adding a grade.
196         $this->setUser($this->teachers[0]);
197         $data = new stdClass();
198         $data->grade = '50.0';
199         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
201         // Simulate a submission.
202         $this->setUser($this->students[0]);
203         $submission = $assign->get_user_submission($this->students[0]->id, true);
204         $data = new stdClass();
205         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
206                                          'text'=>'Submission text',
207                                          'format'=>FORMAT_MOODLE);
208         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
209         $plugin->save($submission, $data);
211         // Now try and delete.
212         $this->assertEquals(true, $assign->delete_instance());
213     }
215     public function test_reset_userdata() {
216         global $DB;
218         $now = time();
219         $this->setUser($this->editingteachers[0]);
220         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1,
221                                                'duedate'=>$now));
223         // Simulate adding a grade.
224         $this->setUser($this->teachers[0]);
225         $data = new stdClass();
226         $data->grade = '50.0';
227         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
229         // Simulate a submission.
230         $this->setUser($this->students[0]);
231         $submission = $assign->get_user_submission($this->students[0]->id, true);
232         $data = new stdClass();
233         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
234                                          'text'=>'Submission text',
235                                          'format'=>FORMAT_MOODLE);
236         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
237         $plugin->save($submission, $data);
239         $this->assertEquals(true, $assign->has_submissions_or_grades());
240         // Now try and reset.
241         $data = new stdClass();
242         $data->reset_assign_submissions = 1;
243         $data->reset_gradebook_grades = 1;
244         $data->courseid = $this->course->id;
245         $data->timeshift = 24*60*60;
246         $this->setUser($this->editingteachers[0]);
247         $assign->reset_userdata($data);
248         $this->assertEquals(false, $assign->has_submissions_or_grades());
250         // Reload the instance data.
251         $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
252         $this->assertEquals($now + 24*60*60, $instance->duedate);
253     }
255     public function test_plugin_settings() {
256         global $DB;
258         $now = time();
259         $this->setUser($this->editingteachers[0]);
260         $assign = $this->create_instance(array('assignsubmission_file_enabled'=>1,
261                                                'assignsubmission_file_maxfiles'=>12,
262                                                'assignsubmission_file_maxsizebytes'=>10));
264         $plugin = $assign->get_submission_plugin_by_type('file');
265         $this->assertEquals('12', $plugin->get_config('maxfilesubmissions'));
266     }
268     public function test_update_calendar() {
269         global $DB;
271         $now = time();
272         $this->setUser($this->editingteachers[0]);
273         $assign = $this->create_instance(array('duedate'=>$now));
275         // See if there is an event in the calendar.
276         $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
277         $id = $DB->get_field('event', 'id', $params);
279         $this->assertEquals(false, empty($id));
280     }
282     public function test_update_instance() {
283         global $DB;
285         $this->setUser($this->editingteachers[0]);
286         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
288         $now = time();
289         $instance = $assign->get_instance();
290         $instance->duedate = $now;
291         $instance->instance = $instance->id;
292         $instance->assignsubmission_onlinetext_enabled = 1;
294         $assign->update_instance($instance);
296         $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
297         $this->assertEquals($now, $instance->duedate);
298     }
300     public function test_cannot_submit_empty() {
301         global $PAGE;
303         $this->setUser($this->editingteachers[0]);
304         $assign = $this->create_instance(array('submissiondrafts'=>1));
306         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
308         // Test you cannot see the submit button for an offline assignment regardless.
309         $this->setUser($this->students[0]);
310         $output = $assign->view_student_summary($this->students[0], true);
311         $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Can submit empty offline assignment');
313         // Test you cannot see the submit button for an online text assignment with no submission.
314         $this->setUser($this->editingteachers[0]);
315         $instance = $assign->get_instance();
316         $instance->instance = $instance->id;
317         $instance->assignsubmission_onlinetext_enabled = 1;
319         $assign->update_instance($instance);
320         $this->setUser($this->students[0]);
321         $output = $assign->view_student_summary($this->students[0], true);
322         $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Cannot submit empty onlinetext assignment');
324         // Simulate a submission.
325         $submission = $assign->get_user_submission($this->students[0]->id, true);
326         $data = new stdClass();
327         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
328                                          'text'=>'Submission text',
329                                          'format'=>FORMAT_MOODLE);
330         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
331         $plugin->save($submission, $data);
332         // Test you can see the submit button for an online text assignment with a submission.
333         $output = $assign->view_student_summary($this->students[0], true);
334         $this->assertContains(get_string('submitassignment', 'assign'), $output, 'Can submit non empty onlinetext assignment');
335     }
337     public function test_list_participants() {
338         $this->create_extra_users();
339         $this->setUser($this->editingteachers[0]);
340         $assign = $this->create_instance(array('grade'=>100));
342         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
344         // Teacher with user preference set should see suspended users as well.
345         set_user_preference('grade_report_showonlyactiveenrol', false);
346         $assign = $this->create_instance(array('grade'=>100));
347         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT,
348                 count($assign->list_participants(null, true)));
350         // Non-editing teacher should not see suspended users, even if user preference is set.
351         $this->setUser($this->teachers[0]);
352         set_user_preference('grade_report_showonlyactiveenrol', false);
353         $assign = $this->create_instance(array('grade'=>100));
354         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
355     }
357     public function test_count_teams() {
358         $this->create_extra_users();
359         $this->setUser($this->editingteachers[0]);
360         $assign = $this->create_instance(array('teamsubmission'=>1));
362         $this->assertEquals(self::GROUP_COUNT + 1, $assign->count_teams());
363     }
365     public function test_count_submissions() {
366         $this->create_extra_users();
367         $this->setUser($this->editingteachers[0]);
368         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
370         // Simulate a submission.
371         $this->setUser($this->extrastudents[0]);
372         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
373         // Leave this one as DRAFT.
374         $data = new stdClass();
375         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
376                                          'text'=>'Submission text',
377                                          'format'=>FORMAT_MOODLE);
378         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
379         $plugin->save($submission, $data);
381         // Simulate adding a grade.
382         $this->setUser($this->teachers[0]);
383         $data = new stdClass();
384         $data->grade = '50.0';
385         $assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
387         // Simulate a submission.
388         $this->setUser($this->extrastudents[1]);
389         $submission = $assign->get_user_submission($this->extrastudents[1]->id, true);
390         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
391         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
392         $data = new stdClass();
393         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
394                                          'text'=>'Submission text',
395                                          'format'=>FORMAT_MOODLE);
396         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
397         $plugin->save($submission, $data);
399         // Simulate a submission.
400         $this->setUser($this->extrastudents[2]);
401         $submission = $assign->get_user_submission($this->extrastudents[2]->id, true);
402         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
403         $assign->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
404         $data = new stdClass();
405         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
406                                          'text'=>'Submission text',
407                                          'format'=>FORMAT_MOODLE);
408         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
409         $plugin->save($submission, $data);
411         // Simulate a submission.
412         $this->setUser($this->extrastudents[3]);
413         $submission = $assign->get_user_submission($this->extrastudents[3]->id, true);
414         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
415         $assign->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
416         $data = new stdClass();
417         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
418                                          'text'=>'Submission text',
419                                          'format'=>FORMAT_MOODLE);
420         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
421         $plugin->save($submission, $data);
423         // Simulate a submission for suspended user, this will never be counted.
424         $this->setUser($this->extrastudents[3]);
425         $submission = $assign->get_user_submission($this->extrasuspendedstudents[0]->id, true);
426         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
427         $assign->testable_update_submission($submission, $this->extrasuspendedstudents[0]->id, true, false);
428         $data = new stdClass();
429         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
430                                          'text'=>'Submission text',
431                                          'format'=>FORMAT_MOODLE);
432         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
433         $plugin->save($submission, $data);
435         // Simulate adding a grade.
436         $this->setUser($this->teachers[0]);
437         $data = new stdClass();
438         $data->grade = '50.0';
439         $assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
440         $assign->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
442         $this->assertEquals(2, $assign->count_grades());
443         $this->assertEquals(4, $assign->count_submissions());
444         $this->assertEquals(2, $assign->count_submissions_need_grading());
445         $this->assertEquals(3, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
446         $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
447     }
449     public function test_get_grading_userid_list() {
450         $this->create_extra_users();
451         $this->setUser($this->editingteachers[0]);
452         $assign = $this->create_instance();
454         $users = $assign->testable_get_grading_userid_list();
455         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($users));
457         $this->setUser($this->editingteachers[0]);
458         set_user_preference('grade_report_showonlyactiveenrol', false);
459         $assign = $this->create_instance();
461         $users = $assign->testable_get_grading_userid_list();
462         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT, count($users));
463     }
465     public function test_cron() {
466         // First run cron so there are no messages waiting to be sent (from other tests).
467         cron_setup_user();
468         assign::cron();
470         // Now create an assignment and add some feedback.
471         $this->setUser($this->editingteachers[0]);
472         $assign = $this->create_instance();
474         // Simulate adding a grade.
475         $this->setUser($this->teachers[0]);
476         $data = new stdClass();
477         $data->grade = '50.0';
478         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
480         // Now run cron and see that one message was sent.
481         $this->preventResetByRollback();
482         $sink = $this->redirectMessages();
483         cron_setup_user();
484         $this->expectOutputRegex('/Done processing 1 assignment submissions/');
485         assign::cron();
487         $messages = $sink->get_messages();
488         $this->assertEquals(1, count($messages));
489         $this->assertEquals(1, $messages[0]->notification);
490         $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
491     }
493     public function test_is_graded() {
494         $this->setUser($this->editingteachers[0]);
495         $assign = $this->create_instance();
497         // Simulate adding a grade.
498         $this->setUser($this->teachers[0]);
499         $data = new stdClass();
500         $data->grade = '50.0';
501         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
503         $this->assertEquals(true, $assign->testable_is_graded($this->students[0]->id));
504         $this->assertEquals(false, $assign->testable_is_graded($this->students[1]->id));
505     }
507     public function test_can_view_submission() {
508         $this->create_extra_users();
509         $this->setUser($this->editingteachers[0]);
510         $assign = $this->create_instance();
512         $this->setUser($this->students[0]);
513         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
514         $this->assertEquals(false, $assign->can_view_submission($this->students[1]->id));
515         $this->assertEquals(false, $assign->can_view_submission($this->teachers[0]->id));
516         $this->setUser($this->teachers[0]);
517         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
518         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
519         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
520         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
521         $this->setUser($this->editingteachers[0]);
522         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
523         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
524         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
525         $this->assertEquals(true, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
526     }
529     public function test_update_submission() {
530         $this->create_extra_users();
531         $this->setUser($this->editingteachers[0]);
532         $assign = $this->create_instance();
534         $this->setUser($this->extrastudents[0]);
535         $now = time();
536         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
537         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
539         $this->setUser($this->teachers[0]);
540         // Verify the gradebook update.
541         $gradinginfo = grade_get_grades($this->course->id,
542                                         'mod',
543                                         'assign',
544                                         $assign->get_instance()->id,
545                                         $this->extrastudents[0]->id);
547         $this->assertEquals($this->extrastudents[0]->id,
548                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
550         // Now verify group assignments.
551         $this->setUser($this->editingteachers[0]);
552         $assign = $this->create_instance(array('teamsubmission'=>1));
554         $this->setUser($this->extrastudents[0]);
555         $now = time();
556         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
557         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
559         // Check that at least 2 active members and 1 suspended member of the submission group had their submission updated.
561         $this->setUser($this->editingteachers[0]);
562         $gradinginfo = grade_get_grades($this->course->id,
563                                         'mod',
564                                         'assign',
565                                         $assign->get_instance()->id,
566                                         $this->extrastudents[0]->id);
568         $this->assertEquals($this->extrastudents[0]->id,
569                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
571         $gradinginfo = grade_get_grades($this->course->id,
572                                         'mod',
573                                         'assign',
574                                         $assign->get_instance()->id,
575                                         $this->extrastudents[self::GROUP_COUNT]->id);
577         $this->assertEquals($this->extrastudents[self::GROUP_COUNT]->id,
578                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT]->id]->usermodified);
580         $gradinginfo = grade_get_grades($this->course->id,
581                                         'mod',
582                                         'assign',
583                                         $assign->get_instance()->id,
584                                         $this->extrasuspendedstudents[0]->id);
585         $this->assertEquals($this->extrasuspendedstudents[0]->id,
586                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[0]->id]->usermodified);
588         // Check the same with non-editing teacher and make sure submission is not updated for suspended user.
589         $this->setUser($this->editingteachers[0]);
590         $assign = $this->create_instance(array('teamsubmission'=>1));
592         $this->setUser($this->extrastudents[1]);
593         $now = time();
594         $submission = $assign->get_group_submission($this->extrastudents[1]->id, 0, true);
595         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, true);
597         $this->setUser($this->teachers[0]);
598         $gradinginfo = grade_get_grades($this->course->id,
599                                         'mod',
600                                         'assign',
601                                         $assign->get_instance()->id,
602                                         $this->extrastudents[1]->id);
604         $this->assertEquals($this->extrastudents[1]->id,
605                             $gradinginfo->items[0]->grades[$this->extrastudents[1]->id]->usermodified);
607         $gradinginfo = grade_get_grades($this->course->id,
608                                         'mod',
609                                         'assign',
610                                         $assign->get_instance()->id,
611                                         $this->extrastudents[self::GROUP_COUNT+1]->id);
613         $this->assertEquals($this->extrastudents[self::GROUP_COUNT+1]->id,
614                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT+1]->id]->usermodified);
616         $gradinginfo = grade_get_grades($this->course->id,
617                                         'mod',
618                                         'assign',
619                                         $assign->get_instance()->id,
620                                         $this->extrasuspendedstudents[1]->id);
621         $this->assertEquals($this->extrasuspendedstudents[1]->id,
622                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[1]->id]->usermodified);
624         // Now verify blind marking.
625         $this->setUser($this->editingteachers[0]);
626         $assign = $this->create_instance(array('blindmarking'=>1));
628         $this->setUser($this->extrastudents[0]);
629         $now = time();
630         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
631         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
633         $this->setUser($this->editingteachers[0]);
634         $gradinginfo = grade_get_grades($this->course->id,
635                                         'mod',
636                                         'assign',
637                                         $assign->get_instance()->id,
638                                         $this->extrastudents[0]->id);
640         $this->assertEquals(null, $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->datesubmitted);
641     }
643     public function test_submissions_open() {
644         $this->setUser($this->editingteachers[0]);
646         $now = time();
647         $tomorrow = $now + 24*60*60;
648         $oneweek = $now + 7*24*60*60;
649         $yesterday = $now - 24*60*60;
651         $assign = $this->create_instance();
652         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
654         $assign = $this->create_instance(array('duedate'=>$tomorrow));
655         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
657         $assign = $this->create_instance(array('duedate'=>$yesterday));
658         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
660         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$tomorrow));
661         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
663         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$yesterday));
664         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
666         $assign->testable_save_user_extension($this->students[0]->id, $tomorrow);
667         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
669         $assign = $this->create_instance(array('submissiondrafts'=>1));
670         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
672         $this->setUser($this->students[0]);
673         $now = time();
674         $submission = $assign->get_user_submission($this->students[0]->id, true);
675         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
676         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
677         $this->setUser($this->editingteachers[0]);
678         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
679     }
681     public function test_get_graders() {
682         $this->create_extra_users();
683         $this->setUser($this->editingteachers[0]);
684         $assign = $this->create_instance();
686         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
687                            self::DEFAULT_EDITING_TEACHER_COUNT +
688                            self::EXTRA_TEACHER_COUNT +
689                            self::EXTRA_EDITING_TEACHER_COUNT,
690                            $assign->testable_get_graders($this->students[0]->id));
692         $assign = $this->create_instance();
693         // Force create an assignment with SEPARATEGROUPS.
694         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
695         $params = array('course'=>$this->course->id);
696         $instance = $generator->create_instance($params);
697         $cm = get_coursemodule_from_instance('assign', $instance->id);
698         set_coursemodule_groupmode($cm->id, SEPARATEGROUPS);
699         $cm->groupmode = SEPARATEGROUPS;
700         $context = context_module::instance($cm->id);
701         $assign = new testable_assign($context, $cm, $this->course);
703         $this->setUser($this->students[1]);
704         $this->assertCount(4, $assign->testable_get_graders($this->students[0]->id));
705     }
707     public function test_get_uniqueid_for_user() {
708         $this->setUser($this->editingteachers[0]);
709         $assign = $this->create_instance();
711         foreach ($this->students as $student) {
712             $uniqueid = $assign->get_uniqueid_for_user($student->id);
713             $this->assertEquals($student->id, $assign->get_user_id_for_uniqueid($uniqueid));
714         }
715     }
717     public function test_show_student_summary() {
718         global $CFG, $PAGE;
720         $this->setUser($this->editingteachers[0]);
721         $assign = $this->create_instance();
722         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
724         // No feedback should be available because this student has not been graded.
725         $this->setUser($this->students[0]);
726         $output = $assign->view_student_summary($this->students[0], true);
727         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if there is no grade');
728         // Simulate adding a grade.
729         $this->setUser($this->teachers[0]);
730         $data = new stdClass();
731         $data->grade = '50.0';
732         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
734         // Now we should see the feedback.
735         $this->setUser($this->students[0]);
736         $output = $assign->view_student_summary($this->students[0], true);
737         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback if there is a grade');
739         // Now hide the grade in gradebook.
740         $this->setUser($this->teachers[0]);
741         require_once($CFG->libdir.'/gradelib.php');
742         $gradeitem = new grade_item(array(
743             'itemtype'      => 'mod',
744             'itemmodule'    => 'assign',
745             'iteminstance'  => $assign->get_instance()->id,
746             'courseid'      => $this->course->id));
748         $gradeitem->set_hidden(1, false);
750         // No feedback should be available because the grade is hidden.
751         $this->setUser($this->students[0]);
752         $output = $assign->view_student_summary($this->students[0], true);
753         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
755         // Do the same but add feedback.
756         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
758         $this->setUser($this->teachers[0]);
759         $grade = $assign->get_user_grade($this->students[0]->id, true);
760         $data = new stdClass();
761         $data->assignfeedbackcomments_editor = array('text'=>'Tomato sauce',
762                                          'format'=>FORMAT_MOODLE);
763         $plugin = $assign->get_feedback_plugin_by_type('comments');
764         $plugin->save($grade, $data);
766         // Should have feedback but no grade.
767         $this->setUser($this->students[0]);
768         $output = $assign->view_student_summary($this->students[0], true);
769         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback even if there is no grade');
770         $this->assertEquals(false, strpos($output, 'Grade'), 'Do not show grade when there is no grade.');
771         $this->assertEquals(false, strpos($output, 'Graded on'), 'Do not show graded date when there is no grade.');
773         // Now hide the grade in gradebook.
774         $this->setUser($this->teachers[0]);
775         $gradeitem = new grade_item(array(
776             'itemtype'      => 'mod',
777             'itemmodule'    => 'assign',
778             'iteminstance'  => $assign->get_instance()->id,
779             'courseid'      => $this->course->id));
781         $gradeitem->set_hidden(1, false);
783         // No feedback should be available because the grade is hidden.
784         $this->setUser($this->students[0]);
785         $output = $assign->view_student_summary($this->students[0], true);
786         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
787     }
789     public function test_attempt_reopen_method_manual() {
790         global $PAGE;
792         $this->setUser($this->editingteachers[0]);
793         $assign = $this->create_instance(array('attemptreopenmethod'=>ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
794                                                'maxattempts'=>3,
795                                                'submissiondrafts'=>1,
796                                                'assignsubmission_onlinetext_enabled'=>1));
797         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
799         // Student should be able to see an add submission button.
800         $this->setUser($this->students[0]);
801         $output = $assign->view_student_summary($this->students[0], true);
802         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
804         // Add a submission.
805         $now = time();
806         $submission = $assign->get_user_submission($this->students[0]->id, true);
807         $data = new stdClass();
808         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
809                                          'text'=>'Submission text',
810                                          'format'=>FORMAT_MOODLE);
811         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
812         $plugin->save($submission, $data);
814         // And now submit it for marking.
815         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
816         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
818         // Verify the student cannot make changes to the submission.
819         $output = $assign->view_student_summary($this->students[0], true);
820         $this->assertEquals(false, strpos($output, get_string('addsubmission', 'assign')));
822         // Mark the submission.
823         $this->setUser($this->teachers[0]);
824         $data = new stdClass();
825         $data->grade = '50.0';
826         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
828         // Check the student can see the grade.
829         $this->setUser($this->students[0]);
830         $output = $assign->view_student_summary($this->students[0], true);
831         $this->assertNotEquals(false, strpos($output, '50.0'));
833         // Allow the student another attempt.
834         $this->teachers[0]->ignoresesskey = true;
835         $this->setUser($this->teachers[0]);
836         $result = $assign->testable_process_add_attempt($this->students[0]->id);
837         $this->assertEquals(true, $result);
839         // Check that the previous attempt is now in the submission history table.
840         $this->setUser($this->students[0]);
841         $output = $assign->view_student_summary($this->students[0], true);
842         // Need a better check.
843         $this->assertNotEquals(false, strpos($output, 'Submission text'), 'Contains: Submission text');
845         // Check that the student now has a button for Add a new attempt".
846         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
847         // Check that the student now does not have a button for Submit.
848         $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
850         // Check that the student now has a submission history.
851         $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
853         $this->setUser($this->teachers[0]);
854         // Check that the grading table loads correctly and contains this user.
855         // This is also testing that we do not get duplicate rows in the grading table.
856         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
857         $output = $assign->get_renderer()->render($gradingtable);
858         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
860         // Should be 1 not 2.
861         $this->assertEquals(1, $assign->count_submissions());
862         $this->assertEquals(1, $assign->count_submissions_with_status('reopened'));
863         $this->assertEquals(0, $assign->count_submissions_need_grading());
864         $this->assertEquals(1, $assign->count_grades());
866         // Change max attempts to unlimited.
867         $formdata = clone($assign->get_instance());
868         $formdata->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
869         $formdata->instance = $formdata->id;
870         $assign->update_instance($formdata);
872         // Check we can repopen still.
873         $result = $assign->testable_process_add_attempt($this->students[0]->id);
874         $this->assertEquals(true, $result);
876         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
877         $this->assertEquals(50, (int)$grades[$this->students[0]->id]->rawgrade);
879     }
881     public function test_markingworkflow() {
882         global $PAGE;
884         $this->setUser($this->editingteachers[0]);
885         $assign = $this->create_instance(array('markingworkflow'=>1));
886         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
888         // Mark the submission and set to notmarked.
889         $this->setUser($this->teachers[0]);
890         $data = new stdClass();
891         $data->grade = '50.0';
892         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED;
893         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
895         // Check the student can't see the grade.
896         $this->setUser($this->students[0]);
897         $output = $assign->view_student_summary($this->students[0], true);
898         $this->assertEquals(false, strpos($output, '50.0'));
900         // Mark the submission and set to inmarking.
901         $this->setUser($this->teachers[0]);
902         $data = new stdClass();
903         $data->grade = '50.0';
904         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INMARKING;
905         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
907         // Check the student can't see the grade.
908         $this->setUser($this->students[0]);
909         $output = $assign->view_student_summary($this->students[0], true);
910         $this->assertEquals(false, strpos($output, '50.0'));
912         // Mark the submission and set to readyforreview.
913         $this->setUser($this->teachers[0]);
914         $data = new stdClass();
915         $data->grade = '50.0';
916         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW;
917         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
919         // Check the student can't see the grade.
920         $this->setUser($this->students[0]);
921         $output = $assign->view_student_summary($this->students[0], true);
922         $this->assertEquals(false, strpos($output, '50.0'));
924         // Mark the submission and set to inreview.
925         $this->setUser($this->teachers[0]);
926         $data = new stdClass();
927         $data->grade = '50.0';
928         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW;
929         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
931         // Check the student can't see the grade.
932         $this->setUser($this->students[0]);
933         $output = $assign->view_student_summary($this->students[0], true);
934         $this->assertEquals(false, strpos($output, '50.0'));
936         // Mark the submission and set to readyforrelease.
937         $this->setUser($this->teachers[0]);
938         $data = new stdClass();
939         $data->grade = '50.0';
940         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
941         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
943         // Check the student can't see the grade.
944         $this->setUser($this->students[0]);
945         $output = $assign->view_student_summary($this->students[0], true);
946         $this->assertEquals(false, strpos($output, '50.0'));
948         // Mark the submission and set to released.
949         $this->setUser($this->teachers[0]);
950         $data = new stdClass();
951         $data->grade = '50.0';
952         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
953         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
955         // Check the student can see the grade.
956         $this->setUser($this->students[0]);
957         $output = $assign->view_student_summary($this->students[0], true);
958         $this->assertNotEquals(false, strpos($output, '50.0'));
959     }
961     public function test_markerallocation() {
962         global $PAGE;
964         $this->setUser($this->editingteachers[0]);
965         $assign = $this->create_instance(array('markingworkflow'=>1, 'markingallocation'=>1));
966         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
968         // Allocate marker to submission.
969         $data = new stdClass();
970         $data->allocatedmarker = $this->teachers[0]->id;
971         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
973         // Check the allocated marker can view the submission.
974         $this->setUser($this->teachers[0]);
975         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
976         $output = $assign->get_renderer()->render($gradingtable);
977         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
979         // Check that other teachers can't view this submission.
980         $this->setUser($this->teachers[1]);
981         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
982         $output = $assign->get_renderer()->render($gradingtable);
983         $this->assertNotEquals(true, strpos($output, $this->students[0]->lastname));
984     }
986     public function test_extension_granted_event() {
987         $this->setUser($this->editingteachers[0]);
989         $tomorrow = time() + 24*60*60;
990         $yesterday = time() - 24*60*60;
992         $assign = $this->create_instance(array('duedate' => $yesterday, 'cutoffdate' => $yesterday));
993         $sink = $this->redirectEvents();
995         $assign->testable_save_user_extension($this->students[0]->id, $tomorrow);
997         $events = $sink->get_events();
998         $this->assertCount(1, $events);
999         $event = reset($events);
1000         $this->assertInstanceOf('\mod_assign\event\extension_granted', $event);
1001         $this->assertEquals($assign->get_context(), $event->get_context());
1002         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1003         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1004         $expected = array(
1005             $assign->get_course()->id,
1006             'assign',
1007             'grant extension',
1008             'view.php?id=' . $assign->get_course_module()->id,
1009             $this->students[0]->id,
1010             $assign->get_course_module()->id,
1011             $this->editingteachers[0]->id
1012         );
1013         $this->assertEventLegacyLogData($expected, $event);
1014         $sink->close();
1015     }
1017     public function test_submission_locked_event() {
1018         $this->editingteachers[0]->ignoresesskey = true;
1019         $this->setUser($this->editingteachers[0]);
1021         $assign = $this->create_instance();
1022         $sink = $this->redirectEvents();
1024         $assign->lock_submission($this->students[0]->id);
1026         $events = $sink->get_events();
1027         $this->assertCount(1, $events);
1028         $event = reset($events);
1029         $this->assertInstanceOf('\mod_assign\event\submission_locked', $event);
1030         $this->assertEquals($assign->get_context(), $event->get_context());
1031         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1032         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1033         $expected = array(
1034             $assign->get_course()->id,
1035             'assign',
1036             'lock submission',
1037             'view.php?id=' . $assign->get_course_module()->id,
1038             get_string('locksubmissionforstudent', 'assign', array('id' => $this->students[0]->id,
1039                 'fullname' => fullname($this->students[0]))),
1040             $assign->get_course_module()->id,
1041             $this->editingteachers[0]->id
1042         );
1043         $this->assertEventLegacyLogData($expected, $event);
1044         $sink->close();
1046         // Revert to defaults.
1047         $this->editingteachers[0]->ignoresesskey = false;
1048     }
1050     public function test_identities_revealed_event() {
1051         $this->editingteachers[0]->ignoresesskey = true;
1052         $this->setUser($this->editingteachers[0]);
1054         $assign = $this->create_instance(array('blindmarking'=>1));
1055         $sink = $this->redirectEvents();
1057         $assign->reveal_identities();
1059         $events = $sink->get_events();
1060         $this->assertCount(1, $events);
1061         $event = reset($events);
1062         $this->assertInstanceOf('\mod_assign\event\identities_revealed', $event);
1063         $this->assertEquals($assign->get_context(), $event->get_context());
1064         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1065         $expected = array(
1066             $assign->get_course()->id,
1067             'assign',
1068             'reveal identities',
1069             'view.php?id=' . $assign->get_course_module()->id,
1070             get_string('revealidentities', 'assign'),
1071             $assign->get_course_module()->id,
1072             $this->editingteachers[0]->id
1073         );
1074         $this->assertEventLegacyLogData($expected, $event);
1075         $sink->close();
1077         // Revert to defaults.
1078         $this->editingteachers[0]->ignoresesskey = false;
1079     }
1081     public function test_submission_status_updated_event() {
1082         $this->editingteachers[0]->ignoresesskey = true;
1083         $this->setUser($this->editingteachers[0]);
1085         $assign = $this->create_instance();
1086         $submission = $assign->get_user_submission($this->students[0]->id, true);
1087         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1088         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1090         $sink = $this->redirectEvents();
1091         $assign->revert_to_draft($this->students[0]->id);
1093         $events = $sink->get_events();
1094         $this->assertCount(1, $events);
1095         $event = reset($events);
1096         $this->assertInstanceOf('\mod_assign\event\submission_status_updated', $event);
1097         $this->assertEquals($assign->get_context(), $event->get_context());
1098         $this->assertEquals($submission->id, $event->objectid);
1099         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1100         $this->assertEquals(ASSIGN_SUBMISSION_STATUS_DRAFT, $event->other['newstatus']);
1101         $expected = array(
1102             $assign->get_course()->id,
1103             'assign',
1104             'revert submission to draft',
1105             'view.php?id=' . $assign->get_course_module()->id,
1106             get_string('reverttodraftforstudent', 'assign', array('id' => $this->students[0]->id,
1107                 'fullname' => fullname($this->students[0]))),
1108             $assign->get_course_module()->id,
1109             $this->editingteachers[0]->id
1110         );
1111         $this->assertEventLegacyLogData($expected, $event);
1112         $sink->close();
1114         // Revert to defaults.
1115         $this->editingteachers[0]->ignoresesskey = false;
1116     }
1118     public function test_marker_updated_event() {
1119         $this->editingteachers[0]->ignoresesskey = true;
1120         $this->setUser($this->editingteachers[0]);
1122         $assign = $this->create_instance();
1124         $sink = $this->redirectEvents();
1125         $assign->testable_process_set_batch_marking_allocation($this->students[0]->id, $this->teachers[0]->id);
1127         $events = $sink->get_events();
1128         $this->assertCount(1, $events);
1129         $event = reset($events);
1130         $this->assertInstanceOf('\mod_assign\event\marker_updated', $event);
1131         $this->assertEquals($assign->get_context(), $event->get_context());
1132         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1133         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1134         $this->assertEquals($this->editingteachers[0]->id, $event->userid);
1135         $this->assertEquals($this->teachers[0]->id, $event->other['markerid']);
1136         $expected = array(
1137             $assign->get_course()->id,
1138             'assign',
1139             'set marking allocation',
1140             'view.php?id=' . $assign->get_course_module()->id,
1141             get_string('setmarkerallocationforlog', 'assign', array('id' => $this->students[0]->id,
1142                 'fullname' => fullname($this->students[0]), 'marker' => fullname($this->teachers[0]))),
1143             $assign->get_course_module()->id,
1144             $this->editingteachers[0]->id
1145         );
1146         $this->assertEventLegacyLogData($expected, $event);
1147         $sink->close();
1149         // Revert to defaults.
1150         $this->editingteachers[0]->ignoresesskey = false;
1151     }
1153     public function test_workflow_state_updated_event() {
1154         $this->editingteachers[0]->ignoresesskey = true;
1155         $this->setUser($this->editingteachers[0]);
1157         $assign = $this->create_instance();
1159         $sink = $this->redirectEvents();
1160         $assign->testable_process_set_batch_marking_workflow_state($this->students[0]->id, ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW);
1162         $events = $sink->get_events();
1163         $this->assertCount(1, $events);
1164         $event = reset($events);
1165         $this->assertInstanceOf('\mod_assign\event\workflow_state_updated', $event);
1166         $this->assertEquals($assign->get_context(), $event->get_context());
1167         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1168         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1169         $this->assertEquals($this->editingteachers[0]->id, $event->userid);
1170         $this->assertEquals(ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW, $event->other['newstate']);
1171         $expected = array(
1172             $assign->get_course()->id,
1173             'assign',
1174             'set marking workflow state',
1175             'view.php?id=' . $assign->get_course_module()->id,
1176             get_string('setmarkingworkflowstateforlog', 'assign', array('id' => $this->students[0]->id,
1177                 'fullname' => fullname($this->students[0]), 'state' => ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW)),
1178             $assign->get_course_module()->id,
1179             $this->editingteachers[0]->id
1180         );
1181         $this->assertEventLegacyLogData($expected, $event);
1182         $sink->close();
1184         // Revert to defaults.
1185         $this->editingteachers[0]->ignoresesskey = false;
1186     }
1188     public function test_submission_duplicated_event() {
1189         $this->setUser($this->students[0]);
1191         $assign = $this->create_instance();
1192         $submission1 = $assign->get_user_submission($this->students[0]->id, true, 0);
1193         $submission2 = $assign->get_user_submission($this->students[0]->id, true, 1);
1194         $submission2->status = ASSIGN_SUBMISSION_STATUS_REOPENED;
1195         $assign->testable_update_submission($submission2, $this->students[0]->id, time(), $assign->get_instance()->teamsubmission);
1197         $sink = $this->redirectEvents();
1198         $notices = null;
1199         $assign->copy_previous_attempt($notices);
1201         $events = $sink->get_events();
1202         $this->assertCount(1, $events);
1203         $event = reset($events);
1204         $this->assertInstanceOf('\mod_assign\event\submission_duplicated', $event);
1205         $this->assertEquals($assign->get_context(), $event->get_context());
1206         $this->assertEquals($submission2->id, $event->objectid);
1207         $this->assertEquals($this->students[0]->id, $event->userid);
1208         $submission2->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
1209         $expected = array(
1210             $assign->get_course()->id,
1211             'assign',
1212             'submissioncopied',
1213             'view.php?id=' . $assign->get_course_module()->id,
1214             $assign->testable_format_submission_for_log($submission2),
1215             $assign->get_course_module()->id,
1216             $this->students[0]->id
1217         );
1218         $this->assertEventLegacyLogData($expected, $event);
1219         $sink->close();
1220     }
1222     public function test_submission_unlocked_event() {
1223         $this->editingteachers[0]->ignoresesskey = true;
1224         $this->setUser($this->editingteachers[0]);
1226         $assign = $this->create_instance();
1227         $sink = $this->redirectEvents();
1229         $assign->unlock_submission($this->students[0]->id);
1231         $events = $sink->get_events();
1232         $this->assertCount(1, $events);
1233         $event = reset($events);
1234         $this->assertInstanceOf('\mod_assign\event\submission_unlocked', $event);
1235         $this->assertEquals($assign->get_context(), $event->get_context());
1236         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1237         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1238         $expected = array(
1239             $assign->get_course()->id,
1240             'assign',
1241             'unlock submission',
1242             'view.php?id=' . $assign->get_course_module()->id,
1243             get_string('unlocksubmissionforstudent', 'assign', array('id' => $this->students[0]->id,
1244                 'fullname' => fullname($this->students[0]))),
1245             $assign->get_course_module()->id,
1246             $this->editingteachers[0]->id
1247         );
1248         $this->assertEventLegacyLogData($expected, $event);
1249         $sink->close();
1251         // Revert to defaults.
1252         $this->editingteachers[0]->ignoresesskey = false;
1253     }
1255     public function test_submission_graded_event() {
1256         $this->setUser($this->editingteachers[0]);
1257         $assign = $this->create_instance();
1259         // Test apply_grade_to_user.
1260         $sink = $this->redirectEvents();
1262         $data = new stdClass();
1263         $data->grade = '50.0';
1264         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1265         $grade = $assign->get_user_grade($this->students[0]->id, false, 0);
1267         $events = $sink->get_events();
1268         $this->assertCount(1, $events);
1269         $event = reset($events);
1270         $this->assertInstanceOf('\mod_assign\event\submission_graded', $event);
1271         $this->assertEquals($assign->get_context(), $event->get_context());
1272         $this->assertEquals($grade->id, $event->objectid);
1273         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1274         $expected = array(
1275             $assign->get_course()->id,
1276             'assign',
1277             'grade submission',
1278             'view.php?id=' . $assign->get_course_module()->id,
1279             $assign->format_grade_for_log($grade),
1280             $assign->get_course_module()->id,
1281             $this->editingteachers[0]->id
1282         );
1283         $this->assertEventLegacyLogData($expected, $event);
1284         $sink->close();
1286         // Test process_save_quick_grades.
1287         $sink = $this->redirectEvents();
1289         $data = array(
1290             'grademodified_' . $this->students[0]->id => time(),
1291             'quickgrade_' . $this->students[0]->id => '60.0'
1292         );
1293         $assign->testable_process_save_quick_grades($data);
1294         $grade = $assign->get_user_grade($this->students[0]->id, false);
1295         $this->assertEquals('60.0', $grade->grade);
1297         $events = $sink->get_events();
1298         $this->assertCount(1, $events);
1299         $event = reset($events);
1300         $this->assertInstanceOf('\mod_assign\event\submission_graded', $event);
1301         $this->assertEquals($assign->get_context(), $event->get_context());
1302         $this->assertEquals($grade->id, $event->objectid);
1303         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1304         $expected = array(
1305             $assign->get_course()->id,
1306             'assign',
1307             'grade submission',
1308             'view.php?id=' . $assign->get_course_module()->id,
1309             $assign->format_grade_for_log($grade),
1310             $assign->get_course_module()->id,
1311             $this->editingteachers[0]->id
1312         );
1313         $this->assertEventLegacyLogData($expected, $event);
1314         $sink->close();
1315     }
1317     public function test_disable_submit_after_cutoff_date() {
1318         global $PAGE;
1320         $this->setUser($this->editingteachers[0]);
1321         $now = time();
1322         $tomorrow = $now + 24*60*60;
1323         $lastweek = $now - 7*24*60*60;
1324         $yesterday = $now - 24*60*60;
1326         $assign = $this->create_instance(array('duedate'=>$yesterday,
1327                                                'cutoffdate'=>$tomorrow,
1328                                                'assignsubmission_onlinetext_enabled'=>1));
1329         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1331         // Student should be able to see an add submission button.
1332         $this->setUser($this->students[0]);
1333         $output = $assign->view_student_summary($this->students[0], true);
1334         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1336         // Add a submission but don't submit now.
1337         $submission = $assign->get_user_submission($this->students[0]->id, true);
1338         $data = new stdClass();
1339         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1340                                          'text'=>'Submission text',
1341                                          'format'=>FORMAT_MOODLE);
1342         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1343         $plugin->save($submission, $data);
1345         // Create another instance with cut-off and due-date already passed.
1346         $this->setUser($this->editingteachers[0]);
1347         $now = time();
1348         $assign = $this->create_instance(array('duedate'=>$lastweek,
1349                                                'cutoffdate'=>$yesterday,
1350                                                'assignsubmission_onlinetext_enabled'=>1));
1352         $this->setUser($this->students[0]);
1353         $output = $assign->view_student_summary($this->students[0], true);
1354         $this->assertNotContains($output, get_string('editsubmission', 'assign'),
1355                                  'Should not be able to edit after cutoff date.');
1356         $this->assertNotContains($output, get_string('submitassignment', 'assign'),
1357                                  'Should not be able to submit after cutoff date.');
1358     }