This commit is contained in:
David Monllao 2016-08-24 09:26:29 +08:00
commit 30daa61475
15 changed files with 1851 additions and 357 deletions

View file

@ -25,9 +25,7 @@
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../lib.php');
require_once(__DIR__ . '/behat_command.php');
require_once(__DIR__ . '/../../testing/classes/tests_finder.php');
require_once(__DIR__ . '/behat_config_util.php');
/**
* Behat configuration manager
@ -47,6 +45,24 @@ class behat_config_manager {
*/
public static $autoprofileconversion = false;
/**
* @var behat_config_util keep object of behat_config_util for use.
*/
public static $behatconfigutil = null;
/**
* Returns behat_config_util.
*
* @return behat_config_util
*/
private static function get_behat_config_util() {
if (!self::$behatconfigutil) {
self::$behatconfigutil = new behat_config_util();
}
return self::$behatconfigutil;
}
/**
* Updates a config file
*
@ -59,9 +75,14 @@ class behat_config_manager {
* @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
* @param string $tags features files including tags.
* @param bool $themesuitewithallfeatures if only theme specific features need to be included in the suite.
* @param int $parallelruns number of parallel runs.
* @param int $run current run for which config needs to be updated.
* @return void
*/
public static function update_config_file($component = '', $testsrunner = true, $tags = '') {
public static function update_config_file($component = '', $testsrunner = true, $tags = '',
$themesuitewithallfeatures = false, $parallelruns = 0, $run = 0) {
global $CFG;
// Behat must have a separate behat.yml to have access to the whole set of features and steps definitions.
@ -72,49 +93,36 @@ class behat_config_manager {
$configfilepath = self::get_steps_list_config_filepath();
}
// Gets all the components with features.
$behatconfigutil = self::get_behat_config_util();
$behatconfigutil->set_theme_suite_to_include_core_features($themesuitewithallfeatures);
$behatconfigutil->set_tag_for_feature_filter($tags);
// Gets all the components with features, if running the tests otherwise not required.
$features = array();
$components = tests_finder::get_components_with_tests('features');
if ($components) {
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;
}
}
foreach ($featurespaths as $path) {
$additional = glob("$path/*.feature");
$features = array_merge($features, $additional);
}
}
// Optionally include features from additional directories.
if (!empty($CFG->behat_additionalfeatures)) {
$features = array_merge($features, array_map("realpath", $CFG->behat_additionalfeatures));
if ($testsrunner) {
$features = $behatconfigutil->get_components_features();
}
// Gets all the components with steps definitions.
$stepsdefinitions = array();
$steps = self::get_components_steps_definitions();
if ($steps) {
foreach ($steps as $key => $filepath) {
if ($component == '' || $component === $key) {
$stepsdefinitions[$key] = $filepath;
}
}
}
$stepsdefinitions = $behatconfigutil->get_components_contexts($component);
// We don't want the deprecated steps definitions here.
if (!$testsrunner) {
unset($stepsdefinitions['behat_deprecated']);
}
// Get current run.
if (empty($run) && ($run !== false) && !empty($CFG->behatrunprocess)) {
$run = $CFG->behatrunprocess;
}
// Get number of parallel runs if not passed.
if (empty($parallelruns) && ($parallelruns !== false)) {
$parallelruns = self::get_parallel_test_runs();
}
// Behat config file specifing the main context class,
// the required Behat extensions and Moodle test wwwroot.
$contents = self::get_config_file_contents(self::get_features_with_tags($features, $tags), $stepsdefinitions);
$contents = $behatconfigutil->get_config_file_contents($features, $stepsdefinitions, $tags, $parallelruns, $run);
// Stores the file.
if (!file_put_contents($configfilepath, $contents)) {
@ -129,55 +137,13 @@ class behat_config_manager {
* @param array $features set of feature files.
* @param string $tags list of tags (currently support && only.)
* @return array filtered list of feature files with tags.
* @deprecated since 3.2 MDL-55072 - please use behat_config_util.php
* @todo MDL-55365 This will be deleted in Moodle 3.6.
*/
public static function get_features_with_tags($features, $tags) {
if (empty($tags)) {
return $features;
}
$newfeaturelist = array();
// Split tags in and and or.
$tags = explode('&&', $tags);
$andtags = array();
$ortags = array();
foreach ($tags as $tag) {
// Explode all tags seperated by , and add it to ortags.
$ortags = array_merge($ortags, explode(',', $tag));
// And tags will be the first one before comma(,).
$andtags[] = preg_replace('/,.*/', '', $tag);
}
foreach ($features as $featurefile) {
$contents = file_get_contents($featurefile);
$includefeature = true;
foreach ($andtags as $tag) {
// If negitive tag, then ensure it don't exist.
if (strpos($tag, '~') !== false) {
$tag = substr($tag, 1);
if ($contents && strpos($contents, $tag) !== false) {
$includefeature = false;
break;
}
} else if ($contents && strpos($contents, $tag) === false) {
$includefeature = false;
break;
}
}
// If feature not included then check or tags.
if (!$includefeature && !empty($ortags)) {
foreach ($ortags as $tag) {
if ($contents && (strpos($tag, '~') === false) && (strpos($contents, $tag) !== false)) {
$includefeature = true;
break;
}
}
}
if ($includefeature) {
$newfeaturelist[] = $featurefile;
}
}
return $newfeaturelist;
debugging('Use of get_features_with_tags is deprecated, please see behat_config_util', DEBUG_DEVELOPER);
return self::get_behat_config_util()->filtered_features_with_tags($features, $tags);
}
/**
@ -189,32 +155,14 @@ class behat_config_manager {
* it from the steps definitions web interface
*
* @return array
* @deprecated since 3.2 MDL-55072 - please use behat_config_util.php
* @todo MDL-55365 This will be deleted in Moodle 3.6.
*/
public static function get_components_steps_definitions() {
$components = tests_finder::get_components_with_tests('stepsdefinitions');
if (!$components) {
return false;
}
$stepsdefinitions = array();
foreach ($components as $componentname => $componentpath) {
$componentpath = self::clean_path($componentpath);
if (!file_exists($componentpath . self::get_behat_tests_path())) {
continue;
}
$diriterator = new DirectoryIterator($componentpath . self::get_behat_tests_path());
$regite = new RegexIterator($diriterator, '|behat_.*\.php$|');
// All behat_*.php inside behat_config_manager::get_behat_tests_path() are added as steps definitions files.
foreach ($regite as $file) {
$key = $file->getBasename('.php');
$stepsdefinitions[$key] = $file->getPathname();
}
}
return $stepsdefinitions;
debugging('Use of get_components_steps_definitions is deprecated, please see behat_config_util::get_components_contexts',
DEBUG_DEVELOPER);
return self::get_behat_config_util()->get_components_contexts();
}
/**
@ -361,91 +309,13 @@ class behat_config_manager {
* @param array $features The system feature files
* @param array $stepsdefinitions The system steps definitions
* @return string
* @deprecated since 3.2 MDL-55072 - please use behat_config_util.php
* @todo MDL-55365 This will be deleted in Moodle 3.6.
*/
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');
$selenium2wdhost = array('wd_host' => 'http://localhost:4444/wd/hub');
$parallelruns = self::get_parallel_test_runs();
// If parallel run, then only divide features.
if (!empty($CFG->behatrunprocess) && !empty($parallelruns)) {
// Attempt to split into weighted buckets using timing information, if available.
if ($alloc = self::profile_guided_allocate($features, max(1, $parallelruns), $CFG->behatrunprocess)) {
$features = $alloc;
} else {
// Divide the list of feature files amongst the parallel runners.
srand(crc32(floor(time() / 3600 / 24) . var_export($features, true)));
shuffle($features);
// Pull out the features for just this worker.
if (count($features)) {
$features = array_chunk($features, ceil(count($features) / max(1, $parallelruns)));
// Check if there is any feature file for this process.
if (!empty($features[$CFG->behatrunprocess - 1])) {
$features = $features[$CFG->behatrunprocess - 1];
} else {
$features = null;
}
}
}
// Set proper selenium2 wd_host if defined.
if (!empty($CFG->behat_parallel_run[$CFG->behatrunprocess - 1]['wd_host'])) {
$selenium2wdhost = array('wd_host' => $CFG->behat_parallel_run[$CFG->behatrunprocess - 1]['wd_host']);
}
}
// It is possible that it has no value as we don't require a full behat setup to list the step definitions.
if (empty($CFG->behat_wwwroot)) {
$CFG->behat_wwwroot = 'http://itwillnotbeused.com';
}
// Comments use black color, so failure path is not visible. Using color other then black/white is safer.
// https://github.com/Behat/Behat/pull/628.
$config = array(
'default' => array(
'formatters' => array(
'moodle_progress' => array(
'output_styles' => array(
'comment' => array('magenta'))
)
),
'suites' => array(
'default' => array(
'paths' => $features,
'contexts' => array_keys($stepsdefinitions)
)
),
'extensions' => array(
'Behat\MinkExtension' => array(
'base_url' => $CFG->behat_wwwroot,
'goutte' => null,
'selenium2' => $selenium2wdhost
),
'Moodle\BehatExtension' => array(
'moodledirroot' => $CFG->dirroot,
'steps_definitions' => $stepsdefinitions
)
)
)
);
// In case user defined overrides respect them over our default ones.
if (!empty($CFG->behat_config)) {
foreach ($CFG->behat_config as $profile => $values) {
$config = self::merge_config($config, self::merge_behat_config($profile, $values));
}
}
// Check for Moodle custom ones.
if (!empty($CFG->behat_profiles) && is_array($CFG->behat_profiles)) {
foreach ($CFG->behat_profiles as $profile => $values) {
$config = self::merge_config($config, self::get_behat_profile($profile, $values));
}
}
return Symfony\Component\Yaml\Yaml::dump($config, 10, 2);
debugging('Use of get_config_file_contents is deprecated, please see behat_config_util', DEBUG_DEVELOPER);
return self::get_behat_config_util()->get_config_file_contents($features, $stepsdefinitions);
}
/**
@ -454,41 +324,13 @@ class behat_config_manager {
* @param string $profile profile name
* @param array $values values for profile
* @return array
* @deprecated since 3.2 MDL-55072 - please use behat_config_util.php
* @todo MDL-55365 This will be deleted in Moodle 3.6.
*/
protected static function merge_behat_config($profile, $values) {
// Only add profile which are compatible with Behat 3.x
// Just check if any of Bheat 2.5 config is set. Not checking for 3.x as it might have some other configs
// Like : rerun_cache etc.
if (!isset($values['filters']['tags']) && !isset($values['extensions']['Behat\MinkExtension\Extension'])) {
return array($profile => $values);
}
// Parse 2.5 format and get related values.
$oldconfigvalues = array();
if (isset($values['extensions']['Behat\MinkExtension\Extension'])) {
$extensionvalues = $values['extensions']['Behat\MinkExtension\Extension'];
if (isset($extensionvalues['selenium2']['browser'])) {
$oldconfigvalues['browser'] = $extensionvalues['selenium2']['browser'];
}
if (isset($extensionvalues['selenium2']['wd_host'])) {
$oldconfigvalues['wd_host'] = $extensionvalues['selenium2']['wd_host'];
}
if (isset($extensionvalues['capabilities'])) {
$oldconfigvalues['capabilities'] = $extensionvalues['capabilities'];
}
}
if (isset($values['filters']['tags'])) {
$oldconfigvalues['tags'] = $values['filters']['tags'];
}
if (!empty($oldconfigvalues)) {
self::$autoprofileconversion = true;
return self::get_behat_profile($profile, $oldconfigvalues);
}
// If nothing set above then return empty array.
return array();
debugging('Use of merge_behat_config is deprecated, please see behat_config_util', DEBUG_DEVELOPER);
self::get_behat_config_util()->get_behat_config_for_profile($profile, $values);
}
/**
@ -569,64 +411,8 @@ class behat_config_manager {
*/
protected static function profile_guided_allocate($features, $nbuckets, $instance) {
$behattimingfile = defined('BEHAT_FEATURE_TIMING_FILE') &&
@filesize(BEHAT_FEATURE_TIMING_FILE) ? BEHAT_FEATURE_TIMING_FILE : false;
if (!$behattimingfile || !$behattimingdata = @json_decode(file_get_contents($behattimingfile), true)) {
// No data available, fall back to relying on steps data.
$stepfile = "";
if (defined('BEHAT_FEATURE_STEP_FILE') && BEHAT_FEATURE_STEP_FILE) {
$stepfile = BEHAT_FEATURE_STEP_FILE;
}
// We should never get this. But in case we can't do this then fall back on simple splitting.
if (empty($stepfile) || !$behattimingdata = @json_decode(file_get_contents($stepfile), true)) {
return false;
}
}
arsort($behattimingdata); // Ensure most expensive is first.
$realroot = realpath(__DIR__.'/../../../').'/';
$defaultweight = array_sum($behattimingdata) / count($behattimingdata);
$weights = array_fill(0, $nbuckets, 0);
$buckets = array_fill(0, $nbuckets, array());
$totalweight = 0;
// Re-key the features list to match timing data.
foreach ($features as $k => $file) {
$key = str_replace($realroot, '', $file);
$features[$key] = $file;
unset($features[$k]);
if (!isset($behattimingdata[$key])) {
$behattimingdata[$key] = $defaultweight;
}
}
// Sort features by known weights; largest ones should be allocated first.
$behattimingorder = array();
foreach ($features as $key => $file) {
$behattimingorder[$key] = $behattimingdata[$key];
}
arsort($behattimingorder);
// Finally, add each feature one by one to the lightest bucket.
foreach ($behattimingorder as $key => $weight) {
$file = $features[$key];
$lightbucket = array_search(min($weights), $weights);
$weights[$lightbucket] += $weight;
$buckets[$lightbucket][] = $file;
$totalweight += $weight;
}
if ($totalweight && !defined('BEHAT_DISABLE_HISTOGRAM') && $instance == $nbuckets) {
echo "Bucket weightings:\n";
foreach ($weights as $k => $weight) {
echo $k + 1 . ": " . str_repeat('*', 70 * $nbuckets * $weight / $totalweight) . PHP_EOL;
}
}
// Return the features for this worker.
return $buckets[$instance - 1];
debugging('Use of profile_guided_allocate is deprecated, please see behat_config_util', DEBUG_DEVELOPER);
return self::get_behat_config_util()->profile_guided_allocate($features, $nbuckets, $instance);
}
/**
@ -637,34 +423,13 @@ class behat_config_manager {
* @param mixed $config The node of the default config
* @param mixed $localconfig The node of the local config
* @return mixed The merge result
* @deprecated since 3.2 MDL-55072 - please use behat_config_util.php
* @todo MDL-55365 This will be deleted in Moodle 3.6.
*/
protected static function merge_config($config, $localconfig) {
if (!is_array($config) && !is_array($localconfig)) {
return $localconfig;
}
// Local overrides also deeper default values.
if (is_array($config) && !is_array($localconfig)) {
return $localconfig;
}
foreach ($localconfig as $key => $value) {
// If defaults are not as deep as local values let locals override.
if (!is_array($config)) {
unset($config);
}
// Add the param if it doesn't exists or merge branches.
if (empty($config[$key])) {
$config[$key] = $value;
} else {
$config[$key] = self::merge_config($config[$key], $localconfig[$key]);
}
}
return $config;
debugging('Use of merge_config is deprecated, please see behat_config_util', DEBUG_DEVELOPER);
return self::get_behat_config_util()->merge_config($config, $localconfig);
}
/**
@ -673,28 +438,25 @@ class behat_config_manager {
* @see tests_finder::get_all_directories_with_tests() it returns the path including /tests/
* @param string $path
* @return string The string without the last /tests part
* @deprecated since 3.2 MDL-55072 - please use behat_config_util.php
* @todo MDL-55365 This will be deleted in Moodle 3.6.
*/
protected final static function clean_path($path) {
$path = rtrim($path, DIRECTORY_SEPARATOR);
$parttoremove = DIRECTORY_SEPARATOR . 'tests';
$substr = substr($path, strlen($path) - strlen($parttoremove));
if ($substr == $parttoremove) {
$path = substr($path, 0, strlen($path) - strlen($parttoremove));
}
return rtrim($path, DIRECTORY_SEPARATOR);
debugging('Use of clean_path is deprecated, please see behat_config_util', DEBUG_DEVELOPER);
return self::get_behat_config_util()->clean_path($path);
}
/**
* The relative path where components stores their behat tests
*
* @return string
* @deprecated since 3.2 MDL-55072 - please use behat_config_util.php
* @todo MDL-55365 This will be deleted in Moodle 3.6.
*/
protected final static function get_behat_tests_path() {
return DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'behat';
debugging('Use of get_behat_tests_path is deprecated, please see behat_config_util', DEBUG_DEVELOPER);
return self::get_behat_config_util()->get_behat_tests_path();
}
}

File diff suppressed because it is too large Load diff

View file

@ -55,8 +55,22 @@ class behat_context_helper {
*
* @param Environment $environment
* @return void
* @deprecated since 3.2 MDL-55072 - please use behat_context_helper::set_environment()
* @todo MDL-55365 This will be deleted in Moodle 3.6.
*/
public static function set_session(Environment $environment) {
debugging('set_session is deprecated. Please use set_environment instead.', DEBUG_DEVELOPER);
self::set_environment($environment);
}
/**
* Sets behat environment.
*
* @param Environment $environment
* @return void
*/
public static function set_environment(Environment $environment) {
self::$environment = $environment;
}
@ -67,17 +81,27 @@ class behat_context_helper {
* that uses direct API calls; steps returning step chains
* can not be executed like this.
*
* @throws coding_exception
* @throws Behat\Behat\Context\Exception\ContextNotFoundException
* @param string $classname Context identifier (the class name).
* @return behat_base
*/
public static function get($classname) {
if (!$subcontext = self::$environment->getContext($classname)) {
throw coding_exception('The required "' . $classname . '" class does not exist');
$suitename = self::$environment->getSuite()->getName();
$overridencontextname = 'behat_theme_'.$suitename.'_'.$classname;
// Check if overridden context class exists.
if ($suitename !== 'default') {
try {
$subcontext = self::$environment->getContext($overridencontextname);
return $subcontext;
} catch (Behat\Behat\Context\Exception\ContextNotFoundException $e) {
// If context not found then it's not overridden.
}
}
return $subcontext;
// Get the actual context.
return self::$environment->getContext($classname);
}
/**

View file

@ -23,7 +23,6 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Moodle selectors manager.

View file

@ -215,10 +215,13 @@ class behat_util extends testing_util {
*
* Stores a file in dataroot/behat to allow Moodle to switch
* to the test environment when using cli-server.
* @param bool $themesuitewithallfeatures if only theme specific features need to be included in the suite.
* @param int $parallelruns number of parallel runs.
* @param int $run current run.
* @throws coding_exception
* @return void
*/
public static function start_test_mode() {
public static function start_test_mode($themesuitewithallfeatures = false, $parallelruns = 0, $run = 0) {
global $CFG;
if (!defined('BEHAT_UTIL')) {
@ -234,7 +237,7 @@ class behat_util extends testing_util {
self::test_environment_problem();
// Updates all the Moodle features and steps definitions.
behat_config_manager::update_config_file();
behat_config_manager::update_config_file('', true, '', $themesuitewithallfeatures, $parallelruns, $run);
if (self::is_test_mode_enabled()) {
return;