Skip to content

Commit

Permalink
Merge branch 'MDL-66675-master' of git://github.com/andrewnicols/moodle
Browse files Browse the repository at this point in the history
  • Loading branch information
stronk7 committed Sep 25, 2019
2 parents ba62391 + 1162e2c commit b57e984
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 32 deletions.
4 changes: 4 additions & 0 deletions config-dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,10 @@
// Example:
// $CFG->behat_faildump_path = '/my/path/to/save/failure/dumps';
//
// You can make behat pause upon failure to help you diagnose and debug problems with your tests.
//
// $CFG->behat_pause_on_fail = true;
//
// You can specify db, selenium wd_host etc. for behat parallel run by setting following variable.
// Example:
// $CFG->behat_parallel_run = array (
Expand Down
37 changes: 37 additions & 0 deletions lib/behat/classes/util.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
require_once(__DIR__ . '/behat_config_manager.php');

require_once(__DIR__ . '/../../filelib.php');
require_once(__DIR__ . '/../../clilib.php');

use Behat\Mink\Session;

/**
* Init/reset utilities for Behat database and dataroot
Expand Down Expand Up @@ -374,4 +377,38 @@ public static function reset_all_data() {
// $CFG values from the old run. @see set_config.
initialise_cfg();
}

/**
* Pause execution immediately.
*
* @param Session $session
* @param string $message The message to show when pausing.
* This will be passed through cli_ansi_format so appropriate ANSI formatting and features are available.
*/
public static function pause(Session $session, string $message): void {
$posixexists = function_exists('posix_isatty');

// Make sure this step is only used with interactive terminal (if detected).
if ($posixexists && !@posix_isatty(STDOUT)) {
throw new ExpectationException('Break point should only be used with interactive terminal.', $session);
}

// Save the cursor position, ring the bell, and add a new line.
fwrite(STDOUT, cli_ansi_format("<cursor:save><bell><newline>"));

// Output the formatted message and reset colour back to normal.
$formattedmessage = cli_ansi_format("{$message}<colour:normal>");
fwrite(STDOUT, $formattedmessage);

// Wait for input.
fread(STDIN, 1024);

// Move the cursor back up to the previous position, then restore the original position stored earlier, and move
// it back down again.
fwrite(STDOUT, cli_ansi_format("<cursor:up><cursor:up><cursor:restore><cursor:down><cursor:down>"));

// Add any extra lines back if the provided message was spread over multiple lines.
$linecount = count(explode("\n", $formattedmessage));
fwrite(STDOUT, str_repeat(cli_ansi_format("<cursor:down>"), $linecount - 1));
}
}
56 changes: 56 additions & 0 deletions lib/clilib.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,59 @@ function cli_logo($padding=2, $return=false) {
cli_write($logo);
}
}

