Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
16
 
17
namespace tool_generator\local\testscenario;
18
 
19
use behat_data_generators;
20
use Behat\Gherkin\Node\StepNode;
21
 
22
/**
23
 * Class to validate and process a scenario step.
24
 *
25
 * @package    tool_generator
26
 * @copyright  2023 Ferran Recio <ferran@moodle.com>
27
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28
 */
29
class steprunner {
30
    /** @var behat_data_generators the behat data generator instance. */
31
    private behat_data_generators $generator;
32
 
33
    /** @var array the valid steps indexed by given expression tag. */
34
    private array $validsteps;
35
 
36
    /** @var StepNode the step node to process. */
37
    private StepNode $stepnode;
38
 
39
    /** @var string|null the generator method to call. */
40
    private ?string $method = null;
41
 
42
    /** @var array the parameters to pass to the generator method. */
43
    private array $params = [];
44
 
45
    /** @var bool if the step is valid. */
46
    private bool $isvalid = false;
47
 
48
    /** @var bool if the step has been executed. */
49
    private bool $executed = false;
50
 
51
    /** @var string the error message if any. */
52
    private string $error = '';
53
 
54
    /**
55
     * Constructor.
56
     * @param behat_data_generators $generator the behat data generator instance.
57
     * @param array $validsteps the valid steps indexed by given expression tag.
58
     * @param StepNode $stepnode the step node to process.
59
     */
60
    public function __construct(behat_data_generators $generator, array $validsteps, StepNode $stepnode) {
61
        $this->generator = $generator;
62
        $this->validsteps = $validsteps;
63
        $this->stepnode = $stepnode;
64
        $this->init();
65
    }
66
 
67
    /**
68
     * Init the step runner.
69
     *
70
     * This method will check if the step is valid and all the needed information
71
     * in case it is executed.
72
     */
73
    private function init() {
74
        $matches = [];
75
        $linetext = $this->stepnode->getText();
76
        foreach ($this->validsteps as $pattern => $method) {
77
            if (!$this->match_given($pattern, $linetext, $matches)) {
78
                continue;
79
            }
80
            $this->method = $method;
81
            $this->params = $this->build_method_params($method, $matches);
82
            $this->isvalid = true;
83
            return;
84
        }
85
        $this->error = get_string('testscenario_invalidstep', 'tool_generator');
86
    }
87
 
88
    /**
89
     * Build the method parameters.
90
     * @param string $methodname the method name.
91
     * @param array $matches the matches.
92
     * @return array the method parameters.
93
     */
94
    private function build_method_params($methodname, $matches) {
95
        $method = new \ReflectionMethod($this->generator, $methodname);
96
        $params = [];
97
        foreach ($method->getParameters() as $param) {
98
            $paramname = $param->getName();
99
            if (isset($matches[$paramname])) {
100
                $params[] = $matches[$paramname];
101
                unset($matches[$paramname]);
102
            } else if (count($matches) > 0) {
103
                // If the param is not present means the regular expressions does not use
104
                // proper names. So we will try to find the param by position.
105
                $params[] = array_pop($matches);
106
            } else {
107
                // No more params to match.
108
                break;
109
            }
110
        }
111
        return array_merge($params, $this->stepnode->getArguments());
112
    }
113
 
114
    /**
115
     * Return if the step is valid.
116
     * @return bool
117
     */
118
    public function is_valid(): bool {
119
        return $this->isvalid;
120
    }
121
 
122
    /**
123
     * Return if the step has been executed.
124
     * @return bool
125
     */
126
    public function is_executed(): bool {
127
        return $this->executed;
128
    }
129
 
130
    /**
131
     * Return the step text.
132
     * @return string
133
     */
134
    public function get_text(): string {
135
        return $this->stepnode->getText();
136
    }
137
 
138
    /**
139
     * Return the step error message.
140
     * @return string
141
     */
142
    public function get_error(): string {
143
        return $this->error;
144
    }
145
 
146
    /**
147
     * Return the step arguments as string.
148
     * @return string
149
     */
150
    public function get_arguments_string(): string {
151
        $result = '';
152
        foreach ($this->stepnode->getArguments() as $argument) {
153
            $result .= $argument->getTableAsString();
154
        }
155
        return $result;
156
    }
157
 
158
    /**
159
     * Match a given expression with a text.
160
     * @param string $pattern the given expression.
161
     * @param string $text the text to match.
162
     * @param array $matches the matches.
163
     * @return bool if the step matched the generator given expression.
164
     */
165
    private function match_given(string $pattern, $text, array &$matches) {
166
        $internalmatcher = [];
167
        if (substr($pattern, 0, 1) === '/') {
168
            // Pattern is a regular expression.
169
            $result = preg_match($pattern, $text, $matches);
170
            foreach ($matches as $key => $value) {
171
                if (is_int($key)) {
172
                    unset($matches[$key]);
173
                }
174
            }
175
            return $result;
176
        }
177
 
178
        // Patter is a string with parameters.
179
        $elementmatches = [];
180
        preg_match_all('/:([^ ]+)/', $pattern, $elementmatches, PREG_SET_ORDER, 0);
181
 
182
        $pattern = preg_replace('/:([^ ]+)/', '(?P<$1>"[^"]+"|[^" ]+)', $pattern);
183
        $pattern = '/^' . $pattern . '$/';
184
        $result = preg_match($pattern, $text, $internalmatcher);
185
        if (!$result) {
186
            return false;
187
        }
188
        foreach ($elementmatches as $elementmatch) {
189
            // Remove any possible " at the beggining and end of $internalmatcher[$elementmatch[1]].
190
            $paramvalue = preg_replace('/^"(.*)"$/', '$1', $internalmatcher[$elementmatch[1]]);
191
            $matches[$elementmatch[1]] = $paramvalue;
192
        }
193
        return true;
194
    }
195
 
196
    /**
197
     * Execute the step.
198
     * @return bool if the step is executed or not.
199
     */
200
    public function execute(): bool {
201
        if (!$this->isvalid) {
202
            return false;
203
        }
204
        $this->executed = true;
205
        try {
206
            call_user_func_array(
207
                [$this->generator, $this->method],
208
                $this->params
209
            );
210
        } catch (\moodle_exception $exception) {
211
            $this->error = $exception->getMessage();
212
            $this->isvalid = false;
213
            return false;
214
        }
215
        return true;
216
    }
217
}