_reporter = $reporter; } /** * Get the reporter used for showing results. * @return Sweety_Reporter */ public function getReporter() { return $this->_reporter; } /** * Register a test locator instance. * @param Sweety_TestLocator $locator */ public function registerTestLocator(Sweety_TestLocator $locator) { $this->_testLocators[] = $locator; } /** * Set the regular expression used to filter out certain class names. * @param string $ignoredClassRegex */ public function setIgnoredClassRegex($ignoredClassRegex) { $this->_ignoredClassRegex = $ignoredClassRegex; } /** * Get the filtering regular expression for ignoring certain classes. * @return string */ public function getIgnoredClassRegex() { return $this->_ignoredClassRegex; } /** * Run a single test case with the given name, using the provided output format. * @param string $testName * @param string $format (xml, text or html) * @return int */ public function runTestCase($testName, $format = Sweety_Runner::REPORT_TEXT) { foreach ($this->_testLocators as $locator) { if ($locator->includeTest($testName)) { break; } } $testClass = new ReflectionClass($testName); if ($testClass->getConstructor()) { //We don't want test output to be cached if (!SimpleReporter::inCli()) { header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); } switch ($format) { case Sweety_Runner::REPORT_HTML: $reporter = new HtmlReporter(); break; case Sweety_Runner::REPORT_XML: if (!SimpleReporter::inCli()) { header("Content-Type: text/xml"); //Sigh! SimpleTest (skip() issues). } $reporter = new XmlReporter(); break; case Sweety_Runner::REPORT_TEXT: default: $reporter = new TextReporter(); break; } $test = $testClass->newInstance(); return $test->run($reporter) ? 0 : 1; } return 1; } /** * Use strategies to find tests which are runnable. * @param string[] $dirs * @return string[] */ protected function findTests($dirs = array()) { $tests = array(); foreach ($this->_testLocators as $locator) { $tests += $locator->getTests($dirs); } return $tests; } /** * Parse an XML response from a test case an report to the reporter. * @param string $xml * @param string $testCase */ protected function parseXml($xml, $testCase) { $reporter = $this->_reporter->getReporterFor($testCase); if (!$reporter->isStarted()) { $reporter->start(); } $xml = str_replace("\0", '?', trim($xml)); $xml = preg_replace('/[^\x01-\x7F]/e', 'sprintf("&#%d;", ord("$0"));', $xml); //Do something better? if (!empty($xml)) { $document = @simplexml_load_string($xml); if ($document) { $this->_parseDocument($document, $testCase, $reporter); $reporter->finish(); return; } } $reporter->reportException( 'Invalid XML response: ' . trim(strip_tags( preg_replace('/^\s*<\?xml.+<\/(?:name|pass|fail|exception)>/s', '', $xml) )), $testCase ); } /** * Parse formatted test output. * @param SimpleXMLElement The node containing the output * @param string $path to this test method * @access private */ private function _parseFormatted(SimpleXMLElement $formatted, $path = '', Sweety_Reporter $reporter) { $reporter->reportOutput((string)$formatted, $path); } /** * Parse test output. * @param SimpleXMLElement The node containing the output * @param string $path to this test method * @access private */ private function _parseMessage(SimpleXMLElement $message, $path = '', Sweety_Reporter $reporter) { $reporter->reportOutput((string)$message, $path); } /** * Parse a test failure. * @param SimpleXMLElement The node containing the fail * @param string $path to this test method * @access private */ private function _parseFailure(SimpleXMLElement $failure, $path = '', Sweety_Reporter $reporter) { $reporter->reportFail((string)$failure, $path); } /** * Parse an exception. * @param SimpleXMLElement The node containing the exception * @param string $path to this test method * @access private */ private function _parseException(SimpleXMLElement $exception, $path = '', Sweety_Reporter $reporter) { $reporter->reportException((string)$exception, $path); } /** * Parse a pass. * @param SimpleXMLElement The node containing this pass. * @param string $path to this test method * @access private */ private function _parsePass(SimpleXMLElement $pass, $path = '', Sweety_Reporter $reporter) { $reporter->reportPass((string)$pass, $path); } /** * Parse a single test case. * @param SimpleXMLElement The node containing the test case * @param string $path to this test case * @access private */ private function _parseTestCase(SimpleXMLElement $testCase, $path = '', Sweety_Reporter $reporter) { foreach ($testCase->xpath('./test') as $testMethod) { $testMethodName = (string) $this->_firstNodeValue($testMethod->xpath('./name')); foreach ($testMethod->xpath('./formatted') as $formatted) { $this->_parseFormatted( $formatted, $path . ' -> ' . $testMethodName, $reporter); } foreach ($testMethod->xpath('./message') as $message) { $this->_parseMessage( $message, $path . ' -> ' . $testMethodName, $reporter); } foreach ($testMethod->xpath('./fail') as $failure) { $this->_parseFailure( $failure, $path . ' -> ' . $testMethodName, $reporter); } foreach ($testMethod->xpath('./exception') as $exception) { $this->_parseException( $exception, $path . ' -> ' . $testMethodName, $reporter); } foreach ($testMethod->xpath('./pass') as $pass) { $this->_parsePass($pass, $path . ' -> ' . $testMethodName, $reporter); } $stdout = trim((string) $testMethod); if ($stdout) { $reporter->reportOutput($stdout, $path . ' -> ' . $testMethodName); } } } /** * Parse the results of all tests. * @param SimpleXMLElement The node containing the tests * @param string $path to the tests * @access private */ private function _parseResults(SimpleXMLElement $document, $path = '', Sweety_Reporter $reporter) { $groups = $document->xpath('./group'); if (!empty($groups)) { foreach ($groups as $group) { $groupName = (string) $this->_firstNodeValue($group->xpath('./name')); $this->_parseResults($group, $path . ' -> ' . $groupName, $reporter); } } else { foreach ($document->xpath('./case') as $testCase) { $this->_parseTestCase($testCase, $path, $reporter); } } } /** * Parse the entire SimpleTest XML document from a test case. * @param SimpleXMLElement $document to parse * @param string $path to the test * @access private */ private function _parseDocument(SimpleXMLElement $document, $path = '', Sweety_Reporter $reporter) { if ($everything = $this->_firstNodeValue($document->xpath('/run'))) { $this->_parseResults($everything, $path, $reporter); } elseif ($skip = $this->_firstNodeValue($document->xpath('/skip'))) { $reporter->reportSkip((string) $skip, $path); } } protected function _sort($a, $b) { $apkg = preg_replace('/_[^_]+$/D', '', $a); $bpkg = preg_replace('/_[^_]+$/D', '', $b); if ($apkg == $bpkg) { if ($a == $b) { return 0; } else { return ($a > $b) ? 1 : -1; } } else { return ($apkg > $bpkg) ? 1 : -1; } } private function _firstNodeValue($nodeSet) { $first = array_shift($nodeSet); return $first; } }