diff --git a/.gitignore b/.gitignore index 2a50123abb3f6..e1cc2333b6ac2 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,4 @@ phpunit.xml composer.phar composer.lock /vendor/ -behat.yml +/behat.yml diff --git a/admin/tool/behat/cli/util.php b/admin/tool/behat/cli/util.php index 5e580aac355ea..4fd147f717e63 100644 --- a/admin/tool/behat/cli/util.php +++ b/admin/tool/behat/cli/util.php @@ -15,23 +15,32 @@ // along with Moodle. If not, see . /** - * CLI tool + * CLI tool with utilities to manage Behat integration in Moodle + * + * All CLI utilities uses $CFG->behat_dataroot and $CFG->prefix_dataroot as + * $CFG->dataroot and $CFG->prefix * * @package tool_behat * @copyright 2012 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define('CLI_SCRIPT', true); -require(__DIR__ . '/../../../../config.php'); -require_once($CFG->libdir . '/clilib.php'); -require_once($CFG->libdir . '/behat/behat_util.php'); +if (isset($_SERVER['REMOTE_ADDR'])) { + die(); // No access from web!. +} + +// Basic functions. +require_once(__DIR__ . '/../../../../lib/clilib.php'); +require_once(__DIR__ . '/../../../../lib/behat/lib.php'); + // CLI options. list($options, $unrecognized) = cli_get_params( array( 'help' => false, + 'install' => false, + 'drop' => false, 'enable' => false, 'disable' => false, ), @@ -40,12 +49,16 @@ ) ); + +// Checking util.php CLI script usage. $help = " -Behat tool +Behat utilities to manage the test environment Options: ---enable Enables test environment and updates tests list ---disable Disables test environment +--install Installs the test environment for acceptance tests +--drop Drops the database tables and the dataroot contents +--enable Enables test environment and updates tests list +--disable Disables test environment -h, --help Print out this help @@ -60,22 +73,110 @@ exit(0); } + +// Checking $CFG->behat_* vars and values. +define('BEHAT_UTIL', true); +define('CLI_SCRIPT', true); +define('ABORT_AFTER_CONFIG', true); +define('NO_OUTPUT_BUFFERING', true); + +error_reporting(E_ALL | E_STRICT); +ini_set('display_errors', '1'); +ini_set('log_errors', '1'); + +require_once(__DIR__ . '/../../../../config.php'); + +// CFG->behat_prefix must be set and with value different than CFG->prefix and phpunit_prefix. +if (!isset($CFG->behat_prefix) || + (isset($CFG->behat_prefix) && + ($CFG->behat_prefix == $CFG->prefix || + $CFG->behat_prefix == $CFG->phpunit_prefix))) { + behat_error(BEHAT_EXITCODE_CONFIG, + 'Define $CFG->behat_prefix in config.php with a value different than $CFG->prefix and $CFG->phpunit_prefix'); +} + +// CFG->behat_dataroot must be set and with value different than CFG->dataroot and phpunit_dataroot. +if (!isset($CFG->behat_dataroot) || + (isset($CFG->behat_dataroot) && + ($CFG->behat_dataroot == $CFG->dataroot || + $CFG->behat_dataroot == $CFG->phpunit_dataroot))) { + behat_error(BEHAT_EXITCODE_CONFIG, + 'Define $CFG->behat_dataroot in config.php with a value different than $CFG->dataroot and $CFG->phpunit_dataroot'); +} + +// Create behat_dataroot if it doesn't exists. +if (!file_exists($CFG->behat_dataroot)) { + if (!mkdir($CFG->behat_dataroot, $CFG->directorypermissions)) { + behat_error(BEHAT_EXITCODE_PERMISSIONS, '$CFG->behat_dataroot directory can not be created'); + } +} +if (!is_dir($CFG->behat_dataroot) || !is_writable($CFG->behat_dataroot)) { + behat_error(BEHAT_EXITCODE_PERMISSIONS, '$CFG->behat_dataroot directory has no permissions or is not a directory'); +} + +// Check that the directory does not contains other things. +if (!file_exists("$CFG->behat_dataroot/behattestdir.txt")) { + if ($dh = opendir($CFG->behat_dataroot)) { + while (($file = readdir($dh)) !== false) { + if ($file === 'behat' or $file === '.' or $file === '..' or $file === '.DS_Store') { + continue; + } + behat_error(BEHAT_EXITCODE_CONFIG, '$CFG->behat_dataroot directory is not empty, ensure this is the directory where you want to install behat test dataroot'); + } + closedir($dh); + unset($dh); + unset($file); + } + + // Now we create dataroot directory structure for behat tests. + testing_initdataroot($CFG->behat_dataroot, 'behat'); +} + +// Overrides vars with behat-test ones. +$vars = array('wwwroot', 'prefix', 'dataroot'); +foreach ($vars as $var) { + $CFG->{$var} = $CFG->{'behat_' . $var}; +} + +$CFG->noemailever = true; +$CFG->passwordsaltmain = 'moodle'; + +// Continues setup. +define('ABORT_AFTER_CONFIG_CANCEL', true); +require("$CFG->dirroot/lib/setup.php"); + +require_once($CFG->libdir.'/adminlib.php'); +require_once($CFG->libdir.'/upgradelib.php'); +require_once($CFG->libdir.'/clilib.php'); +require_once($CFG->libdir.'/pluginlib.php'); +require_once($CFG->libdir.'/installlib.php'); + if ($unrecognized) { $unrecognized = implode("\n ", $unrecognized); cli_error(get_string('cliunknowoption', 'admin', $unrecognized)); } -// Run command. -if ($options['enable']) { - $action = 'enable'; +// Behat utilities. +require_once($CFG->libdir . '/behat/classes/util.php'); +require_once($CFG->libdir . '/behat/classes/behat_command.php'); + +// Run command (only one per time). +if ($options['install']) { + behat_util::install_site(); + mtrace("Acceptance tests site installed"); +} else if ($options['drop']) { + behat_util::drop_site(); + mtrace("Acceptance tests site dropped"); +} else if ($options['enable']) { + behat_util::start_test_mode(); + $runtestscommand = behat_command::get_behat_command() . ' --config ' + . $CFG->behat_dataroot . DIRECTORY_SEPARATOR . 'behat' . DIRECTORY_SEPARATOR . 'behat.yml'; + mtrace("Acceptance tests environment enabled, to run the tests use:\n " . $runtestscommand); } else if ($options['disable']) { - $action = 'disable'; + behat_util::stop_test_mode(); + mtrace("Acceptance tests environment disabled"); } else { echo $help; - exit(0); } -behat_util::switchenvironment($action); - -mtrace(get_string('testenvironment' . $action, 'tool_behat')); - +exit(0); diff --git a/admin/tool/behat/lang/en/tool_behat.php b/admin/tool/behat/lang/en/tool_behat.php index abe5b87242be6..d890976b99db5 100644 --- a/admin/tool/behat/lang/en/tool_behat.php +++ b/admin/tool/behat/lang/en/tool_behat.php @@ -30,15 +30,11 @@ $string['newtestsinfo'] = 'Read {$a} for info about how to write new tests'; $string['nostepsdefinitions'] = 'There aren\'t steps definitions matching this filters'; $string['pluginname'] = 'Acceptance testing'; -$string['phpunitenvproblem'] = 'PHPUnit environment problem'; $string['stepsdefinitionscomponent'] = 'Area'; $string['stepsdefinitionscontains'] = 'Contains'; $string['stepsdefinitionsfilters'] = 'Steps definitions'; $string['stepsdefinitionstype'] = 'Type'; -$string['testenvironmentenable'] = 'Test environment enabled'; -$string['testenvironmentdisable'] = 'Test environment disabled'; $string['theninfo'] = 'Then. Checkings to ensure the outcomes are the expected ones'; $string['viewsteps'] = 'Filter'; $string['wheninfo'] = 'When. Actions that provokes an event'; -$string['wrongphpversion'] = 'PHP 5.4 or higher is required to run acceptance tests. See config-dist.php for alternatives.'; $string['wrongbehatsetup'] = 'Something is wrong with the setup, ensure you ran the composer installer and vendor/bin/behat file has execution permissions'; diff --git a/admin/tool/behat/locallib.php b/admin/tool/behat/locallib.php index fd1ba3016a5d4..33a5fd8c50314 100644 --- a/admin/tool/behat/locallib.php +++ b/admin/tool/behat/locallib.php @@ -22,6 +22,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +defined('MOODLE_INTERNAL') || die(); + global $CFG; require_once($CFG->libdir . '/behat/classes/behat_command.php'); require_once($CFG->libdir . '/behat/classes/behat_config_manager.php'); @@ -45,8 +47,9 @@ class tool_behat { * @return string */ public static function stepsdefinitions($type, $component, $filter) { - global $CFG; + // We don't require the test environment to be enabled to list the steps definitions + // so test writers can more easily set up the environment. behat_command::check_behat_setup(); // The loaded steps depends on the component specified. @@ -63,16 +66,15 @@ public static function stepsdefinitions($type, $component, $filter) { $filteroption = ' -di'; } - $currentcwd = getcwd(); - chdir($CFG->dirroot); - exec(behat_command::get_behat_command() . ' --config="'.behat_config_manager::get_steps_list_config_filepath(). '" '.$filteroption, $steps, $code); - chdir($currentcwd); + // Get steps definitions from Behat. + $options = ' --config="'.behat_config_manager::get_steps_list_config_filepath(). '" '.$filteroption; + list($steps, $code) = behat_command::run($options); if ($steps) { $stepshtml = implode('', $steps); } - if (!isset($stepshtml) || $stepshtml == '') { + if (empty($stepshtml)) { $stepshtml = get_string('nostepsdefinitions', 'tool_behat'); } diff --git a/admin/tool/behat/renderer.php b/admin/tool/behat/renderer.php index e927ca107f598..1a70fcef31d1c 100644 --- a/admin/tool/behat/renderer.php +++ b/admin/tool/behat/renderer.php @@ -22,7 +22,10 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined('MOODLE_INTERNAL') || die; +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir . '/behat/classes/behat_command.php'); /** * Renderer for behat tool web features @@ -33,11 +36,6 @@ */ class tool_behat_renderer extends plugin_renderer_base { - /** - * @var string Docs url - */ - protected $docsurl = 'http://docs.moodle.org/dev/Acceptance_testing'; - /** * Renders the list of available steps according to the submitted filters * @@ -54,20 +52,28 @@ public function render_stepsdefinitions($stepsdefinitions, $form) { $html .= $this->output->heading($title); // Info. - $installurl = $this->docsurl . '#Installation'; + $installurl = behat_command::DOCS_URL . '#Installation'; $installlink = html_writer::tag('a', $installurl, array('href' => $installurl, 'target' => '_blank')); - $writetestsurl = $this->docsurl . '#Writting_features'; + $writetestsurl = behat_command::DOCS_URL . '#Writting_features'; $writetestslink = html_writer::tag('a', $writetestsurl, array('href' => $writetestsurl, 'target' => '_blank')); - $writestepsurl = $this->docsurl . '#Adding_steps_definitions'; + $writestepsurl = behat_command::DOCS_URL . '#Adding_steps_definitions'; $writestepslink = html_writer::tag('a', $writestepsurl, array('href' => $writestepsurl, 'target' => '_blank')); $infos = array( get_string('installinfo', 'tool_behat', $installlink), get_string('newtestsinfo', 'tool_behat', $writetestslink), get_string('newstepsinfo', 'tool_behat', $writestepslink) ); + + // List of steps $html .= $this->output->box_start(); $html .= html_writer::tag('h1', 'Info'); - $html .= html_writer::tag('div', ''); + $html .= html_writer::empty_tag('div'); + $html .= html_writer::empty_tag('ul'); + $html .= html_writer::empty_tag('li'); + $html .= implode(html_writer::end_tag('li') . html_writer::empty_tag('li'), $infos); + $html .= html_writer::end_tag('li'); + $html .= html_writer::end_tag('ul'); + $html .= html_writer::end_tag('div'); $html .= $this->output->box_end(); // Form. @@ -77,7 +83,7 @@ public function render_stepsdefinitions($stepsdefinitions, $form) { ob_end_clean(); // Steps definitions. - $html .= html_writer::tag('div', $stepsdefinitions, array('id' => 'steps-definitions')); + $html .= html_writer::tag('div', $stepsdefinitions, array('class' => 'steps-definitions')); $html .= $this->output->footer(); diff --git a/admin/tool/behat/settings.php b/admin/tool/behat/settings.php index 8939cdd84d9ae..7f4b5a03c9910 100644 --- a/admin/tool/behat/settings.php +++ b/admin/tool/behat/settings.php @@ -23,7 +23,7 @@ */ -defined('MOODLE_INTERNAL') || die; +defined('MOODLE_INTERNAL') || die(); if ($hassiteconfig) { $url = $CFG->wwwroot . '/' . $CFG->admin . '/tool/behat/index.php'; diff --git a/admin/tool/behat/steps_definitions_form.php b/admin/tool/behat/steps_definitions_form.php index 788a3fb164b94..c6b06f317b36b 100644 --- a/admin/tool/behat/steps_definitions_form.php +++ b/admin/tool/behat/steps_definitions_form.php @@ -37,6 +37,7 @@ class steps_definitions_form extends moodleform { /** * Form definition + * @return void */ public function definition() { diff --git a/admin/tool/behat/styles.css b/admin/tool/behat/styles.css index 9f9bb963915da..d7ca4d1e795ff 100644 --- a/admin/tool/behat/styles.css +++ b/admin/tool/behat/styles.css @@ -1,5 +1,5 @@ -div#steps-definitions{border-style:solid;border-width:1px;border-color:#BBB;padding:5px;margin:auto;width:50%;} -div#steps-definitions .step{margin: 10px 0px 10px 0px;} -div#steps-definitions .stepdescription{color:#bf8c12;} -div#steps-definitions .steptype{color:#1467a6;margin-right: 5px;} -div#steps-definitions .stepregex{color:#060;} +.steps-definitions{border-style:solid;border-width:1px;border-color:#BBB;padding:5px;margin:auto;width:50%;} +.steps-definitions .step{margin: 10px 0px 10px 0px;} +.steps-definitions .stepdescription{color:#bf8c12;} +.steps-definitions .steptype{color:#1467a6;margin-right: 5px;} +.steps-definitions .stepregex{color:#060;} diff --git a/admin/tool/behat/tests/tool_behat_test.php b/admin/tool/behat/tests/tool_behat_test.php index 00ebcce9cedff..12db2e0ee237c 100644 --- a/admin/tool/behat/tests/tool_behat_test.php +++ b/admin/tool/behat/tests/tool_behat_test.php @@ -15,7 +15,7 @@ // along with Moodle. If not, see . /** - * Unit tests for admin/tool/behat + * Unit tests for admin/tool/behat. * * @package tool_behat * @copyright 2012 David Monllaó @@ -26,11 +26,11 @@ global $CFG; require_once($CFG->dirroot . '/' . $CFG->admin .'/tool/behat/locallib.php'); -require_once($CFG->libdir . '/behat/classes/behat_util.php'); +require_once($CFG->libdir . '/behat/classes/util.php'); require_once($CFG->libdir . '/behat/classes/behat_config_manager.php'); /** - * Allows access to internal methods without exposing them + * Allows access to internal methods without exposing them. * * @package tool_behat * @copyright 2012 David Monllaó @@ -57,13 +57,13 @@ public static function merge_config($config, $localconfig) { * @param array $stepsdefinitions * @return string */ - public static function get_config_file_contents($prefix, $features, $stepsdefinitions) { - return parent::get_config_file_contents($prefix, $features, $stepsdefinitions); + public static function get_config_file_contents($features, $stepsdefinitions) { + return parent::get_config_file_contents($features, $stepsdefinitions); } } /** - * Tool behat tests + * Tool behat tests. * * @package tool_behat * @copyright 2012 David Monllaó @@ -72,37 +72,7 @@ public static function get_config_file_contents($prefix, $features, $stepsdefini class tool_behat_testcase extends advanced_testcase { /** - * behat_util tests - */ - public function test_switch_environment() { - - // Only run the tests if behat dependencies are installed. - // We don't need to pre-check PHPUnit initialisation because we are running on it. - if (version_compare(PHP_VERSION, '5.4.0', '>=') && behat_command::are_behat_dependencies_installed()) { - behat_util::switchenvironment('enable'); - $this->assertTrue(behat_util::is_test_mode_enabled()); - $this->assertFalse(behat_util::is_test_environment_running()); - - // We trigger a debugging() if it's already enabled. - behat_util::switchenvironment('enable'); - $this->assertDebuggingCalled(); - - behat_util::switchenvironment('disable'); - $this->assertFalse(behat_util::is_test_mode_enabled()); - $this->assertFalse(behat_util::is_test_environment_running()); - - // We trigger a debugging() if it's already enabled. - behat_util::switchenvironment('disable'); - $this->assertDebuggingCalled(); - - // Ensure all continues disabled. - $this->assertFalse(behat_util::is_test_mode_enabled()); - $this->assertFalse(behat_util::is_test_environment_running()); - } - } - - /** - * behat_config_manager tests + * behat_config_manager tests. */ public function test_merge_configs() { @@ -170,11 +140,12 @@ public function test_merge_configs() { } /** - * behat_config_manager tests + * behat_config_manager tests. */ public function test_config_file_contents() { global $CFG; + // To avoid user value at config.php level. unset($CFG->behat_config); // List. @@ -190,9 +161,9 @@ public function test_config_file_contents() { 'anoche' => '/cuando/yo/dormia' ); - $contents = testable_behat_config_manager::get_config_file_contents('/i/am/a/prefix/', $features, $stepsdefinitions); + $contents = testable_behat_config_manager::get_config_file_contents($features, $stepsdefinitions); - $this->assertContains('features: /i/am/a/prefix/lib/behat/features', $contents); + $this->assertContains('features: ' . $CFG->dirroot . '/lib/behat/features', $contents); $this->assertContains('micarro: /me/lo/robaron', $contents); $this->assertContains('base_url: \'' . $CFG->behat_wwwroot . '\'', $contents); $this->assertContains('class: behat_init_context', $contents); diff --git a/config-dist.php b/config-dist.php index aac3a62a2af16..3cd7b3ecd236c 100644 --- a/config-dist.php +++ b/config-dist.php @@ -552,8 +552,8 @@ // // You can override default Moodle configuration for Behat and add your own // params; here you can add more profiles, use different Mink drivers than Selenium... -// This params would be merged with the default Moodle behat.yml, giving priority -// to the ones specified here. The array format is YAML, following the behat +// These params would be merged with the default Moodle behat.yml, giving priority +// to the ones specified here. The array format is YAML, following the Behat // params hierarchy. More info: http://docs.behat.org/guides/7.config.html // Example: // $CFG->behat_config = array( @@ -568,7 +568,7 @@ // ) // ); // -// You can completely switch to test environment when "php admin/tool/behatcli/util --enable", +// You can completely switch to test environment when "php admin/tool/behat/cli/util --enable", // this means that all the site accesses will be routed to the test environment instead of // the regular one, so NEVER USE THIS SETTING IN PRODUCTION SITES. This setting is useful // when working with cloud CI (continous integration) servers which requires public sites to run the diff --git a/lib/behat/classes/behat_command.php b/lib/behat/classes/behat_command.php index ece80390e51e5..5ef219936ecf3 100644 --- a/lib/behat/classes/behat_command.php +++ b/lib/behat/classes/behat_command.php @@ -23,7 +23,9 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -require_once(__DIR__ . '/../../filestorage/file_exceptions.php'); +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__ . '/../lib.php'); /** * Behat command related utils @@ -35,24 +37,28 @@ */ class behat_command { + /** + * Docs url + */ + const DOCS_URL = 'http://docs.moodle.org/dev/Acceptance_testing'; + /** * Ensures the behat dir exists in moodledata - * @throws file_exception * @return string Full path */ public static function get_behat_dir() { global $CFG; - $behatdir = $CFG->dataroot . '/behat'; + $behatdir = $CFG->behat_dataroot . '/behat'; if (!is_dir($behatdir)) { if (!mkdir($behatdir, $CFG->directorypermissions, true)) { - throw new file_exception('storedfilecannotcreatefiledirs'); + behat_error(BEHAT_EXITCODE_PERMISSIONS, 'Directory ' . $behatdir . ' can not be created'); } } if (!is_writable($behatdir)) { - throw new file_exception('storedfilecannotcreatefiledirs'); + behat_error(BEHAT_EXITCODE_PERMISSIONS, 'Directory ' . $behatdir . ' is not writable'); } return $behatdir; @@ -66,21 +72,44 @@ public final static function get_behat_command() { return 'vendor' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'behat'; } + /** + * Runs behat command with provided options + * + * Execution continues when the process finishes + * + * @param string $options Defaults to '' so tests would be executed + * @return array CLI command outputs [0] => string, [1] => integer + */ + public final static function run($options = '') { + global $CFG; + + $currentcwd = getcwd(); + chdir($CFG->dirroot); + exec(self::get_behat_command() . ' ' . $options, $output, $code); + chdir($currentcwd); + + return array($output, $code); + } + /** * Checks if behat is set up and working * + * Uses notice() instead of behat_error() because is + * also called from web interface + * * It checks behat dependencies have been installed and runs * the behat help command to ensure it works as expected * - * @throw Exception - * @param boolean $checkphp Extra check for the PHP version + * @param bool $checkphp Extra check for the PHP version + * @return void */ public static function check_behat_setup($checkphp = false) { global $CFG; // We don't check the PHP version if $CFG->behat_switchcompletely has been enabled. + // Here we are in CLI. if (empty($CFG->behat_switchcompletely) && $checkphp && version_compare(PHP_VERSION, '5.4.0', '<')) { - throw new Exception(get_string('wrongphpversion', 'tool_behat')); + behat_error(BEHAT_EXITCODE_REQUIREMENT, 'PHP 5.4 is required. See config-dist.php for possible alternatives'); } // Moodle setting. @@ -89,7 +118,7 @@ public static function check_behat_setup($checkphp = false) { $msg = get_string('wrongbehatsetup', 'tool_behat'); // With HTML. - $docslink = 'http://docs.moodle.org/dev/Acceptance_testing#Installation'; + $docslink = self::DOCS_URL . '#Installation'; if (!CLI_SCRIPT) { $docslink = html_writer::tag('a', $docslink, array('href' => $docslink, 'target' => '_blank')); } @@ -98,10 +127,7 @@ public static function check_behat_setup($checkphp = false) { } // Behat test command. - $currentcwd = getcwd(); - chdir($CFG->dirroot); - exec(self::get_behat_command() . ' --help', $output, $code); - chdir($currentcwd); + list($output, $code) = self::run(' --help'); if ($code != 0) { notice(get_string('wrongbehatsetup', 'tool_behat')); @@ -110,7 +136,7 @@ public static function check_behat_setup($checkphp = false) { /** * Has the site installed composer with --dev option - * @return boolean + * @return bool */ public static function are_behat_dependencies_installed() { if (!is_dir(__DIR__ . '/../../../vendor/behat')) { diff --git a/lib/behat/classes/behat_config_manager.php b/lib/behat/classes/behat_config_manager.php index 53bdbef8fb0a9..1c45dbd485e87 100644 --- a/lib/behat/classes/behat_config_manager.php +++ b/lib/behat/classes/behat_config_manager.php @@ -23,8 +23,10 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__ . '/../lib.php'); require_once(__DIR__ . '/behat_command.php'); -require_once(__DIR__ . '/../../filestorage/file_exceptions.php'); require_once(__DIR__ . '/../../testing/classes/tests_finder.php'); /** @@ -47,24 +49,21 @@ class behat_config_manager { * config files to avoid problems with concurrent executions. * * The steps definitions list can be filtered by component so it's - * behat.yml can be different from the dirroot one. + * behat.yml is different from the $CFG->dirroot one. * - * @param string $component Restricts the obtained steps definitions to the specified component - * @param string $testsrunner If the config file will be used to run tests - * @throws file_exception + * @param string $component Restricts the obtained steps definitions to the specified component + * @param string $testsrunner If the config file will be used to run tests + * @return void */ public static function update_config_file($component = '', $testsrunner = true) { global $CFG; - // Behat must run with the whole set of features and steps definitions. + // Behat must have a separate behat.yml to have access to the whole set of features and steps definitions. if ($testsrunner === true) { - $prefix = ''; - $configfilepath = $CFG->dirroot . '/behat.yml'; - - // Alternative for steps definitions filtering. + $configfilepath = behat_command::get_behat_dir() . '/behat.yml'; } else { + // Alternative for steps definitions filtering, one for each user. $configfilepath = self::get_steps_list_config_filepath(); - $prefix = $CFG->dirroot .'/'; } // Gets all the components with features. @@ -74,6 +73,8 @@ public static function update_config_file($component = '', $testsrunner = true) foreach ($components as $componentname => $path) { $path = self::clean_path($path) . self::get_behat_tests_path(); if (empty($featurespaths[$path]) && file_exists($path)) { + + // Standarizes separator (some dirs. comes with OS-dependant separator). $uniquekey = str_replace('\\', '/', $path); $featurespaths[$uniquekey] = $path; } @@ -94,11 +95,11 @@ public static function update_config_file($component = '', $testsrunner = true) // Behat config file specifing the main context class, // the required Behat extensions and Moodle test wwwroot. - $contents = self::get_config_file_contents($prefix, $features, $stepsdefinitions); + $contents = self::get_config_file_contents($features, $stepsdefinitions); // Stores the file. if (!file_put_contents($configfilepath, $contents)) { - throw new file_exception('cannotcreatefile', $configfilepath); + behat_error(BEHAT_EXITCODE_PERMISSIONS, 'File ' . $configfilepath . ' can not be created'); } } @@ -138,32 +139,42 @@ public static function get_components_steps_definitions() { /** * Returns the behat config file path used by the steps definition list + * + * Note this can only be called from web-based scripts so it will return the + * production dataroot not behat_dataroot. With this the steps definitions + * list is accessible without having to install the behat test site. + * * @return string */ public static function get_steps_list_config_filepath() { - return behat_command::get_behat_dir() . '/behat.yml'; + global $USER; + + $userdir = behat_command::get_behat_dir() . '/users/' . $USER->id; + make_writable_directory($userdir); + + return $userdir . '/behat.yml'; } /** * Behat config file specifing the main context class, * the required Behat extensions and Moodle test wwwroot. * - * @param string $prefix The filesystem prefix * @param array $features The system feature files * @param array $stepsdefinitions The system steps definitions * @return string */ - protected static function get_config_file_contents($prefix, $features, $stepsdefinitions) { + protected static function get_config_file_contents($features, $stepsdefinitions) { global $CFG; // We require here when we are sure behat dependencies are available. require_once($CFG->dirroot . '/vendor/autoload.php'); + $basedir = $CFG->dirroot . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'behat'; $config = array( 'default' => array( 'paths' => array( - 'features' => $prefix . 'lib/behat/features', - 'bootstrap' => $prefix . 'lib/behat/features/bootstrap', + 'features' => $basedir . DIRECTORY_SEPARATOR . 'features', + 'bootstrap' => $basedir . DIRECTORY_SEPARATOR . 'features' . DIRECTORY_SEPARATOR . 'bootstrap', ), 'context' => array( 'class' => 'behat_init_context' diff --git a/lib/behat/classes/behat_util.php b/lib/behat/classes/behat_util.php deleted file mode 100644 index ec47b61d89dd2..0000000000000 --- a/lib/behat/classes/behat_util.php +++ /dev/null @@ -1,199 +0,0 @@ -. - -/** - * Utils for behat-related stuff - * - * @package core - * @category test - * @copyright 2012 David Monllaó - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require_once(__DIR__ . '/../../testing/classes/util.php'); - -require_once(__DIR__ . '/behat_command.php'); -require_once(__DIR__ . '/behat_config_manager.php'); - -require_once(__DIR__ . '/../../filestorage/file_exceptions.php'); -require_once(__DIR__ . '/../../phpunit/bootstraplib.php'); - -/** - * Init/reset utilities for Behat database and dataroot - * - * @package core - * @category test - * @copyright 2013 David Monllaó - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class behat_util extends testing_util { - - /** - * Allows / disables the test environment to be accessed through the built-in server - * - * Built-in server must be started separately - * - * @param string $testenvironment enable|disable - */ - public static function switchenvironment($testenvironment) { - if ($testenvironment == 'enable') { - self::start_test_mode(); - } else if ($testenvironment == 'disable') { - self::stop_test_mode(); - } - } - - /** - * Checks if $CFG->behat_wwwroot is available - * - * @return boolean - */ - public static function is_server_running() { - global $CFG; - - $request = new curl(); - $request->get($CFG->behat_wwwroot); - return (true && !$request->get_errno()); - } - - /** - * Checks whether the test database and dataroot is ready - * Stops execution if something went wrong - */ - protected static function test_environment_problem() { - global $CFG; - - // PHPUnit --diag returns nothing if the test environment is set up correctly. - exec('php ' . $CFG->dirroot . '/' . $CFG->admin . '/tool/phpunit/cli/util.php --diag', $output, $code); - - // If something is not ready stop execution and display the CLI command output. - if ($code != 0) { - notice(get_string('phpunitenvproblem', 'tool_behat') . ': ' . implode(' ', $output)); - } - } - - /** - * Enables test mode - * - * Starts the test mode checking the composer installation and - * the phpunit test environment and updating the available - * features and steps definitions. - * - * Stores a file in dataroot/behat to allow Moodle to switch - * to the test environment when using cli-server (or $CFG->behat_switchcompletely) - * - * @throws file_exception - */ - protected static function start_test_mode() { - global $CFG; - - // Checks the behat set up and the PHP version. - behat_command::check_behat_setup(true); - - // Check that PHPUnit test environment is correctly set up. - self::test_environment_problem(); - - // Updates all the Moodle features and steps definitions. - behat_config_manager::update_config_file(); - - if (self::is_test_mode_enabled()) { - debugging('Test environment was already enabled'); - return; - } - - $behatdir = behat_command::get_behat_dir(); - - $contents = '$CFG->behat_wwwroot, $CFG->phpunit_prefix and $CFG->phpunit_dataroot' . - ' are currently used as $CFG->wwwroot, $CFG->prefix and $CFG->dataroot'; - $filepath = $behatdir . '/test_environment_enabled.txt'; - if (!file_put_contents($filepath, $contents)) { - throw new file_exception('cannotcreatefile', $filepath); - } - chmod($filepath, $CFG->directorypermissions); - } - - /** - * Disables test mode - * @throws file_exception - */ - protected static function stop_test_mode() { - - $testenvfile = self::get_test_filepath(); - - if (!self::is_test_mode_enabled()) { - debugging('Test environment was already disabled'); - } else { - if (!unlink($testenvfile)) { - throw new file_exception('cannotdeletetestenvironmentfile'); - } - } - } - - /** - * Checks whether test environment is enabled or disabled - * - * To check is the current script is running in the test - * environment - * - * @see tool_behat::is_test_environment_running() - * @return bool - */ - public static function is_test_mode_enabled() { - - $testenvfile = self::get_test_filepath(); - if (file_exists($testenvfile)) { - return true; - } - - return false; - } - - /** - * Returns true if Moodle is currently running with the test database and dataroot - * @return bool - */ - public static function is_test_environment_running() { - global $CFG; - - if (!empty($CFG->originaldataroot)) { - return true; - } - - return false; - } - - /** - * Returns the path to the file which specifies if test environment is enabled - * - * The file is in dataroot/behat/ but we need to - * know if test mode is running because then we swap - * it to phpunit_dataroot and we need the original value - * - * @return string - */ - protected final static function get_test_filepath() { - global $CFG; - - if (self::is_test_environment_running()) { - $prefix = $CFG->originaldataroot; - } else { - $prefix = $CFG->dataroot; - } - - return $prefix . '/behat/test_environment_enabled.txt'; - } - -} diff --git a/lib/behat/classes/util.php b/lib/behat/classes/util.php new file mode 100644 index 0000000000000..828f452c91a22 --- /dev/null +++ b/lib/behat/classes/util.php @@ -0,0 +1,258 @@ +. + +/** + * Utils for behat-related stuff + * + * @package core + * @category test + * @copyright 2012 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__ . '/../lib.php'); +require_once(__DIR__ . '/../../testing/classes/util.php'); +require_once(__DIR__ . '/behat_command.php'); +require_once(__DIR__ . '/behat_config_manager.php'); + +require_once(__DIR__ . '/../../filelib.php'); + +/** + * Init/reset utilities for Behat database and dataroot + * + * @package core + * @category test + * @copyright 2013 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_util extends testing_util { + + /** + * @var array Files to skip when resetting dataroot folder + */ + protected static $datarootskiponreset = array('.', '..', 'behat', 'behattestdir.txt'); + + /** + * @var array Files to skip when dropping dataroot folder + */ + protected static $datarootskipondrop = array('.', '..', 'lock'); + + /** + * Installs a site using $CFG->dataroot and $CFG->prefix + * @throws coding_exception + * @return void + */ + public static function install_site() { + global $DB; + + if (!defined('BEHAT_UTIL')) { + throw new coding_exception('This method can be only used by Behat CLI tool'); + } + + // New dataroot. + self::reset_dataroot(); + + $options = array(); + $options['adminuser'] = 'admin'; + $options['adminpass'] = 'admin'; + $options['fullname'] = 'Acceptance test site'; + + install_cli_database($options, false); + + // Update admin user info. + $user = $DB->get_record('user', array('username' => 'admin')); + $user->email = 'moodle@moodlemoodle.com'; + $user->firstname = 'Admin'; + $user->lastname = 'User'; + $user->city = 'Perth'; + $user->country = 'AU'; + $DB->update_record('user', $user); + + // Sets maximum debug level. + set_config('debug', DEBUG_DEVELOPER); + set_config('debugdisplay', true); + + // Keeps the current version of database and dataroot. + self::store_versions_hash(); + + // Stores the database contents for fast reset. + self::store_database_state(); + } + + /** + * Drops dataroot and remove test database tables + * @throws coding_exception + * @return void + */ + public static function drop_site() { + + if (!defined('BEHAT_UTIL')) { + throw new coding_exception('This method can be only used by Behat CLI tool'); + } + + self::reset_dataroot(); + self::drop_dataroot(); + self::drop_database(true); + } + + /** + * Checks if $CFG->behat_wwwroot is available + * + * @return bool + */ + public static function is_server_running() { + global $CFG; + + $request = new curl(); + $request->get($CFG->behat_wwwroot); + return (true && !$request->get_errno()); + } + + /** + * Checks whether the test database and dataroot is ready + * Stops execution if something went wrong + * @throws coding_exception + * @return void + */ + protected static function test_environment_problem() { + global $CFG, $DB; + + if (!defined('BEHAT_UTIL')) { + throw new coding_exception('This method can be only used by Behat CLI tool'); + } + + if (!self::is_test_site()) { + behat_error(1, 'This is not a behat test site!'); + } + + $tables = $DB->get_tables(false); + if (empty($tables)) { + behat_error(BEHAT_EXITCODE_INSTALL, ''); + } + + if (!self::is_test_data_updated()) { + behat_error(BEHAT_EXITCODE_REINSTALL, 'The test environment was initialised for a different version'); + } + } + + /** + * Enables test mode + * + * It uses CFG->behat_dataroot + * + * Starts the test mode checking the composer installation and + * the test environment and updating the available + * features and steps definitions. + * + * Stores a file in dataroot/behat to allow Moodle to switch + * to the test environment when using cli-server (or $CFG->behat_switchcompletely) + * @throws coding_exception + * @return void + */ + public static function start_test_mode() { + global $CFG; + + if (!defined('BEHAT_UTIL')) { + throw new coding_exception('This method can be only used by Behat CLI tool'); + } + + // Checks the behat set up and the PHP version. + behat_command::check_behat_setup(true); + + // Check that test environment is correctly set up. + self::test_environment_problem(); + + // Updates all the Moodle features and steps definitions. + behat_config_manager::update_config_file(); + + if (self::is_test_mode_enabled()) { + return; + } + + $contents = '$CFG->behat_wwwroot, $CFG->behat_prefix and $CFG->behat_dataroot' . + ' are currently used as $CFG->wwwroot, $CFG->prefix and $CFG->dataroot'; + $filepath = self::get_test_file_path(); + if (!file_put_contents($filepath, $contents)) { + behat_error(BEHAT_EXITCODE_PERMISSIONS, 'File ' . $filepath . ' can not be created'); + } + } + + /** + * Disables test mode + * @throws coding_exception + * @return void + */ + public static function stop_test_mode() { + + if (!defined('BEHAT_UTIL')) { + throw new coding_exception('This method can be only used by Behat CLI tool'); + } + + $testenvfile = self::get_test_file_path(); + + if (!self::is_test_mode_enabled()) { + echo "Test environment was already disabled\n"; + } else { + if (!unlink($testenvfile)) { + behat_error(BEHAT_EXITCODE_PERMISSIONS, 'Can not delete test environment file'); + } + } + } + + /** + * Checks whether test environment is enabled or disabled + * + * To check is the current script is running in the test + * environment + * + * @see tool_behat::is_using_test_environment() + * @return bool + */ + public static function is_test_mode_enabled() { + + $testenvfile = self::get_test_file_path(); + if (file_exists($testenvfile)) { + return true; + } + + return false; + } + + /** + * Returns true if Moodle is currently running with the test database and dataroot + * @return bool + */ + public static function is_using_test_environment() { + global $CFG; + + if (!empty($CFG->originaldataroot)) { + return true; + } + + return false; + } + + /** + * Returns the path to the file which specifies if test environment is enabled + * @return string + */ + protected final static function get_test_file_path() { + return behat_command::get_behat_dir() . '/test_environment_enabled.txt'; + } + +} diff --git a/lib/behat/features/bootstrap/behat_init_context.php b/lib/behat/features/bootstrap/behat_init_context.php index c1830b6e423f0..01a0d18e21feb 100644 --- a/lib/behat/features/bootstrap/behat_init_context.php +++ b/lib/behat/features/bootstrap/behat_init_context.php @@ -17,7 +17,8 @@ /** * Contexts initializer class * - * @package tool_behat + * @package core + * @category test * @copyright 2012 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -34,7 +35,8 @@ * Renamed from behat FeatureContext class according * to Moodle coding styles conventions * - * @package tool_behat + * @package core + * @category test * @copyright 2012 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -43,7 +45,8 @@ class behat_init_context extends BehatContext { /** * Initializes subcontexts * - * @param array $parameters context parameters (set them up through behat.yml) + * @param array $parameters context parameters (set them up through behat.yml) + * @return void */ public function __construct(array $parameters) { $this->useContext('moodle', new MoodleContext($parameters)); diff --git a/lib/behat/lib.php b/lib/behat/lib.php new file mode 100644 index 0000000000000..dda2deb40b345 --- /dev/null +++ b/lib/behat/lib.php @@ -0,0 +1,71 @@ +. + +/** + * Behat basic functions + * + * It does not include MOODLE_INTERNAL because is part of the bootstrap + * + * @package core + * @category test + * @copyright 2012 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(__DIR__ . '/../testing/lib.php'); + +define('BEHAT_EXITCODE_CONFIG', 250); +define('BEHAT_EXITCODE_REQUIREMENT', 251); +define('BEHAT_EXITCODE_PERMISSIONS', 252); +define('BEHAT_EXITCODE_REINSTALL', 253); +define('BEHAT_EXITCODE_INSTALL', 254); + +/** + * Exits with an error code + * + * @param mixed $errorcode + * @param string $text + * @return void Stops execution with error code + */ +function behat_error($errorcode, $text = '') { + + // Adding error prefixes. + switch ($errorcode) { + case BEHAT_EXITCODE_CONFIG: + $text = 'Behat config error: ' . $text; + break; + case BEHAT_EXITCODE_REQUIREMENT: + $text = 'Behat requirement not satisfied: ' . $text; + break; + case BEHAT_EXITCODE_PERMISSIONS: + $text = 'Behat permissions problem: ' . $text . ', check the permissions'; + break; + case BEHAT_EXITCODE_REINSTALL: + $path = testing_cli_argument_path('/admin/tool/behat/cli/util.php'); + $text = "Reinstall Behat: ".$text.", use:\n php ".$path." --drop \n php ".$path." --install"; + break; + case BEHAT_EXITCODE_INSTALL: + $path = testing_cli_argument_path('/admin/tool/behat/cli/util.php'); + $text = "Install Behat before enabling it, use:\n php ".$path." --install"; + break; + default: + $text = 'Unknown error ' . $errorcode . ' ' . $text; + break; + } + + testing_error($errorcode, $text); +} + diff --git a/lib/setup.php b/lib/setup.php index 2c19eda1332c3..9ceabedfa5fe2 100644 --- a/lib/setup.php +++ b/lib/setup.php @@ -90,7 +90,7 @@ exit(1); } -// Ignore $CFG->behat_wwwroot and use the same wwwroot in case test env. is enabled. +// Ignore $CFG->behat_wwwroot and use the same wwwroot. if (isset($CFG->behat_switchcompletely)) { $CFG->behat_wwwroot = $CFG->wwwroot; @@ -99,19 +99,28 @@ $CFG->behat_wwwroot = 'http://localhost:8000'; } -// Test environment is requested if: Behat is running, if we are accessing though cli-server -// or if $CFG->behat_switchcompletely has been set (maintains CLI scripts behaviour). -$testenvironmentrequested = (isset($CFG->behat_switchcompletely) && php_sapi_name() !== 'cli') || - php_sapi_name() === 'cli-server' || - defined('BEHAT_RUNNING'); -// Only switch to test environment if it has been enabled. -$testenvironmentenabled = file_exists($CFG->dataroot . '/behat/test_environment_enabled.txt'); -if ($testenvironmentenabled && $testenvironmentrequested) { - $CFG->wwwroot = $CFG->behat_wwwroot; - $CFG->passwordsaltmain = 'phpunit'; - $CFG->originaldataroot = $CFG->dataroot; - $CFG->prefix = $CFG->phpunit_prefix; - $CFG->dataroot = $CFG->phpunit_dataroot; + +// Test environment is requested if: +// * Behat is running (constant set hooking the behat init process before requiring config.php). +// * If we are accessing though the built-in web server (cli-server). +// * If $CFG->behat_switchcompletely has been set (maintains CLI scripts behaviour, which ATM is only preventive). +// Test environment is enabled if: +// * User has previously enabled through admin/tool/behat/cli/util.php --enable. +// Both are required to switch to test mode +if (isset($CFG->behat_dataroot) && isset($CFG->behat_prefix) && file_exists($CFG->behat_dataroot)) { + $CFG->behat_dataroot = realpath($CFG->behat_dataroot); + $testenvironmentrequested = (isset($CFG->behat_switchcompletely) && php_sapi_name() !== 'cli') || + php_sapi_name() === 'cli-server' || + defined('BEHAT_RUNNING'); + // Only switch to test environment if it has been enabled. + $testenvironmentenabled = file_exists($CFG->behat_dataroot . '/behat/test_environment_enabled.txt'); + if ($testenvironmentenabled && $testenvironmentrequested) { + $CFG->wwwroot = $CFG->behat_wwwroot; + $CFG->passwordsaltmain = 'moodle'; + $CFG->originaldataroot = $CFG->dataroot; + $CFG->prefix = $CFG->behat_prefix; + $CFG->dataroot = $CFG->behat_dataroot; + } } // Define admin directory