Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
   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   * Privacy test for the event monitor
  19   *
  20   * @package    mod_assignment
  21   * @category   test
  22   * @copyright  2018 Zig Tan <zig@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  namespace mod_assignment\privacy;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  require_once (__DIR__ . '/../../lib.php');
  30  
  31  use mod_assignment\privacy\provider;
  32  use core_privacy\local\request\approved_contextlist;
  33  use core_privacy\local\request\transform;
  34  use core_privacy\local\request\writer;
  35  use core_privacy\tests\provider_testcase;
  36  
  37  /**
  38   * Privacy test for the event monitor
  39   *
  40   * @package    mod_assignment
  41   * @category   test
  42   * @copyright  2018 Zig Tan <zig@moodle.com>
  43   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  44   */
  45  class provider_test extends provider_testcase {
  46  
  47      /**
  48       * @var int array   Array of test student ids associated for Course 1.
  49       */
  50      private $course1students = [];
  51  
  52      /**
  53       * @var int array   Array of test student ids associated for Course 2.
  54       */
  55      private $course2students = [];
  56  
  57      /**
  58       * @var int array   Array of test assignments associated for Course 1.
  59       */
  60      private $course1assignments = [];
  61  
  62      /**
  63       * @var int array   Array of test assignments associated for Course 2.
  64       */
  65      private $course2assignments = [];
  66  
  67      /**
  68       * Test for provider::get_contexts_for_userid().
  69       *
  70       * @throws coding_exception
  71       */
  72      public function test_get_contexts_for_userid() {
  73          global $DB;
  74  
  75          $this->resetAfterTest(true);
  76          $this->create_courses_and_assignments();
  77  
  78          // Get Teacher 1 to test get_contexts_for_userid().
  79          $teacher1 = $DB->get_record('user', ['username' => 'teacher1']);
  80          $contextids = provider::get_contexts_for_userid($teacher1->id);
  81          // Verify there should be 4 contexts, as Teacher 1 has submitted tests and marked Assignments in Course 1 and 2.
  82          $this->assertEquals(4, count($contextids->get_contextids()));
  83  
  84          // Get Teacher 2 to test get_contexts_for_userid().
  85          $teacher2 = $DB->get_record('user', ['username' => 'teacher2']);
  86          $contextids = provider::get_contexts_for_userid($teacher2->id);
  87          // Verify there should be 0 contexts, as teacher 2 has not marked any Assignments.
  88          $this->assertEquals(0, count($contextids->get_contextids()));
  89  
  90          // Get Student 1 to test get_contexts_for_userid().
  91          $student1 = $DB->get_record('user', ['username' => 'student1']);
  92          $contextids = provider::get_contexts_for_userid($student1->id);
  93          // Verify there should be 2 contexts, as student 1 added submissions for both Assignments in Course 1.
  94          $this->assertEquals(2, count($contextids->get_contextids()));
  95  
  96          // Get Student 2 to test get_contexts_for_userid().
  97          $student2 = $DB->get_record('user', ['username' => 'student2']);
  98          $contextids = provider::get_contexts_for_userid($student2->id);
  99          // Verify there should be 2 context, as student 2 added submissions for both Assignments in Course 2.
 100          $this->assertEquals(2, count($contextids->get_contextids()));
 101      }
 102  
 103      /**
 104       * Test that the correct userids are returned for a specific context.
 105       */
 106      public function test_get_users_in_context() {
 107  
 108          $this->resetAfterTest(true);
 109  
 110          $student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);
 111          $student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);
 112          // Student 3 should not turn up in the results of this test.
 113          $student3 = $this->getDataGenerator()->create_user(['username' => 'student3']);
 114          $teacher1 = $this->getDataGenerator()->create_user(['username' => 'teacher1']);
 115  
 116          $course = $this->getDataGenerator()->create_course();
 117          $course1assignment1 = $this->getDataGenerator()->create_module('assignment',
 118              [
 119                  'course' => $course->id,
 120                  'name' => 'Course 1 - Assignment 1 (onlinetext)',
 121                  'assignmenttype' => 'onlinetext',
 122              ]
 123          );
 124          // Create a second assignment in the same course.
 125          $course1assignment2 = $this->getDataGenerator()->create_module('assignment',
 126              [
 127                  'course' => $course->id,
 128                  'name' => 'Course 1 - Assignment 1 (onlinetext)',
 129                  'assignmenttype' => 'onlinetext',
 130              ]
 131          );
 132  
 133          $this->add_assignment_submission(
 134              $course1assignment1,
 135              $student1,
 136              "Course 1 - Ass 1: Student1 Test Submission"
 137          );
 138  
 139          $this->add_assignment_submission(
 140              $course1assignment1,
 141              $student2,
 142              "Course 1 - Ass 1: Student2 Test Submission"
 143          );
 144          // Add a submission for the second assignment.
 145          $this->add_assignment_submission(
 146              $course1assignment2,
 147              $student3,
 148              "Course 1 - Ass 2: Student3 Test Submission"
 149          );
 150  
 151          $submissions = $this->get_course_assignment_submissions($course->id);
 152          foreach ($submissions as $submission) {
 153              $this->mark_assignment_submission($submission->assignment, $submission->id, $teacher1, 50);
 154          }
 155  
 156          $c1ass1ctx = \context_module::instance($course1assignment1->cmid);
 157          $userlist = new \core_privacy\local\request\userlist($c1ass1ctx, 'mod_assignment');
 158  
 159          provider::get_users_in_context($userlist);
 160          $userids = $userlist->get_userids();
 161  
 162          // This is both students and the teacher who marked both assignments.
 163          $this->assertCount(3, $userids);
 164          // Make sure that student 3 is not in the returned userids.
 165          $this->assertFalse(in_array($student3->id, $userids));
 166          // Try with the course context.
 167          $coursecontext = \context_course::instance($course->id);
 168          $userlist = new \core_privacy\local\request\userlist($coursecontext, 'mod_assignment');
 169          provider::get_users_in_context($userlist);
 170          $this->assertEmpty($userlist->get_userids());
 171      }
 172  
 173      /**
 174       * Test for provider::export_user_data().
 175       *
 176       * @throws coding_exception
 177       */
 178      public function test_export_user_data_teacher() {
 179          global $DB;
 180  
 181          $this->resetAfterTest(true);
 182          $this->create_courses_and_assignments();
 183  
 184          // Test Teacher 1 export_data_for_user() - marking assignment submissions for both Course 1 and 2.
 185          $teacher1 = $DB->get_record('user', ['username' => 'teacher1']);
 186  
 187          $contextlist = provider::get_contexts_for_userid($teacher1->id);
 188          $approvedcontextlist = new approved_contextlist($teacher1, 'mod_assignment', $contextlist->get_contextids());
 189  
 190          // Verify Teacher 1 has four contexts.
 191          $this->assertCount(4, $contextlist->get_contextids());
 192  
 193          // Retrieve Assignment Submissions data for Teacher 1.
 194          provider::export_user_data($approvedcontextlist);
 195  
 196          $contexts = $contextlist->get_contexts();
 197  
 198          // Context 1 - Course 1's Assignment 1 -- (onlinetext).
 199          $context = \context_module::instance($this->course1assignments['ass1']->cmid);
 200          $this->assertContains($context, $contexts);
 201  
 202          $writer = writer::with_context($context);
 203          $subcontexts = [
 204              get_string('privacy:markedsubmissionspath', 'mod_assignment'),
 205              transform::user($teacher1->id)
 206          ];
 207          // Verify the test assignment submission from Teacher 1 exists.
 208          $submission = $writer->get_data($subcontexts);
 209          $this->assertEquals('<p>Course 1 - Ass 1: Teacher Test Submission</p>', $submission->data1);
 210  
 211          foreach ($this->course1students as $student) {
 212              $subcontexts = [
 213                  get_string('privacy:markedsubmissionspath', 'mod_assignment'),
 214                  transform::user($student->id)
 215              ];
 216              // Verify the student assignment submissions exists.
 217              $submission = $writer->get_data($subcontexts);
 218              $this->assertEquals("<p>Course 1 - Ass 1: " . $student->id . "</p>", $submission->data1);
 219          }
 220  
 221          // Context 2 - Course 1's Assignment 2 -- (single file upload).
 222          $context = \context_module::instance($this->course1assignments['ass2']->cmid);
 223          $this->assertContains($context, $contexts);
 224  
 225          $writer = writer::with_context($context);
 226          foreach ($this->course1students as $student) {
 227              $subcontexts = [
 228                  get_string('privacy:markedsubmissionspath', 'mod_assignment'),
 229                  transform::user($student->id)
 230              ];
 231              // Verify the student assignment submissions exists.
 232              $submission = $writer->get_data($subcontexts);
 233              $this->assertEquals("<p>Course 1 - Ass 2: " . $student->id . "</p>", $submission->data1);
 234  
 235              // Verify the student assignment submission file upload exists.
 236              $submissionfiles = $writer->get_files($subcontexts);
 237              $this->assertTrue(array_key_exists('Student' . $student->id . '-Course1-Ass2-(File 1 of 1)', $submissionfiles));
 238          }
 239  
 240          // Context 3 - Course 2's Assignment 1 -- (offline).
 241          $context = \context_module::instance($this->course2assignments['ass1']->cmid);
 242          $this->assertContains($context, $contexts);
 243  
 244          $writer = writer::with_context($context);
 245          foreach ($this->course2students as $student) {
 246              $subcontexts = [
 247                  get_string('privacy:markedsubmissionspath', 'mod_assignment'),
 248                  transform::user($student->id)
 249              ];
 250              // Verify the student assignment submissions exists.
 251              $submission = $writer->get_data($subcontexts);
 252              $this->assertEquals("<p>Course 2 - Ass 1: " . $student->id . "</p>", $submission->data1);
 253          }
 254  
 255          // Context 4 - Course 2's Assignment 2 -- (multiple file upload).
 256          $context = \context_module::instance($this->course2assignments['ass2']->cmid);
 257          $this->assertContains($context, $contexts);
 258  
 259          $writer = writer::with_context($context);
 260          foreach ($this->course2students as $student) {
 261              $subcontexts = [
 262                  get_string('privacy:markedsubmissionspath', 'mod_assignment'),
 263                  transform::user($student->id)
 264              ];
 265              // Verify the student assignment submissions exists.
 266              $submission = $writer->get_data($subcontexts);
 267              $this->assertEquals("<p>Course 2 - Ass 2: " . $student->id . "</p>", $submission->data1);
 268  
 269              // Verify the student assignment submission file upload exists.
 270              $submissionfiles = $writer->get_files($subcontexts);
 271              $this->assertTrue(array_key_exists('Student' . $student->id . '-Course2-Ass2-(File 1 of 2)', $submissionfiles));
 272              $this->assertTrue(array_key_exists('Student' . $student->id . '-Course2-Ass2-(File 2 of 2)', $submissionfiles));
 273          }
 274      }
 275  
 276      /**
 277       * Test for provider::export_user_data().
 278       *
 279       * @throws dml_exception
 280       */
 281      public function test_export_user_data_student() {
 282          global $DB;
 283  
 284          $this->resetAfterTest(true);
 285          $this->create_courses_and_assignments();
 286  
 287          // Test Student 1 export_data_for_user() - added assignment submissions for both assignments in Course 1.
 288          $student1 = $DB->get_record('user', ['username' => 'student1']);
 289  
 290          $contextlist = provider::get_contexts_for_userid($student1->id);
 291          $approvedcontextlist = new approved_contextlist($student1, 'mod_assignment', $contextlist->get_contextids());
 292  
 293          // Retrieve Assignment Submissions data for Student 1.
 294          provider::export_user_data($approvedcontextlist);
 295          $contexts = $contextlist->get_contexts();
 296  
 297          // Context 1 - Course 1's Assignment 1 -- (onlinetext).
 298          $context = \context_module::instance($this->course1assignments['ass1']->cmid);
 299          $this->assertContains($context, $contexts);
 300  
 301          $writer = writer::with_context($context);
 302          $subcontexts = [
 303              get_string('privacy:submissionpath', 'mod_assignment')
 304          ];
 305  
 306          // Verify the student assignment submissions exists.
 307          $submission = $writer->get_data($subcontexts);
 308          $this->assertEquals("<p>Course 1 - Ass 1: " . $student1->id . "</p>", $submission->data1);
 309  
 310          // Context 2 - Course 1's Assignment 2 -- (single file upload).
 311          $context = \context_module::instance($this->course1assignments['ass2']->cmid);
 312          $this->assertContains($context, $contexts);
 313  
 314          $writer = writer::with_context($context);
 315          $subcontexts = [
 316              get_string('privacy:submissionpath', 'mod_assignment')
 317          ];
 318  
 319          // Verify the student assignment submission exists.
 320          $submission = $writer->get_data($subcontexts);
 321          $this->assertEquals("<p>Course 1 - Ass 2: " . $student1->id . "</p>", $submission->data1);
 322  
 323          // Verify the student assignment submission file upload exists.
 324          $submissionfiles = $writer->get_files($subcontexts);
 325          $this->assertTrue(array_key_exists('Student' . $student1->id . '-Course1-Ass2-(File 1 of 1)', $submissionfiles));
 326  
 327          // Test Student 2 export_data_for_user() - added assignment submissions for both assignments in Course 2.
 328          $student2 = $DB->get_record('user', ['username' => 'student2']);
 329  
 330          $contextlist = provider::get_contexts_for_userid($student2->id);
 331          $approvedcontextlist = new approved_contextlist($student2, 'mod_assignment', $contextlist->get_contextids());
 332  
 333          // Retrieve Assignment Submissions data for Student 2.
 334          provider::export_user_data($approvedcontextlist);
 335          $contexts = $contextlist->get_contexts();
 336  
 337          // Context 1 - Course 2's Assignment 1 -- (offline).
 338          $context = \context_module::instance($this->course2assignments['ass1']->cmid);
 339          $this->assertContains($context, $contexts);
 340  
 341          $writer = writer::with_context($context);
 342          $subcontexts = [
 343              get_string('privacy:submissionpath', 'mod_assignment')
 344          ];
 345  
 346          // Verify the student assignment submissions exists.
 347          $submission = $writer->get_data($subcontexts);
 348          $this->assertEquals("<p>Course 2 - Ass 1: " . $student2->id . "</p>", $submission->data1);
 349  
 350          // Context 2 - Course 2's Assignment 2 -- (multiple file upload).
 351          $context = \context_module::instance($this->course2assignments['ass2']->cmid);
 352          $this->assertContains($context, $contexts);
 353  
 354          $writer = writer::with_context($context);
 355          $subcontexts = [
 356              get_string('privacy:submissionpath', 'mod_assignment')
 357          ];
 358  
 359          // Verify the student assignment submission exists.
 360          $submission = $writer->get_data($subcontexts);
 361          $this->assertEquals("<p>Course 2 - Ass 2: " . $student2->id . "</p>", $submission->data1);
 362  
 363          // Verify the student assignment submission file upload exists.
 364          $submissionfiles = $writer->get_files($subcontexts);
 365          $this->assertTrue(array_key_exists('Student' . $student2->id . '-Course2-Ass2-(File 1 of 2)', $submissionfiles));
 366          $this->assertTrue(array_key_exists('Student' . $student2->id . '-Course2-Ass2-(File 2 of 2)', $submissionfiles));
 367      }
 368  
 369      /**
 370       * Test for provider::delete_data_for_all_users_in_context().
 371       *
 372       * @throws dml_exception
 373       */
 374      public function test_delete_data_for_all_users_in_context() {
 375          global $DB;
 376  
 377          $this->resetAfterTest(true);
 378          $this->create_courses_and_assignments();
 379  
 380          // Test teacher1 delete_data_for_all_users_in_context().
 381          $teacher1 = $DB->get_record('user', ['username' => 'teacher1']);
 382          $contextlist = provider::get_contexts_for_userid($teacher1->id);
 383  
 384          foreach ($contextlist as $context) {
 385              provider::delete_data_for_all_users_in_context($context);
 386  
 387              // Verify assignment submission(s) were deleted for the context.
 388              $deleted = $this->get_assignment_submissions($context->id);
 389              $this->assertCount(0, $deleted);
 390  
 391              // Verify all the file submissions associated with the context for all users were deleted.
 392              $files = $DB->get_records('files', ['component' => 'mod_assignment', 'filearea' => 'submission', 'contextid' => $context->id]);
 393              $this->assertEquals(0, count($files));
 394          }
 395      }
 396  
 397      /**
 398       * Test for provider::delete_data_for_user().
 399       *
 400       * @throws dml_exception
 401       */
 402      public function test_delete_data_for_user() {
 403          global $DB;
 404  
 405          $this->resetAfterTest(true);
 406          $this->create_courses_and_assignments();
 407  
 408          // Test Teacher 1 delete_data_for_user(), should only remove the 1 test submission added by Teacher 1.
 409          // Should not remove any assignment submission records marked by the teacher.
 410          $teacher1 = $DB->get_record('user', ['username' => 'teacher1']);
 411          $contextlist = provider::get_contexts_for_userid($teacher1->id);
 412          $approvedcontextlist = new approved_contextlist($teacher1, 'mod_assignment', $contextlist->get_contextids());
 413          provider::delete_data_for_user($approvedcontextlist);
 414  
 415          // Verify the submissions submitted by students still exists.
 416          $markedsubmissions = $DB->get_records('assignment_submissions', ['teacher' => $teacher1->id]);
 417          $this->assertCount(4, $markedsubmissions);
 418  
 419          // Test student 1 delete_data_for_user().
 420          $student1 = $DB->get_record('user', ['username' => 'student1']);
 421          $contextlist = provider::get_contexts_for_userid($student1->id);
 422          $approvedcontextlist = new approved_contextlist($student1, 'mod_assignment', $contextlist->get_contextids());
 423          provider::delete_data_for_user($approvedcontextlist);
 424  
 425          // Verify student 1's assignment submissions were deleted.
 426          $assignmentsubmissions = $DB->get_records('assignment_submissions', ['userid' => $student1->id]);
 427          $this->assertEquals(0, count($assignmentsubmissions));
 428  
 429          // Verify student 1's file submissions were deleted.
 430          foreach ($contextlist->get_contextids() as $contextid) {
 431              $files = $DB->get_records('files', ['component' => 'mod_assignment', 'filearea' => 'submission', 'contextid' => $contextid]);
 432              $this->assertEquals(0, count($files));
 433          }
 434      }
 435  
 436      /**
 437       * Test the deletion of a data for users.
 438       */
 439      public function test_delete_data_for_users() {
 440          global $DB;
 441          $this->resetAfterTest();
 442  
 443          $student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);
 444          $student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);
 445          $student3 = $this->getDataGenerator()->create_user(['username' => 'student3']);
 446  
 447          $course = $this->getDataGenerator()->create_course();
 448          $course1assignment = $this->getDataGenerator()->create_module('assignment',
 449              [
 450                  'course' => $course->id,
 451                  'name' => 'Course 1 - Assignment 1 (single file upload)',
 452                  'assignmenttype' => 'uploadsingle'
 453              ]
 454          );
 455          $context = \context_module::instance($course1assignment->cmid);
 456  
 457          // Student one submission.
 458          $this->add_file_assignment_submission(
 459              $course1assignment,
 460              $student1,
 461              "Course 1 - Ass 2: " . $student1->id,
 462              'Student' . $student1->id . '-Course1-Ass2'
 463          );
 464          // Student two submission.
 465          $this->add_file_assignment_submission(
 466              $course1assignment,
 467              $student2,
 468              "Course 1 - Ass 2: " . $student2->id,
 469              'Student' . $student2->id . '-Course1-Ass2'
 470          );
 471          // Student three submission to be retained.
 472          $this->add_file_assignment_submission(
 473              $course1assignment,
 474              $student3,
 475              "Course 1 - Ass 2: " . $student3->id,
 476              'Student' . $student3->id . '-Course1-Ass2'
 477          );
 478  
 479          $files = $DB->get_records('files', [
 480              'component' => 'mod_assignment',
 481              'filearea' => 'submission',
 482              'contextid' => $context->id
 483          ]);
 484          $this->assertCount(6, $files);
 485  
 486          $submissions = $this->get_assignment_submissions($context->id);
 487          $this->assertCount(3, $submissions);
 488  
 489          $userlist = new \core_privacy\local\request\approved_userlist($context, 'mod_assignment', [$student1->id, $student2->id]);
 490          provider::delete_data_for_users($userlist);
 491  
 492          $files = $DB->get_records('files', [
 493              'component' => 'mod_assignment',
 494              'filearea' => 'submission',
 495              'contextid' => $context->id
 496          ]);
 497          $this->assertCount(2, $files);
 498  
 499          $submissions = $this->get_assignment_submissions($context->id);
 500          $this->assertCount(1, $submissions);
 501      }
 502  
 503      // Start of helper functions.
 504  
 505      /**
 506       * Helper function to setup Course, users, and assignments for testing.
 507       */
 508      protected function create_courses_and_assignments() {
 509          // Create Courses, Users, and Assignments.
 510          $course1 = $this->getDataGenerator()->create_course(['shortname' => 'course1']);
 511          $course2 = $this->getDataGenerator()->create_course(['shortname' => 'course2']);
 512  
 513          $teacher1 = $this->getDataGenerator()->create_user(['username' => 'teacher1']);
 514          $teacher2 = $this->getDataGenerator()->create_user(['username' => 'teacher2']);
 515  
 516          $student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);
 517          $student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);
 518  
 519          $this->course1students = [
 520              $student1
 521          ];
 522  
 523          $this->course2students = [
 524              $student2
 525          ];
 526  
 527          $course1assignment1 = $this->getDataGenerator()->create_module('assignment',
 528              [
 529                  'course' => $course1->id,
 530                  'name' => 'Course 1 - Assignment 1 (onlinetext)',
 531                  'assignmenttype' => 'onlinetext',
 532              ]
 533          );
 534          $course1assignment2 = $this->getDataGenerator()->create_module('assignment',
 535              [
 536                  'course' => $course1->id,
 537                  'name' => 'Course 1 - Assignment 2 (single file upload)',
 538                  'assignmenttype' => 'uploadsingle',
 539              ]
 540          );
 541  
 542          $this->course1assignments = [
 543              'ass1' => $course1assignment1,
 544              'ass2' => $course1assignment2
 545          ];
 546  
 547          $course2assignment1 = $this->getDataGenerator()->create_module('assignment',
 548              [
 549                  'course' => $course2->id,
 550                  'name' => 'Course 2 - Assignment 1 (offline)',
 551                  'assignmenttype' => 'offline',
 552              ]
 553          );
 554          $course2assignment2 = $this->getDataGenerator()->create_module('assignment',
 555              [
 556                  'course' => $course2->id,
 557                  'name' => 'Course 2 - Assignment 2 (multiple file upload)',
 558                  'assignmenttype' => 'upload',
 559              ]
 560          );
 561  
 562          $this->course2assignments = [
 563              'ass1' => $course2assignment1,
 564              'ass2' => $course2assignment2
 565          ];
 566  
 567          // Teacher 1 add test assignment submission for Course 1 - Assignment 1.
 568          $this->add_assignment_submission(
 569              $course1assignment1,
 570              $teacher1,
 571              "Course 1 - Ass 1: Teacher Test Submission"
 572          );
 573  
 574          // Student 1 add assignment submissions for Course 1 - Assignment 1 and 2.
 575          $this->add_assignment_submission(
 576              $course1assignment1,
 577              $student1,
 578              "Course 1 - Ass 1: " . $student1->id
 579          );
 580          $this->add_file_assignment_submission(
 581              $course1assignment2,
 582              $student1,
 583              "Course 1 - Ass 2: " . $student1->id,
 584              'Student' . $student1->id . '-Course1-Ass2'
 585          );
 586  
 587          // Student 2 add assignment submissions for Course 2 - Assignment 1 and 2.
 588          $this->add_assignment_submission(
 589              $course2assignment1,
 590              $student2,
 591              "Course 2 - Ass 1: " . $student2->id
 592          );
 593          $this->add_file_assignment_submission(
 594              $course2assignment2,
 595              $student2,
 596              "Course 2 - Ass 2: " . $student2->id,
 597              'Student' . $student2->id . '-Course2-Ass2',
 598              2
 599          );
 600  
 601          // Teacher 1 to mark assignment submissions for Course 1's Assignment 1 and 2.
 602          $course1submissions = $this->get_course_assignment_submissions($course1->id);
 603          foreach ($course1submissions as $submission) {
 604              $this->mark_assignment_submission($submission->assignment, $submission->id, $teacher1, 49);
 605          }
 606  
 607          // Teacher 1 to mark assignment submissions for Course 2's Assignment 1 and 2.
 608          $course2submissions = $this->get_course_assignment_submissions($course2->id);
 609          foreach ($course2submissions as $submission) {
 610              $this->mark_assignment_submission($submission->assignment, $submission->id, $teacher1, 50);
 611          }
 612      }
 613  
 614      /**
 615       * Helper function to add an assignment submission for testing.
 616       *
 617       * @param object $assignment        Object containing assignment submission details to create for testing.
 618       * @param object $user              Object of the user making the assignment submission.
 619       * @param string $submissiondata    The onlintext string value of the assignment submission.
 620       * @throws dml_exception
 621       */
 622      protected function add_assignment_submission($assignment, $user, $submissiondata) {
 623          global $DB;
 624  
 625          $submission = (object) [
 626              'assignment' => $assignment->id,
 627              'userid' => $user->id,
 628              'timecreated' => date('U'),
 629              'data1' => '<p>' . $submissiondata . '</p>',
 630              'submissioncomment' => 'My submission by ' . $user->username
 631          ];
 632  
 633          return $DB->insert_record('assignment_submissions', $submission);
 634      }
 635  
 636      /**
 637       * Helper function to add an assignment submission with file submissions for testing.
 638       *
 639       * @param object $assignment        Object containing assignment submission details to create for testing.
 640       * @param object $user              Object of the user making the assignment submission.
 641       * @param string $submissiondata    The onlintext string value of the assignment submission.
 642       * @param string $filename          The filename of the file submission included with the assignment submission.
 643       * @param int $numfiles             The number of files included with the assignment submission.
 644       * @throws dml_exception
 645       * @throws file_exception
 646       * @throws stored_file_creation_exception
 647       */
 648      protected function add_file_assignment_submission($assignment, $user, $submissiondata, $filename, $numfiles = 1) {
 649          global $CFG, $DB;
 650  
 651          $submission = (object) [
 652              'assignment' => $assignment->id,
 653              'userid' => $user->id,
 654              'timecreated' => date('U'),
 655              'data1' => '<p>' . $submissiondata . '</p>',
 656              'numfiles' => $numfiles,
 657              'submissioncomment' => 'My submission by ' . $user->username
 658          ];
 659  
 660          $submissionid = $DB->insert_record('assignment_submissions', $submission);
 661  
 662          // Create a file submission with the test pdf.
 663          $this->setUser($user->id);
 664          $context = \context_module::instance($assignment->cmid);
 665  
 666          $fs = get_file_storage();
 667          $sourcefile = $CFG->dirroot . '/mod/assign/feedback/editpdf/tests/fixtures/submission.pdf';
 668  
 669          for ($f = 1; $f <= $numfiles; $f++) {
 670              $pdfsubmission = (object)array(
 671                  'contextid' => $context->id,
 672                  'component' => 'mod_assignment',
 673                  'filearea' => 'submission',
 674                  'itemid' => $submissionid,
 675                  'filepath' => '/',
 676                  'filename' => $filename . "-(File $f of $numfiles)"
 677              );
 678              $fs->create_file_from_pathname($pdfsubmission, $sourcefile);
 679          }
 680      }
 681  
 682      /**
 683       * Helper function to retrieve the assignment submission records for a given course.
 684       *
 685       * @param int $courseid     The course ID to get assignment submissions by.
 686       * @return array            Array of assignment submission details.
 687       * @throws dml_exception
 688       */
 689      protected function get_course_assignment_submissions($courseid) {
 690          global $DB;
 691  
 692          $sql = "SELECT s.id,
 693                         s.assignment,
 694                         s.userid,
 695                         s.timecreated,
 696                         s.timemodified,
 697                         s.numfiles,
 698                         s.data1,
 699                         s.data2,
 700                         s.grade,
 701                         s.submissioncomment,
 702                         s.format,
 703                         s.teacher,
 704                         s.timemarked,
 705                         s.mailed
 706                    FROM {assignment} a
 707                    JOIN {assignment_submissions} s ON s.assignment = a.id
 708                   WHERE a.course = :courseid";
 709          $params = [
 710              'courseid' => $courseid
 711          ];
 712  
 713          return $DB->get_records_sql($sql, $params);
 714      }
 715  
 716      /**
 717       * Helper function to update an assignment submission with grading details for a teacher.
 718       *
 719       * @param int $assignmentid     The assignment ID to update assignment submissions with marking/graded details.
 720       * @param int $submissionid     The assignment submission ID to update with marking/grading details.
 721       * @param int $teacher          The teacher user ID to making the marking/grading details.
 722       * @param int $gradedata        The grade value set for the marking/grading details.
 723       */
 724      protected function mark_assignment_submission($assignmentid, $submissionid, $teacher, $gradedata) {
 725          global $DB;
 726  
 727          $submission = (object) [
 728              'id' => $submissionid,
 729              'assignment' => $assignmentid,
 730              'grade' => $gradedata,
 731              'teacher' => $teacher->id,
 732              'timemarked' => date('U')
 733          ];
 734  
 735          return $DB->update_record('assignment_submissions', $submission);
 736      }
 737  
 738      /**
 739       * Helper function to retrieve the assignment records for a given context.
 740       *
 741       * @param int $contextid    The context module ID value to retrieve assignment IDs by.
 742       * @return array            Array of assignment IDs.
 743       * @throws dml_exception
 744       */
 745      protected function get_assignments($contextid) {
 746          global $DB;
 747  
 748          $sql = "SELECT a.id
 749                    FROM {assignment} a
 750                    JOIN {course_modules} cm ON a.id = cm.instance
 751                    JOIN {modules} m ON m.id = cm.module AND m.name = :modulename
 752                    JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :contextmodule
 753                   WHERE ctx.id = :contextid";
 754          $params = [
 755              'modulename' => 'assignment',
 756              'contextmodule' => CONTEXT_MODULE,
 757              'contextid' => $contextid
 758          ];
 759  
 760          return $DB->get_records_sql($sql, $params);
 761      }
 762  
 763      /**
 764       * Helper function to retrieve the assignment submission records for a given context.
 765       *
 766       * @param int $contextid    The context module ID value to retrieve assignment submission IDs by.
 767       * @return array            Array of assignment submission IDs.
 768       * @throws dml_exception
 769       */
 770      protected function get_assignment_submissions($contextid) {
 771          global $DB;
 772  
 773          $sql = "SELECT s.id
 774                    FROM {assignment_submissions} s
 775                    JOIN {assignment} a ON a.id = s.assignment
 776                    JOIN {course_modules} cm ON a.id = cm.instance
 777                    JOIN {modules} m ON m.id = cm.module AND m.name = :modulename
 778                    JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :contextmodule
 779                   WHERE ctx.id = :contextid";
 780          $params = [
 781              'modulename' => 'assignment',
 782              'contextmodule' => CONTEXT_MODULE,
 783              'contextid' => $contextid
 784          ];
 785  
 786          return $DB->get_records_sql($sql, $params);
 787      }
 788  }