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   * Data provider tests.
  19   *
  20   * @package    mod_feedback
  21   * @category   test
  22   * @copyright  2018 Frédéric Massart
  23   * @author     Frédéric Massart <fred@branchup.tech>
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  namespace mod_feedback\privacy;
  27  
  28  defined('MOODLE_INTERNAL') || die();
  29  global $CFG;
  30  
  31  use core_privacy\tests\provider_testcase;
  32  use core_privacy\local\request\approved_contextlist;
  33  use core_privacy\local\request\approved_userlist;
  34  use core_privacy\local\request\transform;
  35  use core_privacy\local\request\writer;
  36  use mod_feedback\privacy\provider;
  37  
  38  require_once($CFG->dirroot . '/mod/feedback/lib.php');
  39  
  40  /**
  41   * Data provider testcase class.
  42   *
  43   * @package    mod_feedback
  44   * @category   test
  45   * @copyright  2018 Frédéric Massart
  46   * @author     Frédéric Massart <fred@branchup.tech>
  47   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  48   */
  49  class provider_test extends provider_testcase {
  50  
  51      public function setUp(): void {
  52          $this->resetAfterTest();
  53      }
  54  
  55      /**
  56       * Test getting the contexts for a user.
  57       */
  58      public function test_get_contexts_for_userid() {
  59          global $DB;
  60          $dg = $this->getDataGenerator();
  61          $fg = $dg->get_plugin_generator('mod_feedback');
  62  
  63          $c1 = $dg->create_course();
  64          $c2 = $dg->create_course();
  65          $cm0a = $dg->create_module('feedback', ['course' => SITEID]);
  66          $cm1a = $dg->create_module('feedback', ['course' => $c1, 'anonymous' => FEEDBACK_ANONYMOUS_NO]);
  67          $cm1b = $dg->create_module('feedback', ['course' => $c1]);
  68          $cm2a = $dg->create_module('feedback', ['course' => $c2]);
  69          $cm2b = $dg->create_module('feedback', ['course' => $c2]);
  70          $cm2c = $dg->create_module('feedback', ['course' => $c2]);
  71  
  72          $u1 = $dg->create_user();
  73          $u2 = $dg->create_user();
  74  
  75          foreach ([$cm0a, $cm1a, $cm1b, $cm2a] as $feedback) {
  76              $i1 = $fg->create_item_numeric($feedback);
  77              $i2 = $fg->create_item_multichoice($feedback);
  78              $answers = ['numeric_' . $i1->id => '1', 'multichoice_' . $i2->id => [1]];
  79  
  80              if ($feedback == $cm1b) {
  81                  $this->create_submission_with_answers($feedback, $u2, $answers);
  82              } else {
  83                  $this->create_submission_with_answers($feedback, $u1, $answers);
  84              }
  85          }
  86  
  87          // Unsaved submission for u1 in cm2b.
  88          $feedback = $cm2b;
  89          $i1 = $fg->create_item_numeric($feedback);
  90          $i2 = $fg->create_item_multichoice($feedback);
  91          $answers = ['numeric_' . $i1->id => '1', 'multichoice_' . $i2->id => [1]];
  92          $this->create_tmp_submission_with_answers($feedback, $u1, $answers);
  93  
  94          // Unsaved submission for u2 in cm2c.
  95          $feedback = $cm2c;
  96          $i1 = $fg->create_item_numeric($feedback);
  97          $i2 = $fg->create_item_multichoice($feedback);
  98          $answers = ['numeric_' . $i1->id => '1', 'multichoice_' . $i2->id => [1]];
  99          $this->create_tmp_submission_with_answers($feedback, $u2, $answers);
 100  
 101          $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
 102          $this->assertCount(4, $contextids);
 103          $this->assertTrue(in_array(\context_module::instance($cm0a->cmid)->id, $contextids));
 104          $this->assertTrue(in_array(\context_module::instance($cm1a->cmid)->id, $contextids));
 105          $this->assertTrue(in_array(\context_module::instance($cm2a->cmid)->id, $contextids));
 106          $this->assertFalse(in_array(\context_module::instance($cm1b->cmid)->id, $contextids));
 107          $this->assertTrue(in_array(\context_module::instance($cm2b->cmid)->id, $contextids));
 108          $this->assertFalse(in_array(\context_module::instance($cm2c->cmid)->id, $contextids));
 109  
 110          $contextids = provider::get_contexts_for_userid($u2->id)->get_contextids();
 111          $this->assertCount(2, $contextids);
 112          $this->assertFalse(in_array(\context_module::instance($cm0a->cmid)->id, $contextids));
 113          $this->assertFalse(in_array(\context_module::instance($cm1a->cmid)->id, $contextids));
 114          $this->assertFalse(in_array(\context_module::instance($cm2a->cmid)->id, $contextids));
 115          $this->assertTrue(in_array(\context_module::instance($cm1b->cmid)->id, $contextids));
 116          $this->assertFalse(in_array(\context_module::instance($cm2b->cmid)->id, $contextids));
 117          $this->assertTrue(in_array(\context_module::instance($cm2c->cmid)->id, $contextids));
 118      }
 119  
 120      /**
 121       * Test getting the users in a context.
 122       */
 123      public function test_get_users_in_context() {
 124          global $DB;
 125          $dg = $this->getDataGenerator();
 126          $fg = $dg->get_plugin_generator('mod_feedback');
 127          $component = 'mod_feedback';
 128  
 129          $c1 = $dg->create_course();
 130          $c2 = $dg->create_course();
 131          $cm0 = $dg->create_module('feedback', ['course' => SITEID]);
 132          $cm1a = $dg->create_module('feedback', ['course' => $c1, 'anonymous' => FEEDBACK_ANONYMOUS_NO]);
 133          $cm1b = $dg->create_module('feedback', ['course' => $c1]);
 134          $cm2 = $dg->create_module('feedback', ['course' => $c2]);
 135  
 136          $u1 = $dg->create_user();
 137          $u2 = $dg->create_user();
 138  
 139          foreach ([$cm0, $cm1a, $cm1b, $cm2] as $feedback) {
 140              $i1 = $fg->create_item_numeric($feedback);
 141              $i2 = $fg->create_item_multichoice($feedback);
 142              $answers = ['numeric_' . $i1->id => '1', 'multichoice_' . $i2->id => [1]];
 143  
 144              if ($feedback == $cm1b) {
 145                  $this->create_submission_with_answers($feedback, $u2, $answers);
 146              } else {
 147                  $this->create_submission_with_answers($feedback, $u1, $answers);
 148              }
 149          }
 150  
 151          // Unsaved submission for u2 in cm1a.
 152          $feedback = $cm1a;
 153          $i1 = $fg->create_item_numeric($feedback);
 154          $i2 = $fg->create_item_multichoice($feedback);
 155          $answers = ['numeric_' . $i1->id => '1', 'multichoice_' . $i2->id => [1]];
 156          $this->create_tmp_submission_with_answers($feedback, $u2, $answers);
 157  
 158          // Only u1 in cm0.
 159          $context = \context_module::instance($cm0->cmid);
 160          $userlist = new \core_privacy\local\request\userlist($context, $component);
 161          provider::get_users_in_context($userlist);
 162  
 163          $this->assertCount(1, $userlist);
 164          $this->assertEquals([$u1->id], $userlist->get_userids());
 165  
 166          $context = \context_module::instance($cm1a->cmid);
 167          $userlist = new \core_privacy\local\request\userlist($context, $component);
 168          provider::get_users_in_context($userlist);
 169  
 170          // Two submissions in cm1a: saved for u1, unsaved for u2.
 171          $this->assertCount(2, $userlist);
 172  
 173          $expected = [$u1->id, $u2->id];
 174          $actual = $userlist->get_userids();
 175          sort($expected);
 176          sort($actual);
 177  
 178          $this->assertEquals($expected, $actual);
 179  
 180          // Only u2 in cm1b.
 181          $context = \context_module::instance($cm1b->cmid);
 182          $userlist = new \core_privacy\local\request\userlist($context, $component);
 183          provider::get_users_in_context($userlist);
 184  
 185          $this->assertCount(1, $userlist);
 186          $this->assertEquals([$u2->id], $userlist->get_userids());
 187  
 188          // Only u1 in cm2.
 189          $context = \context_module::instance($cm2->cmid);
 190          $userlist = new \core_privacy\local\request\userlist($context, $component);
 191          provider::get_users_in_context($userlist);
 192  
 193          $this->assertCount(1, $userlist);
 194          $this->assertEquals([$u1->id], $userlist->get_userids());
 195      }
 196  
 197      /**
 198       * Test deleting user data.
 199       */
 200      public function test_delete_data_for_user() {
 201          global $DB;
 202          $dg = $this->getDataGenerator();
 203          $fg = $dg->get_plugin_generator('mod_feedback');
 204  
 205          $c1 = $dg->create_course();
 206          $c2 = $dg->create_course();
 207          $cm0a = $dg->create_module('feedback', ['course' => SITEID]);
 208          $cm1a = $dg->create_module('feedback', ['course' => $c1, 'anonymous' => FEEDBACK_ANONYMOUS_NO]);
 209          $cm2a = $dg->create_module('feedback', ['course' => $c2]);
 210  
 211          $u1 = $dg->create_user();
 212          $u2 = $dg->create_user();
 213  
 214          // Create a bunch of data.
 215          foreach ([$cm1a, $cm0a, $cm2a] as $feedback) {
 216              $i1 = $fg->create_item_numeric($feedback);
 217              $i2 = $fg->create_item_multichoice($feedback);
 218              $answers = ['numeric_' . $i1->id => '1', 'multichoice_' . $i2->id => [1]];
 219  
 220              // Create u2 user data for this module.
 221              if ($feedback == $cm1a) {
 222                  $this->create_submission_with_answers($feedback, $u2, $answers);
 223                  $this->create_tmp_submission_with_answers($feedback, $u2, $answers);
 224              }
 225  
 226              $this->create_submission_with_answers($feedback, $u1, $answers);
 227              $this->create_tmp_submission_with_answers($feedback, $u1, $answers);
 228          }
 229  
 230          $appctx = new approved_contextlist($u1, 'mod_feedback', [
 231              \context_module::instance($cm0a->cmid)->id,
 232              \context_module::instance($cm1a->cmid)->id
 233          ]);
 234          provider::delete_data_for_user($appctx);
 235  
 236          // Confirm all data is gone in those, except for u2.
 237          foreach ([$cm0a, $cm1a] as $feedback) {
 238              $this->assert_no_feedback_data_for_user($feedback, $u1);
 239              if ($feedback == $cm1a) {
 240                  $this->assert_feedback_data_for_user($feedback, $u2);
 241                  $this->assert_feedback_tmp_data_for_user($feedback, $u2);
 242              }
 243          }
 244  
 245          // Confirm cm2a wasn't affected.
 246          $this->assert_feedback_data_for_user($cm2a, $u1);
 247          $this->assert_feedback_tmp_data_for_user($cm2a, $u1);
 248  
 249      }
 250  
 251      /**
 252       * Test deleting data within a context for an approved userlist.
 253       */
 254      public function test_delete_data_for_users() {
 255          global $DB;
 256          $dg = $this->getDataGenerator();
 257          $fg = $dg->get_plugin_generator('mod_feedback');
 258  
 259          $c1 = $dg->create_course();
 260          $c2 = $dg->create_course();
 261          $cm0 = $dg->create_module('feedback', ['course' => SITEID]);
 262          $cm1 = $dg->create_module('feedback', ['course' => $c1, 'anonymous' => FEEDBACK_ANONYMOUS_NO]);
 263          $cm2 = $dg->create_module('feedback', ['course' => $c2]);
 264          $context0 = \context_module::instance($cm0->cmid);
 265          $context1 = \context_module::instance($cm1->cmid);
 266  
 267          $u1 = $dg->create_user();
 268          $u2 = $dg->create_user();
 269  
 270          // Create a bunch of data.
 271          foreach ([$cm0, $cm1, $cm2] as $feedback) {
 272              $i1 = $fg->create_item_numeric($feedback);
 273              $i2 = $fg->create_item_multichoice($feedback);
 274              $answers = ['numeric_' . $i1->id => '1', 'multichoice_' . $i2->id => [1]];
 275  
 276              $this->create_submission_with_answers($feedback, $u1, $answers);
 277              $this->create_tmp_submission_with_answers($feedback, $u1, $answers);
 278  
 279              $this->create_submission_with_answers($feedback, $u2, $answers);
 280              $this->create_tmp_submission_with_answers($feedback, $u2, $answers);
 281          }
 282  
 283          // Delete u1 from cm0, ensure u2 data is retained.
 284          $approveduserlist = new approved_userlist($context0, 'mod_feedback', [$u1->id]);
 285          provider::delete_data_for_users($approveduserlist);
 286  
 287          $this->assert_no_feedback_data_for_user($cm0, $u1);
 288          $this->assert_feedback_data_for_user($cm0, $u2);
 289          $this->assert_feedback_tmp_data_for_user($cm0, $u2);
 290  
 291          // Ensure cm1 unaffected by cm1 deletes.
 292          $this->assert_feedback_data_for_user($cm1, $u1);
 293          $this->assert_feedback_tmp_data_for_user($cm1, $u1);
 294          $this->assert_feedback_data_for_user($cm1, $u2);
 295          $this->assert_feedback_tmp_data_for_user($cm1, $u2);
 296  
 297          // Delete u1 and u2 from cm1, ensure no data is retained.
 298          $approveduserlist = new approved_userlist($context1, 'mod_feedback', [$u1->id, $u2->id]);
 299          provider::delete_data_for_users($approveduserlist);
 300  
 301          $this->assert_no_feedback_data_for_user($cm1, $u1);
 302          $this->assert_no_feedback_data_for_user($cm1, $u2);
 303  
 304          // Ensure cm2 is unaffected by any of the deletes.
 305          $this->assert_feedback_data_for_user($cm2, $u1);
 306          $this->assert_feedback_tmp_data_for_user($cm2, $u1);
 307          $this->assert_feedback_data_for_user($cm2, $u2);
 308          $this->assert_feedback_tmp_data_for_user($cm2, $u2);
 309      }
 310  
 311      /**
 312       * Test deleting a whole context.
 313       */
 314      public function test_delete_data_for_all_users_in_context() {
 315          global $DB;
 316          $dg = $this->getDataGenerator();
 317          $fg = $dg->get_plugin_generator('mod_feedback');
 318  
 319          $c1 = $dg->create_course();
 320          $c2 = $dg->create_course();
 321          $cm0a = $dg->create_module('feedback', ['course' => SITEID]);
 322          $cm1a = $dg->create_module('feedback', ['course' => $c1, 'anonymous' => FEEDBACK_ANONYMOUS_NO]);
 323  
 324          $u1 = $dg->create_user();
 325          $u2 = $dg->create_user();
 326  
 327          // Create a bunch of data.
 328          foreach ([$cm1a, $cm0a] as $feedback) {
 329              $i1 = $fg->create_item_numeric($feedback);
 330              $i2 = $fg->create_item_multichoice($feedback);
 331              $answers = ['numeric_' . $i1->id => '1', 'multichoice_' . $i2->id => [1]];
 332  
 333              $this->create_submission_with_answers($feedback, $u1, $answers);
 334              $this->create_tmp_submission_with_answers($feedback, $u1, $answers);
 335  
 336              $this->create_submission_with_answers($feedback, $u2, $answers);
 337              $this->create_tmp_submission_with_answers($feedback, $u2, $answers);
 338          }
 339  
 340          provider::delete_data_for_all_users_in_context(\context_module::instance($cm1a->cmid));
 341  
 342          $this->assert_no_feedback_data_for_user($cm1a, $u1);
 343          $this->assert_no_feedback_data_for_user($cm1a, $u2);
 344          $this->assert_feedback_data_for_user($cm0a, $u1);
 345          $this->assert_feedback_data_for_user($cm0a, $u2);
 346          $this->assert_feedback_tmp_data_for_user($cm0a, $u1);
 347          $this->assert_feedback_tmp_data_for_user($cm0a, $u2);
 348      }
 349  
 350      /**
 351       * Test exporting data.
 352       */
 353      public function test_export_user_data() {
 354          global $DB;
 355          $dg = $this->getDataGenerator();
 356          $fg = $dg->get_plugin_generator('mod_feedback');
 357  
 358          $c1 = $dg->create_course();
 359          $c2 = $dg->create_course();
 360          $cm0a = $dg->create_module('feedback', ['course' => SITEID]);
 361          $cm1a = $dg->create_module('feedback', ['course' => $c1, 'anonymous' => FEEDBACK_ANONYMOUS_NO]);
 362          $cm2a = $dg->create_module('feedback', ['course' => $c2, 'anonymous' => FEEDBACK_ANONYMOUS_YES, 'multiple_submit' => 1]);
 363          $cm2b = $dg->create_module('feedback', ['course' => $c2]);
 364          $cm2c = $dg->create_module('feedback', ['course' => $c2]);
 365  
 366          $u1 = $dg->create_user();
 367          $u2 = $dg->create_user();
 368  
 369          // Create a bunch of data.
 370          foreach ([$cm0a, $cm1a, $cm2a, $cm2b] as $feedback) {
 371              $i1 = $fg->create_item_numeric($feedback, ['name' => 'Q1', 'label' => 'L1']);
 372              $i2 = $fg->create_item_multichoice($feedback, ['name' => 'Q2', 'label' => 'L2']);
 373              $answersu1 = ['numeric_' . $i1->id => '1', 'multichoice_' . $i2->id => [1]];
 374              $answersu2 = ['numeric_' . $i1->id => '2', 'multichoice_' . $i2->id => [2]];
 375  
 376              if ($cm0a == $feedback) {
 377                  $this->create_submission_with_answers($feedback, $u1, $answersu1);
 378                  $this->create_tmp_submission_with_answers($feedback, $u1, $answersu1);
 379              } else if ($cm1a == $feedback) {
 380                  $this->create_tmp_submission_with_answers($feedback, $u1, $answersu1);
 381              } else if ($cm2a == $feedback) {
 382                  $this->create_submission_with_answers($feedback, $u1, $answersu1);
 383                  $this->create_submission_with_answers($feedback, $u1, ['numeric_' . $i1->id => '1337'], 2);
 384              } else if ($cm2c == $feedback) {
 385                  $this->create_submission_with_answers($feedback, $u1, $answersu1);
 386                  $this->create_tmp_submission_with_answers($feedback, $u1, $answersu1);
 387              }
 388  
 389              $this->create_submission_with_answers($feedback, $u2, $answersu2);
 390              $this->create_tmp_submission_with_answers($feedback, $u2, $answersu2);
 391          }
 392  
 393          $appctx = new approved_contextlist($u1, 'mod_feedback', [
 394              \context_module::instance($cm0a->cmid)->id,
 395              \context_module::instance($cm1a->cmid)->id,
 396              \context_module::instance($cm2a->cmid)->id,
 397              \context_module::instance($cm2b->cmid)->id,
 398          ]);
 399          provider::export_user_data($appctx);
 400  
 401          // CM0A.
 402          $data = writer::with_context(\context_module::instance($cm0a->cmid))->get_data();
 403          $this->assertCount(2, $data->submissions);
 404          $submission = $data->submissions[0];
 405          $this->assertEquals(transform::yesno(false), $submission['inprogress']);
 406          $this->assertEquals(transform::yesno(true), $submission['anonymousresponse']);
 407          $this->assertCount(2, $submission['answers']);
 408          $this->assertEquals('Q1', $submission['answers'][0]['question']);
 409          $this->assertEquals('1', $submission['answers'][0]['answer']);
 410          $this->assertEquals('Q2', $submission['answers'][1]['question']);
 411          $this->assertEquals('a', $submission['answers'][1]['answer']);
 412          $submission = $data->submissions[1];
 413          $this->assertEquals(transform::yesno(true), $submission['inprogress']);
 414          $this->assertEquals(transform::yesno(true), $submission['anonymousresponse']);
 415          $this->assertCount(2, $submission['answers']);
 416          $this->assertEquals('Q1', $submission['answers'][0]['question']);
 417          $this->assertEquals('1', $submission['answers'][0]['answer']);
 418          $this->assertEquals('Q2', $submission['answers'][1]['question']);
 419          $this->assertEquals('a', $submission['answers'][1]['answer']);
 420  
 421          // CM1A.
 422          $data = writer::with_context(\context_module::instance($cm1a->cmid))->get_data();
 423          $this->assertCount(1, $data->submissions);
 424          $submission = $data->submissions[0];
 425          $this->assertEquals(transform::yesno(true), $submission['inprogress']);
 426          $this->assertEquals(transform::yesno(false), $submission['anonymousresponse']);
 427          $this->assertCount(2, $submission['answers']);
 428          $this->assertEquals('Q1', $submission['answers'][0]['question']);
 429          $this->assertEquals('1', $submission['answers'][0]['answer']);
 430          $this->assertEquals('Q2', $submission['answers'][1]['question']);
 431          $this->assertEquals('a', $submission['answers'][1]['answer']);
 432  
 433          // CM2A.
 434          $data = writer::with_context(\context_module::instance($cm2a->cmid))->get_data();
 435          $this->assertCount(2, $data->submissions);
 436          $submission = $data->submissions[0];
 437          $this->assertEquals(transform::yesno(false), $submission['inprogress']);
 438          $this->assertEquals(transform::yesno(true), $submission['anonymousresponse']);
 439          $this->assertCount(2, $submission['answers']);
 440          $this->assertEquals('Q1', $submission['answers'][0]['question']);
 441          $this->assertEquals('1', $submission['answers'][0]['answer']);
 442          $this->assertEquals('Q2', $submission['answers'][1]['question']);
 443          $this->assertEquals('a', $submission['answers'][1]['answer']);
 444          $submission = $data->submissions[1];
 445          $this->assertEquals(transform::yesno(false), $submission['inprogress']);
 446          $this->assertEquals(transform::yesno(true), $submission['anonymousresponse']);
 447          $this->assertCount(1, $submission['answers']);
 448          $this->assertEquals('Q1', $submission['answers'][0]['question']);
 449          $this->assertEquals('1337', $submission['answers'][0]['answer']);
 450  
 451          // CM2B (no data).
 452          $data = writer::with_context(\context_module::instance($cm2b->cmid))->get_data();
 453          $this->assertEmpty($data);
 454  
 455          // CM2C (not exported).
 456          $data = writer::with_context(\context_module::instance($cm2b->cmid))->get_data();
 457          $this->assertEmpty($data);
 458      }
 459  
 460      /**
 461       * Assert there is no feedback data for a user.
 462       *
 463       * @param object $feedback The feedback.
 464       * @param object $user The user.
 465       * @return void
 466       */
 467      protected function assert_no_feedback_data_for_user($feedback, $user) {
 468          global $DB;
 469          $this->assertFalse($DB->record_exists('feedback_completed', ['feedback' => $feedback->id, 'userid' => $user->id]));
 470          $this->assertFalse($DB->record_exists('feedback_completedtmp', ['feedback' => $feedback->id, 'userid' => $user->id]));
 471  
 472          // Check that there aren't orphan values because we can't check by userid.
 473          $sql = "
 474              SELECT fv.id
 475                FROM {%s} fv
 476           LEFT JOIN {%s} fc
 477                  ON fc.id = fv.completed
 478               WHERE fc.id IS NULL";
 479          $this->assertFalse($DB->record_exists_sql(sprintf($sql, 'feedback_value', 'feedback_completed'), []));
 480          $this->assertFalse($DB->record_exists_sql(sprintf($sql, 'feedback_valuetmp', 'feedback_completedtmp'), []));
 481      }
 482  
 483      /**
 484       * Assert there are submissions and answers for user.
 485       *
 486       * @param object $feedback The feedback.
 487       * @param object $user The user.
 488       * @param int $submissioncount The number of submissions.
 489       * @param int $valuecount The number of values per submission.
 490       * @return void
 491       */
 492      protected function assert_feedback_data_for_user($feedback, $user, $submissioncount = 1, $valuecount = 2) {
 493          global $DB;
 494          $completeds = $DB->get_records('feedback_completed', ['feedback' => $feedback->id, 'userid' => $user->id]);
 495          $this->assertCount($submissioncount, $completeds);
 496          foreach ($completeds as $record) {
 497              $this->assertEquals($valuecount, $DB->count_records('feedback_value', ['completed' => $record->id]));
 498          }
 499      }
 500  
 501      /**
 502       * Assert there are temporary submissions and answers for user.
 503       *
 504       * @param object $feedback The feedback.
 505       * @param object $user The user.
 506       * @param int $submissioncount The number of submissions.
 507       * @param int $valuecount The number of values per submission.
 508       * @return void
 509       */
 510      protected function assert_feedback_tmp_data_for_user($feedback, $user, $submissioncount = 1, $valuecount = 2) {
 511          global $DB;
 512          $completedtmps = $DB->get_records('feedback_completedtmp', ['feedback' => $feedback->id, 'userid' => $user->id]);
 513          $this->assertCount($submissioncount, $completedtmps);
 514          foreach ($completedtmps as $record) {
 515              $this->assertEquals($valuecount, $DB->count_records('feedback_valuetmp', ['completed' => $record->id]));
 516          }
 517      }
 518  
 519      /**
 520       * Create an submission with answers.
 521       *
 522       * @param object $feedback The feedback.
 523       * @param object $user The user.
 524       * @param array $answers Answers.
 525       * @param int $submissioncount The number of submissions expected after this entry.
 526       * @return void
 527       */
 528      protected function create_submission_with_answers($feedback, $user, $answers, $submissioncount = 1) {
 529          global $DB;
 530  
 531          $modinfo = get_fast_modinfo($feedback->course);
 532          $cm = $modinfo->get_cm($feedback->cmid);
 533  
 534          $feedbackcompletion = new \mod_feedback_completion($feedback, $cm, $feedback->course, false, null, null, $user->id);
 535          $feedbackcompletion->save_response_tmp((object) $answers);
 536          $feedbackcompletion->save_response();
 537          $this->assertEquals($submissioncount, $DB->count_records('feedback_completed', ['feedback' => $feedback->id,
 538              'userid' => $user->id]));
 539          $this->assertEquals(count($answers), $DB->count_records('feedback_value', [
 540              'completed' => $feedbackcompletion->get_completed()->id]));
 541      }
 542  
 543      /**
 544       * Create a temporary submission with answers.
 545       *
 546       * @param object $feedback The feedback.
 547       * @param object $user The user.
 548       * @param array $answers Answers.
 549       * @return void
 550       */
 551      protected function create_tmp_submission_with_answers($feedback, $user, $answers) {
 552          global $DB;
 553  
 554          $modinfo = get_fast_modinfo($feedback->course);
 555          $cm = $modinfo->get_cm($feedback->cmid);
 556  
 557          $feedbackcompletion = new \mod_feedback_completion($feedback, $cm, $feedback->course, false, null, null, $user->id);
 558          $feedbackcompletion->save_response_tmp((object) $answers);
 559          $this->assertEquals(1, $DB->count_records('feedback_completedtmp', ['feedback' => $feedback->id, 'userid' => $user->id]));
 560          $this->assertEquals(2, $DB->count_records('feedback_valuetmp', [
 561              'completed' => $feedbackcompletion->get_current_completed_tmp()->id]));
 562      }
 563  }