芝麻web文件管理V1.00
编辑当前文件:/home2/sdektunc/www/cepali/lib/behat/classes/behat_generator_base.php
. /** * Base class for data generators component support for acceptance testing. * * @package core * @category test * @copyright 2012 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); use Behat\Gherkin\Node\TableNode as TableNode; use Behat\Behat\Tester\Exception\PendingException as PendingException; /** * Class to quickly create Behat test data using component data generators. * * There is a subclass of class for each component that wants to be able to * generate entities using the Behat step * Given the following "entity types" exist: * | test | data | * * For core entities, the entity type is like "courses" or "users" and * generating those is handled by behat_core_generator. For other components * the entity type is like "mod_quiz > User override" and that is handled by * behat_mod_quiz_generator defined in mod/quiz/tests/generator/behat_mod_quiz_generator.php. * * The types of entities that can be generated are described by the array returned * by the {@link get_generateable_entities()} method. The list in * {@link behat_core_generator} is a good (if complex) example. * * How things work is best explained with a few examples. All this is implemented * in the {@link generate_items()} method below, if you want to see every detail of * how it works. * * Simple example from behat_core_generator: * 'users' => [ * 'datagenerator' => 'user', * 'required' => ['username'], * ], * The steps performed are: * * 1. 'datagenerator' => 'user' means that the word used in the method names below is 'user'. * * 2. Because 'required' is present, check the supplied data exists 'username' column is present * in the supplied data table and if not display an error. * * 3. Then for each row in the table as an array $elementdata (array keys are column names) * and process it as follows * * 4. (Not used in this example.) * * 5. If the method 'preprocess_user' exists, then call it to update $elementdata. * (It does, in this case it sets the password to the username, if password was not given.) * * We then do one of 4 things: * * 6a. If there is a method 'process_user' we call it. (It doesn't for user, * but there are other examples like process_enrol_user() in behat_core_generator.) * * 6b. (Not used in this example.) * * 6c. Else, if testing_data_generator::create_user exists, we call it with $elementdata. (it does.) * * 6d. If none of these three things work. an error is thrown. * * To understand the missing steps above, consider the example from behat_mod_quiz_generator: * 'group override' => [ * 'datagenerator' => 'override', * 'required' => ['quiz', 'group'], * 'switchids' => ['quiz' => 'quiz', 'group' => 'groupid'], * ], * Processing is as above, except that: * * 1. Note 'datagenerator' is 'override' (not group_override). 'user override' maps to the * same datagenerator. This works fine. * * 4. Because 'switchids' is present, human-readable data in the table gets converted to ids. * They array key 'group' refers to a column which may be present in the table (it will be * here because it is required, but it does not have to be in general). If that column * is present and contains a value, then the method matching name like get_group_id() is * called with the value from that column in the data table. You must implement this * method. You can see several examples of this sort of method below. * * If that method returns a group id, then $elementdata['group'] is unset and * $elementdata['groupid'] is set to the result of the get_group_id() call. 'groupid' here * because of the definition is 'switchids' => [..., 'group' => 'groupid']. * If get_group_id() cannot find the group, it should throw a helpful exception. * * Similarly, 'quiz' (the quiz name) is looked up with a call to get_quiz_id(). Here, the * new array key set matches the old one removed. This is fine. * * 6b. We are in a plugin, so before checking whether testing_data_generator::create_override * exists we first check whether mod_quiz_generator::create_override() exists. It does, * and this is what gets called. * * This second example shows why the get_..._id methods for core entities are in this base * class, not in behat_core_generator. Plugins may need to look up the ids of * core entities. * * behat_core_generator is defined in lib/behat/classes/behat_core_generator.php * and for components, behat_..._generator is defined in tests/generator/behat_..._generator.php * inside the plugin. For example behat_mod_quiz_generator is defined in * mod/quiz/tests/generator/behat_mod_quiz_generator.php. * * @package core * @category test * @copyright 2012 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class behat_generator_base { /** * @var string the name of the component we belong to. * * This should probably only be used to make error messages clearer. */ protected $component; /** * @var testing_data_generator the core data generator */ protected $datagenerator; /** * @var testing_data_generator the data generator for this component. */ protected $componentdatagenerator; /** * Constructor. * * @param string $component component name, to make error messages more readable. */ public function __construct(string $component) { $this->component = $component; } /** * Get a list of the entities that can be created for this component. * * This function must be overridden in subclasses. See class comment * above for a description of the data structure. * See {@link behat_core_generator} for an example. * * @return array entity name => information about how to generate. */ protected abstract function get_creatable_entities(): array; /** * Do the work to generate an entity. * * This is called by {@link behat_data_generators::the_following_entities_exist()}. * * @param string $generatortype The name of the entity to create. * @param TableNode $data from the step. */ public function generate_items(string $generatortype, TableNode $data) { // Now that we need them require the data generators. require_once(__DIR__ . '/../../testing/generator/lib.php'); $elements = $this->get_creatable_entities(); if (!isset($elements[$generatortype])) { throw new PendingException($this->name_for_errors($generatortype) . ' is not a known type of entity that can be generated.'); } $entityinfo = $elements[$generatortype]; $this->datagenerator = testing_util::get_data_generator(); if ($this->component === 'core') { $this->componentdatagenerator = $this->datagenerator; } else { $this->componentdatagenerator = $this->datagenerator->get_plugin_generator($this->component); } $generatortype = $entityinfo['datagenerator']; foreach ($data->getHash() as $elementdata) { // Check if all the required fields are there. foreach ($entityinfo['required'] as $requiredfield) { if (!isset($elementdata[$requiredfield])) { throw new Exception($this->name_for_errors($generatortype) . ' requires the field ' . $requiredfield . ' to be specified'); } } // Switch from human-friendly references to ids. if (!empty($entityinfo['switchids'])) { foreach ($entityinfo['switchids'] as $element => $field) { $methodname = 'get_' . $element . '_id'; // Not all the switch fields are required, default vars will be assigned by data generators. if (isset($elementdata[$element])) { if (!method_exists($this, $methodname)) { throw new coding_exception('The generator for ' . $this->name_for_errors($generatortype) . ' entities specifies \'switchids\' => [..., \'' . $element . '\' => \'' . $field . '\', ...] but the required method ' . $methodname . '() has not been defined in ' . get_class($this) . '.'); } // Temp $id var to avoid problems when $element == $field. $id = $this->{$methodname}($elementdata[$element]); unset($elementdata[$element]); $elementdata[$field] = $id; } } } // Preprocess the entities that requires a special treatment. if (method_exists($this, 'preprocess_' . $generatortype)) { $elementdata = $this->{'preprocess_' . $generatortype}($elementdata); } // Creates element. if (method_exists($this, 'process_' . $generatortype)) { // Use a method on this class to do the work. $this->{'process_' . $generatortype}($elementdata); } else if (method_exists($this->componentdatagenerator, 'create_' . $generatortype)) { // Using the component't own data generator if it exists. $this->componentdatagenerator->{'create_' . $generatortype}($elementdata); } else if (method_exists($this->datagenerator, 'create_' . $generatortype)) { // Use a method on the core data geneator, if there is one. $this->datagenerator->{'create_' . $generatortype}($elementdata); } else { // Give up. throw new PendingException($this->name_for_errors($generatortype) . ' data generator is not implemented'); } } } /** * Helper for formatting error messages. * * @param string $entitytype entity type without prefix, e.g. 'frog'. * @return string either 'frog' for core entities, or 'mod_mymod > frog' for components. */ protected function name_for_errors(string $entitytype): string { if ($this->component === 'core') { return '"' . $entitytype . '"'; } else { return '"' . $this->component . ' > ' . $entitytype . '"'; } } /** * Gets the grade category id from the grade category fullname * * @param string $fullname the grade category name. * @return int corresponding id. */ protected function get_gradecategory_id($fullname) { global $DB; if (!$id = $DB->get_field('grade_categories', 'id', array('fullname' => $fullname))) { throw new Exception('The specified grade category with fullname "' . $fullname . '" does not exist'); } return $id; } /** * Gets the user id from it's username. * @throws Exception * @param string $username * @return int */ protected function get_user_id($username) { global $DB; if (!$id = $DB->get_field('user', 'id', array('username' => $username))) { throw new Exception('The specified user with username "' . $username . '" does not exist'); } return $id; } /** * Gets the role id from it's shortname. * @throws Exception * @param string $roleshortname * @return int */ protected function get_role_id($roleshortname) { global $DB; if (!$id = $DB->get_field('role', 'id', array('shortname' => $roleshortname))) { throw new Exception('The specified role with shortname "' . $roleshortname . '" does not exist'); } return $id; } /** * Gets the category id from it's idnumber. * @throws Exception * @param string $idnumber * @return int */ protected function get_category_id($idnumber) { global $DB; // If no category was specified use the data generator one. if ($idnumber == false) { return null; } if (!$id = $DB->get_field('course_categories', 'id', array('idnumber' => $idnumber))) { throw new Exception('The specified category with idnumber "' . $idnumber . '" does not exist'); } return $id; } /** * Gets the course id from it's shortname. * @throws Exception * @param string $shortname * @return int */ protected function get_course_id($shortname) { global $DB; if (!$id = $DB->get_field('course', 'id', array('shortname' => $shortname))) { throw new Exception('The specified course with shortname "' . $shortname . '" does not exist'); } return $id; } /** * Gets the group id from it's idnumber. * @throws Exception * @param string $idnumber * @return int */ protected function get_group_id($idnumber) { global $DB; if (!$id = $DB->get_field('groups', 'id', array('idnumber' => $idnumber))) { throw new Exception('The specified group with idnumber "' . $idnumber . '" does not exist'); } return $id; } /** * Gets the grouping id from it's idnumber. * @throws Exception * @param string $idnumber * @return int */ protected function get_grouping_id($idnumber) { global $DB; // Do not fetch grouping ID for empty grouping idnumber. if (empty($idnumber)) { return null; } if (!$id = $DB->get_field('groupings', 'id', array('idnumber' => $idnumber))) { throw new Exception('The specified grouping with idnumber "' . $idnumber . '" does not exist'); } return $id; } /** * Gets the cohort id from it's idnumber. * @throws Exception * @param string $idnumber * @return int */ protected function get_cohort_id($idnumber) { global $DB; if (!$id = $DB->get_field('cohort', 'id', array('idnumber' => $idnumber))) { throw new Exception('The specified cohort with idnumber "' . $idnumber . '" does not exist'); } return $id; } /** * Gets the outcome item id from its shortname. * @throws Exception * @param string $shortname * @return int */ protected function get_outcome_id($shortname) { global $DB; if (!$id = $DB->get_field('grade_outcomes', 'id', array('shortname' => $shortname))) { throw new Exception('The specified outcome with shortname "' . $shortname . '" does not exist'); } return $id; } /** * Get the id of a named scale. * @param string $name the name of the scale. * @return int the scale id. */ protected function get_scale_id($name) { global $DB; if (!$id = $DB->get_field('scale', 'id', array('name' => $name))) { throw new Exception('The specified scale with name "' . $name . '" does not exist'); } return $id; } /** * Get the id of a named question category (must be globally unique). * Note that 'Top' is a special value, used when setting the parent of another * category, meaning top-level. * * @param string $name the question category name. * @return int the question category id. */ protected function get_questioncategory_id($name) { global $DB; if ($name == 'Top') { return 0; } if (!$id = $DB->get_field('question_categories', 'id', array('name' => $name))) { throw new Exception('The specified question category with name "' . $name . '" does not exist'); } return $id; } /** * Gets the internal context id from the context reference. * * The context reference changes depending on the context * level, it can be the system, a user, a category, a course or * a module. * * @throws Exception * @param string $levelname The context level string introduced by the test writer * @param string $contextref The context reference introduced by the test writer * @return context */ protected function get_context($levelname, $contextref) { global $DB; // Getting context levels and names (we will be using the English ones as it is the test site language). $contextlevels = context_helper::get_all_levels(); $contextnames = array(); foreach ($contextlevels as $level => $classname) { $contextnames[context_helper::get_level_name($level)] = $level; } if (empty($contextnames[$levelname])) { throw new Exception('The specified "' . $levelname . '" context level does not exist'); } $contextlevel = $contextnames[$levelname]; // Return it, we don't need to look for other internal ids. if ($contextlevel == CONTEXT_SYSTEM) { return context_system::instance(); } switch ($contextlevel) { case CONTEXT_USER: $instanceid = $DB->get_field('user', 'id', array('username' => $contextref)); break; case CONTEXT_COURSECAT: $instanceid = $DB->get_field('course_categories', 'id', array('idnumber' => $contextref)); break; case CONTEXT_COURSE: $instanceid = $DB->get_field('course', 'id', array('shortname' => $contextref)); break; case CONTEXT_MODULE: $instanceid = $DB->get_field('course_modules', 'id', array('idnumber' => $contextref)); break; default: break; } $contextclass = $contextlevels[$contextlevel]; if (!$context = $contextclass::instance($instanceid, IGNORE_MISSING)) { throw new Exception('The specified "' . $contextref . '" context reference does not exist'); } return $context; } /** * Gets the contact id from it's username. * @throws Exception * @param string $username * @return int */ protected function get_contact_id($username) { global $DB; if (!$id = $DB->get_field('user', 'id', array('username' => $username))) { throw new Exception('The specified user with username "' . $username . '" does not exist'); } return $id; } }