diff --git a/lib/behat/lib.php b/lib/behat/lib.php index b1a747a31f3..70543f04184 100644 --- a/lib/behat/lib.php +++ b/lib/behat/lib.php @@ -70,3 +70,60 @@ function behat_error($errorcode, $text = '') { testing_error($errorcode, $text); } +/** + * PHP errors handler to use when running behat tests. + * + * Adds specific CSS classes to identify + * the messages. + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param array $errcontext + * @return bool + */ +function behat_error_handler($errno, $errstr, $errfile, $errline, $errcontext) { + global $OUTPUT; + + // Only after something has been writen. + if (!$OUTPUT->has_started()) { + return false; + } + + // If is preceded by an @ we don't show it. + if (!error_reporting()) { + return true; + } + + // Using the default one in case there is a fatal catchable error. + default_error_handler($errno, $errstr, $errfile, $errline, $errcontext); + + switch ($errno) { + case E_USER_ERROR: + $errnostr = 'Fatal error'; + break; + case E_WARNING: + case E_USER_WARNING: + $errnostr = 'Warning'; + break; + case E_NOTICE: + case E_USER_NOTICE: + case E_STRICT: + $errnostr = 'Notice'; + break; + case E_RECOVERABLE_ERROR: + $errnostr = 'Catchable'; + break; + default: + $errnostr = 'Unknown error type'; + } + + // Wrapping the output. + echo '
' . PHP_EOL; + echo "$errnostr: $errstr in $errfile on line $errline" . PHP_EOL; + echo '
'; + + // Also use the internal error handler so we keep the usual behaviour. + return false; +} diff --git a/lib/setup.php b/lib/setup.php index a5db7803e93..213ac7030ed 100644 --- a/lib/setup.php +++ b/lib/setup.php @@ -455,6 +455,12 @@ if (!PHPUNIT_TEST or PHPUNIT_UTIL) { set_error_handler('default_error_handler', E_ALL | E_STRICT); } +// Acceptance tests needs special output to capture the errors. +if (!empty($CFG->originaldataroot) && !defined('BEHAT_RUNNING')) { + require_once(__DIR__ . '/behat/lib.php'); + set_error_handler('behat_error_handler', E_ALL | E_STRICT); +} + // If there are any errors in the standard libraries we want to know! error_reporting(E_ALL | E_STRICT); diff --git a/lib/tests/behat/behat_hooks.php b/lib/tests/behat/behat_hooks.php index a8893482ae7..4fb968e5e93 100644 --- a/lib/tests/behat/behat_hooks.php +++ b/lib/tests/behat/behat_hooks.php @@ -132,6 +132,9 @@ class behat_hooks extends behat_base { // Assing valid data to admin user (some generator-related code needs a valid user). $user = $DB->get_record('user', array('username' => 'admin')); session_set_user($user); + + // Start always in the the homepage. + $this->getSession()->visit($this->locate_path('/')); } /** @@ -178,4 +181,73 @@ class behat_hooks extends behat_base { } } + /** + * Internal step definition to find exceptions, debugging() messages and PHP debug messages. + * + * Part of behat_hooks class as is part of the testing framework, is auto-executed + * after each step so no features will splicitly use it. + * + * @Given /^I look for exceptions$/ + * @see Moodle\BehatExtension\Tester\MoodleStepTester + */ + public function i_look_for_exceptions() { + + // Exceptions. + if ($errormsg = $this->getSession()->getPage()->find('css', '.errorbox p.errormessage')) { + + // Getting the debugging info and the backtrace. + $errorinfoboxes = $this->getSession()->getPage()->findAll('css', 'div.notifytiny'); + $errorinfo = $this->get_debug_text($errorinfoboxes[0]->getHtml()) . "\n" . + $this->get_debug_text($errorinfoboxes[1]->getHtml()); + + $msg = "Moodle exception: " . $errormsg->getText() . "\n" . $errorinfo; + throw new \Exception(html_entity_decode($msg)); + } + + // Debugging messages. + if ($debuggingmessages = $this->getSession()->getPage()->findAll('css', '.debuggingmessage')) { + $msgs = array(); + foreach ($debuggingmessages as $debuggingmessage) { + $msgs[] = $this->get_debug_text($debuggingmessage->getHtml()); + } + $msg = "debugging() message/s found:\n" . implode("\n", $msgs); + throw new \Exception(html_entity_decode($msg)); + } + + // PHP debug messages. + if ($phpmessages = $this->getSession()->getPage()->findAll('css', '.phpdebugmessage')) { + + $msgs = array(); + foreach ($phpmessages as $phpmessage) { + $msgs[] = $this->get_debug_text($phpmessage->getHtml()); + } + $msg = "PHP debug message/s found:\n" . implode("\n", $msgs); + throw new \Exception(html_entity_decode($msg)); + } + + // Any other backtrace. + $backtracespattern = '/(line [0-9]* of [^:]*: call to [\->&;:a-zA-Z_\x7f-\xff][\->&;:a-zA-Z0-9_\x7f-\xff]*)/'; + if (preg_match_all($backtracespattern, $this->getSession()->getPage()->getContent(), $backtraces)) { + $msgs = array(); + foreach ($backtraces[0] as $backtrace) { + $msgs[] = $backtrace . '()'; + } + $msg = "Other backtraces found:\n" . implode("\n", $msgs); + throw new \Exception(htmlentities($msg)); + } + } + + /** + * Converts HTML tags to line breaks to display the info in CLI + * + * @param string $html + * @return string + */ + protected function get_debug_text($html) { + + // Replacing HTML tags for new lines and keeping only the text. + $notags = preg_replace('/<+\s*\/*\s*([A-Z][A-Z0-9]*)\b[^>]*\/*\s*>*/i', "\n", $html); + return preg_replace("/(\n)+/s", "\n", $notags); + } + } diff --git a/lib/weblib.php b/lib/weblib.php index b61f1bce46f..1985b7ec7f7 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -2792,7 +2792,7 @@ function debugging($message = '', $level = DEBUG_NORMAL, $backtrace = null) { if (CLI_SCRIPT) { echo "++ $message ++\n$from"; } else { - echo '
' . $message . $from . '
'; + echo '
' . $message . $from . '
'; } } else {