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 * Unit tests for mod/workshop/lib.php.
18 *
19 * @package    mod_workshop
20 * @copyright  2017 Simey Lameze <simey@moodle.com>
21 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22 */
23defined('MOODLE_INTERNAL') || die();
24
25global $CFG;
26require_once($CFG->dirroot . '/mod/workshop/lib.php');
27
28/**
29 * Unit tests for mod/workshop/lib.php.
30 *
31 * @copyright  2017 Simey Lameze <simey@moodle.com>
32 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
33 */
34class mod_workshop_lib_testcase extends advanced_testcase {
35
36    /**
37     * Test calendar event provide action open.
38     */
39    public function test_workshop_core_calendar_provide_event_action_open() {
40        $this->resetAfterTest();
41        $this->setAdminUser();
42
43        $now = time();
44        $course = $this->getDataGenerator()->create_course();
45        $workshop = $this->getDataGenerator()->create_module('workshop', ['course' => $course->id,
46            'submissionstart' => $now - DAYSECS, 'submissionend' => $now + DAYSECS]);
47        $event = $this->create_action_event($course->id, $workshop->id, WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN);
48
49        $factory = new \core_calendar\action_factory();
50        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory);
51
52        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
53        $this->assertEquals(get_string('viewworkshopsummary', 'workshop'), $actionevent->get_name());
54        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
55        $this->assertEquals(1, $actionevent->get_item_count());
56        $this->assertTrue($actionevent->is_actionable());
57    }
58
59    /**
60     * Test calendar event provide action open for a non user.
61     */
62    public function test_workshop_core_calendar_provide_event_action_open_for_non_user() {
63        global $CFG;
64
65        $this->resetAfterTest();
66        $this->setAdminUser();
67
68        $now = time();
69        $course = $this->getDataGenerator()->create_course();
70        $workshop = $this->getDataGenerator()->create_module('workshop', ['course' => $course->id,
71            'submissionstart' => $now - DAYSECS, 'submissionend' => $now + DAYSECS]);
72        $event = $this->create_action_event($course->id, $workshop->id, WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN);
73
74        // Now, log out.
75        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
76        $this->setUser();
77
78        $factory = new \core_calendar\action_factory();
79        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory);
80
81        // Confirm the event is not shown at all.
82        $this->assertNull($actionevent);
83    }
84
85    /**
86     * Test calendar event provide action open when user id is provided.
87     */
88    public function test_workshop_core_calendar_provide_event_action_open_for_user() {
89        global $CFG;
90
91        $this->resetAfterTest();
92        $this->setAdminUser();
93
94        $now = time();
95        $course = $this->getDataGenerator()->create_course();
96        $workshop = $this->getDataGenerator()->create_module('workshop', ['course' => $course->id,
97            'submissionstart' => $now - DAYSECS, 'submissionend' => $now + DAYSECS]);
98        $event = $this->create_action_event($course->id, $workshop->id, WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN);
99        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
100
101        // Now log out.
102        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
103        $this->setUser();
104
105        $factory = new \core_calendar\action_factory();
106        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory, $student->id);
107
108        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
109        $this->assertEquals(get_string('viewworkshopsummary', 'workshop'), $actionevent->get_name());
110        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
111        $this->assertEquals(1, $actionevent->get_item_count());
112        $this->assertTrue($actionevent->is_actionable());
113    }
114
115    /**
116     * Test calendar event provide action closed.
117     */
118    public function test_workshop_core_calendar_provide_event_action_closed() {
119        $this->resetAfterTest();
120        $this->setAdminUser();
121
122        $course = $this->getDataGenerator()->create_course();
123        $workshop = $this->getDataGenerator()->create_module('workshop', array('course' => $course->id,
124            'submissionend' => time() - DAYSECS));
125        $event = $this->create_action_event($course->id, $workshop->id, WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN);
126
127        $factory = new \core_calendar\action_factory();
128        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory);
129
130        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
131        $this->assertEquals(get_string('viewworkshopsummary', 'workshop'), $actionevent->get_name());
132        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
133        $this->assertEquals(1, $actionevent->get_item_count());
134        $this->assertTrue($actionevent->is_actionable());
135    }
136
137    /**
138     * Test calendar event provide action closed for a non user.
139     */
140    public function test_workshop_core_calendar_provide_event_action_closed_for_non_user() {
141        global $CFG;
142
143        $this->resetAfterTest();
144        $this->setAdminUser();
145
146        $course = $this->getDataGenerator()->create_course();
147        $workshop = $this->getDataGenerator()->create_module('workshop', array('course' => $course->id,
148            'submissionend' => time() - DAYSECS));
149        $event = $this->create_action_event($course->id, $workshop->id, WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN);
150
151        // Now, log out.
152        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
153        $this->setUser();
154
155        $factory = new \core_calendar\action_factory();
156        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory);
157
158        // Confirm the event is not shown at all.
159        $this->assertNull($actionevent);
160    }
161
162    /**
163     * Test calendar event provide action closed when user id is provided.
164     */
165    public function test_workshop_core_calendar_provide_event_action_closed_for_user() {
166        global $CFG;
167
168        $this->resetAfterTest();
169        $this->setAdminUser();
170
171        $course = $this->getDataGenerator()->create_course();
172        $workshop = $this->getDataGenerator()->create_module('workshop', array('course' => $course->id,
173            'submissionend' => time() - DAYSECS));
174        $event = $this->create_action_event($course->id, $workshop->id, WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN);
175        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
176
177        // Now log out.
178        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
179        $this->setUser();
180
181        $factory = new \core_calendar\action_factory();
182        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory, $student->id);
183
184        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
185        $this->assertEquals(get_string('viewworkshopsummary', 'workshop'), $actionevent->get_name());
186        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
187        $this->assertEquals(1, $actionevent->get_item_count());
188        $this->assertTrue($actionevent->is_actionable());
189    }
190
191    /**
192     * Test calendar event action open in future.
193     */
194    public function test_workshop_core_calendar_provide_event_action_open_in_future() {
195        $this->resetAfterTest();
196        $this->setAdminUser();
197
198        $course = $this->getDataGenerator()->create_course();
199        $workshop = $this->getDataGenerator()->create_module('workshop', ['course' => $course->id,
200            'submissionstart' => time() + DAYSECS]);
201        $event = $this->create_action_event($course->id, $workshop->id, WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN);
202
203        $factory = new \core_calendar\action_factory();
204        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory);
205
206        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
207        $this->assertEquals(get_string('viewworkshopsummary', 'workshop'), $actionevent->get_name());
208        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
209        $this->assertEquals(1, $actionevent->get_item_count());
210        $this->assertTrue($actionevent->is_actionable());
211    }
212
213    /**
214     * Test calendar event action open in future for a non user.
215     */
216    public function test_workshop_core_calendar_provide_event_action_open_in_future_for_non_user() {
217        global $CFG;
218
219        $this->resetAfterTest();
220        $this->setAdminUser();
221
222        $course = $this->getDataGenerator()->create_course();
223        $workshop = $this->getDataGenerator()->create_module('workshop', ['course' => $course->id,
224            'submissionstart' => time() + DAYSECS]);
225        $event = $this->create_action_event($course->id, $workshop->id, WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN);
226
227        // Now, log out.
228        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
229        $this->setUser();
230
231        $factory = new \core_calendar\action_factory();
232        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory);
233
234        // Confirm the event is not shown at all.
235        $this->assertNull($actionevent);
236    }
237
238    /**
239     * Test calendar event action open in future when user id is provided.
240     */
241    public function test_workshop_core_calendar_provide_event_action_open_in_future_for_user() {
242        global $CFG;
243
244        $this->resetAfterTest();
245        $this->setAdminUser();
246
247        $course = $this->getDataGenerator()->create_course();
248        $workshop = $this->getDataGenerator()->create_module('workshop', ['course' => $course->id,
249            'submissionstart' => time() + DAYSECS]);
250        $event = $this->create_action_event($course->id, $workshop->id, WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN);
251        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
252
253        // Now log out.
254        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
255        $this->setUser();
256
257        $factory = new \core_calendar\action_factory();
258        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory, $student->id);
259
260        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
261        $this->assertEquals(get_string('viewworkshopsummary', 'workshop'), $actionevent->get_name());
262        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
263        $this->assertEquals(1, $actionevent->get_item_count());
264        $this->assertTrue($actionevent->is_actionable());
265    }
266
267    /**
268     * Test calendar event with no time specified.
269     */
270    public function test_workshop_core_calendar_provide_event_action_no_time_specified() {
271        $this->resetAfterTest();
272        $this->setAdminUser();
273
274        $course = $this->getDataGenerator()->create_course();
275        $workshop = $this->getDataGenerator()->create_module('workshop', ['course' => $course->id]);
276        $event = $this->create_action_event($course->id, $workshop->id, WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN);
277
278        $factory = new \core_calendar\action_factory();
279        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory);
280
281        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
282        $this->assertEquals(get_string('viewworkshopsummary', 'workshop'), $actionevent->get_name());
283        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
284        $this->assertEquals(1, $actionevent->get_item_count());
285        $this->assertTrue($actionevent->is_actionable());
286    }
287
288    /**
289     * Test calendar event with no time specified for a non user.
290     */
291    public function test_workshop_core_calendar_provide_event_action_no_time_specified_for_non_user() {
292        global $CFG;
293
294        $this->resetAfterTest();
295        $this->setAdminUser();
296
297        $course = $this->getDataGenerator()->create_course();
298        $workshop = $this->getDataGenerator()->create_module('workshop', ['course' => $course->id]);
299        $event = $this->create_action_event($course->id, $workshop->id, WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN);
300
301        // Now, log out.
302        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
303        $this->setUser();
304
305        $factory = new \core_calendar\action_factory();
306        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory);
307
308        // Confirm the event is not shown at all.
309        $this->assertNull($actionevent);
310    }
311
312    public function test_workshop_core_calendar_provide_event_action_already_completed() {
313        $this->resetAfterTest();
314        set_config('enablecompletion', 1);
315        $this->setAdminUser();
316
317        // Create the activity.
318        $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
319        $workshop = $this->getDataGenerator()->create_module('workshop', array('course' => $course->id),
320            array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
321
322        // Get some additional data.
323        $cm = get_coursemodule_from_instance('workshop', $workshop->id);
324
325        // Create a calendar event.
326        $event = $this->create_action_event($course->id, $workshop->id,
327            \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
328
329        // Mark the activity as completed.
330        $completion = new completion_info($course);
331        $completion->set_module_viewed($cm);
332
333        // Create an action factory.
334        $factory = new \core_calendar\action_factory();
335
336        // Decorate action event.
337        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory);
338
339        // Ensure result was null.
340        $this->assertNull($actionevent);
341    }
342
343    public function test_workshop_core_calendar_provide_event_action_already_completed_for_user() {
344        $this->resetAfterTest();
345        set_config('enablecompletion', 1);
346        $this->setAdminUser();
347
348        // Create the activity.
349        $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
350        $workshop = $this->getDataGenerator()->create_module('workshop', array('course' => $course->id),
351            array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
352
353        // Enrol a student in the course.
354        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
355
356        // Get some additional data.
357        $cm = get_coursemodule_from_instance('workshop', $workshop->id);
358
359        // Create a calendar event.
360        $event = $this->create_action_event($course->id, $workshop->id,
361            \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
362
363        // Mark the activity as completed for the student.
364        $completion = new completion_info($course);
365        $completion->set_module_viewed($cm, $student->id);
366
367        // Create an action factory.
368        $factory = new \core_calendar\action_factory();
369
370        // Decorate action event for the student.
371        $actionevent = mod_workshop_core_calendar_provide_event_action($event, $factory, $student->id);
372
373        // Ensure result was null.
374        $this->assertNull($actionevent);
375    }
376
377    /**
378     * Creates an action event.
379     *
380     * @param int $courseid The course id.
381     * @param int $instanceid The workshop id.
382     * @param string $eventtype The event type. eg. WORKSHOP_EVENT_TYPE_OPEN.
383     * @return bool|calendar_event
384     */
385    private function create_action_event($courseid, $instanceid, $eventtype) {
386        $event = new stdClass();
387        $event->name = 'Calendar event';
388        $event->modulename = 'workshop';
389        $event->courseid = $courseid;
390        $event->instance = $instanceid;
391        $event->type = CALENDAR_EVENT_TYPE_ACTION;
392        $event->eventtype = $eventtype;
393        $event->timestart = time();
394
395        return calendar_event::create($event);
396    }
397
398    /**
399     * Test check_updates_since callback.
400     */
401    public function test_check_updates_since() {
402        global $DB;
403
404        $this->resetAfterTest();
405        $this->setAdminUser();
406        $course = $this->getDataGenerator()->create_course();
407
408        // Create user.
409        $student = self::getDataGenerator()->create_user();
410        $teacher = self::getDataGenerator()->create_user();
411
412        // User enrolment.
413        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
414        $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
415        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
416        $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual');
417
418        $this->setCurrentTimeStart();
419        $record = array(
420            'course' => $course->id,
421            'custom' => 0,
422            'feedback' => 1,
423        );
424        $workshop = $this->getDataGenerator()->create_module('workshop', $record);
425        $cm = get_coursemodule_from_instance('workshop', $workshop->id, $course->id);
426        $context = context_module::instance($cm->id);
427        $cm = cm_info::create($cm);
428
429        $this->setUser($student);
430        // Check that upon creation, the updates are only about the new configuration created.
431        $onehourago = time() - HOURSECS;
432        $updates = workshop_check_updates_since($cm, $onehourago);
433        foreach ($updates as $el => $val) {
434            if ($el == 'configuration') {
435                $this->assertTrue($val->updated);
436                $this->assertTimeCurrent($val->timeupdated);
437            } else {
438                $this->assertFalse($val->updated);
439            }
440        }
441
442        // Set up a generator to create content.
443        $generator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
444        // Submission.
445        $submissionid = $generator->create_submission($workshop->id, $student->id, array(
446            'title' => 'My custom title',
447        ));
448        // Now assessment.
449        $assessmentid = $generator->create_assessment($submissionid, $student->id, array(
450            'weight' => 3,
451            'grade' => 95.00000,
452        ));
453        // Add files to one editor file area.
454        $fs = get_file_storage();
455        $filerecordinline = array(
456            'contextid' => $context->id,
457            'component' => 'mod_workshop',
458            'filearea'  => 'instructauthors',
459            'itemid'    => 0,
460            'filepath'  => '/',
461            'filename'  => 'image.png',
462        );
463        $instructauthorsfile = $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
464
465        $updates = workshop_check_updates_since($cm, $onehourago);
466        $this->assertTrue($updates->submissions->updated);
467        $this->assertCount(1, $updates->submissions->itemids);
468        $this->assertEquals($submissionid, $updates->submissions->itemids[0]);
469        $this->assertTrue($updates->assessments->updated);
470        $this->assertCount(1, $updates->assessments->itemids);
471        $this->assertEquals($assessmentid, $updates->assessments->itemids[0]);
472        $this->assertTrue($updates->instructauthorsfiles->updated);
473        $this->assertCount(1, $updates->instructauthorsfiles->itemids);
474        $this->assertEquals($instructauthorsfile->get_id(), $updates->instructauthorsfiles->itemids[0]);
475
476        // Check I see the user updates as teacher.
477        $this->setUser($teacher);
478        $updates = workshop_check_updates_since($cm, $onehourago);
479        $this->assertTrue($updates->usersubmissions->updated);
480        $this->assertCount(1, $updates->usersubmissions->itemids);
481        $this->assertEquals($submissionid, $updates->usersubmissions->itemids[0]);
482        $this->assertTrue($updates->userassessments->updated);
483        $this->assertCount(1, $updates->userassessments->itemids);
484        $this->assertEquals($assessmentid, $updates->userassessments->itemids[0]);
485        $this->assertTrue($updates->instructauthorsfiles->updated);
486        $this->assertCount(1, $updates->instructauthorsfiles->itemids);
487        $this->assertEquals($instructauthorsfile->get_id(), $updates->instructauthorsfiles->itemids[0]);
488
489        // The teacher didn't do anything.
490        $this->assertFalse($updates->submissions->updated);
491        $this->assertFalse($updates->assessments->updated);
492    }
493
494    /**
495     * An unknown event type should not have any limits
496     */
497    public function test_mod_workshop_core_calendar_get_valid_event_timestart_range_unknown_event() {
498        global $CFG;
499        require_once($CFG->dirroot . "/calendar/lib.php");
500
501        $this->resetAfterTest(true);
502        $this->setAdminUser();
503
504        $course = $this->getDataGenerator()->create_course();
505        $timestart = time();
506        $timeend = $timestart + DAYSECS;
507        $workshop = new \stdClass();
508        $workshop->submissionstart = $timestart;
509        $workshop->submissionend = $timeend;
510        $workshop->assessmentstart = 0;
511        $workshop->assessmentend = 0;
512
513        // Create a valid event.
514        $event = new \calendar_event([
515            'name' => 'Test event',
516            'description' => '',
517            'format' => 1,
518            'courseid' => $course->id,
519            'groupid' => 0,
520            'userid' => 2,
521            'modulename' => 'workshop',
522            'instance' => 1,
523            'eventtype' => WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE . "SOMETHING ELSE",
524            'timestart' => 1,
525            'timeduration' => 86400,
526            'visible' => 1
527        ]);
528        list ($min, $max) = mod_workshop_core_calendar_get_valid_event_timestart_range($event, $workshop);
529        $this->assertNull($min);
530        $this->assertNull($max);
531    }
532
533    /**
534     * Provider for test_mod_workshop_core_calendar_get_valid_event_timestart_range.
535     *
536     * @return array of (submissionstart, submissionend, assessmentstart, assessmentend, eventtype, expectedmin, expectedmax)
537     */
538    public function mod_workshop_core_calendar_get_valid_event_timestart_range_due_no_limit_provider() {
539        $submissionstart = time() + DAYSECS;
540        $submissionend = $submissionstart + DAYSECS;
541        $assessmentstart = $submissionend + DAYSECS;
542        $assessmentend = $assessmentstart + DAYSECS;
543
544        return [
545            'Only with submissionstart' => [$submissionstart, 0, 0, 0, WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN, null, null],
546            'Only with submissionend' => [0, $submissionend, 0, 0, WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE, null, null],
547            'Only with assessmentstart' => [0, 0, $assessmentstart, 0, WORKSHOP_EVENT_TYPE_ASSESSMENT_OPEN, null, null],
548            'Only with assessmentend' => [0, 0, 0, $assessmentend, WORKSHOP_EVENT_TYPE_ASSESSMENT_CLOSE, null, null],
549
550            'Move submissionstart when with submissionend' => [$submissionstart, $submissionend, 0, 0,
551                    WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN, null, $submissionend - 1],
552            'Move submissionend when with submissionstart' => [$submissionstart, $submissionend, 0, 0,
553                    WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE, $submissionstart + 1, null],
554            'Move assessmentstart when with assessmentend' => [0, 0, $assessmentstart, $assessmentend,
555                    WORKSHOP_EVENT_TYPE_ASSESSMENT_OPEN, null, $assessmentend - 1],
556            'Move assessmentend when with assessmentstart' => [0, 0, $assessmentstart, $assessmentend,
557                    WORKSHOP_EVENT_TYPE_ASSESSMENT_CLOSE, $assessmentstart + 1, null],
558
559            'Move submissionstart when with assessmentstart' => [$submissionstart, 0, $assessmentstart, 0,
560                    WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN, null, $assessmentstart],
561            'Move submissionstart when with assessmentend' => [$submissionstart, 0, 0, $assessmentend,
562                    WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN, null, $assessmentend],
563            'Move submissionend when with assessmentstart' => [0, $submissionend, $assessmentstart, 0,
564                    WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE, null, $assessmentstart],
565            'Move submissionend when with assessmentend' => [0, $submissionend, 0, $assessmentend,
566                    WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE, null, $assessmentend],
567
568            'Move assessmentstart when with submissionstart' => [$submissionstart, 0, $assessmentstart, 0,
569                    WORKSHOP_EVENT_TYPE_ASSESSMENT_OPEN, $submissionstart, null],
570            'Move assessmentstart when with submissionend' => [0, $submissionend, $assessmentstart, 0,
571                    WORKSHOP_EVENT_TYPE_ASSESSMENT_OPEN, $submissionend, null],
572            'Move assessmentend when with submissionstart' => [$submissionstart, 0, 0, $assessmentend,
573                    WORKSHOP_EVENT_TYPE_ASSESSMENT_CLOSE, $submissionstart, null],
574            'Move assessmentend when with submissionend' => [0, $submissionend, 0, $assessmentend,
575                    WORKSHOP_EVENT_TYPE_ASSESSMENT_CLOSE, $submissionend, null],
576
577            'Move submissionstart when with others' => [$submissionstart, $submissionend, $assessmentstart, $assessmentend,
578                    WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN, null, $submissionend - 1],
579            'Move submissionend when with others' => [$submissionstart, $submissionend, $assessmentstart, $assessmentend,
580                    WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE, $submissionstart + 1, $assessmentstart],
581            'Move assessmentstart when with others' => [$submissionstart, $submissionend, $assessmentstart, $assessmentend,
582                    WORKSHOP_EVENT_TYPE_ASSESSMENT_OPEN, $submissionend, $assessmentend - 1],
583            'Move assessmentend when with others' => [$submissionstart, $submissionend, $assessmentstart, $assessmentend,
584                    WORKSHOP_EVENT_TYPE_ASSESSMENT_CLOSE, $assessmentstart + 1, null],
585        ];
586    }
587
588    /**
589     * Tests mod_workshop_core_calendar_get_valid_event_timestart_range in various settings.
590     *
591     * @dataProvider mod_workshop_core_calendar_get_valid_event_timestart_range_due_no_limit_provider
592     *
593     * @param int $submissionstart  The start of the submission phase
594     * @param int $submissionend    The end of the submission phase
595     * @param int $assessmentstart  The start of the assessment phase
596     * @param int $assessmentend    The end of the assessment phase
597     * @param string $eventtype     The type if the event
598     * @param int|null $expectedmin The expected value for min of the valid event range
599     * @param int|null $expectedmax The expected value for max of the valid event range
600     */
601    public function test_mod_workshop_core_calendar_get_valid_event_timestart_range($submissionstart, $submissionend,
602            $assessmentstart, $assessmentend, $eventtype, $expectedmin, $expectedmax) {
603
604        global $CFG;
605        require_once($CFG->dirroot . '/calendar/lib.php');
606
607        $this->resetAfterTest(true);
608        $this->setAdminUser();
609
610        $course = $this->getDataGenerator()->create_course();
611        $workshop = new \stdClass();
612        $workshop->submissionstart = $submissionstart;
613        $workshop->submissionend = $submissionend;
614        $workshop->assessmentstart = $assessmentstart;
615        $workshop->assessmentend = $assessmentend;
616
617        // Create a valid event.
618        $event = new \calendar_event([
619            'name' => 'Test event',
620            'description' => '',
621            'format' => 1,
622            'courseid' => $course->id,
623            'groupid' => 0,
624            'userid' => 2,
625            'modulename' => 'workshop',
626            'instance' => 1,
627            'eventtype' => $eventtype,
628            'timestart' => 1,
629            'timeduration' => 86400,
630            'visible' => 1
631        ]);
632        list($min, $max) = mod_workshop_core_calendar_get_valid_event_timestart_range($event, $workshop);
633
634        $this->assertSame($expectedmin, is_array($min) ? $min[0] : $min);
635        $this->assertSame($expectedmax, is_array($max) ? $max[0] : $max);
636    }
637
638    /**
639     * An unknown event type should not change the workshop instance.
640     */
641    public function test_mod_workshop_core_calendar_event_timestart_updated_unknown_event() {
642        global $CFG, $DB;
643        require_once($CFG->dirroot . "/calendar/lib.php");
644
645        $this->resetAfterTest(true);
646        $this->setAdminUser();
647
648        $generator = $this->getDataGenerator();
649        $course = $generator->create_course();
650
651        $workshopgenerator = $generator->get_plugin_generator('mod_workshop');
652        $submissionstart = time() + DAYSECS;
653        $submissionend = $submissionstart + DAYSECS;
654        $assessmentstart = $submissionend + DAYSECS;
655        $assessmentend = $assessmentstart + DAYSECS;
656        $workshop = $workshopgenerator->create_instance(['course' => $course->id]);
657        $workshop->submissionstart = $submissionstart;
658        $workshop->submissionend = $submissionend;
659        $workshop->assessmentstart = $assessmentstart;
660        $workshop->assessmentend = $assessmentend;
661        $DB->update_record('workshop', $workshop);
662
663        // Create a valid event.
664        $event = new \calendar_event([
665            'name' => 'Test event',
666            'description' => '',
667            'format' => 1,
668            'courseid' => $course->id,
669            'groupid' => 0,
670            'userid' => 2,
671            'modulename' => 'workshop',
672            'instance' => $workshop->id,
673            'eventtype' => WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE . "SOMETHING ELSE",
674            'timestart' => 1,
675            'timeduration' => 86400,
676            'visible' => 1
677        ]);
678
679        mod_workshop_core_calendar_event_timestart_updated($event, $workshop);
680
681        $workshop = $DB->get_record('workshop', ['id' => $workshop->id]);
682        $this->assertEquals($submissionstart, $workshop->submissionstart);
683        $this->assertEquals($submissionend, $workshop->submissionend);
684        $this->assertEquals($assessmentstart, $workshop->assessmentstart);
685        $this->assertEquals($assessmentend, $workshop->assessmentend);
686    }
687
688    /**
689     * Provider for test_mod_workshop_core_calendar_event_timestart_updated.
690     *
691     * @return array of (submissionstart, submissionend, assessmentstart, assessmentend, eventtype, fieldtoupdate, newtime)
692     */
693    public function mod_workshop_core_calendar_event_timestart_updated_provider() {
694        $submissionstart = time() + DAYSECS;
695        $submissionend = $submissionstart + DAYSECS;
696        $assessmentstart = $submissionend + DAYSECS;
697        $assessmentend = $assessmentstart + DAYSECS;
698
699        return [
700            'Move submissionstart' => [$submissionstart, $submissionend, $assessmentstart, $assessmentend,
701                    WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN, 'submissionstart', $submissionstart + 50],
702            'Move submissionend' => [$submissionstart, $submissionend, $assessmentstart, $assessmentend,
703                    WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE, 'submissionend', $submissionend + 50],
704            'Move assessmentstart' => [$submissionstart, $submissionend, $assessmentstart, $assessmentend,
705                    WORKSHOP_EVENT_TYPE_ASSESSMENT_OPEN, 'assessmentstart', $assessmentstart + 50],
706            'Move assessmentend' => [$submissionstart, $submissionend, $assessmentstart, $assessmentend,
707                    WORKSHOP_EVENT_TYPE_ASSESSMENT_CLOSE, 'assessmentend', $assessmentend + 50],
708        ];
709    }
710
711    /**
712     * Due date events should update the workshop due date.
713     *
714     * @dataProvider mod_workshop_core_calendar_event_timestart_updated_provider
715     *
716     * @param int $submissionstart  The start of the submission phase
717     * @param int $submissionend    The end of the submission phase
718     * @param int $assessmentstart  The start of the assessment phase
719     * @param int $assessmentend    The end of the assessment phase
720     * @param string $eventtype     The type if the event
721     * @param string $fieldtoupdate The field that is supposed to be updated.
722     *                              Either of 'submissionstart', 'submissionend', 'assessmentstart' or 'assessmentend'.
723     * @param int $newtime          The new value for the $fieldtoupdate
724     */
725    public function test_mod_workshop_core_calendar_event_timestart_updated($submissionstart, $submissionend, $assessmentstart,
726            $assessmentend, $eventtype, $fieldtoupdate, $newtime) {
727        global $CFG, $DB;
728        require_once($CFG->dirroot . "/calendar/lib.php");
729
730        $this->resetAfterTest(true);
731        $this->setAdminUser();
732
733        $generator = $this->getDataGenerator();
734        $course = $generator->create_course();
735
736        $workshopgenerator = $generator->get_plugin_generator('mod_workshop');
737        $workshop = $workshopgenerator->create_instance(['course' => $course->id]);
738        $workshop->submissionstart = $submissionstart;
739        $workshop->submissionend = $submissionend;
740        $workshop->assessmentstart = $assessmentstart;
741        $workshop->assessmentend = $assessmentend;
742        $DB->update_record('workshop', $workshop);
743
744        // Create a valid event.
745        $event = new \calendar_event([
746            'name' => 'Test event',
747            'description' => '',
748            'format' => 1,
749            'courseid' => $course->id,
750            'groupid' => 0,
751            'userid' => 2,
752            'modulename' => 'workshop',
753            'instance' => $workshop->id,
754            'eventtype' => $eventtype,
755            'timestart' => $newtime,
756            'timeduration' => 86400,
757            'visible' => 1
758        ]);
759        mod_workshop_core_calendar_event_timestart_updated($event, $workshop);
760
761        $$fieldtoupdate = $newtime;
762
763        $workshop = $DB->get_record('workshop', ['id' => $workshop->id]);
764        $this->assertEquals($submissionstart, $workshop->submissionstart);
765        $this->assertEquals($submissionend, $workshop->submissionend);
766        $this->assertEquals($assessmentstart, $workshop->assessmentstart);
767        $this->assertEquals($assessmentend, $workshop->assessmentend);
768    }
769}
770