Commit 647b675e authored by Jake Dallimore's avatar Jake Dallimore
Browse files

Merge branch 'MDL-69232-37' of git://github.com/andrewnicols/moodle into MOODLE_37_STABLE

parents 87f67ecc eec91baf
......@@ -68,6 +68,9 @@ class behat_hooks extends behat_base {
*/
protected static $initprocessesfinished = false;
/** @var bool Whether the first javascript scenario has been seen yet */
protected static $firstjavascriptscenarioseen = false;
/**
* @var bool Scenario running
*/
......@@ -110,39 +113,22 @@ class behat_hooks extends behat_base {
protected static $scenariotags;
/**
* Hook to capture BeforeSuite event so as to give access to moodle codebase.
* This will try and catch any exception and exists if anything fails.
* Gives access to moodle codebase, ensures all is ready and sets up the test lock.
*
* Includes config.php to use moodle codebase with $CFG->behat_* instead of $CFG->prefix and $CFG->dataroot, called
* once per suite.
*
* @param BeforeSuiteScope $scope scope passed by event fired before suite.
* @BeforeSuite
* @param BeforeSuiteScope $scope scope passed by event fired before suite.
*/
public static function before_suite_hook(BeforeSuiteScope $scope) {
global $CFG;
// If behat has been initialised then no need to do this again.
if (self::$initprocessesfinished) {
if (!self::is_first_scenario()) {
return;
}
try {
self::before_suite($scope);
} catch (behat_stop_exception $e) {
echo $e->getMessage() . PHP_EOL;
exit(1);
}
}
/**
* Gives access to moodle codebase, ensures all is ready and sets up the test lock.
*
* Includes config.php to use moodle codebase with $CFG->behat_*
* instead of $CFG->prefix and $CFG->dataroot, called once per suite.
*
* @param BeforeSuiteScope $scope scope passed by event fired before suite.
* @static
* @throws behat_stop_exception
*/
public static function before_suite(BeforeSuiteScope $scope) {
global $CFG;
// Defined only when the behat CLI command is running, the moodle init setup process will
// read this value and switch to $CFG->behat_dataroot and $CFG->behat_prefix instead of
// the normal site.
......@@ -170,8 +156,7 @@ class behat_hooks extends behat_base {
// before each scenario (accidental user deletes) in the BeforeScenario hook.
if (!behat_util::is_test_mode_enabled()) {
throw new behat_stop_exception('Behat only can run if test mode is enabled. More info in ' .
behat_command::DOCS_URL);
self::log_and_stop('Behat only can run if test mode is enabled. More info in ' . behat_command::DOCS_URL);
}
// Reset all data, before checking for check_server_status.
......@@ -179,20 +164,28 @@ class behat_hooks extends behat_base {
behat_util::clean_tables_updated_by_scenario_list();
behat_util::reset_all_data();
// Check if server is running and using same version for cli and apache.
// Check if the web server is running and using same version for cli and apache.
behat_util::check_server_status();
// Prevents using outdated data, upgrade script would start and tests would fail.
if (!behat_util::is_test_data_updated()) {
$commandpath = 'php admin/tool/behat/cli/init.php';
throw new behat_stop_exception("Your behat test site is outdated, please run\n\n " .
$commandpath . "\n\nfrom your moodle dirroot to drop and install the behat test site again.");
$message = <<<EOF
Your behat test site is outdated, please run the following command from your Moodle dirroot to drop, and reinstall the Behat test site.
{$comandpath}
EOF;
self::log_and_stop($message);
}
// Avoid parallel tests execution, it continues when the previous lock is released.
test_lock::acquire('behat');
if (!empty($CFG->behat_faildump_path) && !is_writable($CFG->behat_faildump_path)) {
throw new behat_stop_exception('You set $CFG->behat_faildump_path to a non-writable directory');
self::log_and_stop(
"The \$CFG->behat_faildump_path value is set to a non-writable directory ({$CFG->behat_faildump_path})."
);
}
// Handle interrupts on PHP7.
......@@ -204,6 +197,25 @@ class behat_hooks extends behat_base {
}
}
/**
* Run final tests before running the suite.
*
* @BeforeSuite
* @param BeforeSuiteScope $scope scope passed by event fired before suite.
*/
public static function before_suite_final_checks(BeforeSuiteScope $scope) {
$happy = defined('BEHAT_TEST');
$happy = $happy && defined('BEHAT_SITE_RUNNING');
$happy = $happy && php_sapi_name() == 'cli';
$happy = $happy && behat_util::is_test_mode_enabled();
$happy = $happy && behat_util::is_test_site();
if (!$happy) {
error_log('Behat only can modify the test database and the test dataroot!');
exit(1);
}
}
/**
* Gives access to moodle codebase, to keep track of feature start time.
*
......@@ -260,53 +272,108 @@ class behat_hooks extends behat_base {
}
/**
* Hook to capture before scenario event to get scope.
* Helper function to restart the Mink session.
*/
protected function restart_session(): void {
$session = $this->getSession();
if ($session->isStarted()) {
$session->restart();
} else {
$session->start();
}
if ($this->running_javascript() && $this->getSession()->getDriver()->getWebDriverSessionId() === 'session') {
throw new DriverException('Unable to create a valid session');
}
}
/**
* Restart the session before each non-javascript scenario.
*
* @BeforeScenario @~javascript
* @param BeforeScenarioScope $scope scope passed by event fired before scenario.
* @BeforeScenario
*/
public function before_scenario_hook(BeforeScenarioScope $scope) {
try {
$this->before_scenario($scope);
} catch (behat_stop_exception $e) {
echo $e->getMessage() . PHP_EOL;
exit(1);
public function before_goutte_scenarios(BeforeScenarioScope $scope) {
if ($this->running_javascript()) {
// A bug in the BeforeScenario filtering prevents the @~javascript filter on this hook from working
// properly.
// See https://github.com/Behat/Behat/issues/1235 for further information.
return;
}
$this->restart_session();
}
/**
* Resets the test environment.
* Start the session before the first javascript scenario.
*
* This is treated slightly differently to try to capture when Selenium is not running at all.
*
* @BeforeScenario @javascript
* @param BeforeScenarioScope $scope scope passed by event fired before scenario.
* @throws behat_stop_exception If here we are not using the test database it should be because of a coding error
*/
public function before_scenario(BeforeScenarioScope $scope) {
global $DB, $CFG;
// As many checks as we can.
if (!defined('BEHAT_TEST') ||
!defined('BEHAT_SITE_RUNNING') ||
php_sapi_name() != 'cli' ||
!behat_util::is_test_mode_enabled() ||
!behat_util::is_test_site()) {
throw new behat_stop_exception('Behat only can modify the test database and the test dataroot!');
public function before_first_scenario_start_session(BeforeScenarioScope $scope) {
if (!self::is_first_javascript_scenario()) {
// The first Scenario has started.
// The `before_subsequent_scenario_start_session` function will restart the session instead.
return;
}
self::$firstjavascriptscenarioseen = true;
$docsurl = behat_command::DOCS_URL;
$driverexceptionmsg = <<<EOF
The Selenium or WebDriver server is not running. You must start it to run tests that involve Javascript.
See {$docsurl} for more information.
The following debugging information is available:
EOF;
$moreinfo = 'More info in ' . behat_command::DOCS_URL;
$driverexceptionmsg = 'Selenium server is not running, you need to start it to run tests that involve Javascript. ' . $moreinfo;
try {
$session = $this->getSession();
} catch (CurlExec $e) {
// Exception thrown by WebDriver, so only @javascript tests will be caugth; in
// behat_util::check_server_status() we already checked that the server is running.
throw new behat_stop_exception($driverexceptionmsg);
} catch (DriverException $e) {
throw new behat_stop_exception($driverexceptionmsg);
$this->restart_session();
} catch (CurlExec | DriverException $e) {
// The CurlExec Exception is thrown by WebDriver.
self::log_and_stop(
$driverexceptionmsg . '. ' .
$e->getMessage() . "\n\n" .
format_backtrace($e->getTrace(), true)
);
} catch (UnknownError $e) {
// Generic 'I have no idea' Selenium error. Custom exception to provide more feedback about possible solutions.
throw new behat_stop_exception($e->getMessage());
self::log_and_stop(
$e->getMessage() . "\n\n" .
format_backtrace($e->getTrace(), true)
);
}
}
/**
* Start the session before each javascript scenario.
*
* Note: Before the first scenario the @see before_first_scenario_start_session() function is used instead.
*
* @BeforeScenario @javascript
* @param BeforeScenarioScope $scope scope passed by event fired before scenario.
*/
public function before_subsequent_scenario_start_session(BeforeScenarioScope $scope) {
if (self::is_first_javascript_scenario()) {
// The initial init has not yet finished.
// The `before_first_scenario_start_session` function will have started the session instead.
return;
}
$this->restart_session();
}
/**
* Resets the test environment.
*
* @BeforeScenario
* @param BeforeScenarioScope $scope scope passed by event fired before scenario.
*/
public function before_scenario_hook(BeforeScenarioScope $scope) {
global $DB;
$suitename = $scope->getSuite()->getName();
// Register behat selectors for theme, if suite is changed. We do it for every suite change.
......@@ -340,9 +407,6 @@ class behat_hooks extends behat_base {
}
// Reset mink session between the scenarios.
$session->reset();
// Reset $SESSION.
\core\session\manager::init_empty_session();
......@@ -695,6 +759,15 @@ class behat_hooks extends behat_base {
return !(self::$initprocessesfinished);
}
/**
* Returns whether the first scenario of the suite is running
*
* @return bool
*/
protected static function is_first_javascript_scenario(): bool {
return !self::$firstjavascriptscenarioseen;
}
/**
* Register a set of component selectors.
*
......@@ -734,20 +807,19 @@ class behat_hooks extends behat_base {
* @param BeforeStepScope $scope
* @BeforeStep
*/
public function first_step_setup_complete(BeforeStepScope $scope) {
public function first_step_setup_complete(BeforeStepScope $scope): void {
self::$initprocessesfinished = true;
}
}
/**
* Log a notification, and then exit.
*
* @param string $message The content to dispaly
*/
protected static function log_and_stop(string $message): void {
error_log($message);
exit(1);
}
/**
* Behat stop exception
*
* This exception is thrown from before suite or scenario if any setup problem found.
*
* @package core_test
* @copyright 2016 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_stop_exception extends \Exception {
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment