360 lines
8.5 KiB
PHP
360 lines
8.5 KiB
PHP
|
<?php defined('SYSPATH') or die ('No direct script access.');
|
||
|
/**
|
||
|
* PHPUnit Kohana web based test runner
|
||
|
*
|
||
|
* @package Kohana/Unittest
|
||
|
* @author Kohana Team
|
||
|
* @author BRMatt <matthew@sigswitch.com>
|
||
|
* @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
|