. /** * PHPUnit data generator class * * @package core * @category phpunit * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Data generator for unit tests * * @package core * @category phpunit * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class phpunit_data_generator { protected $usercounter = 0; protected $categorycount = 0; protected $coursecount = 0; protected $scalecount = 0; protected $groupcount = 0; protected $groupingcount = 0; /** @var array list of plugin generators */ protected $generators = array(); /** @var array lis of common last names */ public $lastnames = array( 'Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller', 'Davis', 'García', 'Rodríguez', 'Wilson', 'Müller', 'Schmidt', 'Schneider', 'Fischer', 'Meyer', 'Weber', 'Schulz', 'Wagner', 'Becker', 'Hoffmann', 'Novák', 'Svoboda', 'Novotný', 'Dvořák', 'Černý', 'Procházková', 'Kučerová', 'Veselá', 'Horáková', 'Němcová', 'Смирнов', 'Иванов', 'Кузнецов', 'Соколов', 'Попов', 'Лебедева', 'Козлова', 'Новикова', 'Морозова', 'Петрова', '王', '李', '张', '刘', '陈', '楊', '黃', '趙', '吳', '周', '佐藤', '鈴木', '高橋', '田中', '渡辺', '伊藤', '山本', '中村', '小林', '斎藤', ); /** @var array lis of common first names */ public $firstnames = array( 'Jacob', 'Ethan', 'Michael', 'Jayden', 'William', 'Isabella', 'Sophia', 'Emma', 'Olivia', 'Ava', 'Lukas', 'Leon', 'Luca', 'Timm', 'Paul', 'Leonie', 'Leah', 'Lena', 'Hanna', 'Laura', 'Jakub', 'Jan', 'Tomáš', 'Lukáš', 'Matěj', 'Tereza', 'Eliška', 'Anna', 'Adéla', 'Karolína', 'Даниил', 'Максим', 'Артем', 'Иван', 'Александр', 'София', 'Анастасия', 'Дарья', 'Мария', 'Полина', '伟', '伟', '芳', '伟', '秀英', '秀英', '娜', '秀英', '伟', '敏', '翔', '大翔', '拓海', '翔太', '颯太', '陽菜', 'さくら', '美咲', '葵', '美羽', ); public $loremipsum = <<usercounter = 0; $this->categorycount = 0; $this->coursecount = 0; $this->scalecount = 0; foreach($this->generators as $generator) { $generator->reset(); } } /** * Return generator for given plugin * @param string $component * @return mixed plugin data generator */ public function get_plugin_generator($component) { list($type, $plugin) = normalize_component($component); if ($type !== 'mod' and $type !== 'block') { throw new coding_exception("Plugin type $type does not support generators yet"); } $dir = get_plugin_directory($type, $plugin); if (!isset($this->generators[$type.'_'.$plugin])) { $lib = "$dir/tests/generator/lib.php"; if (!include_once($lib)) { throw new coding_exception("Plugin $component does not support data generator, missing tests/generator/lib"); } $classname = $type.'_'.$plugin.'_generator'; $this->generators[$type.'_'.$plugin] = new $classname($this); } return $this->generators[$type.'_'.$plugin]; } /** * Create a test user * @param array|stdClass $record * @param array $options * @return stdClass user record */ public function create_user($record=null, array $options=null) { global $DB, $CFG; $this->usercounter++; $i = $this->usercounter; $record = (array)$record; if (!isset($record['auth'])) { $record['auth'] = 'manual'; } if (!isset($record['firstname']) and !isset($record['lastname'])) { $country = rand(0, 5); $firstname = rand(0, 4); $lastname = rand(0, 4); $female = rand(0, 1); $record['firstname'] = $this->firstnames[($country*10) + $firstname + ($female*5)]; $record['lastname'] = $this->lastnames[($country*10) + $lastname + ($female*5)]; } else if (!isset($record['firstname'])) { $record['firstname'] = 'Firstname'.$i; } else if (!isset($record['lastname'])) { $record['lastname'] = 'Lastname'.$i; } if (!isset($record['idnumber'])) { $record['idnumber'] = ''; } if (!isset($record['mnethostid'])) { $record['mnethostid'] = $CFG->mnet_localhost_id; } if (!isset($record['username'])) { $record['username'] = textlib::strtolower($record['firstname']).textlib::strtolower($record['lastname']); while ($DB->record_exists('user', array('username'=>$record['username'], 'mnethostid'=>$record['mnethostid']))) { $record['username'] = $record['username'].'_'.$i; } } if (!isset($record['password'])) { $record['password'] = 'lala'; } if (!isset($record['email'])) { $record['email'] = $record['username'].'@example.com'; } if (!isset($record['confirmed'])) { $record['confirmed'] = 1; } if (!isset($record['lang'])) { $record['lang'] = 'en'; } if (!isset($record['maildisplay'])) { $record['maildisplay'] = 1; } if (!isset($record['deleted'])) { $record['deleted'] = 0; } $record['timecreated'] = time(); $record['timemodified'] = $record['timecreated']; $record['lastip'] = '0.0.0.0'; $record['password'] = hash_internal_user_password($record['password']); if ($record['deleted']) { $delname = $record['email'].'.'.time(); while ($DB->record_exists('user', array('username'=>$delname))) { $delname++; } $record['idnumber'] = ''; $record['email'] = md5($record['username']); $record['username'] = $delname; $record['picture'] = 0; } $userid = $DB->insert_record('user', $record); if (!$record['deleted']) { context_user::instance($userid); } return $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST); } /** * Create a test course category * @param array|stdClass $record * @param array $options * @return stdClass course category record */ function create_category($record=null, array $options=null) { global $DB, $CFG; require_once("$CFG->dirroot/course/lib.php"); $this->categorycount++; $i = $this->categorycount; $record = (array)$record; if (!isset($record['name'])) { $record['name'] = 'Course category '.$i; } if (!isset($record['idnumber'])) { $record['idnumber'] = ''; } if (!isset($record['description'])) { $record['description'] = "Test course category $i\n$this->loremipsum"; } if (!isset($record['descriptionformat'])) { $record['description'] = FORMAT_MOODLE; } if (!isset($record['parent'])) { $record['descriptionformat'] = 0; } if (empty($record['parent'])) { $parent = new stdClass(); $parent->path = ''; $parent->depth = 0; } else { $parent = $DB->get_record('course_categories', array('id'=>$record['parent']), '*', MUST_EXIST); } $record['depth'] = $parent->depth+1; $record['sortorder'] = 0; $record['timemodified'] = time(); $record['timecreated'] = $record['timemodified']; $catid = $DB->insert_record('course_categories', $record); $path = $parent->path . '/' . $catid; $DB->set_field('course_categories', 'path', $path, array('id'=>$catid)); context_coursecat::instance($catid); fix_course_sortorder(); return $DB->get_record('course_categories', array('id'=>$catid), '*', MUST_EXIST); } /** * Create a test course * @param array|stdClass $record * @param array $options with keys: * 'createsections'=>bool precreate all sections * @return stdClass course record */ function create_course($record=null, array $options=null) { global $DB, $CFG; require_once("$CFG->dirroot/course/lib.php"); $this->coursecount++; $i = $this->coursecount; $record = (array)$record; if (!isset($record['fullname'])) { $record['fullname'] = 'Test course '.$i; } if (!isset($record['shortname'])) { $record['shortname'] = 'tc_'.$i; } if (!isset($record['idnumber'])) { $record['idnumber'] = ''; } if (!isset($record['format'])) { $record['format'] = 'topics'; } if (!isset($record['newsitems'])) { $record['newsitems'] = 0; } if (!isset($record['numsections'])) { $record['numsections'] = 5; } if (!isset($record['description'])) { $record['description'] = "Test course $i\n$this->loremipsum"; } if (!isset($record['descriptionformat'])) { $record['description'] = FORMAT_MOODLE; } if (!isset($record['category'])) { $record['category'] = $DB->get_field_select('course_categories', "MIN(id)", "parent=0"); } $course = create_course((object)$record); context_course::instance($course->id); if (!empty($options['createsections'])) { for($i=1; $i<$record['numsections']; $i++) { self::create_course_section(array('course'=>$course->id, 'section'=>$i)); } } return $course; } /** * Create course section if does not exist yet * @param mixed $record * @param array|null $options * @return stdClass * @throws coding_exception */ public function create_course_section($record = null, array $options = null) { global $DB; $record = (array)$record; if (empty($record['course'])) { throw new coding_exception('course must be present in phpunit_util::create_course_section() $record'); } if (!isset($record['section'])) { throw new coding_exception('section must be present in phpunit_util::create_course_section() $record'); } if (!isset($record['name'])) { $record['name'] = ''; } if (!isset($record['summary'])) { $record['summary'] = ''; } if (!isset($record['summaryformat'])) { $record['summaryformat'] = FORMAT_MOODLE; } if ($section = $DB->get_record('course_sections', array('course'=>$record['course'], 'section'=>$record['section']))) { return $section; } $section = new stdClass(); $section->course = $record['course']; $section->section = $record['section']; $section->name = $record['name']; $section->summary = $record['summary']; $section->summaryformat = $record['summaryformat']; $id = $DB->insert_record('course_sections', $section); return $DB->get_record('course_sections', array('id'=>$id)); } /** * Create a test block * @param string $blockname * @param array|stdClass $record * @param array $options * @return stdClass block instance record */ public function create_block($blockname, $record=null, array $options=null) { $generator = $this->get_plugin_generator('block_'.$blockname); return $generator->create_instance($record, $options); } /** * Create a test module * @param string $modulename * @param array|stdClass $record * @param array $options * @return stdClass activity record */ public function create_module($modulename, $record=null, array $options=null) { $generator = $this->get_plugin_generator('mod_'.$modulename); return $generator->create_instance($record, $options); } /** * Create a test group for the specified course * * $record should be either an array or a stdClass containing infomation about the group to create. * At the very least it needs to contain courseid. * Default values are added for name, description, and descriptionformat if they are not present. * * This function calls {@see groups_create_group()} to create the group within the database. * * @param array|stdClass $record * @return stdClass group record */ public function create_group($record) { global $DB, $CFG; require_once($CFG->dirroot . '/group/lib.php'); $this->groupcount++; $i = $this->groupcount; $record = (array)$record; if (empty($record['courseid'])) { throw new coding_exception('courseid must be present in phpunit_util::create_group() $record'); } if (!isset($record['name'])) { $record['name'] = 'group-' . $i; } if (!isset($record['description'])) { $record['description'] = "Test Group $i\n{$this->loremipsum}"; } if (!isset($record['descriptionformat'])) { $record['descriptionformat'] = FORMAT_MOODLE; } $id = groups_create_group((object)$record); return $DB->get_record('groups', array('id'=>$id)); } /** * Create a test grouping for the specified course * * $record should be either an array or a stdClass containing infomation about the grouping to create. * At the very least it needs to contain courseid. * Default values are added for name, description, and descriptionformat if they are not present. * * This function calls {@see groups_create_grouping()} to create the grouping within the database. * * @param array|stdClass $record * @return stdClass grouping record */ public function create_grouping($record) { global $DB, $CFG; require_once($CFG->dirroot . '/group/lib.php'); $this->groupingcount++; $i = $this->groupingcount; $record = (array)$record; if (empty($record['courseid'])) { throw new coding_exception('courseid must be present in phpunit_util::create_grouping() $record'); } if (!isset($record['name'])) { $record['name'] = 'grouping-' . $i; } if (!isset($record['description'])) { $record['description'] = "Test Grouping $i\n{$this->loremipsum}"; } if (!isset($record['descriptionformat'])) { $record['descriptionformat'] = FORMAT_MOODLE; } $id = groups_create_grouping((object)$record); return $DB->get_record('groupings', array('id'=>$id)); } /** * Create a test scale * @param array|stdClass $record * @param array $options * @return stdClass block instance record */ public function create_scale($record=null, array $options=null) { global $DB; $this->scalecount++; $i = $this->scalecount; $record = (array)$record; if (!isset($record['name'])) { $record['name'] = 'Test scale '.$i; } if (!isset($record['scale'])) { $record['scale'] = 'A,B,C,D,F'; } if (!isset($record['courseid'])) { $record['courseid'] = 0; } if (!isset($record['userid'])) { $record['userid'] = 0; } if (!isset($record['description'])) { $record['description'] = 'Test scale description '.$i; } if (!isset($record['descriptionformat'])) { $record['descriptionformat'] = FORMAT_MOODLE; } $record['timemodified'] = time(); if (isset($record['id'])) { $DB->import_record('scale', $record); $DB->get_manager()->reset_sequence('scale'); $id = $record['id']; } else { $id = $DB->insert_record('scale', $record); } return $DB->get_record('scale', array('id'=>$id), '*', MUST_EXIST); } } /** * Module generator base class. * * Extend in mod/xxxx/tests/generator/lib.php as class mod_xxxx_generator. * * @package core * @category phpunit * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class phpunit_module_generator { /** @var phpunit_data_generator@var */ protected $datagenerator; /** @var number of created instances */ protected $instancecount = 0; public function __construct(phpunit_data_generator $datagenerator) { $this->datagenerator = $datagenerator; } /** * To be called from data reset code only, * do not use in tests. * @return void */ public function reset() { $this->instancecount = 0; } /** * Returns module name * @return string name of module that this class describes * @throws coding_exception if class invalid */ public function get_modulename() { $matches = null; if (!preg_match('/^mod_([a-z0-9]+)_generator$/', get_class($this), $matches)) { throw new coding_exception('Invalid module generator class name: '.get_class($this)); } if (empty($matches[1])) { throw new coding_exception('Invalid module generator class name: '.get_class($this)); } return $matches[1]; } /** * Create course module and link it to course * @param int $courseid * @param array $options: section, visible * @return int $cm instance id */ protected function precreate_course_module($courseid, array $options) { global $DB, $CFG; require_once("$CFG->dirroot/course/lib.php"); $modulename = $this->get_modulename(); $cm = new stdClass(); $cm->course = $courseid; $cm->module = $DB->get_field('modules', 'id', array('name'=>$modulename)); $cm->instance = 0; $cm->section = isset($options['section']) ? $options['section'] : 0; $cm->idnumber = isset($options['idnumber']) ? $options['idnumber'] : 0; $cm->added = time(); $columns = $DB->get_columns('course_modules'); foreach ($options as $key=>$value) { if ($key === 'id' or !isset($columns[$key])) { continue; } if (property_exists($cm, $key)) { continue; } $cm->$key = $value; } $cm->id = $DB->insert_record('course_modules', $cm); $cm->coursemodule = $cm->id; add_mod_to_section($cm); return $cm->id; } /** * Called after *_add_instance() * @param int $id * @param int $cmid * @return stdClass module instance */ protected function post_add_instance($id, $cmid) { global $DB; $DB->set_field('course_modules', 'instance', $id, array('id'=>$cmid)); $instance = $DB->get_record($this->get_modulename(), array('id'=>$id), '*', MUST_EXIST); $cm = get_coursemodule_from_id($this->get_modulename(), $cmid, $instance->course, true, MUST_EXIST); context_module::instance($cm->id); $instance->cmid = $cm->id; return $instance; } /** * Create a test module * @param array|stdClass $record * @param array $options * @return stdClass activity record */ abstract public function create_instance($record = null, array $options = null); } /** * Block generator base class. * * Extend in blocks/xxxx/tests/generator/lib.php as class block_xxxx_generator. * * @package core * @category phpunit * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class phpunit_block_generator { /** @var phpunit_data_generator@var */ protected $datagenerator; /** @var number of created instances */ protected $instancecount = 0; public function __construct(phpunit_data_generator $datagenerator) { $this->datagenerator = $datagenerator; } /** * To be called from data reset code only, * do not use in tests. * @return void */ public function reset() { $this->instancecount = 0; } /** * Returns block name * @return string name of block that this class describes * @throws coding_exception if class invalid */ public function get_blockname() { $matches = null; if (!preg_match('/^block_([a-z0-9_]+)_generator$/', get_class($this), $matches)) { throw new coding_exception('Invalid block generator class name: '.get_class($this)); } if (empty($matches[1])) { throw new coding_exception('Invalid block generator class name: '.get_class($this)); } return $matches[1]; } /** * Fill in record defaults * @param stdClass $record * @return stdClass */ protected function prepare_record(stdClass $record) { $record->blockname = $this->get_blockname(); if (!isset($record->parentcontextid)) { $record->parentcontextid = context_system::instance()->id; } if (!isset($record->showinsubcontexts)) { $record->showinsubcontexts = 1; } if (!isset($record->pagetypepattern)) { $record->pagetypepattern = ''; } if (!isset($record->subpagepattern)) { $record->subpagepattern = null; } if (!isset($record->defaultregion)) { $record->defaultregion = ''; } if (!isset($record->defaultweight)) { $record->defaultweight = ''; } if (!isset($record->configdata)) { $record->configdata = null; } return $record; } /** * Create a test block * @param array|stdClass $record * @param array $options * @return stdClass activity record */ abstract public function create_instance($record = null, array $options = null); }