MDL-52292 block testing generators: improve the API
[moodle.git] / lib / testing / generator / data_generator.php
CommitLineData
7e7cfe7a
PS
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 generator.
19 *
20 * @package core
6b219869 21 * @category test
7e7cfe7a
PS
22 * @copyright 2012 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
c29e3e24 26defined('MOODLE_INTERNAL') || die();
7e7cfe7a
PS
27
28/**
6b219869 29 * Data generator class for unit tests and other tools that need to create fake test sites.
7e7cfe7a
PS
30 *
31 * @package core
6b219869 32 * @category test
7e7cfe7a
PS
33 * @copyright 2012 Petr Skoda {@link http://skodak.org}
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35 */
5c3c2c81 36class testing_data_generator {
cfa91962 37 /** @var int The number of grade categories created */
15ace204 38 protected $gradecategorycounter = 0;
cfa91962
JO
39 /** @var int The number of grade items created */
40 protected $gradeitemcounter = 0;
41 /** @var int The number of grade outcomes created */
42 protected $gradeoutcomecounter = 0;
7e7cfe7a
PS
43 protected $usercounter = 0;
44 protected $categorycount = 0;
4729332b 45 protected $cohortcount = 0;
7e7cfe7a
PS
46 protected $coursecount = 0;
47 protected $scalecount = 0;
48 protected $groupcount = 0;
49 protected $groupingcount = 0;
702851c0 50 protected $rolecount = 0;
e0c86198 51 protected $tagcount = 0;
7e7cfe7a
PS
52
53 /** @var array list of plugin generators */
54 protected $generators = array();
55
56 /** @var array lis of common last names */
57 public $lastnames = array(
58 'Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller', 'Davis', 'García', 'Rodríguez', 'Wilson',
59 'Müller', 'Schmidt', 'Schneider', 'Fischer', 'Meyer', 'Weber', 'Schulz', 'Wagner', 'Becker', 'Hoffmann',
60 'Novák', 'Svoboda', 'Novotný', 'Dvořák', 'Černý', 'Procházková', 'Kučerová', 'Veselá', 'Horáková', 'Němcová',
61 'Смирнов', 'Иванов', 'Кузнецов', 'Соколов', 'Попов', 'Лебедева', 'Козлова', 'Новикова', 'Морозова', 'Петрова',
62 '王', '李', '张', '刘', '陈', '楊', '黃', '趙', '吳', '周',
63 '佐藤', '鈴木', '高橋', '田中', '渡辺', '伊藤', '山本', '中村', '小林', '斎藤',
64 );
65
66 /** @var array lis of common first names */
67 public $firstnames = array(
68 'Jacob', 'Ethan', 'Michael', 'Jayden', 'William', 'Isabella', 'Sophia', 'Emma', 'Olivia', 'Ava',
69 'Lukas', 'Leon', 'Luca', 'Timm', 'Paul', 'Leonie', 'Leah', 'Lena', 'Hanna', 'Laura',
70 'Jakub', 'Jan', 'Tomáš', 'Lukáš', 'Matěj', 'Tereza', 'Eliška', 'Anna', 'Adéla', 'Karolína',
71 'Даниил', 'Максим', 'Артем', 'Иван', 'Александр', 'София', 'Анастасия', 'Дарья', 'Мария', 'Полина',
72 '伟', '伟', '芳', '伟', '秀英', '秀英', '娜', '秀英', '伟', '敏',
73 '翔', '大翔', '拓海', '翔太', '颯太', '陽菜', 'さくら', '美咲', '葵', '美羽',
74 );
75
76 public $loremipsum = <<<EOD
77Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nulla non arcu lacinia neque faucibus fringilla. Vivamus porttitor turpis ac leo. Integer in sapien. Nullam eget nisl. Aliquam erat volutpat. Cras elementum. Mauris suscipit, ligula sit amet pharetra semper, nibh ante cursus purus, vel sagittis velit mauris vel metus. Integer malesuada. Nullam lectus justo, vulputate eget mollis sed, tempor sed magna. Mauris elementum mauris vitae tortor. Aliquam erat volutpat.
78Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Pellentesque ipsum. Cras pede libero, dapibus nec, pretium sit amet, tempor quis. Aliquam ante. Proin in tellus sit amet nibh dignissim sagittis. Vivamus porttitor turpis ac leo. Duis bibendum, lectus ut viverra rhoncus, dolor nunc faucibus libero, eget facilisis enim ipsum id lacus. In sem justo, commodo ut, suscipit at, pharetra vitae, orci. Aliquam erat volutpat. Nulla est.
79Vivamus luctus egestas leo. Aenean fermentum risus id tortor. Mauris dictum facilisis augue. Aliquam erat volutpat. Aliquam ornare wisi eu metus. Aliquam id dolor. Duis condimentum augue id magna semper rutrum. Donec iaculis gravida nulla. Pellentesque ipsum. Etiam dictum tincidunt diam. Quisque tincidunt scelerisque libero. Etiam egestas wisi a erat.
80Integer lacinia. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris tincidunt sem sed arcu. Nullam feugiat, turpis at pulvinar vulputate, erat libero tristique tellus, nec bibendum odio risus sit amet ante. Aliquam id dolor. Maecenas sollicitudin. Et harum quidem rerum facilis est et expedita distinctio. Mauris suscipit, ligula sit amet pharetra semper, nibh ante cursus purus, vel sagittis velit mauris vel metus. Nullam dapibus fermentum ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Pellentesque sapien. Duis risus. Mauris elementum mauris vitae tortor. Suspendisse nisl. Integer rutrum, orci vestibulum ullamcorper ultricies, lacus quam ultricies odio, vitae placerat pede sem sit amet enim.
81In laoreet, magna id viverra tincidunt, sem odio bibendum justo, vel imperdiet sapien wisi sed libero. Proin pede metus, vulputate nec, fermentum fringilla, vehicula vitae, justo. Nullam justo enim, consectetuer nec, ullamcorper ac, vestibulum in, elit. Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? Maecenas lorem. Etiam posuere lacus quis dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Curabitur ligula sapien, pulvinar a vestibulum quis, facilisis vel sapien. Nam sed tellus id magna elementum tincidunt. Suspendisse nisl. Vivamus luctus egestas leo. Nulla non arcu lacinia neque faucibus fringilla. Etiam dui sem, fermentum vitae, sagittis id, malesuada in, quam. Etiam dictum tincidunt diam. Etiam commodo dui eget wisi. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Proin pede metus, vulputate nec, fermentum fringilla, vehicula vitae, justo. Duis ante orci, molestie vitae vehicula venenatis, tincidunt ac pede. Pellentesque sapien.
82EOD;
83
84 /**
85 * To be called from data reset code only,
86 * do not use in tests.
87 * @return void
88 */
89 public function reset() {
90 $this->usercounter = 0;
91 $this->categorycount = 0;
92 $this->coursecount = 0;
93 $this->scalecount = 0;
94
5c3c2c81 95 foreach ($this->generators as $generator) {
7e7cfe7a
PS
96 $generator->reset();
97 }
98 }
99
100 /**
ba203de1
TH
101 * Return generator for given plugin or component.
102 * @param string $component the component name, e.g. 'mod_forum' or 'core_question'.
103 * @return component_generator_base or rather an instance of the appropriate subclass.
7e7cfe7a
PS
104 */
105 public function get_plugin_generator($component) {
56da374e 106 list($type, $plugin) = core_component::normalize_component($component);
ba203de1
TH
107 $cleancomponent = $type . '_' . $plugin;
108 if ($cleancomponent != $component) {
109 debugging("Please specify the component you want a generator for as " .
110 "{$cleancomponent}, not {$component}.", DEBUG_DEVELOPER);
111 $component = $cleancomponent;
112 }
7e7cfe7a 113
ba203de1
TH
114 if (isset($this->generators[$component])) {
115 return $this->generators[$component];
7e7cfe7a
PS
116 }
117
b0d1d941 118 $dir = core_component::get_component_directory($component);
ba203de1
TH
119 $lib = $dir . '/tests/generator/lib.php';
120 if (!$dir || !is_readable($lib)) {
121 throw new coding_exception("Component {$component} does not support " .
122 "generators yet. Missing tests/generator/lib.php.");
123 }
7e7cfe7a 124
ba203de1
TH
125 include_once($lib);
126 $classname = $component . '_generator';
127
128 if (!class_exists($classname)) {
129 throw new coding_exception("Component {$component} does not support " .
130 "data generators yet. Class {$classname} not found.");
7e7cfe7a
PS
131 }
132
ba203de1
TH
133 $this->generators[$component] = new $classname($this);
134 return $this->generators[$component];
7e7cfe7a
PS
135 }
136
137 /**
138 * Create a test user
139 * @param array|stdClass $record
140 * @param array $options
141 * @return stdClass user record
142 */
143 public function create_user($record=null, array $options=null) {
144 global $DB, $CFG;
145
146 $this->usercounter++;
147 $i = $this->usercounter;
148
149 $record = (array)$record;
150
151 if (!isset($record['auth'])) {
152 $record['auth'] = 'manual';
153 }
154
155 if (!isset($record['firstname']) and !isset($record['lastname'])) {
156 $country = rand(0, 5);
157 $firstname = rand(0, 4);
158 $lastname = rand(0, 4);
159 $female = rand(0, 1);
160 $record['firstname'] = $this->firstnames[($country*10) + $firstname + ($female*5)];
161 $record['lastname'] = $this->lastnames[($country*10) + $lastname + ($female*5)];
162
163 } else if (!isset($record['firstname'])) {
164 $record['firstname'] = 'Firstname'.$i;
165
166 } else if (!isset($record['lastname'])) {
167 $record['lastname'] = 'Lastname'.$i;
168 }
169
a327f25e
AG
170 if (!isset($record['firstnamephonetic'])) {
171 $firstnamephonetic = rand(0, 59);
172 $record['firstnamephonetic'] = $this->firstnames[$firstnamephonetic];
173 }
174
e5035ecb 175 if (!isset($record['lastnamephonetic'])) {
a327f25e
AG
176 $lastnamephonetic = rand(0, 59);
177 $record['lastnamephonetic'] = $this->lastnames[$lastnamephonetic];
178 }
179
180 if (!isset($record['middlename'])) {
181 $middlename = rand(0, 59);
182 $record['middlename'] = $this->firstnames[$middlename];
183 }
184
185 if (!isset($record['alternatename'])) {
186 $alternatename = rand(0, 59);
187 $record['alternatename'] = $this->firstnames[$alternatename];
188 }
189
7e7cfe7a
PS
190 if (!isset($record['idnumber'])) {
191 $record['idnumber'] = '';
192 }
193
194 if (!isset($record['mnethostid'])) {
195 $record['mnethostid'] = $CFG->mnet_localhost_id;
196 }
197
198 if (!isset($record['username'])) {
fe67134e
PS
199 $record['username'] = 'username'.$i;
200 $j = 2;
7e7cfe7a 201 while ($DB->record_exists('user', array('username'=>$record['username'], 'mnethostid'=>$record['mnethostid']))) {
fe67134e
PS
202 $record['username'] = 'username'.$i.'_'.$j;
203 $j++;
7e7cfe7a
PS
204 }
205 }
206
dbf60a04
PS
207 if (isset($record['password'])) {
208 $record['password'] = hash_internal_user_password($record['password']);
209 } else {
210 // The auth plugin may not fully support this,
211 // but it is still better/faster than hashing random stuff.
212 $record['password'] = AUTH_PASSWORD_NOT_CACHED;
7e7cfe7a
PS
213 }
214
215 if (!isset($record['email'])) {
216 $record['email'] = $record['username'].'@example.com';
217 }
218
219 if (!isset($record['confirmed'])) {
220 $record['confirmed'] = 1;
221 }
222
223 if (!isset($record['lang'])) {
224 $record['lang'] = 'en';
225 }
226
227 if (!isset($record['maildisplay'])) {
9f7379e9
MG
228 $record['maildisplay'] = $CFG->defaultpreference_maildisplay;
229 }
230
231 if (!isset($record['mailformat'])) {
232 $record['mailformat'] = $CFG->defaultpreference_mailformat;
233 }
234
235 if (!isset($record['maildigest'])) {
236 $record['maildigest'] = $CFG->defaultpreference_maildigest;
237 }
238
239 if (!isset($record['autosubscribe'])) {
240 $record['autosubscribe'] = $CFG->defaultpreference_autosubscribe;
241 }
242
243 if (!isset($record['trackforums'])) {
244 $record['trackforums'] = $CFG->defaultpreference_trackforums;
7e7cfe7a
PS
245 }
246
247 if (!isset($record['deleted'])) {
248 $record['deleted'] = 0;
249 }
250
2d35b7d3
GPL
251 if (!isset($record['timecreated'])) {
252 $record['timecreated'] = time();
253 }
254
7e7cfe7a
PS
255 $record['timemodified'] = $record['timecreated'];
256 $record['lastip'] = '0.0.0.0';
257
7e7cfe7a
PS
258 if ($record['deleted']) {
259 $delname = $record['email'].'.'.time();
260 while ($DB->record_exists('user', array('username'=>$delname))) {
261 $delname++;
262 }
263 $record['idnumber'] = '';
264 $record['email'] = md5($record['username']);
265 $record['username'] = $delname;
266 $record['picture'] = 0;
267 }
268
269 $userid = $DB->insert_record('user', $record);
270
271 if (!$record['deleted']) {
272 context_user::instance($userid);
273 }
274
810805da
MG
275 $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
276
277 if (!$record['deleted'] && isset($record['interests'])) {
278 require_once($CFG->dirroot . '/user/editlib.php');
279 if (!is_array($record['interests'])) {
280 $record['interests'] = preg_split('/\s*,\s*/', trim($record['interests']), -1, PREG_SPLIT_NO_EMPTY);
281 }
282 useredit_update_interests($user, $record['interests']);
283 }
284
285 return $user;
7e7cfe7a
PS
286 }
287
288 /**
289 * Create a test course category
290 * @param array|stdClass $record
291 * @param array $options
b28bb7e8 292 * @return coursecat course category record
7e7cfe7a 293 */
4729332b 294 public function create_category($record=null, array $options=null) {
7e7cfe7a 295 global $DB, $CFG;
b28bb7e8 296 require_once("$CFG->libdir/coursecatlib.php");
7e7cfe7a
PS
297
298 $this->categorycount++;
299 $i = $this->categorycount;
300
301 $record = (array)$record;
302
303 if (!isset($record['name'])) {
304 $record['name'] = 'Course category '.$i;
305 }
306
7e7cfe7a
PS
307 if (!isset($record['description'])) {
308 $record['description'] = "Test course category $i\n$this->loremipsum";
309 }
310
b28bb7e8
MG
311 if (!isset($record['idnumber'])) {
312 $record['idnumber'] = '';
7e7cfe7a 313 }
7e7cfe7a 314
b28bb7e8 315 return coursecat::create($record);
7e7cfe7a
PS
316 }
317
4729332b
PS
318 /**
319 * Create test cohort.
320 * @param array|stdClass $record
321 * @param array $options
322 * @return stdClass cohort record
323 */
324 public function create_cohort($record=null, array $options=null) {
325 global $DB, $CFG;
326 require_once("$CFG->dirroot/cohort/lib.php");
327
328 $this->cohortcount++;
329 $i = $this->cohortcount;
330
331 $record = (array)$record;
332
333 if (!isset($record['contextid'])) {
334 $record['contextid'] = context_system::instance()->id;
335 }
336
337 if (!isset($record['name'])) {
338 $record['name'] = 'Cohort '.$i;
339 }
340
341 if (!isset($record['idnumber'])) {
342 $record['idnumber'] = '';
343 }
344
345 if (!isset($record['description'])) {
346 $record['description'] = "Test cohort $i\n$this->loremipsum";
347 }
348
349 if (!isset($record['descriptionformat'])) {
350 $record['descriptionformat'] = FORMAT_MOODLE;
351 }
352
80f98467
MG
353 if (!isset($record['visible'])) {
354 $record['visible'] = 1;
355 }
356
4729332b
PS
357 if (!isset($record['component'])) {
358 $record['component'] = '';
359 }
360
361 $id = cohort_add_cohort((object)$record);
362
363 return $DB->get_record('cohort', array('id'=>$id), '*', MUST_EXIST);
364 }
365
7e7cfe7a
PS
366 /**
367 * Create a test course
368 * @param array|stdClass $record
369 * @param array $options with keys:
370 * 'createsections'=>bool precreate all sections
371 * @return stdClass course record
372 */
4729332b 373 public function create_course($record=null, array $options=null) {
7e7cfe7a
PS
374 global $DB, $CFG;
375 require_once("$CFG->dirroot/course/lib.php");
376
377 $this->coursecount++;
378 $i = $this->coursecount;
379
380 $record = (array)$record;
381
382 if (!isset($record['fullname'])) {
383 $record['fullname'] = 'Test course '.$i;
384 }
385
386 if (!isset($record['shortname'])) {
387 $record['shortname'] = 'tc_'.$i;
388 }
389
390 if (!isset($record['idnumber'])) {
391 $record['idnumber'] = '';
392 }
393
394 if (!isset($record['format'])) {
395 $record['format'] = 'topics';
396 }
397
398 if (!isset($record['newsitems'])) {
399 $record['newsitems'] = 0;
400 }
401
402 if (!isset($record['numsections'])) {
403 $record['numsections'] = 5;
404 }
405
4a38e659
PS
406 if (!isset($record['summary'])) {
407 $record['summary'] = "Test course $i\n$this->loremipsum";
7e7cfe7a
PS
408 }
409
4a38e659
PS
410 if (!isset($record['summaryformat'])) {
411 $record['summaryformat'] = FORMAT_MOODLE;
7e7cfe7a
PS
412 }
413
414 if (!isset($record['category'])) {
415 $record['category'] = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
416 }
417
810805da
MG
418 if (isset($record['tags']) && !is_array($record['tags'])) {
419 $record['tags'] = preg_split('/\s*,\s*/', trim($record['tags']), -1, PREG_SPLIT_NO_EMPTY);
420 }
421
7e7cfe7a
PS
422 $course = create_course((object)$record);
423 context_course::instance($course->id);
7e7cfe7a 424 if (!empty($options['createsections'])) {
384c3510
MG
425 if (isset($course->numsections)) {
426 course_create_sections_if_missing($course, range(0, $course->numsections));
427 } else {
428 course_create_sections_if_missing($course, 0);
7e7cfe7a
PS
429 }
430 }
431
432 return $course;
433 }
434
435 /**
436 * Create course section if does not exist yet
384c3510 437 * @param array|stdClass $record must contain 'course' and 'section' attributes
7e7cfe7a
PS
438 * @param array|null $options
439 * @return stdClass
440 * @throws coding_exception
441 */
442 public function create_course_section($record = null, array $options = null) {
443 global $DB;
444
445 $record = (array)$record;
446
447 if (empty($record['course'])) {
5c3c2c81 448 throw new coding_exception('course must be present in testing_data_generator::create_course_section() $record');
7e7cfe7a
PS
449 }
450
451 if (!isset($record['section'])) {
5c3c2c81 452 throw new coding_exception('section must be present in testing_data_generator::create_course_section() $record');
7e7cfe7a
PS
453 }
454
b46be6ad
MG
455 course_create_sections_if_missing($record['course'], $record['section']);
456 return get_fast_modinfo($record['course'])->get_section_info($record['section']);
7e7cfe7a
PS
457 }
458
459 /**
eb3884e4
TH
460 * Create a test block.
461 *
462 * The $record passed in becomes the basis for the new row added to the
463 * block_instances table. You only need to supply the values of interest.
464 * Any missing values have sensible defaults filled in, and ->blockname will be set based on $blockname.
465 *
466 * The $options array provides additional data, not directly related to what
467 * will be inserted in the block_instance table, which may affect the block
468 * that is created. The meanings of any data passed here depends on the particular
469 * type of block being created.
470 *
471 * @param string $blockname the type of block to create. E.g. 'html'.
472 * @param array|stdClass $record forms the basis for the entry to be inserted in the block_instances table.
473 * @param array $options further, block-specific options to control how the block is created.
474 * @return stdClass new block_instance record.
7e7cfe7a
PS
475 */
476 public function create_block($blockname, $record=null, array $options=null) {
477 $generator = $this->get_plugin_generator('block_'.$blockname);
478 return $generator->create_instance($record, $options);
479 }
480
481 /**
eb3884e4
TH
482 * Create a test activity module.
483 *
484 * The $record should contain the same data that you would call from
485 * ->get_data() when the mod_[type]_mod_form is submitted, except that you
486 * only need to supply values of interest. The only required value is
487 * 'course'. Any missing values will have a sensible default supplied.
488 *
489 * The $options array provides additional data, not directly related to what
490 * would come back from the module edit settings form, which may affect the activity
491 * that is created. The meanings of any data passed here depends on the particular
492 * type of activity being created.
493 *
494 * @param string $modulename the type of activity to create. E.g. 'forum' or 'quiz'.
495 * @param array|stdClass $record data, as if from the module edit settings form.
496 * @param array $options additional data that may affect how the module is created.
497 * @return stdClass activity record new new record that was just inserted in the table
498 * like 'forum' or 'quiz', with a ->cmid field added.
7e7cfe7a
PS
499 */
500 public function create_module($modulename, $record=null, array $options=null) {
501 $generator = $this->get_plugin_generator('mod_'.$modulename);
502 return $generator->create_instance($record, $options);
503 }
504
505 /**
506 * Create a test group for the specified course
507 *
508 * $record should be either an array or a stdClass containing infomation about the group to create.
509 * At the very least it needs to contain courseid.
510 * Default values are added for name, description, and descriptionformat if they are not present.
511 *
6b219869
DM
512 * This function calls groups_create_group() to create the group within the database.
513 * @see groups_create_group
7e7cfe7a
PS
514 * @param array|stdClass $record
515 * @return stdClass group record
516 */
517 public function create_group($record) {
518 global $DB, $CFG;
519
520 require_once($CFG->dirroot . '/group/lib.php');
521
522 $this->groupcount++;
523 $i = $this->groupcount;
524
525 $record = (array)$record;
526
527 if (empty($record['courseid'])) {
5c3c2c81 528 throw new coding_exception('courseid must be present in testing_data_generator::create_group() $record');
7e7cfe7a
PS
529 }
530
531 if (!isset($record['name'])) {
532 $record['name'] = 'group-' . $i;
533 }
534
535 if (!isset($record['description'])) {
536 $record['description'] = "Test Group $i\n{$this->loremipsum}";
537 }
538
539 if (!isset($record['descriptionformat'])) {
540 $record['descriptionformat'] = FORMAT_MOODLE;
541 }
542
543 $id = groups_create_group((object)$record);
544
545 return $DB->get_record('groups', array('id'=>$id));
546 }
547
87bb583c
DM
548 /**
549 * Create a test group member
550 * @param array|stdClass $record
551 * @throws coding_exception
552 * @return boolean
553 */
554 public function create_group_member($record) {
555 global $DB, $CFG;
556
557 require_once($CFG->dirroot . '/group/lib.php');
558
559 $record = (array)$record;
560
561 if (empty($record['userid'])) {
562 throw new coding_exception('user must be present in testing_util::create_group_member() $record');
563 }
564
565 if (!isset($record['groupid'])) {
566 throw new coding_exception('group must be present in testing_util::create_group_member() $record');
567 }
568
569 if (!isset($record['component'])) {
570 $record['component'] = null;
571 }
572 if (!isset($record['itemid'])) {
573 $record['itemid'] = 0;
574 }
575
576 return groups_add_member($record['groupid'], $record['userid'], $record['component'], $record['itemid']);
577 }
578
7e7cfe7a
PS
579 /**
580 * Create a test grouping for the specified course
581 *
582 * $record should be either an array or a stdClass containing infomation about the grouping to create.
583 * At the very least it needs to contain courseid.
584 * Default values are added for name, description, and descriptionformat if they are not present.
585 *
6b219869
DM
586 * This function calls groups_create_grouping() to create the grouping within the database.
587 * @see groups_create_grouping
7e7cfe7a
PS
588 * @param array|stdClass $record
589 * @return stdClass grouping record
590 */
591 public function create_grouping($record) {
592 global $DB, $CFG;
593
594 require_once($CFG->dirroot . '/group/lib.php');
595
596 $this->groupingcount++;
597 $i = $this->groupingcount;
598
599 $record = (array)$record;
600
601 if (empty($record['courseid'])) {
5c3c2c81 602 throw new coding_exception('courseid must be present in testing_data_generator::create_grouping() $record');
7e7cfe7a
PS
603 }
604
605 if (!isset($record['name'])) {
606 $record['name'] = 'grouping-' . $i;
607 }
608
609 if (!isset($record['description'])) {
610 $record['description'] = "Test Grouping $i\n{$this->loremipsum}";
611 }
612
613 if (!isset($record['descriptionformat'])) {
614 $record['descriptionformat'] = FORMAT_MOODLE;
615 }
616
617 $id = groups_create_grouping((object)$record);
618
619 return $DB->get_record('groupings', array('id'=>$id));
620 }
621
87bb583c
DM
622 /**
623 * Create a test grouping group
624 * @param array|stdClass $record
625 * @throws coding_exception
626 * @return boolean
627 */
628 public function create_grouping_group($record) {
629 global $DB, $CFG;
630
631 require_once($CFG->dirroot . '/group/lib.php');
632
633 $record = (array)$record;
634
635 if (empty($record['groupingid'])) {
636 throw new coding_exception('grouping must be present in testing::create_grouping_group() $record');
637 }
638
639 if (!isset($record['groupid'])) {
640 throw new coding_exception('group must be present in testing_util::create_grouping_group() $record');
641 }
642
643 return groups_assign_grouping($record['groupingid'], $record['groupid']);
644 }
645
0852bbae
FM
646 /**
647 * Create an instance of a repository.
648 *
649 * @param string type of repository to create an instance for.
650 * @param array|stdClass $record data to use to up set the instance.
651 * @param array $options options
652 * @return stdClass repository instance record
5bcfd504 653 * @since Moodle 2.5.1
0852bbae
FM
654 */
655 public function create_repository($type, $record=null, array $options = null) {
0852bbae
FM
656 $generator = $this->get_plugin_generator('repository_'.$type);
657 return $generator->create_instance($record, $options);
658 }
659
660 /**
661 * Create an instance of a repository.
662 *
663 * @param string type of repository to create an instance for.
664 * @param array|stdClass $record data to use to up set the instance.
665 * @param array $options options
666 * @return repository_type object
5bcfd504 667 * @since Moodle 2.5.1
0852bbae
FM
668 */
669 public function create_repository_type($type, $record=null, array $options = null) {
0852bbae
FM
670 $generator = $this->get_plugin_generator('repository_'.$type);
671 return $generator->create_type($record, $options);
672 }
673
674
7e7cfe7a
PS
675 /**
676 * Create a test scale
677 * @param array|stdClass $record
678 * @param array $options
679 * @return stdClass block instance record
680 */
681 public function create_scale($record=null, array $options=null) {
682 global $DB;
683
684 $this->scalecount++;
685 $i = $this->scalecount;
686
687 $record = (array)$record;
688
689 if (!isset($record['name'])) {
690 $record['name'] = 'Test scale '.$i;
691 }
692
693 if (!isset($record['scale'])) {
694 $record['scale'] = 'A,B,C,D,F';
695 }
696
697 if (!isset($record['courseid'])) {
698 $record['courseid'] = 0;
699 }
700
701 if (!isset($record['userid'])) {
702 $record['userid'] = 0;
703 }
704
705 if (!isset($record['description'])) {
706 $record['description'] = 'Test scale description '.$i;
707 }
708
709 if (!isset($record['descriptionformat'])) {
710 $record['descriptionformat'] = FORMAT_MOODLE;
711 }
712
713 $record['timemodified'] = time();
714
715 if (isset($record['id'])) {
716 $DB->import_record('scale', $record);
717 $DB->get_manager()->reset_sequence('scale');
718 $id = $record['id'];
719 } else {
720 $id = $DB->insert_record('scale', $record);
721 }
722
723 return $DB->get_record('scale', array('id'=>$id), '*', MUST_EXIST);
724 }
4f5789ea 725
702851c0
DM
726 /**
727 * Creates a new role in the system.
728 *
729 * You can fill $record with the role 'name',
730 * 'shortname', 'description' and 'archetype'.
731 *
732 * If an archetype is specified it's capabilities,
733 * context where the role can be assigned and
734 * all other properties are copied from the archetype;
735 * if no archetype is specified it will create an
736 * empty role.
737 *
738 * @param array|stdClass $record
739 * @return int The new role id
740 */
741 public function create_role($record=null) {
742 global $DB;
743
744 $this->rolecount++;
745 $i = $this->rolecount;
746
747 $record = (array)$record;
748
749 if (empty($record['shortname'])) {
750 $record['shortname'] = 'role-' . $i;
751 }
752
753 if (empty($record['name'])) {
754 $record['name'] = 'Test role ' . $i;
755 }
756
757 if (empty($record['description'])) {
758 $record['description'] = 'Test role ' . $i . ' description';
759 }
760
761 if (empty($record['archetype'])) {
762 $record['archetype'] = '';
763 } else {
764 $archetypes = get_role_archetypes();
765 if (empty($archetypes[$record['archetype']])) {
766 throw new coding_exception('\'role\' requires the field \'archetype\' to specify a ' .
767 'valid archetype shortname (editingteacher, student...)');
768 }
769 }
770
771 // Creates the role.
772 if (!$newroleid = create_role($record['name'], $record['shortname'], $record['description'], $record['archetype'])) {
773 throw new coding_exception('There was an error creating \'' . $record['shortname'] . '\' role');
774 }
775
776 // If no archetype was specified we allow it to be added to all contexts,
777 // otherwise we allow it in the archetype contexts.
778 if (!$record['archetype']) {
779 $contextlevels = array_keys(context_helper::get_all_levels());
780 } else {
781 // Copying from the archetype default rol.
782 $archetyperoleid = $DB->get_field(
783 'role',
784 'id',
785 array('shortname' => $record['archetype'], 'archetype' => $record['archetype'])
786 );
787 $contextlevels = get_role_contextlevels($archetyperoleid);
788 }
789 set_role_contextlevels($newroleid, $contextlevels);
790
791 if ($record['archetype']) {
792
793 // We copy all the roles the archetype can assign, override and switch to.
794 if ($record['archetype']) {
795 $types = array('assign', 'override', 'switch');
796 foreach ($types as $type) {
797 $rolestocopy = get_default_role_archetype_allows($type, $record['archetype']);
798 foreach ($rolestocopy as $tocopy) {
799 $functionname = 'allow_' . $type;
800 $functionname($newroleid, $tocopy);
801 }
802 }
803 }
804
805 // Copying the archetype capabilities.
806 $sourcerole = $DB->get_record('role', array('id' => $archetyperoleid));
807 role_cap_duplicate($sourcerole, $newroleid);
808 }
809
810 return $newroleid;
811 }
812
e0c86198
MN
813 /**
814 * Create a tag.
815 *
816 * @param array|stdClass $record
817 * @return stdClass the tag record
818 */
819 public function create_tag($record = null) {
820 global $DB, $USER;
821
822 $this->tagcount++;
823 $i = $this->tagcount;
824
825 $record = (array) $record;
826
827 if (!isset($record['userid'])) {
828 $record['userid'] = $USER->id;
829 }
830
9a7e3986
MG
831 if (!isset($record['rawname'])) {
832 if (isset($record['name'])) {
833 $record['rawname'] = $record['name'];
834 } else {
835 $record['rawname'] = 'Tag name ' . $i;
836 }
e0c86198
MN
837 }
838
9a7e3986
MG
839 // Attribute 'name' should be a lowercase version of 'rawname', if not set.
840 if (!isset($record['name'])) {
841 $record['name'] = core_text::strtolower($record['rawname']);
842 } else {
843 $record['name'] = core_text::strtolower($record['name']);
e0c86198
MN
844 }
845
846 if (!isset($record['tagtype'])) {
847 $record['tagtype'] = 'default';
848 }
849
850 if (!isset($record['description'])) {
851 $record['description'] = 'Tag description';
852 }
853
854 if (!isset($record['descriptionformat'])) {
855 $record['descriptionformat'] = FORMAT_MOODLE;
856 }
857
858 if (!isset($record['flag'])) {
859 $record['flag'] = 0;
860 }
861
862 if (!isset($record['timemodified'])) {
863 $record['timemodified'] = time();
864 }
865
866 $id = $DB->insert_record('tag', $record);
867
868 return $DB->get_record('tag', array('id' => $id), '*', MUST_EXIST);
869 }
870
ba203de1
TH
871 /**
872 * Helper method which combines $defaults with the values specified in $record.
873 * If $record is an object, it is converted to an array.
874 * Then, for each key that is in $defaults, but not in $record, the value
875 * from $defaults is copied.
876 * @param array $defaults the default value for each field with
877 * @param array|stdClass $record
878 * @return array updated $record.
879 */
880 public function combine_defaults_and_record(array $defaults, $record) {
881 $record = (array) $record;
882
883 foreach ($defaults as $key => $defaults) {
884 if (!array_key_exists($key, $record)) {
885 $record[$key] = $defaults;
886 }
887 }
888 return $record;
889 }
890
4f5789ea
PS
891 /**
892 * Simplified enrolment of user to course using default options.
893 *
894 * It is strongly recommended to use only this method for 'manual' and 'self' plugins only!!!
895 *
896 * @param int $userid
897 * @param int $courseid
898 * @param int $roleid optional role id, use only with manual plugin
899 * @param string $enrol name of enrol plugin,
900 * there must be exactly one instance in course,
901 * it must support enrol_user() method.
1ecb8044
RT
902 * @param int $timestart (optional) 0 means unknown
903 * @param int $timeend (optional) 0 means forever
904 * @param int $status (optional) default to ENROL_USER_ACTIVE for new enrolments
4f5789ea
PS
905 * @return bool success
906 */
e6cc5347 907 public function enrol_user($userid, $courseid, $roleid = null, $enrol = 'manual', $timestart = 0, $timeend = 0, $status = null) {
4f5789ea
PS
908 global $DB;
909
910 if (!$plugin = enrol_get_plugin($enrol)) {
911 return false;
912 }
913
914 $instances = $DB->get_records('enrol', array('courseid'=>$courseid, 'enrol'=>$enrol));
915 if (count($instances) != 1) {
916 return false;
917 }
918 $instance = reset($instances);
919
920 if (is_null($roleid) and $instance->roleid) {
921 $roleid = $instance->roleid;
922 }
923
e6cc5347 924 $plugin->enrol_user($instance, $userid, $roleid, $timestart, $timeend, $status);
4f5789ea
PS
925 return true;
926 }
72ddc05f
DM
927
928 /**
929 * Assigns the specified role to a user in the context.
930 *
931 * @param int $roleid
932 * @param int $userid
933 * @param int $contextid Defaults to the system context
934 * @return int new/existing id of the assignment
935 */
936 public function role_assign($roleid, $userid, $contextid = false) {
937
938 // Default to the system context.
939 if (!$contextid) {
940 $context = context_system::instance();
941 $contextid = $context->id;
942 }
943
944 if (empty($roleid)) {
945 throw new coding_exception('roleid must be present in testing_data_generator::role_assign() arguments');
946 }
947
948 if (empty($userid)) {
949 throw new coding_exception('userid must be present in testing_data_generator::role_assign() arguments');
950 }
951
952 return role_assign($roleid, $userid, $contextid);
953 }
954
15ace204
DW
955 /**
956 * Create a grade_category.
957 *
958 * @param array|stdClass $record
959 * @return stdClass the grade category record
960 */
961 public function create_grade_category($record = null) {
962 global $CFG;
963
964 $this->gradecategorycounter++;
15ace204 965
f4f2045a
MG
966 $record = (array)$record;
967
968 if (empty($record['courseid'])) {
969 throw new coding_exception('courseid must be present in testing::create_grade_category() $record');
970 }
971
15ace204 972 if (!isset($record['fullname'])) {
cfa91962 973 $record['fullname'] = 'Grade category ' . $this->gradecategorycounter;
15ace204
DW
974 }
975
976 // For gradelib classes.
977 require_once($CFG->libdir . '/gradelib.php');
978 // Create new grading category in this course.
f4f2045a 979 $gradecategory = new grade_category(array('courseid' => $record['courseid']), false);
15ace204 980 $gradecategory->apply_default_settings();
f4f2045a 981 grade_category::set_properties($gradecategory, $record);
15ace204
DW
982 $gradecategory->apply_forced_settings();
983 $gradecategory->insert();
f4f2045a 984
15ace204
DW
985 // This creates a default grade item for the category
986 $gradeitem = $gradecategory->load_grade_item();
987
15ace204
DW
988 $gradecategory->update_from_db();
989 return $gradecategory->get_record_data();
990 }
cfa91962
JO
991
992 /**
993 * Create a grade_item.
994 *
995 * @param array|stdClass $record
996 * @return stdClass the grade item record
997 */
998 public function create_grade_item($record = null) {
999 global $CFG;
1000 require_once("$CFG->libdir/gradelib.php");
1001
1002 $this->gradeitemcounter++;
1003
1004 if (!isset($record['itemtype'])) {
1005 $record['itemtype'] = 'manual';
1006 }
1007
1008 if (!isset($record['itemname'])) {
1009 $record['itemname'] = 'Grade item ' . $this->gradeitemcounter;
1010 }
1011
1012 if (isset($record['outcomeid'])) {
1013 $outcome = new grade_outcome(array('id' => $record['outcomeid']));
1014 $record['scaleid'] = $outcome->scaleid;
1015 }
1016 if (isset($record['scaleid'])) {
1017 $record['gradetype'] = GRADE_TYPE_SCALE;
1018 } else if (!isset($record['gradetype'])) {
1019 $record['gradetype'] = GRADE_TYPE_VALUE;
1020 }
1021
1022 // Create new grade item in this course.
1023 $gradeitem = new grade_item($record, false);
1024 $gradeitem->insert();
1025
1026 $gradeitem->update_from_db();
1027 return $gradeitem->get_record_data();
1028 }
1029
1030 /**
1031 * Create a grade_outcome.
1032 *
1033 * @param array|stdClass $record
1034 * @return stdClass the grade outcome record
1035 */
1036 public function create_grade_outcome($record = null) {
1037 global $CFG;
1038
1039 $this->gradeoutcomecounter++;
1040 $i = $this->gradeoutcomecounter;
1041
1042 if (!isset($record['fullname'])) {
1043 $record['fullname'] = 'Grade outcome ' . $i;
1044 }
1045
1046 // For gradelib classes.
1047 require_once($CFG->libdir . '/gradelib.php');
1048 // Create new grading outcome in this course.
1049 $gradeoutcome = new grade_outcome($record, false);
1050 $gradeoutcome->insert();
1051
1052 $gradeoutcome->update_from_db();
1053 return $gradeoutcome->get_record_data();
1054 }
7e7cfe7a 1055}