Merge branch 'MDL-67410-37' of https://github.com/felicemcc/moodle into MOODLE_37_STABLE
[moodle.git] / lib / behat / classes / util.php
blob5338be1c7f6d53e0027937c94757550e3cdcecaf
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 * Utils for behat-related stuff
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');
29 require_once(__DIR__ . '/../../testing/classes/util.php');
30 require_once(__DIR__ . '/behat_command.php');
31 require_once(__DIR__ . '/behat_config_manager.php');
33 require_once(__DIR__ . '/../../filelib.php');
34 require_once(__DIR__ . '/../../clilib.php');
36 use Behat\Mink\Session;
38 /**
39 * Init/reset utilities for Behat database and dataroot
41 * @package core
42 * @category test
43 * @copyright 2013 David MonllaĆ³
44 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
46 class behat_util extends testing_util {
48 /**
49 * The behat test site fullname and shortname.
51 const BEHATSITENAME = "Acceptance test site";
53 /**
54 * @var array Files to skip when resetting dataroot folder
56 protected static $datarootskiponreset = array('.', '..', 'behat', 'behattestdir.txt');
58 /**
59 * @var array Files to skip when dropping dataroot folder
61 protected static $datarootskipondrop = array('.', '..', 'lock');
63 /**
64 * Installs a site using $CFG->dataroot and $CFG->prefix
65 * @throws coding_exception
66 * @return void
68 public static function install_site() {
69 global $DB, $CFG;
70 require_once($CFG->dirroot.'/user/lib.php');
71 if (!defined('BEHAT_UTIL')) {
72 throw new coding_exception('This method can be only used by Behat CLI tool');
75 $tables = $DB->get_tables(false);
76 if (!empty($tables)) {
77 behat_error(BEHAT_EXITCODE_INSTALLED);
80 // New dataroot.
81 self::reset_dataroot();
83 $options = array();
84 $options['adminuser'] = 'admin';
85 $options['adminpass'] = 'admin';
86 $options['fullname'] = self::BEHATSITENAME;
87 $options['shortname'] = self::BEHATSITENAME;
89 install_cli_database($options, false);
91 // We need to keep the installed dataroot filedir files.
92 // So each time we reset the dataroot before running a test, the default files are still installed.
93 self::save_original_data_files();
95 $frontpagesummary = new admin_setting_special_frontpagedesc();
96 $frontpagesummary->write_setting(self::BEHATSITENAME);
98 // Update admin user info.
99 $user = $DB->get_record('user', array('username' => 'admin'));
100 $user->email = 'moodle@example.com';
101 $user->firstname = 'Admin';
102 $user->lastname = 'User';
103 $user->city = 'Perth';
104 $user->country = 'AU';
105 user_update_user($user, false);
107 // Disable email message processor.
108 $DB->set_field('message_processors', 'enabled', '0', array('name' => 'email'));
110 // Sets maximum debug level.
111 set_config('debug', DEBUG_DEVELOPER);
112 set_config('debugdisplay', 1);
114 // Disable some settings that are not wanted on test sites.
115 set_config('noemailever', 1);
117 // Enable web cron.
118 set_config('cronclionly', 0);
120 // Set editor autosave to high value, so as to avoid unwanted ajax.
121 set_config('autosavefrequency', '604800', 'editor_atto');
123 // Set noreplyaddress to an example domain, as it should be valid email address and test site can be a localhost.
124 set_config('noreplyaddress', 'noreply@example.com');
126 // Keeps the current version of database and dataroot.
127 self::store_versions_hash();
129 // Stores the database contents for fast reset.
130 self::store_database_state();
134 * Drops dataroot and remove test database tables
135 * @throws coding_exception
136 * @return void
138 public static function drop_site() {
140 if (!defined('BEHAT_UTIL')) {
141 throw new coding_exception('This method can be only used by Behat CLI tool');
144 self::reset_dataroot();
145 self::drop_database(true);
146 self::drop_dataroot();
150 * Delete files and directories under dataroot.
152 public static function drop_dataroot() {
153 global $CFG;
155 // As behat directory is now created under default $CFG->behat_dataroot_parent, so remove the whole dir.
156 if ($CFG->behat_dataroot !== $CFG->behat_dataroot_parent) {
157 remove_dir($CFG->behat_dataroot, false);
158 } else {
159 // It should never come here.
160 throw new moodle_exception("Behat dataroot should not be same as parent behat data root.");
165 * Checks if $CFG->behat_wwwroot is available and using same versions for cli and web.
167 * @return void
169 public static function check_server_status() {
170 global $CFG;
172 $url = $CFG->behat_wwwroot . '/admin/tool/behat/tests/behat/fixtures/environment.php';
174 // Get web versions used by behat site.
175 $ch = curl_init($url);
176 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
177 $result = curl_exec($ch);
178 $statuscode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
179 curl_close($ch);
181 if ($statuscode !== 200 || empty($result) || (!$result = json_decode($result, true))) {
183 behat_error (BEHAT_EXITCODE_REQUIREMENT, $CFG->behat_wwwroot . ' is not available, ensure you specified ' .
184 'correct url and that the server is set up and started.' . PHP_EOL . ' More info in ' .
185 behat_command::DOCS_URL . PHP_EOL);
188 // Check if cli version is same as web version.
189 $clienv = self::get_environment();
190 if ($result != $clienv) {
191 $output = 'Differences detected between cli and webserver...'.PHP_EOL;
192 foreach ($result as $key => $version) {
193 if ($clienv[$key] != $version) {
194 $output .= ' ' . $key . ': ' . PHP_EOL;
195 $output .= ' - web server: ' . $version . PHP_EOL;
196 $output .= ' - cli: ' . $clienv[$key] . PHP_EOL;
199 echo $output;
200 ob_flush();
205 * Checks whether the test database and dataroot is ready
206 * Stops execution if something went wrong
207 * @throws coding_exception
208 * @return void
210 protected static function test_environment_problem() {
211 global $CFG, $DB;
213 if (!defined('BEHAT_UTIL')) {
214 throw new coding_exception('This method can be only used by Behat CLI tool');
217 if (!self::is_test_site()) {
218 behat_error(1, 'This is not a behat test site!');
221 $tables = $DB->get_tables(false);
222 if (empty($tables)) {
223 behat_error(BEHAT_EXITCODE_INSTALL, '');
226 if (!self::is_test_data_updated()) {
227 behat_error(BEHAT_EXITCODE_REINSTALL, 'The test environment was initialised for a different version');
232 * Enables test mode
234 * It uses CFG->behat_dataroot
236 * Starts the test mode checking the composer installation and
237 * the test environment and updating the available
238 * features and steps definitions.
240 * Stores a file in dataroot/behat to allow Moodle to switch
241 * to the test environment when using cli-server.
242 * @param bool $themesuitewithallfeatures List themes to include core features.
243 * @param string $tags comma separated tag, which will be given preference while distributing features in parallel run.
244 * @param int $parallelruns number of parallel runs.
245 * @param int $run current run.
246 * @throws coding_exception
247 * @return void
249 public static function start_test_mode($themesuitewithallfeatures = false, $tags = '', $parallelruns = 0, $run = 0) {
250 global $CFG;
252 if (!defined('BEHAT_UTIL')) {
253 throw new coding_exception('This method can be only used by Behat CLI tool');
256 // Checks the behat set up and the PHP version.
257 if ($errorcode = behat_command::behat_setup_problem()) {
258 exit($errorcode);
261 // Check that test environment is correctly set up.
262 self::test_environment_problem();
264 // Updates all the Moodle features and steps definitions.
265 behat_config_manager::update_config_file('', true, $tags, $themesuitewithallfeatures, $parallelruns, $run);
267 if (self::is_test_mode_enabled()) {
268 return;
271 $contents = '$CFG->behat_wwwroot, $CFG->behat_prefix and $CFG->behat_dataroot' .
272 ' are currently used as $CFG->wwwroot, $CFG->prefix and $CFG->dataroot';
273 $filepath = self::get_test_file_path();
274 if (!file_put_contents($filepath, $contents)) {
275 behat_error(BEHAT_EXITCODE_PERMISSIONS, 'File ' . $filepath . ' can not be created');
280 * Returns the status of the behat test environment
282 * @return int Error code
284 public static function get_behat_status() {
286 if (!defined('BEHAT_UTIL')) {
287 throw new coding_exception('This method can be only used by Behat CLI tool');
290 // Checks the behat set up and the PHP version, returning an error code if something went wrong.
291 if ($errorcode = behat_command::behat_setup_problem()) {
292 return $errorcode;
295 // Check that test environment is correctly set up, stops execution.
296 self::test_environment_problem();
300 * Disables test mode
301 * @throws coding_exception
302 * @return void
304 public static function stop_test_mode() {
306 if (!defined('BEHAT_UTIL')) {
307 throw new coding_exception('This method can be only used by Behat CLI tool');
310 $testenvfile = self::get_test_file_path();
311 behat_config_manager::set_behat_run_config_value('behatsiteenabled', 0);
313 if (!self::is_test_mode_enabled()) {
314 echo "Test environment was already disabled\n";
315 } else {
316 if (!unlink($testenvfile)) {
317 behat_error(BEHAT_EXITCODE_PERMISSIONS, 'Can not delete test environment file');
323 * Checks whether test environment is enabled or disabled
325 * To check is the current script is running in the test
326 * environment
328 * @return bool
330 public static function is_test_mode_enabled() {
332 $testenvfile = self::get_test_file_path();
333 if (file_exists($testenvfile)) {
334 return true;
337 return false;
341 * Returns the path to the file which specifies if test environment is enabled
342 * @return string
344 public final static function get_test_file_path() {
345 return behat_command::get_parent_behat_dir() . '/test_environment_enabled.txt';
349 * Removes config settings that were added to the main $CFG config within the Behat CLI
350 * run.
352 * Database storage is already handled by reset_database and existing config values will
353 * be reset automatically by initialise_cfg(), so we only need to remove added ones.
355 public static function remove_added_config() {
356 global $CFG;
357 if (!empty($CFG->behat_cli_added_config)) {
358 foreach ($CFG->behat_cli_added_config as $key => $value) {
359 unset($CFG->{$key});
361 unset($CFG->behat_cli_added_config);
366 * Reset contents of all database tables to initial values, reset caches, etc.
368 public static function reset_all_data() {
369 // Reset database.
370 self::reset_database();
372 // Purge dataroot directory.
373 self::reset_dataroot();
375 // Reset all static caches.
376 accesslib_clear_all_caches(true);
377 accesslib_reset_role_cache();
378 // Reset the nasty strings list used during the last test.
379 nasty_strings::reset_used_strings();
381 filter_manager::reset_caches();
383 // Reset course and module caches.
384 if (class_exists('format_base')) {
385 // If file containing class is not loaded, there is no cache there anyway.
386 format_base::reset_course_cache(0);
388 get_fast_modinfo(0, 0, true);
390 // Inform data generator.
391 self::get_data_generator()->reset();
393 // Initialise $CFG with default values. This is needed for behat cli process, so we don't have modified
394 // $CFG values from the old run. @see set_config.
395 self::remove_added_config();
396 initialise_cfg();
400 * Pause execution immediately.
402 * @param Session $session
403 * @param string $message The message to show when pausing.
404 * This will be passed through cli_ansi_format so appropriate ANSI formatting and features are available.
406 public static function pause(Session $session, string $message): void {
407 $posixexists = function_exists('posix_isatty');
409 // Make sure this step is only used with interactive terminal (if detected).
410 if ($posixexists && !@posix_isatty(STDOUT)) {
411 throw new ExpectationException('Break point should only be used with interactive terminal.', $session);
414 // Save the cursor position, ring the bell, and add a new line.
415 fwrite(STDOUT, cli_ansi_format("<cursor:save><bell><newline>"));
417 // Output the formatted message and reset colour back to normal.
418 $formattedmessage = cli_ansi_format("{$message}<colour:normal>");
419 fwrite(STDOUT, $formattedmessage);
421 // Wait for input.
422 fread(STDIN, 1024);
424 // Move the cursor back up to the previous position, then restore the original position stored earlier, and move
425 // it back down again.
426 fwrite(STDOUT, cli_ansi_format("<cursor:up><cursor:up><cursor:restore><cursor:down><cursor:down>"));
428 // Add any extra lines back if the provided message was spread over multiple lines.
429 $linecount = count(explode("\n", $formattedmessage));
430 fwrite(STDOUT, str_repeat(cli_ansi_format("<cursor:down>"), $linecount - 1));