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/>. 16 17/** 18 * CLI tool with utilities to manage Behat integration in Moodle 19 * 20 * All CLI utilities uses $CFG->behat_dataroot and $CFG->prefix_dataroot as 21 * $CFG->dataroot and $CFG->prefix 22 * 23 * @package tool_behat 24 * @copyright 2012 David Monllaó 25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 */ 27 28 29if (isset($_SERVER['REMOTE_ADDR'])) { 30 die(); // No access from web!. 31} 32 33// Basic functions. 34require_once(__DIR__ . '/../../../../lib/clilib.php'); 35require_once(__DIR__ . '/../../../../lib/behat/lib.php'); 36 37// CLI options. 38list($options, $unrecognized) = cli_get_params( 39 array( 40 'help' => false, 41 'install' => false, 42 'parallel' => 0, 43 'run' => 0, 44 'drop' => false, 45 'enable' => false, 46 'disable' => false, 47 'diag' => false, 48 'tags' => '', 49 'updatesteps' => false, 50 'optimize-runs' => '', 51 'add-core-features-to-theme' => false, 52 'axe' => false, 53 ), 54 array( 55 'h' => 'help', 56 'o' => 'optimize-runs', 57 'a' => 'add-core-features-to-theme', 58 ) 59); 60 61if ($options['install'] or $options['drop']) { 62 define('CACHE_DISABLE_ALL', true); 63} 64 65// Checking util_single_run.php CLI script usage. 66$help = " 67Behat utilities to manage the test environment 68 69Usage: 70 php util_single_run.php [--install|--drop|--enable|--disable|--diag|--updatesteps|--help] 71 72Options: 73--install Installs the test environment for acceptance tests 74--drop Drops the database tables and the dataroot contents 75--enable Enables test environment and updates tests list 76--disable Disables test environment 77--diag Get behat test environment status code 78--updatesteps Update feature step file. 79--axe Include axe accessibility tests 80 81-o, --optimize-runs Split features with specified tags in all parallel runs. 82-a, --add-core-features-to-theme Add all core features to specified theme's 83 84-h, --help Print out this help 85 86Example from Moodle root directory: 87\$ php admin/tool/behat/cli/util_single_run.php --enable 88 89More info in http://docs.moodle.org/dev/Acceptance_testing#Running_tests 90"; 91 92if (!empty($options['help'])) { 93 echo $help; 94 exit(0); 95} 96 97// Describe this script. 98define('BEHAT_UTIL', true); 99define('CLI_SCRIPT', true); 100define('NO_OUTPUT_BUFFERING', true); 101define('IGNORE_COMPONENT_CACHE', true); 102 103// Set run value, to be used by setup for configuring proper CFG variables. 104if ($options['run']) { 105 define('BEHAT_CURRENT_RUN', $options['run']); 106} 107 108// Only load CFG from config.php, stop ASAP in lib/setup.php. 109define('ABORT_AFTER_CONFIG', true); 110require_once(__DIR__ . '/../../../../config.php'); 111 112// Remove error handling overrides done in config.php. 113$CFG->debug = (E_ALL | E_STRICT); 114$CFG->debugdisplay = 1; 115error_reporting($CFG->debug); 116ini_set('display_errors', '1'); 117ini_set('log_errors', '1'); 118 119// Finish moodle init. 120define('ABORT_AFTER_CONFIG_CANCEL', true); 121require("$CFG->dirroot/lib/setup.php"); 122 123raise_memory_limit(MEMORY_HUGE); 124 125require_once($CFG->libdir.'/adminlib.php'); 126require_once($CFG->libdir.'/upgradelib.php'); 127require_once($CFG->libdir.'/clilib.php'); 128require_once($CFG->libdir.'/installlib.php'); 129require_once($CFG->libdir.'/testing/classes/test_lock.php'); 130 131if ($unrecognized) { 132 $unrecognized = implode(PHP_EOL . " ", $unrecognized); 133 cli_error(get_string('cliunknowoption', 'admin', $unrecognized)); 134} 135 136// Behat utilities. 137require_once($CFG->libdir . '/behat/classes/util.php'); 138require_once($CFG->libdir . '/behat/classes/behat_command.php'); 139require_once($CFG->libdir . '/behat/classes/behat_config_manager.php'); 140 141// Ensure run option is <= parallel run installed. 142$run = 0; 143$parallel = 0; 144if ($options['run']) { 145 $run = $options['run']; 146 // If parallel option is not passed, then try get it form config. 147 if (!$options['parallel']) { 148 $parallel = behat_config_manager::get_behat_run_config_value('parallel'); 149 } else { 150 $parallel = $options['parallel']; 151 } 152 153 if (empty($parallel) || $run > $parallel) { 154 echo "Parallel runs can't be more then ".$parallel.PHP_EOL; 155 exit(1); 156 } 157 $CFG->behatrunprocess = $run; 158} 159 160// Run command (only one per time). 161if ($options['install']) { 162 behat_util::install_site(); 163 164 // This is only displayed once for parallel install. 165 if (empty($run)) { 166 mtrace("Acceptance tests site installed"); 167 } 168 169 // Note: Do not build the themes here. This is done during the 'enable' stage. 170 171} else if ($options['drop']) { 172 // Ensure no tests are running. 173 test_lock::acquire('behat'); 174 behat_util::drop_site(); 175 // This is only displayed once for parallel install. 176 if (empty($run)) { 177 mtrace("Acceptance tests site dropped"); 178 } 179 180} else if ($options['enable']) { 181 if (!empty($parallel)) { 182 // Save parallel site info for enable and install options. 183 behat_config_manager::set_behat_run_config_value('behatsiteenabled', 1); 184 } 185 186 // Define whether to run Behat with axe tests. 187 behat_config_manager::set_behat_run_config_value('axe', $options['axe']); 188 189 // Enable test mode. 190 $timestart = microtime(true); 191 mtrace('Creating Behat configuration ...', ''); 192 behat_util::start_test_mode($options['add-core-features-to-theme'], $options['optimize-runs'], $parallel, $run); 193 mtrace(' done in ' . round(microtime(true) - $timestart, 2) . ' seconds.'); 194 195 // Themes are only built in the 'enable' command. 196 behat_util::build_themes(true); 197 mtrace("Testing environment themes built"); 198 199 // This is only displayed once for parallel install. 200 if (empty($run)) { 201 // Notify user that 2.5 profile has been converted to 3.5. 202 if (behat_config_manager::$autoprofileconversion) { 203 mtrace("2.5 behat profile detected, automatically converted to current 3.x format"); 204 } 205 206 $runtestscommand = behat_command::get_behat_command(true, !empty($run)); 207 208 $runtestscommand .= ' --config ' . behat_config_manager::get_behat_cli_config_filepath(); 209 mtrace("Acceptance tests environment enabled on $CFG->behat_wwwroot, to run the tests use: " . PHP_EOL . 210 $runtestscommand); 211 } 212 213} else if ($options['disable']) { 214 behat_util::stop_test_mode($run); 215 // This is only displayed once for parallel install. 216 if (empty($run)) { 217 mtrace("Acceptance tests environment disabled"); 218 } 219 220} else if ($options['diag']) { 221 $code = behat_util::get_behat_status(); 222 exit($code); 223 224} else if ($options['updatesteps']) { 225 if (defined('BEHAT_FEATURE_STEP_FILE') && BEHAT_FEATURE_STEP_FILE) { 226 $behatstepfile = BEHAT_FEATURE_STEP_FILE; 227 } else { 228 echo "BEHAT_FEATURE_STEP_FILE is not set, please ensure you set this to writable file" . PHP_EOL; 229 exit(1); 230 } 231 232 // Run behat command to get steps in feature files. 233 $featurestepscmd = behat_command::get_behat_command(true); 234 $featurestepscmd .= ' --config ' . behat_config_manager::get_behat_cli_config_filepath(); 235 $featurestepscmd .= ' --dry-run --format=moodle_stepcount'; 236 $processes = cli_execute_parallel(array($featurestepscmd), __DIR__ . "/../../../../"); 237 $status = print_update_step_output(array_pop($processes), $behatstepfile); 238 239 exit($status); 240} else { 241 echo $help; 242 exit(1); 243} 244 245exit(0); 246 247/** 248 * Print update progress as dots for updating feature file step list. 249 * 250 * @param Process $process process executing update step command. 251 * @param string $featurestepfile feature step file in which steps will be saved. 252 * @return int exitcode. 253 */ 254function print_update_step_output($process, $featurestepfile) { 255 $printedlength = 0; 256 257 echo "Updating steps feature file for parallel behat runs" . PHP_EOL; 258 259 // Show progress while running command. 260 while ($process->isRunning()) { 261 usleep(10000); 262 $op = $process->getIncrementalOutput(); 263 if (trim($op)) { 264 echo "."; 265 $printedlength++; 266 if ($printedlength > 70) { 267 $printedlength = 0; 268 echo PHP_EOL; 269 } 270 } 271 } 272 273 // If any error then exit. 274 $exitcode = $process->getExitCode(); 275 // Output err. 276 if ($exitcode != 0) { 277 echo $process->getErrorOutput(); 278 exit($exitcode); 279 } 280 281 // Extract features with step info and save it in file. 282 $featuresteps = $process->getOutput(); 283 $featuresteps = explode(PHP_EOL, $featuresteps); 284 285 $realroot = realpath(__DIR__.'/../../../../').'/'; 286 foreach ($featuresteps as $featurestep) { 287 if (trim($featurestep)) { 288 $step = explode("::", $featurestep); 289 $step[0] = str_replace($realroot, '', $step[0]); 290 $steps[$step[0]] = $step[1]; 291 } 292 } 293 294 if ($existing = @json_decode(file_get_contents($featurestepfile), true)) { 295 $steps = array_merge($existing, $steps); 296 } 297 arsort($steps); 298 299 if (!@file_put_contents($featurestepfile, json_encode($steps, JSON_PRETTY_PRINT))) { 300 behat_error(BEHAT_EXITCODE_PERMISSIONS, 'File ' . $featurestepfile . ' can not be created'); 301 $exitcode = -1; 302 } 303 304 echo PHP_EOL. "Updated step count in " . $featurestepfile . PHP_EOL; 305 306 return $exitcode; 307} 308