MDL-55609 testing: Add a create_and_enrol helper
[moodle.git] / mod / assign / tests / locallib_test.php
CommitLineData
47f48152
DW
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Unit tests for (some of) mod/assign/locallib.php.
19 *
20 * @package mod_assign
21 * @category phpunit
22 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26
27defined('MOODLE_INTERNAL') || die();
28
29global $CFG;
30require_once($CFG->dirroot . '/mod/assign/locallib.php');
31require_once($CFG->dirroot . '/mod/assign/upgradelib.php');
9c986ee0 32require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
47f48152
DW
33
34/**
35 * Unit tests for (some of) mod/assign/locallib.php.
36 *
37 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 */
9c986ee0 40class mod_assign_locallib_testcase extends mod_assign_base_testcase {
47f48152
DW
41
42 public function test_return_links() {
c2114099 43 global $PAGE;
47f48152
DW
44 $this->setUser($this->editingteachers[0]);
45 $returnaction = 'RETURNACTION';
c2114099 46 $returnparams = array('param'=>'1');
47f48152 47 $assign = $this->create_instance();
c2114099 48 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
47f48152
DW
49 $assign->register_return_link($returnaction, $returnparams);
50 $this->assertEquals($returnaction, $assign->get_return_action());
51 $this->assertEquals($returnparams, $assign->get_return_params());
52 }
53
54 public function test_get_feedback_plugins() {
55 $this->setUser($this->editingteachers[0]);
56 $assign = $this->create_instance();
bd3b3bba 57 $installedplugins = array_keys(core_component::get_plugin_list('assignfeedback'));
11527706
DW
58
59 foreach ($assign->get_feedback_plugins() as $plugin) {
60 $this->assertContains($plugin->get_type(), $installedplugins, 'Feedback plugin not in list of installed plugins');
61 }
47f48152
DW
62 }
63
64 public function test_get_submission_plugins() {
65 $this->setUser($this->editingteachers[0]);
66 $assign = $this->create_instance();
bd3b3bba 67 $installedplugins = array_keys(core_component::get_plugin_list('assignsubmission'));
11527706
DW
68
69 foreach ($assign->get_submission_plugins() as $plugin) {
70 $this->assertContains($plugin->get_type(), $installedplugins, 'Submission plugin not in list of installed plugins');
71 }
47f48152
DW
72 }
73
74 public function test_is_blind_marking() {
75 $this->setUser($this->editingteachers[0]);
76 $assign = $this->create_instance(array('blindmarking'=>1));
77 $this->assertEquals(true, $assign->is_blind_marking());
78
79 // Test cannot see student names.
80 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
81 $output = $assign->get_renderer()->render($gradingtable);
82 $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));
83
84 // Test students cannot reveal identities.
85 $nopermission = false;
05a6445a 86 $this->students[0]->ignoresesskey = true;
47f48152 87 $this->setUser($this->students[0]);
52f3e060 88 $this->expectException('required_capability_exception');
05a6445a
DW
89 $assign->reveal_identities();
90 $this->students[0]->ignoresesskey = false;
47f48152
DW
91
92 // Test teachers cannot reveal identities.
93 $nopermission = false;
05a6445a 94 $this->teachers[0]->ignoresesskey = true;
47f48152 95 $this->setUser($this->teachers[0]);
52f3e060 96 $this->expectException('required_capability_exception');
05a6445a
DW
97 $assign->reveal_identities();
98 $this->teachers[0]->ignoresesskey = false;
47f48152
DW
99
100 // Test sesskey is required.
47f48152 101 $this->setUser($this->editingteachers[0]);
52f3e060 102 $this->expectException('moodle_exception');
05a6445a 103 $assign->reveal_identities();
47f48152
DW
104
105 // Test editingteacher can reveal identities if sesskey is ignored.
106 $this->editingteachers[0]->ignoresesskey = true;
107 $this->setUser($this->editingteachers[0]);
05a6445a 108 $assign->reveal_identities();
47f48152 109 $this->assertEquals(false, $assign->is_blind_marking());
05a6445a 110 $this->editingteachers[0]->ignoresesskey = false;
47f48152
DW
111
112 // Test student names are visible.
113 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
114 $output = $assign->get_renderer()->render($gradingtable);
115 $this->assertEquals(false, strpos($output, get_string('hiddenuser', 'assign')));
116
117 // Set this back to default.
118 $this->editingteachers[0]->ignoresesskey = false;
119 }
120
80989850
BH
121 /**
122 * Data provider for test_get_assign_perpage
123 *
124 * @return array Provider data
125 */
126 public function get_assign_perpage_provider() {
127 return array(
128 array(
129 'maxperpage' => -1,
130 'userprefs' => array(
131 -1 => -1,
132 10 => 10,
133 20 => 20,
134 50 => 50,
135 ),
136 ),
137 array(
138 'maxperpage' => 15,
139 'userprefs' => array(
140 -1 => 15,
141 10 => 10,
142 20 => 15,
143 50 => 15,
144 ),
145 ),
146 );
147 }
148
149 /**
150 * Test maxperpage
151 *
152 * @dataProvider get_assign_perpage_provider
153 * @param integer $maxperpage site config value
154 * @param array $userprefs Array of user preferences and expected page sizes
155 */
156 public function test_get_assign_perpage($maxperpage, $userprefs) {
157
158 $this->setUser($this->editingteachers[0]);
159 $assign = $this->create_instance();
160 set_config('maxperpage', $maxperpage, 'assign');
161 set_user_preference('assign_perpage', null);
162 $this->assertEquals(10, $assign->get_assign_perpage());
163 foreach ($userprefs as $pref => $perpage) {
164 set_user_preference('assign_perpage', $pref);
165 $this->assertEquals($perpage, $assign->get_assign_perpage());
166 }
167 }
168
79397b56
DB
169 /**
170 * Test submissions with extension date.
171 */
172 public function test_gradingtable_extension_due_date() {
173 global $PAGE;
174
175 // Setup the assignment.
176 $this->create_extra_users();
177 $this->setUser($this->editingteachers[0]);
178 $assign = $this->create_instance(array(
179 'assignsubmission_onlinetext_enabled'=>1,
180 'duedate' => time() - 4 * 24 * 60 * 60,
181 ));
182 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
183 'id' => $assign->get_course_module()->id,
184 'action' => 'grading',
185 )));
186
187 // Check that the assignment is late.
188 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
189 $output = $assign->get_renderer()->render($gradingtable);
190 $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
191 $this->assertContains(get_string('overdue', 'assign', format_time(4*24*60*60)), $output);
192
193 // Grant an extension.
5b460d65
MG
194 $extendedtime = time() + 2 * 24 * 60 * 60;
195 $assign->testable_save_user_extension($this->students[0]->id, $extendedtime);
79397b56
DB
196 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
197 $output = $assign->get_renderer()->render($gradingtable);
198 $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
5b460d65 199 $this->assertContains(get_string('userextensiondate', 'assign', userdate($extendedtime)), $output);
79397b56
DB
200
201 // Simulate a submission.
202 $this->setUser($this->students[0]);
203 $submission = $assign->get_user_submission($this->students[0]->id, true);
204 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
205 $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
206 $data = new stdClass();
207 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
208 'text'=>'Submission text',
209 'format'=>FORMAT_MOODLE);
210 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
211 $plugin->save($submission, $data);
212
213 // Verify output.
214 $this->setUser($this->editingteachers[0]);
215 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
216 $output = $assign->get_renderer()->render($gradingtable);
217 $this->assertContains(get_string('submissionstatus_submitted', 'assign'), $output);
5b460d65 218 $this->assertContains(get_string('userextensiondate', 'assign', userdate($extendedtime)), $output);
79397b56
DB
219 }
220
221 /**
222 * Test that late submissions with extension date calculate correctly.
223 */
224 public function test_gradingtable_extension_date_calculation_for_lateness() {
225 global $PAGE;
226
227 // Setup the assignment.
228 $this->create_extra_users();
229 $this->setUser($this->editingteachers[0]);
a0f8db77 230 $time = time();
79397b56
DB
231 $assign = $this->create_instance(array(
232 'assignsubmission_onlinetext_enabled'=>1,
a0f8db77 233 'duedate' => $time - 4 * 24 * 60 * 60,
79397b56
DB
234 ));
235 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
236 'id' => $assign->get_course_module()->id,
237 'action' => 'grading',
238 )));
239
240 // Check that the assignment is late.
241 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
242 $output = $assign->get_renderer()->render($gradingtable);
243 $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
a0f8db77
DW
244 $difftime = time() - $time;
245 $this->assertContains(get_string('overdue', 'assign', format_time(4*24*60*60 + $difftime)), $output);
79397b56
DB
246
247 // Grant an extension that is in the past.
a0f8db77 248 $assign->testable_save_user_extension($this->students[0]->id, $time - 2 * 24 * 60 * 60);
79397b56
DB
249 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
250 $output = $assign->get_renderer()->render($gradingtable);
251 $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
a0f8db77
DW
252 $this->assertContains(get_string('userextensiondate', 'assign', userdate($time - 2*24*60*60)), $output);
253 $difftime = time() - $time;
254 $this->assertContains(get_string('overdue', 'assign', format_time(2*24*60*60 + $difftime)), $output);
79397b56
DB
255
256 // Simulate a submission.
257 $this->setUser($this->students[0]);
258 $submission = $assign->get_user_submission($this->students[0]->id, true);
259 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
260 $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
261 $data = new stdClass();
262 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
263 'text'=>'Submission text',
264 'format'=>FORMAT_MOODLE);
265 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
266 $plugin->save($submission, $data);
a0f8db77 267 $submittedtime = time();
79397b56
DB
268
269 // Verify output.
270 $this->setUser($this->editingteachers[0]);
271 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
272 $output = $assign->get_renderer()->render($gradingtable);
273 $this->assertContains(get_string('submissionstatus_submitted', 'assign'), $output);
a0f8db77
DW
274 $this->assertContains(get_string('userextensiondate', 'assign', userdate($time - 2*24*60*60)), $output);
275
276 $difftime = $submittedtime - $time;
277 $this->assertContains(get_string('submittedlateshort', 'assign', format_time(2*24*60*60 + $difftime)), $output);
79397b56
DB
278 }
279
232b26f0
CS
280 public function test_gradingtable_status_rendering() {
281 global $PAGE;
282
283 // Setup the assignment.
284 $this->create_extra_users();
285 $this->setUser($this->editingteachers[0]);
286 $time = time();
287 $assign = $this->create_instance(array(
288 'assignsubmission_onlinetext_enabled' => 1,
289 'duedate' => $time - 4 * 24 * 60 * 60,
290 ));
291 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
292 'id' => $assign->get_course_module()->id,
293 'action' => 'grading',
294 )));
295
296 // Check that the assignment is late.
297 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
298 $output = $assign->get_renderer()->render($gradingtable);
299 $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
300 $difftime = time() - $time;
301 $this->assertContains(get_string('overdue', 'assign', format_time(4 * 24 * 60 * 60 + $difftime)), $output);
302
303 // Simulate a student viewing the assignment without submitting.
304 $this->setUser($this->students[0]);
305 $submission = $assign->get_user_submission($this->students[0]->id, true);
306 $submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
307 $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
308 $submittedtime = time();
309
310 // Verify output.
311 $this->setUser($this->editingteachers[0]);
312 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
313 $output = $assign->get_renderer()->render($gradingtable);
314 $difftime = $submittedtime - $time;
315 $this->assertContains(get_string('overdue', 'assign', format_time(4 * 24 * 60 * 60 + $difftime)), $output);
316
317 $document = new DOMDocument();
890c2603 318 @$document->loadHTML($output);
232b26f0
CS
319 $xpath = new DOMXPath($document);
320 $this->assertEquals('', $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c8"])'));
321 }
322
79397b56
DB
323 /**
324 * Check that group submission information is rendered correctly in the
325 * grading table.
326 */
327 public function test_gradingtable_group_submissions_rendering() {
328 global $PAGE;
329
330 $this->create_extra_users();
331 // Now verify group assignments.
332 $this->setUser($this->teachers[0]);
333 $assign = $this->create_instance(array(
334 'teamsubmission' => 1,
335 'assignsubmission_onlinetext_enabled' => 1,
336 'submissiondrafts' => 1,
337 'requireallteammemberssubmit' => 0,
338 ));
339 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
340 'id' => $assign->get_course_module()->id,
341 'action' => 'grading',
342 )));
343
344 // Add a submission.
345 $this->setUser($this->extrastudents[0]);
346 $data = new stdClass();
347 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
348 'text'=>'Submission text',
349 'format'=>FORMAT_MOODLE);
350 $notices = array();
351 $assign->save_submission($data, $notices);
352
353 $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
354 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
355 $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
356
357 // Check output.
358 $this->setUser($this->teachers[0]);
359 $gradingtable = new assign_grading_table($assign, 4, '', 0, true);
360 $output = $assign->get_renderer()->render($gradingtable);
361 $document = new DOMDocument();
890c2603 362 @$document->loadHTML($output);
79397b56
DB
363 $xpath = new DOMXPath($document);
364
365 // Check status.
366 $this->assertSame(get_string('submissionstatus_submitted', 'assign'), $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c4"]/div[@class="submissionstatussubmitted"])'));
367 $this->assertSame(get_string('submissionstatus_submitted', 'assign'), $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c4"]/div[@class="submissionstatussubmitted"])'));
368
369 // Check submission last modified date
370 $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c8"])')));
371 $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c8"])')));
372
373 // Check group.
374 $this->assertSame($this->groups[0]->name, $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c5"])'));
375 $this->assertSame($this->groups[0]->name, $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c5"])'));
376
377 // Check submission text.
378 $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c9"]/div/div)'));
379 $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c9"]/div/div)'));
380
381 // Check comments can be made.
382 $this->assertSame(1, (int)$xpath->evaluate('count(//td[@id="mod_assign_grading_r0_c10"]//textarea)'));
383 $this->assertSame(1, (int)$xpath->evaluate('count(//td[@id="mod_assign_grading_r3_c10"]//textarea)'));
384 }
385
47f48152
DW
386 public function test_show_intro() {
387 // Test whether we are showing the intro at the correct times.
388 $this->setUser($this->editingteachers[0]);
389 $assign = $this->create_instance(array('alwaysshowdescription'=>1));
390
391 $this->assertEquals(true, $assign->testable_show_intro());
392
393 $tomorrow = time() + (24*60*60);
394
395 $assign = $this->create_instance(array('alwaysshowdescription'=>0,
396 'allowsubmissionsfromdate'=>$tomorrow));
397 $this->assertEquals(false, $assign->testable_show_intro());
398 $yesterday = time() - (24*60*60);
399 $assign = $this->create_instance(array('alwaysshowdescription'=>0,
400 'allowsubmissionsfromdate'=>$yesterday));
401 $this->assertEquals(true, $assign->testable_show_intro());
402 }
403
404 public function test_has_submissions_or_grades() {
405 $this->setUser($this->editingteachers[0]);
406 $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
407
408 $instance = $assign->get_instance();
409
410 // Should start empty.
411 $this->assertEquals(false, $assign->has_submissions_or_grades());
412
413 // Simulate a submission.
414 $this->setUser($this->students[0]);
415 $submission = $assign->get_user_submission($this->students[0]->id, true);
ab14ab74
FM
416
417 // The submission is still new.
418 $this->assertEquals(false, $assign->has_submissions_or_grades());
419
420 // Submit the submission.
421 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
422 $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
47f48152
DW
423 $data = new stdClass();
424 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
425 'text'=>'Submission text',
426 'format'=>FORMAT_MOODLE);
427 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
428 $plugin->save($submission, $data);
429
430 // Now test again.
431 $this->assertEquals(true, $assign->has_submissions_or_grades());
432 // Set this back to default.
433 $this->students[0]->ignoresesskey = false;
434 }
435
436 public function test_delete_grades() {
437 $this->setUser($this->editingteachers[0]);
438 $assign = $this->create_instance();
439
440 // Simulate adding a grade.
441 $this->setUser($this->teachers[0]);
442 $data = new stdClass();
443 $data->grade = '50.0';
df211804 444 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
47f48152
DW
445
446 // Now see if the data is in the gradebook.
447 $gradinginfo = grade_get_grades($this->course->id,
448 'mod',
449 'assign',
450 $assign->get_instance()->id);
451
452 $this->assertNotEquals(0, count($gradinginfo->items));
453
454 $assign->testable_delete_grades();
455 $gradinginfo = grade_get_grades($this->course->id,
456 'mod',
457 'assign',
458 $assign->get_instance()->id);
459
460 $this->assertEquals(0, count($gradinginfo->items));
461 }
462
463 public function test_delete_instance() {
464 $this->setUser($this->editingteachers[0]);
465 $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
466
467 // Simulate adding a grade.
468 $this->setUser($this->teachers[0]);
469 $data = new stdClass();
470 $data->grade = '50.0';
df211804 471 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
47f48152
DW
472
473 // Simulate a submission.
474 $this->setUser($this->students[0]);
475 $submission = $assign->get_user_submission($this->students[0]->id, true);
476 $data = new stdClass();
477 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
478 'text'=>'Submission text',
479 'format'=>FORMAT_MOODLE);
480 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
481 $plugin->save($submission, $data);
482
483 // Now try and delete.
484 $this->assertEquals(true, $assign->delete_instance());
485 }
486
487 public function test_reset_userdata() {
488 global $DB;
489
490 $now = time();
491 $this->setUser($this->editingteachers[0]);
492 $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1,
493 'duedate'=>$now));
494
495 // Simulate adding a grade.
496 $this->setUser($this->teachers[0]);
497 $data = new stdClass();
498 $data->grade = '50.0';
df211804 499 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
47f48152
DW
500
501 // Simulate a submission.
502 $this->setUser($this->students[0]);
503 $submission = $assign->get_user_submission($this->students[0]->id, true);
504 $data = new stdClass();
505 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
506 'text'=>'Submission text',
507 'format'=>FORMAT_MOODLE);
508 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
509 $plugin->save($submission, $data);
510
511 $this->assertEquals(true, $assign->has_submissions_or_grades());
512 // Now try and reset.
513 $data = new stdClass();
514 $data->reset_assign_submissions = 1;
515 $data->reset_gradebook_grades = 1;
d685b959
IT
516 $data->reset_assign_user_overrides = 1;
517 $data->reset_assign_group_overrides = 1;
47f48152
DW
518 $data->courseid = $this->course->id;
519 $data->timeshift = 24*60*60;
520 $this->setUser($this->editingteachers[0]);
521 $assign->reset_userdata($data);
522 $this->assertEquals(false, $assign->has_submissions_or_grades());
523
524 // Reload the instance data.
525 $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
526 $this->assertEquals($now + 24*60*60, $instance->duedate);
e63515ba
RT
527
528 // Test reset using assign_reset_userdata().
529 $assignduedate = $instance->duedate; // Keep old updated value for comparison.
530 $data->timeshift = 2*24*60*60;
531 assign_reset_userdata($data);
532 $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
533 $this->assertEquals($assignduedate + 2*24*60*60, $instance->duedate);
534
535 // Create one more assignment and reset, make sure time shifted for previous assignment is not changed.
536 $assign2 = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1,
537 'duedate' => $now));
538 $assignduedate = $instance->duedate;
539 $data->timeshift = 3*24*60*60;
540 $assign2->reset_userdata($data);
541 $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
542 $this->assertEquals($assignduedate, $instance->duedate);
543 $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
544 $this->assertEquals($now + 3*24*60*60, $instance2->duedate);
545
546 // Reset both assignments using assign_reset_userdata() and make sure both assignments have same date.
547 $assignduedate = $instance->duedate;
548 $assign2duedate = $instance2->duedate;
549 $data->timeshift = 4*24*60*60;
550 assign_reset_userdata($data);
551 $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
552 $this->assertEquals($assignduedate + 4*24*60*60, $instance->duedate);
553 $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
554 $this->assertEquals($assign2duedate + 4*24*60*60, $instance2->duedate);
47f48152
DW
555 }
556
557 public function test_plugin_settings() {
558 global $DB;
559
560 $now = time();
561 $this->setUser($this->editingteachers[0]);
562 $assign = $this->create_instance(array('assignsubmission_file_enabled'=>1,
563 'assignsubmission_file_maxfiles'=>12,
564 'assignsubmission_file_maxsizebytes'=>10));
565
566 $plugin = $assign->get_submission_plugin_by_type('file');
567 $this->assertEquals('12', $plugin->get_config('maxfilesubmissions'));
568 }
569
570 public function test_update_calendar() {
571 global $DB;
572
47f48152 573 $this->setUser($this->editingteachers[0]);
f588ea50
FM
574 $userctx = context_user::instance($this->editingteachers[0]->id)->id;
575
576 // Hack to pretend that there was an editor involved. We need both $_POST and $_REQUEST, and a sesskey.
577 $draftid = file_get_unused_draft_itemid();
578 $_REQUEST['introeditor'] = $draftid;
579 $_POST['introeditor'] = $draftid;
580 $_POST['sesskey'] = sesskey();
581
582 // Write links to a draft area.
583 $fakearealink1 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">link</a>', 'draftfile.php', $userctx,
584 'user', 'draft', $draftid);
585 $fakearealink2 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">new</a>', 'draftfile.php', $userctx,
586 'user', 'draft', $draftid);
587
588 // Create a new assignment with links to a draft area.
589 $now = time();
590 $assign = $this->create_instance(array(
591 'duedate' => $now,
592 'intro' => $fakearealink1,
593 'introformat' => FORMAT_HTML
594 ));
47f48152
DW
595
596 // See if there is an event in the calendar.
597 $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
f588ea50
FM
598 $event = $DB->get_record('event', $params);
599 $this->assertNotEmpty($event);
600 $this->assertSame('link', $event->description); // The pluginfile links are removed.
47f48152 601
f588ea50
FM
602 // Make sure the same works when updating the assignment.
603 $instance = $assign->get_instance();
604 $instance->instance = $instance->id;
605 $instance->intro = $fakearealink2;
606 $instance->introformat = FORMAT_HTML;
607 $assign->update_instance($instance);
608 $params = array('modulename' => 'assign', 'instance' => $assign->get_instance()->id);
609 $event = $DB->get_record('event', $params);
610 $this->assertNotEmpty($event);
611 $this->assertSame('new', $event->description); // The pluginfile links are removed.
ae7638f7
DW
612
613 // Create an assignment with a description that should be hidden.
614 $assign = $this->create_instance(array('duedate'=>$now + 160,
615 'alwaysshowdescription'=>false,
8112670d 616 'allowsubmissionsfromdate'=>$now + 60,
ae7638f7
DW
617 'intro'=>'Some text'));
618
619 // Get the event from the calendar.
620 $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
621 $event = $DB->get_record('event', $params);
622
623 $this->assertEmpty($event->description);
8112670d
DW
624
625 // Change the allowsubmissionfromdate to the past - do this directly in the DB
626 // because if we call the assignment update method - it will update the calendar
627 // and we want to test that this works from cron.
628 $DB->set_field('assign', 'allowsubmissionsfromdate', $now - 60, array('id'=>$assign->get_instance()->id));
ae7638f7
DW
629 // Run cron to update the event in the calendar.
630 assign::cron();
631 $event = $DB->get_record('event', $params);
632
633 $this->assertContains('Some text', $event->description);
634
47f48152
DW
635 }
636
637 public function test_update_instance() {
638 global $DB;
639
640 $this->setUser($this->editingteachers[0]);
641 $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
642
643 $now = time();
644 $instance = $assign->get_instance();
645 $instance->duedate = $now;
646 $instance->instance = $instance->id;
647 $instance->assignsubmission_onlinetext_enabled = 1;
47f48152
DW
648
649 $assign->update_instance($instance);
650
651 $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
652 $this->assertEquals($now, $instance->duedate);
653 }
654
8fd00a24
DW
655 public function test_cannot_submit_empty() {
656 global $PAGE;
657
658 $this->setUser($this->editingteachers[0]);
659 $assign = $this->create_instance(array('submissiondrafts'=>1));
660
661 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
662
663 // Test you cannot see the submit button for an offline assignment regardless.
664 $this->setUser($this->students[0]);
665 $output = $assign->view_student_summary($this->students[0], true);
666 $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Can submit empty offline assignment');
667
668 // Test you cannot see the submit button for an online text assignment with no submission.
669 $this->setUser($this->editingteachers[0]);
670 $instance = $assign->get_instance();
671 $instance->instance = $instance->id;
672 $instance->assignsubmission_onlinetext_enabled = 1;
673
674 $assign->update_instance($instance);
675 $this->setUser($this->students[0]);
676 $output = $assign->view_student_summary($this->students[0], true);
677 $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Cannot submit empty onlinetext assignment');
678
679 // Simulate a submission.
680 $submission = $assign->get_user_submission($this->students[0]->id, true);
681 $data = new stdClass();
682 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
683 'text'=>'Submission text',
684 'format'=>FORMAT_MOODLE);
685 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
686 $plugin->save($submission, $data);
687 // Test you can see the submit button for an online text assignment with a submission.
688 $output = $assign->view_student_summary($this->students[0], true);
689 $this->assertContains(get_string('submitassignment', 'assign'), $output, 'Can submit non empty onlinetext assignment');
690 }
691
c89d23ee
CB
692 /**
693 * Test new_submission_empty
694 *
695 * We only test combinations of plugins here. Individual plugins are tested
696 * in their respective test files.
697 *
698 * @dataProvider test_new_submission_empty_testcases
699 * @param string $data The file submission data
700 * @param bool $expected The expected return value
701 */
702 public function test_new_submission_empty($data, $expected) {
703 $this->resetAfterTest();
704 $assign = $this->create_instance(['assignsubmission_file_enabled' => 1,
705 'assignsubmission_file_maxfiles' => 12,
706 'assignsubmission_file_maxsizebytes' => 10,
707 'assignsubmission_onlinetext_enabled' => 1]);
708 $this->setUser($this->students[0]);
709 $submission = new stdClass();
710
711 if ($data['file'] && isset($data['file']['filename'])) {
712 $itemid = file_get_unused_draft_itemid();
713 $submission->files_filemanager = $itemid;
714 $data['file'] += ['contextid' => context_user::instance($this->students[0]->id)->id, 'itemid' => $itemid];
715 $fs = get_file_storage();
716 $fs->create_file_from_string((object)$data['file'], 'Content of ' . $data['file']['filename']);
717 }
718
719 if ($data['onlinetext']) {
720 $submission->onlinetext_editor = ['text' => $data['onlinetext']];
721 }
722
723 $result = $assign->new_submission_empty($submission);
724 $this->assertTrue($result === $expected);
725 }
726
727 /**
728 * Dataprovider for the test_new_submission_empty testcase
729 *
730 * @return array of testcases
731 */
732 public function test_new_submission_empty_testcases() {
733 return [
734 'With file and onlinetext' => [
735 [
736 'file' => [
737 'component' => 'user',
738 'filearea' => 'draft',
739 'filepath' => '/',
740 'filename' => 'not_a_virus.exe'
741 ],
742 'onlinetext' => 'Balin Fundinul Uzbadkhazaddumu'
743 ],
744 false
745 ]
746 ];
747 }
748
47f48152 749 public function test_list_participants() {
c13ac85d 750 global $CFG, $DB;
751
df211804 752 $this->create_extra_users();
47f48152
DW
753 $this->setUser($this->editingteachers[0]);
754 $assign = $this->create_instance(array('grade'=>100));
755
a5c793c3 756 $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
e6cc5347
RT
757
758 // Teacher with user preference set should see suspended users as well.
759 set_user_preference('grade_report_showonlyactiveenrol', false);
760 $assign = $this->create_instance(array('grade'=>100));
761 $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT,
762 count($assign->list_participants(null, true)));
763
1ecb8044 764 // Non-editing teacher should not see suspended users, even if user preference is set.
e6cc5347
RT
765 $this->setUser($this->teachers[0]);
766 set_user_preference('grade_report_showonlyactiveenrol', false);
767 $assign = $this->create_instance(array('grade'=>100));
768 $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
c13ac85d 769
770 // Turn on availability and a group restriction, and check that it doesn't
771 // show users who aren't in the group.
772 $CFG->enableavailability = true;
773 $specialgroup = $this->getDataGenerator()->create_group(
774 array('courseid' => $this->course->id));
775 $assign = $this->create_instance(array('grade' => 100,
776 'availability' => json_encode(\core_availability\tree::get_root_json(
777 array(\availability_group\condition::get_json($specialgroup->id))))));
778 groups_add_member($specialgroup, $this->students[0]);
779 groups_add_member($specialgroup, $this->students[1]);
780 $this->assertEquals(2, count($assign->list_participants(null, true)));
47f48152
DW
781 }
782
1b2f9dc6
RW
783 public function test_get_participant_user_not_exist() {
784 $assign = $this->create_instance(array('grade' => 100));
785 $this->assertNull($assign->get_participant('-1'));
786 }
787
788 public function test_get_participant_not_enrolled() {
789 $assign = $this->create_instance(array('grade' => 100));
790 $user = $this->getDataGenerator()->create_user();
791 $this->assertNull($assign->get_participant($user->id));
792 }
793
794 public function test_get_participant_no_submission() {
795 $assign = $this->create_instance(array('grade' => 100));
796 $student = $this->students[0];
797 $participant = $assign->get_participant($student->id);
798
799 $this->assertEquals($student->id, $participant->id);
800 $this->assertFalse($participant->submitted);
801 $this->assertFalse($participant->requiregrading);
eb43ef6c
TB
802 $this->assertFalse($participant->grantedextension);
803 }
804
805 public function test_get_participant_granted_extension() {
806 $assign = $this->create_instance(array('grade' => 100));
807 $student = $this->students[0];
808 $this->setUser($this->editingteachers[0]);
809 $assign->save_user_extension($student->id, time());
810 $participant = $assign->get_participant($student->id);
811
812 $this->assertEquals($student->id, $participant->id);
813 $this->assertFalse($participant->submitted);
814 $this->assertFalse($participant->requiregrading);
815 $this->assertTrue($participant->grantedextension);
1b2f9dc6
RW
816 }
817
818 public function test_get_participant_with_ungraded_submission() {
819 $assign = $this->create_instance(array('grade' => 100));
820 $student = $this->students[0];
821 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
822
823 $this->setUser($student);
824
825 // Simulate a submission.
826 $data = new stdClass();
827 $data->onlinetext_editor = array(
828 'itemid' => file_get_unused_draft_itemid(),
829 'text' => 'Student submission text',
830 'format' => FORMAT_MOODLE
831 );
832
833 $notices = array();
834 $assign->save_submission($data, $notices);
835
836 $data = new stdClass;
837 $data->userid = $student->id;
838 $assign->submit_for_grading($data, array());
839
840 $participant = $assign->get_participant($student->id);
841
842 $this->assertEquals($student->id, $participant->id);
843 $this->assertTrue($participant->submitted);
844 $this->assertTrue($participant->requiregrading);
eb43ef6c 845 $this->assertFalse($participant->grantedextension);
1b2f9dc6
RW
846 }
847
848 public function test_get_participant_with_graded_submission() {
849 $assign = $this->create_instance(array('grade' => 100));
850 $student = $this->students[0];
851 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
852
853 $this->setUser($student);
854
855 // Simulate a submission.
856 $data = new stdClass();
857 $data->onlinetext_editor = array(
858 'itemid' => file_get_unused_draft_itemid(),
859 'text' => 'Student submission text',
860 'format' => FORMAT_MOODLE
861 );
862
863 $notices = array();
864 $assign->save_submission($data, $notices);
865
866 $data = new stdClass;
867 $data->userid = $student->id;
868 $assign->submit_for_grading($data, array());
869
870 // This is to make sure the grade happens after the submission because
871 // we have no control over the timemodified values.
74ee9d29 872 $this->waitForSecond();
1b2f9dc6
RW
873 // Grade the submission.
874 $this->setUser($this->teachers[0]);
875
876 $data = new stdClass();
877 $data->grade = '50.0';
878 $assign->testable_apply_grade_to_user($data, $student->id, 0);
879
880 $participant = $assign->get_participant($student->id);
881
882 $this->assertEquals($student->id, $participant->id);
883 $this->assertTrue($participant->submitted);
884 $this->assertFalse($participant->requiregrading);
eb43ef6c 885 $this->assertFalse($participant->grantedextension);
1b2f9dc6
RW
886 }
887
47f48152 888 public function test_count_teams() {
df211804 889 $this->create_extra_users();
47f48152 890 $this->setUser($this->editingteachers[0]);
2dda9417
DM
891 $assign1 = $this->create_instance(array('teamsubmission' => 1));
892 $this->assertEquals(self::GROUP_COUNT + 1, $assign1->count_teams());
47f48152 893
2dda9417
DM
894 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id));
895 $this->getDataGenerator()->create_grouping_group(array('groupid' => $this->groups[0]->id, 'groupingid' => $grouping->id));
896 $this->getDataGenerator()->create_grouping_group(array('groupid' => $this->groups[1]->id, 'groupingid' => $grouping->id));
897
e528997a
AH
898 // No active group and non group submissions allowed => 2 groups + the default one.
899 $params = array(
900 'teamsubmission' => 1,
901 'teamsubmissiongroupingid' => $grouping->id,
902 'preventsubmissionnotingroup' => false
903 );
904 $assign2 = $this->create_instance($params);
2dda9417
DM
905 $this->assertEquals(3, $assign2->count_teams());
906
907 // An active group => Just the selected one.
908 $this->assertEquals(1, $assign2->count_teams($this->groups[0]->id));
e528997a
AH
909
910 // No active group and non group submissions allowed => 2 groups + no default one.
911 $params = array('teamsubmission' => 1, 'teamsubmissiongroupingid' => $grouping->id, 'preventsubmissionnotingroup' => true);
912 $assign3 = $this->create_instance($params);
913 $this->assertEquals(2, $assign3->count_teams());
914
915 $assign4 = $this->create_instance(array('teamsubmission' => 1, 'preventsubmissionnotingroup' => true));
916 $this->assertEquals(self::GROUP_COUNT, $assign4->count_teams());
47f48152
DW
917 }
918
e2d2d8a1 919 public function test_submit_to_default_group() {
2dda9417 920 global $DB, $SESSION;
e2d2d8a1
DW
921
922 $this->preventResetByRollback();
923 $sink = $this->redirectMessages();
924
925 $this->setUser($this->editingteachers[0]);
926 $params = array('teamsubmission' => 1,
927 'assignsubmission_onlinetext_enabled' => 1,
2dda9417
DM
928 'submissiondrafts' => 0,
929 'groupmode' => VISIBLEGROUPS);
e2d2d8a1
DW
930 $assign = $this->create_instance($params);
931
932 $newstudent = $this->getDataGenerator()->create_user();
933 $studentrole = $DB->get_record('role', array('shortname'=>'student'));
934 $this->getDataGenerator()->enrol_user($newstudent->id,
935 $this->course->id,
936 $studentrole->id);
937 $this->setUser($newstudent);
938 $data = new stdClass();
939 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
940 'text'=>'Submission text',
941 'format'=>FORMAT_MOODLE);
942 $notices = array();
943
944 $group = $assign->get_submission_group($newstudent->id);
945 $this->assertFalse($group, 'New student is in default group');
946 $assign->save_submission($data, $notices);
947 $this->assertEmpty($notices, 'No errors on save submission');
948
2dda9417 949 // Set active groups to all groups.
4ceed4df 950 $this->setUser($this->editingteachers[0]);
2dda9417
DM
951 $SESSION->activegroup[$this->course->id]['aag'][0] = 0;
952 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
953
954 // Set an active group.
955 $anothergroup = $this->groups[0];
956 $SESSION->activegroup[$this->course->id]['aag'][0] = (int)$anothergroup->id;
957 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
958
e2d2d8a1
DW
959 $sink->close();
960 }
961
47f48152 962 public function test_count_submissions() {
2dda9417
DM
963 global $SESSION;
964
df211804 965 $this->create_extra_users();
47f48152 966 $this->setUser($this->editingteachers[0]);
2dda9417 967 $assign1 = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1));
47f48152
DW
968
969 // Simulate a submission.
a5c793c3 970 $this->setUser($this->extrastudents[0]);
2dda9417 971 $submission = $assign1->get_user_submission($this->extrastudents[0]->id, true);
9e3eee67 972 $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
2dda9417 973 $assign1->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
47f48152
DW
974 // Leave this one as DRAFT.
975 $data = new stdClass();
976 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
977 'text'=>'Submission text',
978 'format'=>FORMAT_MOODLE);
2dda9417 979 $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
47f48152
DW
980 $plugin->save($submission, $data);
981
982 // Simulate adding a grade.
983 $this->setUser($this->teachers[0]);
984 $data = new stdClass();
985 $data->grade = '50.0';
2dda9417 986 $assign1->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
47f48152
DW
987
988 // Simulate a submission.
a5c793c3 989 $this->setUser($this->extrastudents[1]);
2dda9417 990 $submission = $assign1->get_user_submission($this->extrastudents[1]->id, true);
47f48152 991 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2dda9417 992 $assign1->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
47f48152
DW
993 $data = new stdClass();
994 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
995 'text'=>'Submission text',
996 'format'=>FORMAT_MOODLE);
2dda9417 997 $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
47f48152
DW
998 $plugin->save($submission, $data);
999
1000 // Simulate a submission.
a5c793c3 1001 $this->setUser($this->extrastudents[2]);
2dda9417 1002 $submission = $assign1->get_user_submission($this->extrastudents[2]->id, true);
47f48152 1003 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2dda9417 1004 $assign1->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
47f48152
DW
1005 $data = new stdClass();
1006 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1007 'text'=>'Submission text',
1008 'format'=>FORMAT_MOODLE);
2dda9417 1009 $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
47f48152
DW
1010 $plugin->save($submission, $data);
1011
1012 // Simulate a submission.
a5c793c3 1013 $this->setUser($this->extrastudents[3]);
2dda9417 1014 $submission = $assign1->get_user_submission($this->extrastudents[3]->id, true);
47f48152 1015 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2dda9417 1016 $assign1->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
47f48152
DW
1017 $data = new stdClass();
1018 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1019 'text'=>'Submission text',
1020 'format'=>FORMAT_MOODLE);
2dda9417 1021 $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
47f48152
DW
1022 $plugin->save($submission, $data);
1023
e6cc5347
RT
1024 // Simulate a submission for suspended user, this will never be counted.
1025 $this->setUser($this->extrastudents[3]);
2dda9417 1026 $submission = $assign1->get_user_submission($this->extrasuspendedstudents[0]->id, true);
e6cc5347 1027 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2dda9417 1028 $assign1->testable_update_submission($submission, $this->extrasuspendedstudents[0]->id, true, false);
e6cc5347
RT
1029 $data = new stdClass();
1030 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1031 'text'=>'Submission text',
1032 'format'=>FORMAT_MOODLE);
2dda9417 1033 $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
e6cc5347
RT
1034 $plugin->save($submission, $data);
1035
fb6bae3e 1036 // Wait 1 second so the submission and grade do not have the same timemodified.
74ee9d29 1037 $this->waitForSecond();
47f48152 1038 // Simulate adding a grade.
50c734e8 1039 $this->setUser($this->editingteachers[0]);
47f48152
DW
1040 $data = new stdClass();
1041 $data->grade = '50.0';
2dda9417
DM
1042 $assign1->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
1043 $assign1->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
47f48152 1044
ab14ab74
FM
1045 // Create a new submission with status NEW.
1046 $this->setUser($this->extrastudents[4]);
2dda9417
DM
1047 $submission = $assign1->get_user_submission($this->extrastudents[4]->id, true);
1048
1049 $this->assertEquals(2, $assign1->count_grades());
1050 $this->assertEquals(4, $assign1->count_submissions());
1051 $this->assertEquals(5, $assign1->count_submissions(true));
1052 $this->assertEquals(2, $assign1->count_submissions_need_grading());
1053 $this->assertEquals(3, $assign1->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1054 $this->assertEquals(1, $assign1->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
1055
1056 // Groups.
1057 $assign2 = $this->create_instance(array(
1058 'assignsubmission_onlinetext_enabled' => 1,
1059 'groupmode' => VISIBLEGROUPS
1060 ));
1061
1062 $this->setUser($this->extrastudents[1]);
1063 $submission = $assign2->get_user_submission($this->extrastudents[1]->id, true);
1064 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1065 $assign2->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
1066 $data = new stdClass();
1067 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1068 'text' => 'Submission text',
1069 'format' => FORMAT_MOODLE);
1070 $plugin = $assign2->get_submission_plugin_by_type('onlinetext');
1071 $plugin->save($submission, $data);
1072
1073 $this->assertEquals(1, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1074
1075 // Set active groups to all groups.
4ceed4df 1076 $this->setUser($this->editingteachers[0]);
2dda9417
DM
1077 $SESSION->activegroup[$this->course->id]['aag'][0] = 0;
1078 $this->assertEquals(1, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1079
1080 // Set the user group.
1081 $studentgroups = groups_get_user_groups($this->course->id, $this->extrastudents[1]->id);
1082 $this->assertEquals(1, count($studentgroups));
1083 $studentgroup = array_pop($studentgroups);
1084 $SESSION->activegroup[$this->course->id]['aag'][0] = $studentgroup[0];
1085 $this->assertEquals(1, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1086
1087 // Set another group.
1088 $anothergroup = $this->groups[0];
1089 $this->assertNotEquals($anothergroup->id, $studentgroup[0]);
1090 $SESSION->activegroup[$this->course->id]['aag'][0] = (int)$anothergroup->id;
1091 $this->assertEquals(0, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
47f48152
DW
1092 }
1093
ab14ab74
FM
1094 public function test_count_submissions_for_groups() {
1095 $this->create_extra_users();
1096 $groupid = null;
1097 $this->setUser($this->editingteachers[0]);
1098 $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1, 'teamsubmission' => 1));
1099
1100 // Simulate a submission.
1101 $this->setUser($this->extrastudents[0]);
1102 $submission = $assign->get_group_submission($this->extrastudents[0]->id, $groupid, true);
1103 $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
1104 $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
1105 // Leave this one as DRAFT.
1106 $data = new stdClass();
1107 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1108 'text' => 'Submission text',
1109 'format' => FORMAT_MOODLE);
1110 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1111 $plugin->save($submission, $data);
1112
1113 // Simulate adding a grade.
1114 $this->setUser($this->teachers[0]);
1115 $data = new stdClass();
1116 $data->grade = '50.0';
1117 $assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
1118
1119 // Simulate a submission.
1120 $this->setUser($this->extrastudents[1]);
1121 $submission = $assign->get_group_submission($this->extrastudents[1]->id, $groupid, true);
1122 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1123 $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
1124 $data = new stdClass();
1125 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1126 'text' => 'Submission text',
1127 'format' => FORMAT_MOODLE);
1128 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1129 $plugin->save($submission, $data);
1130
1131 // Simulate a submission.
1132 $this->setUser($this->extrastudents[2]);
1133 $submission = $assign->get_group_submission($this->extrastudents[2]->id, $groupid, true);
1134 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1135 $assign->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
1136 $data = new stdClass();
1137 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1138 'text' => 'Submission text',
1139 'format' => FORMAT_MOODLE);
1140 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1141 $plugin->save($submission, $data);
1142
1143 // Simulate a submission.
1144 $this->setUser($this->extrastudents[3]);
1145 $submission = $assign->get_group_submission($this->extrastudents[3]->id, $groupid, true);
1146 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1147 $assign->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
1148 $data = new stdClass();
1149 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1150 'text' => 'Submission text',
1151 'format' => FORMAT_MOODLE);
1152 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1153 $plugin->save($submission, $data);
1154
1155 // Simulate adding a grade.
50c734e8 1156 $this->setUser($this->editingteachers[0]);
ab14ab74
FM
1157 $data = new stdClass();
1158 $data->grade = '50.0';
1159 $assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
1160 $assign->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
1161
1162 // Create a new submission with status NEW.
1163 $this->setUser($this->extrastudents[4]);
1164 $submission = $assign->get_group_submission($this->extrastudents[4]->id, $groupid, true);
1165
1166 $this->assertEquals(2, $assign->count_grades());
1167 $this->assertEquals(4, $assign->count_submissions());
1168 $this->assertEquals(5, $assign->count_submissions(true));
1169 $this->assertEquals(3, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1170 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
1171 }
1172
47f48152 1173 public function test_get_grading_userid_list() {
df211804 1174 $this->create_extra_users();
47f48152
DW
1175 $this->setUser($this->editingteachers[0]);
1176 $assign = $this->create_instance();
1177
1178 $users = $assign->testable_get_grading_userid_list();
a5c793c3 1179 $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($users));
e6cc5347
RT
1180
1181 $this->setUser($this->editingteachers[0]);
1182 set_user_preference('grade_report_showonlyactiveenrol', false);
1183 $assign = $this->create_instance();
1184
1185 $users = $assign->testable_get_grading_userid_list();
1186 $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT, count($users));
47f48152
DW
1187 }
1188
1189 public function test_cron() {
1190 // First run cron so there are no messages waiting to be sent (from other tests).
1191 cron_setup_user();
1192 assign::cron();
1193
1194 // Now create an assignment and add some feedback.
1195 $this->setUser($this->editingteachers[0]);
8e1266bf 1196 $assign = $this->create_instance(array('sendstudentnotifications'=>1));
47f48152
DW
1197
1198 // Simulate adding a grade.
1199 $this->setUser($this->teachers[0]);
1200 $data = new stdClass();
1201 $data->grade = '50.0';
df211804 1202 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
4d2bd673 1203 $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
47f48152 1204
f7dc9871
DW
1205 $data->sendstudentnotifications = false;
1206 $assign->testable_apply_grade_to_user($data, $this->students[2]->id, 0);
1207
47f48152
DW
1208 // Now run cron and see that one message was sent.
1209 $this->preventResetByRollback();
1210 $sink = $this->redirectMessages();
1211 cron_setup_user();
4d2bd673 1212 $this->expectOutputRegex('/Done processing 2 assignment submissions/');
47f48152
DW
1213 assign::cron();
1214
1215 $messages = $sink->get_messages();
f7dc9871 1216 // The sent count should be 2, because the 3rd one was marked as do not send notifications.
4d2bd673 1217 $this->assertEquals(2, count($messages));
47f48152
DW
1218 $this->assertEquals(1, $messages[0]->notification);
1219 $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
a4b10a52
ZD
1220
1221 // Regrading a grade causes a notification to the user.
1222 $data->sendstudentnotifications = true;
1223 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1224 assign::cron();
1225 $messages = $sink->get_messages();
1226 $this->assertEquals(3, count($messages));
47f48152
DW
1227 }
1228
70cfc878
JF
1229 /**
1230 * Test delivery of grade notifications as controlled by marking workflow.
1231 */
1232 public function test_markingworkflow_cron() {
1233 // First run cron so there are no messages waiting to be sent (from other tests).
1234 cron_setup_user();
1235 assign::cron();
1236
1237 // Now create an assignment with marking workflow enabled.
1238 $this->setUser($this->editingteachers[0]);
1239 $assign = $this->create_instance(array('sendstudentnotifications' => 1, 'markingworkflow' => 1));
1240
1241 // Simulate adding a grade.
1242 $this->setUser($this->teachers[0]);
1243 $data = new stdClass();
1244 $data->grade = '50.0';
1245
1246 // This student will not receive notification.
f2a6b9ca 1247 $data->sendstudentnotifications = 1;
70cfc878
JF
1248 $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
1249 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1250
1251 // This student will receive notification.
f2a6b9ca 1252 $data->sendstudentnotifications = 1;
70cfc878
JF
1253 $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
1254 $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
1255
1256 // Now run cron and see that one message was sent.
1257 $this->preventResetByRollback();
1258 $sink = $this->redirectMessages();
1259 cron_setup_user();
1260 $this->expectOutputRegex('/Done processing 1 assignment submissions/');
1261 assign::cron();
1262
1263 $messages = $sink->get_messages();
1264 $this->assertEquals(1, count($messages));
1265 $this->assertEquals($messages[0]->useridto, $this->students[1]->id);
1266 $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
1267 }
1268
cc350fd9
AD
1269 public function test_cron_message_includes_courseid() {
1270 // First run cron so there are no messages waiting to be sent (from other tests).
1271 cron_setup_user();
1272 assign::cron();
1273
1274 // Now create an assignment.
1275 $this->setUser($this->editingteachers[0]);
1276 $assign = $this->create_instance(array('sendstudentnotifications' => 1));
1277
1278 // Simulate adding a grade.
1279 $this->setUser($this->teachers[0]);
1280 $data = new stdClass();
1281 $data->grade = '50.0';
1282 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1283
1284 $this->preventResetByRollback();
1285 $sink = $this->redirectEvents();
0e8b5160 1286 $this->expectOutputRegex('/Done processing 1 assignment submissions/');
cc350fd9
AD
1287
1288 assign::cron();
1289
1290 $events = $sink->get_events();
376a79c2 1291 // Two notifications are sent, one to student and one to teacher. This generates
cc350fd9 1292 // four events:
376a79c2
MN
1293 // core\event\notification_sent
1294 // core\event\notification_viewed
1295 // core\event\notification_sent
1296 // core\event\notification_viewed.
cc350fd9 1297 $event = reset($events);
376a79c2 1298 $this->assertInstanceOf('\core\event\notification_sent', $event);
cc350fd9
AD
1299 $this->assertEquals($assign->get_course()->id, $event->other['courseid']);
1300 $sink->close();
1301 }
1302
47f48152
DW
1303 public function test_is_graded() {
1304 $this->setUser($this->editingteachers[0]);
1305 $assign = $this->create_instance();
1306
1307 // Simulate adding a grade.
1308 $this->setUser($this->teachers[0]);
1309 $data = new stdClass();
1310 $data->grade = '50.0';
df211804 1311 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
47f48152
DW
1312
1313 $this->assertEquals(true, $assign->testable_is_graded($this->students[0]->id));
1314 $this->assertEquals(false, $assign->testable_is_graded($this->students[1]->id));
1315 }
1316
4a47008c
DW
1317 public function test_can_grade() {
1318 global $DB;
1319
1320 $this->setUser($this->editingteachers[0]);
1321 $assign = $this->create_instance();
1322
1323 $this->setUser($this->students[0]);
1324 $this->assertEquals(false, $assign->can_grade());
1325 $this->setUser($this->editingteachers[0]);
1326 $this->assertEquals(true, $assign->can_grade());
1327 $this->setUser($this->teachers[0]);
1328 $this->assertEquals(true, $assign->can_grade());
1329
1330 // Test the viewgrades capability - without mod/assign:grade.
1331 $this->setUser($this->students[0]);
1332 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1333 assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
1334 $this->assertEquals(false, $assign->can_grade());
1335 }
1336
47f48152 1337 public function test_can_view_submission() {
4a47008c
DW
1338 global $DB;
1339
e6cc5347 1340 $this->create_extra_users();
47f48152
DW
1341 $this->setUser($this->editingteachers[0]);
1342 $assign = $this->create_instance();
1343
1344 $this->setUser($this->students[0]);
1345 $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1346 $this->assertEquals(false, $assign->can_view_submission($this->students[1]->id));
1347 $this->assertEquals(false, $assign->can_view_submission($this->teachers[0]->id));
1348 $this->setUser($this->teachers[0]);
1349 $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1350 $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
1351 $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
e6cc5347 1352 $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
47f48152
DW
1353 $this->setUser($this->editingteachers[0]);
1354 $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1355 $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
1356 $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
e6cc5347 1357 $this->assertEquals(true, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
4a47008c
DW
1358
1359 // Test the viewgrades capability - without mod/assign:grade.
1360 $this->setUser($this->students[0]);
1361 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1362 assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
1363 $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1364 $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
1365 $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
1366 $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
47f48152
DW
1367 }
1368
1369
1370 public function test_update_submission() {
df211804 1371 $this->create_extra_users();
47f48152
DW
1372 $this->setUser($this->editingteachers[0]);
1373 $assign = $this->create_instance();
1374
a5c793c3 1375 $this->setUser($this->extrastudents[0]);
47f48152 1376 $now = time();
a5c793c3
DW
1377 $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
1378 $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
47f48152
DW
1379
1380 $this->setUser($this->teachers[0]);
1381 // Verify the gradebook update.
1382 $gradinginfo = grade_get_grades($this->course->id,
1383 'mod',
1384 'assign',
1385 $assign->get_instance()->id,
a5c793c3 1386 $this->extrastudents[0]->id);
47f48152 1387
a5c793c3
DW
1388 $this->assertEquals($this->extrastudents[0]->id,
1389 $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
47f48152
DW
1390
1391 // Now verify group assignments.
1392 $this->setUser($this->editingteachers[0]);
1393 $assign = $this->create_instance(array('teamsubmission'=>1));
1394
a5c793c3 1395 $this->setUser($this->extrastudents[0]);
47f48152 1396 $now = time();
a5c793c3
DW
1397 $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1398 $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
47f48152 1399
e6cc5347 1400 // Check that at least 2 active members and 1 suspended member of the submission group had their submission updated.
47f48152
DW
1401
1402 $this->setUser($this->editingteachers[0]);
1403 $gradinginfo = grade_get_grades($this->course->id,
1404 'mod',
1405 'assign',
1406 $assign->get_instance()->id,
a5c793c3 1407 $this->extrastudents[0]->id);
47f48152 1408
a5c793c3
DW
1409 $this->assertEquals($this->extrastudents[0]->id,
1410 $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
47f48152
DW
1411
1412 $gradinginfo = grade_get_grades($this->course->id,
1413 'mod',
1414 'assign',
1415 $assign->get_instance()->id,
a5c793c3 1416 $this->extrastudents[self::GROUP_COUNT]->id);
47f48152 1417
a5c793c3
DW
1418 $this->assertEquals($this->extrastudents[self::GROUP_COUNT]->id,
1419 $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT]->id]->usermodified);
47f48152 1420
e6cc5347
RT
1421 $gradinginfo = grade_get_grades($this->course->id,
1422 'mod',
1423 'assign',
1424 $assign->get_instance()->id,
1425 $this->extrasuspendedstudents[0]->id);
1426 $this->assertEquals($this->extrasuspendedstudents[0]->id,
1427 $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[0]->id]->usermodified);
1428
1429 // Check the same with non-editing teacher and make sure submission is not updated for suspended user.
1430 $this->setUser($this->editingteachers[0]);
1431 $assign = $this->create_instance(array('teamsubmission'=>1));
1432
1433 $this->setUser($this->extrastudents[1]);
1434 $now = time();
1435 $submission = $assign->get_group_submission($this->extrastudents[1]->id, 0, true);
1436 $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, true);
1437
1438 $this->setUser($this->teachers[0]);
1439 $gradinginfo = grade_get_grades($this->course->id,
1440 'mod',
1441 'assign',
1442 $assign->get_instance()->id,
1443 $this->extrastudents[1]->id);
1444
1445 $this->assertEquals($this->extrastudents[1]->id,
1446 $gradinginfo->items[0]->grades[$this->extrastudents[1]->id]->usermodified);
1447
1448 $gradinginfo = grade_get_grades($this->course->id,
1449 'mod',
1450 'assign',
1451 $assign->get_instance()->id,
1452 $this->extrastudents[self::GROUP_COUNT+1]->id);
1453
1454 $this->assertEquals($this->extrastudents[self::GROUP_COUNT+1]->id,
1455 $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT+1]->id]->usermodified);
1456
1457 $gradinginfo = grade_get_grades($this->course->id,
1458 'mod',
1459 'assign',
1460 $assign->get_instance()->id,
1461 $this->extrasuspendedstudents[1]->id);
1462 $this->assertEquals($this->extrasuspendedstudents[1]->id,
1463 $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[1]->id]->usermodified);
1464
47f48152
DW
1465 // Now verify blind marking.
1466 $this->setUser($this->editingteachers[0]);
1467 $assign = $this->create_instance(array('blindmarking'=>1));
1468
a5c793c3 1469 $this->setUser($this->extrastudents[0]);
47f48152 1470 $now = time();
a5c793c3
DW
1471 $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
1472 $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
47f48152
DW
1473
1474 $this->setUser($this->editingteachers[0]);
1475 $gradinginfo = grade_get_grades($this->course->id,
1476 'mod',
1477 'assign',
1478 $assign->get_instance()->id,
a5c793c3 1479 $this->extrastudents[0]->id);
47f48152 1480
a5c793c3 1481 $this->assertEquals(null, $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->datesubmitted);
47f48152
DW
1482 }
1483
79397b56 1484 public function test_group_submissions_submit_for_marking_requireallteammemberssubmit() {
2bd27878
DW
1485 global $PAGE;
1486
1487 $this->create_extra_users();
1488 // Now verify group assignments.
1489 $this->setUser($this->editingteachers[0]);
1490 $assign = $this->create_instance(array('teamsubmission'=>1,
1491 'assignsubmission_onlinetext_enabled'=>1,
1492 'submissiondrafts'=>1,
1493 'requireallteammemberssubmit'=>1));
1494 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1495
2bd27878 1496 // Add a submission.
79397b56 1497 $this->setUser($this->extrastudents[0]);
2bd27878
DW
1498 $data = new stdClass();
1499 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1500 'text'=>'Submission text',
1501 'format'=>FORMAT_MOODLE);
1502
1503 $notices = array();
1504 $assign->save_submission($data, $notices);
1505
1506 // Check we can see the submit button.
1507 $output = $assign->view_student_summary($this->extrastudents[0], true);
1508 $this->assertContains(get_string('submitassignment', 'assign'), $output);
1509
1510 $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1511 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1512 $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1513
1514 // Check that the student does not see "Submit" button.
1515 $output = $assign->view_student_summary($this->extrastudents[0], true);
1516 $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1517
1518 // Change to another user in the same group.
1519 $this->setUser($this->extrastudents[self::GROUP_COUNT]);
1520 $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1521 $this->assertContains(get_string('submitassignment', 'assign'), $output);
1522
1523 $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
1524 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1525 $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
1526 $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1527 $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1528 }
1529
79397b56
DB
1530 public function test_group_submissions_submit_for_marking() {
1531 global $PAGE;
1532
1533 $this->create_extra_users();
1534 // Now verify group assignments.
1535 $this->setUser($this->editingteachers[0]);
a0f8db77 1536 $time = time();
79397b56
DB
1537 $assign = $this->create_instance(array('teamsubmission'=>1,
1538 'assignsubmission_onlinetext_enabled'=>1,
1539 'submissiondrafts'=>1,
1540 'requireallteammemberssubmit'=>0,
a0f8db77 1541 'duedate' => $time - 2*24*60*60));
79397b56
DB
1542 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1543
1544 $this->setUser($this->extrastudents[0]);
1545 // Add a submission.
1546 $data = new stdClass();
1547 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1548 'text'=>'Submission text',
1549 'format'=>FORMAT_MOODLE);
1550
1551 $notices = array();
1552 $assign->save_submission($data, $notices);
1553
1554 // Check we can see the submit button.
1555 $output = $assign->view_student_summary($this->extrastudents[0], true);
1556 $this->assertContains(get_string('submitassignment', 'assign'), $output);
1557 $this->assertContains(get_string('timeremaining', 'assign'), $output);
a0f8db77
DW
1558 $difftime = time() - $time;
1559 $this->assertContains(get_string('overdue', 'assign', format_time(2*24*60*60 + $difftime)), $output);
79397b56
DB
1560
1561 $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1562 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1563 $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1564
1565 // Check that the student does not see "Submit" button.
1566 $output = $assign->view_student_summary($this->extrastudents[0], true);
1567 $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1568
1569 // Change to another user in the same group.
1570 $this->setUser($this->extrastudents[self::GROUP_COUNT]);
1571 $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1572 $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1573
1574 // Check that time remaining is not overdue.
1575 $this->assertContains(get_string('timeremaining', 'assign'), $output);
a0f8db77
DW
1576 $difftime = time() - $time;
1577 $this->assertContains(get_string('submittedlate', 'assign', format_time(2*24*60*60 + $difftime)), $output);
79397b56
DB
1578
1579 $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
1580 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1581 $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
1582 $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1583 $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1584 }
1585
47f48152
DW
1586 public function test_submissions_open() {
1587 $this->setUser($this->editingteachers[0]);
1588
1589 $now = time();
1590 $tomorrow = $now + 24*60*60;
1591 $oneweek = $now + 7*24*60*60;
1592 $yesterday = $now - 24*60*60;
1593
1594 $assign = $this->create_instance();
1595 $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1596
1597 $assign = $this->create_instance(array('duedate'=>$tomorrow));
1598 $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1599
1600 $assign = $this->create_instance(array('duedate'=>$yesterday));
1601 $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1602
1603 $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$tomorrow));
1604 $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1605
1606 $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$yesterday));
1607 $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
1608
1609 $assign->testable_save_user_extension($this->students[0]->id, $tomorrow);
1610 $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1611
1612 $assign = $this->create_instance(array('submissiondrafts'=>1));
1613 $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1614
1615 $this->setUser($this->students[0]);
1616 $now = time();
1617 $submission = $assign->get_user_submission($this->students[0]->id, true);
1618 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1619 $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1620 $this->setUser($this->editingteachers[0]);
1621 $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
1622 }
1623
1624 public function test_get_graders() {
df211804 1625 $this->create_extra_users();
47f48152 1626 $this->setUser($this->editingteachers[0]);
47f48152 1627
f268fb5d
DW
1628 // Create an assignment with no groups.
1629 $assign = $this->create_instance();
a5c793c3
DW
1630 $this->assertCount(self::DEFAULT_TEACHER_COUNT +
1631 self::DEFAULT_EDITING_TEACHER_COUNT +
1632 self::EXTRA_TEACHER_COUNT +
1633 self::EXTRA_EDITING_TEACHER_COUNT,
1634 $assign->testable_get_graders($this->students[0]->id));
47f48152 1635
47f48152 1636 // Force create an assignment with SEPARATEGROUPS.
f268fb5d
DW
1637 $data = new stdClass();
1638 $data->courseid = $this->course->id;
1639 $data->name = 'Grouping';
1640 $groupingid = groups_create_grouping($data);
1641 groups_assign_grouping($groupingid, $this->groups[0]->id);
1642 $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
47f48152
DW
1643
1644 $this->setUser($this->students[1]);
a5c793c3 1645 $this->assertCount(4, $assign->testable_get_graders($this->students[0]->id));
f268fb5d
DW
1646 // Note the second student is in a group that is not in the grouping.
1647 // This means that we get all graders that are not in a group in the grouping.
1648 $this->assertCount(10, $assign->testable_get_graders($this->students[1]->id));
47f48152
DW
1649 }
1650
83360c8d
S
1651 public function test_get_notified_users() {
1652 global $CFG, $DB;
1653
1654 $capability = 'mod/assign:receivegradernotifications';
1655 $coursecontext = context_course::instance($this->course->id);
1656 $role = $DB->get_record('role', array('shortname' => 'teacher'));
1657
1658 $this->create_extra_users();
1659 $this->setUser($this->editingteachers[0]);
1660
1661 // Create an assignment with no groups.
1662 $assign = $this->create_instance();
1663
1664 $this->assertCount(self::DEFAULT_TEACHER_COUNT +
1665 self::DEFAULT_EDITING_TEACHER_COUNT +
1666 self::EXTRA_TEACHER_COUNT +
1667 self::EXTRA_EDITING_TEACHER_COUNT,
1668 $assign->testable_get_notifiable_users($this->students[0]->id));
1669
1670 // Change nonediting teachers role to not receive grader notifications.
1671 assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
1672
1673 $this->assertCount(self::DEFAULT_EDITING_TEACHER_COUNT +
1674 self::EXTRA_EDITING_TEACHER_COUNT,
1675 $assign->testable_get_notifiable_users($this->students[0]->id));
1676
1677 // Reset nonediting teachers role to default.
1678 unassign_capability($capability, $role->id, $coursecontext);
1679
1680 // Force create an assignment with SEPARATEGROUPS.
1681 $data = new stdClass();
1682 $data->courseid = $this->course->id;
1683 $data->name = 'Grouping';
1684 $groupingid = groups_create_grouping($data);
1685 groups_assign_grouping($groupingid, $this->groups[0]->id);
1686 $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1687
1688 $this->setUser($this->students[1]);
1689 $this->assertCount(4, $assign->testable_get_notifiable_users($this->students[0]->id));
1690 // Note the second student is in a group that is not in the grouping.
1691 // This means that we get all graders that are not in a group in the grouping.
1692 $this->assertCount(10, $assign->testable_get_notifiable_users($this->students[1]->id));
1693
1694 // Change nonediting teachers role to not receive grader notifications.
1695 assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
1696
1697 $this->assertCount(2, $assign->testable_get_notifiable_users($this->students[0]->id));
1698 // Note the second student is in a group that is not in the grouping.
1699 // This means that we get all graders that are not in a group in the grouping.
1700 $this->assertCount(5, $assign->testable_get_notifiable_users($this->students[1]->id));
1701 }
1702
20b7dcfc
DW
1703 public function test_group_members_only() {
1704 global $CFG;
1705
1706 $this->setAdminUser();
1707 $this->create_extra_users();
c13ac85d 1708 $CFG->enableavailability = true;
20b7dcfc
DW
1709 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id));
1710 groups_assign_grouping($grouping->id, $this->groups[0]->id);
1711
1712 // Force create an assignment with SEPARATEGROUPS.
1713 $instance = $this->getDataGenerator()->create_module('assign', array('course'=>$this->course->id),
c13ac85d 1714 array('availability' => json_encode(\core_availability\tree::get_root_json(array(
1715 \availability_grouping\condition::get_json()))),
1716 'groupingid' => $grouping->id));
20b7dcfc
DW
1717
1718 $cm = get_coursemodule_from_instance('assign', $instance->id);
1719 $context = context_module::instance($cm->id);
1720 $assign = new testable_assign($context, $cm, $this->course);
1721
1722 $this->setUser($this->teachers[0]);
8270f0d0 1723 get_fast_modinfo($this->course, 0, true);
20b7dcfc
DW
1724 $this->assertCount(5, $assign->list_participants(0, true));
1725
1726 }
1727
47f48152
DW
1728 public function test_get_uniqueid_for_user() {
1729 $this->setUser($this->editingteachers[0]);
1730 $assign = $this->create_instance();
1731
1732 foreach ($this->students as $student) {
1733 $uniqueid = $assign->get_uniqueid_for_user($student->id);
1734 $this->assertEquals($student->id, $assign->get_user_id_for_uniqueid($uniqueid));
1735 }
1736 }
1737
46692c3a 1738 public function test_show_student_summary() {
c2114099 1739 global $CFG, $PAGE;
46692c3a
DW
1740
1741 $this->setUser($this->editingteachers[0]);
1742 $assign = $this->create_instance();
c2114099 1743 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
46692c3a
DW
1744
1745 // No feedback should be available because this student has not been graded.
1746 $this->setUser($this->students[0]);
1747 $output = $assign->view_student_summary($this->students[0], true);
1748 $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if there is no grade');
1749 // Simulate adding a grade.
1750 $this->setUser($this->teachers[0]);
1751 $data = new stdClass();
1752 $data->grade = '50.0';
df211804 1753 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
46692c3a 1754
b0da618b 1755 // Now we should see the feedback.
46692c3a
DW
1756 $this->setUser($this->students[0]);
1757 $output = $assign->view_student_summary($this->students[0], true);
1758 $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback if there is a grade');
1759
1760 // Now hide the grade in gradebook.
1761 $this->setUser($this->teachers[0]);
1762 require_once($CFG->libdir.'/gradelib.php');
1763 $gradeitem = new grade_item(array(
1764 'itemtype' => 'mod',
1765 'itemmodule' => 'assign',
1766 'iteminstance' => $assign->get_instance()->id,
1767 'courseid' => $this->course->id));
1768
1769 $gradeitem->set_hidden(1, false);
1770
1771 // No feedback should be available because the grade is hidden.
1772 $this->setUser($this->students[0]);
1773 $output = $assign->view_student_summary($this->students[0], true);
1774 $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
1775
b0da618b 1776 // Do the same but add feedback.
46692c3a
DW
1777 $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
1778
1779 $this->setUser($this->teachers[0]);
1780 $grade = $assign->get_user_grade($this->students[0]->id, true);
1781 $data = new stdClass();
1782 $data->assignfeedbackcomments_editor = array('text'=>'Tomato sauce',
1783 'format'=>FORMAT_MOODLE);
1784 $plugin = $assign->get_feedback_plugin_by_type('comments');
1785 $plugin->save($grade, $data);
1786
b0da618b 1787 // Should have feedback but no grade.
46692c3a
DW
1788 $this->setUser($this->students[0]);
1789 $output = $assign->view_student_summary($this->students[0], true);
ea698284 1790 $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback even if there is no grade');
46692c3a
DW
1791 $this->assertEquals(false, strpos($output, 'Grade'), 'Do not show grade when there is no grade.');
1792 $this->assertEquals(false, strpos($output, 'Graded on'), 'Do not show graded date when there is no grade.');
45aac51a 1793
ea698284
AD
1794 // Now hide the grade in gradebook.
1795 $this->setUser($this->teachers[0]);
1796 $gradeitem = new grade_item(array(
1797 'itemtype' => 'mod',
1798 'itemmodule' => 'assign',
1799 'iteminstance' => $assign->get_instance()->id,
1800 'courseid' => $this->course->id));
1801
1802 $gradeitem->set_hidden(1, false);
1803
1804 // No feedback should be available because the grade is hidden.
1805 $this->setUser($this->students[0]);
1806 $output = $assign->view_student_summary($this->students[0], true);
1807 $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
46692c3a
DW
1808 }
1809
df211804
DW
1810 public function test_attempt_reopen_method_manual() {
1811 global $PAGE;
1812
1813 $this->setUser($this->editingteachers[0]);
1814 $assign = $this->create_instance(array('attemptreopenmethod'=>ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
1815 'maxattempts'=>3,
1816 'submissiondrafts'=>1,
1817 'assignsubmission_onlinetext_enabled'=>1));
1818 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1819
1820 // Student should be able to see an add submission button.
1821 $this->setUser($this->students[0]);
1822 $output = $assign->view_student_summary($this->students[0], true);
1823 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1824
1825 // Add a submission.
1826 $now = time();
1827 $submission = $assign->get_user_submission($this->students[0]->id, true);
1828 $data = new stdClass();
1829 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1830 'text'=>'Submission text',
1831 'format'=>FORMAT_MOODLE);
1832 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1833 $plugin->save($submission, $data);
1834
1835 // And now submit it for marking.
1836 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1837 $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1838
1839 // Verify the student cannot make changes to the submission.
1840 $output = $assign->view_student_summary($this->students[0], true);
1841 $this->assertEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1842
1843 // Mark the submission.
1844 $this->setUser($this->teachers[0]);
1845 $data = new stdClass();
1846 $data->grade = '50.0';
1847 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1848
1849 // Check the student can see the grade.
1850 $this->setUser($this->students[0]);
1851 $output = $assign->view_student_summary($this->students[0], true);
1852 $this->assertNotEquals(false, strpos($output, '50.0'));
1853
1854 // Allow the student another attempt.
1855 $this->teachers[0]->ignoresesskey = true;
1856 $this->setUser($this->teachers[0]);
1857 $result = $assign->testable_process_add_attempt($this->students[0]->id);
1858 $this->assertEquals(true, $result);
1859
1860 // Check that the previous attempt is now in the submission history table.
1861 $this->setUser($this->students[0]);
1862 $output = $assign->view_student_summary($this->students[0], true);
1863 // Need a better check.
1864 $this->assertNotEquals(false, strpos($output, 'Submission text'), 'Contains: Submission text');
1865
1866 // Check that the student now has a button for Add a new attempt".
1867 $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1868 // Check that the student now does not have a button for Submit.
1869 $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
1870
1871 // Check that the student now has a submission history.
1872 $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
1873
1874 $this->setUser($this->teachers[0]);
1875 // Check that the grading table loads correctly and contains this user.
1876 // This is also testing that we do not get duplicate rows in the grading table.
1877 $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1878 $output = $assign->get_renderer()->render($gradingtable);
1879 $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
1880
1881 // Should be 1 not 2.
1882 $this->assertEquals(1, $assign->count_submissions());
1883 $this->assertEquals(1, $assign->count_submissions_with_status('reopened'));
1884 $this->assertEquals(0, $assign->count_submissions_need_grading());
1885 $this->assertEquals(1, $assign->count_grades());
1886
1887 // Change max attempts to unlimited.
1888 $formdata = clone($assign->get_instance());
1889 $formdata->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
1890 $formdata->instance = $formdata->id;
1891 $assign->update_instance($formdata);
1892
9e3eee67
DW
1893 // Mark the submission again.
1894 $data = new stdClass();
1895 $data->grade = '60.0';
1896 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1897
1898 // Check the grade exists.
1899 $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1900 $this->assertEquals(60, (int)$grades[$this->students[0]->id]->rawgrade);
1901
1902 // Check we can reopen still.
df211804
DW
1903 $result = $assign->testable_process_add_attempt($this->students[0]->id);
1904 $this->assertEquals(true, $result);
1905
9e3eee67 1906 // Should no longer have a grade because there is no grade for the latest attempt.
df211804 1907 $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
9e3eee67 1908 $this->assertEmpty($grades);
df211804 1909
df211804 1910 }
46692c3a 1911
e77bacab
MP
1912 /**
1913 * Test reopen behavior when in "Reopen until pass" mode.
1914 */
1915 public function test_attempt_reopen_method_untilpass() {
1916 global $PAGE;
1917
1918 $this->setUser($this->editingteachers[0]);
1919 $assign = $this->create_instance(array('attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS,
1920 'maxattempts' => 3,
1921 'submissiondrafts' => 1,
1922 'assignsubmission_onlinetext_enabled' => 1));
1923 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1924
1925 // Set grade to pass to 80.
1926 $gradeitem = $assign->get_grade_item();
1927 $gradeitem->gradepass = '80.0';
1928 $gradeitem->update();
1929
1930 // Student should be able to see an add submission button.
1931 $this->setUser($this->students[0]);
1932 $output = $assign->view_student_summary($this->students[0], true);
1933 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1934
1935 // Add a submission.
1936 $now = time();
1937 $submission = $assign->get_user_submission($this->students[0]->id, true);
1938 $data = new stdClass();
1939 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1940 'text' => 'Submission text',
1941 'format' => FORMAT_MOODLE);
1942 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1943 $plugin->save($submission, $data);
1944
1945 // And now submit it for marking.
1946 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1947 $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1948
1949 // Verify the student cannot make a new attempt.
1950 $output = $assign->view_student_summary($this->students[0], true);
1951 $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1952
1953 // Mark the submission as non-passing.
1954 $this->setUser($this->teachers[0]);
1955 $data = new stdClass();
1956 $data->grade = '50.0';
1957 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1958
1959 // Check the student can see the grade.
1960 $this->setUser($this->students[0]);
1961 $output = $assign->view_student_summary($this->students[0], true);
1962 $this->assertNotEquals(false, strpos($output, '50.0'));
1963
1964 // Check that the student now has a button for Add a new attempt.
1965 $output = $assign->view_student_summary($this->students[0], true);
1966 $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1967
1968 // Check that the student now does not have a button for Submit.
1969 $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
1970
1971 // Check that the student now has a submission history.
1972 $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
1973
1974 // Add a second submission.
1975 $now = time();
1976 $submission = $assign->get_user_submission($this->students[0]->id, true, 1);
1977 $data = new stdClass();
1978 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1979 'text' => 'Submission text',
1980 'format' => FORMAT_MOODLE);
1981 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1982 $plugin->save($submission, $data);
1983
1984 // And now submit it for marking.
1985 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1986 $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1987
1988 // Mark the submission as passing.
1989 $this->setUser($this->teachers[0]);
1990 $data = new stdClass();
1991 $data->grade = '80.0';
1992 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1993
1994 // Check that the student does not have a button for Add a new attempt.
1995 $this->setUser($this->students[0]);
1996 $output = $assign->view_student_summary($this->students[0], true);
1997 $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1998
1999 // Re-mark the submission as not passing.
2000 $this->setUser($this->teachers[0]);
2001 $data = new stdClass();
2002 $data->grade = '50.0';
2003 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
2004
2005 // Check that the student now has a button for Add a new attempt.
2006 $this->setUser($this->students[0]);
2007 $output = $assign->view_student_summary($this->students[0], true);
2008 $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2009
2010 // Add a submission as a second student.
2011 $this->setUser($this->students[1]);
2012 $now = time();
2013 $submission = $assign->get_user_submission($this->students[1]->id, true);
2014 $data = new stdClass();
2015 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
2016 'text' => 'Submission text',
2017 'format' => FORMAT_MOODLE);
2018 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2019 $plugin->save($submission, $data);
2020
2021 // And now submit it for marking.
2022 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2023 $assign->testable_update_submission($submission, $this->students[1]->id, true, false);
2024
2025 // Mark the submission as passing.
2026 $this->setUser($this->teachers[0]);
2027 $data = new stdClass();
2028 $data->grade = '100.0';
2029 $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
2030
2031 // Check the student can see the grade.
2032 $this->setUser($this->students[1]);
2033 $output = $assign->view_student_summary($this->students[1], true);
2034 $this->assertNotEquals(false, strpos($output, '100.0'));
2035
2036 // Check that the student does not have a button for Add a new attempt.
2037 $output = $assign->view_student_summary($this->students[1], true);
2038 $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2039
2040 // Set grade to pass to 0, so that no attempts should reopen.
2041 $gradeitem = $assign->get_grade_item();
2042 $gradeitem->gradepass = '0';
2043 $gradeitem->update();
2044
2045 // Add another submission.
2046 $this->setUser($this->students[2]);
2047 $now = time();
2048 $submission = $assign->get_user_submission($this->students[2]->id, true);
2049 $data = new stdClass();
2050 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
2051 'text' => 'Submission text',
2052 'format' => FORMAT_MOODLE);
2053 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2054 $plugin->save($submission, $data);
2055
2056 // And now submit it for marking.
2057 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2058 $assign->testable_update_submission($submission, $this->students[2]->id, true, false);
2059
2060 // Mark the submission as graded.
2061 $this->setUser($this->teachers[0]);
2062 $data = new stdClass();
2063 $data->grade = '0.0';
2064 $assign->testable_apply_grade_to_user($data, $this->students[2]->id, 0);
2065
2066 // Check the student can see the grade.
2067 $this->setUser($this->students[2]);
2068 $output = $assign->view_student_summary($this->students[2], true);
2069 $this->assertNotEquals(false, strpos($output, '0.0'));
2070
2071 // Check that the student does not have a button for Add a new attempt.
2072 $output = $assign->view_student_summary($this->students[2], true);
2073 $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2074 }
2075
2076
f8d107b3
DM
2077 public function test_markingworkflow() {
2078 global $PAGE;
2079
2080 $this->setUser($this->editingteachers[0]);
2081 $assign = $this->create_instance(array('markingworkflow'=>1));
2082 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2083
13e82f1c 2084 // Mark the submission and set to notmarked.
f8d107b3
DM
2085 $this->setUser($this->teachers[0]);
2086 $data = new stdClass();
2087 $data->grade = '50.0';
2088 $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED;
2089 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2090
2091 // Check the student can't see the grade.
2092 $this->setUser($this->students[0]);
2093 $output = $assign->view_student_summary($this->students[0], true);
2094 $this->assertEquals(false, strpos($output, '50.0'));
2095
5047b0ea
TB
2096 // Make sure the grade isn't pushed to the gradebook.
2097 $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2098 $this->assertEmpty($grades);
2099
13e82f1c 2100 // Mark the submission and set to inmarking.
f8d107b3
DM
2101 $this->setUser($this->teachers[0]);
2102 $data = new stdClass();
2103 $data->grade = '50.0';
2104 $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INMARKING;
2105 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2106
2107 // Check the student can't see the grade.
2108 $this->setUser($this->students[0]);
2109 $output = $assign->view_student_summary($this->students[0], true);
2110 $this->assertEquals(false, strpos($output, '50.0'));
2111
5047b0ea
TB
2112 // Make sure the grade isn't pushed to the gradebook.
2113 $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2114 $this->assertEmpty($grades);
2115
13e82f1c 2116 // Mark the submission and set to readyforreview.
f8d107b3
DM
2117 $this->setUser($this->teachers[0]);
2118 $data = new stdClass();
2119 $data->grade = '50.0';
2120 $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW;
2121 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2122
2123 // Check the student can't see the grade.
2124 $this->setUser($this->students[0]);
2125 $output = $assign->view_student_summary($this->students[0], true);
2126 $this->assertEquals(false, strpos($output, '50.0'));
2127
5047b0ea
TB
2128 // Make sure the grade isn't pushed to the gradebook.
2129 $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2130 $this->assertEmpty($grades);
2131
13e82f1c 2132 // Mark the submission and set to inreview.
f8d107b3
DM
2133 $this->setUser($this->teachers[0]);
2134 $data = new stdClass();
2135 $data->grade = '50.0';
2136 $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW;
2137 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2138
2139 // Check the student can't see the grade.
2140 $this->setUser($this->students[0]);
2141 $output = $assign->view_student_summary($this->students[0], true);
2142 $this->assertEquals(false, strpos($output, '50.0'));
2143
5047b0ea
TB
2144 // Make sure the grade isn't pushed to the gradebook.
2145 $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2146 $this->assertEmpty($grades);
2147
13e82f1c 2148 // Mark the submission and set to readyforrelease.
f8d107b3
DM
2149 $this->setUser($this->teachers[0]);
2150 $data = new stdClass();
2151 $data->grade = '50.0';
2152 $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
2153 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2154
2155 // Check the student can't see the grade.
2156 $this->setUser($this->students[0]);
2157 $output = $assign->view_student_summary($this->students[0], true);
2158 $this->assertEquals(false, strpos($output, '50.0'));
2159
5047b0ea
TB
2160 // Make sure the grade isn't pushed to the gradebook.
2161 $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2162 $this->assertEmpty($grades);
2163
13e82f1c 2164 // Mark the submission and set to released.
f8d107b3
DM
2165 $this->setUser($this->teachers[0]);
2166 $data = new stdClass();
2167 $data->grade = '50.0';
2168 $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
2169 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2170
2171 // Check the student can see the grade.
2172 $this->setUser($this->students[0]);
2173 $output = $assign->view_student_summary($this->students[0], true);
2174 $this->assertNotEquals(false, strpos($output, '50.0'));
5047b0ea
TB
2175
2176 // Make sure the grade is pushed to the gradebook.
2177 $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
2178 $this->assertEquals(50, (int)$grades[$this->students[0]->id]->rawgrade);
f8d107b3
DM
2179 }
2180
2181 public function test_markerallocation() {
2182 global $PAGE;
2183
2184 $this->setUser($this->editingteachers[0]);
13e82f1c 2185 $assign = $this->create_instance(array('markingworkflow'=>1, 'markingallocation'=>1));
f8d107b3
DM
2186 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2187
13e82f1c 2188 // Allocate marker to submission.
f8d107b3
DM
2189 $data = new stdClass();
2190 $data->allocatedmarker = $this->teachers[0]->id;
2191 $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
2192
13e82f1c 2193 // Check the allocated marker can view the submission.
f8d107b3 2194 $this->setUser($this->teachers[0]);
f8d107b3 2195
230ae095
DW
2196 $users = $assign->list_participants(0, true);
2197 $user = reset($users);
2198 $this->assertEquals($this->students[0]->id, $user->id);
2199
2200 $cm = get_coursemodule_from_instance('assign', $assign->get_instance()->id);
2201 $context = context_module::instance($cm->id);
2202 $assign = new testable_assign($context, $cm, $this->course);
13e82f1c 2203 // Check that other teachers can't view this submission.
f8d107b3 2204 $this->setUser($this->teachers[1]);
230ae095
DW
2205 $users = $assign->list_participants(0, true);
2206 $this->assertEquals(0, count($users));
f8d107b3 2207 }
76e77b05 2208
52f3e060
RT
2209 /**
2210 * @expectedException moodle_exception
2211 */
57fbd5f9
DW
2212 public function test_teacher_submit_for_student() {
2213 global $PAGE;
2214
2215 $this->preventResetByRollback();
2216 $sink = $this->redirectMessages();
2217
2218 $this->setUser($this->editingteachers[0]);
2219
2220 $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
2221 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2222
2223 $this->setUser($this->students[0]);
2224 // Simulate a submission.
2225 $data = new stdClass();
2226 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2227 'text'=>'Student submission text',
2228 'format'=>FORMAT_MOODLE);
2229
2230 $notices = array();
2231 $assign->save_submission($data, $notices);
2232
2233 // Check that the submission text was saved.
2234 $output = $assign->view_student_summary($this->students[0], true);
2235 $this->assertContains('Student submission text', $output, 'Contains student submission text');
2236
2237 // Check that a teacher teacher with the extra capability can edit a students submission.
2238 $this->setUser($this->teachers[0]);
2239 $data = new stdClass();
2240 $data->userid = $this->students[0]->id;
2241 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2242 'text'=>'Teacher edited submission text',
2243 'format'=>FORMAT_MOODLE);
2244
2245 // Add the required capability.
2246 $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
2247 assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
2248 role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
2249 accesslib_clear_all_caches_for_unit_testing();
2250
2251 // Try to save the submission.
2252 $notices = array();
2253 $assign->save_submission($data, $notices);
2254
2255 // Check that the teacher can submit the students work.
2256 $data = new stdClass();
2257 $data->userid = $this->students[0]->id;
2258 $notices = array();
2259 $assign->submit_for_grading($data, $notices);
2260
2261 // Revert to draft so the student can edit it.
2262 $assign->revert_to_draft($this->students[0]->id);
2263
2264 $this->setUser($this->students[0]);
2265
2266 // Check that the submission text was saved.
2267 $output = $assign->view_student_summary($this->students[0], true);
2268 $this->assertContains('Teacher edited submission text', $output, 'Contains student submission text');
2269
2270 // Check that the student can submit their work.
2271 $data = new stdClass();
2272 $assign->submit_for_grading($data, $notices);
2273
2274 $output = $assign->view_student_summary($this->students[0], true);
2275 $this->assertNotContains(get_string('addsubmission', 'assign'), $output);
2276
2277 // Set to a default editing teacher who should not be able to edit this submission.
2278 $this->setUser($this->editingteachers[1]);
2279
2280 // Revert to draft so the submission is editable.
2281 $assign->revert_to_draft($this->students[0]->id);
2282
2283 $data = new stdClass();
2284 $data->userid = $this->students[0]->id;
2285 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2286 'text'=>'Teacher 2 edited submission text',
2287 'format'=>FORMAT_MOODLE);
2288
2289 $notices = array();
57fbd5f9
DW
2290 $assign->save_submission($data, $notices);
2291
2292 $sink->close();
2293 }
2294
9054c04d 2295 public function test_disable_submit_after_cutoff_date() {
2296 global $PAGE;
2297
2298 $this->setUser($this->editingteachers[0]);
2299 $now = time();
2300 $tomorrow = $now + 24*60*60;
2301 $lastweek = $now - 7*24*60*60;
2302 $yesterday = $now - 24*60*60;
2303
2304 $assign = $this->create_instance(array('duedate'=>$yesterday,
2305 'cutoffdate'=>$tomorrow,
2306 'assignsubmission_onlinetext_enabled'=>1));
2307 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2308
2309 // Student should be able to see an add submission button.
2310 $this->setUser($this->students[0]);
2311 $output = $assign->view_student_summary($this->students[0], true);
2312 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2313
2314 // Add a submission but don't submit now.
2315 $submission = $assign->get_user_submission($this->students[0]->id, true);
2316 $data = new stdClass();
2317 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2318 'text'=>'Submission text',
2319 'format'=>FORMAT_MOODLE);
2320 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2321 $plugin->save($submission, $data);
2322
2323 // Create another instance with cut-off and due-date already passed.
2324 $this->setUser($this->editingteachers[0]);
2325 $now = time();
2326 $assign = $this->create_instance(array('duedate'=>$lastweek,
2327 'cutoffdate'=>$yesterday,
2328 'assignsubmission_onlinetext_enabled'=>1));
2329
2330 $this->setUser($this->students[0]);
2331 $output = $assign->view_student_summary($this->students[0], true);
2332 $this->assertNotContains($output, get_string('editsubmission', 'assign'),
2333 'Should not be able to edit after cutoff date.');
2334 $this->assertNotContains($output, get_string('submitassignment', 'assign'),
2335 'Should not be able to submit after cutoff date.');
2336 }
f159ad73 2337 /**
2338 * Testing for submission comment plugin settings
2339 */
2340 public function test_submission_comment_plugin_settings() {
2341 global $CFG;
2342
2343 $commentconfig = false;
2344 if (!empty($CFG->usecomments)) {
2345 $commentconfig = $CFG->usecomments;
2346 }
2347
2348 $CFG->usecomments = true;
2349 $assign = $this->create_instance();
2350 $plugin = $assign->get_submission_plugin_by_type('comments');
2351 $this->assertEquals(1, $plugin->is_enabled('enabled'));
2352
2353 $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
2354 $plugin = $assign->get_submission_plugin_by_type('comments');
2355 $this->assertEquals(1, $plugin->is_enabled('enabled'));
2356
2357 $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
2358 $plugin = $assign->get_submission_plugin_by_type('comments');
2359 $this->assertEquals(1, $plugin->is_enabled('enabled'));
2360
2361 $CFG->usecomments = false;
2362 $assign = $this->create_instance();
2363 $plugin = $assign->get_submission_plugin_by_type('comments');
2364 $this->assertEquals(0, $plugin->is_enabled('enabled'));
2365
2366 $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
2367 $plugin = $assign->get_submission_plugin_by_type('comments');
2368 $this->assertEquals(0, $plugin->is_enabled('enabled'));
2369
2370 $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
2371 $plugin = $assign->get_submission_plugin_by_type('comments');
2372 $this->assertEquals(0, $plugin->is_enabled('enabled'));
2373
2374 $CFG->usecomments = $commentconfig;
2375 }
2376
c7a73689
DW
2377 /**
2378 * Testing for comment inline settings
2379 */
2380 public function test_feedback_comment_commentinline() {
2381 global $CFG;
2382
2383 $sourcetext = "Hello!
2384
2385I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
2386
2387URL outside a tag: https://moodle.org/logo/logo-240x60.gif
2388Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
2389
2390External link 1:<img src='https://moodle.org/logo/logo-240x60.gif' alt='Moodle'/>
2391External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\"/>
2392Internal link 1:<img src='@@PLUGINFILE@@/logo-240x60.gif' alt='Moodle'/>
2393Internal link 2:<img alt=\"Moodle\" src=\"@@PLUGINFILE@@logo-240x60.gif\"/>
2394Anchor link 1:<a href=\"@@PLUGINFILE@@logo-240x60.gif\" alt=\"bananas\">Link text</a>
2395Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
2396";
2397
2398 // Note the internal images have been stripped and the html is purified (quotes fixed in this case).
2399 $filteredtext = "Hello!
2400
2401I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
2402
2403URL outside a tag: https://moodle.org/logo/logo-240x60.gif
2404Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
2405
2406External link 1:<img src=\"https://moodle.org/logo/logo-240x60.gif\" alt=\"Moodle\" />
2407External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\" />
2408Internal link 1:
2409Internal link 2:
2410Anchor link 1:Link text
2411Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
2412";
2413
2414 $this->setUser($this->editingteachers[0]);
2415 $params = array('assignsubmission_onlinetext_enabled' => 1,
2416 'assignfeedback_comments_enabled' => 1,
2417 'assignfeedback_comments_commentinline' => 1);
2418 $assign = $this->create_instance($params);
2419
2420 $this->setUser($this->students[0]);
2421 // Add a submission but don't submit now.
2422 $submission = $assign->get_user_submission($this->students[0]->id, true);
2423 $data = new stdClass();
2424
2425 // Test the internal link is stripped, but the external one is not.
2426 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2427 'text'=>$sourcetext,
2428 'format'=>FORMAT_MOODLE);
2429
2430 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2431 $plugin->save($submission, $data);
2432
2433 $this->setUser($this->editingteachers[0]);
2434
2435 $data = new stdClass();
2436 require_once($CFG->dirroot . '/mod/assign/gradeform.php');
2437 $pagination = array('userid'=>$this->students[0]->id,
2438 'rownum'=>0,
2439 'last'=>true,
3fca6937 2440 'useridlistid' => $assign->get_useridlist_key_id(),
c7a73689
DW
2441 'attemptnumber'=>0);
2442 $formparams = array($assign, $data, $pagination);
2443 $mform = new mod_assign_grade_form(null, $formparams);
2444
2445 $this->assertEquals($filteredtext, $data->assignfeedbackcomments_editor['text']);
2446 }
2447
f159ad73 2448 /**
2449 * Testing for feedback comment plugin settings
2450 */
2451 public function test_feedback_plugin_settings() {
2452
2453 $assign = $this->create_instance();
2454 $plugin = $assign->get_feedback_plugin_by_type('comments');
2455 $this->assertEquals(0, $plugin->is_enabled('enabled'));
2456
2457 $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 0));
2458 $plugin = $assign->get_feedback_plugin_by_type('comments');
2459 $this->assertEquals(0, $plugin->is_enabled('enabled'));
2460
2461 $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
2462 $plugin = $assign->get_feedback_plugin_by_type('comments');
2463 $this->assertEquals(1, $plugin->is_enabled('enabled'));
2464 }
456d7bc7
RT
2465
2466 /**
2467 * Testing if gradebook feedback plugin is enabled.
2468 */
2469 public function test_is_gradebook_feedback_enabled() {
2470 $adminconfig = get_config('assign');
2471 $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
2472
2473 // Create assignment with gradebook feedback enabled and grade = 0.
2474 $assign = $this->create_instance(array($gradebookplugin . '_enabled' => 1, 'grades' => 0));
2475
2476 // Get gradebook feedback plugin.
2477 $gradebookplugintype = str_replace('assignfeedback_', '', $gradebookplugin);
2478 $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
2479 $this->assertEquals(1, $plugin->is_enabled('enabled'));
2480 $this->assertEquals(1, $assign->is_gradebook_feedback_enabled());
2481
2482 // Create assignment with gradebook feedback disabled and grade = 0.
2483 $assign = $this->create_instance(array($gradebookplugin . '_enabled' => 0, 'grades' => 0));
2484 $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
2485 $this->assertEquals(0, $plugin->is_enabled('enabled'));
2486 }
57fbd5f9
DW
2487
2488 /**
2489 * Testing can_edit_submission
2490 */
2491 public function test_can_edit_submission() {
2492 global $PAGE, $DB;
2493 $this->create_extra_users();
2494
2495 $this->setAdminUser();
2496 // Create assignment (onlinetext).
2497 $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
2498 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2499
2500 // Check student can edit their own submission.
2501 $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->students[0]->id));
2502 // Check student cannot edit others submission.
2503 $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->students[1]->id));
2504
2505 // Check teacher cannot (by default) edit a students submission.
2506 $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
2507
2508 // Add the required capability to edit a student submission.
2509 $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
2510 assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
2511 role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
2512 accesslib_clear_all_caches_for_unit_testing();
2513 // Retest - should now have access.
2514 $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
2515
2516 // Force create an assignment with SEPARATEGROUPS.
2517 $data = new stdClass();
2518 $data->courseid = $this->course->id;
2519 $data->name = 'Grouping';
2520 $groupingid = groups_create_grouping($data);
2521 groups_assign_grouping($groupingid, $this->groups[0]->id);
2522 groups_assign_grouping($groupingid, $this->groups[1]->id);
2523 $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
2524
2525 // Add the capability to the new assignment for extra students 0 and 1.
2526 assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
2527 role_assign($roleid, $this->extrastudents[0]->id, $assign->get_context()->id);
2528 role_assign($roleid, $this->extrastudents[1]->id, $assign->get_context()->id);
2529 accesslib_clear_all_caches_for_unit_testing();
2530
2531 // Verify the extra student does not have the capability to edit a submission not in their group.
2532 $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[1]->id));
2533 // Verify the extra student does have the capability to edit a submission in their group.
2534 $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[0]->id));
2535
2536 }
323f221b
MH
2537
2538 /**
2539 * Test if the view blind details capability works
2540 */
2541 public function test_can_view_blind_details() {
eb4c4661
AN
2542 global $DB;
2543 // Note: The shared setUp leads to terrible tests. Please don't use it.
2544 $roles = $DB->get_records('role', null, '', 'shortname, id');
2545 $course = $this->getDataGenerator()->create_course([]);
323f221b
MH
2546
2547 $student = $this->students[0];// Get a student user.
eb4c4661
AN
2548 $this->getDataGenerator()->enrol_user($student->id,
2549 $course->id,
2550 $roles['student']->id);
2551
323f221b
MH
2552 // Create a teacher. Shouldn't be able to view blind marking ID.
2553 $teacher = $this->getDataGenerator()->create_user();
2554
2555 $this->getDataGenerator()->enrol_user($teacher->id,
eb4c4661
AN
2556 $course->id,
2557 $roles['teacher']->id);
323f221b
MH
2558
2559 // Create a manager.. Should be able to view blind marking ID.
2560 $manager = $this->getDataGenerator()->create_user();
e28d6b29 2561 $this->getDataGenerator()->enrol_user($manager->id,
eb4c4661
AN
2562 $course->id,
2563 $roles['manager']->id);
323f221b
MH
2564
2565 // Generate blind marking assignment.
eb4c4661 2566 $assign = $this->create_instance(array('course' => $course->id, 'blindmarking' => 1));
323f221b
MH
2567 $this->assertEquals(true, $assign->is_blind_marking());
2568
2569 // Test student names are hidden to teacher.
e28d6b29
MG
2570 $this->setUser($teacher);
2571 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
2572 $output = $assign->get_renderer()->render($gradingtable);
323f221b
MH
2573 $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign'))); // "Participant" is somewhere on the page.
2574 $this->assertEquals(false, strpos($output, fullname($student))); // Students full name doesn't appear.
2575
2576 // Test student names are visible to manager.
e28d6b29
MG
2577 $this->setUser($manager);
2578 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
2579 $output = $assign->get_renderer()->render($gradingtable);
59e95487
MH
2580 $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));
2581 $this->assertEquals(true, strpos($output, fullname($student)));
323f221b 2582 }
6d6e297b
DM
2583
2584 /**
2585 * Testing get_shared_group_members
2586 */
2587 public function test_get_shared_group_members() {
2588 $this->create_extra_users();
2589 $this->setAdminUser();
2590
2591 // Force create an assignment with SEPARATEGROUPS.
2592 $data = new stdClass();
2593 $data->courseid = $this->course->id;
2594 $data->name = 'Grouping';
2595 $groupingid = groups_create_grouping($data);
2596 groups_assign_grouping($groupingid, $this->groups[0]->id);
2597 groups_assign_grouping($groupingid, $this->groups[1]->id);
2598 $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
2599 $cm = $assign->get_course_module();
2600
2601 // Add the capability to access allgroups.
2602 $roleid = create_role('Access all groups role', 'accessallgroupsrole', '');
2603 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $roleid, $assign->get_context()->id);
2604 role_assign($roleid, $this->extrastudents[3]->id, $assign->get_context()->id);
2605 accesslib_clear_all_caches_for_unit_testing();
2606
2607 // Get shared group members for students 0 and 1.
2608 $groupmembers = array();
2609 $groupmembers[0] = $assign->get_shared_group_members($cm, $this->students[0]->id);
2610 $groupmembers[1] = $assign->get_shared_group_members($cm, $this->students[1]->id);
2611
2612 // They should share groups with extrastudents 0 and 1.
2613 $this->assertTrue(in_array($this->extrastudents[0]->id, $groupmembers[0]));
2614 $this->assertFalse(in_array($this->extrastudents[0]->id, $groupmembers[1]));
2615 $this->assertTrue(in_array($this->extrastudents[1]->id, $groupmembers[1]));
2616 $this->assertFalse(in_array($this->extrastudents[1]->id, $groupmembers[0]));
2617
2618 // Lists of group members for students and extrastudents should be the same.
2619 $this->assertEquals($groupmembers[0], $assign->get_shared_group_members($cm, $this->extrastudents[0]->id));
2620 $this->assertEquals($groupmembers[1], $assign->get_shared_group_members($cm, $this->extrastudents[1]->id));
2621
2622 // Get all group members for extrastudent 3 wich can access all groups.
2623 $allgroupmembers = $assign->get_shared_group_members($cm, $this->extrastudents[3]->id);
2624
2625 // Extrastudent 3 should see students 0 and 1, extrastudent 0 and 1.
2626 $this->assertTrue(in_array($this->students[0]->id, $allgroupmembers));
2627 $this->assertTrue(in_array($this->students[1]->id, $allgroupmembers));
2628 $this->assertTrue(in_array($this->extrastudents[0]->id, $allgroupmembers));
2629 $this->assertTrue(in_array($this->extrastudents[1]->id , $allgroupmembers));
2630 }
47f48152 2631
92ea1fa0
JL
2632 /**
2633 * Test get plugins file areas
2634 */
2635 public function test_get_plugins_file_areas() {
2636 $this->setUser($this->editingteachers[0]);
2637 $assign = $this->create_instance();
2638
2639 // Test that all the submission and feedback plugins are returning the expected file aras.
2640 $usingfilearea = 0;
11ee4f71 2641 $coreplugins = core_plugin_manager::standard_plugins_list('assignsubmission');
92ea1fa0
JL
2642 foreach ($assign->get_submission_plugins() as $plugin) {
2643 $type = $plugin->get_type();
11ee4f71
AO
2644 if (!in_array($type, $coreplugins)) {
2645 continue;
2646 }
92ea1fa0
JL
2647 $fileareas = $plugin->get_file_areas();
2648
2649 if ($type == 'onlinetext') {
2650 $this->assertEquals(array('submissions_onlinetext' => 'Online text'), $fileareas);
2651 $usingfilearea++;
2652 } else if ($type == 'file') {
2653 $this->assertEquals(array('submission_files' => 'File submissions'), $fileareas);
2654 $usingfilearea++;
2655 } else {
2656 $this->assertEmpty($fileareas);
2657 }
2658 }
2659 $this->assertEquals(2, $usingfilearea);
2660
2661 $usingfilearea = 0;
11ee4f71 2662 $coreplugins = core_plugin_manager::standard_plugins_list('assignfeedback');
92ea1fa0
JL
2663 foreach ($assign->get_feedback_plugins() as $plugin) {
2664 $type = $plugin->get_type();
11ee4f71
AO
2665 if (!in_array($type, $coreplugins)) {
2666 continue;
2667 }
92ea1fa0
JL
2668 $fileareas = $plugin->get_file_areas();
2669
2670 if ($type == 'editpdf') {
2671 $this->assertEquals(array('download' => 'Annotate PDF'), $fileareas);
2672 $usingfilearea++;
2673 } else if ($type == 'file') {
2674 $this->assertEquals(array('feedback_files' => 'Feedback files'), $fileareas);
2675 $usingfilearea++;
2676 } else {
2677 $this->assertEmpty($fileareas);
2678 }
2679 }
2680 $this->assertEquals(2, $usingfilearea);
2681 }
2682
26aedd4a
CB
2683 /**
2684 * Test override exists
2685 *
2686 * This function needs to obey the group override logic as per the assign grading table and
2687 * the overview block.
2688 */
2689 public function test_override_exists() {
2690 global $DB;
2691
2692 $this->setAdminUser();
2693
2694 $course = $this->getDataGenerator()->create_course();
2695
2696 // Create an assign instance.
2697 $assign = $this->create_instance(['course' => $course]);
2698 $assigninstance = $assign->get_instance();
2699
2700 // Create users.
2701 $users = [
2702 'Only in group A' => $this->getDataGenerator()->create_user(),
2703 'Only in group B' => $this->getDataGenerator()->create_user(),
2704 'In group A and B (no user override)' => $this->getDataGenerator()->create_user(),
2705 'In group A and B (user override)' => $this->getDataGenerator()->create_user(),
2706 'In no groups' => $this->getDataGenerator()->create_user()
2707 ];
2708
2709 // Enrol users.
2710 foreach ($users as $user) {
2711 $this->getDataGenerator()->enrol_user($user->id, $course->id);
2712 }
2713
2714 // Create groups.
2715 $groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
2716 $groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
2717
2718 // Add members to groups.
2719 // Group A.
2720 $this->getDataGenerator()->create_group_member(
2721 ['groupid' => $groupa->id, 'userid' => $users['Only in group A']->id]);
2722 $this->getDataGenerator()->create_group_member(
2723 ['groupid' => $groupa->id, 'userid' => $users['In group A and B (no user override)']->id]);
2724 $this->getDataGenerator()->create_group_member(
2725 ['groupid' => $groupa->id, 'userid' => $users['In group A and B (user override)']->id]);
2726
2727 // Group B.
2728 $this->getDataGenerator()->create_group_member(
2729 ['groupid' => $groupb->id, 'userid' => $users['Only in group B']->id]);
2730 $this->getDataGenerator()->create_group_member(
2731 ['groupid' => $groupb->id, 'userid' => $users['In group A and B (no user override)']->id]);
2732 $this->getDataGenerator()->create_group_member(
2733 ['groupid' => $groupb->id, 'userid' => $users['In group A and B (user override)']->id]);
2734
2735 // Overrides for each of the groups, and a user override.
2736 $overrides = [
2737 // Override for group A, highest priority (numerically lowest sortorder).
2738 [
2739 'assignid' => $assigninstance->id,
2740 'groupid' => $groupa->id,
2741 'userid' => null,
2742 'sortorder' => 1,
2743 'allowsubmissionsfromdate' => 1,
2744 'duedate' => 2,
2745 'cutoffdate' => 3
2746 ],
2747 // Override for group B, lower priority (numerically higher sortorder).
2748 [
2749 'assignid' => $assigninstance->id,
2750 'groupid' => $groupb->id,
2751 'userid' => null,
2752 'sortorder' => 2,
2753 'allowsubmissionsfromdate' => 5,
2754 'duedate' => 6,
2755 'cutoffdate' => 6
2756 ],
2757 // User override.
2758 [
2759 'assignid' => $assigninstance->id,
2760 'groupid' => null,
2761 'userid' => $users['In group A and B (user override)']->id,
2762 'sortorder' => null,
2763 'allowsubmissionsfromdate' => 7,
2764 'duedate' => 8,
2765 'cutoffdate' => 9
2766 ],
2767 ];
2768
2769 // Kinda hacky, need to add the ID to the overrides in the above array
2770 // for later.
2771 foreach ($overrides as &$override) {
2772 $override['id'] = $DB->insert_record('assign_overrides', $override);
2773 }
2774
2775 $returnedoverrides = array_reduce(array_keys($users), function($carry, $description) use ($users, $assign) {
2776 return $carry + ['For user ' . lcfirst($description) => $assign->override_exists($users[$description]->id)];
2777 }, []);
2778
2779 // Test we get back the correct override from override_exists (== checks all object members match).
2780 // User only in group A should see the group A override.
2781 $this->assertTrue($returnedoverrides['For user only in group A'] == (object)$overrides[0]);
2782 // User only in group B should see the group B override.
2783 $this->assertTrue($returnedoverrides['For user only in group B'] == (object)$overrides[1]);
2784 // User in group A and B, with no user override should see the group A override
2785 // as it has higher priority (numerically lower sortorder).
2786 $this->assertTrue($returnedoverrides['For user in group A and B (no user override)'] == (object)$overrides[0]);
2787 // User in group A and B, with a user override should see the user override
2788 // as it has higher priority (numerically lower sortorder).
2789 $this->assertTrue($returnedoverrides['For user in group A and B (user override)'] == (object)$overrides[2]);
2790 // User with no overrides should get nothing.
2791 $this->assertNull($returnedoverrides['For user in no groups']->duedate);
2792 $this->assertNull($returnedoverrides['For user in no groups']->cutoffdate);
2793 $this->assertNull($returnedoverrides['For user in no groups']->allowsubmissionsfromdate);
2794 }
2795
002864cc
EM
2796 /**
2797 * Test the quicksave grades processor
2798 */
2799 public function test_process_save_quick_grades() {
2800 $this->editingteachers[0]->ignoresesskey = true;
2801 $this->setUser($this->editingteachers[0]);
2802
2803 $assign = $this->create_instance(array('attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL));
2804
2805 // Initially grade the user.
2806 $grade = $assign->get_user_grade($this->students[0]->id, false);
2807 if (!$grade) {
2808 $grade = new stdClass();
2809 $grade->attemptnumber = '';
2810 $grade->timemodified = '';
2811 }
2812 $data = array(
2813 'grademodified_' . $this->students[0]->id => $grade->timemodified,
2814 'gradeattempt_' . $this->students[0]->id => $grade->attemptnumber,
2815 'quickgrade_' . $this->students[0]->id => '60.0'
2816 );
2817 $result = $assign->testable_process_save_quick_grades($data);
2818 $this->assertContains(get_string('quickgradingchangessaved', 'assign'), $result);
2819 $grade = $assign->get_user_grade($this->students[0]->id, false);
2820 $this->assertEquals('60.0', $grade->grade);
2821
2822 // Attempt to grade with a past attempts grade info.
2823 $assign->testable_process_add_attempt($this->students[0]->id);
2824 $data = array(
2825 'grademodified_' . $this->students[0]->id => $grade->timemodified,
2826 'gradeattempt_' . $this->students[0]->id => $grade->attemptnumber,
2827 'quickgrade_' . $this->students[0]->id => '50.0'
2828 );
2829 $result = $assign->testable_process_save_quick_grades($data);
2830 $this->assertContains(get_string('errorrecordmodified', 'assign'), $result);
2831 $grade = $assign->get_user_grade($this->students[0]->id, false);
2832 $this->assertFalse($grade);
2833
2834 // Attempt to grade a the attempt.
2835 $submission = $assign->get_user_submission($this->students[0]->id, false);
2836 $data = array(
2837 'grademodified_' . $this->students[0]->id => '',
2838 'gradeattempt_' . $this->students[0]->id => $submission->attemptnumber,
2839 'quickgrade_' . $this->students[0]->id => '40.0'
2840 );
2841 $result = $assign->testable_process_save_quick_grades($data);
2842 $this->assertContains(get_string('quickgradingchangessaved', 'assign'), $result);
2843 $grade = $assign->get_user_grade($this->students[0]->id, false);
2844 $this->assertEquals('40.0', $grade->grade);
2845
2846 // Catch grade update conflicts.
2847 // Save old data for later.
2848 $pastdata = $data;
2849 // Update the grade the 'good' way.
2850 $data = array(
2851 'grademodified_' . $this->students[0]->id => $grade->timemodified,
2852 'gradeattempt_' . $this->students[0]->id => $grade->attemptnumber,
2853 'quickgrade_' . $this->students[0]->id => '30.0'
2854 );
2855 $result = $assign->testable_process_save_quick_grades($data);
2856 $this->assertContains(get_string('quickgradingchangessaved', 'assign'), $result);
2857 $grade = $assign->get_user_grade($this->students[0]->id, false);
2858 $this->assertEquals('30.0', $grade->grade);
2859
2860 // Now update using 'old' data. Should fail.
2861 $result = $assign->testable_process_save_quick_grades($pastdata);
2862 $this->assertContains(get_string('errorrecordmodified', 'assign'), $result);
2863 $grade = $assign->get_user_grade($this->students[0]->id, false);
2864 $this->assertEquals('30.0', $grade->grade);
2865 }
0cf8082a
AG
2866
2867 /**
2868 * Test updating activity completion when submitting an assessment.
2869 */
2870 public function test_update_activity_completion_records_solitary_submission() {
2871 $assign = $this->create_instance(array('grade' => 100,
2872 'completion' => COMPLETION_TRACKING_AUTOMATIC,
2873 'requireallteammemberssubmit' => 0));
2874
2875 $cm = $assign->get_course_module();
2876
2877 $student = $this->students[0];
2878 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
2879
2880 $this->setUser($student);
2881
2882 // Simulate a submission.
2883 $data = new stdClass();
2884 $data->onlinetext_editor = array(
2885 'itemid' => file_get_unused_draft_itemid(),
2886 'text' => 'Student submission text',
2887 'format' => FORMAT_MOODLE
2888 );
2889 $completion = new completion_info($this->course);
2890
2891 $notices = array();
2892 $assign->save_submission($data, $notices);
2893
2894 $submission = $assign->get_user_submission($student->id, true);
2895
2896 // Check that completion is not met yet.
2897 $completiondata = $completion->get_data($cm, false, $student->id);
2898 $this->assertEquals(0, $completiondata->completionstate);
2899 $assign->testable_update_activity_completion_records(0, 0, $submission,
2900 $student->id, COMPLETION_COMPLETE, $completion);
2901 // Completion should now be met.
2902 $completiondata = $completion->get_data($cm, false, $student->id);
2903 $this->assertEquals(1, $completiondata->completionstate);
2904 }
2905
2906 /**
2907 * Test updating activity completion when submitting an assessment.
2908 */
2909 public function test_update_activity_completion_records_team_submission() {
2910 $assign = $this->create_instance(array('grade' => 100,
2911 'completion' => COMPLETION_TRACKING_AUTOMATIC,
2912 'teamsubmission' => 1));
2913
2914 $cm = $assign->get_course_module();
2915
2916 $student1 = $this->students[0];
2917 $student2 = $this->students[1];
2918 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
2919
2920 // Put both users into a group.
2921 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
2922 $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $student1->id));
2923 $this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $student2->id));
2924
2925 $this->setUser($student1);
2926
2927 // Simulate a submission.
2928 $data = new stdClass();
2929 $data->onlinetext_editor = array(
2930 'itemid' => file_get_unused_draft_itemid(),
2931 'text' => 'Student submission text',
2932 'format' => FORMAT_MOODLE
2933 );
2934 $completion = new completion_info($this->course);
2935
2936 $notices = array();
2937 $assign->save_submission($data, $notices);
2938
2939 $submission = $assign->get_user_submission($student1->id, true);
2940 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2941 $submission->groupid = $group1->id;
2942
2943 // Check that completion is not met yet.
2944 $completiondata = $completion->get_data($cm, false, $student1->id);
2945 $this->assertEquals(0, $completiondata->completionstate);
2946 $completiondata = $completion->get_data($cm, false, $student2->id);
2947 $this->assertEquals(0, $completiondata->completionstate);
2948 $assign->testable_update_activity_completion_records(1, 0, $submission, $student1->id,
2949 COMPLETION_COMPLETE, $completion);
2950 // Completion should now be met.
2951 $completiondata = $completion->get_data($cm, false, $student1->id);
2952 $this->assertEquals(1, $completiondata->completionstate);
2953 $completiondata = $completion->get_data($cm, false, $student2->id);
2954 $this->assertEquals(1, $completiondata->completionstate);
2955 }
7323c473
JO
2956
2957 public function get_assignments_with_rescaled_null_grades_provider() {
2958 return [
2959 'Negative less than one is errant' => [
2960 'grade' => -0.64,
2961 'count' => 1,
2962 ],
2963 'Negative more than one is errant' => [
2964 'grade' => -30.18,
2965 'count' => 1,
2966 ],
2967 'Negative one exactly is not errant' => [
2968 'grade' => ASSIGN_GRADE_NOT_SET,
2969 'count' => 0,
2970 ],
2971 'Positive grade is not errant' => [
2972 'grade' => 1,
2973 'count' => 0,
2974 ],
2975 'Large grade is not errant' => [
2976 'grade' => 100,
2977 'count' => 0,
2978 ],
2979 'Zero grade is not errant' => [
2980 'grade' => 0,
2981 'count' => 0,
2982 ],
2983 ];
2984 }
2985
2986 /**
2987 * Test determining if the assignment as any null grades that were rescaled.
2988 * @dataProvider get_assignments_with_rescaled_null_grades_provider
2989 */
2990 public function test_get_assignments_with_rescaled_null_grades($grade, $count) {
2991 global $DB;
2992
2993 $this->resetAfterTest();
2994
2995 $assign = $this->create_instance(array('grade' => 100));
2996
2997 // Try getting a student's grade. This will give a grade of -1.
2998 // Then we can override it with a bad negative grade.
2999 $assign->get_user_grade($this->students[0]->id, true);
3000
3001 // Set the grade to something errant.
3002 $DB->set_field(
3003 'assign_grades',
3004 'grade',
3005 $grade,
3006 [
3007 'userid' => $this->students[0]->id,
3008 'assignment' => $assign->get_instance()->id,
3009 ]
3010 );
3011
3012 $this->assertCount($count, get_assignments_with_rescaled_null_grades());
3013 }
3014
3015 /**
3016 * Data provider for test_fix_null_grades
3017 * @return array[] Test data for test_fix_null_grades. Each element should contain grade, expectedcount and gradebookvalue
3018 */
3019 public function fix_null_grades_provider() {
3020 return [
3021 'Negative less than one is errant' => [
3022 'grade' => -0.64,
3023 'gradebookvalue' => null,
3024 ],
3025 'Negative more than one is errant' => [
3026 'grade' => -30.18,
3027 'gradebookvalue' => null,
3028 ],
3029 'Negative one exactly is not errant, but shouldn\'t be pushed to gradebook' => [
3030 'grade' => ASSIGN_GRADE_NOT_SET,
3031 'gradebookvalue' => null,
3032 ],
3033 'Positive grade is not errant' => [
3034 'grade' => 1,
3035 'gradebookvalue' => 1,
3036 ],
3037 'Large grade is not errant' => [
3038 'grade' => 100,
3039 'gradebookvalue' => 100,
3040 ],
3041 'Zero grade is not errant' => [
3042 'grade' => 0,
3043 'gradebookvalue' => 0,
3044 ],
3045 ];
3046 }
3047
3048 /**
3049 * Test fix_null_grades
3050 * @param number $grade The grade we should set in the assign grading table.
3051 * @param number $expectedcount The finalgrade we expect in the gradebook after fixing the grades.
3052 * @dataProvider fix_null_grades_provider
3053 */
3054 public function test_fix_null_grades($grade, $gradebookvalue) {
3055 global $DB;
3056
3057 $this->resetAfterTest();
3058
3059 $studentid = $this->students[0]->id;
3060
3061 $assign = $this->create_instance();
3062
3063 // Try getting a student's grade. This will give a grade of -1.
3064 // Then we can override it with a bad negative grade.
3065 $assign->get_user_grade($studentid, true);
3066
3067 // Set the grade to something errant.
3068 $DB->set_field(
3069 'assign_grades',
3070 'grade',
3071 $grade,
3072 [
3073 'userid' => $studentid,
3074 'assignment' => $assign->get_instance()->id,
3075 ]
3076 );
3077 $assign->grade = $grade;
3078 $assigntemp = clone $assign->get_instance();
3079 $assigntemp->cmidnumber = $assign->get_course_module()->idnumber;
3080 assign_update_grades($assigntemp);
3081
3082 // Check that the gradebook was updated with the assign grade. So we can guarentee test results later on.
3083 $expectedgrade = $grade == -1 ? null : $grade; // Assign sends null to the gradebook for -1 grades.
3084 $gradegrade = grade_grade::fetch(array('userid' => $studentid, 'itemid' => $assign->get_grade_item()->id));
3085 $this->assertEquals($expectedgrade, $gradegrade->rawgrade);
3086
3087 // Call fix_null_grades().
3088 $method = new ReflectionMethod(assign::class, 'fix_null_grades');
3089 $method->setAccessible(true);
3090 $result = $method->invoke($assign);
3091
3092 $this->assertSame(true, $result);
3093
3094 $gradegrade = grade_grade::fetch(array('userid' => $studentid, 'itemid' => $assign->get_grade_item()->id));
3095
3096 // Check that the grade was updated in the gradebook by fix_null_grades.
3097 $this->assertEquals($gradebookvalue, $gradegrade->finalgrade);
3098 }
69019819
ZT
3099
3100 /**
3101 * Test grade override displays 'Graded' for students
3102 */
3103 public function test_grade_submission_override() {
3104 global $DB, $PAGE, $OUTPUT;
3105
3106 $this->setUser($this->editingteachers[0]);
3107 $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1));
3108
3109 $studentid = $this->students[0]->id;
3110
3111 // Simulate adding a grade.
3112 $this->setUser($this->teachers[0]);
3113 $data = new stdClass();
3114 $data->grade = '50.0';
3115 $assign->testable_apply_grade_to_user($data, $studentid, 0);
3116
3117 // Set grade override.
3118 $gradegrade = grade_grade::fetch(array('userid' => $studentid, 'itemid' => $assign->get_grade_item()->id));
3119
3120 // Check that grade submission is not overridden yet.
3121 $this->assertEquals(false, $gradegrade->is_overridden());
3122
3123 // Simulate a submission.
3124 $this->setUser($this->students[0]);
3125 $submission = $assign->get_user_submission($studentid, true);
3126
3127 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
3128
3129 // Set override grade grade, and check that grade submission has been overridden.
3130 $gradegrade->set_overridden(true);
3131 $this->assertEquals(true, $gradegrade->is_overridden());
3132
3133 // Check that submissionslocked message 'This assignment is not accepting submissions' does not appear for student.
3134 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
3135 $output = $assign->get_renderer()->render($gradingtable);
3136 $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
3137
3138 $assignsubmissionstatus = $assign->get_assign_submission_status_renderable($this->students[0], true);
3139 $output2 = $assign->get_renderer()->render($assignsubmissionstatus);
3140
3141 // Check that submissionslocked 'This assignment is not accepting submissions' message does not appear for student.
3142 $this->assertNotContains(get_string('submissionslocked', 'assign'), $output2);
3143 // Check that submissionstatus_marked 'Graded' message does appear for student.
3144 $this->assertContains(get_string('submissionstatus_marked', 'assign'), $output2);
3145 }
002864cc 3146}