/**
* Substitute cursor, colour, and bell placeholders in a CLI output to ANSI escape characters when ANSI is available.
*
* @param string $message
* @return string
*/
function cli_ansi_format(string $message): string {
global $CFG;

$replacements = [
"<newline>" => "\n",
"<bell>" => "\007",

// Cursor movement: https://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x361.html.
"<cursor:save>" => "\033[s",
"<cursor:restore>" => "\033[u",
"<cursor:up>" => "\033[1A",
"<cursor:down>" => "\033[1B",
"<cursor:forward>" => "\033[1C",
"<cursor:back>" => "\033[1D",
];

$colours = [
'normal' => '0;0',
'black' => '0;30',
'darkGray' => '1;30',
'blue' => '0;34',
'lightBlue' => '1;34',
'green' => '0;32',
'lightGreen' => '1;32',
'cyan' => '0;36',
'lightCyan' => '1;36',
'red' => '0;31',
'lightRed' => '1;31',
'purple' => '0;35',
'lightPurple' => '1;35',
'brown' => '0;33',
'yellow' => '1;33',
'lightYellow' => '0;93',
'lightGray' => '0;37',
'white' => '1;37',
];

foreach ($colours as $colour => $code) {
$replacements["<colour:{$colour}>"] = "\033[{$code}m";
}

// Windows don't support ANSI code by default, but does if ANSICON is available.
$isansicon = getenv('ANSICON');
if (($CFG->ostype === 'WINDOWS') && empty($isansicon)) {
return str_replace(array_keys($replacements), '', $message);
}

return str_replace(array_keys($replacements), array_values($replacements), $message);
}
24 changes: 3 additions & 21 deletions lib/tests/behat/behat_general.php
Original file line number Diff line number Diff line change
Expand Up @@ -1635,27 +1635,9 @@ protected function get_page_load_xpath() {
*
* @Then /^(?:|I )pause(?:| scenario execution)$/
*/
public function i_pause_scenario_executon() {
global $CFG;

$posixexists = function_exists('posix_isatty');

// Make sure this step is only used with interactive terminal (if detected).
if ($posixexists && !@posix_isatty(STDOUT)) {
$session = $this->getSession();
throw new ExpectationException('Break point should only be used with interative terminal.', $session);
}

// Windows don't support ANSI code by default, but with ANSICON.
$isansicon = getenv('ANSICON');
if (($CFG->ostype === 'WINDOWS') && empty($isansicon)) {
fwrite(STDOUT, "Paused. Press Enter/Return to continue.");
fread(STDIN, 1024);
} else {
fwrite(STDOUT, "\033[s\n\033[0;93mPaused. Press \033[1;31mEnter/Return\033[0;93m to continue.\033[0m");
fread(STDIN, 1024);
fwrite(STDOUT, "\033[2A\033[u\033[2B");
}
public function i_pause_scenario_execution() {
$message = "<colour:lightYellow>Paused. Press <colour:lightRed>Enter/Return<colour:lightYellow> to continue.";
behat_util::pause($this->getSession(), $message);
}

/**
Expand Down
31 changes: 20 additions & 11 deletions lib/tests/behat/behat_hooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -500,11 +500,7 @@ public function after_step_javascript(AfterStepScope $scope) {
throw new coding_exception("Step '" . $scope->getStep()->getText() . "'' is undefined.");
}

// Save the page content if the step failed.
if (!empty($CFG->behat_faildump_path) &&
$scope->getTestResult()->getResultCode() === Behat\Testwork\Tester\Result\TestResult::FAILED) {
$this->take_contentdump($scope);
}
$isfailed = $scope->getTestResult()->getResultCode() === Behat\Testwork\Tester\Result\TestResult::FAILED;

// Abort any open transactions to prevent subsequent tests hanging.
// This does the same as abort_all_db_transactions(), but doesn't call error_log() as we don't
Expand All @@ -516,17 +512,30 @@ public function after_step_javascript(AfterStepScope $scope) {
}
}

if ($isfailed && !empty($CFG->behat_faildump_path)) {
// Save the page content (html).
$this->take_contentdump($scope);

if ($this->running_javascript()) {
// Save a screenshot.
$this->take_screenshot($scope);
}
}

if ($isfailed && !empty($CFG->behat_pause_on_fail)) {
$exception = $scope->getTestResult()->getException();
$message = "<colour:lightRed>Scenario failed. ";
$message .= "<colour:lightYellow>Paused for inspection. Press <colour:lightRed>Enter/Return<colour:lightYellow> to continue.<newline>";
$message .= "<colour:lightRed>Exception follows:<newline>";
$message .= trim($exception->getMessage());
behat_util::pause($this->getSession(), $message);
}

// Only run if JS.
if (!$this->running_javascript()) {
return;
}

// Save a screenshot if the step failed.
if (!empty($CFG->behat_faildump_path) &&
$scope->getTestResult()->getResultCode() === Behat\Testwork\Tester\Result\TestResult::FAILED) {
$this->take_screenshot($scope);
}

try {
$this->wait_for_pending_js();
self::$currentstepexception = null;
Expand Down

0 comments on commit b57e984

Please sign in to comment.