MDL-32960 refactor phpunit integration to one class per file structure
[moodle.git] / lib / phpunit / classes / 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
21 * @category phpunit
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
26
27/**
28 * Data generator class for unit tests and other tools
29 * that need to create fake test sites.
30 *
31 * @package core
32 * @category phpunit
33 * @copyright 2012 Petr Skoda {@link http://skodak.org}
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35 */
36class phpunit_data_generator {
37 protected $usercounter = 0;
38 protected $categorycount = 0;
39 protected $coursecount = 0;
40 protected $scalecount = 0;
41 protected $groupcount = 0;
42 protected $groupingcount = 0;
43
44 /** @var array list of plugin generators */
45 protected $generators = array();
46
47 /** @var array lis of common last names */
48 public $lastnames = array(
49 'Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller', 'Davis', 'García', 'Rodríguez', 'Wilson',
50 'Müller', 'Schmidt', 'Schneider', 'Fischer', 'Meyer', 'Weber', 'Schulz', 'Wagner', 'Becker', 'Hoffmann',
51 'Novák', 'Svoboda', 'Novotný', 'Dvořák', 'Černý', 'Procházková', 'Kučerová', 'Veselá', 'Horáková', 'Němcová',
52 'Смирнов', 'Иванов', 'Кузнецов', 'Соколов', 'Попов', 'Лебедева', 'Козлова', 'Новикова', 'Морозова', 'Петрова',
53 '王', '李', '张', '刘', '陈', '楊', '黃', '趙', '吳', '周',
54 '佐藤', '鈴木', '高橋', '田中', '渡辺', '伊藤', '山本', '中村', '小林', '斎藤',
55 );
56
57 /** @var array lis of common first names */
58 public $firstnames = array(
59 'Jacob', 'Ethan', 'Michael', 'Jayden', 'William', 'Isabella', 'Sophia', 'Emma', 'Olivia', 'Ava',
60 'Lukas', 'Leon', 'Luca', 'Timm', 'Paul', 'Leonie', 'Leah', 'Lena', 'Hanna', 'Laura',
61 'Jakub', 'Jan', 'Tomáš', 'Lukáš', 'Matěj', 'Tereza', 'Eliška', 'Anna', 'Adéla', 'Karolína',
62 'Даниил', 'Максим', 'Артем', 'Иван', 'Александр', 'София', 'Анастасия', 'Дарья', 'Мария', 'Полина',
63 '伟', '伟', '芳', '伟', '秀英', '秀英', '娜', '秀英', '伟', '敏',
64 '翔', '大翔', '拓海', '翔太', '颯太', '陽菜', 'さくら', '美咲', '葵', '美羽',
65 );
66
67 public $loremipsum = <<<EOD
68Lorem 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.
69Temporibus 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.
70Vivamus 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.
71Integer 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.
72In 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.
73EOD;
74
75 /**
76 * To be called from data reset code only,
77 * do not use in tests.
78 * @return void
79 */
80 public function reset() {
81 $this->usercounter = 0;
82 $this->categorycount = 0;
83 $this->coursecount = 0;
84 $this->scalecount = 0;
85
86 foreach($this->generators as $generator) {
87 $generator->reset();
88 }
89 }
90
91 /**
92 * Return generator for given plugin
93 * @param string $component
94 * @return mixed plugin data generator
95 */
96 public function get_plugin_generator($component) {
97 list($type, $plugin) = normalize_component($component);
98
99 if ($type !== 'mod' and $type !== 'block') {
100 throw new coding_exception("Plugin type $type does not support generators yet");
101 }
102
103 $dir = get_plugin_directory($type, $plugin);
104
105 if (!isset($this->generators[$type.'_'.$plugin])) {
106 $lib = "$dir/tests/generator/lib.php";
107 if (!include_once($lib)) {
108 throw new coding_exception("Plugin $component does not support data generator, missing tests/generator/lib");
109 }
110 $classname = $type.'_'.$plugin.'_generator';
111 $this->generators[$type.'_'.$plugin] = new $classname($this);
112 }
113
114 return $this->generators[$type.'_'.$plugin];
115 }
116
117 /**
118 * Create a test user
119 * @param array|stdClass $record
120 * @param array $options
121 * @return stdClass user record
122 */
123 public function create_user($record=null, array $options=null) {
124 global $DB, $CFG;
125
126 $this->usercounter++;
127 $i = $this->usercounter;
128
129 $record = (array)$record;
130
131 if (!isset($record['auth'])) {
132 $record['auth'] = 'manual';
133 }
134
135 if (!isset($record['firstname']) and !isset($record['lastname'])) {
136 $country = rand(0, 5);
137 $firstname = rand(0, 4);
138 $lastname = rand(0, 4);
139 $female = rand(0, 1);
140 $record['firstname'] = $this->firstnames[($country*10) + $firstname + ($female*5)];
141 $record['lastname'] = $this->lastnames[($country*10) + $lastname + ($female*5)];
142
143 } else if (!isset($record['firstname'])) {
144 $record['firstname'] = 'Firstname'.$i;
145
146 } else if (!isset($record['lastname'])) {
147 $record['lastname'] = 'Lastname'.$i;
148 }
149
150 if (!isset($record['idnumber'])) {
151 $record['idnumber'] = '';
152 }
153
154 if (!isset($record['mnethostid'])) {
155 $record['mnethostid'] = $CFG->mnet_localhost_id;
156 }
157
158 if (!isset($record['username'])) {
159 $record['username'] = textlib::strtolower($record['firstname']).textlib::strtolower($record['lastname']);
160 while ($DB->record_exists('user', array('username'=>$record['username'], 'mnethostid'=>$record['mnethostid']))) {
161 $record['username'] = $record['username'].'_'.$i;
162 }
163 }
164
165 if (!isset($record['password'])) {
166 $record['password'] = 'lala';
167 }
168
169 if (!isset($record['email'])) {
170 $record['email'] = $record['username'].'@example.com';
171 }
172
173 if (!isset($record['confirmed'])) {
174 $record['confirmed'] = 1;
175 }
176
177 if (!isset($record['lang'])) {
178 $record['lang'] = 'en';
179 }
180
181 if (!isset($record['maildisplay'])) {
182 $record['maildisplay'] = 1;
183 }
184
185 if (!isset($record['deleted'])) {
186 $record['deleted'] = 0;
187 }
188
189 $record['timecreated'] = time();
190 $record['timemodified'] = $record['timecreated'];
191 $record['lastip'] = '0.0.0.0';
192
193 $record['password'] = hash_internal_user_password($record['password']);
194
195 if ($record['deleted']) {
196 $delname = $record['email'].'.'.time();
197 while ($DB->record_exists('user', array('username'=>$delname))) {
198 $delname++;
199 }
200 $record['idnumber'] = '';
201 $record['email'] = md5($record['username']);
202 $record['username'] = $delname;
203 $record['picture'] = 0;
204 }
205
206 $userid = $DB->insert_record('user', $record);
207
208 if (!$record['deleted']) {
209 context_user::instance($userid);
210 }
211
212 return $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
213 }
214
215 /**
216 * Create a test course category
217 * @param array|stdClass $record
218 * @param array $options
219 * @return stdClass course category record
220 */
221 function create_category($record=null, array $options=null) {
222 global $DB, $CFG;
223 require_once("$CFG->dirroot/course/lib.php");
224
225 $this->categorycount++;
226 $i = $this->categorycount;
227
228 $record = (array)$record;
229
230 if (!isset($record['name'])) {
231 $record['name'] = 'Course category '.$i;
232 }
233
234 if (!isset($record['idnumber'])) {
235 $record['idnumber'] = '';
236 }
237
238 if (!isset($record['description'])) {
239 $record['description'] = "Test course category $i\n$this->loremipsum";
240 }
241
242 if (!isset($record['descriptionformat'])) {
243 $record['description'] = FORMAT_MOODLE;
244 }
245
246 if (!isset($record['parent'])) {
247 $record['descriptionformat'] = 0;
248 }
249
250 if (empty($record['parent'])) {
251 $parent = new stdClass();
252 $parent->path = '';
253 $parent->depth = 0;
254 } else {
255 $parent = $DB->get_record('course_categories', array('id'=>$record['parent']), '*', MUST_EXIST);
256 }
257 $record['depth'] = $parent->depth+1;
258
259 $record['sortorder'] = 0;
260 $record['timemodified'] = time();
261 $record['timecreated'] = $record['timemodified'];
262
263 $catid = $DB->insert_record('course_categories', $record);
264 $path = $parent->path . '/' . $catid;
265 $DB->set_field('course_categories', 'path', $path, array('id'=>$catid));
266 context_coursecat::instance($catid);
267
268 fix_course_sortorder();
269
270 return $DB->get_record('course_categories', array('id'=>$catid), '*', MUST_EXIST);
271 }
272
273 /**
274 * Create a test course
275 * @param array|stdClass $record
276 * @param array $options with keys:
277 * 'createsections'=>bool precreate all sections
278 * @return stdClass course record
279 */
280 function create_course($record=null, array $options=null) {
281 global $DB, $CFG;
282 require_once("$CFG->dirroot/course/lib.php");
283
284 $this->coursecount++;
285 $i = $this->coursecount;
286
287 $record = (array)$record;
288
289 if (!isset($record['fullname'])) {
290 $record['fullname'] = 'Test course '.$i;
291 }
292
293 if (!isset($record['shortname'])) {
294 $record['shortname'] = 'tc_'.$i;
295 }
296
297 if (!isset($record['idnumber'])) {
298 $record['idnumber'] = '';
299 }
300
301 if (!isset($record['format'])) {
302 $record['format'] = 'topics';
303 }
304
305 if (!isset($record['newsitems'])) {
306 $record['newsitems'] = 0;
307 }
308
309 if (!isset($record['numsections'])) {
310 $record['numsections'] = 5;
311 }
312
313 if (!isset($record['description'])) {
314 $record['description'] = "Test course $i\n$this->loremipsum";
315 }
316
317 if (!isset($record['descriptionformat'])) {
318 $record['description'] = FORMAT_MOODLE;
319 }
320
321 if (!isset($record['category'])) {
322 $record['category'] = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
323 }
324
325 $course = create_course((object)$record);
326 context_course::instance($course->id);
327
328 if (!empty($options['createsections'])) {
329 for($i=1; $i<$record['numsections']; $i++) {
330 self::create_course_section(array('course'=>$course->id, 'section'=>$i));
331 }
332 }
333
334 return $course;
335 }
336
337 /**
338 * Create course section if does not exist yet
339 * @param mixed $record
340 * @param array|null $options
341 * @return stdClass
342 * @throws coding_exception
343 */
344 public function create_course_section($record = null, array $options = null) {
345 global $DB;
346
347 $record = (array)$record;
348
349 if (empty($record['course'])) {
350 throw new coding_exception('course must be present in phpunit_util::create_course_section() $record');
351 }
352
353 if (!isset($record['section'])) {
354 throw new coding_exception('section must be present in phpunit_util::create_course_section() $record');
355 }
356
357 if (!isset($record['name'])) {
358 $record['name'] = '';
359 }
360
361 if (!isset($record['summary'])) {
362 $record['summary'] = '';
363 }
364
365 if (!isset($record['summaryformat'])) {
366 $record['summaryformat'] = FORMAT_MOODLE;
367 }
368
369 if ($section = $DB->get_record('course_sections', array('course'=>$record['course'], 'section'=>$record['section']))) {
370 return $section;
371 }
372
373 $section = new stdClass();
374 $section->course = $record['course'];
375 $section->section = $record['section'];
376 $section->name = $record['name'];
377 $section->summary = $record['summary'];
378 $section->summaryformat = $record['summaryformat'];
379 $id = $DB->insert_record('course_sections', $section);
380
381 return $DB->get_record('course_sections', array('id'=>$id));
382 }
383
384 /**
385 * Create a test block
386 * @param string $blockname
387 * @param array|stdClass $record
388 * @param array $options
389 * @return stdClass block instance record
390 */
391 public function create_block($blockname, $record=null, array $options=null) {
392 $generator = $this->get_plugin_generator('block_'.$blockname);
393 return $generator->create_instance($record, $options);
394 }
395
396 /**
397 * Create a test module
398 * @param string $modulename
399 * @param array|stdClass $record
400 * @param array $options
401 * @return stdClass activity record
402 */
403 public function create_module($modulename, $record=null, array $options=null) {
404 $generator = $this->get_plugin_generator('mod_'.$modulename);
405 return $generator->create_instance($record, $options);
406 }
407
408 /**
409 * Create a test group for the specified course
410 *
411 * $record should be either an array or a stdClass containing infomation about the group to create.
412 * At the very least it needs to contain courseid.
413 * Default values are added for name, description, and descriptionformat if they are not present.
414 *
415 * This function calls {@see groups_create_group()} to create the group within the database.
416 *
417 * @param array|stdClass $record
418 * @return stdClass group record
419 */
420 public function create_group($record) {
421 global $DB, $CFG;
422
423 require_once($CFG->dirroot . '/group/lib.php');
424
425 $this->groupcount++;
426 $i = $this->groupcount;
427
428 $record = (array)$record;
429
430 if (empty($record['courseid'])) {
431 throw new coding_exception('courseid must be present in phpunit_util::create_group() $record');
432 }
433
434 if (!isset($record['name'])) {
435 $record['name'] = 'group-' . $i;
436 }
437
438 if (!isset($record['description'])) {
439 $record['description'] = "Test Group $i\n{$this->loremipsum}";
440 }
441
442 if (!isset($record['descriptionformat'])) {
443 $record['descriptionformat'] = FORMAT_MOODLE;
444 }
445
446 $id = groups_create_group((object)$record);
447
448 return $DB->get_record('groups', array('id'=>$id));
449 }
450
451 /**
452 * Create a test grouping for the specified course
453 *
454 * $record should be either an array or a stdClass containing infomation about the grouping to create.
455 * At the very least it needs to contain courseid.
456 * Default values are added for name, description, and descriptionformat if they are not present.
457 *
458 * This function calls {@see groups_create_grouping()} to create the grouping within the database.
459 *
460 * @param array|stdClass $record
461 * @return stdClass grouping record
462 */
463 public function create_grouping($record) {
464 global $DB, $CFG;
465
466 require_once($CFG->dirroot . '/group/lib.php');
467
468 $this->groupingcount++;
469 $i = $this->groupingcount;
470
471 $record = (array)$record;
472
473 if (empty($record['courseid'])) {
474 throw new coding_exception('courseid must be present in phpunit_util::create_grouping() $record');
475 }
476
477 if (!isset($record['name'])) {
478 $record['name'] = 'grouping-' . $i;
479 }
480
481 if (!isset($record['description'])) {
482 $record['description'] = "Test Grouping $i\n{$this->loremipsum}";
483 }
484
485 if (!isset($record['descriptionformat'])) {
486 $record['descriptionformat'] = FORMAT_MOODLE;
487 }
488
489 $id = groups_create_grouping((object)$record);
490
491 return $DB->get_record('groupings', array('id'=>$id));
492 }
493
494 /**
495 * Create a test scale
496 * @param array|stdClass $record
497 * @param array $options
498 * @return stdClass block instance record
499 */
500 public function create_scale($record=null, array $options=null) {
501 global $DB;
502
503 $this->scalecount++;
504 $i = $this->scalecount;
505
506 $record = (array)$record;
507
508 if (!isset($record['name'])) {
509 $record['name'] = 'Test scale '.$i;
510 }
511
512 if (!isset($record['scale'])) {
513 $record['scale'] = 'A,B,C,D,F';
514 }
515
516 if (!isset($record['courseid'])) {
517 $record['courseid'] = 0;
518 }
519
520 if (!isset($record['userid'])) {
521 $record['userid'] = 0;
522 }
523
524 if (!isset($record['description'])) {
525 $record['description'] = 'Test scale description '.$i;
526 }
527
528 if (!isset($record['descriptionformat'])) {
529 $record['descriptionformat'] = FORMAT_MOODLE;
530 }
531
532 $record['timemodified'] = time();
533
534 if (isset($record['id'])) {
535 $DB->import_record('scale', $record);
536 $DB->get_manager()->reset_sequence('scale');
537 $id = $record['id'];
538 } else {
539 $id = $DB->insert_record('scale', $record);
540 }
541
542 return $DB->get_record('scale', array('id'=>$id), '*', MUST_EXIST);
543 }
544}