コード例 #1
0
ファイル: Core.php プロジェクト: racontemoi/shibuichi
// If this is a dev site, enable php error reporting
// This is necessary to force developers to acknowledge and fix
// notice level errors (you can override this directive in your _config.php)
if (Director::isLive()) {
    if (defined('E_DEPRECATED')) {
        error_reporting(E_ALL ^ E_NOTICE ^ E_DEPRECATED);
    } else {
        error_reporting(E_ALL ^ E_NOTICE);
    }
}
///////////////////////////////////////////////////////////////////////////////
// POST-MANIFEST COMMANDS
/**
 * Load error handlers
 */
Debug::loadErrorHandlers();
///////////////////////////////////////////////////////////////////////////////
// HELPER FUNCTIONS
/**
 * Returns the temporary folder that sapphire/silverstripe should use for its cache files
 * This is loaded into the TEMP_FOLDER define on start up
 */
function getTempFolder()
{
    if (preg_match('/^(.*)\\/sapphire\\/[^\\/]+$/', $_SERVER['SCRIPT_FILENAME'], $matches)) {
        $cachefolder = "silverstripe-cache" . str_replace(array(' ', "/", ":", "\\"), "-", $matches[1]);
    } else {
        $cachefolder = "silverstripe-cache";
    }
    $ssTmp = BASE_PATH . "/silverstripe-cache";
    if (@file_exists($ssTmp)) {
コード例 #2
0
ファイル: TestRunner.php プロジェクト: Raiser/Praktikum
 /**
  * @param array $classList
  * @param boolean $coverage
  */
 function runTests($classList, $coverage = false)
 {
     $startTime = microtime(true);
     // XDEBUG seem to cause problems with test execution :-(
     if (function_exists('xdebug_disable')) {
         xdebug_disable();
     }
     ini_set('max_execution_time', 0);
     $this->setUp();
     // Optionally skip certain tests
     $skipTests = array();
     if ($this->request->getVar('SkipTests')) {
         $skipTests = explode(',', $this->request->getVar('SkipTests'));
     }
     $classList = array_diff($classList, $skipTests);
     // run tests before outputting anything to the client
     $suite = new PHPUnit_Framework_TestSuite();
     natcasesort($classList);
     foreach ($classList as $className) {
         // Ensure that the autoloader pulls in the test class, as PHPUnit won't know how to do this.
         class_exists($className);
         $suite->addTest(new SapphireTestSuite($className));
     }
     // Remove the error handler so that PHPUnit can add its own
     restore_error_handler();
     /*, array("reportDirectory" => "/Users/sminnee/phpunit-report")*/
     if (Director::is_cli()) {
         $reporter = new CliTestReporter();
     } else {
         $reporter = new SapphireTestReporter();
     }
     self::$default_reporter->writeHeader("Sapphire Test Runner");
     if (count($classList) > 1) {
         self::$default_reporter->writeInfo("All Tests", "Running test cases: ", implode(", ", $classList));
     } else {
         self::$default_reporter->writeInfo($classList[0], "");
     }
     $results = new PHPUnit_Framework_TestResult();
     $results->addListener($reporter);
     if ($coverage === true) {
         foreach (self::$coverage_filter_dirs as $dir) {
             PHPUnit_Util_Filter::addDirectoryToFilter(BASE_PATH . '/' . $dir);
         }
         $results->collectCodeCoverageInformation(true);
         $suite->run($results);
         if (!file_exists(ASSETS_PATH . '/coverage-report')) {
             mkdir(ASSETS_PATH . '/coverage-report');
         }
         PHPUnit_Util_Report::render($results, ASSETS_PATH . '/coverage-report/');
         $coverageApp = ASSETS_PATH . '/coverage-report/' . preg_replace('/[^A-Za-z0-9]/', '_', preg_replace('/(\\/$)|(^\\/)/', '', Director::baseFolder())) . '.html';
         $coverageTemplates = ASSETS_PATH . '/coverage-report/' . preg_replace('/[^A-Za-z0-9]/', '_', preg_replace('/(\\/$)|(^\\/)/', '', realpath(TEMP_FOLDER))) . '.html';
         echo "<p>Coverage reports available here:<ul>\n\t\t\t\t<li><a href=\"{$coverageApp}\">Coverage report of the application</a></li>\n\t\t\t\t<li><a href=\"{$coverageTemplates}\">Coverage report of the templates</a></li>\n\t\t\t</ul>";
     } else {
         $suite->run($results);
     }
     if (!Director::is_cli()) {
         echo '<div class="trace">';
     }
     $reporter->writeResults();
     $endTime = microtime(true);
     if (Director::is_cli()) {
         echo "\n\nTotal time: " . round($endTime - $startTime, 3) . " seconds\n";
     } else {
         echo "<p>Total time: " . round($endTime - $startTime, 3) . " seconds</p>\n";
     }
     if (!Director::is_cli()) {
         echo '</div>';
     }
     // Put the error handlers back
     Debug::loadErrorHandlers();
     if (!Director::is_cli()) {
         self::$default_reporter->writeFooter();
     }
     $this->tearDown();
     // Todo: we should figure out how to pass this data back through Director more cleanly
     if (Director::is_cli() && $results->failureCount() + $results->errorCount() > 0) {
         exit(2);
     }
 }
コード例 #3
0
 /**
  * Tries to logon to the LDAP server with given id and password
  *
  * @access public
  *
  * @param string $source          The Authentication source to be used
  * @param string $external_anchor The ID entered
  * @param string $external_passwd The password of the user
  *
  * @return mixed    Account details if succesful , false if not 
  */
 public function Authenticate($source, $external_anchor, $external_passwd)
 {
     // A password should have some lenght. An empty password will result
     // in a succesfull anonymous bind. A password should not be all spaces
     if (strlen(trim($external_passwd)) == 0) {
         ExternalAuthenticator::setAuthMessage(_t('LDAP_Authenticator.NoPasswd', 'Please enter a password'));
         return false;
     }
     // Do we support password expiration?
     $expire = ExternalAuthenticator::getOption($source, 'passwd_expiration');
     $result = self::Connect($source, $external_anchor);
     if (is_string($result)) {
         ExternalAuthenticator::setAuthMessage($result);
         return false;
     }
     $dn = self::findDN($source, ExternalAuthenticator::getOption($source, 'attribute'), $external_anchor);
     if (is_bool($dn)) {
         @ldap_close(self::$ds);
         ExternalAuthenticator::setAuthMessage(_t('ExternalAuthenticator.Failed'));
         return false;
     }
     // Restore the default error handler. We dont want a red bordered
     // screen on error, but a civilized message to the user
     restore_error_handler();
     $success = false;
     //Initialize the result of the authentication
     ExternalAuthenticator::AuthLog($external_anchor . '.ldap - Binding to LDAP as ' . $dn);
     $bind = @ldap_bind(self::$ds, $dn, $external_passwd);
     if ($bind != false) {
         ExternalAuthenticator::AuthLog($external_anchor . '.ldap - LDAP accepted password for ' . $dn);
         $accountdetails = self::lookupDetails($source, $dn, $external_anchor);
         if (!is_null($expire) && $expire) {
             ExternalAuthenticator::AuthLog($external_anchor . '.ldap - Check if password has expired');
             // Reset the SilverStripe error handler
             Debug::loadErrorHandlers();
             // Do some calculations on the attributes to convert them
             // to the interval [now]-[expires at]
             if ($accountdetails['shadowmax']['value'] && $accountdetails['shadowlastchange']['value'] && $accountdetails['shadowwarning']['value']) {
                 $today = floor(time() / 86400);
                 $warnday = $accountdetails['shadowlastchange']['value'] + $accountdetails['shadowmax']['value'] - $accountdetails['shadowwarning']['value'];
                 $toexpire = $accountdetails['shadowlastchange']['value'] + $accountdetails['shadowmax']['value'] - $today;
                 ExternalAuthenticator::AuthLog($external_anchor . '.ldap - ' . $toexpire . ' before password expires ' . $towarn . ' days before warning');
                 // Out of luck. His password has expired.
                 if ($toexpire < 0) {
                     ExternalAuthenticator::setAuthMessage(_t('LDAP_Authenticator.Expired', 'Your password has expired'));
                     ExternalAuthenticator::AuthLog($external_anchor . '.ldap - LDAP Authentication FAILED due to expired password');
                 } else {
                     ExternalAuthenticator::AuthLog($external_anchor . '.ldap - LDAP Authentication success');
                     $success = array('firstname' => $accountdetails['firstname']['value'], 'surname' => $accountdetails['surname']['value'], 'email' => $accountdetails['email']['value'], 'group' => $accountdetails['group']['value']);
                     // Lets be civilized and warn the user that he should
                     // change his password soon
                     if ($today >= $warnday) {
                         ExternalAuthenticator::setAuthMessage(sprintf(_t('LDAP_Authenticator.WillExpire', 'Your password expires in %d days'), $toexpire));
                     }
                 }
             } else {
                 ExternalAuthenticator::AuthLog($external_anchor . '.ldap - LDAP password expiry enabled, but attributes not set; IGNORING');
                 ExternalAuthenticator::AuthLog($external_anchor . '.ldap - LDAP Authentication success');
                 $success = array('firstname' => $accountdetails['firstname']['value'], 'surname' => $accountdetails['surname']['value'], 'email' => $accountdetails['email']['value'], 'group' => $accountdetails['group']['value']);
             }
         } else {
             ExternalAuthenticator::AuthLog($external_anchor . '.ldap - Password expiry not enabled');
             // Reset the SilverStripe error handler
             Debug::loadErrorHandlers();
             ExternalAuthenticator::AuthLog($external_anchor . '.ldap - LDAP Authentication success');
             $success = array('firstname' => $accountdetails['firstname']['value'], 'surname' => $accountdetails['surname']['value'], 'email' => $accountdetails['email']['value'], 'group' => $accountdetails['group']['value']);
         }
     } else {
         // Reset the SilverStripe error handler
         Debug::loadErrorHandlers();
         ExternalAuthenticator::AuthLog($external_anchor . '.ldap - LDAP authentication for ' . $dn . ' failed');
         ExternalAuthenticator::setAuthMessage(_t('ExternalAuthenticator.Failed'));
         $success = false;
     }
     @ldap_close(self::$ds);
     return $success;
 }
コード例 #4
0
ファイル: TestRunner.php プロジェクト: hemant-chakka/awss
 /**
  * @param array $classList
  * @param boolean $coverage
  */
 public function runTests($classList, $coverage = false)
 {
     $startTime = microtime(true);
     // disable xdebug, as it messes up test execution
     if (function_exists('xdebug_disable')) {
         xdebug_disable();
     }
     ini_set('max_execution_time', 0);
     $this->setUp();
     // Optionally skip certain tests
     $skipTests = array();
     if ($this->request->getVar('SkipTests')) {
         $skipTests = explode(',', $this->request->getVar('SkipTests'));
     }
     $abstractClasses = array();
     foreach ($classList as $className) {
         // Ensure that the autoloader pulls in the test class, as PHPUnit won't know how to do this.
         class_exists($className);
         $reflection = new ReflectionClass($className);
         if ($reflection->isAbstract()) {
             array_push($abstractClasses, $className);
         }
     }
     $classList = array_diff($classList, $skipTests, $abstractClasses);
     // run tests before outputting anything to the client
     $suite = new PHPUnit_Framework_TestSuite();
     natcasesort($classList);
     foreach ($classList as $className) {
         // Ensure that the autoloader pulls in the test class, as PHPUnit won't know how to do this.
         class_exists($className);
         $suite->addTest(new SapphireTestSuite($className));
     }
     // Remove the error handler so that PHPUnit can add its own
     restore_error_handler();
     self::$default_reporter->writeHeader("SilverStripe Test Runner");
     if (count($classList) > 1) {
         self::$default_reporter->writeInfo("All Tests", "Running test cases: ", implode(", ", $classList));
     } elseif (count($classList) == 1) {
         self::$default_reporter->writeInfo($classList[0], '');
     } else {
         // border case: no tests are available.
         self::$default_reporter->writeInfo('', '');
     }
     // perform unit tests (use PhpUnitWrapper or derived versions)
     $phpunitwrapper = PhpUnitWrapper::inst();
     $phpunitwrapper->setSuite($suite);
     $phpunitwrapper->setCoverageStatus($coverage);
     // Make sure TearDown is called (even in the case of a fatal error)
     $self = $this;
     register_shutdown_function(function () use($self) {
         $self->tearDown();
     });
     $phpunitwrapper->runTests();
     // get results of the PhpUnitWrapper class
     $reporter = $phpunitwrapper->getReporter();
     $results = $phpunitwrapper->getFrameworkTestResults();
     if (!Director::is_cli()) {
         echo '<div class="trace">';
     }
     $reporter->writeResults();
     $endTime = microtime(true);
     if (Director::is_cli()) {
         echo "\n\nTotal time: " . round($endTime - $startTime, 3) . " seconds\n";
     } else {
         echo "<p class=\"total-time\">Total time: " . round($endTime - $startTime, 3) . " seconds</p>\n";
     }
     if (!Director::is_cli()) {
         echo '</div>';
     }
     // Put the error handlers back
     Debug::loadErrorHandlers();
     if (!Director::is_cli()) {
         self::$default_reporter->writeFooter();
     }
     $this->tearDown();
     // Todo: we should figure out how to pass this data back through Director more cleanly
     if (Director::is_cli() && $results->failureCount() + $results->errorCount() > 0) {
         exit(2);
     }
 }
コード例 #5
0
	function runTests($classList, $coverage = false) {
		// XDEBUG seem to cause problems with test execution :-(
		if(function_exists('xdebug_disable')) xdebug_disable();
		
		ini_set('max_execution_time', 0);		
		
		$this->setUp();
		
		// run tests before outputting anything to the client
		$suite = new PHPUnit_Framework_TestSuite();
		foreach($classList as $className) {
			// Ensure that the autoloader pulls in the test class, as PHPUnit won't know how to do this.
			class_exists($className);
			$suite->addTest(new PHPUnit_Framework_TestSuite($className));
		}

		// Remove the error handler so that PHPUnit can add its own
		restore_error_handler();

		/*, array("reportDirectory" => "/Users/sminnee/phpunit-report")*/
		if(Director::is_cli()) $reporter = new CliTestReporter();
		else $reporter = new SapphireTestReporter();

		self::$default_reporter->writeHeader("Sapphire Test Runner");
		if (count($classList) > 1) { 
			self::$default_reporter->writeInfo("All Tests", "Running test cases: " . implode(", ", $classList));
		} else {
			self::$default_reporter->writeInfo($classList[0], "");
		}
		
		$results = new PHPUnit_Framework_TestResult();		
		$results->addListener($reporter);

		if($coverage) {
			$results->collectCodeCoverageInformation(true);
			$suite->run($results);

			if(!file_exists(ASSETS_PATH . '/coverage-report')) mkdir(ASSETS_PATH . '/coverage-report');
			PHPUnit_Util_Report::render($results, ASSETS_PATH . '/coverage-report/');
			$coverageApp = ASSETS_PATH . '/coverage-report/' . preg_replace('/[^A-Za-z0-9]/','_',preg_replace('/(\/$)|(^\/)/','',Director::baseFolder())) . '.html';
			$coverageTemplates = ASSETS_PATH . '/coverage-report/' . preg_replace('/[^A-Za-z0-9]/','_',preg_replace('/(\/$)|(^\/)/','',realpath(TEMP_FOLDER))) . '.html';
			echo "<p>Coverage reports available here:<ul>
				<li><a href=\"$coverageApp\">Coverage report of the application</a></li>
				<li><a href=\"$coverageTemplates\">Coverage report of the templates</a></li>
			</ul>";
		} else {
			$suite->run($results);
		}
		
		if(!Director::is_cli()) echo '<div class="trace">';
		$reporter->writeResults();
		
		if(!Director::is_cli()) echo '</div>';
		
		// Put the error handlers back
		Debug::loadErrorHandlers();
		
		if(!Director::is_cli()) self::$default_reporter->writeFooter();
		
		$this->tearDown();
		
		// Todo: we should figure out how to pass this data back through Director more cleanly
		if(Director::is_cli() && ($results->failureCount() + $results->errorCount()) > 0) exit(2);
	}
コード例 #6
0
 /**
  * 
  * Somewhat messy and god-method, but this override exists because
  * 
  * a) we want to change the configuration of items from the user-set TESTING_CONFIG 
  * variable if it exists
  * 
  * b) PhpUnitWrapper is referenced with explicit class settings, rather than allowing us to 
  * override as we like (to change code coverage behaviour). 
  * 
  * @global type TESTING_CONFIG
  * @global type $TESTING_CONFIG
  * @global type $databaseConfig
  * @param type $classList
  * @param type $coverage
  * @throws Exception
  */
 function runTests($classList, $coverage = false)
 {
     global $TESTING_CONFIG;
     $startTime = microtime(true);
     Config::inst()->update('Director', 'environment_type', 'dev');
     if (isset($TESTING_CONFIG['database']) && $TESTING_CONFIG['database'] != 'silverstripe_testing') {
         if (class_exists("Multisites")) {
             Multisites::inst()->resetCurrentSite();
         }
         global $databaseConfig;
         $newConfig = $databaseConfig;
         $newConfig = array_merge($databaseConfig, $TESTING_CONFIG);
         $newConfig['memory'] = isset($TESTING_CONFIG['memory']) ? $TESTING_CONFIG['memory'] : true;
         $newDbName = $TESTING_CONFIG['database'];
         $type = isset($newConfig['type']) ? $newConfig['type'] : 'MySQL';
         Debug::message("Connecting to new {$type} database {$TESTING_CONFIG['database']} as defined by testing config");
         DB::connect($newConfig);
         if (!DB::getConn()->databaseExists($newDbName)) {
             DB::getConn()->createDatabase($newDbName);
         }
         if (!DB::getConn()->selectDatabase($newDbName)) {
             throw new Exception("Could not find database to use for testing");
         }
         if ($newConfig['memory']) {
             Debug::message("Using in memory database");
         }
         $dbadmin = new DatabaseAdmin();
         if (isset($_REQUEST['clear']) && $_REQUEST['clear'] == 0) {
         } else {
             $dbadmin->clearAllData();
         }
         if (!(isset($_REQUEST['build']) && $_REQUEST['build'] == 0)) {
             Debug::message("Executing dev/build as requested");
             $dbadmin->doBuild(true);
         }
     }
     // XDEBUG seem to cause problems with test execution :-(
     if (function_exists('xdebug_disable')) {
         xdebug_disable();
     }
     ini_set('max_execution_time', 0);
     $this->setUp();
     // Optionally skip certain tests
     $skipTests = array();
     if ($this->request->getVar('SkipTests')) {
         $skipTests = explode(',', $this->request->getVar('SkipTests'));
     }
     $abstractClasses = array();
     foreach ($classList as $className) {
         // Ensure that the autoloader pulls in the test class, as PHPUnit won't know how to do this.
         class_exists($className);
         $reflection = new ReflectionClass($className);
         if ($reflection->isAbstract()) {
             array_push($abstractClasses, $className);
         }
     }
     $classList = array_diff($classList, $skipTests);
     // run tests before outputting anything to the client
     $suite = new PHPUnit_Framework_TestSuite();
     natcasesort($classList);
     foreach ($classList as $className) {
         // Ensure that the autoloader pulls in the test class, as PHPUnit won't know how to do this.
         class_exists($className);
         $suite->addTest(new SapphireTestSuite($className));
     }
     // Remove the error handler so that PHPUnit can add its own
     restore_error_handler();
     // CUSTOMISATION
     if (Director::is_cli()) {
         if ($reporterClass = $this->request->requestVar('reporter')) {
             $clazz = $reporterClass;
         } else {
             if (isset($TESTING_CONFIG['reporter'])) {
                 $clazz = $TESTING_CONFIG['reporter'];
             } else {
                 $clazz = "CliTestReporter";
             }
         }
     } else {
         $clazz = "SapphireTestReporter";
     }
     // END CUSTOMISATION
     // CUSTOMISATION
     $outputFile = null;
     if ($TESTING_CONFIG['logfile']) {
         $outputFile = BASE_PATH . '/' . $TESTING_CONFIG['logfile'];
     }
     $reporter = new $clazz($outputFile);
     $default = self::$default_reporter;
     self::$default_reporter->writeHeader("Sapphire Test Runner");
     if (count($classList) > 1) {
         self::$default_reporter->writeInfo("All Tests", "Running test cases: " . implode(",", $classList));
     } else {
         self::$default_reporter->writeInfo($classList[0], "");
     }
     $results = new PHPUnit_Framework_TestResult();
     $results->addListener($reporter);
     if ($coverage === true) {
         $coverer = $this->getCodeCoverage();
         $results->setCodeCoverage($coverer);
         $suite->run($results);
         $writer = new PHP_CodeCoverage_Report_HTML();
         $writer->process($coverer, Director::baseFolder() . '/ssautesting/html/code-coverage-report');
     } else {
         $suite->run($results);
     }
     if (!Director::is_cli()) {
         echo '<div class="trace">';
     }
     if (method_exists($reporter, 'writeResults')) {
         $reporter->writeResults($outputFile);
     } else {
         $reporter->flush();
     }
     // END CUSTOMISATION
     $endTime = microtime(true);
     if (Director::is_cli()) {
         echo "\n\nTotal time: " . round($endTime - $startTime, 3) . " seconds\n";
     } else {
         echo "<p>Total time: " . round($endTime - $startTime, 3) . " seconds</p>\n";
     }
     if (!Director::is_cli()) {
         echo '</div>';
     }
     // Put the error handlers back
     Debug::loadErrorHandlers();
     if (!Director::is_cli()) {
         self::$default_reporter->writeFooter();
     }
     $this->tearDown();
     // Todo: we should figure out how to pass this data back through Director more cleanly
     if (Director::is_cli() && $results->failureCount() + $results->errorCount() > 0) {
         exit(2);
     }
 }
コード例 #7
0
 function runTests($classList, $coverage = false)
 {
     global $TESTING_CONFIG;
     $startTime = microtime(true);
     Config::inst()->update('Director', 'environment_type', 'dev');
     if (isset($TESTING_CONFIG['database']) && $TESTING_CONFIG['database'] != 'silverstripe_testing') {
         global $databaseConfig;
         $newConfig = $databaseConfig;
         $newConfig = array_merge($databaseConfig, $TESTING_CONFIG);
         $newConfig['memory'] = isset($TESTING_CONFIG['memory']) ? $TESTING_CONFIG['memory'] : true;
         $type = isset($newConfig['type']) ? $newConfig['type'] : 'MySQL';
         Debug::message("Connecting to new database {$type} as defined by testing config");
         DB::connect($newConfig);
         DB::getConn()->selectDatabase($TESTING_CONFIG['database']);
         $dbadmin = new DatabaseAdmin();
         $dbadmin->clearAllData();
         if (!(isset($_REQUEST['build']) && $_REQUEST['build'] == 0)) {
             $dbadmin->doBuild(true);
         }
     }
     // XDEBUG seem to cause problems with test execution :-(
     if (function_exists('xdebug_disable')) {
         xdebug_disable();
     }
     ini_set('max_execution_time', 0);
     $this->setUp();
     // Optionally skip certain tests
     $skipTests = array();
     if ($this->request->getVar('SkipTests')) {
         $skipTests = explode(',', $this->request->getVar('SkipTests'));
     }
     $classList = array_diff($classList, $skipTests);
     // run tests before outputting anything to the client
     $suite = new PHPUnit_Framework_TestSuite();
     natcasesort($classList);
     foreach ($classList as $className) {
         // Ensure that the autoloader pulls in the test class, as PHPUnit won't know how to do this.
         class_exists($className);
         $suite->addTest(new SapphireTestSuite($className));
     }
     // Remove the error handler so that PHPUnit can add its own
     restore_error_handler();
     // CUSTOMISATION
     if (Director::is_cli()) {
         if (isset($TESTING_CONFIG['reporter'])) {
             $clazz = $TESTING_CONFIG['reporter'];
         } else {
             $clazz = "CliTestReporter";
         }
     } else {
         $clazz = "SapphireTestReporter";
     }
     // END CUSTOMISATION
     $reporter = new $clazz();
     $default = self::$default_reporter;
     self::$default_reporter->writeHeader("Sapphire Test Runner");
     if (count($classList) > 1) {
         self::$default_reporter->writeInfo("All Tests", "Running test cases: " . implode(",", $classList));
     } else {
         self::$default_reporter->writeInfo($classList[0], "");
     }
     $results = new PHPUnit_Framework_TestResult();
     $results->addListener($reporter);
     if ($coverage === true) {
         $results->collectCodeCoverageInformation(true);
         $suite->run($results);
         if (!file_exists(ASSETS_PATH . '/coverage-report')) {
             mkdir(ASSETS_PATH . '/coverage-report');
         }
         PHPUnit_Util_Report::render($results, ASSETS_PATH . '/coverage-report/');
         $coverageApp = ASSETS_PATH . '/coverage-report/' . preg_replace('/[^A-Za-z0-9]/', '_', preg_replace('/(\\/$)|(^\\/)/', '', Director::baseFolder())) . '.html';
         $coverageTemplates = ASSETS_PATH . '/coverage-report/' . preg_replace('/[^A-Za-z0-9]/', '_', preg_replace('/(\\/$)|(^\\/)/', '', realpath(TEMP_FOLDER))) . '.html';
         echo "<p>Coverage reports available here:<ul>\n\t\t\t\t<li><a href=\"{$coverageApp}\">Coverage report of the application</a></li>\n\t\t\t\t<li><a href=\"{$coverageTemplates}\">Coverage report of the templates</a></li>\n\t\t\t</ul>";
     } else {
         $suite->run($results);
     }
     if (!Director::is_cli()) {
         echo '<div class="trace">';
     }
     // CUSTOMISATION
     $outputFile = null;
     if ($TESTING_CONFIG['logfile']) {
         $outputFile = BASE_PATH . '/' . $TESTING_CONFIG['logfile'];
     }
     $reporter->writeResults($outputFile);
     // END CUSTOMISATION
     $endTime = microtime(true);
     if (Director::is_cli()) {
         echo "\n\nTotal time: " . round($endTime - $startTime, 3) . " seconds\n";
     } else {
         echo "<p>Total time: " . round($endTime - $startTime, 3) . " seconds</p>\n";
     }
     if (!Director::is_cli()) {
         echo '</div>';
     }
     // Put the error handlers back
     Debug::loadErrorHandlers();
     if (!Director::is_cli()) {
         self::$default_reporter->writeFooter();
     }
     $this->tearDown();
     // Todo: we should figure out how to pass this data back through Director more cleanly
     if (Director::is_cli() && $results->failureCount() + $results->errorCount() > 0) {
         exit(2);
     }
 }