MDL-54628 behat: Show parallel run command with proper rerun option
[moodle.git] / lib / behat / classes / behat_command.php
bloba0be36a9c4b9f28b1a509840def1c4651f70eef5
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.
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/>.
17 /**
18 * Behat command utils
20 * @package core
21 * @category test
22 * @copyright 2012 David MonllaĆ³
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') || die();
28 require_once(__DIR__ . '/../lib.php');
30 /**
31 * Behat command related utils
33 * @package core
34 * @category test
35 * @copyright 2013 David MonllaĆ³
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 class behat_command {
40 /**
41 * Docs url
43 const DOCS_URL = 'http://docs.moodle.org/dev/Acceptance_testing';
45 /**
46 * Ensures the behat dir exists in moodledata
47 * @param int $runprocess run process for which behat dir is returned.
48 * @return string Full path
50 public static function get_behat_dir($runprocess = 0) {
51 global $CFG;
53 // If not set then return empty string.
54 if (!isset($CFG->behat_dataroot)) {
55 return "";
58 if (empty($runprocess)) {
59 $behatdir = $CFG->behat_dataroot . '/behat';
60 } else if (isset($CFG->behat_parallel_run[$runprocess - 1]['behat_dataroot'])) {
61 $behatdir = $CFG->behat_parallel_run[$runprocess - 1]['behat_dataroot'] . '/behat';;
62 } else {
63 $behatdir = $CFG->behat_dataroot . $runprocess . '/behat';
66 if (!is_dir($behatdir)) {
67 if (!mkdir($behatdir, $CFG->directorypermissions, true)) {
68 behat_error(BEHAT_EXITCODE_PERMISSIONS, 'Directory ' . $behatdir . ' can not be created');
72 if (!is_writable($behatdir)) {
73 behat_error(BEHAT_EXITCODE_PERMISSIONS, 'Directory ' . $behatdir . ' is not writable');
76 return $behatdir;
79 /**
80 * Returns the executable path
82 * Allows returning a customized command for cygwin when the
83 * command is just displayed, when using exec(), system() and
84 * friends we stay with DIRECTORY_SEPARATOR as they use the
85 * normal cmd.exe (in Windows).
87 * @param bool $custombyterm If the provided command should depend on the terminal where it runs
88 * @param bool $parallelrun If parallel run is installed.
89 * @param bool $absolutepath return command with absolute path.
90 * @return string
92 public final static function get_behat_command($custombyterm = false, $parallerun = false, $absolutepath = false) {
94 $separator = DIRECTORY_SEPARATOR;
95 $exec = 'behat';
97 // Cygwin uses linux-style directory separators.
98 if ($custombyterm && testing_is_cygwin()) {
99 $separator = '/';
101 // MinGW can not execute .bat scripts.
102 if (!testing_is_mingw()) {
103 $exec = 'behat.bat';
107 // If relative path then prefix relative path.
108 if ($absolutepath) {
109 $pathprefix = testing_cli_argument_path('/');
110 if (!empty($pathprefix)) {
111 $pathprefix .= $separator;
113 } else {
114 $pathprefix = '';
117 if (!$parallerun) {
118 $command = $pathprefix . 'vendor' . $separator . 'bin' . $separator . $exec;
119 } else {
120 $command = 'php ' . $pathprefix . 'admin' . $separator . 'tool' . $separator . 'behat' . $separator . 'cli'
121 . $separator . 'run.php';
123 return $command;
127 * Runs behat command with provided options
129 * Execution continues when the process finishes
131 * @param string $options Defaults to '' so tests would be executed
132 * @return array CLI command outputs [0] => string, [1] => integer
134 public final static function run($options = '') {
135 global $CFG;
137 $currentcwd = getcwd();
138 chdir($CFG->dirroot);
139 exec(self::get_behat_command() . ' ' . $options, $output, $code);
140 chdir($currentcwd);
142 return array($output, $code);
146 * Checks if behat is set up and working
148 * Notifies failures both from CLI and web interface.
150 * It checks behat dependencies have been installed and runs
151 * the behat help command to ensure it works as expected
153 * @return int Error code or 0 if all ok
155 public static function behat_setup_problem() {
156 global $CFG;
158 // Moodle setting.
159 if (!self::are_behat_dependencies_installed()) {
161 // Returning composer error code to avoid conflicts with behat and moodle error codes.
162 self::output_msg(get_string('errorcomposer', 'tool_behat'));
163 return TESTING_EXITCODE_COMPOSER;
166 // Behat test command.
167 list($output, $code) = self::run(' --help');
169 if ($code != 0) {
171 // Returning composer error code to avoid conflicts with behat and moodle error codes.
172 self::output_msg(get_string('errorbehatcommand', 'tool_behat', self::get_behat_command()));
173 return TESTING_EXITCODE_COMPOSER;
176 // No empty values.
177 if (empty($CFG->behat_dataroot) || empty($CFG->behat_prefix) || empty($CFG->behat_wwwroot)) {
178 self::output_msg(get_string('errorsetconfig', 'tool_behat'));
179 return BEHAT_EXITCODE_CONFIG;
183 // Not repeated values.
184 // We only need to check this when the behat site is not running as
185 // at this point, when it is running, all $CFG->behat_* vars have
186 // already been copied to $CFG->dataroot, $CFG->prefix and $CFG->wwwroot.
187 if (!defined('BEHAT_SITE_RUNNING') &&
188 ($CFG->behat_prefix == $CFG->prefix ||
189 $CFG->behat_dataroot == $CFG->dataroot ||
190 $CFG->behat_wwwroot == $CFG->wwwroot ||
191 (!empty($CFG->phpunit_prefix) && $CFG->phpunit_prefix == $CFG->behat_prefix) ||
192 (!empty($CFG->phpunit_dataroot) && $CFG->phpunit_dataroot == $CFG->behat_dataroot)
193 )) {
194 self::output_msg(get_string('erroruniqueconfig', 'tool_behat'));
195 return BEHAT_EXITCODE_CONFIG;
198 // Checking behat dataroot existence otherwise echo about admin/tool/behat/cli/init.php.
199 if (!empty($CFG->behat_dataroot)) {
200 $CFG->behat_dataroot = realpath($CFG->behat_dataroot);
202 if (empty($CFG->behat_dataroot) || !is_dir($CFG->behat_dataroot) || !is_writable($CFG->behat_dataroot)) {
203 self::output_msg(get_string('errordataroot', 'tool_behat'));
204 return BEHAT_EXITCODE_CONFIG;
207 return 0;
211 * Has the site installed composer.
212 * @return bool
214 public static function are_behat_dependencies_installed() {
215 if (!is_dir(__DIR__ . '/../../../vendor/behat')) {
216 return false;
218 return true;
222 * Returns the path to the parallel run file which specifies if parallel test environment is enabled
223 * and how many parallel runs to execute.
225 * @param int $runprocess run process for which behat dir is returned.
226 * @return string
228 public final static function get_last_failed_test_status_file_path($runprocess = 0) {
229 return self::get_behat_dir($runprocess) . '/lastfailed.txt';
233 * Outputs a message.
235 * Used in CLI + web UI methods. Stops the
236 * execution in web.
238 * @param string $msg
239 * @return void
241 protected static function output_msg($msg) {
242 global $CFG, $PAGE;
244 // If we are using the web interface we want pretty messages.
245 if (!CLI_SCRIPT) {
247 $renderer = $PAGE->get_renderer('tool_behat');
248 echo $renderer->render_error($msg);
250 // Stopping execution.
251 exit(1);
253 } else {
255 // We continue execution after this.
256 $clibehaterrorstr = "Ensure you set \$CFG->behat_* vars in config.php " .
257 "and you ran admin/tool/behat/cli/init.php.\n" .
258 "More info in " . self::DOCS_URL . "#Installation\n\n";
260 echo 'Error: ' . $msg . "\n\n" . $clibehaterrorstr;