░▒▓████████████████████████████████▓▒░ ░▒▓█ ▓▒░ ░▒▓█ ~ S W A M P ~ ▓▒░ ░▒▓█ ▓▒░ ░▒▓████████████████████████████████▓▒░
pplmvsvm
LOCATION:
/var/www/moodle/public/admin/tool/generator/classes/local/testscenario
☗ ROOT
↻ REFRESH
✎ CARVE FLESH
EDITING: runner.php
<?php // This file is part of Moodle - https://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <https://www.gnu.org/licenses/>. namespace tool_generator\local\testscenario; use behat_admin; use behat_data_generators; use behat_base; use behat_course; use behat_general; use behat_user; use core\attribute_helper; use Behat\Gherkin\Parser; use Behat\Gherkin\Lexer; use Behat\Gherkin\Keywords\ArrayKeywords; use Behat\Gherkin\Node\OutlineNode; use ReflectionClass; use ReflectionMethod; use stdClass; /** * Class to process a scenario generator file. * * @package tool_generator * @copyright 2023 Ferran Recio <ferran@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class runner { /** @var behat_data_generators the behat data generator instance. */ private behat_data_generators $generator; /** @var array of valid steps indexed by given expression tag. */ private array $validsteps; /** * Initi all composer, behat libraries and load the valid steps. */ public function init() { $this->include_composer_libraries(); $this->include_behat_libraries(); $this->load_generator(); $this->load_cleanup(); } /** * Include composer autload. */ public function include_composer_libraries() { global $CFG; if (!file_exists($CFG->dirroot . '/../vendor/autoload.php')) { throw new \moodle_exception('Missing composer.'); } require_once($CFG->dirroot . '/../vendor/autoload.php'); return true; } /** * Include all necessary behat libraries. */ public function include_behat_libraries() { global $CFG; if (!class_exists('Behat\Gherkin\Lexer')) { throw new \moodle_exception('Missing behat classes.'); } // Behat constant. if (!defined('BEHAT_TEST')) { define('BEHAT_TEST', 1); } // Behat utilities. require_once($CFG->libdir . '/behat/classes/util.php'); require_once($CFG->libdir . '/behat/classes/behat_command.php'); require_once($CFG->libdir . '/behat/behat_base.php'); require_once("{$CFG->libdir}/tests/behat/behat_data_generators.php"); require_once("{$CFG->dirroot}/admin/tests/behat/behat_admin.php"); require_once("{$CFG->dirroot}/course/lib.php"); require_once("{$CFG->dirroot}/course/tests/behat/behat_course.php"); require_once("{$CFG->dirroot}/lib/tests/behat/behat_general.php"); require_once("{$CFG->dirroot}/user/tests/behat/behat_user.php"); return true; } /** * Load all generators. */ private function load_generator() { $this->generator = new behat_data_generators(); $this->validsteps = $this->scan_generator($this->generator); // Add some extra steps from other classes. $extrasteps = [ [behat_admin::class, 'the_following_config_values_are_set_as_admin'], [behat_general::class, 'i_enable_plugin'], [behat_general::class, 'i_disable_plugin'], ]; foreach ($extrasteps as $callable) { $classname = $callable[0]; $method = $callable[1]; $extra = $this->scan_method( new ReflectionMethod($classname, $method), new $classname(), ); if ($extra) { $this->validsteps[$extra->given] = $extra; } } } /** * Load all cleanup steps. */ private function load_cleanup() { $extra = $this->scan_method( new ReflectionMethod(behat_course::class, 'the_course_is_deleted'), new behat_course(), ); if ($extra) { $this->validsteps[$extra->given] = $extra; } $extra = $this->scan_method( new ReflectionMethod(behat_user::class, 'the_user_is_deleted'), new behat_user(), ); if ($extra) { $this->validsteps[$extra->given] = $extra; } } /** * Get all valid steps. * @return array the valid steps. */ public function get_valid_steps(): array { return array_values($this->validsteps); } /** * Scan a generator to get all valid steps. * @param behat_data_generators $generator the generator to scan. * @return array the valid steps. */ private function scan_generator(behat_data_generators $generator): array { $result = []; $class = new ReflectionClass($generator); $methods = $class->getMethods(ReflectionMethod::IS_PUBLIC); foreach ($methods as $method) { $scan = $this->scan_method($method, $generator); if ($scan) { $result[$scan->given] = $scan; } } return $result; } /** * Scan a method to get the given expression tag. * @param ReflectionMethod $method the method to scan. * @param behat_base $behatclass the behat class instance to use. * @return stdClass|null the method data (given, name, class). */ private function scan_method(ReflectionMethod $method, behat_base $behatclass): ?stdClass { $given = $this->get_method_given($method); if (!$given) { return null; } $result = (object)[ 'given' => $given, 'name' => $method->getName(), 'generator' => $behatclass, 'example' => null, ]; $reference = $method->getDeclaringClass()->getName() . '::' . $method->getName(); if ($attribute = attribute_helper::instance($reference, \core\attribute\example::class)) { $result->example = (string) $attribute->example; } return $result; } /** * Get the given expression tag of a method. * * @param ReflectionMethod $method the method to get the given expression tag. * @return string|null the given expression tag or null if not found. */ private function get_method_given(ReflectionMethod $method): ?string { $doccomment = $method->getDocComment(); $doccomment = str_replace("\r\n", "\n", $doccomment); $doccomment = str_replace("\r", "\n", $doccomment); $doccomment = explode("\n", $doccomment); foreach ($doccomment as $line) { $matches = []; if (preg_match('/.*\@(given|when|then)\s+(.+)$/i', $line, $matches)) { return $matches[2]; } } return null; } /** * Parse a feature file. * @param string $content the feature file content. * @return parsedfeature */ public function parse_feature(string $content): parsedfeature { return $this->parse_selected_scenarios($content); } /** * Parse all feature file scenarios. * * Note: if no filter is passed, it will execute only the scenarios that are not tagged. * * @param string $content the feature file content. * @param string $filtertag the tag to filter the scenarios. * @return parsedfeature */ private function parse_selected_scenarios(string $content, ?string $filtertag = null): parsedfeature { $result = new parsedfeature(); $parser = $this->get_parser(); $feature = $parser->parse($content); // No need for background in testing scenarios because scenarios can only contain generators. // In the future the background can be used to define clean up steps (when clean up methods // are implemented). if ($feature->hasScenarios()) { $scenarios = $feature->getScenarios(); foreach ($scenarios as $scenario) { // By default, we only execute scenaros that are not tagged. if (empty($filtertag) && !empty($scenario->getTags())) { continue; } if ($filtertag && !in_array($filtertag, $scenario->getTags())) { continue; } if ($scenario->getNodeType() == 'Outline') { $this->parse_scenario_outline($scenario, $result); continue; } $result->add_scenario($scenario->getNodeType(), $scenario->getTitle()); $steps = $scenario->getSteps(); foreach ($steps as $step) { $result->add_step(new steprunner(null, $this->validsteps, $step)); } } } return $result; } /** * Parse a feature file using only the scenarios with cleanup tag. * @param string $content the feature file content. * @return parsedfeature */ public function parse_cleanup(string $content): parsedfeature { return $this->parse_selected_scenarios($content, 'cleanup'); } /** * Parse a scenario outline. * @param OutlineNode $scenario the scenario outline to parse. * @param parsedfeature $result the parsed feature to add the scenario. */ private function parse_scenario_outline(OutlineNode $scenario, parsedfeature $result) { $count = 1; foreach ($scenario->getExamples() as $example) { $result->add_scenario($example->getNodeType(), $example->getOutlineTitle() . " ($count)"); $steps = $example->getSteps(); foreach ($steps as $step) { $result->add_step(new steprunner(null, $this->validsteps, $step)); } $count++; } } /** * Get the parser. * @return Parser */ private function get_parser(): Parser { $keywords = new ArrayKeywords([ 'en' => [ 'feature' => 'Feature', 'background' => 'Background', 'scenario' => 'Scenario', 'scenario_outline' => 'Scenario Outline|Scenario Template', 'examples' => 'Examples|Scenarios', 'given' => 'Given', 'when' => 'When', 'then' => 'Then', 'and' => 'And', 'but' => 'But', ], ]); $lexer = new Lexer($keywords); $parser = new Parser($lexer); return $parser; } /** * Execute a parsed feature. * @param parsedfeature $parsedfeature the parsed feature to execute. * @return bool true if all steps were executed successfully. */ public function execute(parsedfeature $parsedfeature): bool { if (!$parsedfeature->is_valid()) { return false; } $result = true; $steps = $parsedfeature->get_all_steps(); foreach ($steps as $step) { $result = $step->execute() && $result; } return $result; } }
CANCEL
Name
Type
Size
Modified
Actions
↩ ..
DIR
—
—
📄 parsedfeature.php
PHP
3.5 KB
2026-02-11 01:44
EDIT
📄 runner.php
PHP
11.2 KB
2026-02-11 01:44
EDIT
📄 steprunner.php
PHP
7.3 KB
2026-02-11 01:44
EDIT