MDL-48024 behat: allow plugins to have data generators
[moodle.git] / lib / tests / behat / behat_data_generators.php
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/>.
17 /**
18  * Data generators for acceptance testing.
19  *
20  * @package   core
21  * @category  test
22  * @copyright 2012 David MonllaĆ³
23  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
28 require_once(__DIR__ . '/../../behat/behat_base.php');
30 use Behat\Gherkin\Node\TableNode as TableNode;
31 use Behat\Behat\Tester\Exception\PendingException as PendingException;
33 /**
34  * Class to set up quickly a Given environment.
35  *
36  * The entry point is the Behat steps:
37  *     the following "entity types" exist:
38  *       | test | data |
39  *
40  * Entity type will either look like "users" or "activities" for core entities, or
41  * "mod_forum > subscription" or "core_message > message" for entities belonging
42  * to components.
43  *
44  * Generally, you only need to specify properties relevant to your test,
45  * and everything else gets set to sensible defaults.
46  *
47  * The actual generation of entities is done by {@link behat_generator_base}.
48  * There is one subclass for each component, e.g. {@link behat_core_generator}
49  * or {@link behat_mod_quiz_generator}. To see the types of entity
50  * that can be created for each component, look at the arrays returned
51  * by the get_creatable_entities() method in each class.
52  *
53  * @package   core
54  * @category  test
55  * @copyright 2012 David MonllaĆ³
56  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
57  */
58 class behat_data_generators extends behat_base {
60     /**
61      * Convert legacy entity names to the new component-specific form.
62      *
63      * In the past, there was no support for plugins, and everything that
64      * could be created was handled by the core generator. Now, we can
65      * support plugins, and so some thing should probably be moved.
66      *
67      * For example, in the future we should probably add
68      * 'message contacts' => 'core_message > contact'] to
69      * this array, and move generation of message contact
70      * from core to core_message.
71      *
72      * @var array old entity type => new entity type.
73      */
74     protected $movedentitytypes = [
75     ];
77     /**
78      * Creates the specified element.
79      *
80      * See the class comment for an overview.
81      *
82      * @Given /^the following "(?P<element_string>(?:[^"]|\\")*)" exist:$/
83      *
84      * @param string    $entitytype The name of the type entity to add
85      * @param TableNode $data
86      */
87     public function the_following_entities_exist($entitytype, TableNode $data) {
88         if (isset($this->movedentitytypes[$entitytype])) {
89             $entitytype = $this->movedentitytypes[$entitytype];
90         }
91         list($component, $entity) = $this->parse_entity_type($entitytype);
92         $this->get_instance_for_component($component)->generate_items($entity, $data);
93     }
95     /**
96      * Parse a full entity type like 'users' or 'mod_forum > subscription'.
97      *
98      * E.g. parsing 'course' gives ['core', 'course'] and
99      * parsing 'core_message > message' gives ['core_message', 'message'].
100      *
101      * @param string $entitytype the entity type
102      * @return string[] with two elements, component and entity type.
103      */
104     protected function parse_entity_type(string $entitytype): array {
105         $dividercount = substr_count($entitytype, ' > ');
106         if ($dividercount === 0) {
107             return ['core', $entitytype];
108         } else if ($dividercount === 1) {
109             list($component, $type) = explode(' > ', $entitytype);
110             if ($component === 'core') {
111                 throw new coding_exception('Do not specify the component "core > ..." for entity types.');
112             }
113             return [$component, $type];
114         } else {
115             throw new coding_exception('The entity type must be in the form ' .
116                     '"{entity-type}" for core entities, or "{component} > {entity-type}" ' .
117                     'for entities belonging to other components. ' .
118                     'For example "users" or "mod_forum > subscriptions".');
119         }
120     }
122     /**
123      * Get an instance of the appropriate subclass of this class for a given component.
124      *
125      * @param string $component The name of the component to generate entities for.
126      * @return behat_generator_base the subclass of this class for the requested component.
127      */
128     protected function get_instance_for_component(string $component): behat_generator_base {
129         global $CFG;
131         // Ensure the generator class is loaded.
132         require_once($CFG->libdir . '/behat/classes/behat_generator_base.php');
133         if ($component === 'core') {
134             $lib = $CFG->libdir . '/behat/classes/behat_core_generator.php';
135         } else {
136             $dir = core_component::get_component_directory($component);
137             $lib = $dir . '/tests/generator/behat_' . $component . '_generator.php';
138             if (!$dir || !is_readable($lib)) {
139                 throw new coding_exception("Component {$component} does not support " .
140                         "behat generators yet. Missing {$lib}.");
141             }
142         }
143         require_once($lib);
145         // Create an instance.
146         $componentclass = "behat_{$component}_generator";
147         if (!class_exists($componentclass)) {
148             throw new PendingException($component .
149                     ' does not yet support the Behat data generator mechanism. Class ' .
150                     $componentclass . ' not found in file ' . $lib . '.');
151         }
152         $instance = new $componentclass($component);
153         return $instance;
154     }