* @author Paul Banks * @copyright (c) 2008-2009 Kohana Team * @license http://kohanaphp.com/license */ class Kohana_Tests { static protected $cache = array(); /** * Flag to identify whether the installed version of phpunit * is greater than or equal to 3.5 * @var boolean */ static protected $phpunit_v35 = FALSE; /** * Loads test files if they cannot be found by kohana * @param $class */ static function autoload($class) { $file = str_replace('_', '/', $class); if ($file = Kohana::find_file('tests', $file)) { require_once $file; } } /** * Configures the environment for testing * * Does the following: * * * Loads the phpunit framework (for the web ui) * * Restores exception phpunit error handlers (for cli) * * registeres an autoloader to load test files */ static public function configure_environment($do_whitelist = TRUE, $do_blacklist = TRUE) { // During a webui request we need to manually load PHPUnit if ( ! class_exists('PHPUnit_Util_Filter', FALSE) AND ! function_exists('phpunit_autoload')) { try { include_once 'PHPUnit/Autoload.php'; } catch (ErrorException $e) { include_once 'PHPUnit/Framework.php'; } } // Allow PHPUnit to handle exceptions and errors if (Kohana::$is_cli) { restore_exception_handler(); restore_error_handler(); } spl_autoload_register(array('Kohana_Tests', 'autoload')); Kohana_Tests::$cache = (($cache = Kohana::cache('unittest_whitelist_cache')) === NULL) ? array() : $cache; // As of PHPUnit v3.5 there are slight differences in the way files are black|whitelisted self::$phpunit_v35 = function_exists('phpunit_autoload'); $config = Kohana::config('unittest'); if ($do_whitelist AND $config->use_whitelist) { self::whitelist(); } if ($do_blacklist AND count($config['blacklist'])) { Kohana_Tests::blacklist($config->blacklist); } } /** * Helper function to see if unittest is enabled in the config * * @return boolean */ static function enabled() { $p_environment = Kohana::config('unittest.environment'); $k_environment = Kohana::$environment; return (is_array($p_environment) AND in_array($k_environment, $p_environment)) OR ($k_environment === $p_environment); } /** * Creates the test suite for kohana * * @return PHPUnit_Framework_TestSuite */ static function suite() { static $suite = NULL; if ($suite instanceof PHPUnit_Framework_TestSuite) { return $suite; } $files = Kohana::list_files('tests'); $suite = new PHPUnit_Framework_TestSuite; self::addTests($suite, $files); return $suite; } /** * Add files to test suite $suite * * Uses recursion to scan subdirectories * * @param PHPUnit_Framework_TestSuite $suite The test suite to add to * @param array $files Array of files to test */ static function addTests(PHPUnit_Framework_TestSuite $suite, array $files) { if (self::$phpunit_v35) { $filter = PHP_CodeCoverage_Filter::getInstance(); } foreach ($files as $file) { if (is_array($file)) { self::addTests($suite, $file); } else { // Make sure we only include php files if (is_file($file) AND substr($file, -strlen(EXT)) === EXT) { // The default PHPUnit TestCase extension if ( ! strpos($file, 'TestCase'.EXT)) { $suite->addTestFile($file); } else { require_once($file); } if (isset($filter)) { $filter->addFileToBlacklist($file); } else { PHPUnit_Util_Filter::addFileToFilter($file); } } } } } /** * Blacklist a set of files in PHPUnit code coverage * * @param array A set of files to blacklist */ static public function blacklist(array $blacklist_items) { if (self::$phpunit_v35) { $filter = PHP_CodeCoverage_Filter::getInstance(); foreach ($blacklist_items as $item) { if (is_dir($item)) { $filter->addDirectoryToBlacklist($item); } else { $filter->addFileToBlacklist($item); } } } else { foreach ($blacklist_items as $item) { if (is_dir($item)) { PHPUnit_Util_Filter::addDirectoryToFilter($item); } else { PHPUnit_Util_Filter::addFileToFilter($item); } } } } /** * Sets the whitelist * * If no directories are provided then the function'll load the whitelist * set in the config file * * @param array $directories Optional directories to whitelist */ static public function whitelist(array $directories = NULL) { if (empty($directories)) { $directories = self::get_config_whitelist(); } if (count($directories)) { foreach ($directories as & $directory) { $directory = realpath($directory).'/'; } // Only whitelist the "top" files in the cascading filesystem self::set_whitelist(Kohana::list_files('classes', $directories)); } } /** * Works out the whitelist from the config * Used only on the CLI * * @returns array Array of directories to whitelist */ static protected function get_config_whitelist() { $config = Kohana::config('unittest'); $directories = array(); if ($config->whitelist['app']) { $directories['k_app'] = APPPATH; } if ($modules = $config->whitelist['modules']) { $k_modules = Kohana::modules(); // Have to do this because kohana merges config... // If you want to include all modules & override defaults then TRUE must be the first // value in the modules array of your app/config/unittest file if (array_search(TRUE, $modules, TRUE) === (count($modules) - 1)) { $modules = $k_modules; } elseif (array_search(FALSE, $modules, TRUE) === FALSE) { $modules = array_intersect_key($k_modules, array_combine($modules, $modules)); } else { // modules are disabled $modules = array(); } $directories += $modules; } if ($config->whitelist['system']) { $directories['k_sys'] = SYSPATH; } return $directories; } /** * Recursively whitelists an array of files * * @param array $files Array of files to whitelist */ static protected function set_whitelist($files) { if (self::$phpunit_v35) { $filter = PHP_CodeCoverage_Filter::getInstance(); } foreach ($files as $file) { if (is_array($file)) { self::set_whitelist($file); } else { if ( ! isset(Kohana_Tests::$cache[$file])) { $relative_path = substr($file, strrpos($file, 'classes'.DIRECTORY_SEPARATOR) + 8, -strlen(EXT)); $cascading_file = Kohana::find_file('classes', $relative_path); // The theory is that if this file is the highest one in the cascading filesystem // then it's safe to whitelist Kohana_Tests::$cache[$file] = ($cascading_file === $file); } if (Kohana_Tests::$cache[$file]) { if (isset($filter)) { $filter->addFileToWhitelist($file); } else { PHPUnit_Util_Filter::addFileToWhitelist($file); } } } } } }