/** * Lists the available steps definitions * * @param string $type * @param string $component * @param string $filter * @return string */ public static function stepsdefinitions($type, $component, $filter) { // We don't require the test environment to be enabled to list the steps definitions // so test writers can more easily set up the environment. behat_command::check_behat_setup(); // The loaded steps depends on the component specified. behat_config_manager::update_config_file($component, false); // The Moodle\BehatExtension\HelpPrinter\MoodleDefinitionsPrinter will parse this search format. if ($type) { $filter .= '&&' . $type; } if ($filter) { $filteroption = ' -d "' . $filter . '"'; } else { $filteroption = ' -di'; } // Get steps definitions from Behat. $options = ' --config="' . behat_config_manager::get_steps_list_config_filepath() . '" ' . $filteroption; list($steps, $code) = behat_command::run($options); if ($steps) { $stepshtml = implode('', $steps); } if (empty($stepshtml)) { $stepshtml = get_string('nostepsdefinitions', 'tool_behat'); } return $stepshtml; }
/** * Returns behat_config_util. * * @return behat_config_util */ private static function get_behat_config_util() { if (!self::$behatconfigutil) { self::$behatconfigutil = new behat_config_util(); } return self::$behatconfigutil; }
require_once __DIR__ . '/../../../../lib/behat/lib.php'; require_once __DIR__ . '/../../../../lib/behat/classes/behat_command.php'; require_once __DIR__ . '/../../../../lib/behat/classes/behat_config_manager.php'; // CLI options. list($options, $unrecognized) = cli_get_params(array('help' => false, 'install' => false, 'drop' => false, 'enable' => false, 'disable' => false, 'diag' => false, 'parallel' => 0, 'maxruns' => false, 'updatesteps' => false, 'fromrun' => 1, 'torun' => 0), array('h' => 'help', 'j' => 'parallel', 'm' => 'maxruns')); // Checking util.php CLI script usage. $help = "\nBehat utilities to manage the test environment\n\nOptions:\n--install Installs the test environment for acceptance tests\n--drop Drops the database tables and the dataroot contents\n--enable Enables test environment and updates tests list\n--disable Disables test environment\n--diag Get behat test environment status code\n-j, --parallel Number of parallel behat run operation\n-m, --maxruns Max parallel processes to be executed at one time.\n--updatesteps Update feature step file.\n\n-h, --help Print out this help\n\nExample from Moodle root directory:\n\$ php admin/tool/behat/cli/util.php --enable --parallel=4\n\nMore info in http://docs.moodle.org/dev/Acceptance_testing#Running_tests\n"; if (!empty($options['help'])) { echo $help; exit(0); } $cwd = getcwd(); // For drop option check if parallel site. if (empty($options['parallel']) && $options['drop']) { // Get parallel run info from first run. $options['parallel'] = behat_config_manager::get_parallel_test_runs($options['fromrun']); } // If not a parallel site then open single run. if (empty($options['parallel'])) { chdir(__DIR__); // Check if behat is initialised, if not exit. passthru("php util_single_run.php --diag", $status); if ($status) { exit($status); } $cmd = commands_to_execute($options); $processes = cli_execute_parallel(array($cmd), __DIR__); $status = print_sequential_output($processes, false); chdir($cwd); exit($status); }
/** * Signal handler for terminal exit. * * @param int $signal signal number. */ function signal_handler($signal) { switch ($signal) { case SIGTERM: case SIGKILL: case SIGINT: // Remove site symlink if necessary. behat_config_manager::drop_parallel_site_links(); exit(1); } }
/** * Parse $CFG->behat_config and return the array with required config structure for behat.yml * * @param string $profile profile name * @param array $values values for profile * @return array */ public function get_behat_config_for_profile($profile, $values) { // Only add profile which are compatible with Behat 3.x // Just check if any of Bheat 2.5 config is set. Not checking for 3.x as it might have some other configs // Like : rerun_cache etc. if (!isset($values['filters']['tags']) && !isset($values['extensions']['Behat\\MinkExtension\\Extension'])) { return array($profile => $values); } // Parse 2.5 format and get related values. $oldconfigvalues = array(); if (isset($values['extensions']['Behat\\MinkExtension\\Extension'])) { $extensionvalues = $values['extensions']['Behat\\MinkExtension\\Extension']; if (isset($extensionvalues['selenium2']['browser'])) { $oldconfigvalues['browser'] = $extensionvalues['selenium2']['browser']; } if (isset($extensionvalues['selenium2']['wd_host'])) { $oldconfigvalues['wd_host'] = $extensionvalues['selenium2']['wd_host']; } if (isset($extensionvalues['capabilities'])) { $oldconfigvalues['capabilities'] = $extensionvalues['capabilities']; } } if (isset($values['filters']['tags'])) { $oldconfigvalues['tags'] = $values['filters']['tags']; } if (!empty($oldconfigvalues)) { behat_config_manager::$autoprofileconversion = true; return $this->get_behat_profile($profile, $oldconfigvalues); } // If nothing set above then return empty array. return array(); }
/** * Enables test mode * * It uses CFG->behat_dataroot * * Starts the test mode checking the composer installation and * the test environment and updating the available * features and steps definitions. * * Stores a file in dataroot/behat to allow Moodle to switch * to the test environment when using cli-server. * @param bool $themesuitewithallfeatures if only theme specific features need to be included in the suite. * @param string $tags comma separated tag, which will be given preference while distributing features in parallel run. * @param int $parallelruns number of parallel runs. * @param int $run current run. * @throws coding_exception * @return void */ public static function start_test_mode($themesuitewithallfeatures = false, $tags = '', $parallelruns = 0, $run = 0) { global $CFG; if (!defined('BEHAT_UTIL')) { throw new coding_exception('This method can be only used by Behat CLI tool'); } // Checks the behat set up and the PHP version. if ($errorcode = behat_command::behat_setup_problem()) { exit($errorcode); } // Check that test environment is correctly set up. self::test_environment_problem(); // Updates all the Moodle features and steps definitions. behat_config_manager::update_config_file('', true, $tags, $themesuitewithallfeatures, $parallelruns, $run); if (self::is_test_mode_enabled()) { return; } $contents = '$CFG->behat_wwwroot, $CFG->behat_prefix and $CFG->behat_dataroot' . ' are currently used as $CFG->wwwroot, $CFG->prefix and $CFG->dataroot'; $filepath = self::get_test_file_path(); if (!file_put_contents($filepath, $contents)) { behat_error(BEHAT_EXITCODE_PERMISSIONS, 'File ' . $filepath . ' can not be created'); } }
/** * Allow access to protected method * @see parent::get_config_file_contents() * @param array $features * @param array $stepsdefinitions * @return string */ public static function get_config_file_contents($features, $stepsdefinitions) { return parent::get_config_file_contents($features, $stepsdefinitions); }
require_once $CFG->libdir . '/behat/classes/util.php'; require_once $CFG->libdir . '/behat/classes/behat_command.php'; // Run command (only one per time). if ($options['install']) { behat_util::install_site(); mtrace("Acceptance tests site installed"); } else { if ($options['drop']) { // Ensure no tests are running. test_lock::acquire('behat'); behat_util::drop_site(); mtrace("Acceptance tests site dropped"); } else { if ($options['enable']) { behat_util::start_test_mode(); $runtestscommand = behat_command::get_behat_command(true) . ' --config ' . behat_config_manager::get_behat_cli_config_filepath(); mtrace("Acceptance tests environment enabled, to run the tests use:\n " . $runtestscommand); } else { if ($options['disable']) { behat_util::stop_test_mode(); mtrace("Acceptance tests environment disabled"); } else { if ($options['diag']) { $code = behat_util::get_behat_status(); exit($code); } else { echo $help; } } } }
if ($options['diag']) { $code = behat_util::get_behat_status(); exit($code); } else { if ($options['updatesteps']) { if (defined('BEHAT_FEATURE_STEP_FILE') && BEHAT_FEATURE_STEP_FILE) { $behatstepfile = BEHAT_FEATURE_STEP_FILE; } else { echo "BEHAT_FEATURE_STEP_FILE is not set, please ensure you set this to writable file" . PHP_EOL; exit(1); } // Rewrite config file to ensure we have all the features covered. behat_config_manager::update_config_file(); // Run behat command to get steps in feature files. $featurestepscmd = behat_command::get_behat_command(true); $featurestepscmd .= ' --config ' . behat_config_manager::get_behat_cli_config_filepath(); $featurestepscmd .= ' --dry-run --format=moodle_step_count'; $processes = cli_execute_parallel(array($featurestepscmd), __DIR__ . "/../../../../"); $status = print_update_step_output(array_pop($processes), $behatstepfile); exit($status); } else { echo $help; exit(1); } } } } } } exit(0); /**
/** * Updates a config file * * @param array $featurestoadd list of features to add. * @param string $proxy proxy url which will be used for test plan generation example: "localhost:9090" * @return void */ protected static function update_config_file($featurestoadd, $proxy = "") { global $CFG; // Behat must have a separate behat.yml to have access to the whole set of features and steps definitions. $configfilepath = self::get_tool_dir() . DIRECTORY_SEPARATOR . 'behat.yml'; $featurepath = self::get_tool_dir(); // Gets all the components with features. $featureslist = glob("{$featurepath}/*.feature"); $features = array(); foreach ($featurestoadd as $featuretoadd) { $feature = preg_grep('/.\\/' . $featuretoadd . '\\.feature/', $featureslist); if (!empty($feature)) { if (count($feature) > 1) { echo "Found more then 1 feature for the requested order set: ", $featuretoadd . PHP_EOL; } $features = array_merge($features, $feature); } } // Gets all the components with steps definitions. $stepsdefinitions = array(); $steps = \behat_config_manager::get_components_steps_definitions(); if ($steps) { foreach ($steps as $key => $filepath) { $stepsdefinitions[$key] = $filepath; } } // We don't want the deprecated steps definitions here. unset($stepsdefinitions['behat_deprecated']); // Remove default hooks. unset($stepsdefinitions['behat_hooks']); // Add generator contexts. $classname = get_called_class(); preg_match('/.*\\\\([a-z-_]+)\\\\[a-z]+$/i', $classname, $behatcontextdir); $contexts = glob(__DIR__ . "/../" . $behatcontextdir[1] . "/classes/behat_*.php"); foreach ($contexts as $context) { preg_match('/.*\\/(behat_[a-z_].*)\\.php$/', $context, $matches); $classname = $matches[1]; $stepsdefinitions[$classname] = $context; } // Add any other context file defined in config. $generatorconfig = self::get_config(); foreach ($featurestoadd as $featurename) { if (!empty($generatorconfig[$featurename]['contextpath'])) { if (!is_array($generatorconfig[$featurename]['contextpath'])) { $customcontextspaths = array($generatorconfig[$featurename]['contextpath']); } else { $customcontextspaths = $generatorconfig[$featurename]['contextpath']; } foreach ($customcontextspaths as $customcontextpath) { preg_match('/.*\\/(behat_[a-z_].*)\\.php$/', $customcontextpath, $matches); $classname = $matches[1]; $stepsdefinitions[$classname] = $customcontextpath; } } } // Behat config file specifing the main context class, // the required Behat extensions and Moodle test wwwroot. $contents = self::get_config_file_contents($features, $stepsdefinitions, $proxy); // Stores the file. if (!file_put_contents($configfilepath, $contents)) { self::performance_exception('File ' . $configfilepath . ' can not be created'); } }
echo PHP_EOL . PHP_EOL; } $status = (bool) $status || (bool) $exitcode; } } } else { if ($options['updatesteps']) { // Rewrite config file to ensure we have all the features covered. if (empty($options['parallel'])) { behat_config_manager::update_config_file('', true, '', $options['run-with-theme'], false, false); } else { // Update config file, ensuring we have up-to-date behat.yml. for ($i = $options['fromrun']; $i <= $options['torun']; $i++) { $CFG->behatrunprocess = $i; // Update config file for each run. behat_config_manager::update_config_file('', true, '', $options['run-with-theme'], $options['parallel'], $i); } unset($CFG->behatrunprocess); } // Do it sequentially as it's fast and need to be displayed nicely. foreach (array_chunk($cmds, 1, true) as $cmd) { $processes = cli_execute_parallel($cmd, __DIR__); print_sequential_output($processes); } exit(0); } else { // We should never reach here. echo $help; exit(1); } }
/** * Web interface to list and filter steps * * @package tool_behat * @copyright 2012 David MonllaĆ³ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require __DIR__ . '/../../../config.php'; require_once $CFG->libdir . '/adminlib.php'; require_once $CFG->dirroot . '/' . $CFG->admin . '/tool/behat/locallib.php'; require_once $CFG->libdir . '/behat/classes/behat_config_manager.php'; $filter = optional_param('filter', '', PARAM_ALPHANUMEXT); $type = optional_param('type', false, PARAM_ALPHAEXT); $component = optional_param('component', '', PARAM_ALPHAEXT); admin_externalpage_setup('toolbehat'); // Getting available steps definitions from behat. $steps = tool_behat::stepsdefinitions($type, $component, $filter); // Form. $componentswithsteps = array('' => get_string('allavailablesteps', 'tool_behat')); // Complete the components list with the moodle steps definitions. $components = behat_config_manager::get_components_steps_definitions(); if ($components) { foreach ($components as $component => $filepath) { // TODO Use a class static attribute instead of the class name. $componentswithsteps[$component] = 'Moodle ' . substr($component, 6); } } $form = new steps_definitions_form(null, array('components' => $componentswithsteps)); // Output contents. $renderer = $PAGE->get_renderer('tool_behat'); echo $renderer->render_stepsdefinitions($steps, $form);
echo PHP_EOL . PHP_EOL; } $status = (bool) $status || (bool) $exitcode; } } } else { if ($options['updatesteps']) { // Rewrite config file to ensure we have all the features covered. if (empty($options['parallel'])) { behat_config_manager::update_config_file('', true, '', $options['add-core-features-to-theme'], false, false); } else { // Update config file, ensuring we have up-to-date behat.yml. for ($i = $options['fromrun']; $i <= $options['torun']; $i++) { $CFG->behatrunprocess = $i; // Update config file for each run. behat_config_manager::update_config_file('', true, $options['optimize-runs'], $options['add-core-features-to-theme'], $options['parallel'], $i); } unset($CFG->behatrunprocess); } // Do it sequentially as it's fast and need to be displayed nicely. foreach (array_chunk($cmds, 1, true) as $cmd) { $processes = cli_execute_parallel($cmd, __DIR__); print_sequential_output($processes); } exit(0); } else { // We should never reach here. echo $help; exit(1); } }
echo $processes[$name]->getErrorOutput(); echo PHP_EOL . PHP_EOL; } $status = (bool) $status || (bool) $exitcode; } } } else { if ($options['updatesteps']) { // Rewrite config file to ensure we have all the features covered. if (empty($options['parallel'])) { behat_config_manager::update_config_file(); } else { // Update config file, ensuring we have up-to-date behat.yml. for ($i = $options['fromrun']; $i <= $options['torun']; $i++) { $CFG->behatrunprocess = $i; behat_config_manager::update_config_file(); } unset($CFG->behatrunprocess); } // Do it sequentially as it's fast and need to be displayed nicely. foreach (array_chunk($cmds, 1, true) as $cmd) { $processes = cli_execute_parallel($cmd, __DIR__); print_sequential_output($processes); } exit(0); } else { // We should never reach here. echo $help; exit(1); } }