* @author Paul Banks * @copyright (c) 2008-2009 Kohana Team * @license http://kohanaphp.com/license */ Class Controller_UnitTest extends Controller_Template { /** * Whether the archive module is available * @var boolean */ protected $cc_archive_available = FALSE; /** * Unittest config * @var Kohana_Config */ protected $config = NULL; /** * The uri by which the report uri will be executed * @var string */ protected $report_uri = ''; /** * The uri by which the run action will be executed * @var string */ protected $run_uri = ''; /** * Is the XDEBUG extension loaded? * @var boolean */ protected $xdebug_loaded = FALSE; /** * Template * @var string */ public $template = 'unittest/layout'; /** * Loads test suite */ public function before() { parent::before(); if ( ! Kohana_Tests::enabled()) { // Pretend this is a normal 404 error... $this->status = 404; throw new Kohana_Request_Exception('Unable to find a route to match the URI: :uri', array(':uri' => $this->request->uri)); } // Prevent the whitelist from being autoloaded, but allow the blacklist // to be loaded Kohana_Tests::configure_environment(FALSE); $this->config = Kohana::config('unittest'); // This just stops some very very long lines $route = Route::get('unittest'); $this->report_uri = $route->uri(array('action' => 'report')); $this->run_uri = $route->uri(array('action' => 'run')); // Switch used to disable cc settings $this->xdebug_loaded = extension_loaded('xdebug'); $this->cc_archive_enabled = class_exists('Archive'); Kohana_View::set_global('xdebug_enabled', $this->xdebug_loaded); Kohana_View::set_global('cc_archive_enabled', $this->cc_archive_enabled); } /** * Handles index page for /unittest/ and /unittest/index/ */ public function action_index() { $this->template->body = View::factory('unittest/index') ->set('run_uri', $this->run_uri) ->set('report_uri', $this->report_uri) ->set('whitelistable_items', $this->get_whitelistable_items()) ->set('groups', $this->get_groups_list(Kohana_Tests::suite())); } /** * Handles report generation */ public function action_report() { // Fairly foolproof if ( ! $this->config->cc_report_path AND ! class_exists('Archive')) { throw new Kohana_Exception('Cannot generate report'); } // We don't want to use the HTML layout, we're sending the user 100111011100110010101100 $this->auto_render = FALSE; $suite = Kohana_Tests::suite(); $temp_path = rtrim($this->config->temp_path, '/').'/'; $group = (array) Arr::get($_GET, 'group', array()); // Stop unittest from interpretting "all groups" as "no groups" if (empty($group) OR empty($group[0])) { $group = array(); } if (Arr::get($_GET, 'use_whitelist', FALSE)) { $this->whitelist(Arr::get($_GET, 'whitelist', array())); } $runner = new Kohana_Unittest_Runner($suite); // If the user wants to download a report if ($this->cc_archive_enabled AND Arr::get($_GET, 'archive') === '1') { // $report is the actual directory of the report, // $folder is the name component of directory list($report, $folder) = $runner->generate_report($group, $temp_path); $archive = Archive::factory('zip'); // TODO: Include the test results? $archive->add($report, 'report', TRUE); $filename = $folder.'.zip'; $archive->save($temp_path.$filename); // It'd be nice to clear up afterwards but by deleting the report dir we corrupt the archive // And once the archive has been sent to the user Request stops the script so we can't delete anything // It'll be up to the user to delete files periodically $this->request->send_file($temp_path.$filename, $filename); } else { $folder = trim($this->config->cc_report_path, '/').'/'; $path = DOCROOT.$folder; if ( ! file_exists($path)) { throw new Kohana_Exception('Report directory :dir does not exist', array(':dir' => $path)); } if ( ! is_writable($path)) { throw new Kohana_Exception('Script doesn\'t have permission to write to report dir :dir ', array(':dir' => $path)); } $runner->generate_report($group, $path, FALSE); $this->request->redirect(URL::base(FALSE, TRUE).$folder.'index.html'); } } /** * Handles test running interface */ public function action_run() { $this->template->body = View::factory('unittest/results'); // Get the test suite and work out which groups we're testing $suite = Kohana_Tests::suite(); $group = (array) Arr::get($_GET, 'group', array()); // Stop phpunit from interpretting "all groups" as "no groups" if (empty($group) OR empty($group[0])) { $group = array(); } // Only collect code coverage if the user asked for it $collect_cc = (bool) Arr::get($_GET, 'collect_cc', FALSE); if ($collect_cc AND Arr::get($_GET, 'use_whitelist', FALSE)) { $whitelist = $this->whitelist(Arr::get($_GET, 'whitelist', array())); } $runner = new Kohana_Unittest_Runner($suite); try { $runner->run($group, $collect_cc); if ($collect_cc) { $this->template->body->set('coverage', $runner->calculate_cc_percentage()); } if (isset($whitelist)) { $this->template->body->set('coverage_explanation', $this->nice_whitelist_explanation($whitelist)); } } catch(Kohana_Exception $e) { // Code coverage is not allowed, possibly xdebug disabled? // TODO: Tell the user this? $runner->run($group); } // Show some results $this->template->body ->set('results', $runner->results) ->set('totals', $runner->totals) ->set('time', $this->nice_time($runner->time)) // Sets group to the currently selected group, or default all groups ->set('group', Arr::get($this->get_groups_list($suite), reset($group), 'All groups')) ->set('groups', $this->get_groups_list($suite)) ->set('report_uri', $this->report_uri.url::query()) // Whitelist related stuff ->set('whitelistable_items', $this->get_whitelistable_items()) ->set('whitelisted_items', isset($whitelist) ? array_keys($whitelist) : array()) ->set('whitelist', ! empty($whitelist)); } /** * Get the list of groups from the test suite, sorted with 'All groups' prefixed * * @return array Array of groups in the test suite */ protected function get_groups_list($suite) { // Make groups aray suitable for drop down $groups = $suite->getGroups(); if (count($groups) > 0) { sort($groups); $groups = array_combine($groups, $groups); } return array('' => 'All Groups') + $groups; } /** * Gets a list of items that are whitelistable * * @return array */ protected function get_whitelistable_items() { static $whitelist; if (count($whitelist)) { return $whitelist; } $whitelist = array(); $whitelist['k_app'] = 'Application'; $k_modules = array_keys(Kohana::modules()); $whitelist += array_map('ucfirst', array_combine($k_modules, $k_modules)); $whitelist['k_sys'] = 'Kohana Core'; return $whitelist; } /** * Whitelists a specified set of modules specified by the user * * @param array $modules */ protected function whitelist(array $modules) { $k_modules = Kohana::modules(); $whitelist = array(); // Make sure our whitelist is valid foreach ($modules as $item) { if (isset($k_modules[$item])) { $whitelist[$item] = $k_modules[$item]; } elseif ($item === 'k_app') { $whitelist[$item] = APPPATH; } elseif ($item === 'k_sys') { $whitelist[$item] = SYSPATH; } } if (count($whitelist)) { Kohana_Tests::whitelist($whitelist); } return $whitelist; } /** * Prettifies the list of whitelisted modules * * @param array Array of whitelisted items * @return string */ protected function nice_whitelist_explanation(array $whitelist) { $items = array_intersect_key($this->get_whitelistable_items(), $whitelist); return implode(', ', $items); } protected function nice_time($time) { $parts = array(); if ($time > DATE::DAY) { $parts[] = floor($time/DATE::DAY).'d'; $time = $time % DATE::DAY; } if ($time > DATE::HOUR) { $parts[] = floor($time/DATE::HOUR).'h'; $time = $time % DATE::HOUR; } if ($time > DATE::MINUTE) { $parts[] = floor($time/DATE::MINUTE).'m'; $time = $time % DATE::MINUTE; } if ($time > 0) { $parts[] = round($time, 1).'s'; } return implode(' ', $parts); } } // End Controller_PHPUnit