[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 parallel 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 29 if (isset($_SERVER['REMOTE_ADDR'])) { 30 die(); // No access from web!. 31 } 32 33 define('BEHAT_UTIL', true); 34 define('CLI_SCRIPT', true); 35 define('NO_OUTPUT_BUFFERING', true); 36 define('IGNORE_COMPONENT_CACHE', true); 37 define('ABORT_AFTER_CONFIG', true); 38 39 require_once(__DIR__ . '/../../../../config.php'); 40 require_once (__DIR__ . '/../../../../lib/clilib.php'); 41 require_once (__DIR__ . '/../../../../lib/behat/lib.php'); 42 require_once (__DIR__ . '/../../../../lib/behat/classes/behat_command.php'); 43 require_once (__DIR__ . '/../../../../lib/behat/classes/behat_config_manager.php'); 44 45 // CLI options. 46 list($options, $unrecognized) = cli_get_params( 47 array( 48 'help' => false, 49 'install' => false, 50 'drop' => false, 51 'enable' => false, 52 'disable' => false, 53 'diag' => false, 54 'parallel' => 0, 55 'maxruns' => false, 56 'updatesteps' => false, 57 'fromrun' => 1, 58 'torun' => 0, 59 ), 60 array( 61 'h' => 'help', 62 'j' => 'parallel', 63 'm' => 'maxruns' 64 ) 65 ); 66 67 // Checking util.php CLI script usage. 68 $help = " 69 Behat utilities to manage the test environment 70 71 Usage: 72 php util.php [--install|--drop|--enable|--disable|--diag|--updatesteps|--help] [--parallel=value [--maxruns=value]] 73 74 Options: 75 --install Installs the test environment for acceptance tests 76 --drop Drops the database tables and the dataroot contents 77 --enable Enables test environment and updates tests list 78 --disable Disables test environment 79 --diag Get behat test environment status code 80 --updatesteps Update feature step file. 81 -j, --parallel Number of parallel behat run operation 82 -m, --maxruns Max parallel processes to be executed at one time. 83 84 -h, --help Print out this help 85 86 Example from Moodle root directory: 87 \$ php admin/tool/behat/cli/util.php --enable --parallel=4 88 89 More info in http://docs.moodle.org/dev/Acceptance_testing#Running_tests 90 "; 91 92 if (!empty($options['help'])) { 93 echo $help; 94 exit(0); 95 } 96 97 $cwd = getcwd(); 98 99 // For drop option check if parallel site. 100 if ((empty($options['parallel'])) && ($options['drop']) || $options['updatesteps']) { 101 // Get parallel run info from first run. 102 $options['parallel'] = behat_config_manager::get_parallel_test_runs($options['fromrun']); 103 } 104 105 // If not a parallel site then open single run. 106 if (empty($options['parallel'])) { 107 chdir(__DIR__); 108 // Check if behat is initialised, if not exit. 109 passthru("php util_single_run.php --diag", $status); 110 if ($status) { 111 exit ($status); 112 } 113 $cmd = commands_to_execute($options); 114 $processes = cli_execute_parallel(array($cmd), __DIR__); 115 $status = print_sequential_output($processes, false); 116 chdir($cwd); 117 exit($status); 118 } 119 120 // Default torun is maximum parallel runs. 121 if (empty($options['torun'])) { 122 $options['torun'] = $options['parallel']; 123 } 124 125 $status = false; 126 $cmds = commands_to_execute($options); 127 128 // Start executing commands either sequential/parallel for options provided. 129 if ($options['diag'] || $options['enable'] || $options['disable']) { 130 // Do it sequentially as it's fast and need to be displayed nicely. 131 foreach (array_chunk($cmds, 1, true) as $cmd) { 132 $processes = cli_execute_parallel($cmd, __DIR__); 133 print_sequential_output($processes); 134 } 135 136 } else if ($options['drop']) { 137 $processes = cli_execute_parallel($cmds, __DIR__); 138 $exitcodes = print_combined_drop_output($processes); 139 foreach ($exitcodes as $exitcode) { 140 $status = (bool)$status || (bool)$exitcode; 141 } 142 143 } else if ($options['install']) { 144 // This is intensive compared to behat itself so run them in chunk if option maxruns not set. 145 if ($options['maxruns']) { 146 foreach (array_chunk($cmds, $options['maxruns'], true) as $chunk) { 147 $processes = cli_execute_parallel($chunk, __DIR__); 148 $exitcodes = print_combined_install_output($processes); 149 foreach ($exitcodes as $name => $exitcode) { 150 if ($exitcode != 0) { 151 echo "Failed process [[$name]]" . PHP_EOL; 152 echo $processes[$name]->getOutput(); 153 echo PHP_EOL; 154 echo $processes[$name]->getErrorOutput(); 155 echo PHP_EOL . PHP_EOL; 156 } 157 $status = (bool)$status || (bool)$exitcode; 158 } 159 } 160 } else { 161 $processes = cli_execute_parallel($cmds, __DIR__); 162 $exitcodes = print_combined_install_output($processes); 163 foreach ($exitcodes as $name => $exitcode) { 164 if ($exitcode != 0) { 165 echo "Failed process [[$name]]" . PHP_EOL; 166 echo $processes[$name]->getOutput(); 167 echo PHP_EOL; 168 echo $processes[$name]->getErrorOutput(); 169 echo PHP_EOL . PHP_EOL; 170 } 171 $status = (bool)$status || (bool)$exitcode; 172 } 173 } 174 175 } else if ($options['updatesteps']) { 176 // Rewrite config file to ensure we have all the features covered. 177 if (empty($options['parallel'])) { 178 behat_config_manager::update_config_file(); 179 } else { 180 // Update config file, ensuring we have up-to-date behat.yml. 181 for ($i = $options['fromrun']; $i <= $options['torun']; $i++) { 182 $CFG->behatrunprocess = $i; 183 behat_config_manager::update_config_file(); 184 } 185 unset($CFG->behatrunprocess); 186 } 187 188 // Do it sequentially as it's fast and need to be displayed nicely. 189 foreach (array_chunk($cmds, 1, true) as $cmd) { 190 $processes = cli_execute_parallel($cmd, __DIR__); 191 print_sequential_output($processes); 192 } 193 exit(0); 194 195 } else { 196 // We should never reach here. 197 echo $help; 198 exit(1); 199 } 200 201 // Ensure we have success status to show following information. 202 if ($status) { 203 echo "Unknown failure $status" . PHP_EOL; 204 exit((int)$status); 205 } 206 207 // Show command o/p (only one per time). 208 if ($options['install']) { 209 echo "Acceptance tests site installed for sites:".PHP_EOL; 210 211 // Display all sites which are installed/drop/diabled. 212 for ($i = $options['fromrun']; $i <= $options['torun']; $i++) { 213 if (empty($CFG->behat_parallel_run[$i - 1]['behat_wwwroot'])) { 214 echo $CFG->behat_wwwroot . "/" . BEHAT_PARALLEL_SITE_NAME . $i . PHP_EOL; 215 } else { 216 echo $CFG->behat_parallel_run[$i - 1]['behat_wwwroot'] . PHP_EOL; 217 } 218 219 } 220 } else if ($options['drop']) { 221 echo "Acceptance tests site dropped for " . $options['parallel'] . " parallel sites" . PHP_EOL; 222 223 } else if ($options['enable']) { 224 echo "Acceptance tests environment enabled on $CFG->behat_wwwroot, to run the tests use:" . PHP_EOL; 225 echo behat_command::get_behat_command(true, true); 226 echo PHP_EOL; 227 228 } else if ($options['disable']) { 229 echo "Acceptance tests environment disabled for " . $options['parallel'] . " parallel sites" . PHP_EOL; 230 231 } else if ($options['diag']) { 232 // Valid option, so nothing to do. 233 } else { 234 echo $help; 235 chdir($cwd); 236 exit(1); 237 } 238 239 chdir($cwd); 240 exit(0); 241 242 /** 243 * Create commands to be executed for parallel run. 244 * 245 * @param array $options options provided by user. 246 * @return array commands to be executed. 247 */ 248 function commands_to_execute($options) { 249 $removeoptions = array('maxruns', 'fromrun', 'torun'); 250 $cmds = array(); 251 $extraoptions = $options; 252 $extra = ""; 253 254 // Remove extra options not in util_single_run.php. 255 foreach ($removeoptions as $ro) { 256 $extraoptions[$ro] = null; 257 unset($extraoptions[$ro]); 258 } 259 260 foreach ($extraoptions as $option => $value) { 261 if ($options[$option]) { 262 $extra .= " --$option"; 263 if ($value) { 264 $extra .= "=$value"; 265 } 266 } 267 } 268 269 if (empty($options['parallel'])) { 270 $cmds = "php util_single_run.php " . $extra; 271 } else { 272 // Create commands which has to be executed for parallel site. 273 for ($i = $options['fromrun']; $i <= $options['torun']; $i++) { 274 $prefix = BEHAT_PARALLEL_SITE_NAME . $i; 275 $cmds[$prefix] = "php util_single_run.php " . $extra . " --run=" . $i . " 2>&1"; 276 } 277 } 278 return $cmds; 279 } 280 281 /** 282 * Print drop output merging each run. 283 * 284 * @param array $processes list of processes. 285 * @return array exit codes of each process. 286 */ 287 function print_combined_drop_output($processes) { 288 $exitcodes = array(); 289 $maxdotsonline = 70; 290 $remainingprintlen = $maxdotsonline; 291 $progresscount = 0; 292 echo "Dropping tables:" . PHP_EOL; 293 294 while (count($exitcodes) != count($processes)) { 295 usleep(10000); 296 foreach ($processes as $name => $process) { 297 if ($process->isRunning()) { 298 $op = $process->getIncrementalOutput(); 299 if (trim($op)) { 300 $update = preg_filter('#^\s*([FS\.\-]+)(?:\s+\d+)?\s*$#', '$1', $op); 301 $strlentoprint = strlen($update); 302 303 // If not enough dots printed on line then just print. 304 if ($strlentoprint < $remainingprintlen) { 305 echo $update; 306 $remainingprintlen = $remainingprintlen - $strlentoprint; 307 } else if ($strlentoprint == $remainingprintlen) { 308 $progresscount += $maxdotsonline; 309 echo $update . " " . $progresscount . PHP_EOL; 310 $remainingprintlen = $maxdotsonline; 311 } else { 312 while ($part = substr($update, 0, $remainingprintlen) > 0) { 313 $progresscount += $maxdotsonline; 314 echo $part . " " . $progresscount . PHP_EOL; 315 $update = substr($update, $remainingprintlen); 316 $remainingprintlen = $maxdotsonline; 317 } 318 } 319 } 320 } else { 321 // Process exited. 322 $process->clearOutput(); 323 $exitcodes[$name] = $process->getExitCode(); 324 } 325 } 326 } 327 328 echo PHP_EOL; 329 return $exitcodes; 330 } 331 332 /** 333 * Print install output merging each run. 334 * 335 * @param array $processes list of processes. 336 * @return array exit codes of each process. 337 */ 338 function print_combined_install_output($processes) { 339 $exitcodes = array(); 340 $line = array(); 341 342 // Check what best we can do to accommodate all parallel run o/p on single line. 343 // Windows command line has length of 80 chars, so default we will try fit o/p in 80 chars. 344 if (defined('BEHAT_MAX_CMD_LINE_OUTPUT') && BEHAT_MAX_CMD_LINE_OUTPUT) { 345 $lengthofprocessline = (int)max(10, BEHAT_MAX_CMD_LINE_OUTPUT / count($processes)); 346 } else { 347 $lengthofprocessline = (int)max(10, 80 / count($processes)); 348 } 349 350 echo "Installing behat site for " . count($processes) . " parallel behat run" . PHP_EOL; 351 352 // Show process name in first row. 353 foreach ($processes as $name => $process) { 354 // If we don't have enough space to show full run name then show runX. 355 if ($lengthofprocessline < strlen($name + 2)) { 356 $name = substr($name, -5); 357 } 358 // One extra padding as we are adding | separator for rest of the data. 359 $line[$name] = str_pad('[' . $name . '] ', $lengthofprocessline + 1); 360 } 361 ksort($line); 362 $tableheader = array_keys($line); 363 echo implode("", $line) . PHP_EOL; 364 365 // Now print o/p from each process. 366 while (count($exitcodes) != count($processes)) { 367 usleep(50000); 368 $poutput = array(); 369 // Create child process. 370 foreach ($processes as $name => $process) { 371 if ($process->isRunning()) { 372 $output = $process->getIncrementalOutput(); 373 if (trim($output)) { 374 $poutput[$name] = explode(PHP_EOL, $output); 375 } 376 } else { 377 // Process exited. 378 $exitcodes[$name] = $process->getExitCode(); 379 } 380 } 381 ksort($poutput); 382 383 // Get max depth of o/p before displaying. 384 $maxdepth = 0; 385 foreach ($poutput as $pout) { 386 $pdepth = count($pout); 387 $maxdepth = $pdepth >= $maxdepth ? $pdepth : $maxdepth; 388 } 389 390 // Iterate over each process to get line to print. 391 for ($i = 0; $i <= $maxdepth; $i++) { 392 $pline = ""; 393 foreach ($tableheader as $name) { 394 $po = empty($poutput[$name][$i]) ? "" : substr($poutput[$name][$i], 0, $lengthofprocessline - 1); 395 $po = str_pad($po, $lengthofprocessline); 396 $pline .= "|". $po; 397 } 398 if (trim(str_replace("|", "", $pline))) { 399 echo $pline . PHP_EOL; 400 } 401 } 402 unset($poutput); 403 $poutput = null; 404 405 } 406 echo PHP_EOL; 407 return $exitcodes; 408 } 409 410 /** 411 * Print install output merging showing one run at a time. 412 * If any process fail then exit. 413 * 414 * @param array $processes list of processes. 415 * @param bool $showprefix show prefix. 416 * @return bool exitcode. 417 */ 418 function print_sequential_output($processes, $showprefix = true) { 419 $status = false; 420 foreach ($processes as $name => $process) { 421 $shownname = false; 422 while ($process->isRunning()) { 423 $op = $process->getIncrementalOutput(); 424 if (trim($op)) { 425 // Show name of the run once for sequential. 426 if ($showprefix && !$shownname) { 427 echo '[' . $name . '] '; 428 $shownname = true; 429 } 430 echo $op; 431 } 432 } 433 // If any error then exit. 434 $exitcode = $process->getExitCode(); 435 if ($exitcode != 0) { 436 exit($exitcode); 437 } 438 $status = $status || (bool)$exitcode; 439 } 440 return $status; 441 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |