2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Unit tests for (some of) mod/assign/locallib.php.
22 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') ||
die();
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/generator.php');
35 * Unit tests for (some of) mod/assign/locallib.php.
37 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 class mod_assign_locallib_testcase
extends advanced_testcase
{
42 // Use the generator helper.
43 use mod_assign_test_generator
;
45 public function test_return_links() {
48 $this->resetAfterTest();
49 $course = $this->getDataGenerator()->create_course();
51 $assign = $this->create_instance($course);
52 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
54 $assign->register_return_link('RETURNACTION', ['param' => 1]);
55 $this->assertEquals('RETURNACTION', $assign->get_return_action());
56 $this->assertEquals(['param' => 1], $assign->get_return_params());
59 public function test_get_feedback_plugins() {
60 $this->resetAfterTest();
61 $course = $this->getDataGenerator()->create_course();
62 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
64 $this->setUser($teacher);
65 $assign = $this->create_instance($course);
66 $installedplugins = array_keys(core_component
::get_plugin_list('assignfeedback'));
68 foreach ($assign->get_feedback_plugins() as $plugin) {
69 $this->assertContains($plugin->get_type(), $installedplugins, 'Feedback plugin not in list of installed plugins');
73 public function test_get_submission_plugins() {
74 $this->resetAfterTest();
75 $course = $this->getDataGenerator()->create_course();
76 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
78 $this->setUser($teacher);
79 $assign = $this->create_instance($course);
80 $installedplugins = array_keys(core_component
::get_plugin_list('assignsubmission'));
82 foreach ($assign->get_submission_plugins() as $plugin) {
83 $this->assertContains($plugin->get_type(), $installedplugins, 'Submission plugin not in list of installed plugins');
87 public function test_is_blind_marking() {
88 $this->resetAfterTest();
89 $course = $this->getDataGenerator()->create_course();
90 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
91 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
93 $this->setUser($teacher);
94 $assign = $this->create_instance($course, ['blindmarking' => 1]);
95 $this->assertEquals(true, $assign->is_blind_marking());
97 // Test cannot see student names.
98 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
99 $output = $assign->get_renderer()->render($gradingtable);
100 $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));
102 // Test students cannot reveal identities.
103 $nopermission = false;
104 $student->ignoresesskey
= true;
105 $this->setUser($student);
106 $this->expectException('required_capability_exception');
107 $assign->reveal_identities();
108 $student->ignoresesskey
= false;
110 // Test teachers cannot reveal identities.
111 $nopermission = false;
112 $teacher->ignoresesskey
= true;
113 $this->setUser($teacher);
114 $this->expectException('required_capability_exception');
115 $assign->reveal_identities();
116 $teacher->ignoresesskey
= false;
118 // Test sesskey is required.
119 $this->setUser($teacher);
120 $this->expectException('moodle_exception');
121 $assign->reveal_identities();
123 // Test editingteacher can reveal identities if sesskey is ignored.
124 $teacher->ignoresesskey
= true;
125 $this->setUser($teacher);
126 $assign->reveal_identities();
127 $this->assertEquals(false, $assign->is_blind_marking());
128 $teacher->ignoresesskey
= false;
130 // Test student names are visible.
131 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
132 $output = $assign->get_renderer()->render($gradingtable);
133 $this->assertEquals(false, strpos($output, get_string('hiddenuser', 'assign')));
135 // Set this back to default.
136 $teacher->ignoresesskey
= false;
140 * Data provider for test_get_assign_perpage
142 * @return array Provider data
144 public function get_assign_perpage_provider() {
148 'userprefs' => array(
157 'userprefs' => array(
170 * @dataProvider get_assign_perpage_provider
171 * @param integer $maxperpage site config value
172 * @param array $userprefs Array of user preferences and expected page sizes
174 public function test_get_assign_perpage($maxperpage, $userprefs) {
175 $this->resetAfterTest();
176 $course = $this->getDataGenerator()->create_course();
177 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
178 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
180 $this->setUser($teacher);
181 $assign = $this->create_instance($course);
183 set_config('maxperpage', $maxperpage, 'assign');
184 set_user_preference('assign_perpage', null);
185 $this->assertEquals(10, $assign->get_assign_perpage());
186 foreach ($userprefs as $pref => $perpage) {
187 set_user_preference('assign_perpage', $pref);
188 $this->assertEquals($perpage, $assign->get_assign_perpage());
193 * Test filter by requires grading.
195 * This is specifically checking an assignment with no grade to make sure we do not
196 * get an exception thrown when rendering the grading table for this type of assignment.
198 public function test_gradingtable_filter_by_requiresgrading_no_grade() {
201 $this->resetAfterTest();
203 $course = $this->getDataGenerator()->create_course();
204 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
205 $this->setUser($teacher);
206 $assign = $this->create_instance($course, [
207 'assignsubmission_onlinetext_enabled' => 1,
208 'assignfeedback_comments_enabled' => 0,
209 'grade' => GRADE_TYPE_NONE
212 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
213 'id' => $assign->get_course_module()->id
,
214 'action' => 'grading',
217 // Render the table with the requires grading filter.
218 $gradingtable = new assign_grading_table($assign, 1, ASSIGN_FILTER_REQUIRE_GRADING
, 0, true);
219 $output = $assign->get_renderer()->render($gradingtable);
221 // Test that the filter function does not throw errors for assignments with no grade.
222 $this->assertStringContainsString(get_string('nothingtodisplay'), $output);
227 * Test submissions with extension date.
229 public function test_gradingtable_extension_due_date() {
232 $this->resetAfterTest();
233 $course = $this->getDataGenerator()->create_course();
234 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
235 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
237 // Setup the assignment.
238 $this->setUser($teacher);
240 $assign = $this->create_instance($course, [
241 'assignsubmission_onlinetext_enabled' => 1,
242 'duedate' => time() - (4 * DAYSECS
),
244 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
245 'id' => $assign->get_course_module()->id
,
246 'action' => 'grading',
249 // Check that the assignment is late.
250 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
251 $output = $assign->get_renderer()->render($gradingtable);
252 $this->assertStringContainsString(get_string('submissionstatus_', 'assign'), $output);
253 $this->assertStringContainsString(get_string('overdue', 'assign', format_time((4 * DAYSECS
))), $output);
255 // Grant an extension.
256 $extendedtime = $time +
(2 * DAYSECS
);
257 $assign->testable_save_user_extension($student->id
, $extendedtime);
258 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
259 $output = $assign->get_renderer()->render($gradingtable);
260 $this->assertStringContainsString(get_string('submissionstatus_', 'assign'), $output);
261 $this->assertStringContainsString(get_string('userextensiondate', 'assign', userdate($extendedtime)), $output);
263 // Simulate a submission.
264 $this->setUser($student);
265 $submission = $assign->get_user_submission($student->id
, true);
266 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
267 $assign->testable_update_submission($submission, $student->id
, true, false);
268 $data = new stdClass();
269 $data->onlinetext_editor
= [
270 'itemid' => file_get_unused_draft_itemid(),
271 'text' => 'Submission text',
272 'format' => FORMAT_MOODLE
,
274 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
275 $plugin->save($submission, $data);
278 $this->setUser($teacher);
279 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
280 $output = $assign->get_renderer()->render($gradingtable);
281 $this->assertStringContainsString(get_string('submissionstatus_submitted', 'assign'), $output);
282 $this->assertStringContainsString(get_string('userextensiondate', 'assign', userdate($extendedtime)), $output);
286 * Test that late submissions with extension date calculate correctly.
288 public function test_gradingtable_extension_date_calculation_for_lateness() {
291 $this->resetAfterTest();
292 $course = $this->getDataGenerator()->create_course();
293 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
294 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
296 // Setup the assignment.
297 $this->setUser($teacher);
299 $assign = $this->create_instance($course, [
300 'assignsubmission_onlinetext_enabled' => 1,
301 'duedate' => time() - (4 * DAYSECS
),
303 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
304 'id' => $assign->get_course_module()->id
,
305 'action' => 'grading',
308 // Check that the assignment is late.
309 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
310 $output = $assign->get_renderer()->render($gradingtable);
311 $this->assertStringContainsString(get_string('submissionstatus_', 'assign'), $output);
312 $difftime = time() - $time;
313 $this->assertStringContainsString(get_string('overdue', 'assign', format_time((4 * DAYSECS
) +
$difftime)), $output);
315 // Grant an extension that is in the past.
316 $assign->testable_save_user_extension($student->id
, $time - (2 * DAYSECS
));
317 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
318 $output = $assign->get_renderer()->render($gradingtable);
319 $this->assertStringContainsString(get_string('submissionstatus_', 'assign'), $output);
320 $this->assertStringContainsString(get_string('userextensiondate', 'assign', userdate($time - (2 * DAYSECS
))), $output);
321 $difftime = time() - $time;
322 $this->assertStringContainsString(get_string('overdue', 'assign', format_time((2 * DAYSECS
) +
$difftime)), $output);
324 // Simulate a submission.
325 $this->setUser($student);
326 $submission = $assign->get_user_submission($student->id
, true);
327 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
328 $assign->testable_update_submission($submission, $student->id
, true, false);
329 $data = new stdClass();
330 $data->onlinetext_editor
= [
331 'itemid' => file_get_unused_draft_itemid(),
332 'text' => 'Submission text',
333 'format' => FORMAT_MOODLE
,
335 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
336 $plugin->save($submission, $data);
337 $submittedtime = time();
340 $this->setUser($teacher);
341 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
342 $output = $assign->get_renderer()->render($gradingtable);
343 $this->assertStringContainsString(get_string('submissionstatus_submitted', 'assign'), $output);
344 $this->assertStringContainsString(get_string('userextensiondate', 'assign', userdate($time - (2 * DAYSECS
))), $output);
346 $difftime = $submittedtime - $time;
347 $this->assertStringContainsString(get_string('submittedlateshort', 'assign', format_time((2 * DAYSECS
) +
$difftime)), $output);
350 public function test_gradingtable_status_rendering() {
353 $this->resetAfterTest();
354 $course = $this->getDataGenerator()->create_course();
355 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
356 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
358 // Setup the assignment.
359 $this->setUser($teacher);
361 $assign = $this->create_instance($course, [
362 'assignsubmission_onlinetext_enabled' => 1,
363 'duedate' => $time - (4 * DAYSECS
),
365 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
366 'id' => $assign->get_course_module()->id
,
367 'action' => 'grading',
370 // Check that the assignment is late.
371 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
372 $output = $assign->get_renderer()->render($gradingtable);
373 $this->assertStringContainsString(get_string('submissionstatus_', 'assign'), $output);
374 $difftime = time() - $time;
375 $this->assertStringContainsString(get_string('overdue', 'assign', format_time((4 * DAYSECS
) +
$difftime)), $output);
377 // Simulate a student viewing the assignment without submitting.
378 $this->setUser($student);
379 $submission = $assign->get_user_submission($student->id
, true);
380 $submission->status
= ASSIGN_SUBMISSION_STATUS_NEW
;
381 $assign->testable_update_submission($submission, $student->id
, true, false);
382 $submittedtime = time();
385 $this->setUser($teacher);
386 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
387 $output = $assign->get_renderer()->render($gradingtable);
388 $difftime = $submittedtime - $time;
389 $this->assertStringContainsString(get_string('overdue', 'assign', format_time((4 * DAYSECS
) +
$difftime)), $output);
391 $document = new DOMDocument();
392 @$document->loadHTML($output);
393 $xpath = new DOMXPath($document);
394 $this->assertEmpty($xpath->evaluate('string(//td[@id="mod_assign_grading-' . $assign->get_context()->id
. '_r0_c8"])'));
398 * Check that group submission information is rendered correctly in the
401 public function test_gradingtable_group_submissions_rendering() {
404 $this->resetAfterTest();
405 $course = $this->getDataGenerator()->create_course();
406 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
408 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
409 groups_add_member($group, $teacher);
413 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
414 $students[] = $student;
415 groups_add_member($group, $student);
417 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
418 $students[] = $student;
419 groups_add_member($group, $student);
421 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
422 $students[] = $student;
423 groups_add_member($group, $student);
425 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
426 $students[] = $student;
427 groups_add_member($group, $student);
429 // Verify group assignments.
430 $this->setUser($teacher);
431 $assign = $this->create_instance($course, [
432 'teamsubmission' => 1,
433 'assignsubmission_onlinetext_enabled' => 1,
434 'submissiondrafts' => 1,
435 'requireallteammemberssubmit' => 0,
437 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
438 'id' => $assign->get_course_module()->id
,
439 'action' => 'grading',
443 $this->setUser($student);
444 $data = new stdClass();
445 $data->onlinetext_editor
= [
446 'itemid' => file_get_unused_draft_itemid(),
447 'text' => 'Submission text',
448 'format' => FORMAT_MOODLE
,
451 $assign->save_submission($data, $notices);
453 $submission = $assign->get_group_submission($student->id
, 0, true);
454 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
455 $assign->testable_update_submission($submission, $student->id
, true, true);
458 $this->setUser($teacher);
459 $gradingtable = new assign_grading_table($assign, 4, '', 0, true);
460 $output = $assign->get_renderer()->render($gradingtable);
461 $document = new DOMDocument();
462 @$document->loadHTML($output);
463 $xpath = new DOMXPath($document);
465 // The XPath expression is based on the unique ID of the table.
466 $xpathuniqueidroot = 'mod_assign_grading-' . $assign->get_context()->id
;
469 $this->assertSame(get_string('submissionstatus_submitted', 'assign'),
470 $xpath->evaluate('string(//td[@id="' . $xpathuniqueidroot . '_r0_c4"]/div[@class="submissionstatussubmitted"])'));
471 $this->assertSame(get_string('submissionstatus_submitted', 'assign'),
472 $xpath->evaluate('string(//td[@id="' . $xpathuniqueidroot . '_r3_c4"]/div[@class="submissionstatussubmitted"])'));
474 // Check submission last modified date
475 $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="' . $xpathuniqueidroot . '_r0_c8"])')));
476 $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="' . $xpathuniqueidroot . '_r3_c8"])')));
479 $this->assertSame($group->name
, $xpath->evaluate('string(//td[@id="' . $xpathuniqueidroot . '_r0_c5"])'));
480 $this->assertSame($group->name
, $xpath->evaluate('string(//td[@id="' . $xpathuniqueidroot . '_r3_c5"])'));
482 // Check submission text.
483 $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="' . $xpathuniqueidroot . '_r0_c9"]/div/div)'));
484 $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="' . $xpathuniqueidroot . '_r3_c9"]/div/div)'));
486 // Check comments can be made.
487 $this->assertEquals(1, $xpath->evaluate('count(//td[@id="' . $xpathuniqueidroot . '_r0_c10"]//textarea)'));
488 $this->assertEquals(1, $xpath->evaluate('count(//td[@id="' . $xpathuniqueidroot . '_r3_c10"]//textarea)'));
491 public function test_show_intro() {
492 $this->resetAfterTest();
493 $course = $this->getDataGenerator()->create_course();
494 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
496 // Test whether we are showing the intro at the correct times.
497 $this->setUser($teacher);
498 $assign = $this->create_instance($course, ['alwaysshowdescription' => 1]);
500 $this->assertEquals(true, $assign->testable_show_intro());
502 $tomorrow = time() + DAYSECS
;
504 $assign = $this->create_instance($course, [
505 'alwaysshowdescription' => 0,
506 'allowsubmissionsfromdate' => $tomorrow,
508 $this->assertEquals(false, $assign->testable_show_intro());
509 $yesterday = time() - DAYSECS
;
510 $assign = $this->create_instance($course, [
511 'alwaysshowdescription' => 0,
512 'allowsubmissionsfromdate' => $yesterday,
514 $this->assertEquals(true, $assign->testable_show_intro());
517 public function test_has_submissions_or_grades() {
518 $this->resetAfterTest();
519 $course = $this->getDataGenerator()->create_course();
520 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
521 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
523 $this->setUser($teacher);
524 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
525 $instance = $assign->get_instance();
527 // Should start empty.
528 $this->assertEquals(false, $assign->has_submissions_or_grades());
530 // Simulate a submission.
531 $this->setUser($student);
532 $submission = $assign->get_user_submission($student->id
, true);
534 // The submission is still new.
535 $this->assertEquals(false, $assign->has_submissions_or_grades());
537 // Submit the submission.
538 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
539 $assign->testable_update_submission($submission, $student->id
, true, false);
540 $data = new stdClass();
541 $data->onlinetext_editor
= array('itemid'=>file_get_unused_draft_itemid(),
542 'text'=>'Submission text',
543 'format'=>FORMAT_MOODLE
);
544 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
545 $plugin->save($submission, $data);
548 $this->assertEquals(true, $assign->has_submissions_or_grades());
551 public function test_delete_grades() {
552 $this->resetAfterTest();
553 $course = $this->getDataGenerator()->create_course();
554 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
555 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
557 $this->setUser($teacher);
558 $assign = $this->create_instance($course);
560 // Simulate adding a grade.
561 $this->setUser($teacher);
562 $data = new stdClass();
563 $data->grade
= '50.0';
564 $assign->testable_apply_grade_to_user($data, $student->id
, 0);
566 // Now see if the data is in the gradebook.
567 $gradinginfo = grade_get_grades($course->id
, 'mod', 'assign', $assign->get_instance()->id
);
569 $this->assertNotEquals(0, count($gradinginfo->items
));
571 $assign->testable_delete_grades();
572 $gradinginfo = grade_get_grades($course->id
, 'mod', 'assign', $assign->get_instance()->id
);
574 $this->assertEquals(0, count($gradinginfo->items
));
577 public function test_delete_instance() {
578 $this->resetAfterTest();
579 $course = $this->getDataGenerator()->create_course();
580 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
581 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
583 $this->setUser($teacher);
584 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
586 // Simulate adding a grade.
587 $this->setUser($teacher);
588 $data = new stdClass();
589 $data->grade
= '50.0';
590 $assign->testable_apply_grade_to_user($data, $student->id
, 0);
592 // Simulate a submission.
593 $this->add_submission($student, $assign);
595 // Now try and delete.
596 $this->setUser($teacher);
597 $this->assertEquals(true, $assign->delete_instance());
600 public function test_reset_userdata() {
603 $this->resetAfterTest();
604 $course = $this->getDataGenerator()->create_course();
605 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
606 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
609 $this->setUser($teacher);
610 $assign = $this->create_instance($course, [
611 'assignsubmission_onlinetext_enabled' => 1,
615 // Simulate adding a grade.
616 $this->add_submission($student, $assign);
617 $this->submit_for_grading($student, $assign);
618 $this->mark_submission($teacher, $assign, $student, 50.0);
620 // Simulate a submission.
621 $this->setUser($student);
622 $submission = $assign->get_user_submission($student->id
, true);
623 $data = new stdClass();
624 $data->onlinetext_editor
= array('itemid'=>file_get_unused_draft_itemid(),
625 'text'=>'Submission text',
626 'format'=>FORMAT_MOODLE
);
627 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
628 $plugin->save($submission, $data);
630 $this->assertEquals(true, $assign->has_submissions_or_grades());
631 // Now try and reset.
632 $data = new stdClass();
633 $data->reset_assign_submissions
= 1;
634 $data->reset_gradebook_grades
= 1;
635 $data->reset_assign_user_overrides
= 1;
636 $data->reset_assign_group_overrides
= 1;
637 $data->courseid
= $course->id
;
638 $data->timeshift
= DAYSECS
;
639 $this->setUser($teacher);
640 $assign->reset_userdata($data);
641 $this->assertEquals(false, $assign->has_submissions_or_grades());
643 // Reload the instance data.
644 $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id
));
645 $this->assertEquals($now + DAYSECS
, $instance->duedate
);
647 // Test reset using assign_reset_userdata().
648 $assignduedate = $instance->duedate
; // Keep old updated value for comparison.
649 $data->timeshift
= (2 * DAYSECS
);
650 assign_reset_userdata($data);
651 $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id
));
652 $this->assertEquals($assignduedate +
(2 * DAYSECS
), $instance->duedate
);
654 // Create one more assignment and reset, make sure time shifted for previous assignment is not changed.
655 $assign2 = $this->create_instance($course, [
656 'assignsubmission_onlinetext_enabled' => 1,
659 $assignduedate = $instance->duedate
;
660 $data->timeshift
= 3*DAYSECS
;
661 $assign2->reset_userdata($data);
662 $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id
));
663 $this->assertEquals($assignduedate, $instance->duedate
);
664 $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id
));
665 $this->assertEquals($now +
3*DAYSECS
, $instance2->duedate
);
667 // Reset both assignments using assign_reset_userdata() and make sure both assignments have same date.
668 $assignduedate = $instance->duedate
;
669 $assign2duedate = $instance2->duedate
;
670 $data->timeshift
= (4 * DAYSECS
);
671 assign_reset_userdata($data);
672 $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id
));
673 $this->assertEquals($assignduedate +
(4 * DAYSECS
), $instance->duedate
);
674 $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id
));
675 $this->assertEquals($assign2duedate +
(4 * DAYSECS
), $instance2->duedate
);
678 public function test_plugin_settings() {
681 $this->resetAfterTest();
683 $course = $this->getDataGenerator()->create_course();
684 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
687 $this->setUser($teacher);
688 $assign = $this->create_instance($course, [
689 'assignsubmission_file_enabled' => 1,
690 'assignsubmission_file_maxfiles' => 12,
691 'assignsubmission_file_maxsizebytes' => 10,
694 $plugin = $assign->get_submission_plugin_by_type('file');
695 $this->assertEquals('12', $plugin->get_config('maxfilesubmissions'));
698 public function test_update_calendar() {
701 $this->resetAfterTest();
703 $course = $this->getDataGenerator()->create_course();
704 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
706 $this->setUser($teacher);
707 $userctx = context_user
::instance($teacher->id
)->id
;
709 // Hack to pretend that there was an editor involved. We need both $_POST and $_REQUEST, and a sesskey.
710 $draftid = file_get_unused_draft_itemid();
711 $_REQUEST['introeditor'] = $draftid;
712 $_POST['introeditor'] = $draftid;
713 $_POST['sesskey'] = sesskey();
715 // Write links to a draft area.
716 $fakearealink1 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">link</a>', 'draftfile.php', $userctx,
717 'user', 'draft', $draftid);
718 $fakearealink2 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">new</a>', 'draftfile.php', $userctx,
719 'user', 'draft', $draftid);
721 // Create a new assignment with links to a draft area.
723 $assign = $this->create_instance($course, [
725 'intro' => $fakearealink1,
726 'introformat' => FORMAT_HTML
729 // See if there is an event in the calendar.
730 $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id
);
731 $event = $DB->get_record('event', $params);
732 $this->assertNotEmpty($event);
733 $this->assertSame('link', $event->description
); // The pluginfile links are removed.
735 // Make sure the same works when updating the assignment.
736 $instance = $assign->get_instance();
737 $instance->instance
= $instance->id
;
738 $instance->intro
= $fakearealink2;
739 $instance->introformat
= FORMAT_HTML
;
740 $assign->update_instance($instance);
741 $params = array('modulename' => 'assign', 'instance' => $assign->get_instance()->id
);
742 $event = $DB->get_record('event', $params);
743 $this->assertNotEmpty($event);
744 $this->assertSame('new', $event->description
); // The pluginfile links are removed.
746 // Create an assignment with a description that should be hidden.
747 $assign = $this->create_instance($course, [
748 'duedate' => $now +
160,
749 'alwaysshowdescription' => false,
750 'allowsubmissionsfromdate' => $now +
60,
751 'intro' => 'Some text',
754 // Get the event from the calendar.
755 $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id
);
756 $event = $DB->get_record('event', [
757 'modulename' => 'assign',
758 'instance' => $assign->get_instance()->id
,
761 $this->assertEmpty($event->description
);
763 // Change the allowsubmissionfromdate to the past - do this directly in the DB
764 // because if we call the assignment update method - it will update the calendar
765 // and we want to test that this works from cron.
766 $DB->set_field('assign', 'allowsubmissionsfromdate', $now - 60, array('id'=>$assign->get_instance()->id
));
767 // Run cron to update the event in the calendar.
769 $event = $DB->get_record('event', $params);
771 $this->assertStringContainsString('Some text', $event->description
);
775 public function test_update_instance() {
778 $this->resetAfterTest();
780 $course = $this->getDataGenerator()->create_course();
781 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
783 $this->setUser($teacher);
784 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
787 $instance = $assign->get_instance();
788 $instance->duedate
= $now;
789 $instance->instance
= $instance->id
;
790 $instance->assignsubmission_onlinetext_enabled
= 1;
792 $assign->update_instance($instance);
794 $instance = $DB->get_record('assign', ['id' => $assign->get_instance()->id
]);
795 $this->assertEquals($now, $instance->duedate
);
798 public function test_cannot_submit_empty() {
801 $this->resetAfterTest();
803 $course = $this->getDataGenerator()->create_course();
804 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
806 $assign = $this->create_instance($course, ['submissiondrafts' => 1]);
808 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
810 // Test you cannot see the submit button for an offline assignment regardless.
811 $this->setUser($student);
812 $output = $assign->view_student_summary($student, true);
813 $this->assertStringNotContainsString(get_string('submitassignment', 'assign'), $output, 'Can submit empty offline assignment');
816 public function test_cannot_submit_empty_no_submission() {
819 $this->resetAfterTest();
821 $course = $this->getDataGenerator()->create_course();
822 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
824 $assign = $this->create_instance($course, [
825 'submissiondrafts' => 1,
826 'assignsubmission_onlinetext_enabled' => 1,
829 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
831 // Test you cannot see the submit button for an online text assignment with no submission.
832 $this->setUser($student);
833 $output = $assign->view_student_summary($student, true);
834 $this->assertStringNotContainsString(get_string('submitassignment', 'assign'), $output, 'Cannot submit empty onlinetext assignment');
837 public function test_can_submit_with_submission() {
840 $this->resetAfterTest();
842 $course = $this->getDataGenerator()->create_course();
843 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
845 $assign = $this->create_instance($course, [
846 'submissiondrafts' => 1,
847 'assignsubmission_onlinetext_enabled' => 1,
850 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
853 $this->add_submission($student, $assign);
855 // Test you can see the submit button for an online text assignment with a submission.
856 $this->setUser($student);
857 $output = $assign->view_student_summary($student, true);
858 $this->assertStringContainsString(get_string('submitassignment', 'assign'), $output, 'Can submit non empty onlinetext assignment');
862 * Test new_submission_empty
864 * We only test combinations of plugins here. Individual plugins are tested
865 * in their respective test files.
867 * @dataProvider test_new_submission_empty_testcases
868 * @param string $data The file submission data
869 * @param bool $expected The expected return value
871 public function test_new_submission_empty($data, $expected) {
872 $this->resetAfterTest();
874 $course = $this->getDataGenerator()->create_course();
875 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
877 $assign = $this->create_instance($course, [
878 'assignsubmission_file_enabled' => 1,
879 'assignsubmission_file_maxfiles' => 12,
880 'assignsubmission_file_maxsizebytes' => 10,
881 'assignsubmission_onlinetext_enabled' => 1,
883 $this->setUser($student);
884 $submission = new stdClass();
886 if ($data['file'] && isset($data['file']['filename'])) {
887 $itemid = file_get_unused_draft_itemid();
888 $submission->files_filemanager
= $itemid;
889 $data['file'] +
= ['contextid' => context_user
::instance($student->id
)->id
, 'itemid' => $itemid];
890 $fs = get_file_storage();
891 $fs->create_file_from_string((object)$data['file'], 'Content of ' . $data['file']['filename']);
894 if ($data['onlinetext']) {
895 $submission->onlinetext_editor
= ['text' => $data['onlinetext']];
898 $result = $assign->new_submission_empty($submission);
899 $this->assertTrue($result === $expected);
903 * Dataprovider for the test_new_submission_empty testcase
905 * @return array of testcases
907 public function test_new_submission_empty_testcases() {
909 'With file and onlinetext' => [
912 'component' => 'user',
913 'filearea' => 'draft',
915 'filename' => 'not_a_virus.exe'
917 'onlinetext' => 'Balin Fundinul Uzbadkhazaddumu'
924 public function test_list_participants() {
927 $this->resetAfterTest();
929 $course = $this->getDataGenerator()->create_course();
930 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
932 // Create 10 students.
933 for ($i = 0; $i < 10; $i++
) {
934 $this->getDataGenerator()->create_and_enrol($course, 'student');
937 $this->setUser($teacher);
938 $assign = $this->create_instance($course, ['grade' => 100]);
940 $this->assertCount(10, $assign->list_participants(null, true));
943 public function test_list_participants_activeenrol() {
946 $this->resetAfterTest();
948 $course = $this->getDataGenerator()->create_course();
949 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
951 // Create 10 students.
952 for ($i = 0; $i < 10; $i++
) {
953 $this->getDataGenerator()->create_and_enrol($course, 'student');
956 // Create 10 suspended students.
957 for ($i = 0; $i < 10; $i++
) {
958 $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', 0, 0, ENROL_USER_SUSPENDED
);
961 $this->setUser($teacher);
962 set_user_preference('grade_report_showonlyactiveenrol', false);
963 $assign = $this->create_instance($course, ['grade' => 100]);
965 $this->assertCount(10, $assign->list_participants(null, true));
968 public function test_list_participants_with_group_restriction() {
971 $this->resetAfterTest();
973 $course = $this->getDataGenerator()->create_course();
974 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
975 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
976 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
977 $unrelatedstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
979 // Turn on availability and a group restriction, and check that it doesn't show users who aren't in the group.
980 $CFG->enableavailability
= true;
982 $specialgroup = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
983 $assign = $this->create_instance($course, [
985 'availability' => json_encode(
986 \core_availability\tree
::get_root_json([\availability_group\condition
::get_json($specialgroup->id
)])
990 groups_add_member($specialgroup, $student);
991 groups_add_member($specialgroup, $otherstudent);
992 $this->assertEquals(2, count($assign->list_participants(null, true)));
995 public function test_get_participant_user_not_exist() {
996 $this->resetAfterTest();
997 $course = $this->getDataGenerator()->create_course();
999 $assign = $this->create_instance($course);
1000 $this->assertNull($assign->get_participant('-1'));
1003 public function test_get_participant_not_enrolled() {
1004 $this->resetAfterTest();
1005 $course = $this->getDataGenerator()->create_course();
1006 $assign = $this->create_instance($course);
1008 $user = $this->getDataGenerator()->create_user();
1009 $this->assertNull($assign->get_participant($user->id
));
1012 public function test_get_participant_no_submission() {
1013 $this->resetAfterTest();
1014 $course = $this->getDataGenerator()->create_course();
1015 $assign = $this->create_instance($course);
1016 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1018 $participant = $assign->get_participant($student->id
);
1020 $this->assertEquals($student->id
, $participant->id
);
1021 $this->assertFalse($participant->submitted
);
1022 $this->assertFalse($participant->requiregrading
);
1023 $this->assertFalse($participant->grantedextension
);
1026 public function test_get_participant_granted_extension() {
1027 $this->resetAfterTest();
1028 $course = $this->getDataGenerator()->create_course();
1029 $assign = $this->create_instance($course);
1030 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1031 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1033 $this->setUser($teacher);
1034 $assign->save_user_extension($student->id
, time());
1035 $participant = $assign->get_participant($student->id
);
1037 $this->assertEquals($student->id
, $participant->id
);
1038 $this->assertFalse($participant->submitted
);
1039 $this->assertFalse($participant->requiregrading
);
1040 $this->assertTrue($participant->grantedextension
);
1043 public function test_get_participant_with_ungraded_submission() {
1044 $this->resetAfterTest();
1045 $course = $this->getDataGenerator()->create_course();
1046 $assign = $this->create_instance($course);
1047 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1048 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1050 // Simulate a submission.
1051 $this->add_submission($student, $assign);
1052 $this->submit_for_grading($student, $assign);
1054 $participant = $assign->get_participant($student->id
);
1056 $this->assertEquals($student->id
, $participant->id
);
1057 $this->assertTrue($participant->submitted
);
1058 $this->assertTrue($participant->requiregrading
);
1059 $this->assertFalse($participant->grantedextension
);
1062 public function test_get_participant_with_graded_submission() {
1063 $this->resetAfterTest();
1064 $course = $this->getDataGenerator()->create_course();
1065 $assign = $this->create_instance($course);
1066 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1067 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1069 // Simulate a submission.
1070 $this->add_submission($student, $assign);
1071 $this->submit_for_grading($student, $assign);
1073 $this->mark_submission($teacher, $assign, $student, 50.0);
1075 $data = new stdClass();
1076 $data->grade
= '50.0';
1077 $assign->testable_apply_grade_to_user($data, $student->id
, 0);
1079 $participant = $assign->get_participant($student->id
);
1081 $this->assertEquals($student->id
, $participant->id
);
1082 $this->assertTrue($participant->submitted
);
1083 $this->assertFalse($participant->requiregrading
);
1084 $this->assertFalse($participant->grantedextension
);
1088 * No active group and non-group submissions disallowed => 2 groups.
1090 public function test_count_teams_no_active_non_group_allowed() {
1091 $this->resetAfterTest();
1093 $course = $this->getDataGenerator()->create_course();
1094 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1095 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1096 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1098 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
1099 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1100 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1101 groups_add_member($group1, $student1);
1102 groups_add_member($group2, $student2);
1104 $this->setUser($teacher);
1105 $assign = $this->create_instance($course, ['teamsubmission' => 1]);
1107 $this->assertEquals(2, $assign->count_teams());
1111 * No active group and non group submissions allowed => 2 groups + the default one.
1113 public function test_count_teams_non_group_allowed() {
1114 $this->resetAfterTest();
1116 $course = $this->getDataGenerator()->create_course();
1117 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1118 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1119 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1120 $student3 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1122 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
1123 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1125 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group1->id
, 'groupingid' => $grouping->id
));
1126 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1127 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group2->id
, 'groupingid' => $grouping->id
));
1129 groups_add_member($group1, $student1);
1130 groups_add_member($group2, $student2);
1132 $assign = $this->create_instance($course, [
1133 'teamsubmission' => 1,
1134 'teamsubmissiongroupingid' => $grouping->id
,
1135 'preventsubmissionnotingroup' => false,
1138 $this->setUser($teacher);
1139 $this->assertEquals(3, $assign->count_teams());
1141 // Active group only.
1142 $this->assertEquals(1, $assign->count_teams($group1->id
));
1143 $this->assertEquals(1, $assign->count_teams($group2->id
));
1147 * Active group => just selected one.
1149 public function test_count_teams_no_active_group() {
1150 $this->resetAfterTest();
1152 $course = $this->getDataGenerator()->create_course();
1153 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1154 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1155 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1156 $student3 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1158 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
1159 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1161 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group1->id
, 'groupingid' => $grouping->id
));
1162 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1163 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group2->id
, 'groupingid' => $grouping->id
));
1165 groups_add_member($group1, $student1);
1166 groups_add_member($group2, $student2);
1168 $assign = $this->create_instance($course, [
1169 'teamsubmission' => 1,
1170 'preventsubmissionnotingroup' => true,
1173 $this->setUser($teacher);
1174 $this->assertEquals(2, $assign->count_teams());
1176 // Active group only.
1177 $this->assertEquals(1, $assign->count_teams($group1->id
));
1178 $this->assertEquals(1, $assign->count_teams($group2->id
));
1182 * Active group => just selected one.
1184 public function test_count_teams_groups_only() {
1185 $this->resetAfterTest();
1187 $course = $this->getDataGenerator()->create_course();
1188 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
1190 $assign = $this->create_instance($course, [
1191 'teamsubmission' => 1,
1192 'teamsubmissiongroupingid' => $grouping->id
,
1193 'preventsubmissionnotingroup' => false,
1195 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1197 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1198 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1199 groups_add_member($group1, $student1);
1201 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1202 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1203 groups_add_member($group2, $student2);
1205 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group1->id
, 'groupingid' => $grouping->id
));
1206 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group2->id
, 'groupingid' => $grouping->id
));
1208 $this->setUser($teacher);
1210 $assign = $this->create_instance($course, [
1211 'teamsubmission' => 1,
1212 'preventsubmissionnotingroup' => true,
1214 $this->assertEquals(2, $assign->count_teams());
1217 public function test_submit_to_default_group() {
1218 global $DB, $SESSION;
1220 $this->resetAfterTest();
1222 $course = $this->getDataGenerator()->create_course();
1223 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1224 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1226 $grouping = $this->getDataGenerator()->create_grouping(['courseid' => $course->id
]);
1227 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1229 $assign = $this->create_instance($course, [
1230 'teamsubmission' => 1,
1231 'assignsubmission_onlinetext_enabled' => 1,
1232 'submissiondrafts' => 0,
1233 'groupmode' => VISIBLEGROUPS
,
1236 $usergroup = $assign->get_submission_group($student->id
);
1237 $this->assertFalse($usergroup, 'New student is in default group');
1239 // Add a submission.
1240 $this->add_submission($student, $assign);
1241 $this->submit_for_grading($student, $assign);
1243 // Set active groups to all groups.
1244 $this->setUser($teacher);
1245 $SESSION->activegroup
[$course->id
]['aag'][0] = 0;
1246 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED
));
1248 // Set an active group.
1249 $SESSION->activegroup
[$course->id
]['aag'][0] = (int) $group->id
;
1250 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED
));
1253 public function test_count_submissions_no_draft() {
1254 $this->resetAfterTest();
1256 $course = $this->getDataGenerator()->create_course();
1257 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1258 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1260 $assign = $this->create_instance($course, [
1261 'assignsubmission_onlinetext_enabled' => 1,
1264 $assign->get_user_submission($student->id
, true);
1266 // Note: Drafts count as a submission.
1267 $this->assertEquals(0, $assign->count_grades());
1268 $this->assertEquals(0, $assign->count_submissions());
1269 $this->assertEquals(1, $assign->count_submissions(true));
1270 $this->assertEquals(0, $assign->count_submissions_need_grading());
1271 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_NEW
));
1272 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT
));
1273 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED
));
1274 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_REOPENED
));
1277 public function test_count_submissions_draft() {
1278 $this->resetAfterTest();
1280 $course = $this->getDataGenerator()->create_course();
1281 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1282 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1284 $assign = $this->create_instance($course, [
1285 'assignsubmission_onlinetext_enabled' => 1,
1288 $this->add_submission($student, $assign);
1290 // Note: Drafts count as a submission.
1291 $this->assertEquals(0, $assign->count_grades());
1292 $this->assertEquals(1, $assign->count_submissions());
1293 $this->assertEquals(1, $assign->count_submissions(true));
1294 $this->assertEquals(0, $assign->count_submissions_need_grading());
1295 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_NEW
));
1296 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT
));
1297 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED
));
1298 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_REOPENED
));
1301 public function test_count_submissions_submitted() {
1304 $this->resetAfterTest();
1306 $course = $this->getDataGenerator()->create_course();
1307 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1308 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1310 $assign = $this->create_instance($course, [
1311 'assignsubmission_onlinetext_enabled' => 1,
1314 $this->add_submission($student, $assign);
1315 $this->submit_for_grading($student, $assign);
1317 $this->assertEquals(0, $assign->count_grades());
1318 $this->assertEquals(1, $assign->count_submissions());
1319 $this->assertEquals(1, $assign->count_submissions(true));
1320 $this->assertEquals(1, $assign->count_submissions_need_grading());
1321 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_NEW
));
1322 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT
));
1323 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED
));
1324 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_REOPENED
));
1327 public function test_count_submissions_graded() {
1328 $this->resetAfterTest();
1330 $course = $this->getDataGenerator()->create_course();
1331 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1332 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1334 $assign = $this->create_instance($course, [
1335 'assignsubmission_onlinetext_enabled' => 1,
1338 $this->add_submission($student, $assign);
1339 $this->submit_for_grading($student, $assign);
1340 $this->mark_submission($teacher, $assign, $student, 50.0);
1342 // Although it has been graded, it is still marked as submitted.
1343 $this->assertEquals(1, $assign->count_grades());
1344 $this->assertEquals(1, $assign->count_submissions());
1345 $this->assertEquals(1, $assign->count_submissions(true));
1346 $this->assertEquals(0, $assign->count_submissions_need_grading());
1347 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_NEW
));
1348 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT
));
1349 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED
));
1350 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_REOPENED
));
1353 public function test_count_submissions_graded_group() {
1356 $this->resetAfterTest();
1358 $course = $this->getDataGenerator()->create_course();
1359 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1360 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1361 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1362 $othergroup = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1363 groups_add_member($group, $student);
1365 $assign = $this->create_instance($course, [
1366 'assignsubmission_onlinetext_enabled' => 1,
1367 'groupmode' => VISIBLEGROUPS
,
1370 $this->add_submission($student, $assign);
1371 $this->submit_for_grading($student, $assign);
1373 // The user should still be listed when fetching all groups.
1374 $this->setUser($teacher);
1375 $SESSION->activegroup
[$course->id
]['aag'][0] = 0;
1376 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED
));
1378 // The user should still be listed when fetching just their group.
1379 $SESSION->activegroup
[$course->id
]['aag'][0] = $group->id
;
1380 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED
));
1382 // The user should still be listed when fetching just their group.
1383 $SESSION->activegroup
[$course->id
]['aag'][0] = $othergroup->id
;
1384 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED
));
1388 public function x_test_count_submissions_for_team() {
1389 $this->resetAfterTest();
1391 $course = $this->getDataGenerator()->create_course();
1392 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1393 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1394 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1395 $othergroup = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1396 groups_add_member($group, $student);
1398 $assign = $this->create_instance($course, [
1399 'assignsubmission_onlinetext_enabled' => 1,
1400 'teamsubmission' => 1,
1403 // Add a graded submission.
1404 $this->add_submission($student, $assign);
1408 // Simulate adding a grade.
1409 $this->setUser($teacher);
1410 $data = new stdClass();
1411 $data->grade
= '50.0';
1412 $assign->testable_apply_grade_to_user($data, $this->extrastudents
[0]->id
, 0);
1414 // Simulate a submission.
1415 $this->setUser($this->extrastudents
[1]);
1416 $submission = $assign->get_group_submission($this->extrastudents
[1]->id
, $groupid, true);
1417 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
1418 $assign->testable_update_submission($submission, $this->extrastudents
[1]->id
, true, false);
1419 $data = new stdClass();
1420 $data->onlinetext_editor
= array('itemid' => file_get_unused_draft_itemid(),
1421 'text' => 'Submission text',
1422 'format' => FORMAT_MOODLE
);
1423 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1424 $plugin->save($submission, $data);
1426 // Simulate a submission.
1427 $this->setUser($this->extrastudents
[2]);
1428 $submission = $assign->get_group_submission($this->extrastudents
[2]->id
, $groupid, true);
1429 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
1430 $assign->testable_update_submission($submission, $this->extrastudents
[2]->id
, true, false);
1431 $data = new stdClass();
1432 $data->onlinetext_editor
= array('itemid' => file_get_unused_draft_itemid(),
1433 'text' => 'Submission text',
1434 'format' => FORMAT_MOODLE
);
1435 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1436 $plugin->save($submission, $data);
1438 // Simulate a submission.
1439 $this->setUser($this->extrastudents
[3]);
1440 $submission = $assign->get_group_submission($this->extrastudents
[3]->id
, $groupid, true);
1441 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
1442 $assign->testable_update_submission($submission, $this->extrastudents
[3]->id
, true, false);
1443 $data = new stdClass();
1444 $data->onlinetext_editor
= array('itemid' => file_get_unused_draft_itemid(),
1445 'text' => 'Submission text',
1446 'format' => FORMAT_MOODLE
);
1447 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1448 $plugin->save($submission, $data);
1450 // Simulate adding a grade.
1451 $this->setUser($teacher);
1452 $data = new stdClass();
1453 $data->grade
= '50.0';
1454 $assign->testable_apply_grade_to_user($data, $this->extrastudents
[3]->id
, 0);
1455 $assign->testable_apply_grade_to_user($data, $this->extrasuspendedstudents
[0]->id
, 0);
1457 // Create a new submission with status NEW.
1458 $this->setUser($this->extrastudents
[4]);
1459 $submission = $assign->get_group_submission($this->extrastudents
[4]->id
, $groupid, true);
1461 $this->assertEquals(2, $assign->count_grades());
1462 $this->assertEquals(4, $assign->count_submissions());
1463 $this->assertEquals(5, $assign->count_submissions(true));
1464 $this->assertEquals(3, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED
));
1465 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT
));
1468 public function test_get_grading_userid_list_only_active() {
1469 $this->resetAfterTest();
1471 $course = $this->getDataGenerator()->create_course();
1472 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1473 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1474 $suspendedstudent = $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', 0, 0, ENROL_USER_SUSPENDED
);
1476 $this->setUser($teacher);
1478 $assign = $this->create_instance($course);
1479 $this->assertCount(1, $assign->testable_get_grading_userid_list());
1482 public function test_get_grading_userid_list_all() {
1483 $this->resetAfterTest();
1485 $course = $this->getDataGenerator()->create_course();
1486 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1487 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1488 $suspendedstudent = $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', 0, 0, ENROL_USER_SUSPENDED
);
1490 $this->setUser($teacher);
1491 set_user_preference('grade_report_showonlyactiveenrol', false);
1493 $assign = $this->create_instance($course);
1494 $this->assertCount(2, $assign->testable_get_grading_userid_list());
1497 public function test_cron() {
1499 $this->resetAfterTest();
1501 // First run cron so there are no messages waiting to be sent (from other tests).
1505 $course = $this->getDataGenerator()->create_course();
1506 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1507 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1509 // Now create an assignment and add some feedback.
1510 $this->setUser($teacher);
1511 $assign = $this->create_instance($course, [
1512 'sendstudentnotifications' => 1,
1515 $this->add_submission($student, $assign);
1516 $this->submit_for_grading($student, $assign);
1517 $this->mark_submission($teacher, $assign, $student, 50.0);
1519 $this->expectOutputRegex('/Done processing 1 assignment submissions/');
1521 $sink = $this->redirectMessages();
1523 $messages = $sink->get_messages();
1525 $this->assertEquals(1, count($messages));
1526 $this->assertEquals(1, $messages[0]->notification
);
1527 $this->assertEquals($assign->get_instance()->name
, $messages[0]->contexturlname
);
1529 $customdata = json_decode($messages[0]->customdata
);
1530 $this->assertEquals($assign->get_course_module()->id
, $customdata->cmid
);
1531 $this->assertEquals($assign->get_instance()->id
, $customdata->instance
);
1532 $this->assertEquals('feedbackavailable', $customdata->messagetype
);
1533 $userpicture = new user_picture($teacher);
1534 $userpicture->size
= 1; // Use f1 size.
1535 $this->assertEquals($userpicture->get_url($PAGE)->out(false), $customdata->notificationiconurl
);
1536 $this->assertEquals(0, $customdata->uniqueidforuser
); // Not used in this case.
1537 $this->assertFalse($customdata->blindmarking
);
1540 public function test_cron_without_notifications() {
1541 $this->resetAfterTest();
1543 // First run cron so there are no messages waiting to be sent (from other tests).
1547 $course = $this->getDataGenerator()->create_course();
1548 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1549 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1551 // Now create an assignment and add some feedback.
1552 $this->setUser($teacher);
1553 $assign = $this->create_instance($course, [
1554 'sendstudentnotifications' => 1,
1557 $this->add_submission($student, $assign);
1558 $this->submit_for_grading($student, $assign);
1559 $this->mark_submission($teacher, $assign, $student, 50.0, [
1560 'sendstudentnotifications' => 0,
1564 $sink = $this->redirectMessages();
1566 $messages = $sink->get_messages();
1568 $this->assertEquals(0, count($messages));
1571 public function test_cron_regraded() {
1572 $this->resetAfterTest();
1574 // First run cron so there are no messages waiting to be sent (from other tests).
1578 $course = $this->getDataGenerator()->create_course();
1579 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1580 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1582 // Now create an assignment and add some feedback.
1583 $this->setUser($teacher);
1584 $assign = $this->create_instance($course, [
1585 'sendstudentnotifications' => 1,
1588 $this->add_submission($student, $assign);
1589 $this->submit_for_grading($student, $assign);
1590 $this->mark_submission($teacher, $assign, $student, 50.0);
1592 $this->expectOutputRegex('/Done processing 1 assignment submissions/');
1597 $this->mark_submission($teacher, $assign, $student, 50.0);
1599 $this->expectOutputRegex('/Done processing 1 assignment submissions/');
1601 $sink = $this->redirectMessages();
1603 $messages = $sink->get_messages();
1605 $this->assertEquals(1, count($messages));
1606 $this->assertEquals(1, $messages[0]->notification
);
1607 $this->assertEquals($assign->get_instance()->name
, $messages[0]->contexturlname
);
1611 * Test delivery of grade notifications as controlled by marking workflow.
1613 public function test_markingworkflow_cron() {
1614 $this->resetAfterTest();
1616 // First run cron so there are no messages waiting to be sent (from other tests).
1620 $course = $this->getDataGenerator()->create_course();
1621 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1622 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1624 // Now create an assignment and add some feedback.
1625 $this->setUser($teacher);
1626 $assign = $this->create_instance($course, [
1627 'sendstudentnotifications' => 1,
1628 'markingworkflow' => 1,
1631 // Mark a submission but set the workflowstate to an unreleased state.
1632 // This should not trigger a notification.
1633 $this->add_submission($student, $assign);
1634 $this->submit_for_grading($student, $assign);
1635 $this->mark_submission($teacher, $assign, $student, 50.0, [
1636 'sendstudentnotifications' => 1,
1637 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE
,
1641 $sink = $this->redirectMessages();
1643 $messages = $sink->get_messages();
1645 $this->assertEquals(0, count($messages));
1647 // Transition to the released state.
1648 $this->setUser($teacher);
1649 $submission = $assign->get_user_submission($student->id
, true);
1650 $submission->workflowstate
= ASSIGN_MARKING_WORKFLOW_STATE_RELEASED
;
1651 $assign->testable_apply_grade_to_user($submission, $student->id
, 0);
1653 // Now run cron and see that one message was sent.
1655 $sink = $this->redirectMessages();
1656 $this->expectOutputRegex('/Done processing 1 assignment submissions/');
1658 $messages = $sink->get_messages();
1660 $this->assertEquals(1, count($messages));
1661 $this->assertEquals(1, $messages[0]->notification
);
1662 $this->assertEquals($assign->get_instance()->name
, $messages[0]->contexturlname
);
1665 public function test_cron_message_includes_courseid() {
1666 $this->resetAfterTest();
1668 // First run cron so there are no messages waiting to be sent (from other tests).
1672 $course = $this->getDataGenerator()->create_course();
1673 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1674 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1676 // Now create an assignment and add some feedback.
1677 $this->setUser($teacher);
1678 $assign = $this->create_instance($course, [
1679 'sendstudentnotifications' => 1,
1682 // Mark a submission but set the workflowstate to an unreleased state.
1683 // This should not trigger a notification.
1684 $this->add_submission($student, $assign);
1685 $this->submit_for_grading($student, $assign);
1686 $this->mark_submission($teacher, $assign, $student);
1687 phpunit_util
::stop_message_redirection();
1689 // Now run cron and see that one message was sent.
1691 $this->preventResetByRollback();
1692 $sink = $this->redirectEvents();
1693 $this->expectOutputRegex('/Done processing 1 assignment submissions/');
1696 $events = $sink->get_events();
1697 $event = reset($events);
1698 $this->assertInstanceOf('\core\event\notification_sent', $event);
1699 $this->assertEquals($assign->get_course()->id
, $event->other
['courseid']);
1703 public function test_is_graded() {
1704 $this->resetAfterTest();
1706 $course = $this->getDataGenerator()->create_course();
1707 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1708 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1709 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
1711 $assign = $this->create_instance($course);
1713 $this->add_submission($student, $assign);
1714 $this->submit_for_grading($student, $assign);
1715 $this->mark_submission($teacher, $assign, $student, 50.0);
1717 $this->setUser($teacher);
1718 $this->assertEquals(true, $assign->testable_is_graded($student->id
));
1719 $this->assertEquals(false, $assign->testable_is_graded($otherstudent->id
));
1722 public function test_can_grade() {
1725 $this->resetAfterTest();
1727 $course = $this->getDataGenerator()->create_course();
1728 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1729 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1731 $assign = $this->create_instance($course);
1733 $this->setUser($student);
1734 $this->assertEquals(false, $assign->can_grade());
1736 $this->setUser($teacher);
1737 $this->assertEquals(true, $assign->can_grade());
1739 // Test the viewgrades capability for other users.
1741 $this->assertTrue($assign->can_grade($teacher->id
));
1742 $this->assertFalse($assign->can_grade($student->id
));
1744 // Test the viewgrades capability - without mod/assign:grade.
1745 $this->setUser($student);
1747 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1748 assign_capability('mod/assign:viewgrades', CAP_ALLOW
, $studentrole->id
, $assign->get_context()->id
);
1749 $this->assertEquals(false, $assign->can_grade());
1752 public function test_can_view_submission() {
1755 $this->resetAfterTest();
1757 $course = $this->getDataGenerator()->create_course();
1758 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1759 $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1760 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1761 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
1762 $suspendedstudent = $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', 0, 0, ENROL_USER_SUSPENDED
);
1764 $assign = $this->create_instance($course);
1766 $this->setUser($student);
1767 $this->assertEquals(true, $assign->can_view_submission($student->id
));
1768 $this->assertEquals(false, $assign->can_view_submission($otherstudent->id
));
1769 $this->assertEquals(false, $assign->can_view_submission($teacher->id
));
1771 $this->setUser($teacher);
1772 $this->assertEquals(true, $assign->can_view_submission($student->id
));
1773 $this->assertEquals(true, $assign->can_view_submission($otherstudent->id
));
1774 $this->assertEquals(true, $assign->can_view_submission($teacher->id
));
1775 $this->assertEquals(false, $assign->can_view_submission($suspendedstudent->id
));
1777 $this->setUser($editingteacher);
1778 $this->assertEquals(true, $assign->can_view_submission($student->id
));
1779 $this->assertEquals(true, $assign->can_view_submission($otherstudent->id
));
1780 $this->assertEquals(true, $assign->can_view_submission($teacher->id
));
1781 $this->assertEquals(true, $assign->can_view_submission($suspendedstudent->id
));
1783 // Test the viewgrades capability - without mod/assign:grade.
1784 $this->setUser($student);
1785 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1786 assign_capability('mod/assign:viewgrades', CAP_ALLOW
, $studentrole->id
, $assign->get_context()->id
);
1787 $this->assertEquals(true, $assign->can_view_submission($student->id
));
1788 $this->assertEquals(true, $assign->can_view_submission($otherstudent->id
));
1789 $this->assertEquals(true, $assign->can_view_submission($teacher->id
));
1790 $this->assertEquals(false, $assign->can_view_submission($suspendedstudent->id
));
1793 public function test_update_submission() {
1794 $this->resetAfterTest();
1796 $course = $this->getDataGenerator()->create_course();
1797 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1798 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1800 $assign = $this->create_instance($course);
1802 $this->add_submission($student, $assign);
1803 $submission = $assign->get_user_submission($student->id
, 0);
1804 $assign->testable_update_submission($submission, $student->id
, true, true);
1806 $this->setUser($teacher);
1808 // Verify the gradebook update.
1809 $gradinginfo = grade_get_grades($course->id
, 'mod', 'assign', $assign->get_instance()->id
, $student->id
);
1811 $this->assertTrue(isset($gradinginfo->items
[0]->grades
[$student->id
]));
1812 $this->assertEquals($student->id
, $gradinginfo->items
[0]->grades
[$student->id
]->usermodified
);
1815 public function test_update_submission_team() {
1816 $this->resetAfterTest();
1818 $course = $this->getDataGenerator()->create_course();
1819 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1820 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1822 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1823 groups_add_member($group, $student);
1825 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
1826 groups_add_member($group, $otherstudent);
1828 $assign = $this->create_instance($course, [
1829 'teamsubmission' => 1,
1832 $gradinginfo = grade_get_grades($course->id
, 'mod', 'assign', $assign->get_instance()->id
, $student->id
);
1833 $this->assertTrue(isset($gradinginfo->items
[0]->grades
[$student->id
]));
1834 $this->assertNull($gradinginfo->items
[0]->grades
[$student->id
]->usermodified
);
1836 $gradinginfo = grade_get_grades($course->id
, 'mod', 'assign', $assign->get_instance()->id
, $otherstudent->id
);
1837 $this->asserttrue(isset($gradinginfo->items
[0]->grades
[$otherstudent->id
]));
1838 $this->assertNull($gradinginfo->items
[0]->grades
[$otherstudent->id
]->usermodified
);
1840 $this->add_submission($student, $assign);
1841 $submission = $assign->get_group_submission($student->id
, 0, true);
1842 $assign->testable_update_submission($submission, $student->id
, true, true);
1844 // Verify the gradebook update for the student.
1845 $gradinginfo = grade_get_grades($course->id
, 'mod', 'assign', $assign->get_instance()->id
, $student->id
);
1847 $this->assertTrue(isset($gradinginfo->items
[0]->grades
[$student->id
]));
1848 $this->assertEquals($student->id
, $gradinginfo->items
[0]->grades
[$student->id
]->usermodified
);
1850 // Verify the gradebook update for the other student.
1851 $gradinginfo = grade_get_grades($course->id
, 'mod', 'assign', $assign->get_instance()->id
, $otherstudent->id
);
1853 $this->assertTrue(isset($gradinginfo->items
[0]->grades
[$otherstudent->id
]));
1854 $this->assertEquals($otherstudent->id
, $gradinginfo->items
[0]->grades
[$otherstudent->id
]->usermodified
);
1857 public function test_update_submission_suspended() {
1858 $this->resetAfterTest();
1860 $course = $this->getDataGenerator()->create_course();
1861 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1862 $student = $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', 0, 0, ENROL_USER_SUSPENDED
);
1864 $assign = $this->create_instance($course);
1866 $this->add_submission($student, $assign);
1867 $submission = $assign->get_user_submission($student->id
, 0);
1868 $assign->testable_update_submission($submission, $student->id
, true, false);
1870 $this->setUser($teacher);
1872 // Verify the gradebook update.
1873 $gradinginfo = grade_get_grades($course->id
, 'mod', 'assign', $assign->get_instance()->id
, $student->id
);
1875 $this->assertTrue(isset($gradinginfo->items
[0]->grades
[$student->id
]));
1876 $this->assertEquals($student->id
, $gradinginfo->items
[0]->grades
[$student->id
]->usermodified
);
1879 public function test_update_submission_blind() {
1880 $this->resetAfterTest();
1882 $course = $this->getDataGenerator()->create_course();
1883 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1884 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1886 $assign = $this->create_instance($course, [
1887 'blindmarking' => 1,
1890 $this->add_submission($student, $assign);
1891 $submission = $assign->get_user_submission($student->id
, 0);
1892 $assign->testable_update_submission($submission, $student->id
, true, false);
1894 // Verify the gradebook update.
1895 $gradinginfo = grade_get_grades($course->id
, 'mod', 'assign', $assign->get_instance()->id
, $student->id
);
1897 // The usermodified is not set because this is blind marked.
1898 $this->assertTrue(isset($gradinginfo->items
[0]->grades
[$student->id
]));
1899 $this->assertNull($gradinginfo->items
[0]->grades
[$student->id
]->usermodified
);
1902 public function test_group_submissions_submit_for_marking_requireallteammemberssubmit() {
1905 $this->resetAfterTest();
1907 $course = $this->getDataGenerator()->create_course();
1908 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1909 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1911 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1912 groups_add_member($group, $student);
1914 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
1915 groups_add_member($group, $otherstudent);
1917 $assign = $this->create_instance($course, [
1918 'teamsubmission' => 1,
1919 'assignsubmission_onlinetext_enabled' => 1,
1920 'submissiondrafts' => 1,
1921 'requireallteammemberssubmit' => 1,
1924 // Now verify group assignments.
1925 $this->setUser($teacher);
1926 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
1928 // Add a submission.
1929 $this->add_submission($student, $assign);
1931 // Check we can see the submit button.
1932 $this->setUser($student);
1933 $output = $assign->view_student_summary($student, true);
1934 $this->assertStringContainsString(get_string('submitassignment', 'assign'), $output);
1936 $submission = $assign->get_group_submission($student->id
, 0, true);
1937 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
1938 $assign->testable_update_submission($submission, $student->id
, true, true);
1940 // Check that the student does not see "Submit" button.
1941 $output = $assign->view_student_summary($student, true);
1942 $this->assertStringNotContainsString(get_string('submitassignment', 'assign'), $output);
1944 // Change to another user in the same group.
1945 $this->setUser($otherstudent);
1946 $output = $assign->view_student_summary($otherstudent, true);
1947 $this->assertStringContainsString(get_string('submitassignment', 'assign'), $output);
1949 $submission = $assign->get_group_submission($otherstudent->id
, 0, true);
1950 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
1951 $assign->testable_update_submission($submission, $otherstudent->id
, true, true);
1952 $output = $assign->view_student_summary($otherstudent, true);
1953 $this->assertStringNotContainsString(get_string('submitassignment', 'assign'), $output);
1956 public function test_group_submissions_submit_for_marking() {
1959 $this->resetAfterTest();
1961 $course = $this->getDataGenerator()->create_course();
1962 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1963 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
1965 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1966 groups_add_member($group, $student);
1968 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
1969 groups_add_member($group, $otherstudent);
1971 // Now verify group assignments.
1972 $this->setUser($teacher);
1974 $assign = $this->create_instance($course, [
1975 'teamsubmission' => 1,
1976 'assignsubmission_onlinetext_enabled' => 1,
1977 'submissiondrafts' => 1,
1978 'requireallteammemberssubmit' => 0,
1979 'duedate' => $time - (2 * DAYSECS
),
1981 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
1983 // Add a submission.
1984 $this->add_submission($student, $assign);
1987 // Check we can see the submit button.
1988 $output = $assign->view_student_summary($student, true);
1989 $this->assertStringContainsString(get_string('submitassignment', 'assign'), $output);
1990 $this->assertStringContainsString(get_string('timeremaining', 'assign'), $output);
1991 $difftime = time() - $time;
1992 $this->assertStringContainsString(get_string('overdue', 'assign', format_time((2 * DAYSECS
) +
$difftime)), $output);
1994 $submission = $assign->get_group_submission($student->id
, 0, true);
1995 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
1996 $assign->testable_update_submission($submission, $student->id
, true, true);
1998 // Check that the student does not see "Submit" button.
1999 $output = $assign->view_student_summary($student, true);
2000 $this->assertStringNotContainsString(get_string('submitassignment', 'assign'), $output);
2002 // Change to another user in the same group.
2003 $this->setUser($otherstudent);
2004 $output = $assign->view_student_summary($otherstudent, true);
2005 $this->assertStringNotContainsString(get_string('submitassignment', 'assign'), $output);
2007 // Check that time remaining is not overdue.
2008 $this->assertStringContainsString(get_string('timeremaining', 'assign'), $output);
2009 $difftime = time() - $time;
2010 $this->assertStringContainsString(get_string('submittedlate', 'assign', format_time((2 * DAYSECS
) +
$difftime)), $output);
2012 $submission = $assign->get_group_submission($otherstudent->id
, 0, true);
2013 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
2014 $assign->testable_update_submission($submission, $otherstudent->id
, true, true);
2015 $output = $assign->view_student_summary($otherstudent, true);
2016 $this->assertStringNotContainsString(get_string('submitassignment', 'assign'), $output);
2019 public function test_submissions_open() {
2022 $this->resetAfterTest();
2024 $course = $this->getDataGenerator()->create_course();
2025 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2026 $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2027 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2028 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
2029 $suspendedstudent = $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', 0, 0, ENROL_USER_SUSPENDED
);
2031 $this->setAdminUser();
2034 $tomorrow = $now + DAYSECS
;
2035 $oneweek = $now + WEEKSECS
;
2036 $yesterday = $now - DAYSECS
;
2038 $assign = $this->create_instance($course);
2039 $this->assertEquals(true, $assign->testable_submissions_open($student->id
));
2041 $assign = $this->create_instance($course, ['duedate' => $tomorrow]);
2042 $this->assertEquals(true, $assign->testable_submissions_open($student->id
));
2044 $assign = $this->create_instance($course, ['duedate' => $yesterday]);
2045 $this->assertEquals(true, $assign->testable_submissions_open($student->id
));
2047 $assign = $this->create_instance($course, ['duedate' => $yesterday, 'cutoffdate' => $tomorrow]);
2048 $this->assertEquals(true, $assign->testable_submissions_open($student->id
));
2050 $assign = $this->create_instance($course, ['duedate' => $yesterday, 'cutoffdate' => $yesterday]);
2051 $this->assertEquals(false, $assign->testable_submissions_open($student->id
));
2053 $assign->testable_save_user_extension($student->id
, $tomorrow);
2054 $this->assertEquals(true, $assign->testable_submissions_open($student->id
));
2056 $assign = $this->create_instance($course, ['submissiondrafts' => 1]);
2057 $this->assertEquals(true, $assign->testable_submissions_open($student->id
));
2059 $this->setUser($student);
2060 $submission = $assign->get_user_submission($student->id
, true);
2061 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
2062 $assign->testable_update_submission($submission, $student->id
, true, false);
2064 $this->setUser($teacher);
2065 $this->assertEquals(false, $assign->testable_submissions_open($student->id
));
2068 public function test_get_graders() {
2071 $this->resetAfterTest();
2073 $course = $this->getDataGenerator()->create_course();
2074 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2075 $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2076 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2078 $this->setAdminUser();
2080 // Create an assignment with no groups.
2081 $assign = $this->create_instance($course);
2082 $this->assertCount(2, $assign->testable_get_graders($student->id
));
2085 public function test_get_graders_separate_groups() {
2088 $this->resetAfterTest();
2090 $course = $this->getDataGenerator()->create_course();
2091 $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2092 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2093 $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2094 $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2095 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2096 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
2098 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
2099 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
2100 groups_add_member($group1, $student);
2102 $this->setAdminUser();
2104 // Force create an assignment with SEPARATEGROUPS.
2105 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
2106 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
2108 $assign = $this->create_instance($course, [
2109 'groupingid' => $grouping->id
,
2110 'groupmode' => SEPARATEGROUPS
,
2113 $this->assertCount(4, $assign->testable_get_graders($student->id
));
2115 // Note the second student is in a group that is not in the grouping.
2116 // This means that we get all graders that are not in a group in the grouping.
2117 $this->assertCount(4, $assign->testable_get_graders($otherstudent->id
));
2120 public function test_get_notified_users() {
2123 $this->resetAfterTest();
2125 $course = $this->getDataGenerator()->create_course();
2126 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
2127 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
2128 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group1->id
, 'groupingid' => $grouping->id
));
2130 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2131 groups_add_member($group1, $teacher);
2133 $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2134 groups_add_member($group1, $editingteacher);
2136 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2137 groups_add_member($group1, $student);
2139 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
2140 $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2142 $capability = 'mod/assign:receivegradernotifications';
2143 $coursecontext = context_course
::instance($course->id
);
2144 $role = $DB->get_record('role', array('shortname' => 'teacher'));
2146 $this->setUser($teacher);
2148 // Create an assignment with no groups.
2149 $assign = $this->create_instance($course);
2151 $this->assertCount(3, $assign->testable_get_notifiable_users($student->id
));
2153 // Change nonediting teachers role to not receive grader notifications.
2154 assign_capability($capability, CAP_PROHIBIT
, $role->id
, $coursecontext);
2156 // Only the editing teachers will be returned.
2157 $this->assertCount(1, $assign->testable_get_notifiable_users($student->id
));
2159 // Note the second student is in a group that is not in the grouping.
2160 // This means that we get all graders that are not in a group in the grouping.
2161 $this->assertCount(1, $assign->testable_get_notifiable_users($otherstudent->id
));
2164 public function test_get_notified_users_in_grouping() {
2167 $this->resetAfterTest();
2169 $course = $this->getDataGenerator()->create_course();
2170 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
2171 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
2172 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group1->id
, 'groupingid' => $grouping->id
));
2174 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2175 groups_add_member($group1, $teacher);
2177 $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2178 groups_add_member($group1, $editingteacher);
2180 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2181 groups_add_member($group1, $student);
2183 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
2184 $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2186 // Force create an assignment with SEPARATEGROUPS.
2187 $assign = $this->create_instance($course, [
2188 'groupingid' => $grouping->id
,
2189 'groupmode' => SEPARATEGROUPS
,
2192 // Student is in a group - only the tacher and editing teacher in the group shoudl be present.
2193 $this->setUser($student);
2194 $this->assertCount(2, $assign->testable_get_notifiable_users($student->id
));
2196 // Note the second student is in a group that is not in the grouping.
2197 // This means that we get all graders that are not in a group in the grouping.
2198 $this->assertCount(1, $assign->testable_get_notifiable_users($otherstudent->id
));
2200 // Change nonediting teachers role to not receive grader notifications.
2201 $capability = 'mod/assign:receivegradernotifications';
2202 $coursecontext = context_course
::instance($course->id
);
2203 $role = $DB->get_record('role', ['shortname' => 'teacher']);
2204 assign_capability($capability, CAP_PROHIBIT
, $role->id
, $coursecontext);
2206 // Only the editing teachers will be returned.
2207 $this->assertCount(1, $assign->testable_get_notifiable_users($student->id
));
2209 // Note the second student is in a group that is not in the grouping.
2210 // This means that we get all graders that are not in a group in the grouping.
2211 // Unfortunately there are no editing teachers who are not in a group.
2212 $this->assertCount(0, $assign->testable_get_notifiable_users($otherstudent->id
));
2215 public function test_group_members_only() {
2218 $this->resetAfterTest();
2220 $course = $this->getDataGenerator()->create_course();
2221 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
2222 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
2223 $this->getDataGenerator()->create_grouping_group([
2224 'groupid' => $group1->id
,
2225 'groupingid' => $grouping->id
,
2228 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
2229 $this->getDataGenerator()->create_grouping_group([
2230 'groupid' => $group2->id
,
2231 'groupingid' => $grouping->id
,
2234 $group3 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
2236 // Add users in the following groups
2237 // - Teacher - Group 1.
2238 // - Student - Group 1.
2239 // - Student - Group 2.
2240 // - Student - Unrelated Group
2241 // - Student - No group.
2242 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2243 groups_add_member($group1, $teacher);
2245 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2246 groups_add_member($group1, $student);
2248 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
2249 groups_add_member($group2, $otherstudent);
2251 $yetotherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
2252 groups_add_member($group2, $otherstudent);
2254 $this->getDataGenerator()->create_and_enrol($course, 'student');
2256 $this->setAdminUser();
2258 $CFG->enableavailability
= true;
2259 $assign = $this->create_instance($course, [], [
2260 'availability' => json_encode(
2261 \core_availability\tree
::get_root_json([\availability_grouping\condition
::get_json()])
2263 'groupingid' => $grouping->id
,
2266 // The two students in groups should be returned, but not the teacher in the group, or the student not in the
2267 // group, or the student in an unrelated group.
2268 $this->setUser($teacher);
2269 $participants = $assign->list_participants(0, true);
2270 $this->assertCount(2, $participants);
2271 $this->assertTrue(isset($participants[$student->id
]));
2272 $this->assertTrue(isset($participants[$otherstudent->id
]));
2275 public function test_get_uniqueid_for_user() {
2276 $this->resetAfterTest();
2278 $course = $this->getDataGenerator()->create_course();
2279 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2281 for ($i = 0; $i < 10; $i++
) {
2282 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2283 $students[$student->id
] = $student;
2286 $this->setUser($teacher);
2287 $assign = $this->create_instance($course);
2289 foreach ($students as $student) {
2290 $uniqueid = $assign->get_uniqueid_for_user($student->id
);
2291 $this->assertEquals($student->id
, $assign->get_user_id_for_uniqueid($uniqueid));
2295 public function test_show_student_summary() {
2298 $this->resetAfterTest();
2300 $course = $this->getDataGenerator()->create_course();
2301 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2302 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2303 $this->setUser($teacher);
2304 $assign = $this->create_instance($course);
2305 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
2307 // No feedback should be available because this student has not been graded.
2308 $this->setUser($student);
2309 $output = $assign->view_student_summary($student, true);
2310 $this->assertDoesNotMatchRegularExpression('/Feedback/', $output, 'Do not show feedback if there is no grade');
2312 // Simulate adding a grade.
2313 $this->add_submission($student, $assign);
2314 $this->submit_for_grading($student, $assign);
2315 $this->mark_submission($teacher, $assign, $student);
2317 // Now we should see the feedback.
2318 $this->setUser($student);
2319 $output = $assign->view_student_summary($student, true);
2320 $this->assertMatchesRegularExpression('/Feedback/', $output, 'Show feedback if there is a grade');
2322 // Now hide the grade in gradebook.
2323 $this->setUser($teacher);
2324 require_once($CFG->libdir
.'/gradelib.php');
2325 $gradeitem = new grade_item(array(
2326 'itemtype' => 'mod',
2327 'itemmodule' => 'assign',
2328 'iteminstance' => $assign->get_instance()->id
,
2329 'courseid' => $course->id
));
2331 $gradeitem->set_hidden(1, false);
2333 // No feedback should be available because the grade is hidden.
2334 $this->setUser($student);
2335 $output = $assign->view_student_summary($student, true);
2336 $this->assertDoesNotMatchRegularExpression('/Feedback/', $output, 'Do not show feedback if the grade is hidden in the gradebook');
2339 public function test_show_student_summary_with_feedback() {
2342 $this->resetAfterTest();
2344 $course = $this->getDataGenerator()->create_course();
2345 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2346 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2347 $this->setUser($teacher);
2348 $assign = $this->create_instance($course, [
2349 'assignfeedback_comments_enabled' => 1
2351 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
2353 // No feedback should be available because this student has not been graded.
2354 $this->setUser($student);
2355 $output = $assign->view_student_summary($student, true);
2356 $this->assertDoesNotMatchRegularExpression('/Feedback/', $output);
2358 // Simulate adding a grade.
2359 $this->add_submission($student, $assign);
2360 $this->submit_for_grading($student, $assign);
2361 $this->mark_submission($teacher, $assign, $student, null, [
2362 'assignfeedbackcomments_editor' => [
2363 'text' => 'Tomato sauce',
2364 'format' => FORMAT_MOODLE
,
2368 // Should have feedback but no grade.
2369 $this->setUser($student);
2370 $output = $assign->view_student_summary($student, true);
2371 $this->assertMatchesRegularExpression('/Feedback/', $output);
2372 $this->assertMatchesRegularExpression('/Tomato sauce/', $output);
2373 $this->assertDoesNotMatchRegularExpression('/Grade/', $output, 'Do not show grade when there is no grade.');
2374 $this->assertDoesNotMatchRegularExpression('/Graded on/', $output, 'Do not show graded date when there is no grade.');
2377 $this->mark_submission($teacher, $assign, $student, 50.0, [
2378 'assignfeedbackcomments_editor' => [
2379 'text' => 'Bechamel sauce',
2380 'format' => FORMAT_MOODLE
,
2384 // Should have feedback but no grade.
2385 $this->setUser($student);
2386 $output = $assign->view_student_summary($student, true);
2387 $this->assertDoesNotMatchRegularExpression('/Tomato sauce/', $output);
2388 $this->assertMatchesRegularExpression('/Bechamel sauce/', $output);
2389 $this->assertMatchesRegularExpression('/Grade/', $output);
2390 $this->assertMatchesRegularExpression('/Graded on/', $output);
2392 // Now hide the grade in gradebook.
2393 $this->setUser($teacher);
2394 $gradeitem = new grade_item(array(
2395 'itemtype' => 'mod',
2396 'itemmodule' => 'assign',
2397 'iteminstance' => $assign->get_instance()->id
,
2398 'courseid' => $course->id
));
2400 $gradeitem->set_hidden(1, false);
2402 // No feedback should be available because the grade is hidden.
2403 $this->setUser($student);
2404 $output = $assign->view_student_summary($student, true);
2405 $this->assertDoesNotMatchRegularExpression('/Feedback/', $output, 'Do not show feedback if the grade is hidden in the gradebook');
2409 * Test reopen behavior when in "Manual" mode.
2411 public function test_attempt_reopen_method_manual() {
2414 $this->resetAfterTest();
2415 $course = $this->getDataGenerator()->create_course();
2416 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2417 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2419 $assign = $this->create_instance($course, [
2420 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL
,
2422 'submissiondrafts' => 1,
2423 'assignsubmission_onlinetext_enabled' => 1,
2425 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
2427 // Student should be able to see an add submission button.
2428 $this->setUser($student);
2429 $output = $assign->view_student_summary($student, true);
2430 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2432 // Add a submission.
2433 $this->add_submission($student, $assign);
2434 $this->submit_for_grading($student, $assign);
2436 // Verify the student cannot make changes to the submission.
2437 $output = $assign->view_student_summary($student, true);
2438 $this->assertEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2440 // Mark the submission.
2441 $this->mark_submission($teacher, $assign, $student);
2443 // Check the student can see the grade.
2444 $this->setUser($student);
2445 $output = $assign->view_student_summary($student, true);
2446 $this->assertNotEquals(false, strpos($output, '50.0'));
2448 // Allow the student another attempt.
2449 $teacher->ignoresesskey
= true;
2450 $this->setUser($teacher);
2451 $result = $assign->testable_process_add_attempt($student->id
);
2452 $this->assertEquals(true, $result);
2454 // Check that the previous attempt is now in the submission history table.
2455 $this->setUser($student);
2456 $output = $assign->view_student_summary($student, true);
2457 // Need a better check.
2458 $this->assertNotEquals(false, strpos($output, 'Submission text'), 'Contains: Submission text');
2460 // Check that the student now has a button for Add a new attempt".
2461 $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2462 // Check that the student now does not have a button for Submit.
2463 $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
2465 // Check that the student now has a submission history.
2466 $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
2468 $this->setUser($teacher);
2469 // Check that the grading table loads correctly and contains this user.
2470 // This is also testing that we do not get duplicate rows in the grading table.
2471 $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
2472 $output = $assign->get_renderer()->render($gradingtable);
2473 $this->assertEquals(true, strpos($output, $student->lastname
));
2475 // Should be 1 not 2.
2476 $this->assertEquals(1, $assign->count_submissions());
2477 $this->assertEquals(1, $assign->count_submissions_with_status('reopened'));
2478 $this->assertEquals(0, $assign->count_submissions_need_grading());
2479 $this->assertEquals(1, $assign->count_grades());
2481 // Change max attempts to unlimited.
2482 $formdata = clone($assign->get_instance());
2483 $formdata->maxattempts
= ASSIGN_UNLIMITED_ATTEMPTS
;
2484 $formdata->instance
= $formdata->id
;
2485 $assign->update_instance($formdata);
2487 // Mark the submission again.
2488 $this->mark_submission($teacher, $assign, $student, 60.0, [], 1);
2490 // Check the grade exists.
2491 $this->setUser($teacher);
2492 $grades = $assign->get_user_grades_for_gradebook($student->id
);
2493 $this->assertEquals(60, (int) $grades[$student->id
]->rawgrade
);
2495 // Check we can reopen still.
2496 $result = $assign->testable_process_add_attempt($student->id
);
2497 $this->assertEquals(true, $result);
2499 // Should no longer have a grade because there is no grade for the latest attempt.
2500 $grades = $assign->get_user_grades_for_gradebook($student->id
);
2501 $this->assertEmpty($grades);
2505 * Test reopen behavior when in "Reopen until pass" mode.
2507 public function test_attempt_reopen_method_untilpass() {
2510 $this->resetAfterTest();
2511 $course = $this->getDataGenerator()->create_course();
2512 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2513 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2515 $assign = $this->create_instance($course, [
2516 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS
,
2518 'submissiondrafts' => 1,
2519 'assignsubmission_onlinetext_enabled' => 1,
2521 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
2523 // Set grade to pass to 80.
2524 $gradeitem = $assign->get_grade_item();
2525 $gradeitem->gradepass
= '80.0';
2526 $gradeitem->update();
2528 // Student should be able to see an add submission button.
2529 $this->setUser($student);
2530 $output = $assign->view_student_summary($student, true);
2531 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2533 // Add a submission.
2534 $this->add_submission($student, $assign);
2535 $this->submit_for_grading($student, $assign);
2537 // Verify the student cannot make a new attempt.
2538 $output = $assign->view_student_summary($student, true);
2539 $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2541 // Mark the submission as non-passing.
2542 $this->mark_submission($teacher, $assign, $student, 50.0);
2544 // Check the student can see the grade.
2545 $this->setUser($student);
2546 $output = $assign->view_student_summary($student, true);
2547 $this->assertNotEquals(false, strpos($output, '50.0'));
2549 // Check that the student now has a button for Add a new attempt.
2550 $output = $assign->view_student_summary($student, true);
2551 $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2553 // Check that the student now does not have a button for Submit.
2554 $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
2556 // Check that the student now has a submission history.
2557 $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
2559 // Add a second submission.
2560 $this->add_submission($student, $assign);
2561 $this->submit_for_grading($student, $assign);
2563 // Mark the submission as passing.
2564 $this->mark_submission($teacher, $assign, $student, 80.0);
2566 // Check that the student does not have a button for Add a new attempt.
2567 $this->setUser($student);
2568 $output = $assign->view_student_summary($student, true);
2569 $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2571 // Re-mark the submission as not passing.
2572 $this->mark_submission($teacher, $assign, $student, 40.0, [], 1);
2574 // Check that the student now has a button for Add a new attempt.
2575 $this->setUser($student);
2576 $output = $assign->view_student_summary($student, true);
2577 $this->assertMatchesRegularExpression('/' . get_string('addnewattempt', 'assign') . '/', $output);
2578 $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2581 public function test_attempt_reopen_method_untilpass_passing() {
2584 $this->resetAfterTest();
2585 $course = $this->getDataGenerator()->create_course();
2586 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2587 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2589 $assign = $this->create_instance($course, [
2590 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS
,
2592 'submissiondrafts' => 1,
2593 'assignsubmission_onlinetext_enabled' => 1,
2595 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
2597 // Set grade to pass to 80.
2598 $gradeitem = $assign->get_grade_item();
2599 $gradeitem->gradepass
= '80.0';
2600 $gradeitem->update();
2602 // Student should be able to see an add submission button.
2603 $this->setUser($student);
2604 $output = $assign->view_student_summary($student, true);
2605 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2607 // Add a submission as a student.
2608 $this->add_submission($student, $assign);
2609 $this->submit_for_grading($student, $assign);
2611 // Mark the submission as passing.
2612 $this->mark_submission($teacher, $assign, $student, 100.0);
2614 // Check the student can see the grade.
2615 $this->setUser($student);
2616 $output = $assign->view_student_summary($student, true);
2617 $this->assertNotEquals(false, strpos($output, '100.0'));
2619 // Check that the student does not have a button for Add a new attempt.
2620 $output = $assign->view_student_summary($student, true);
2621 $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2624 public function test_attempt_reopen_method_untilpass_no_passing_requirement() {
2627 $this->resetAfterTest();
2628 $course = $this->getDataGenerator()->create_course();
2629 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2630 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2632 $assign = $this->create_instance($course, [
2633 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS
,
2635 'submissiondrafts' => 1,
2636 'assignsubmission_onlinetext_enabled' => 1,
2638 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
2640 // Set grade to pass to 0, so that no attempts should reopen.
2641 $gradeitem = $assign->get_grade_item();
2642 $gradeitem->gradepass
= '0';
2643 $gradeitem->update();
2645 // Student should be able to see an add submission button.
2646 $this->setUser($student);
2647 $output = $assign->view_student_summary($student, true);
2648 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2650 // Add a submission.
2651 $this->add_submission($student, $assign);
2652 $this->submit_for_grading($student, $assign);
2654 // Mark the submission with any grade.
2655 $this->mark_submission($teacher, $assign, $student, 0.0);
2657 // Check the student can see the grade.
2658 $this->setUser($student);
2659 $output = $assign->view_student_summary($student, true);
2660 $this->assertNotEquals(false, strpos($output, '0.0'));
2662 // Check that the student does not have a button for Add a new attempt.
2663 $output = $assign->view_student_summary($student, true);
2664 $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2668 * Test student visibility for each stage of the marking workflow.
2670 public function test_markingworkflow() {
2673 $this->resetAfterTest();
2674 $course = $this->getDataGenerator()->create_course();
2675 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2676 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2678 $assign = $this->create_instance($course, [
2679 'markingworkflow' => 1,
2682 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
2684 // Mark the submission and set to notmarked.
2685 $this->mark_submission($teacher, $assign, $student, 50.0, [
2686 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED
,
2689 // Check the student can't see the grade.
2690 $this->setUser($student);
2691 $output = $assign->view_student_summary($student, true);
2692 $this->assertEquals(false, strpos($output, '50.0'));
2694 // Make sure the grade isn't pushed to the gradebook.
2695 $grades = $assign->get_user_grades_for_gradebook($student->id
);
2696 $this->assertEmpty($grades);
2698 // Mark the submission and set to inmarking.
2699 $this->mark_submission($teacher, $assign, $student, 50.0, [
2700 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_INMARKING
,
2703 // Check the student can't see the grade.
2704 $this->setUser($student);
2705 $output = $assign->view_student_summary($student, true);
2706 $this->assertEquals(false, strpos($output, '50.0'));
2708 // Make sure the grade isn't pushed to the gradebook.
2709 $grades = $assign->get_user_grades_for_gradebook($student->id
);
2710 $this->assertEmpty($grades);
2712 // Mark the submission and set to readyforreview.
2713 $this->mark_submission($teacher, $assign, $student, 50.0, [
2714 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW
,
2717 // Check the student can't see the grade.
2718 $this->setUser($student);
2719 $output = $assign->view_student_summary($student, true);
2720 $this->assertEquals(false, strpos($output, '50.0'));
2722 // Make sure the grade isn't pushed to the gradebook.
2723 $grades = $assign->get_user_grades_for_gradebook($student->id
);
2724 $this->assertEmpty($grades);
2726 // Mark the submission and set to inreview.
2727 $this->mark_submission($teacher, $assign, $student, 50.0, [
2728 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW
,
2731 // Check the student can't see the grade.
2732 $this->setUser($student);
2733 $output = $assign->view_student_summary($student, true);
2734 $this->assertEquals(false, strpos($output, '50.0'));
2736 // Make sure the grade isn't pushed to the gradebook.
2737 $grades = $assign->get_user_grades_for_gradebook($student->id
);
2738 $this->assertEmpty($grades);
2740 // Mark the submission and set to readyforrelease.
2741 $this->mark_submission($teacher, $assign, $student, 50.0, [
2742 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE
,
2745 // Check the student can't see the grade.
2746 $this->setUser($student);
2747 $output = $assign->view_student_summary($student, true);
2748 $this->assertEquals(false, strpos($output, '50.0'));
2750 // Make sure the grade isn't pushed to the gradebook.
2751 $grades = $assign->get_user_grades_for_gradebook($student->id
);
2752 $this->assertEmpty($grades);
2754 // Mark the submission and set to released.
2755 $this->mark_submission($teacher, $assign, $student, 50.0, [
2756 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_RELEASED
,
2759 // Check the student can see the grade.
2760 $this->setUser($student);
2761 $output = $assign->view_student_summary($student, true);
2762 $this->assertNotEquals(false, strpos($output, '50.0'));
2764 // Make sure the grade is pushed to the gradebook.
2765 $grades = $assign->get_user_grades_for_gradebook($student->id
);
2766 $this->assertEquals(50, (int)$grades[$student->id
]->rawgrade
);
2770 * Test that a student allocated a specific marker is only shown to that marker.
2772 public function test_markerallocation() {
2775 $this->resetAfterTest();
2776 $course = $this->getDataGenerator()->create_course();
2777 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2778 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2779 $otherteacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2781 $assign = $this->create_instance($course, [
2782 'markingworkflow' => 1,
2783 'markingallocation' => 1
2786 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
2788 // Allocate marker to submission.
2789 $this->mark_submission($teacher, $assign, $student, null, [
2790 'allocatedmarker' => $teacher->id
,
2793 // Check the allocated marker can view the submission.
2794 $this->setUser($teacher);
2795 $users = $assign->list_participants(0, true);
2796 $this->assertEquals(1, count($users));
2797 $this->assertTrue(isset($users[$student->id
]));
2799 $cm = get_coursemodule_from_instance('assign', $assign->get_instance()->id
);
2800 $context = context_module
::instance($cm->id
);
2801 $assign = new mod_assign_testable_assign($context, $cm, $course);
2803 // Check that other teachers can't view this submission.
2804 $this->setUser($otherteacher);
2805 $users = $assign->list_participants(0, true);
2806 $this->assertEquals(0, count($users));
2810 * Ensure that a teacher cannot submit for students as standard.
2812 public function test_teacher_submit_for_student() {
2815 $this->resetAfterTest();
2816 $course = $this->getDataGenerator()->create_course();
2817 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2818 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2820 $assign = $this->create_instance($course, [
2821 'assignsubmission_onlinetext_enabled' => 1,
2822 'submissiondrafts' => 1,
2825 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
2827 // Add a submission but do not submit.
2828 $this->add_submission($student, $assign, 'Student submission text');
2830 $this->setUser($student);
2831 $output = $assign->view_student_summary($student, true);
2832 $this->assertStringContainsString('Student submission text', $output, 'Contains student submission text');
2834 // Check that a teacher can not edit the submission as they do not have the capability.
2835 $this->setUser($teacher);
2836 $this->expectException('moodle_exception');
2837 $this->expectExceptionMessage('error/nopermission');
2838 $this->add_submission($student, $assign, 'Teacher edited submission text', false);
2842 * Ensure that a teacher with the editothersubmission capability can submit on behalf of a student.
2844 public function test_teacher_submit_for_student_with_capability() {
2847 $this->resetAfterTest();
2848 $course = $this->getDataGenerator()->create_course();
2849 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2850 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2851 $otherteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2853 $assign = $this->create_instance($course, [
2854 'assignsubmission_onlinetext_enabled' => 1,
2855 'submissiondrafts' => 1,
2858 // Add the required capability.
2859 $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
2860 assign_capability('mod/assign:editothersubmission', CAP_ALLOW
, $roleid, $assign->get_context()->id
);
2861 role_assign($roleid, $teacher->id
, $assign->get_context()->id
);
2862 accesslib_clear_all_caches_for_unit_testing();
2864 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
2866 // Add a submission but do not submit.
2867 $this->add_submission($student, $assign, 'Student submission text');
2869 $this->setUser($student);
2870 $output = $assign->view_student_summary($student, true);
2871 $this->assertStringContainsString('Student submission text', $output, 'Contains student submission text');
2873 // Check that a teacher can edit the submission.
2874 $this->setUser($teacher);
2875 $this->add_submission($student, $assign, 'Teacher edited submission text', false);
2877 $this->setUser($student);
2878 $output = $assign->view_student_summary($student, true);
2879 $this->assertStringNotContainsString('Student submission text', $output, 'Contains student submission text');
2880 $this->assertStringContainsString('Teacher edited submission text', $output, 'Contains teacher edited submission text');
2882 // Check that the teacher can submit the students work.
2883 $this->setUser($teacher);
2884 $this->submit_for_grading($student, $assign, [], false);
2886 // Revert to draft so the student can edit it.
2887 $assign->revert_to_draft($student->id
);
2889 $this->setUser($student);
2891 // Check that the submission text was saved.
2892 $output = $assign->view_student_summary($student, true);
2893 $this->assertStringContainsString('Teacher edited submission text', $output, 'Contains student submission text');
2895 // Check that the student can submit their work.
2896 $this->submit_for_grading($student, $assign, []);
2898 $output = $assign->view_student_summary($student, true);
2899 $this->assertStringNotContainsString(get_string('addsubmission', 'assign'), $output);
2901 // An editing teacher without the extra role should still be able to revert to draft.
2902 $this->setUser($otherteacher);
2904 // Revert to draft so the submission is editable.
2905 $assign->revert_to_draft($student->id
);
2909 * Ensure that disabling submit after the cutoff date works as expected.
2911 public function test_disable_submit_after_cutoff_date() {
2914 $this->resetAfterTest();
2915 $course = $this->getDataGenerator()->create_course();
2916 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2919 $tomorrow = $now + DAYSECS
;
2920 $lastweek = $now - (7 * DAYSECS
);
2921 $yesterday = $now - DAYSECS
;
2923 $this->setAdminUser();
2924 $assign = $this->create_instance($course, [
2925 'duedate' => $yesterday,
2926 'cutoffdate' => $tomorrow,
2927 'assignsubmission_onlinetext_enabled' => 1,
2930 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
2932 // Student should be able to see an add submission button.
2933 $this->setUser($student);
2934 $output = $assign->view_student_summary($student, true);
2935 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2937 // Add a submission but don't submit now.
2938 $this->add_submission($student, $assign);
2940 // Create another instance with cut-off and due-date already passed.
2941 $this->setAdminUser();
2942 $assign = $this->create_instance($course, [
2943 'duedate' => $lastweek,
2944 'cutoffdate' => $yesterday,
2945 'assignsubmission_onlinetext_enabled' => 1,
2948 $this->setUser($student);
2949 $output = $assign->view_student_summary($student, true);
2950 $this->assertStringNotContainsString($output, get_string('editsubmission', 'assign'),
2951 'Should not be able to edit after cutoff date.');
2952 $this->assertStringNotContainsString($output, get_string('submitassignment', 'assign'),
2953 'Should not be able to submit after cutoff date.');
2957 * Testing for submission comment plugin settings.
2959 * @dataProvider submission_plugin_settings_provider
2960 * @param bool $globalenabled
2961 * @param array $instanceconfig
2962 * @param bool $isenabled
2964 public function test_submission_comment_plugin_settings($globalenabled, $instanceconfig, $isenabled) {
2967 $this->resetAfterTest();
2968 $course = $this->getDataGenerator()->create_course();
2970 $CFG->usecomments
= $globalenabled;
2971 $assign = $this->create_instance($course, $instanceconfig);
2972 $plugin = $assign->get_submission_plugin_by_type('comments');
2973 $this->assertEquals($isenabled, (bool) $plugin->is_enabled('enabled'));
2976 public function submission_plugin_settings_provider() {
2978 'CFG->usecomments true, empty config => Enabled by default' => [
2983 'CFG->usecomments true, config enabled => Comments enabled' => [
2986 'assignsubmission_comments_enabled' => 1,
2990 'CFG->usecomments true, config idisabled => Comments enabled' => [
2993 'assignsubmission_comments_enabled' => 0,
2997 'CFG->usecomments false, empty config => Disabled by default' => [
3002 'CFG->usecomments false, config enabled => Comments disabled' => [
3005 'assignsubmission_comments_enabled' => 1,
3009 'CFG->usecomments false, config disabled => Comments disabled' => [
3012 'assignsubmission_comments_enabled' => 0,
3020 * Testing for comment inline settings
3022 public function test_feedback_comment_commentinline() {
3025 $this->resetAfterTest();
3026 $course = $this->getDataGenerator()->create_course();
3027 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3028 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3030 $sourcetext = "Hello!
3032 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
3034 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
3035 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
3037 External link 1:<img src='https://moodle.org/logo/logo-240x60.gif' alt='Moodle'/>
3038 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\"/>
3039 Internal link 1:<img src='@@PLUGINFILE@@/logo-240x60.gif' alt='Moodle'/>
3040 Internal link 2:<img alt=\"Moodle\" src=\"@@PLUGINFILE@@logo-240x60.gif\"/>
3041 Anchor link 1:<a href=\"@@PLUGINFILE@@logo-240x60.gif\" alt=\"bananas\">Link text</a>
3042 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
3045 $this->setUser($teacher);
3046 $assign = $this->create_instance($course, [
3047 'assignsubmission_onlinetext_enabled' => 1,
3048 'assignfeedback_comments_enabled' => 1,
3049 'assignfeedback_comments_commentinline' => 1,
3052 $this->setUser($student);
3054 // Add a submission but don't submit now.
3055 $this->add_submission($student, $assign, $sourcetext);
3057 $this->setUser($teacher);
3059 $data = new stdClass();
3060 require_once($CFG->dirroot
. '/mod/assign/gradeform.php');
3062 'userid' => $student->id
,
3065 'useridlistid' => $assign->get_useridlist_key_id(),
3066 'attemptnumber' => 0,
3068 $formparams = array($assign, $data, $pagination);
3069 $mform = new mod_assign_grade_form(null, [$assign, $data, $pagination]);
3071 // We need to get the URL these will be transformed to.
3072 $context = context_user
::instance($USER->id
);
3073 $itemid = $data->assignfeedbackcomments_editor
['itemid'];
3074 $url = $CFG->wwwroot
. '/draftfile.php/' . $context->id
. '/user/draft/' . $itemid;
3076 // Note the internal images have been stripped and the html is purified (quotes fixed in this case).
3077 $filteredtext = "Hello!
3079 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
3081 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
3082 Plugin url outside a tag: $url/logo-240x60.gif
3084 External link 1:<img src=\"https://moodle.org/logo/logo-240x60.gif\" alt=\"Moodle\" />
3085 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\" />
3086 Internal link 1:<img src=\"$url/logo-240x60.gif\" alt=\"Moodle\" />
3087 Internal link 2:<img alt=\"Moodle\" src=\"@@PLUGINFILE@@logo-240x60.gif\" />
3088 Anchor link 1:<a href=\"@@PLUGINFILE@@logo-240x60.gif\">Link text</a>
3089 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
3092 $this->assertEquals($filteredtext, $data->assignfeedbackcomments_editor
['text']);
3096 * Testing for feedback comment plugin settings.
3098 * @dataProvider feedback_plugin_settings_provider
3099 * @param array $instanceconfig
3100 * @param bool $isenabled
3102 public function test_feedback_plugin_settings($instanceconfig, $isenabled) {
3103 $this->resetAfterTest();
3104 $course = $this->getDataGenerator()->create_course();
3106 $assign = $this->create_instance($course, $instanceconfig);
3107 $plugin = $assign->get_feedback_plugin_by_type('comments');
3108 $this->assertEquals($isenabled, (bool) $plugin->is_enabled('enabled'));
3111 public function feedback_plugin_settings_provider() {
3113 'No configuration => disabled' => [
3117 'Actively disabled' => [
3119 'assignfeedback_comments_enabled' => 0,
3123 'Actively enabled' => [
3125 'assignfeedback_comments_enabled' => 1,
3133 * Testing if gradebook feedback plugin is enabled.
3135 public function test_is_gradebook_feedback_enabled() {
3136 $this->resetAfterTest();
3137 $course = $this->getDataGenerator()->create_course();
3138 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3139 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3141 $adminconfig = get_config('assign');
3142 $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook
;
3144 // Create assignment with gradebook feedback enabled and grade = 0.
3145 $assign = $this->create_instance($course, [
3146 "{$gradebookplugin}_enabled" => 1,
3150 // Get gradebook feedback plugin.
3151 $gradebookplugintype = str_replace('assignfeedback_', '', $gradebookplugin);
3152 $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
3153 $this->assertEquals(1, $plugin->is_enabled('enabled'));
3154 $this->assertEquals(1, $assign->is_gradebook_feedback_enabled());
3158 * Testing if gradebook feedback plugin is disabled.
3160 public function test_is_gradebook_feedback_disabled() {
3161 $this->resetAfterTest();
3162 $course = $this->getDataGenerator()->create_course();
3163 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3164 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3166 $adminconfig = get_config('assign');
3167 $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook
;
3169 // Create assignment with gradebook feedback disabled and grade = 0.
3170 $assign = $this->create_instance($course, [
3171 "{$gradebookplugin}_enabled" => 0,
3175 $gradebookplugintype = str_replace('assignfeedback_', '', $gradebookplugin);
3176 $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
3177 $this->assertEquals(0, $plugin->is_enabled('enabled'));
3181 * Testing can_edit_submission.
3183 public function test_can_edit_submission() {
3184 $this->resetAfterTest();
3185 $course = $this->getDataGenerator()->create_course();
3186 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3187 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3188 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
3190 $assign = $this->create_instance($course, [
3191 'assignsubmission_onlinetext_enabled' => 1,
3192 'submissiondrafts' => 1,
3195 // Check student can edit their own submission.
3196 $this->assertTrue($assign->can_edit_submission($student->id
, $student->id
));
3198 // Check student cannot edit others submission.
3199 $this->assertFalse($assign->can_edit_submission($otherstudent->id
, $student->id
));
3201 // Check teacher cannot (by default) edit a students submission.
3202 $this->assertFalse($assign->can_edit_submission($student->id
, $teacher->id
));
3206 * Testing can_edit_submission with the editothersubmission capability.
3208 public function test_can_edit_submission_with_editothersubmission() {
3209 $this->resetAfterTest();
3210 $course = $this->getDataGenerator()->create_course();
3211 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3212 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3213 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
3215 $assign = $this->create_instance($course, [
3216 'assignsubmission_onlinetext_enabled' => 1,
3217 'submissiondrafts' => 1,
3220 // Add the required capability to edit a student submission.
3221 $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
3222 assign_capability('mod/assign:editothersubmission', CAP_ALLOW
, $roleid, $assign->get_context()->id
);
3223 role_assign($roleid, $teacher->id
, $assign->get_context()->id
);
3224 accesslib_clear_all_caches_for_unit_testing();
3226 // Check student can edit their own submission.
3227 $this->assertTrue($assign->can_edit_submission($student->id
, $student->id
));
3229 // Check student cannot edit others submission.
3230 $this->assertFalse($assign->can_edit_submission($otherstudent->id
, $student->id
));
3232 // Retest - should now have access.
3233 $this->assertTrue($assign->can_edit_submission($student->id
, $teacher->id
));
3237 * Testing can_edit_submission
3239 public function test_can_edit_submission_separategroups() {
3240 $this->resetAfterTest();
3241 $course = $this->getDataGenerator()->create_course();
3242 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3244 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3245 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3246 $student3 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3247 $student4 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3249 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
3250 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
3251 groups_assign_grouping($grouping->id
, $group1->id
);
3252 groups_add_member($group1, $student1);
3253 groups_add_member($group1, $student2);
3255 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
3256 groups_assign_grouping($grouping->id
, $group2->id
);
3257 groups_add_member($group2, $student3);
3258 groups_add_member($group2, $student4);
3260 $assign = $this->create_instance($course, [
3261 'assignsubmission_onlinetext_enabled' => 1,
3262 'submissiondrafts' => 1,
3263 'groupingid' => $grouping->id
,
3264 'groupmode' => SEPARATEGROUPS
,
3267 // Verify a student does not have the ability to edit submissions for other users.
3268 $this->assertTrue($assign->can_edit_submission($student1->id
, $student1->id
));
3269 $this->assertFalse($assign->can_edit_submission($student2->id
, $student1->id
));
3270 $this->assertFalse($assign->can_edit_submission($student3->id
, $student1->id
));
3271 $this->assertFalse($assign->can_edit_submission($student4->id
, $student1->id
));
3275 * Testing can_edit_submission
3277 public function test_can_edit_submission_separategroups_with_editothersubmission() {
3278 $this->resetAfterTest();
3279 $course = $this->getDataGenerator()->create_course();
3280 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3282 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3283 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3284 $student3 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3285 $student4 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3287 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
3288 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
3289 groups_assign_grouping($grouping->id
, $group1->id
);
3290 groups_add_member($group1, $student1);
3291 groups_add_member($group1, $student2);
3293 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
3294 groups_assign_grouping($grouping->id
, $group2->id
);
3295 groups_add_member($group2, $student3);
3296 groups_add_member($group2, $student4);
3298 $assign = $this->create_instance($course, [
3299 'assignsubmission_onlinetext_enabled' => 1,
3300 'submissiondrafts' => 1,
3301 'groupingid' => $grouping->id
,
3302 'groupmode' => SEPARATEGROUPS
,
3305 // Add the capability to the new assignment for student 1.
3306 $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
3307 assign_capability('mod/assign:editothersubmission', CAP_ALLOW
, $roleid, $assign->get_context()->id
);
3308 role_assign($roleid, $student1->id
, $assign->get_context()->id
);
3309 accesslib_clear_all_caches_for_unit_testing();
3311 // Verify student1 has the ability to edit submissions for other users in their group, but not other groups.
3312 $this->assertTrue($assign->can_edit_submission($student1->id
, $student1->id
));
3313 $this->assertTrue($assign->can_edit_submission($student2->id
, $student1->id
));
3314 $this->assertFalse($assign->can_edit_submission($student3->id
, $student1->id
));
3315 $this->assertFalse($assign->can_edit_submission($student4->id
, $student1->id
));
3317 // Verify other students do not have the ability to edit submissions for other users.
3318 $this->assertTrue($assign->can_edit_submission($student2->id
, $student2->id
));
3319 $this->assertFalse($assign->can_edit_submission($student1->id
, $student2->id
));
3320 $this->assertFalse($assign->can_edit_submission($student3->id
, $student2->id
));
3321 $this->assertFalse($assign->can_edit_submission($student4->id
, $student2->id
));
3325 * Test if the view blind details capability works
3327 public function test_can_view_blind_details() {
3330 $this->resetAfterTest();
3331 $course = $this->getDataGenerator()->create_course();
3332 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3333 $manager = $this->getDataGenerator()->create_and_enrol($course, 'manager');
3334 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3336 $assign = $this->create_instance($course, [
3337 'blindmarking' => 1,
3340 $this->assertTrue($assign->is_blind_marking());
3342 // Test student names are hidden to teacher.
3343 $this->setUser($teacher);
3344 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
3345 $output = $assign->get_renderer()->render($gradingtable);
3346 $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign'))); // "Participant" is somewhere on the page.
3347 $this->assertEquals(false, strpos($output, fullname($student))); // Students full name doesn't appear.
3349 // Test student names are visible to manager.
3350 $this->setUser($manager);
3351 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
3352 $output = $assign->get_renderer()->render($gradingtable);
3353 $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));
3354 $this->assertEquals(true, strpos($output, fullname($student)));
3358 * Testing get_shared_group_members
3360 public function test_get_shared_group_members() {
3361 $this->resetAfterTest();
3362 $course = $this->getDataGenerator()->create_course();
3363 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3365 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3366 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3367 $student3 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3368 $student4 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3370 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
3371 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
3372 groups_assign_grouping($grouping->id
, $group1->id
);
3373 groups_add_member($group1, $student1);
3374 groups_add_member($group1, $student2);
3376 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
3377 groups_assign_grouping($grouping->id
, $group2->id
);
3378 groups_add_member($group2, $student3);
3379 groups_add_member($group2, $student4);
3381 $assign = $this->create_instance($course, [
3382 'groupingid' => $grouping->id
,
3383 'groupmode' => SEPARATEGROUPS
,
3386 $cm = $assign->get_course_module();
3388 // Get shared group members for students 0 and 1.
3389 $groupmembers = $assign->get_shared_group_members($cm, $student1->id
);
3390 $this->assertCount(2, $groupmembers);
3391 $this->assertContainsEquals($student1->id
, $groupmembers);
3392 $this->assertContainsEquals($student2->id
, $groupmembers);
3394 $groupmembers = $assign->get_shared_group_members($cm, $student2->id
);
3395 $this->assertCount(2, $groupmembers);
3396 $this->assertContainsEquals($student1->id
, $groupmembers);
3397 $this->assertContainsEquals($student2->id
, $groupmembers);
3399 $groupmembers = $assign->get_shared_group_members($cm, $student3->id
);
3400 $this->assertCount(2, $groupmembers);
3401 $this->assertContainsEquals($student3->id
, $groupmembers);
3402 $this->assertContainsEquals($student4->id
, $groupmembers);
3404 $groupmembers = $assign->get_shared_group_members($cm, $student4->id
);
3405 $this->assertCount(2, $groupmembers);
3406 $this->assertContainsEquals($student3->id
, $groupmembers);
3407 $this->assertContainsEquals($student4->id
, $groupmembers);
3411 * Testing get_shared_group_members
3413 public function test_get_shared_group_members_override() {
3414 $this->resetAfterTest();
3415 $course = $this->getDataGenerator()->create_course();
3416 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3418 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3419 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3420 $student3 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3421 $student4 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3423 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
3424 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
3425 groups_assign_grouping($grouping->id
, $group1->id
);
3426 groups_add_member($group1, $student1);
3427 groups_add_member($group1, $student2);
3429 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
3430 groups_assign_grouping($grouping->id
, $group2->id
);
3431 groups_add_member($group2, $student3);
3432 groups_add_member($group2, $student4);
3434 $assign = $this->create_instance($course, [
3435 'groupingid' => $grouping->id
,
3436 'groupmode' => SEPARATEGROUPS
,
3439 $cm = $assign->get_course_module();
3441 // Add the capability to access allgroups for one of the students.
3442 $roleid = create_role('Access all groups role', 'accessallgroupsrole', '');
3443 assign_capability('moodle/site:accessallgroups', CAP_ALLOW
, $roleid, $assign->get_context()->id
);
3444 role_assign($roleid, $student1->id
, $assign->get_context()->id
);
3445 accesslib_clear_all_caches_for_unit_testing();
3447 // Get shared group members for students 0 and 1.
3448 $groupmembers = $assign->get_shared_group_members($cm, $student1->id
);
3449 $this->assertCount(4, $groupmembers);
3450 $this->assertContainsEquals($student1->id
, $groupmembers);
3451 $this->assertContainsEquals($student2->id
, $groupmembers);
3452 $this->assertContainsEquals($student3->id
, $groupmembers);
3453 $this->assertContainsEquals($student4->id
, $groupmembers);
3455 $groupmembers = $assign->get_shared_group_members($cm, $student2->id
);
3456 $this->assertCount(2, $groupmembers);
3457 $this->assertContainsEquals($student1->id
, $groupmembers);
3458 $this->assertContainsEquals($student2->id
, $groupmembers);
3460 $groupmembers = $assign->get_shared_group_members($cm, $student3->id
);
3461 $this->assertCount(2, $groupmembers);
3462 $this->assertContainsEquals($student3->id
, $groupmembers);
3463 $this->assertContainsEquals($student4->id
, $groupmembers);
3465 $groupmembers = $assign->get_shared_group_members($cm, $student4->id
);
3466 $this->assertCount(2, $groupmembers);
3467 $this->assertContainsEquals($student3->id
, $groupmembers);
3468 $this->assertContainsEquals($student4->id
, $groupmembers);
3472 * Test get plugins file areas
3474 public function test_get_plugins_file_areas() {
3477 $this->resetAfterTest();
3478 $course = $this->getDataGenerator()->create_course();
3479 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3481 $assign = $this->create_instance($course);
3483 // Test that all the submission and feedback plugins are returning the expected file aras.
3485 $coreplugins = core_plugin_manager
::standard_plugins_list('assignsubmission');
3486 foreach ($assign->get_submission_plugins() as $plugin) {
3487 $type = $plugin->get_type();
3488 if (!in_array($type, $coreplugins)) {
3491 $fileareas = $plugin->get_file_areas();
3493 if ($type == 'onlinetext') {
3494 $this->assertEquals(array('submissions_onlinetext' => 'Online text'), $fileareas);
3496 } else if ($type == 'file') {
3497 $this->assertEquals(array('submission_files' => 'File submissions'), $fileareas);
3500 $this->assertEmpty($fileareas);
3503 $this->assertEquals(2, $usingfilearea);
3506 $coreplugins = core_plugin_manager
::standard_plugins_list('assignfeedback');
3507 foreach ($assign->get_feedback_plugins() as $plugin) {
3508 $type = $plugin->get_type();
3509 if (!in_array($type, $coreplugins)) {
3512 $fileareas = $plugin->get_file_areas();
3514 if ($type == 'editpdf') {
3515 $this->assertEquals(array('download' => 'Annotate PDF'), $fileareas);
3517 } else if ($type == 'file') {
3518 $this->assertEquals(array('feedback_files' => 'Feedback files'), $fileareas);
3520 } else if ($type == 'comments') {
3521 $this->assertEquals(array('feedback' => 'Feedback comments'), $fileareas);
3524 $this->assertEmpty($fileareas);
3527 $this->assertEquals(3, $usingfilearea);
3531 * Test override exists
3533 * This function needs to obey the group override logic as per the assign grading table and
3534 * the overview block.
3536 public function test_override_exists() {
3539 $this->resetAfterTest();
3540 $course = $this->getDataGenerator()->create_course();
3541 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3543 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
3544 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
3545 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
3549 // - student1 => group A only
3550 // - student2 => group B only
3551 // - student3 => Group A + Group B (No user override)
3552 // - student4 => Group A + Group B (With user override)
3553 // - student4 => No groups.
3554 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3555 groups_add_member($group1, $student1);
3557 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3558 groups_add_member($group2, $student2);
3560 $student3 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3561 groups_add_member($group1, $student3);
3562 groups_add_member($group2, $student3);
3564 $student4 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3565 groups_add_member($group1, $student4);
3566 groups_add_member($group2, $student4);
3568 $student5 = $this->getDataGenerator()->create_and_enrol($course, 'student');
3570 $assign = $this->create_instance($course);
3571 $instance = $assign->get_instance();
3573 // Overrides for each of the groups, and a user override.
3576 // Override for group 1, highest priority (numerically lowest sortorder).
3577 'assignid' => $instance->id
,
3578 'groupid' => $group1->id
,
3581 'allowsubmissionsfromdate' => 1,
3586 // Override for group 2, lower priority (numerically higher sortorder).
3587 'assignid' => $instance->id
,
3588 'groupid' => $group2->id
,
3591 'allowsubmissionsfromdate' => 5,
3597 'assignid' => $instance->id
,
3599 'userid' => $student3->id
,
3600 'sortorder' => null,
3601 'allowsubmissionsfromdate' => 7,
3607 foreach ($overrides as &$override) {
3608 $override->id
= $DB->insert_record('assign_overrides', $override);
3611 // User only in group 1 should see the group 1 override.
3612 $this->assertEquals($overrides[0], $assign->override_exists($student1->id
));
3614 // User only in group 2 should see the group 2 override.
3615 $this->assertEquals($overrides[1], $assign->override_exists($student2->id
));
3617 // User only in both groups with an override should see the user override as it has higher priority.
3618 $this->assertEquals($overrides[2], $assign->override_exists($student3->id
));
3620 // User only in both groups with no override should see the group 1 override as it has higher priority.
3621 $this->assertEquals($overrides[0], $assign->override_exists($student4->id
));
3623 // User with no overrides shoudl get nothing.
3624 $override = $assign->override_exists($student5->id
);
3625 $this->assertNull($override->duedate
);
3626 $this->assertNull($override->cutoffdate
);
3627 $this->assertNull($override->allowsubmissionsfromdate
);
3631 * Test the quicksave grades processor
3633 public function test_process_save_quick_grades() {
3634 $this->resetAfterTest();
3635 $course = $this->getDataGenerator()->create_course();
3636 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3637 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3639 $teacher->ignoresesskey
= true;
3640 $this->setUser($teacher);
3641 $assign = $this->create_instance($course, [
3642 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL
,
3645 // Initially grade the user.
3647 'attemptnumber' => '',
3648 'timemodified' => '',
3651 "grademodified_{$student->id}" => $grade->timemodified
,
3652 "gradeattempt_{$student->id}" => $grade->attemptnumber
,
3653 "quickgrade_{$student->id}" => '60.0',
3656 $result = $assign->testable_process_save_quick_grades($data);
3657 $this->assertStringContainsString(get_string('quickgradingchangessaved', 'assign'), $result);
3658 $grade = $assign->get_user_grade($student->id
, false);
3659 $this->assertEquals(60.0, $grade->grade
);
3661 // Attempt to grade with a past attempts grade info.
3662 $assign->testable_process_add_attempt($student->id
);
3664 'grademodified_' . $student->id
=> $grade->timemodified
,
3665 'gradeattempt_' . $student->id
=> $grade->attemptnumber
,
3666 'quickgrade_' . $student->id
=> '50.0'
3668 $result = $assign->testable_process_save_quick_grades($data);
3669 $this->assertStringContainsString(get_string('errorrecordmodified', 'assign'), $result);
3670 $grade = $assign->get_user_grade($student->id
, false);
3671 $this->assertFalse($grade);
3673 // Attempt to grade a the attempt.
3674 $submission = $assign->get_user_submission($student->id
, false);
3676 'grademodified_' . $student->id
=> '',
3677 'gradeattempt_' . $student->id
=> $submission->attemptnumber
,
3678 'quickgrade_' . $student->id
=> '40.0'
3680 $result = $assign->testable_process_save_quick_grades($data);
3681 $this->assertStringContainsString(get_string('quickgradingchangessaved', 'assign'), $result);
3682 $grade = $assign->get_user_grade($student->id
, false);
3683 $this->assertEquals(40.0, $grade->grade
);
3685 // Catch grade update conflicts.
3686 // Save old data for later.
3688 // Update the grade the 'good' way.
3690 'grademodified_' . $student->id
=> $grade->timemodified
,
3691 'gradeattempt_' . $student->id
=> $grade->attemptnumber
,
3692 'quickgrade_' . $student->id
=> '30.0'
3694 $result = $assign->testable_process_save_quick_grades($data);
3695 $this->assertStringContainsString(get_string('quickgradingchangessaved', 'assign'), $result);
3696 $grade = $assign->get_user_grade($student->id
, false);
3697 $this->assertEquals(30.0, $grade->grade
);
3699 // Now update using 'old' data. Should fail.
3700 $result = $assign->testable_process_save_quick_grades($pastdata);
3701 $this->assertStringContainsString(get_string('errorrecordmodified', 'assign'), $result);
3702 $grade = $assign->get_user_grade($student->id
, false);
3703 $this->assertEquals(30.0, $grade->grade
);
3707 * Test updating activity completion when submitting an assessment.
3709 public function test_update_activity_completion_records_solitary_submission() {
3710 $this->resetAfterTest();
3712 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
3713 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3714 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3716 $this->setUser($teacher);
3717 $assign = $this->create_instance($course, [
3719 'completion' => COMPLETION_TRACKING_AUTOMATIC
,
3720 'requireallteammemberssubmit' => 0,
3722 $cm = $assign->get_course_module();
3724 // Submit the assignment as the student.
3725 $this->add_submission($student, $assign);
3727 // Check that completion is not met yet.
3728 $completion = new completion_info($course);
3729 $completiondata = $completion->get_data($cm, false, $student->id
);
3730 $this->assertEquals(0, $completiondata->completionstate
);
3732 // Update to mark as complete.
3733 $submission = $assign->get_user_submission($student->id
, true);
3734 $assign->testable_update_activity_completion_records(0, 0, $submission,
3735 $student->id
, COMPLETION_COMPLETE
, $completion);
3737 // Completion should now be met.
3738 $completiondata = $completion->get_data($cm, false, $student->id
);
3739 $this->assertEquals(1, $completiondata->completionstate
);
3743 * Test updating activity completion when submitting an assessment.
3745 public function test_update_activity_completion_records_team_submission() {
3746 $this->resetAfterTest();
3748 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
3749 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3750 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3751 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
3753 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
3754 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
3756 groups_add_member($group1, $student);
3757 groups_add_member($group1, $otherstudent);
3759 $assign = $this->create_instance($course, [
3761 'completion' => COMPLETION_TRACKING_AUTOMATIC
,
3762 'teamsubmission' => 1,
3765 $cm = $assign->get_course_module();
3767 $this->add_submission($student, $assign);
3768 $this->submit_for_grading($student, $assign, ['groupid' => $group1->id
]);
3770 $completion = new completion_info($course);
3772 // Check that completion is not met yet.
3773 $completiondata = $completion->get_data($cm, false, $student->id
);
3774 $this->assertEquals(0, $completiondata->completionstate
);
3776 $completiondata = $completion->get_data($cm, false, $otherstudent->id
);
3777 $this->assertEquals(0, $completiondata->completionstate
);
3779 $submission = $assign->get_user_submission($student->id
, true);
3780 $submission->status
= ASSIGN_SUBMISSION_STATUS_SUBMITTED
;
3781 $submission->groupid
= $group1->id
;
3783 $assign->testable_update_activity_completion_records(1, 0, $submission, $student->id
, COMPLETION_COMPLETE
, $completion);
3785 // Completion should now be met.
3786 $completiondata = $completion->get_data($cm, false, $student->id
);
3787 $this->assertEquals(1, $completiondata->completionstate
);
3789 $completiondata = $completion->get_data($cm, false, $otherstudent->id
);
3790 $this->assertEquals(1, $completiondata->completionstate
);
3794 * Test updating activity completion when submitting an assessment for MDL-67126.
3796 public function test_update_activity_completion_records_team_submission_new() {
3797 $this->resetAfterTest();
3799 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
3800 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3801 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3802 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
3804 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id
));
3805 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
3807 groups_add_member($group1, $student);
3808 groups_add_member($group1, $otherstudent);
3810 $assign = $this->create_instance($course, [
3811 'submissiondrafts' => 0,
3812 'completion' => COMPLETION_TRACKING_AUTOMATIC
,
3813 'completionsubmit' => 1,
3814 'teamsubmission' => 1,
3815 'assignsubmission_onlinetext_enabled' => 1
3818 $cm = $assign->get_course_module();
3820 $this->add_submission($student, $assign);
3822 $completion = new completion_info($course);
3824 // Completion should now be met.
3825 $completiondata = $completion->get_data($cm, false, $student->id
);
3826 $this->assertEquals(1, $completiondata->completionstate
);
3828 $completiondata = $completion->get_data($cm, false, $otherstudent->id
);
3829 $this->assertEquals(1, $completiondata->completionstate
);
3833 * Data provider for test_fix_null_grades
3834 * @return array[] Test data for test_fix_null_grades. Each element should contain grade, expectedcount and gradebookvalue
3836 public function fix_null_grades_provider() {
3838 'Negative less than one is errant' => [
3840 'gradebookvalue' => null,
3842 'Negative more than one is errant' => [
3844 'gradebookvalue' => null,
3846 'Negative one exactly is not errant, but shouldn\'t be pushed to gradebook' => [
3847 'grade' => ASSIGN_GRADE_NOT_SET
,
3848 'gradebookvalue' => null,
3850 'Positive grade is not errant' => [
3852 'gradebookvalue' => 1,
3854 'Large grade is not errant' => [
3856 'gradebookvalue' => 100,
3858 'Zero grade is not errant' => [
3860 'gradebookvalue' => 0,
3866 * Test fix_null_grades
3867 * @param number $grade The grade we should set in the assign grading table.
3868 * @param number $expectedcount The finalgrade we expect in the gradebook after fixing the grades.
3869 * @dataProvider fix_null_grades_provider
3871 public function test_fix_null_grades($grade, $gradebookvalue) {
3874 $this->resetAfterTest();
3876 $course = $this->getDataGenerator()->create_course();
3877 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3878 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3880 $this->setUser($teacher);
3881 $assign = $this->create_instance($course);
3883 // Try getting a student's grade. This will give a grade of -1.
3884 // Then we can override it with a bad negative grade.
3885 $assign->get_user_grade($student->id
, true);
3887 // Set the grade to something errant.
3888 // We don't set the grader here, so we expect it to be -1 as a result.
3894 'userid' => $student->id
,
3895 'assignment' => $assign->get_instance()->id
,
3898 $assign->grade
= $grade;
3899 $assigntemp = clone $assign->get_instance();
3900 $assigntemp->cmidnumber
= $assign->get_course_module()->idnumber
;
3901 assign_update_grades($assigntemp);
3903 // Check that the gradebook was updated with the assign grade. So we can guarentee test results later on.
3904 $expectedgrade = $grade == -1 ?
null : $grade; // Assign sends null to the gradebook for -1 grades.
3905 $gradegrade = grade_grade
::fetch(array('userid' => $student->id
, 'itemid' => $assign->get_grade_item()->id
));
3906 $this->assertEquals(-1, $gradegrade->usermodified
);
3907 $this->assertEquals($expectedgrade, $gradegrade->rawgrade
);
3909 // Call fix_null_grades().
3910 $method = new ReflectionMethod(assign
::class, 'fix_null_grades');
3911 $method->setAccessible(true);
3912 $result = $method->invoke($assign);
3914 $this->assertSame(true, $result);
3916 $gradegrade = grade_grade
::fetch(array('userid' => $student->id
, 'itemid' => $assign->get_grade_item()->id
));
3918 $this->assertEquals(-1, $gradegrade->usermodified
);
3919 $this->assertEquals($gradebookvalue, $gradegrade->finalgrade
);
3921 // Check that the grade was updated in the gradebook by fix_null_grades.
3922 $this->assertEquals($gradebookvalue, $gradegrade->finalgrade
);
3926 * Test grade override displays 'Graded' for students
3928 public function test_grade_submission_override() {
3929 global $DB, $PAGE, $OUTPUT;
3931 $this->resetAfterTest();
3933 $course = $this->getDataGenerator()->create_course();
3934 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3935 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3937 $this->setUser($teacher);
3938 $assign = $this->create_instance($course, [
3939 'assignsubmission_onlinetext_enabled' => 1,
3942 // Simulate adding a grade.
3943 $this->setUser($teacher);
3944 $data = new stdClass();
3945 $data->grade
= '50.0';
3946 $assign->testable_apply_grade_to_user($data, $student->id
, 0);
3948 // Set grade override.
3949 $gradegrade = grade_grade
::fetch([
3950 'userid' => $student->id
,
3951 'itemid' => $assign->get_grade_item()->id
,
3954 // Check that grade submission is not overridden yet.
3955 $this->assertEquals(false, $gradegrade->is_overridden());
3957 // Simulate a submission.
3958 $this->setUser($student);
3959 $submission = $assign->get_user_submission($student->id
, true);
3961 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id
]));
3963 // Set override grade grade, and check that grade submission has been overridden.
3964 $gradegrade->set_overridden(true);
3965 $this->assertEquals(true, $gradegrade->is_overridden());
3967 // Check that submissionslocked message 'This assignment is not accepting submissions' does not appear for student.
3968 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
3969 $output = $assign->get_renderer()->render($gradingtable);
3970 $this->assertStringContainsString(get_string('submissionstatus_', 'assign'), $output);
3972 $assignsubmissionstatus = $assign->get_assign_submission_status_renderable($student, true);
3973 $output2 = $assign->get_renderer()->render($assignsubmissionstatus);
3975 // Check that submissionslocked 'This assignment is not accepting submissions' message does not appear for student.
3976 $this->assertStringNotContainsString(get_string('submissionslocked', 'assign'), $output2);
3977 // Check that submissionstatus_marked 'Graded' message does appear for student.
3978 $this->assertStringContainsString(get_string('submissionstatus_marked', 'assign'), $output2);
3982 * Test the result of get_filters is consistent.
3984 public function test_get_filters() {
3985 $this->resetAfterTest();
3987 $course = $this->getDataGenerator()->create_course();
3988 $assign = $this->create_instance($course);
3989 $valid = $assign->get_filters();
3991 $this->assertEquals(count($valid), 6);
3995 * Test assign->get_instance() for a number of cases, as defined in the data provider.
3997 * @dataProvider assign_get_instance_provider
3998 * @param array $courseconfig the config to use when creating the course.
3999 * @param array $assignconfig the config to use when creating the assignment.
4000 * @param array $enrolconfig the config to use when enrolling the user (this will be the active user).
4001 * @param array $expectedproperties an map containing the expected names and values for the assign instance data.
4003 public function test_assign_get_instance(array $courseconfig, array $assignconfig, array $enrolconfig,
4004 array $expectedproperties) {
4005 $this->resetAfterTest();
4007 set_config('enablecourserelativedates', true); // Enable relative dates at site level.
4009 $course = $this->getDataGenerator()->create_course($courseconfig);
4010 $assign = $this->create_instance($course, $assignconfig);
4011 $user = $this->getDataGenerator()->create_and_enrol($course, ...array_values($enrolconfig));
4013 $instance = $assign->get_instance($user->id
);
4015 foreach ($expectedproperties as $propertyname => $propertyval) {
4016 $this->assertEquals($propertyval, $instance->$propertyname);
4021 * The test_assign_get_instance data provider.
4023 public function assign_get_instance_provider() {
4026 // The get_default_instance() method shouldn't calculate any properties per-user. It should just return the record data.
4027 // We'll confirm this works for a few different user types anyway, just like we do for get_instance().
4029 'Teacher whose enrolment starts after the course start date, relative dates mode enabled' => [
4030 'courseconfig' => ['relativedatesmode' => true, 'startdate' => $timenow - 10 * DAYSECS
],
4031 'assignconfig' => ['duedate' => $timenow +
4 * DAYSECS
],
4032 'enrolconfig' => ['shortname' => 'teacher', 'userparams' => null, 'method' => 'manual',
4033 'startdate' => $timenow - 8 * DAYSECS
],
4034 'expectedproperties' => ['duedate' => $timenow +
6 * DAYSECS
]
4036 'Teacher whose enrolment starts before the course start date, relative dates mode enabled' => [
4037 'courseconfig' => ['relativedatesmode' => true, 'startdate' => $timenow - 10 * DAYSECS
],
4038 'assignconfig' => ['duedate' => $timenow +
4 * DAYSECS
],
4039 'enrolconfig' => ['shortname' => 'teacher', 'userparams' => null, 'method' => 'manual',
4040 'startdate' => $timenow - 12 * DAYSECS
],
4041 'expectedproperties' => ['duedate' => $timenow +
4 * DAYSECS
]
4043 'Teacher whose enrolment starts after the course start date, relative dates mode disabled' => [
4044 'courseconfig' => ['relativedatesmode' => false, 'startdate' => $timenow - 10 * DAYSECS
],
4045 'assignconfig' => ['duedate' => $timenow +
4 * DAYSECS
],
4046 'enrolconfig' => ['shortname' => 'teacher', 'userparams' => null, 'method' => 'manual',
4047 'startdate' => $timenow - 8 * DAYSECS
],
4048 'expectedproperties' => ['duedate' => $timenow +
4 * DAYSECS
]
4050 'Student whose enrolment starts after the course start date, relative dates mode enabled' => [
4051 'courseconfig' => ['relativedatesmode' => true, 'startdate' => $timenow - 10 * DAYSECS
],
4052 'assignconfig' => ['duedate' => $timenow +
4 * DAYSECS
],
4053 'enrolconfig' => ['shortname' => 'student', 'userparams' => null, 'method' => 'manual',
4054 'startdate' => $timenow - 8 * DAYSECS
],
4055 'expectedproperties' => ['duedate' => $timenow +
6 * DAYSECS
]
4057 'Student whose enrolment starts before the course start date, relative dates mode enabled' => [
4058 'courseconfig' => ['relativedatesmode' => true, 'startdate' => $timenow - 10 * DAYSECS
],
4059 'assignconfig' => ['duedate' => $timenow +
4 * DAYSECS
],
4060 'enrolconfig' => ['shortname' => 'student', 'userparams' => null, 'method' => 'manual',
4061 'startdate' => $timenow - 12 * DAYSECS
],
4062 'expectedproperties' => ['duedate' => $timenow +
4 * DAYSECS
]
4064 'Student whose enrolment starts after the course start date, relative dates mode disabled' => [
4065 'courseconfig' => ['relativedatesmode' => false, 'startdate' => $timenow - 10 * DAYSECS
],
4066 'assignconfig' => ['duedate' => $timenow +
4 * DAYSECS
],
4067 'enrolconfig' => ['shortname' => 'student', 'userparams' => null, 'method' => 'manual',
4068 'startdate' => $timenow - 8 * DAYSECS
],
4069 'expectedproperties' => ['duedate' => $timenow +
4 * DAYSECS
]
4075 * Test assign->get_default_instance() for a number of cases, as defined in the date provider.
4077 * @dataProvider assign_get_default_instance_provider
4078 * @param array $courseconfig the config to use when creating the course.
4079 * @param array $assignconfig the config to use when creating the assignment.
4080 * @param array $enrolconfig the config to use when enrolling the user (this will be the active user).
4081 * @param array $expectedproperties an map containing the expected names and values for the assign instance data.
4083 public function test_assign_get_default_instance(array $courseconfig, array $assignconfig, array $enrolconfig,
4084 array $expectedproperties) {
4085 $this->resetAfterTest();
4087 set_config('enablecourserelativedates', true); // Enable relative dates at site level.
4089 $course = $this->getDataGenerator()->create_course($courseconfig);
4090 $assign = $this->create_instance($course, $assignconfig);
4091 $user = $this->getDataGenerator()->create_and_enrol($course, ...array_values($enrolconfig));
4093 $this->setUser($user);
4094 $defaultinstance = $assign->get_default_instance();
4096 foreach ($expectedproperties as $propertyname => $propertyval) {
4097 $this->assertEquals($propertyval, $defaultinstance->$propertyname);
4102 * The test_assign_get_default_instance data provider.
4104 public function assign_get_default_instance_provider() {
4107 // The get_default_instance() method shouldn't calculate any properties per-user. It should just return the record data.
4108 // We'll confirm this works for a few different user types anyway, just like we do for get_instance().
4110 'Teacher whose enrolment starts after the course start date, relative dates mode enabled' => [
4111 'courseconfig' => ['relativedatesmode' => true, 'startdate' => $timenow - 10 * DAYSECS
],
4112 'assignconfig' => ['duedate' => $timenow +
4 * DAYSECS
],
4113 'enrolconfig' => ['shortname' => 'teacher', 'userparams' => null, 'method' => 'manual',
4114 'startdate' => $timenow - 8 * DAYSECS
],
4115 'expectedproperties' => ['duedate' => $timenow +
4 * DAYSECS
]
4117 'Teacher whose enrolment starts before the course start date, relative dates mode enabled' => [
4118 'courseconfig' => ['relativedatesmode' => true, 'startdate' => $timenow - 10 * DAYSECS
],
4119 'assignconfig' => ['duedate' => $timenow +
4 * DAYSECS
],
4120 'enrolconfig' => ['shortname' => 'teacher', 'userparams' => null, 'method' => 'manual',
4121 'startdate' => $timenow - 12 * DAYSECS
],
4122 'expectedproperties' => ['duedate' => $timenow +
4 * DAYSECS
]
4124 'Teacher whose enrolment starts after the course start date, relative dates mode disabled' => [
4125 'courseconfig' => ['relativedatesmode' => false, 'startdate' => $timenow - 10 * DAYSECS
],
4126 'assignconfig' => ['duedate' => $timenow +
4 * DAYSECS
],
4127 'enrolconfig' => ['shortname' => 'teacher', 'userparams' => null, 'method' => 'manual',
4128 'startdate' => $timenow - 8 * DAYSECS
],
4129 'expectedproperties' => ['duedate' => $timenow +
4 * DAYSECS
]
4131 'Student whose enrolment starts after the course start date, relative dates mode enabled' => [
4132 'courseconfig' => ['relativedatesmode' => true, 'startdate' => $timenow - 10 * DAYSECS
],
4133 'assignconfig' => ['duedate' => $timenow +
4 * DAYSECS
],
4134 'enrolconfig' => ['shortname' => 'student', 'userparams' => null, 'method' => 'manual',
4135 'startdate' => $timenow - 8 * DAYSECS
],
4136 'expectedproperties' => ['duedate' => $timenow +
4 * DAYSECS
]
4142 * Test showing group override duedate for admin
4144 public function test_view_group_override() {
4147 $this->resetAfterTest();
4148 $course = $this->getDataGenerator()->create_course();
4150 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
4151 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id
]);
4153 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
4154 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
4155 groups_add_member($group1, $student1);
4156 groups_add_member($group1, $teacher);
4158 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
4159 groups_add_member($group2, $student2);
4161 $assign = $this->create_instance($course, [
4163 'duedate' => 1558999899,
4165 $instance = $assign->get_instance();
4167 // Overrides for two groups.
4170 'assignid' => $instance->id
,
4171 'groupid' => $group1->id
,
4174 'duedate' => 1568990258,
4177 'assignid' => $instance->id
,
4178 'groupid' => $group2->id
,
4181 'duedate' => 1559900258,
4185 foreach ($overrides as &$override) {
4186 $override->id
= $DB->insert_record('assign_overrides', $override);
4189 $currenturl = new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id
));
4190 $PAGE->set_url($currenturl);
4192 // Other users should see duedate of the assignment.
4193 $this->setUser($student2);
4194 $summary = $assign->get_assign_grading_summary_renderable($group1->id
);
4195 $output1 .= $assign->get_renderer()->render($summary);
4196 $this->assertStringContainsStringIgnoringCase('Tuesday, 28 May 2019, 7:31 AM', $output1);
4199 // Teacher should be able to see all group override duedate.
4200 $this->setUser($teacher);
4201 $summary = $assign->get_assign_grading_summary_renderable($group1->id
);
4202 $output2 .= $assign->get_renderer()->render($summary);
4203 $this->assertStringContainsStringIgnoringCase('Friday, 20 September 2019, 10:37 PM', $output2);
4204 $summary = $assign->get_assign_grading_summary_renderable($group2->id
);
4206 $output3 .= $assign->get_renderer()->render($summary);
4207 $this->assertStringContainsStringIgnoringCase('Friday, 7 June 2019, 5:37 PM', $output3);
4211 * Test that cron task uses task API to get its last run time.
4213 public function test_cron_use_task_api_to_get_lastruntime() {
4215 $this->resetAfterTest();
4216 $course = $this->getDataGenerator()->create_course();
4218 // Create an assignment which allows submissions from 3 days ago.
4219 $assign1 = $this->create_instance($course, [
4220 'duedate' => time() + DAYSECS
,
4221 'alwaysshowdescription' => 0,
4222 'allowsubmissionsfromdate' => time() - 3 * DAYSECS
,
4223 'intro' => 'This one should not be re-created',
4226 // Create an assignment which allows submissions from 1 day ago.
4227 $assign2 = $this->create_instance($course, [
4228 'duedate' => time() + DAYSECS
,
4229 'alwaysshowdescription' => 0,
4230 'allowsubmissionsfromdate' => time() - DAYSECS
,
4231 'intro' => 'This one should be re-created',
4234 // Set last run time 2 days ago.
4235 $DB->set_field('task_scheduled', 'lastruntime', time() - 2 * DAYSECS
, ['classname' => '\mod_assign\task\cron_task']);
4237 // Remove events to make sure cron will update calendar and re-create one of them.
4238 $params = array('modulename' => 'assign', 'instance' => $assign1->get_instance()->id
);
4239 $DB->delete_records('event', $params);
4240 $params = array('modulename' => 'assign', 'instance' => $assign2->get_instance()->id
);
4241 $DB->delete_records('event', $params);
4246 // Assert that calendar hasn't been updated for the first assignment as it's supposed to be
4247 // updated as part of previous cron runs (allowsubmissionsfromdate is less than lastruntime).
4248 $params = array('modulename' => 'assign', 'instance' => $assign1->get_instance()->id
);
4249 $event1 = $DB->get_record('event', $params);
4250 $this->assertEmpty($event1);
4252 // Assert that calendar has been updated for the second assignment
4253 // because its allowsubmissionsfromdate is greater than lastruntime.
4254 $params = array('modulename' => 'assign', 'instance' => $assign2->get_instance()->id
);
4255 $event2 = $DB->get_record('event', $params);
4256 $this->assertNotEmpty($event2);
4257 $this->assertSame('This one should be re-created', $event2->description
);