3 * A SimpleTest report format for Moodle.
5 * @copyright © 2006 The Open University
6 * @author N.D.Freear@open.ac.uk, T.J.Hunt@open.ac.uk
7 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
8 * @package SimpleTestEx
11 if (!defined('MOODLE_INTERNAL')) {
12 die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
15 require_once($CFG->libdir . '/simpletestlib/reporter.php');
18 * Extended in-browser test displayer. HtmlReporter generates
19 * only failure messages and a pass count. ExHtmlReporter also
20 * generates pass messages and a time-stamp.
22 * @package SimpleTestEx
24 class ExHtmlReporter extends HtmlReporter {
26 // Options set when the class is created.
29 // Lang strings. Set in the constructor.
30 var $strrunonlyfolder;
40 * @param bool $showpasses Whether this reporter should output anything for passes.
42 function ExHtmlReporter($showpasses) {
43 $this->HtmlReporter();
44 $this->showpasses = $showpasses;
46 $this->strrunonlyfolder = $this->get_string('runonlyfolder');
47 $this->strrunonlyfile = $this->get_string('runonlyfile');
48 $this->strseparator = get_separator();
52 * Called when a pass needs to be output.
54 function paintPass($message) {
55 //(Implicitly call grandparent, as parent not implemented.)
56 parent::paintPass($message);
57 if ($this->showpasses) {
58 $this->_paintPassFail('pass', $message);
63 * Called when a fail needs to be output.
65 function paintFail($message) {
66 // Explicitly call grandparent, not parent::paintFail.
67 SimpleScorer::paintFail($message);
68 $this->_paintPassFail('fail', $message, debug_backtrace());
72 * Called when a skip needs to be output.
74 function paintSkip($message) {
75 // Explicitly call grandparent, not parent::paintFail.
76 SimpleScorer::paintSkip($message);
77 $this->_paintPassFail('skip', $message);
81 * Called when an error (uncaught exception or PHP error) needs to be output.
83 function paintError($message) {
84 // Explicitly call grandparent, not parent::paintError.
85 SimpleScorer::paintError($message);
86 $this->_paintPassFail('exception', $message);
90 * Called when a caught exception needs to be output.
92 function paintException($exception) {
93 // Explicitly call grandparent, not parent::paintException.
94 SimpleScorer::paintException($exception);
96 if (is_a($exception, 'moodle_exception') &&
97 !get_string_manager()->string_exists($exception->errorcode, $exception->module)) {
98 $exceptionmessage = 'Exception with missing language string {' .
99 $exception->errorcode . '} from language file {' . $exception->module . '}';
101 if (!empty($exception->a)) {
102 if (is_string($exception->a)) {
103 $data = $exception->a;
106 foreach ((array)$exception->a as $name => $value) {
107 $data[] = $name . ' => [' . $value . ']';
109 $data = implode(', ', $data);
111 $exceptionmessage .= ' with data {' . $data . '}';
115 $exceptionmessage = $exception->getMessage();
117 $message = 'Unexpected exception of type [' . get_class($exception) .
118 '] with message ['. $exceptionmessage .
119 '] in ['. $exception->getFile() .
120 ' line ' . $exception->getLine() . ']';
123 if (!empty($exception->debuginfo)) {
124 $debuginfo = $exception->debuginfo;
127 $this->_paintPassFail('exception', $message, $exception->getTrace(), $debuginfo);
131 * Private method. Used by printPass/Fail/Skip/Error.
133 function _paintPassFail($passorfail, $message, $stacktrace = null, $debuginfo = null) {
134 global $FULLME, $CFG, $OUTPUT;
136 echo $OUTPUT->box_start($passorfail . ' generalbox ');
138 $url = $this->_htmlEntities($this->_stripParameterFromUrl($FULLME, 'path'));
139 echo '<b class="', $passorfail, '">', $this->get_string($passorfail), '</b>: ';
140 $breadcrumb = $this->getTestList();
141 array_shift($breadcrumb);
142 $file = array_shift($breadcrumb);
143 $pathbits = preg_split('/\/|\\\\/', substr($file, strlen($CFG->dirroot) + 1));
144 $file = array_pop($pathbits);
146 foreach ($pathbits as $pathbit) {
147 $folder .= $pathbit . '/';
148 echo "<a href=\"{$url}path=$folder\" title=\"$this->strrunonlyfolder\">$pathbit</a>/";
150 echo "<a href=\"{$url}path=$folder$file\" title=\"$this->strrunonlyfile\">$file</a>";
151 echo $this->strseparator, implode($this->strseparator, $breadcrumb);
153 echo '<br />', $this->_htmlEntities($message), "\n\n";
155 if (!empty($debuginfo)) {
156 print_object('Debug info:');
157 print_object($debuginfo);
162 $interestinglines = 0;
163 $filteredstacktrace = array();
164 foreach ($stacktrace as $frame) {
165 if (empty($frame['file']) || (strpos($frame['file'], 'simpletestlib') === false &&
166 strpos($frame['file'], 'simpletestcoveragelib') === false
167 && strpos($frame['file'], 'report/unittest') === false)) {
168 $filteredstacktrace[] = $frame;
169 $interestinglines += 1;
171 } else if (!$dotsadded) {
172 $filteredstacktrace[] = array('line' => '...', 'file' => '...');
176 if ($interestinglines > 1 || ($passorfail == 'exception' && $interestinglines > 0)) {
177 echo '<div class="notifytiny">' . format_backtrace($filteredstacktrace) . "</div>\n\n";
181 echo $OUTPUT->box_end();
186 * Called when a notice needs to be output.
188 function paintNotice($message) {
189 $this->paintMessage($this->_htmlEntities($message));
193 * Paints a simple supplementary message.
194 * @param string $message Text to display.
196 function paintMessage($message) {
198 if ($this->showpasses) {
199 echo $OUTPUT->box_start();
200 echo '<span class="notice">', $this->get_string('notice'), '</span>: ';
201 $breadcrumb = $this->getTestList();
202 array_shift($breadcrumb);
203 echo implode($this->strseparator, $breadcrumb);
204 echo $this->strseparator, '<br />', $message, "\n";
205 echo $OUTPUT->box_end();
211 * Output anything that should appear above all the test output.
213 function paintHeader($test_name) {
214 $this->timestart = time();
215 // We do this the moodle way instead.
219 * Output anything that should appear below all the test output, e.g. summary information.
221 function paintFooter($test_name) {
222 $summarydata = new stdClass;
223 $summarydata->run = $this->getTestCaseProgress();
224 $summarydata->total = $this->getTestCaseCount();
225 $summarydata->passes = $this->getPassCount();
226 $summarydata->fails = $this->getFailCount();
227 $summarydata->exceptions = $this->getExceptionCount();
229 if ($summarydata->fails == 0 && $summarydata->exceptions == 0) {
234 echo '<div class="unittestsummary ', $status, '">';
235 echo $this->get_string('summary', $summarydata);
238 echo '<div class="performanceinfo">',
239 $this->get_string('runat', userdate($this->timestart)), ' ',
240 $this->get_string('timetakes', format_time(time() - $this->timestart)), ' ',
241 $this->get_string('version', SimpleTestOptions::getVersion()),
246 * Strip a specified parameter from the query string of a URL, if present.
247 * Adds a separator to the end of the URL, so that a new parameter
248 * can easily be appended. For example (assuming $param = 'frog'):
250 * http://example.com/index.php -> http://example.com/index.php?
251 * http://example.com/index.php?frog=1 -> http://example.com/index.php?
252 * http://example.com/index.php?toad=1 -> http://example.com/index.php?toad=1&
253 * http://example.com/index.php?frog=1&toad=1 -> http://example.com/index.php?toad=1&
255 * @param string $url the URL to modify.
256 * @param string $param the parameter to strip from the URL, if present.
258 * @return string The modified URL.
260 function _stripParameterFromUrl($url, $param) {
261 $url = preg_replace('/(\?|&)' . $param . '=[^&]*&?/', '$1', $url);
262 if (strpos($url, '?') === false) {
271 * Look up a lang string in the appropriate file.
273 function get_string($identifier, $a = NULL) {
274 return get_string($identifier, 'simpletest', $a);