mirror of
https://github.com/brmlab/brmbiolab_sklad.git
synced 2025-06-11 06:33:59 +02:00
Initial commit
This commit is contained in:
commit
3b93da31de
1004 changed files with 265840 additions and 0 deletions
737
lib/Cake/TestSuite/CakeTestCase.php
Normal file
737
lib/Cake/TestSuite/CakeTestCase.php
Normal file
|
@ -0,0 +1,737 @@
|
|||
<?php
|
||||
/**
|
||||
* CakeTestCase file
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
|
||||
* @package Cake.TestSuite
|
||||
* @since CakePHP(tm) v 1.2.0.4667
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('CakeFixtureManager', 'TestSuite/Fixture');
|
||||
App::uses('CakeTestFixture', 'TestSuite/Fixture');
|
||||
|
||||
/**
|
||||
* CakeTestCase class
|
||||
*
|
||||
* @package Cake.TestSuite
|
||||
*/
|
||||
abstract class CakeTestCase extends PHPUnit_Framework_TestCase {
|
||||
|
||||
/**
|
||||
* The class responsible for managing the creation, loading and removing of fixtures
|
||||
*
|
||||
* @var CakeFixtureManager
|
||||
*/
|
||||
public $fixtureManager = null;
|
||||
|
||||
/**
|
||||
* By default, all fixtures attached to this class will be truncated and reloaded after each test.
|
||||
* Set this to false to handle manually
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $autoFixtures = true;
|
||||
|
||||
/**
|
||||
* Control table create/drops on each test method.
|
||||
*
|
||||
* Set this to false to avoid tables to be dropped if they already exist
|
||||
* between each test method. Tables will still be dropped at the
|
||||
* end of each test runner execution.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $dropTables = true;
|
||||
|
||||
/**
|
||||
* Configure values to restore at end of test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_configure = array();
|
||||
|
||||
/**
|
||||
* Path settings to restore at the end of the test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_pathRestore = array();
|
||||
|
||||
/**
|
||||
* Runs the test case and collects the results in a TestResult object.
|
||||
* If no TestResult object is passed a new one will be created.
|
||||
* This method is run for each test method in this class
|
||||
*
|
||||
* @param PHPUnit_Framework_TestResult $result The test result object
|
||||
* @return PHPUnit_Framework_TestResult
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function run(PHPUnit_Framework_TestResult $result = null) {
|
||||
if (!empty($this->fixtureManager)) {
|
||||
$this->fixtureManager->load($this);
|
||||
}
|
||||
$result = parent::run($result);
|
||||
if (!empty($this->fixtureManager)) {
|
||||
$this->fixtureManager->unload($this);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a test case method is about to start (to be overridden when needed.)
|
||||
*
|
||||
* @param string $method Test method about to get executed.
|
||||
* @return void
|
||||
*/
|
||||
public function startTest($method) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a test case method has been executed (to be overridden when needed.)
|
||||
*
|
||||
* @param string $method Test method about that was executed.
|
||||
* @return void
|
||||
*/
|
||||
public function endTest($method) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides SimpleTestCase::skipIf to provide a boolean return value
|
||||
*
|
||||
* @param bool $shouldSkip Whether or not the test should be skipped.
|
||||
* @param string $message The message to display.
|
||||
* @return bool
|
||||
*/
|
||||
public function skipIf($shouldSkip, $message = '') {
|
||||
if ($shouldSkip) {
|
||||
$this->markTestSkipped($message);
|
||||
}
|
||||
return $shouldSkip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the test case, backup the static object values so they can be restored.
|
||||
* Specifically backs up the contents of Configure and paths in App if they have
|
||||
* not already been backed up.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
if (empty($this->_configure)) {
|
||||
$this->_configure = Configure::read();
|
||||
}
|
||||
if (empty($this->_pathRestore)) {
|
||||
$this->_pathRestore = App::paths();
|
||||
}
|
||||
if (class_exists('Router', false)) {
|
||||
Router::reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* teardown any static object changes and restore them.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function tearDown() {
|
||||
parent::tearDown();
|
||||
App::build($this->_pathRestore, App::RESET);
|
||||
if (class_exists('ClassRegistry', false)) {
|
||||
ClassRegistry::flush();
|
||||
}
|
||||
if (!empty($this->_configure)) {
|
||||
Configure::clear();
|
||||
Configure::write($this->_configure);
|
||||
}
|
||||
if (isset($_GET['debug']) && $_GET['debug']) {
|
||||
ob_flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See CakeTestSuiteDispatcher::date()
|
||||
*
|
||||
* @param string $format format to be used.
|
||||
* @return string
|
||||
*/
|
||||
public static function date($format = 'Y-m-d H:i:s') {
|
||||
return CakeTestSuiteDispatcher::date($format);
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart PHPUnit overrides don't match CakePHP
|
||||
|
||||
/**
|
||||
* Announces the start of a test.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function assertPreConditions() {
|
||||
parent::assertPreConditions();
|
||||
$this->startTest($this->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Announces the end of a test.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function assertPostConditions() {
|
||||
parent::assertPostConditions();
|
||||
$this->endTest($this->getName());
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
/**
|
||||
* Chooses which fixtures to load for a given test
|
||||
*
|
||||
* Each parameter is a model name that corresponds to a fixture, i.e. 'Post', 'Author', etc.
|
||||
*
|
||||
* @return void
|
||||
* @see CakeTestCase::$autoFixtures
|
||||
* @throws Exception when no fixture manager is available.
|
||||
*/
|
||||
public function loadFixtures() {
|
||||
if (empty($this->fixtureManager)) {
|
||||
throw new Exception(__d('cake_dev', 'No fixture manager to load the test fixture'));
|
||||
}
|
||||
$args = func_get_args();
|
||||
foreach ($args as $class) {
|
||||
$this->fixtureManager->loadSingle($class, null, $this->dropTables);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert text equality, ignoring differences in newlines.
|
||||
* Helpful for doing cross platform tests of blocks of text.
|
||||
*
|
||||
* @param string $expected The expected value.
|
||||
* @param string $result The actual value.
|
||||
* @param string $message The message to use for failure.
|
||||
* @return bool
|
||||
*/
|
||||
public function assertTextNotEquals($expected, $result, $message = '') {
|
||||
$expected = str_replace(array("\r\n", "\r"), "\n", $expected);
|
||||
$result = str_replace(array("\r\n", "\r"), "\n", $result);
|
||||
return $this->assertNotEquals($expected, $result, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert text equality, ignoring differences in newlines.
|
||||
* Helpful for doing cross platform tests of blocks of text.
|
||||
*
|
||||
* @param string $expected The expected value.
|
||||
* @param string $result The actual value.
|
||||
* @param string $message message The message to use for failure.
|
||||
* @return bool
|
||||
*/
|
||||
public function assertTextEquals($expected, $result, $message = '') {
|
||||
$expected = str_replace(array("\r\n", "\r"), "\n", $expected);
|
||||
$result = str_replace(array("\r\n", "\r"), "\n", $result);
|
||||
return $this->assertEquals($expected, $result, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a string starts with a given prefix, ignoring differences in newlines.
|
||||
* Helpful for doing cross platform tests of blocks of text.
|
||||
*
|
||||
* @param string $prefix The prefix to check for.
|
||||
* @param string $string The string to search in.
|
||||
* @param string $message The message to use for failure.
|
||||
* @return bool
|
||||
*/
|
||||
public function assertTextStartsWith($prefix, $string, $message = '') {
|
||||
$prefix = str_replace(array("\r\n", "\r"), "\n", $prefix);
|
||||
$string = str_replace(array("\r\n", "\r"), "\n", $string);
|
||||
return $this->assertStringStartsWith($prefix, $string, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a string starts not with a given prefix, ignoring differences in newlines.
|
||||
* Helpful for doing cross platform tests of blocks of text.
|
||||
*
|
||||
* @param string $prefix The prefix to not find.
|
||||
* @param string $string The string to search.
|
||||
* @param string $message The message to use for failure.
|
||||
* @return bool
|
||||
*/
|
||||
public function assertTextStartsNotWith($prefix, $string, $message = '') {
|
||||
$prefix = str_replace(array("\r\n", "\r"), "\n", $prefix);
|
||||
$string = str_replace(array("\r\n", "\r"), "\n", $string);
|
||||
return $this->assertStringStartsNotWith($prefix, $string, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a string ends with a given prefix, ignoring differences in newlines.
|
||||
* Helpful for doing cross platform tests of blocks of text.
|
||||
*
|
||||
* @param string $suffix The suffix to find.
|
||||
* @param string $string The string to search.
|
||||
* @param string $message The message to use for failure.
|
||||
* @return bool
|
||||
*/
|
||||
public function assertTextEndsWith($suffix, $string, $message = '') {
|
||||
$suffix = str_replace(array("\r\n", "\r"), "\n", $suffix);
|
||||
$string = str_replace(array("\r\n", "\r"), "\n", $string);
|
||||
return $this->assertStringEndsWith($suffix, $string, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a string ends not with a given prefix, ignoring differences in newlines.
|
||||
* Helpful for doing cross platform tests of blocks of text.
|
||||
*
|
||||
* @param string $suffix The suffix to not find.
|
||||
* @param string $string The string to search.
|
||||
* @param string $message The message to use for failure.
|
||||
* @return bool
|
||||
*/
|
||||
public function assertTextEndsNotWith($suffix, $string, $message = '') {
|
||||
$suffix = str_replace(array("\r\n", "\r"), "\n", $suffix);
|
||||
$string = str_replace(array("\r\n", "\r"), "\n", $string);
|
||||
return $this->assertStringEndsNotWith($suffix, $string, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a string contains another string, ignoring differences in newlines.
|
||||
* Helpful for doing cross platform tests of blocks of text.
|
||||
*
|
||||
* @param string $needle The string to search for.
|
||||
* @param string $haystack The string to search through.
|
||||
* @param string $message The message to display on failure.
|
||||
* @param bool $ignoreCase Whether or not the search should be case-sensitive.
|
||||
* @return bool
|
||||
*/
|
||||
public function assertTextContains($needle, $haystack, $message = '', $ignoreCase = false) {
|
||||
$needle = str_replace(array("\r\n", "\r"), "\n", $needle);
|
||||
$haystack = str_replace(array("\r\n", "\r"), "\n", $haystack);
|
||||
return $this->assertContains($needle, $haystack, $message, $ignoreCase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a text doesn't contain another text, ignoring differences in newlines.
|
||||
* Helpful for doing cross platform tests of blocks of text.
|
||||
*
|
||||
* @param string $needle The string to search for.
|
||||
* @param string $haystack The string to search through.
|
||||
* @param string $message The message to display on failure.
|
||||
* @param bool $ignoreCase Whether or not the search should be case-sensitive.
|
||||
* @return bool
|
||||
*/
|
||||
public function assertTextNotContains($needle, $haystack, $message = '', $ignoreCase = false) {
|
||||
$needle = str_replace(array("\r\n", "\r"), "\n", $needle);
|
||||
$haystack = str_replace(array("\r\n", "\r"), "\n", $haystack);
|
||||
return $this->assertNotContains($needle, $haystack, $message, $ignoreCase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array $expected and generates a regex from it to match the provided $string.
|
||||
* Samples for $expected:
|
||||
*
|
||||
* Checks for an input tag with a name attribute (contains any non-empty value) and an id
|
||||
* attribute that contains 'my-input':
|
||||
*
|
||||
* {{{
|
||||
* array('input' => array('name', 'id' => 'my-input'))
|
||||
* }}}
|
||||
*
|
||||
* Checks for two p elements with some text in them:
|
||||
*
|
||||
* {{{
|
||||
* array(
|
||||
* array('p' => true),
|
||||
* 'textA',
|
||||
* '/p',
|
||||
* array('p' => true),
|
||||
* 'textB',
|
||||
* '/p'
|
||||
* )
|
||||
* }}}
|
||||
*
|
||||
* You can also specify a pattern expression as part of the attribute values, or the tag
|
||||
* being defined, if you prepend the value with preg: and enclose it with slashes, like so:
|
||||
*
|
||||
* {{{
|
||||
* array(
|
||||
* array('input' => array('name', 'id' => 'preg:/FieldName\d+/')),
|
||||
* 'preg:/My\s+field/'
|
||||
* )
|
||||
* }}}
|
||||
*
|
||||
* Important: This function is very forgiving about whitespace and also accepts any
|
||||
* permutation of attribute order. It will also allow whitespace between specified tags.
|
||||
*
|
||||
* @param string $string An HTML/XHTML/XML string
|
||||
* @param array $expected An array, see above
|
||||
* @param string $fullDebug Whether or not more verbose output should be used.
|
||||
* @return bool
|
||||
*/
|
||||
public function assertTags($string, $expected, $fullDebug = false) {
|
||||
$regex = array();
|
||||
$normalized = array();
|
||||
foreach ((array)$expected as $key => $val) {
|
||||
if (!is_numeric($key)) {
|
||||
$normalized[] = array($key => $val);
|
||||
} else {
|
||||
$normalized[] = $val;
|
||||
}
|
||||
}
|
||||
$i = 0;
|
||||
foreach ($normalized as $tags) {
|
||||
if (!is_array($tags)) {
|
||||
$tags = (string)$tags;
|
||||
}
|
||||
$i++;
|
||||
if (is_string($tags) && $tags{0} === '<') {
|
||||
$tags = array(substr($tags, 1) => array());
|
||||
} elseif (is_string($tags)) {
|
||||
$tagsTrimmed = preg_replace('/\s+/m', '', $tags);
|
||||
|
||||
if (preg_match('/^\*?\//', $tags, $match) && $tagsTrimmed !== '//') {
|
||||
$prefix = array(null, null);
|
||||
|
||||
if ($match[0] === '*/') {
|
||||
$prefix = array('Anything, ', '.*?');
|
||||
}
|
||||
$regex[] = array(
|
||||
sprintf('%sClose %s tag', $prefix[0], substr($tags, strlen($match[0]))),
|
||||
sprintf('%s<[\s]*\/[\s]*%s[\s]*>[\n\r]*', $prefix[1], substr($tags, strlen($match[0]))),
|
||||
$i,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (!empty($tags) && preg_match('/^preg\:\/(.+)\/$/i', $tags, $matches)) {
|
||||
$tags = $matches[1];
|
||||
$type = 'Regex matches';
|
||||
} else {
|
||||
$tags = preg_quote($tags, '/');
|
||||
$type = 'Text equals';
|
||||
}
|
||||
$regex[] = array(
|
||||
sprintf('%s "%s"', $type, $tags),
|
||||
$tags,
|
||||
$i,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
foreach ($tags as $tag => $attributes) {
|
||||
$regex[] = array(
|
||||
sprintf('Open %s tag', $tag),
|
||||
sprintf('[\s]*<%s', preg_quote($tag, '/')),
|
||||
$i,
|
||||
);
|
||||
if ($attributes === true) {
|
||||
$attributes = array();
|
||||
}
|
||||
$attrs = array();
|
||||
$explanations = array();
|
||||
$i = 1;
|
||||
foreach ($attributes as $attr => $val) {
|
||||
if (is_numeric($attr) && preg_match('/^preg\:\/(.+)\/$/i', $val, $matches)) {
|
||||
$attrs[] = $matches[1];
|
||||
$explanations[] = sprintf('Regex "%s" matches', $matches[1]);
|
||||
continue;
|
||||
} else {
|
||||
$quotes = '["\']';
|
||||
if (is_numeric($attr)) {
|
||||
$attr = $val;
|
||||
$val = '.+?';
|
||||
$explanations[] = sprintf('Attribute "%s" present', $attr);
|
||||
} elseif (!empty($val) && preg_match('/^preg\:\/(.+)\/$/i', $val, $matches)) {
|
||||
$val = str_replace(
|
||||
array('.*', '.+'),
|
||||
array('.*?', '.+?'),
|
||||
$matches[1]
|
||||
);
|
||||
$quotes = $val !== $matches[1] ? '["\']' : '["\']?';
|
||||
|
||||
$explanations[] = sprintf('Attribute "%s" matches "%s"', $attr, $val);
|
||||
} else {
|
||||
$explanations[] = sprintf('Attribute "%s" == "%s"', $attr, $val);
|
||||
$val = preg_quote($val, '/');
|
||||
}
|
||||
$attrs[] = '[\s]+' . preg_quote($attr, '/') . '=' . $quotes . $val . $quotes;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
if ($attrs) {
|
||||
$regex[] = array(
|
||||
'explains' => $explanations,
|
||||
'attrs' => $attrs,
|
||||
);
|
||||
}
|
||||
$regex[] = array(
|
||||
sprintf('End %s tag', $tag),
|
||||
'[\s]*\/?[\s]*>[\n\r]*',
|
||||
$i,
|
||||
);
|
||||
}
|
||||
}
|
||||
foreach ($regex as $i => $assertion) {
|
||||
$matches = false;
|
||||
if (isset($assertion['attrs'])) {
|
||||
$string = $this->_assertAttributes($assertion, $string);
|
||||
continue;
|
||||
}
|
||||
|
||||
list($description, $expressions, $itemNum) = $assertion;
|
||||
foreach ((array)$expressions as $expression) {
|
||||
if (preg_match(sprintf('/^%s/s', $expression), $string, $match)) {
|
||||
$matches = true;
|
||||
$string = substr($string, strlen($match[0]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$matches) {
|
||||
$this->assertTrue(false, sprintf('Item #%d / regex #%d failed: %s', $itemNum, $i, $description));
|
||||
if ($fullDebug) {
|
||||
debug($string, true);
|
||||
debug($regex, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertTrue(true, '%s');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the attributes as part of an assertTags() check.
|
||||
*
|
||||
* @param array $assertions Assertions to run.
|
||||
* @param string $string The HTML string to check.
|
||||
* @return void
|
||||
*/
|
||||
protected function _assertAttributes($assertions, $string) {
|
||||
$asserts = $assertions['attrs'];
|
||||
$explains = $assertions['explains'];
|
||||
$len = count($asserts);
|
||||
do {
|
||||
$matches = false;
|
||||
foreach ($asserts as $j => $assert) {
|
||||
if (preg_match(sprintf('/^%s/s', $assert), $string, $match)) {
|
||||
$matches = true;
|
||||
$string = substr($string, strlen($match[0]));
|
||||
array_splice($asserts, $j, 1);
|
||||
array_splice($explains, $j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($matches === false) {
|
||||
$this->assertTrue(false, 'Attribute did not match. Was expecting ' . $explains[$j]);
|
||||
}
|
||||
$len = count($asserts);
|
||||
} while ($len > 0);
|
||||
return $string;
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
|
||||
/**
|
||||
* Compatibility wrapper function for assertEquals
|
||||
*
|
||||
* @param mixed $result
|
||||
* @param mixed $expected
|
||||
* @param string $message the text to display if the assertion is not correct
|
||||
* @deprecated This is a compatiblity wrapper for 1.x. It will be removed in 3.0
|
||||
* @return void
|
||||
*/
|
||||
protected static function assertEqual($result, $expected, $message = '') {
|
||||
return self::assertEquals($expected, $result, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility wrapper function for assertNotEquals
|
||||
*
|
||||
* @param mixed $result
|
||||
* @param mixed $expected
|
||||
* @param string $message the text to display if the assertion is not correct
|
||||
* @deprecated This is a compatiblity wrapper for 1.x. It will be removed in 3.0
|
||||
* @return void
|
||||
*/
|
||||
protected static function assertNotEqual($result, $expected, $message = '') {
|
||||
return self::assertNotEquals($expected, $result, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility wrapper function for assertRegexp
|
||||
*
|
||||
* @param mixed $pattern a regular expression
|
||||
* @param string $string the text to be matched
|
||||
* @param string $message the text to display if the assertion is not correct
|
||||
* @deprecated This is a compatiblity wrapper for 1.x. It will be removed in 3.0
|
||||
* @return void
|
||||
*/
|
||||
protected static function assertPattern($pattern, $string, $message = '') {
|
||||
return self::assertRegExp($pattern, $string, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility wrapper function for assertEquals
|
||||
*
|
||||
* @param mixed $actual
|
||||
* @param mixed $expected
|
||||
* @param string $message the text to display if the assertion is not correct
|
||||
* @deprecated This is a compatiblity wrapper for 1.x. It will be removed in 3.0
|
||||
* @return void
|
||||
*/
|
||||
protected static function assertIdentical($actual, $expected, $message = '') {
|
||||
return self::assertSame($expected, $actual, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility wrapper function for assertNotEquals
|
||||
*
|
||||
* @param mixed $actual
|
||||
* @param mixed $expected
|
||||
* @param string $message the text to display if the assertion is not correct
|
||||
* @deprecated This is a compatiblity wrapper for 1.x. It will be removed in 3.0
|
||||
* @return void
|
||||
*/
|
||||
protected static function assertNotIdentical($actual, $expected, $message = '') {
|
||||
return self::assertNotSame($expected, $actual, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility wrapper function for assertNotRegExp
|
||||
*
|
||||
* @param mixed $pattern a regular expression
|
||||
* @param string $string the text to be matched
|
||||
* @param string $message the text to display if the assertion is not correct
|
||||
* @deprecated This is a compatiblity wrapper for 1.x. It will be removed in 3.0
|
||||
* @return void
|
||||
*/
|
||||
protected static function assertNoPattern($pattern, $string, $message = '') {
|
||||
return self::assertNotRegExp($pattern, $string, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* assert no errors
|
||||
*
|
||||
* @deprecated This is a compatiblity wrapper for 1.x. It will be removed in 3.0
|
||||
* @return void
|
||||
*/
|
||||
protected function assertNoErrors() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility wrapper function for setExpectedException
|
||||
*
|
||||
* @param mixed $expected the name of the Exception or error
|
||||
* @param string $message the text to display if the assertion is not correct
|
||||
* @deprecated This is a compatiblity wrapper for 1.x. It will be removed in 3.0
|
||||
* @return void
|
||||
*/
|
||||
protected function expectError($expected = false, $message = '') {
|
||||
if (!$expected) {
|
||||
$expected = 'Exception';
|
||||
}
|
||||
$this->setExpectedException($expected, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility wrapper function for setExpectedException
|
||||
*
|
||||
* @param mixed $expected the name of the Exception
|
||||
* @param string $message the text to display if the assertion is not correct
|
||||
* @deprecated This is a compatiblity wrapper for 1.x. It will be removed in 3.0
|
||||
* @return void
|
||||
*/
|
||||
protected function expectException($name = 'Exception', $message = '') {
|
||||
$this->setExpectedException($name, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility wrapper function for assertSame
|
||||
*
|
||||
* @param mixed $first
|
||||
* @param mixed $second
|
||||
* @param string $message the text to display if the assertion is not correct
|
||||
* @deprecated This is a compatiblity wrapper for 1.x. It will be removed in 3.0
|
||||
* @return void
|
||||
*/
|
||||
protected static function assertReference(&$first, &$second, $message = '') {
|
||||
return self::assertSame($first, $second, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility wrapper for assertIsA
|
||||
*
|
||||
* @param string $object
|
||||
* @param string $type
|
||||
* @param string $message
|
||||
* @deprecated This is a compatiblity wrapper for 1.x. It will be removed in 3.0
|
||||
* @return void
|
||||
*/
|
||||
protected static function assertIsA($object, $type, $message = '') {
|
||||
return self::assertInstanceOf($type, $object, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility function to test if value is between an acceptable range
|
||||
*
|
||||
* @param mixed $result
|
||||
* @param mixed $expected
|
||||
* @param mixed $margin the rage of acceptation
|
||||
* @param string $message the text to display if the assertion is not correct
|
||||
* @return void
|
||||
*/
|
||||
protected static function assertWithinMargin($result, $expected, $margin, $message = '') {
|
||||
$upper = $result + $margin;
|
||||
$lower = $result - $margin;
|
||||
return self::assertTrue((($expected <= $upper) && ($expected >= $lower)), $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility function for skipping.
|
||||
*
|
||||
* @param bool $condition Condition to trigger skipping
|
||||
* @param string $message Message for skip
|
||||
* @return bool
|
||||
*/
|
||||
protected function skipUnless($condition, $message = '') {
|
||||
if (!$condition) {
|
||||
$this->markTestSkipped($message);
|
||||
}
|
||||
return $condition;
|
||||
}
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
/**
|
||||
* Mock a model, maintain fixtures and table association
|
||||
*
|
||||
* @param string $model The model to get a mock for.
|
||||
* @param mixed $methods The list of methods to mock
|
||||
* @param array $config The config data for the mock's constructor.
|
||||
* @throws MissingModelException
|
||||
* @return Model
|
||||
*/
|
||||
public function getMockForModel($model, $methods = array(), $config = array()) {
|
||||
$config += ClassRegistry::config('Model');
|
||||
|
||||
list($plugin, $name) = pluginSplit($model, true);
|
||||
App::uses($name, $plugin . 'Model');
|
||||
$config = array_merge((array)$config, array('name' => $name));
|
||||
|
||||
if (!class_exists($name)) {
|
||||
throw new MissingModelException(array($model));
|
||||
}
|
||||
|
||||
$mock = $this->getMock($name, $methods, array($config));
|
||||
ClassRegistry::removeObject($name);
|
||||
ClassRegistry::addObject($name, $mock);
|
||||
return $mock;
|
||||
}
|
||||
|
||||
}
|
125
lib/Cake/TestSuite/CakeTestLoader.php
Normal file
125
lib/Cake/TestSuite/CakeTestLoader.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
/**
|
||||
* TestLoader for CakePHP Test suite.
|
||||
*
|
||||
* Turns partial paths used on the testsuite console and web UI into full file paths.
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
* @package Cake.TestSuite
|
||||
*/
|
||||
|
||||
/**
|
||||
* TestLoader for CakePHP Test suite.
|
||||
*
|
||||
* Turns partial paths used on the testsuite console and web UI into full file paths.
|
||||
*
|
||||
* @package Cake.TestSuite
|
||||
*/
|
||||
class CakeTestLoader extends PHPUnit_Runner_StandardTestSuiteLoader {
|
||||
|
||||
/**
|
||||
* Load a file and find the first test case / suite in that file.
|
||||
*
|
||||
* @param string $filePath The file path to load
|
||||
* @param string $params Additional parameters
|
||||
* @return ReflectionClass
|
||||
*/
|
||||
public function load($filePath, $params = '') {
|
||||
$file = $this->_resolveTestFile($filePath, $params);
|
||||
return parent::load('', $file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert path fragments used by CakePHP's test runner to absolute paths that can be fed to PHPUnit.
|
||||
*
|
||||
* @param string $filePath The file path to load
|
||||
* @param string $params Additional parameters
|
||||
* @return void
|
||||
*/
|
||||
protected function _resolveTestFile($filePath, $params) {
|
||||
$basePath = $this->_basePath($params) . DS . $filePath;
|
||||
$ending = 'Test.php';
|
||||
return (strpos($basePath, $ending) === (strlen($basePath) - strlen($ending))) ? $basePath : $basePath . $ending;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the base path to a set of tests based on the parameters.
|
||||
*
|
||||
* @param array $params The path parameters.
|
||||
* @return string The base path.
|
||||
*/
|
||||
protected static function _basePath($params) {
|
||||
$result = null;
|
||||
if (!empty($params['core'])) {
|
||||
$result = CORE_TEST_CASES;
|
||||
} elseif (!empty($params['plugin'])) {
|
||||
if (!CakePlugin::loaded($params['plugin'])) {
|
||||
try {
|
||||
CakePlugin::load($params['plugin']);
|
||||
$result = CakePlugin::path($params['plugin']) . 'Test' . DS . 'Case';
|
||||
} catch (MissingPluginException $e) {
|
||||
}
|
||||
} else {
|
||||
$result = CakePlugin::path($params['plugin']) . 'Test' . DS . 'Case';
|
||||
}
|
||||
} elseif (!empty($params['app'])) {
|
||||
$result = APP_TEST_CASES;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of files for the test listing.
|
||||
*
|
||||
* @param string $params Path parameters
|
||||
* @return array
|
||||
*/
|
||||
public static function generateTestList($params) {
|
||||
$directory = self::_basePath($params);
|
||||
$fileList = self::_getRecursiveFileList($directory);
|
||||
|
||||
$testCases = array();
|
||||
foreach ($fileList as $testCaseFile) {
|
||||
$case = str_replace($directory . DS, '', $testCaseFile);
|
||||
$case = str_replace('Test.php', '', $case);
|
||||
$testCases[$testCaseFile] = $case;
|
||||
}
|
||||
sort($testCases);
|
||||
return $testCases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a recursive list of files from a given directory and matches then against
|
||||
* a given fileTestFunction, like isTestCaseFile()
|
||||
*
|
||||
* @param string $directory The directory to scan for files.
|
||||
* @return array
|
||||
*/
|
||||
protected static function _getRecursiveFileList($directory = '.') {
|
||||
$fileList = array();
|
||||
if (!is_dir($directory)) {
|
||||
return $fileList;
|
||||
}
|
||||
|
||||
$files = new RegexIterator(
|
||||
new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory)),
|
||||
'/.*Test.php$/'
|
||||
);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$fileList[] = $file->getPathname();
|
||||
}
|
||||
return $fileList;
|
||||
}
|
||||
|
||||
}
|
107
lib/Cake/TestSuite/CakeTestRunner.php
Normal file
107
lib/Cake/TestSuite/CakeTestRunner.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
/**
|
||||
* TestRunner for CakePHP Test suite.
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
require_once 'PHPUnit/TextUI/TestRunner.php';
|
||||
|
||||
App::uses('CakeFixtureManager', 'TestSuite/Fixture');
|
||||
|
||||
/**
|
||||
* A custom test runner for CakePHP's use of PHPUnit.
|
||||
*
|
||||
* @package Cake.TestSuite
|
||||
*/
|
||||
class CakeTestRunner extends PHPUnit_TextUI_TestRunner {
|
||||
|
||||
/**
|
||||
* Lets us pass in some options needed for CakePHP's webrunner.
|
||||
*
|
||||
* @param mixed $loader The test suite loader
|
||||
* @param array $params list of options to be used for this run
|
||||
*/
|
||||
public function __construct($loader, $params) {
|
||||
parent::__construct($loader);
|
||||
$this->_params = $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually run a suite of tests. Cake initializes fixtures here using the chosen fixture manager
|
||||
*
|
||||
* @param PHPUnit_Framework_Test $suite The test suite to run
|
||||
* @param array $arguments The CLI arguments
|
||||
* @return void
|
||||
*/
|
||||
public function doRun(PHPUnit_Framework_Test $suite, array $arguments = array()) {
|
||||
if (isset($arguments['printer'])) {
|
||||
self::$versionStringPrinted = true;
|
||||
}
|
||||
|
||||
$fixture = $this->_getFixtureManager($arguments);
|
||||
foreach ($suite->getIterator() as $test) {
|
||||
if ($test instanceof CakeTestCase) {
|
||||
$fixture->fixturize($test);
|
||||
$test->fixtureManager = $fixture;
|
||||
}
|
||||
}
|
||||
|
||||
$return = parent::doRun($suite, $arguments);
|
||||
$fixture->shutdown();
|
||||
return $return;
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart PHPUnit overrides don't match CakePHP
|
||||
/**
|
||||
* Create the test result and splice on our code coverage reports.
|
||||
*
|
||||
* @return PHPUnit_Framework_TestResult
|
||||
*/
|
||||
protected function createTestResult() {
|
||||
$result = new PHPUnit_Framework_TestResult;
|
||||
if (!empty($this->_params['codeCoverage'])) {
|
||||
if (method_exists($result, 'collectCodeCoverageInformation')) {
|
||||
$result->collectCodeCoverageInformation(true);
|
||||
}
|
||||
if (method_exists($result, 'setCodeCoverage')) {
|
||||
$result->setCodeCoverage(new PHP_CodeCoverage());
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
/**
|
||||
* Get the fixture manager class specified or use the default one.
|
||||
*
|
||||
* @param array $arguments The CLI arguments.
|
||||
* @return mixed instance of a fixture manager.
|
||||
* @throws RuntimeException When fixture manager class cannot be loaded.
|
||||
*/
|
||||
protected function _getFixtureManager($arguments) {
|
||||
if (isset($arguments['fixtureManager'])) {
|
||||
App::uses($arguments['fixtureManager'], 'TestSuite');
|
||||
if (class_exists($arguments['fixtureManager'])) {
|
||||
return new $arguments['fixtureManager'];
|
||||
}
|
||||
throw new RuntimeException(__d('cake_dev', 'Could not find fixture manager %s.', $arguments['fixtureManager']));
|
||||
}
|
||||
App::uses('AppFixtureManager', 'TestSuite');
|
||||
if (class_exists('AppFixtureManager')) {
|
||||
return new AppFixtureManager();
|
||||
}
|
||||
return new CakeFixtureManager();
|
||||
}
|
||||
|
||||
}
|
62
lib/Cake/TestSuite/CakeTestSuite.php
Normal file
62
lib/Cake/TestSuite/CakeTestSuite.php
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
/**
|
||||
* A class to contain test cases and run them with shared fixtures
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.TestSuite
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('Folder', 'Utility');
|
||||
|
||||
/**
|
||||
* A class to contain test cases and run them with shared fixtures
|
||||
*
|
||||
* @package Cake.TestSuite
|
||||
*/
|
||||
class CakeTestSuite extends PHPUnit_Framework_TestSuite {
|
||||
|
||||
/**
|
||||
* Adds all the files in a directory to the test suite. Does not recurse through directories.
|
||||
*
|
||||
* @param string $directory The directory to add tests from.
|
||||
* @return void
|
||||
*/
|
||||
public function addTestDirectory($directory = '.') {
|
||||
$Folder = new Folder($directory);
|
||||
list(, $files) = $Folder->read(true, true, true);
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (substr($file, -4) === '.php') {
|
||||
$this->addTestFile($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively adds all the files in a directory to the test suite.
|
||||
*
|
||||
* @param string $directory The directory subtree to add tests from.
|
||||
* @return void
|
||||
*/
|
||||
public function addTestDirectoryRecursive($directory = '.') {
|
||||
$Folder = new Folder($directory);
|
||||
$files = $Folder->tree(null, true, 'files');
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (substr($file, -4) === '.php') {
|
||||
$this->addTestFile($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
155
lib/Cake/TestSuite/CakeTestSuiteCommand.php
Normal file
155
lib/Cake/TestSuite/CakeTestSuiteCommand.php
Normal file
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
/**
|
||||
* TestRunner for CakePHP Test suite.
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.TestSuite
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
require_once 'PHPUnit/TextUI/Command.php';
|
||||
|
||||
App::uses('CakeTestRunner', 'TestSuite');
|
||||
App::uses('CakeTestLoader', 'TestSuite');
|
||||
App::uses('CakeTestSuite', 'TestSuite');
|
||||
App::uses('CakeTestCase', 'TestSuite');
|
||||
App::uses('ControllerTestCase', 'TestSuite');
|
||||
App::uses('CakeTestModel', 'TestSuite/Fixture');
|
||||
|
||||
/**
|
||||
* Class to customize loading of test suites from CLI
|
||||
*
|
||||
* @package Cake.TestSuite
|
||||
*/
|
||||
class CakeTestSuiteCommand extends PHPUnit_TextUI_Command {
|
||||
|
||||
/**
|
||||
* Construct method
|
||||
*
|
||||
* @param mixed $loader The loader instance to use.
|
||||
* @param array $params list of options to be used for this run
|
||||
* @throws MissingTestLoaderException When a loader class could not be found.
|
||||
*/
|
||||
public function __construct($loader, $params = array()) {
|
||||
if ($loader && !class_exists($loader)) {
|
||||
throw new MissingTestLoaderException(array('class' => $loader));
|
||||
}
|
||||
$this->arguments['loader'] = $loader;
|
||||
$this->arguments['test'] = $params['case'];
|
||||
$this->arguments['testFile'] = $params;
|
||||
$this->_params = $params;
|
||||
|
||||
$this->longOptions['fixture='] = 'handleFixture';
|
||||
$this->longOptions['output='] = 'handleReporter';
|
||||
}
|
||||
|
||||
/**
|
||||
* Ugly hack to get around PHPUnit having a hard coded class name for the Runner. :(
|
||||
*
|
||||
* @param array $argv The command arguments
|
||||
* @param bool $exit The exit mode.
|
||||
* @return void
|
||||
*/
|
||||
public function run(array $argv, $exit = true) {
|
||||
$this->handleArguments($argv);
|
||||
|
||||
$runner = $this->getRunner($this->arguments['loader']);
|
||||
|
||||
if (is_object($this->arguments['test']) &&
|
||||
$this->arguments['test'] instanceof PHPUnit_Framework_Test) {
|
||||
$suite = $this->arguments['test'];
|
||||
} else {
|
||||
$suite = $runner->getTest(
|
||||
$this->arguments['test'],
|
||||
$this->arguments['testFile']
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->arguments['listGroups']) {
|
||||
PHPUnit_TextUI_TestRunner::printVersionString();
|
||||
|
||||
print "Available test group(s):\n";
|
||||
|
||||
$groups = $suite->getGroups();
|
||||
sort($groups);
|
||||
|
||||
foreach ($groups as $group) {
|
||||
print " - $group\n";
|
||||
}
|
||||
|
||||
exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
|
||||
}
|
||||
|
||||
unset($this->arguments['test']);
|
||||
unset($this->arguments['testFile']);
|
||||
|
||||
try {
|
||||
$result = $runner->doRun($suite, $this->arguments);
|
||||
} catch (PHPUnit_Framework_Exception $e) {
|
||||
print $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
if ($exit) {
|
||||
if (isset($result) && $result->wasSuccessful()) {
|
||||
exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
|
||||
} elseif (!isset($result) || $result->errorCount() > 0) {
|
||||
exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT);
|
||||
}
|
||||
exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a runner for the command.
|
||||
*
|
||||
* @param mixed $loader The loader to be used for the test run.
|
||||
* @return CakeTestRunner
|
||||
*/
|
||||
public function getRunner($loader) {
|
||||
return new CakeTestRunner($loader, $this->_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for customizing the FixtureManager class/
|
||||
*
|
||||
* @param string $class Name of the class that will be the fixture manager
|
||||
* @return void
|
||||
*/
|
||||
public function handleFixture($class) {
|
||||
$this->arguments['fixtureManager'] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles output flag used to change printing on webrunner.
|
||||
*
|
||||
* @param string $reporter The reporter class to use.
|
||||
* @return void
|
||||
*/
|
||||
public function handleReporter($reporter) {
|
||||
$object = null;
|
||||
|
||||
$reporter = ucwords($reporter);
|
||||
$coreClass = 'Cake' . $reporter . 'Reporter';
|
||||
App::uses($coreClass, 'TestSuite/Reporter');
|
||||
|
||||
$appClass = $reporter . 'Reporter';
|
||||
App::uses($appClass, 'TestSuite/Reporter');
|
||||
|
||||
if (!class_exists($appClass)) {
|
||||
$object = new $coreClass(null, $this->_params);
|
||||
} else {
|
||||
$object = new $appClass(null, $this->_params);
|
||||
}
|
||||
return $this->arguments['printer'] = $object;
|
||||
}
|
||||
|
||||
}
|
287
lib/Cake/TestSuite/CakeTestSuiteDispatcher.php
Normal file
287
lib/Cake/TestSuite/CakeTestSuiteDispatcher.php
Normal file
|
@ -0,0 +1,287 @@
|
|||
<?php
|
||||
/**
|
||||
* CakeTestSuiteDispatcher controls dispatching TestSuite web based requests.
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.TestSuite
|
||||
* @since CakePHP(tm) v 1.3
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
define('CORE_TEST_CASES', CAKE . 'Test' . DS . 'Case');
|
||||
define('APP_TEST_CASES', TESTS . 'Case');
|
||||
|
||||
App::uses('CakeTestSuiteCommand', 'TestSuite');
|
||||
|
||||
/**
|
||||
* CakeTestSuiteDispatcher handles web requests to the test suite and runs the correct action.
|
||||
*
|
||||
* @package Cake.TestSuite
|
||||
*/
|
||||
class CakeTestSuiteDispatcher {
|
||||
|
||||
/**
|
||||
* 'Request' parameters
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $params = array(
|
||||
'codeCoverage' => false,
|
||||
'case' => null,
|
||||
'core' => false,
|
||||
'app' => true,
|
||||
'plugin' => null,
|
||||
'output' => 'html',
|
||||
'show' => 'groups',
|
||||
'show_passes' => false,
|
||||
'filter' => false,
|
||||
'fixture' => null
|
||||
);
|
||||
|
||||
/**
|
||||
* Baseurl for the request
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_baseUrl;
|
||||
|
||||
/**
|
||||
* Base dir of the request. Used for accessing assets.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_baseDir;
|
||||
|
||||
/**
|
||||
* boolean to set auto parsing of params.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_paramsParsed = false;
|
||||
|
||||
/**
|
||||
* reporter instance used for the request
|
||||
*
|
||||
* @var CakeBaseReporter
|
||||
*/
|
||||
protected static $_Reporter = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->_baseUrl = $_SERVER['PHP_SELF'];
|
||||
$dir = rtrim(dirname($this->_baseUrl), '\\');
|
||||
$this->_baseDir = ($dir === '/') ? $dir : $dir . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the actions required by the URL parameters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dispatch() {
|
||||
$this->_checkPHPUnit();
|
||||
$this->_parseParams();
|
||||
|
||||
if ($this->params['case']) {
|
||||
$value = $this->_runTestCase();
|
||||
} else {
|
||||
$value = $this->_testCaseList();
|
||||
}
|
||||
|
||||
$output = ob_get_clean();
|
||||
echo $output;
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to initialize the test runner, keeps global space clean
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function run() {
|
||||
$dispatcher = new CakeTestSuiteDispatcher();
|
||||
$dispatcher->dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that PHPUnit is installed. Will exit if it doesn't
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _checkPHPUnit() {
|
||||
$found = $this->loadTestFramework();
|
||||
if (!$found) {
|
||||
$baseDir = $this->_baseDir;
|
||||
include CAKE . 'TestSuite' . DS . 'templates' . DS . 'phpunit.php';
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for the existence of the test framework files
|
||||
*
|
||||
* @return bool true if found, false otherwise
|
||||
*/
|
||||
public function loadTestFramework() {
|
||||
if (class_exists('PHPUnit_Framework_TestCase')) {
|
||||
return true;
|
||||
}
|
||||
$phpunitPath = 'phpunit' . DS . 'phpunit';
|
||||
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
|
||||
$composerGlobalDir[] = env('APPDATA') . DS . 'Composer' . DS . 'vendor' . DS;
|
||||
} else {
|
||||
$composerGlobalDir[] = env('HOME') . DS . '.composer' . DS . 'vendor' . DS;
|
||||
}
|
||||
$vendors = array_merge(App::path('vendors'), $composerGlobalDir);
|
||||
foreach ($vendors as $vendor) {
|
||||
$vendor = rtrim($vendor, DS);
|
||||
if (is_dir($vendor . DS . $phpunitPath)) {
|
||||
ini_set('include_path', $vendor . DS . $phpunitPath . PATH_SEPARATOR . ini_get('include_path'));
|
||||
break;
|
||||
} elseif (is_dir($vendor . DS . 'PHPUnit')) {
|
||||
ini_set('include_path', $vendor . PATH_SEPARATOR . ini_get('include_path'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
include 'PHPUnit' . DS . 'Autoload.php';
|
||||
return class_exists('PHPUnit_Framework_TestCase');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for the xdebug extension required to do code coverage. Displays an error
|
||||
* if xdebug isn't installed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _checkXdebug() {
|
||||
if (!extension_loaded('xdebug')) {
|
||||
$baseDir = $this->_baseDir;
|
||||
include CAKE . 'TestSuite' . DS . 'templates' . DS . 'xdebug.php';
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a page containing the a list of test cases that could be run.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _testCaseList() {
|
||||
$command = new CakeTestSuiteCommand('', $this->params);
|
||||
$Reporter = $command->handleReporter($this->params['output']);
|
||||
$Reporter->paintDocumentStart();
|
||||
$Reporter->paintTestMenu();
|
||||
$Reporter->testCaseList();
|
||||
$Reporter->paintDocumentEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the params, calling this will bypass the auto parameter parsing.
|
||||
*
|
||||
* @param array $params Array of parameters for the dispatcher
|
||||
* @return void
|
||||
*/
|
||||
public function setParams($params) {
|
||||
$this->params = $params;
|
||||
$this->_paramsParsed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse URL params into a 'request'
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _parseParams() {
|
||||
if (!$this->_paramsParsed) {
|
||||
if (!isset($_SERVER['SERVER_NAME'])) {
|
||||
$_SERVER['SERVER_NAME'] = '';
|
||||
}
|
||||
foreach ($this->params as $key => $value) {
|
||||
if (isset($_GET[$key])) {
|
||||
$this->params[$key] = $_GET[$key];
|
||||
}
|
||||
}
|
||||
if (isset($_GET['code_coverage'])) {
|
||||
$this->params['codeCoverage'] = true;
|
||||
$this->_checkXdebug();
|
||||
}
|
||||
}
|
||||
if (empty($this->params['plugin']) && empty($this->params['core'])) {
|
||||
$this->params['app'] = true;
|
||||
}
|
||||
$this->params['baseUrl'] = $this->_baseUrl;
|
||||
$this->params['baseDir'] = $this->_baseDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a test case file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _runTestCase() {
|
||||
$commandArgs = array(
|
||||
'case' => $this->params['case'],
|
||||
'core' => $this->params['core'],
|
||||
'app' => $this->params['app'],
|
||||
'plugin' => $this->params['plugin'],
|
||||
'codeCoverage' => $this->params['codeCoverage'],
|
||||
'showPasses' => !empty($this->params['show_passes']),
|
||||
'baseUrl' => $this->_baseUrl,
|
||||
'baseDir' => $this->_baseDir,
|
||||
);
|
||||
|
||||
$options = array(
|
||||
'--filter', $this->params['filter'],
|
||||
'--output', $this->params['output'],
|
||||
'--fixture', $this->params['fixture']
|
||||
);
|
||||
restore_error_handler();
|
||||
|
||||
try {
|
||||
self::time();
|
||||
$command = new CakeTestSuiteCommand('CakeTestLoader', $commandArgs);
|
||||
$command->run($options);
|
||||
} catch (MissingConnectionException $exception) {
|
||||
ob_end_clean();
|
||||
$baseDir = $this->_baseDir;
|
||||
include CAKE . 'TestSuite' . DS . 'templates' . DS . 'missing_connection.php';
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a static timestamp
|
||||
*
|
||||
* @param bool $reset to set new static timestamp.
|
||||
* @return int timestamp
|
||||
*/
|
||||
public static function time($reset = false) {
|
||||
static $now;
|
||||
if ($reset || !$now) {
|
||||
$now = time();
|
||||
}
|
||||
return $now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns formatted date string using static time
|
||||
* This method is being used as formatter for created, modified and updated fields in Model::save()
|
||||
*
|
||||
* @param string $format format to be used.
|
||||
* @return string formatted date
|
||||
*/
|
||||
public static function date($format) {
|
||||
return date($format, self::time());
|
||||
}
|
||||
|
||||
}
|
381
lib/Cake/TestSuite/ControllerTestCase.php
Normal file
381
lib/Cake/TestSuite/ControllerTestCase.php
Normal file
|
@ -0,0 +1,381 @@
|
|||
<?php
|
||||
/**
|
||||
* ControllerTestCase file
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
|
||||
* @package Cake.TestSuite
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('Dispatcher', 'Routing');
|
||||
App::uses('CakeTestCase', 'TestSuite');
|
||||
App::uses('Router', 'Routing');
|
||||
App::uses('CakeRequest', 'Network');
|
||||
App::uses('CakeResponse', 'Network');
|
||||
App::uses('Helper', 'View');
|
||||
App::uses('CakeEvent', 'Event');
|
||||
|
||||
/**
|
||||
* ControllerTestDispatcher class
|
||||
*
|
||||
* @package Cake.TestSuite
|
||||
*/
|
||||
class ControllerTestDispatcher extends Dispatcher {
|
||||
|
||||
/**
|
||||
* The controller to use in the dispatch process
|
||||
*
|
||||
* @var Controller
|
||||
*/
|
||||
public $testController = null;
|
||||
|
||||
/**
|
||||
* Use custom routes during tests
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $loadRoutes = true;
|
||||
|
||||
/**
|
||||
* Returns the test controller
|
||||
*
|
||||
* @param CakeRequest $request The request instance.
|
||||
* @param CakeResponse $response The response instance.
|
||||
* @return Controller
|
||||
*/
|
||||
protected function _getController($request, $response) {
|
||||
if ($this->testController === null) {
|
||||
$this->testController = parent::_getController($request, $response);
|
||||
}
|
||||
$this->testController->helpers = array_merge(array('InterceptContent'), $this->testController->helpers);
|
||||
$this->testController->setRequest($request);
|
||||
$this->testController->response = $this->response;
|
||||
foreach ($this->testController->Components->loaded() as $component) {
|
||||
$object = $this->testController->Components->{$component};
|
||||
if (isset($object->response)) {
|
||||
$object->response = $response;
|
||||
}
|
||||
if (isset($object->request)) {
|
||||
$object->request = $request;
|
||||
}
|
||||
}
|
||||
return $this->testController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads routes and resets if the test case dictates it should
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _loadRoutes() {
|
||||
parent::_loadRoutes();
|
||||
if (!$this->loadRoutes) {
|
||||
Router::reload();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* InterceptContentHelper class
|
||||
*
|
||||
* @package Cake.TestSuite
|
||||
*/
|
||||
class InterceptContentHelper extends Helper {
|
||||
|
||||
/**
|
||||
* Intercepts and stores the contents of the view before the layout is rendered
|
||||
*
|
||||
* @param string $viewFile The view file
|
||||
* @return void
|
||||
*/
|
||||
public function afterRender($viewFile) {
|
||||
$this->_View->assign('__view_no_layout__', $this->_View->fetch('content'));
|
||||
$this->_View->Helpers->unload('InterceptContent');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* ControllerTestCase class
|
||||
*
|
||||
* @package Cake.TestSuite
|
||||
*/
|
||||
abstract class ControllerTestCase extends CakeTestCase {
|
||||
|
||||
/**
|
||||
* The controller to test in testAction
|
||||
*
|
||||
* @var Controller
|
||||
*/
|
||||
public $controller = null;
|
||||
|
||||
/**
|
||||
* Automatically mock controllers that aren't mocked
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $autoMock = true;
|
||||
|
||||
/**
|
||||
* Use custom routes during tests
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $loadRoutes = true;
|
||||
|
||||
/**
|
||||
* The resulting view vars of the last testAction call
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $vars = null;
|
||||
|
||||
/**
|
||||
* The resulting rendered view of the last testAction call
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $view = null;
|
||||
|
||||
/**
|
||||
* The resulting rendered layout+view of the last testAction call
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $contents = null;
|
||||
|
||||
/**
|
||||
* The returned result of the dispatch (requestAction), if any
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $result = null;
|
||||
|
||||
/**
|
||||
* The headers that would have been sent by the action
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $headers = null;
|
||||
|
||||
/**
|
||||
* Flag for checking if the controller instance is dirty.
|
||||
* Once a test has been run on a controller it should be rebuilt
|
||||
* to clean up properties.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_dirtyController = false;
|
||||
|
||||
/**
|
||||
* Used to enable calling ControllerTestCase::testAction() without the testing
|
||||
* framework thinking that it's a test case
|
||||
*
|
||||
* @param string $name The name of the function
|
||||
* @param array $arguments Array of arguments
|
||||
* @return the return of _testAction
|
||||
* @throws BadMethodCallException when you call methods that don't exist.
|
||||
*/
|
||||
public function __call($name, $arguments) {
|
||||
if ($name === 'testAction') {
|
||||
return call_user_func_array(array($this, '_testAction'), $arguments);
|
||||
}
|
||||
throw new BadMethodCallException("Method '{$name}' does not exist.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets you do functional tests of a controller action.
|
||||
*
|
||||
* ### Options:
|
||||
*
|
||||
* - `data` Will be used as the request data. If the `method` is GET,
|
||||
* data will be used a GET params. If the `method` is POST, it will be used
|
||||
* as POST data. By setting `$options['data']` to a string, you can simulate XML or JSON
|
||||
* payloads to your controllers allowing you to test REST webservices.
|
||||
* - `method` POST or GET. Defaults to POST.
|
||||
* - `return` Specify the return type you want. Choose from:
|
||||
* - `vars` Get the set view variables.
|
||||
* - `view` Get the rendered view, without a layout.
|
||||
* - `contents` Get the rendered view including the layout.
|
||||
* - `result` Get the return value of the controller action. Useful
|
||||
* for testing requestAction methods.
|
||||
*
|
||||
* @param string $url The url to test
|
||||
* @param array $options See options
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _testAction($url = '', $options = array()) {
|
||||
$this->vars = $this->result = $this->view = $this->contents = $this->headers = null;
|
||||
|
||||
$options += array(
|
||||
'data' => array(),
|
||||
'method' => 'POST',
|
||||
'return' => 'result'
|
||||
);
|
||||
|
||||
$restore = array('get' => $_GET, 'post' => $_POST);
|
||||
|
||||
$_SERVER['REQUEST_METHOD'] = strtoupper($options['method']);
|
||||
if (is_array($options['data'])) {
|
||||
if (strtoupper($options['method']) === 'GET') {
|
||||
$_GET = $options['data'];
|
||||
$_POST = array();
|
||||
} else {
|
||||
$_POST = $options['data'];
|
||||
$_GET = array();
|
||||
}
|
||||
}
|
||||
$request = $this->getMock('CakeRequest', array('_readInput'), array($url));
|
||||
|
||||
if (is_string($options['data'])) {
|
||||
$request->expects($this->any())
|
||||
->method('_readInput')
|
||||
->will($this->returnValue($options['data']));
|
||||
}
|
||||
|
||||
$Dispatch = new ControllerTestDispatcher();
|
||||
foreach (Router::$routes as $route) {
|
||||
if ($route instanceof RedirectRoute) {
|
||||
$route->response = $this->getMock('CakeResponse', array('send'));
|
||||
}
|
||||
}
|
||||
$Dispatch->loadRoutes = $this->loadRoutes;
|
||||
$Dispatch->parseParams(new CakeEvent('ControllerTestCase', $Dispatch, array('request' => $request)));
|
||||
if (!isset($request->params['controller']) && Router::currentRoute()) {
|
||||
$this->headers = Router::currentRoute()->response->header();
|
||||
return;
|
||||
}
|
||||
if ($this->_dirtyController) {
|
||||
$this->controller = null;
|
||||
}
|
||||
|
||||
$plugin = empty($request->params['plugin']) ? '' : Inflector::camelize($request->params['plugin']) . '.';
|
||||
if ($this->controller === null && $this->autoMock) {
|
||||
$this->generate($plugin . Inflector::camelize($request->params['controller']));
|
||||
}
|
||||
$params = array();
|
||||
if ($options['return'] === 'result') {
|
||||
$params['return'] = 1;
|
||||
$params['bare'] = 1;
|
||||
$params['requested'] = 1;
|
||||
}
|
||||
$Dispatch->testController = $this->controller;
|
||||
$Dispatch->response = $this->getMock('CakeResponse', array('send'));
|
||||
$this->result = $Dispatch->dispatch($request, $Dispatch->response, $params);
|
||||
$this->controller = $Dispatch->testController;
|
||||
$this->vars = $this->controller->viewVars;
|
||||
$this->contents = $this->controller->response->body();
|
||||
if (isset($this->controller->View)) {
|
||||
$this->view = $this->controller->View->fetch('__view_no_layout__');
|
||||
}
|
||||
$this->_dirtyController = true;
|
||||
$this->headers = $Dispatch->response->header();
|
||||
|
||||
$_GET = $restore['get'];
|
||||
$_POST = $restore['post'];
|
||||
|
||||
return $this->{$options['return']};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a mocked controller and mocks any classes passed to `$mocks`. By
|
||||
* default, `_stop()` is stubbed as is sending the response headers, so to not
|
||||
* interfere with testing.
|
||||
*
|
||||
* ### Mocks:
|
||||
*
|
||||
* - `methods` Methods to mock on the controller. `_stop()` is mocked by default
|
||||
* - `models` Models to mock. Models are added to the ClassRegistry so any
|
||||
* time they are instantiated the mock will be created. Pass as key value pairs
|
||||
* with the value being specific methods on the model to mock. If `true` or
|
||||
* no value is passed, the entire model will be mocked.
|
||||
* - `components` Components to mock. Components are only mocked on this controller
|
||||
* and not within each other (i.e., components on components)
|
||||
*
|
||||
* @param string $controller Controller name
|
||||
* @param array $mocks List of classes and methods to mock
|
||||
* @return Controller Mocked controller
|
||||
* @throws MissingControllerException When controllers could not be created.
|
||||
* @throws MissingComponentException When components could not be created.
|
||||
*/
|
||||
public function generate($controller, $mocks = array()) {
|
||||
list($plugin, $controller) = pluginSplit($controller);
|
||||
if ($plugin) {
|
||||
App::uses($plugin . 'AppController', $plugin . '.Controller');
|
||||
$plugin .= '.';
|
||||
}
|
||||
App::uses($controller . 'Controller', $plugin . 'Controller');
|
||||
if (!class_exists($controller . 'Controller')) {
|
||||
throw new MissingControllerException(array(
|
||||
'class' => $controller . 'Controller',
|
||||
'plugin' => substr($plugin, 0, -1)
|
||||
));
|
||||
}
|
||||
ClassRegistry::flush();
|
||||
|
||||
$mocks = array_merge_recursive(array(
|
||||
'methods' => array('_stop'),
|
||||
'models' => array(),
|
||||
'components' => array()
|
||||
), (array)$mocks);
|
||||
|
||||
list($plugin, $name) = pluginSplit($controller);
|
||||
$controllerObj = $this->getMock($name . 'Controller', $mocks['methods'], array(), '', false);
|
||||
$controllerObj->name = $name;
|
||||
$request = $this->getMock('CakeRequest');
|
||||
$response = $this->getMock('CakeResponse', array('_sendHeader'));
|
||||
$controllerObj->__construct($request, $response);
|
||||
$controllerObj->Components->setController($controllerObj);
|
||||
|
||||
$config = ClassRegistry::config('Model');
|
||||
foreach ($mocks['models'] as $model => $methods) {
|
||||
if (is_string($methods)) {
|
||||
$model = $methods;
|
||||
$methods = true;
|
||||
}
|
||||
if ($methods === true) {
|
||||
$methods = array();
|
||||
}
|
||||
$this->getMockForModel($model, $methods, $config);
|
||||
}
|
||||
|
||||
foreach ($mocks['components'] as $component => $methods) {
|
||||
if (is_string($methods)) {
|
||||
$component = $methods;
|
||||
$methods = true;
|
||||
}
|
||||
if ($methods === true) {
|
||||
$methods = array();
|
||||
}
|
||||
list($plugin, $name) = pluginSplit($component, true);
|
||||
$componentClass = $name . 'Component';
|
||||
App::uses($componentClass, $plugin . 'Controller/Component');
|
||||
if (!class_exists($componentClass)) {
|
||||
throw new MissingComponentException(array(
|
||||
'class' => $componentClass
|
||||
));
|
||||
}
|
||||
$config = isset($controllerObj->components[$component]) ? $controllerObj->components[$component] : array();
|
||||
$componentObj = $this->getMock($componentClass, $methods, array($controllerObj->Components, $config));
|
||||
$controllerObj->Components->set($name, $componentObj);
|
||||
$controllerObj->Components->enable($name);
|
||||
}
|
||||
|
||||
$controllerObj->constructClasses();
|
||||
$this->_dirtyController = false;
|
||||
|
||||
$this->controller = $controllerObj;
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
}
|
182
lib/Cake/TestSuite/Coverage/BaseCoverageReport.php
Normal file
182
lib/Cake/TestSuite/Coverage/BaseCoverageReport.php
Normal file
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
/**
|
||||
* Abstract class for common CoverageReport methods.
|
||||
* Provides several template methods for custom output.
|
||||
*
|
||||
* PHP5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.TestSuite.Coverage
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('Inflector', 'Utility');
|
||||
App::uses('CakePlugin', 'Core');
|
||||
|
||||
/**
|
||||
* Abstract class for common CoverageReport methods.
|
||||
* Provides several template methods for custom output.
|
||||
*
|
||||
* @package Cake.TestSuite.Coverage
|
||||
*/
|
||||
abstract class BaseCoverageReport {
|
||||
|
||||
/**
|
||||
* coverage data
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_rawCoverage;
|
||||
|
||||
/**
|
||||
* is the test an app test
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $appTest = false;
|
||||
|
||||
/**
|
||||
* is the test a plugin test
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pluginTest = false;
|
||||
|
||||
/**
|
||||
* Array of test case file names. Used to do basename() matching with
|
||||
* files that have coverage to decide which results to show on page load.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_testNames = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $coverage Array of coverage data from PHPUnit_Test_Result
|
||||
* @param CakeBaseReporter $reporter A reporter to use for the coverage report.
|
||||
*/
|
||||
public function __construct($coverage, CakeBaseReporter $reporter) {
|
||||
$this->_rawCoverage = $coverage;
|
||||
$this->_setParams($reporter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls params out of the reporter.
|
||||
*
|
||||
* @param CakeBaseReporter $reporter Reporter to suck params out of.
|
||||
* @return void
|
||||
*/
|
||||
protected function _setParams(CakeBaseReporter $reporter) {
|
||||
if ($reporter->params['app']) {
|
||||
$this->appTest = true;
|
||||
}
|
||||
if ($reporter->params['plugin']) {
|
||||
$this->pluginTest = Inflector::camelize($reporter->params['plugin']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the coverage data array
|
||||
*
|
||||
* @param array $coverage Coverage data to use.
|
||||
* @return void
|
||||
*/
|
||||
public function setCoverage($coverage) {
|
||||
$this->_rawCoverage = $coverage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base path that the files we are interested in live in.
|
||||
*
|
||||
* @return string Path
|
||||
*/
|
||||
public function getPathFilter() {
|
||||
$path = ROOT . DS;
|
||||
if ($this->appTest) {
|
||||
$path .= APP_DIR . DS;
|
||||
} elseif ($this->pluginTest) {
|
||||
$path = CakePlugin::path($this->pluginTest);
|
||||
} else {
|
||||
$path = CAKE;
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the coverage data by path. Files not in the provided path will be removed.
|
||||
*
|
||||
* @param string $path Path to filter files by.
|
||||
* @return array Array of coverage data for files that match the given path.
|
||||
*/
|
||||
public function filterCoverageDataByPath($path) {
|
||||
$files = array();
|
||||
foreach ($this->_rawCoverage as $fileName => $fileCoverage) {
|
||||
if (strpos($fileName, $path) !== 0) {
|
||||
continue;
|
||||
}
|
||||
$files[$fileName] = $fileCoverage;
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates how many lines are covered and what the total number of executable lines is.
|
||||
*
|
||||
* Handles both PHPUnit3.5 and 3.6 formats.
|
||||
*
|
||||
* 3.5 uses -1 for uncovered, and -2 for dead.
|
||||
* 3.6 uses array() for uncovered and null for dead.
|
||||
*
|
||||
* @param array $fileLines The lines in the file.
|
||||
* @param array $coverageData The raw coverage data.
|
||||
* @return array Array of covered, total lines.
|
||||
*/
|
||||
protected function _calculateCoveredLines($fileLines, $coverageData) {
|
||||
$covered = $total = 0;
|
||||
|
||||
//shift line numbers forward one
|
||||
array_unshift($fileLines, ' ');
|
||||
unset($fileLines[0]);
|
||||
|
||||
foreach ($fileLines as $lineno => $line) {
|
||||
if (!isset($coverageData[$lineno])) {
|
||||
continue;
|
||||
}
|
||||
if (is_array($coverageData[$lineno]) && !empty($coverageData[$lineno])) {
|
||||
$covered++;
|
||||
$total++;
|
||||
} elseif ($coverageData[$lineno] === -1 || $coverageData[$lineno] === array()) {
|
||||
$total++;
|
||||
}
|
||||
}
|
||||
return array($covered, $total);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates report to display.
|
||||
*
|
||||
* @return string compiled html report.
|
||||
*/
|
||||
abstract public function report();
|
||||
|
||||
/**
|
||||
* Generates an coverage 'diff' for $file based on $coverageData.
|
||||
*
|
||||
* @param string $filename Name of the file having coverage generated
|
||||
* @param array $fileLines File data as an array. See file() for how to get one of these.
|
||||
* @param array $coverageData Array of coverage data to use to generate HTML diffs with
|
||||
* @return string prepared report for a single file.
|
||||
*/
|
||||
abstract public function generateDiff($filename, $fileLines, $coverageData);
|
||||
|
||||
}
|
228
lib/Cake/TestSuite/Coverage/HtmlCoverageReport.php
Normal file
228
lib/Cake/TestSuite/Coverage/HtmlCoverageReport.php
Normal file
|
@ -0,0 +1,228 @@
|
|||
<?php
|
||||
/**
|
||||
* PHP5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.TestSuite.Coverage
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('BaseCoverageReport', 'TestSuite/Coverage');
|
||||
|
||||
/**
|
||||
* Generates code coverage reports in HTML from data obtained from PHPUnit
|
||||
*
|
||||
* @package Cake.TestSuite.Coverage
|
||||
*/
|
||||
class HtmlCoverageReport extends BaseCoverageReport {
|
||||
|
||||
/**
|
||||
* Holds the total number of processed rows.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_total = 0;
|
||||
|
||||
/**
|
||||
* Holds the total number of covered rows.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_covered = 0;
|
||||
|
||||
/**
|
||||
* Generates report HTML to display.
|
||||
*
|
||||
* @return string Compiled HTML report.
|
||||
*/
|
||||
public function report() {
|
||||
$pathFilter = $this->getPathFilter();
|
||||
$coverageData = $this->filterCoverageDataByPath($pathFilter);
|
||||
if (empty($coverageData)) {
|
||||
return '<h3>No files to generate coverage for</h3>';
|
||||
}
|
||||
$output = $this->coverageScript();
|
||||
$output .= <<<HTML
|
||||
<h3>Code coverage results
|
||||
<a href="#" onclick="coverage_toggle_all()" class="coverage-toggle">Toggle all files</a>
|
||||
</h3>
|
||||
HTML;
|
||||
foreach ($coverageData as $file => $coverageData) {
|
||||
$fileData = file($file);
|
||||
$output .= $this->generateDiff($file, $fileData, $coverageData);
|
||||
}
|
||||
|
||||
$percentCovered = 100;
|
||||
if ($this->_total > 0) {
|
||||
$percentCovered = round(100 * $this->_covered / $this->_total, 2);
|
||||
}
|
||||
$output .= '<div class="total">Overall coverage: <span class="coverage">' . $percentCovered . '%</span></div>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an HTML diff for $file based on $coverageData.
|
||||
*
|
||||
* Handles both PHPUnit3.5 and 3.6 formats.
|
||||
*
|
||||
* 3.5 uses -1 for uncovered, and -2 for dead.
|
||||
* 3.6 uses array() for uncovered and null for dead.
|
||||
*
|
||||
* @param string $filename Name of the file having coverage generated
|
||||
* @param array $fileLines File data as an array. See file() for how to get one of these.
|
||||
* @param array $coverageData Array of coverage data to use to generate HTML diffs with
|
||||
* @return string HTML diff.
|
||||
*/
|
||||
public function generateDiff($filename, $fileLines, $coverageData) {
|
||||
$output = '';
|
||||
$diff = array();
|
||||
|
||||
list($covered, $total) = $this->_calculateCoveredLines($fileLines, $coverageData);
|
||||
$this->_covered += $covered;
|
||||
$this->_total += $total;
|
||||
|
||||
//shift line numbers forward one;
|
||||
array_unshift($fileLines, ' ');
|
||||
unset($fileLines[0]);
|
||||
|
||||
foreach ($fileLines as $lineno => $line) {
|
||||
$class = 'ignored';
|
||||
$coveringTests = array();
|
||||
if (!empty($coverageData[$lineno]) && is_array($coverageData[$lineno])) {
|
||||
$coveringTests = array();
|
||||
foreach ($coverageData[$lineno] as $test) {
|
||||
$class = (is_array($test) && isset($test['id'])) ? $test['id'] : $test;
|
||||
$testReflection = new ReflectionClass(current(explode('::', $class)));
|
||||
$this->_testNames[] = $this->_guessSubjectName($testReflection);
|
||||
$coveringTests[] = $class;
|
||||
}
|
||||
$class = 'covered';
|
||||
} elseif (isset($coverageData[$lineno]) && ($coverageData[$lineno] === -1 || $coverageData[$lineno] === array())) {
|
||||
$class = 'uncovered';
|
||||
} elseif (array_key_exists($lineno, $coverageData) && ($coverageData[$lineno] === -2 || $coverageData[$lineno] === null)) {
|
||||
$class .= ' dead';
|
||||
}
|
||||
$diff[] = $this->_paintLine($line, $lineno, $class, $coveringTests);
|
||||
}
|
||||
|
||||
$percentCovered = 100;
|
||||
if ($total > 0) {
|
||||
$percentCovered = round(100 * $covered / $total, 2);
|
||||
}
|
||||
$output .= $this->coverageHeader($filename, $percentCovered);
|
||||
$output .= implode("", $diff);
|
||||
$output .= $this->coverageFooter();
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess the class name the test was for based on the test case filename.
|
||||
*
|
||||
* @param ReflectionClass $testReflection The class to reflect
|
||||
* @return string Possible test subject name.
|
||||
*/
|
||||
protected function _guessSubjectName($testReflection) {
|
||||
$basename = basename($testReflection->getFilename());
|
||||
if (strpos($basename, '.test') !== false) {
|
||||
list($subject, ) = explode('.', $basename, 2);
|
||||
return $subject;
|
||||
}
|
||||
$subject = str_replace('Test.php', '', $basename);
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the HTML for a single line in the HTML diff.
|
||||
*
|
||||
* @param string $line The line content.
|
||||
* @param int $linenumber The line number
|
||||
* @param string $class The classname to use.
|
||||
* @param array $coveringTests The tests covering the line.
|
||||
* @return string
|
||||
*/
|
||||
protected function _paintLine($line, $linenumber, $class, $coveringTests) {
|
||||
$coveredBy = '';
|
||||
if (!empty($coveringTests)) {
|
||||
$coveredBy = "Covered by:\n";
|
||||
foreach ($coveringTests as $test) {
|
||||
$coveredBy .= $test . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<div class="code-line %s" title="%s"><span class="line-num">%s</span><span class="content">%s</span></div>',
|
||||
$class,
|
||||
$coveredBy,
|
||||
$linenumber,
|
||||
htmlspecialchars($line)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* generate some javascript for the coverage report.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function coverageScript() {
|
||||
return <<<HTML
|
||||
<script type="text/javascript">
|
||||
function coverage_show_hide(selector) {
|
||||
var element = document.getElementById(selector);
|
||||
element.style.display = (element.style.display === 'none') ? '' : 'none';
|
||||
}
|
||||
function coverage_toggle_all() {
|
||||
var divs = document.querySelectorAll('div.coverage-container');
|
||||
var i = divs.length;
|
||||
while (i--) {
|
||||
if (divs[i] && divs[i].className.indexOf('primary') == -1) {
|
||||
divs[i].style.display = (divs[i].style.display === 'none') ? '' : 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an HTML snippet for coverage headers
|
||||
*
|
||||
* @param string $filename The file name being covered
|
||||
* @param string $percent The percentage covered
|
||||
* @return string
|
||||
*/
|
||||
public function coverageHeader($filename, $percent) {
|
||||
$filename = basename($filename);
|
||||
list($file) = explode('.', $filename);
|
||||
$display = in_array($file, $this->_testNames) ? 'block' : 'none';
|
||||
$primary = $display === 'block' ? 'primary' : '';
|
||||
return <<<HTML
|
||||
<div class="coverage-container $primary" style="display:$display;">
|
||||
<h4>
|
||||
<a href="#coverage-$filename" onclick="coverage_show_hide('coverage-$filename');">
|
||||
$filename Code coverage: $percent%
|
||||
</a>
|
||||
</h4>
|
||||
<div class="code-coverage-results" id="coverage-$filename" style="display:none;">
|
||||
<pre>
|
||||
HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an HTML snippet for coverage footers
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function coverageFooter() {
|
||||
return "</pre></div></div>";
|
||||
}
|
||||
|
||||
}
|
64
lib/Cake/TestSuite/Coverage/TextCoverageReport.php
Normal file
64
lib/Cake/TestSuite/Coverage/TextCoverageReport.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
/**
|
||||
* Generates code coverage reports in Simple plain text from data obtained from PHPUnit
|
||||
*
|
||||
* PHP5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.TestSuite.Coverage
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('BaseCoverageReport', 'TestSuite/Coverage');
|
||||
|
||||
/**
|
||||
* Generates code coverage reports in Simple plain text from data obtained from PHPUnit
|
||||
*
|
||||
* @package Cake.TestSuite.Coverage
|
||||
*/
|
||||
class TextCoverageReport extends BaseCoverageReport {
|
||||
|
||||
/**
|
||||
* Generates report text to display.
|
||||
*
|
||||
* @return string compiled plain text report.
|
||||
*/
|
||||
public function report() {
|
||||
$pathFilter = $this->getPathFilter();
|
||||
$coverageData = $this->filterCoverageDataByPath($pathFilter);
|
||||
if (empty($coverageData)) {
|
||||
return 'No files to generate coverage for';
|
||||
}
|
||||
$output = "\nCoverage Report:\n\n";
|
||||
foreach ($coverageData as $file => $coverageData) {
|
||||
$fileData = file($file);
|
||||
$output .= $this->generateDiff($file, $fileData, $coverageData);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a 'diff' report for a file.
|
||||
* Since diffs are too big for plain text reports a simple file => % covered is done.
|
||||
*
|
||||
* @param string $filename Name of the file having coverage generated
|
||||
* @param array $fileLines File data as an array. See file() for how to get one of these.
|
||||
* @param array $coverageData Array of coverage data to use to generate HTML diffs with
|
||||
* @return string
|
||||
*/
|
||||
public function generateDiff($filename, $fileLines, $coverageData) {
|
||||
list($covered, $total) = $this->_calculateCoveredLines($fileLines, $coverageData);
|
||||
$percentCovered = round(100 * $covered / $total, 2);
|
||||
return "$filename : $percentCovered%\n";
|
||||
}
|
||||
|
||||
}
|
306
lib/Cake/TestSuite/Fixture/CakeFixtureManager.php
Normal file
306
lib/Cake/TestSuite/Fixture/CakeFixtureManager.php
Normal file
|
@ -0,0 +1,306 @@
|
|||
<?php
|
||||
/**
|
||||
* A factory class to manage the life cycle of test fixtures
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.TestSuite.Fixture
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('ConnectionManager', 'Model');
|
||||
App::uses('ClassRegistry', 'Utility');
|
||||
|
||||
/**
|
||||
* A factory class to manage the life cycle of test fixtures
|
||||
*
|
||||
* @package Cake.TestSuite.Fixture
|
||||
*/
|
||||
class CakeFixtureManager {
|
||||
|
||||
/**
|
||||
* Was this class already initialized?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_initialized = false;
|
||||
|
||||
/**
|
||||
* Default datasource to use
|
||||
*
|
||||
* @var DataSource
|
||||
*/
|
||||
protected $_db = null;
|
||||
|
||||
/**
|
||||
* Holds the fixture classes that where instantiated
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_loaded = array();
|
||||
|
||||
/**
|
||||
* Holds the fixture classes that where instantiated indexed by class name
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_fixtureMap = array();
|
||||
|
||||
/**
|
||||
* Inspects the test to look for unloaded fixtures and loads them
|
||||
*
|
||||
* @param CakeTestCase $test the test case to inspect
|
||||
* @return void
|
||||
*/
|
||||
public function fixturize($test) {
|
||||
if (!$this->_initialized) {
|
||||
ClassRegistry::config(array('ds' => 'test', 'testing' => true));
|
||||
}
|
||||
if (empty($test->fixtures) || !empty($this->_processed[get_class($test)])) {
|
||||
$test->db = $this->_db;
|
||||
return;
|
||||
}
|
||||
$this->_initDb();
|
||||
$test->db = $this->_db;
|
||||
if (!is_array($test->fixtures)) {
|
||||
$test->fixtures = array_map('trim', explode(',', $test->fixtures));
|
||||
}
|
||||
if (isset($test->fixtures)) {
|
||||
$this->_loadFixtures($test->fixtures);
|
||||
}
|
||||
|
||||
$this->_processed[get_class($test)] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this class with a DataSource object to use as default for all fixtures
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _initDb() {
|
||||
if ($this->_initialized) {
|
||||
return;
|
||||
}
|
||||
$db = ConnectionManager::getDataSource('test');
|
||||
$db->cacheSources = false;
|
||||
$this->_db = $db;
|
||||
$this->_initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the fixture path included in test cases, to get the fixture class name, and the
|
||||
* real fixture path including sub-directories
|
||||
*
|
||||
* @param string $fixturePath the fixture path to parse
|
||||
* @return array containing fixture class name and optional additional path
|
||||
*/
|
||||
protected function _parseFixturePath($fixturePath) {
|
||||
$pathTokenArray = explode('/', $fixturePath);
|
||||
$fixture = array_pop($pathTokenArray);
|
||||
$additionalPath = '';
|
||||
foreach ($pathTokenArray as $pathToken) {
|
||||
$additionalPath .= DS . $pathToken;
|
||||
}
|
||||
return array('fixture' => $fixture, 'additionalPath' => $additionalPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for fixture files and instantiates the classes accordingly
|
||||
*
|
||||
* @param array $fixtures the fixture names to load using the notation {type}.{name}
|
||||
* @return void
|
||||
* @throws UnexpectedValueException when a referenced fixture does not exist.
|
||||
*/
|
||||
protected function _loadFixtures($fixtures) {
|
||||
foreach ($fixtures as $fixture) {
|
||||
$fixtureFile = null;
|
||||
$fixtureIndex = $fixture;
|
||||
if (isset($this->_loaded[$fixture])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strpos($fixture, 'core.') === 0) {
|
||||
$fixture = substr($fixture, strlen('core.'));
|
||||
$fixturePaths[] = CAKE . 'Test' . DS . 'Fixture';
|
||||
} elseif (strpos($fixture, 'app.') === 0) {
|
||||
$fixturePrefixLess = substr($fixture, strlen('app.'));
|
||||
$fixtureParsedPath = $this->_parseFixturePath($fixturePrefixLess);
|
||||
$fixture = $fixtureParsedPath['fixture'];
|
||||
$fixturePaths = array(
|
||||
TESTS . 'Fixture' . $fixtureParsedPath['additionalPath']
|
||||
);
|
||||
} elseif (strpos($fixture, 'plugin.') === 0) {
|
||||
$explodedFixture = explode('.', $fixture, 3);
|
||||
$pluginName = $explodedFixture[1];
|
||||
$fixtureParsedPath = $this->_parseFixturePath($explodedFixture[2]);
|
||||
$fixture = $fixtureParsedPath['fixture'];
|
||||
$fixturePaths = array(
|
||||
CakePlugin::path(Inflector::camelize($pluginName)) . 'Test' . DS . 'Fixture' . $fixtureParsedPath['additionalPath'],
|
||||
TESTS . 'Fixture' . $fixtureParsedPath['additionalPath']
|
||||
);
|
||||
} else {
|
||||
$fixturePaths = array(
|
||||
TESTS . 'Fixture',
|
||||
CAKE . 'Test' . DS . 'Fixture'
|
||||
);
|
||||
}
|
||||
|
||||
$loaded = false;
|
||||
foreach ($fixturePaths as $path) {
|
||||
$className = Inflector::camelize($fixture);
|
||||
if (is_readable($path . DS . $className . 'Fixture.php')) {
|
||||
$fixtureFile = $path . DS . $className . 'Fixture.php';
|
||||
require_once $fixtureFile;
|
||||
$fixtureClass = $className . 'Fixture';
|
||||
$this->_loaded[$fixtureIndex] = new $fixtureClass();
|
||||
$this->_fixtureMap[$fixtureClass] = $this->_loaded[$fixtureIndex];
|
||||
$loaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$loaded) {
|
||||
$firstPath = str_replace(array(APP, CAKE_CORE_INCLUDE_PATH, ROOT), '', $fixturePaths[0] . DS . $className . 'Fixture.php');
|
||||
throw new UnexpectedValueException(__d('cake_dev', 'Referenced fixture class %s (%s) not found', $className, $firstPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the drop and create commands on the fixtures if necessary.
|
||||
*
|
||||
* @param CakeTestFixture $fixture the fixture object to create
|
||||
* @param DataSource $db the datasource instance to use
|
||||
* @param bool $drop whether drop the fixture if it is already created or not
|
||||
* @return void
|
||||
*/
|
||||
protected function _setupTable($fixture, $db = null, $drop = true) {
|
||||
if (!$db) {
|
||||
if (!empty($fixture->useDbConfig)) {
|
||||
$db = ConnectionManager::getDataSource($fixture->useDbConfig);
|
||||
} else {
|
||||
$db = $this->_db;
|
||||
}
|
||||
}
|
||||
if (!empty($fixture->created) && in_array($db->configKeyName, $fixture->created)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sources = (array)$db->listSources();
|
||||
$table = $db->config['prefix'] . $fixture->table;
|
||||
$exists = in_array($table, $sources);
|
||||
|
||||
if ($drop && $exists) {
|
||||
$fixture->drop($db);
|
||||
$fixture->create($db);
|
||||
} elseif (!$exists) {
|
||||
$fixture->create($db);
|
||||
} else {
|
||||
$fixture->created[] = $db->configKeyName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the fixtures tables and inserts data on them.
|
||||
*
|
||||
* @param CakeTestCase $test the test to inspect for fixture loading
|
||||
* @return void
|
||||
*/
|
||||
public function load(CakeTestCase $test) {
|
||||
if (empty($test->fixtures)) {
|
||||
return;
|
||||
}
|
||||
$fixtures = $test->fixtures;
|
||||
if (empty($fixtures) || !$test->autoFixtures) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($fixtures as $f) {
|
||||
if (!empty($this->_loaded[$f])) {
|
||||
$fixture = $this->_loaded[$f];
|
||||
$db = ConnectionManager::getDataSource($fixture->useDbConfig);
|
||||
$db->begin();
|
||||
$this->_setupTable($fixture, $db, $test->dropTables);
|
||||
$fixture->truncate($db);
|
||||
$fixture->insert($db);
|
||||
$db->commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates the fixtures tables
|
||||
*
|
||||
* @param CakeTestCase $test the test to inspect for fixture unloading
|
||||
* @return void
|
||||
*/
|
||||
public function unload(CakeTestCase $test) {
|
||||
$fixtures = !empty($test->fixtures) ? $test->fixtures : array();
|
||||
foreach (array_reverse($fixtures) as $f) {
|
||||
if (isset($this->_loaded[$f])) {
|
||||
$fixture = $this->_loaded[$f];
|
||||
if (!empty($fixture->created)) {
|
||||
foreach ($fixture->created as $ds) {
|
||||
$db = ConnectionManager::getDataSource($ds);
|
||||
$fixture->truncate($db);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a single fixture table and loads data into it.
|
||||
*
|
||||
* @param string $name of the fixture
|
||||
* @param DataSource $db DataSource instance or leave null to get DataSource from the fixture
|
||||
* @param bool $dropTables Whether or not tables should be dropped and re-created.
|
||||
* @return void
|
||||
* @throws UnexpectedValueException if $name is not a previously loaded class
|
||||
*/
|
||||
public function loadSingle($name, $db = null, $dropTables = true) {
|
||||
$name .= 'Fixture';
|
||||
if (isset($this->_fixtureMap[$name])) {
|
||||
$fixture = $this->_fixtureMap[$name];
|
||||
if (!$db) {
|
||||
$db = ConnectionManager::getDataSource($fixture->useDbConfig);
|
||||
}
|
||||
$this->_setupTable($fixture, $db, $dropTables);
|
||||
$fixture->truncate($db);
|
||||
$fixture->insert($db);
|
||||
} else {
|
||||
throw new UnexpectedValueException(__d('cake_dev', 'Referenced fixture class %s not found', $name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop all fixture tables loaded by this class
|
||||
*
|
||||
* This will also close the session, as failing to do so will cause
|
||||
* fatal errors with database sessions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function shutDown() {
|
||||
if (session_id()) {
|
||||
session_write_close();
|
||||
}
|
||||
foreach ($this->_loaded as $fixture) {
|
||||
if (!empty($fixture->created)) {
|
||||
foreach ($fixture->created as $ds) {
|
||||
$db = ConnectionManager::getDataSource($ds);
|
||||
$fixture->drop($db);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
328
lib/Cake/TestSuite/Fixture/CakeTestFixture.php
Normal file
328
lib/Cake/TestSuite/Fixture/CakeTestFixture.php
Normal file
|
@ -0,0 +1,328 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
|
||||
* @package Cake.TestSuite.Fixture
|
||||
* @since CakePHP(tm) v 1.2.0.4667
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('CakeSchema', 'Model');
|
||||
|
||||
/**
|
||||
* CakeTestFixture is responsible for building and destroying tables to be used
|
||||
* during testing.
|
||||
*
|
||||
* @package Cake.TestSuite.Fixture
|
||||
*/
|
||||
class CakeTestFixture {
|
||||
|
||||
/**
|
||||
* Name of the object
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = null;
|
||||
|
||||
/**
|
||||
* CakePHP's DBO driver (e.g: DboMysql).
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
public $db = null;
|
||||
|
||||
/**
|
||||
* Fixture Datasource
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $useDbConfig = 'test';
|
||||
|
||||
/**
|
||||
* Full Table Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $table = null;
|
||||
|
||||
/**
|
||||
* List of datasources where this fixture has been created
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $created = array();
|
||||
|
||||
/**
|
||||
* Fields / Schema for the fixture.
|
||||
* This array should match the output of Model::schema()
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $fields = array();
|
||||
|
||||
/**
|
||||
* Fixture records to be inserted.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $records = array();
|
||||
|
||||
/**
|
||||
* The primary key for the table this fixture represents.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $primaryKey = null;
|
||||
|
||||
/**
|
||||
* Fixture data can be stored in memory by default.
|
||||
* When table is created for a fixture the MEMORY engine is used
|
||||
* where possible. Set $canUseMemory to false if you don't want this.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $canUseMemory = true;
|
||||
|
||||
/**
|
||||
* Instantiate the fixture.
|
||||
*
|
||||
* @throws CakeException on invalid datasource usage.
|
||||
*/
|
||||
public function __construct() {
|
||||
if ($this->name === null) {
|
||||
if (preg_match('/^(.*)Fixture$/', get_class($this), $matches)) {
|
||||
$this->name = $matches[1];
|
||||
} else {
|
||||
$this->name = get_class($this);
|
||||
}
|
||||
}
|
||||
$connection = 'test';
|
||||
if (!empty($this->useDbConfig)) {
|
||||
$connection = $this->useDbConfig;
|
||||
if (strpos($connection, 'test') !== 0) {
|
||||
$message = __d(
|
||||
'cake_dev',
|
||||
'Invalid datasource name "%s" for "%s" fixture. Fixture datasource names must begin with "test".',
|
||||
$connection,
|
||||
$this->name
|
||||
);
|
||||
throw new CakeException($message);
|
||||
}
|
||||
}
|
||||
$this->Schema = new CakeSchema(array('name' => 'TestSuite', 'connection' => $connection));
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the fixture.
|
||||
*
|
||||
* @return void
|
||||
* @throws MissingModelException Whe importing from a model that does not exist.
|
||||
*/
|
||||
public function init() {
|
||||
if (isset($this->import) && (is_string($this->import) || is_array($this->import))) {
|
||||
$import = array_merge(
|
||||
array('connection' => 'default', 'records' => false),
|
||||
is_array($this->import) ? $this->import : array('model' => $this->import)
|
||||
);
|
||||
|
||||
$this->Schema->connection = $import['connection'];
|
||||
if (isset($import['model'])) {
|
||||
list($plugin, $modelClass) = pluginSplit($import['model'], true);
|
||||
App::uses($modelClass, $plugin . 'Model');
|
||||
if (!class_exists($modelClass)) {
|
||||
throw new MissingModelException(array('class' => $modelClass));
|
||||
}
|
||||
$model = new $modelClass(null, null, $import['connection']);
|
||||
$db = $model->getDataSource();
|
||||
if (empty($model->tablePrefix)) {
|
||||
$model->tablePrefix = $db->config['prefix'];
|
||||
}
|
||||
$this->fields = $model->schema(true);
|
||||
$this->fields[$model->primaryKey]['key'] = 'primary';
|
||||
$this->table = $db->fullTableName($model, false, false);
|
||||
$this->primaryKey = $model->primaryKey;
|
||||
ClassRegistry::config(array('ds' => 'test'));
|
||||
ClassRegistry::flush();
|
||||
} elseif (isset($import['table'])) {
|
||||
$model = new Model(null, $import['table'], $import['connection']);
|
||||
$db = ConnectionManager::getDataSource($import['connection']);
|
||||
$db->cacheSources = false;
|
||||
$model->useDbConfig = $import['connection'];
|
||||
$model->name = Inflector::camelize(Inflector::singularize($import['table']));
|
||||
$model->table = $import['table'];
|
||||
$model->tablePrefix = $db->config['prefix'];
|
||||
$this->fields = $model->schema(true);
|
||||
$this->primaryKey = $model->primaryKey;
|
||||
ClassRegistry::flush();
|
||||
}
|
||||
|
||||
if (!empty($db->config['prefix']) && strpos($this->table, $db->config['prefix']) === 0) {
|
||||
$this->table = str_replace($db->config['prefix'], '', $this->table);
|
||||
}
|
||||
|
||||
if (isset($import['records']) && $import['records'] !== false && isset($model) && isset($db)) {
|
||||
$this->records = array();
|
||||
$query = array(
|
||||
'fields' => $db->fields($model, null, array_keys($this->fields)),
|
||||
'table' => $db->fullTableName($model),
|
||||
'alias' => $model->alias,
|
||||
'conditions' => array(),
|
||||
'order' => null,
|
||||
'limit' => null,
|
||||
'group' => null
|
||||
);
|
||||
$records = $db->fetchAll($db->buildStatement($query, $model), false, $model->alias);
|
||||
|
||||
if ($records !== false && !empty($records)) {
|
||||
$this->records = Hash::extract($records, '{n}.' . $model->alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($this->table)) {
|
||||
$this->table = Inflector::underscore(Inflector::pluralize($this->name));
|
||||
}
|
||||
|
||||
if (!isset($this->primaryKey) && isset($this->fields['id'])) {
|
||||
$this->primaryKey = 'id';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run before all tests execute, should return SQL statement to create table for this fixture could be executed successfully.
|
||||
*
|
||||
* @param DboSource $db An instance of the database object used to create the fixture table
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
public function create($db) {
|
||||
if (!isset($this->fields) || empty($this->fields)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($this->fields['tableParameters']['engine'])) {
|
||||
$canUseMemory = $this->canUseMemory;
|
||||
foreach ($this->fields as $args) {
|
||||
|
||||
if (is_string($args)) {
|
||||
$type = $args;
|
||||
} elseif (!empty($args['type'])) {
|
||||
$type = $args['type'];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($type, array('blob', 'text', 'binary'))) {
|
||||
$canUseMemory = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($canUseMemory) {
|
||||
$this->fields['tableParameters']['engine'] = 'MEMORY';
|
||||
}
|
||||
}
|
||||
$this->Schema->build(array($this->table => $this->fields));
|
||||
try {
|
||||
$db->execute($db->createSchema($this->Schema), array('log' => false));
|
||||
$this->created[] = $db->configKeyName;
|
||||
} catch (Exception $e) {
|
||||
$msg = __d(
|
||||
'cake_dev',
|
||||
'Fixture creation for "%s" failed "%s"',
|
||||
$this->table,
|
||||
$e->getMessage()
|
||||
);
|
||||
CakeLog::error($msg);
|
||||
trigger_error($msg, E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run after all tests executed, should return SQL statement to drop table for this fixture.
|
||||
*
|
||||
* @param DboSource $db An instance of the database object used to create the fixture table
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
public function drop($db) {
|
||||
if (empty($this->fields)) {
|
||||
return false;
|
||||
}
|
||||
$this->Schema->build(array($this->table => $this->fields));
|
||||
try {
|
||||
|
||||
$db->execute($db->dropSchema($this->Schema), array('log' => false));
|
||||
$this->created = array_diff($this->created, array($db->configKeyName));
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run before each tests is executed, should return a set of SQL statements to insert records for the table
|
||||
* of this fixture could be executed successfully.
|
||||
*
|
||||
* @param DboSource $db An instance of the database into which the records will be inserted
|
||||
* @return bool on success or if there are no records to insert, or false on failure
|
||||
* @throws CakeException if counts of values and fields do not match.
|
||||
*/
|
||||
public function insert($db) {
|
||||
if (!isset($this->_insert)) {
|
||||
$values = array();
|
||||
if (isset($this->records) && !empty($this->records)) {
|
||||
$fields = array();
|
||||
foreach ($this->records as $record) {
|
||||
$fields = array_merge($fields, array_keys(array_intersect_key($record, $this->fields)));
|
||||
}
|
||||
$fields = array_unique($fields);
|
||||
$default = array_fill_keys($fields, null);
|
||||
foreach ($this->records as $record) {
|
||||
$merge = array_values(array_merge($default, $record));
|
||||
if (count($fields) !== count($merge)) {
|
||||
throw new CakeException('Fixture invalid: Count of fields does not match count of values in ' . get_class($this));
|
||||
}
|
||||
$values[] = $merge;
|
||||
}
|
||||
$nested = $db->useNestedTransactions;
|
||||
$db->useNestedTransactions = false;
|
||||
$result = $db->insertMulti($this->table, $fields, $values);
|
||||
if (
|
||||
$this->primaryKey &&
|
||||
isset($this->fields[$this->primaryKey]['type']) &&
|
||||
in_array($this->fields[$this->primaryKey]['type'], array('integer', 'biginteger'))
|
||||
) {
|
||||
$db->resetSequence($this->table, $this->primaryKey);
|
||||
}
|
||||
$db->useNestedTransactions = $nested;
|
||||
return $result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates the current fixture. Can be overwritten by classes extending
|
||||
* CakeFixture to trigger other events before / after truncate.
|
||||
*
|
||||
* @param DboSource $db A reference to a db instance
|
||||
* @return bool
|
||||
*/
|
||||
public function truncate($db) {
|
||||
$fullDebug = $db->fullDebug;
|
||||
$db->fullDebug = false;
|
||||
$return = $db->truncate($this->table);
|
||||
$db->fullDebug = $fullDebug;
|
||||
return $return;
|
||||
}
|
||||
|
||||
}
|
58
lib/Cake/TestSuite/Fixture/CakeTestModel.php
Normal file
58
lib/Cake/TestSuite/Fixture/CakeTestModel.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
|
||||
* @package Cake.TestSuite.Fixture
|
||||
* @since CakePHP(tm) v 1.2.0.4667
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('Model', 'Model');
|
||||
|
||||
/**
|
||||
* A model to extend from to help you during testing.
|
||||
*
|
||||
* @package Cake.TestSuite.Fixture
|
||||
*/
|
||||
class CakeTestModel extends Model {
|
||||
|
||||
public $useDbConfig = 'test';
|
||||
|
||||
public $cacheSources = false;
|
||||
|
||||
/**
|
||||
* Sets default order for the model to avoid failing tests caused by
|
||||
* incorrect order when no order has been defined in the finds.
|
||||
* Postgres can return the results in any order it considers appropriate if none is specified
|
||||
*
|
||||
* @param int|string|array $id Set this ID for this model on startup, can also be an array of options, see above.
|
||||
* @param string $table Name of database table to use.
|
||||
* @param string $ds DataSource connection name.
|
||||
*/
|
||||
public function __construct($id = false, $table = null, $ds = null) {
|
||||
parent::__construct($id, $table, $ds);
|
||||
$this->order = array($this->alias . '.' . $this->primaryKey => 'ASC');
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriding save() to set CakeTestSuiteDispatcher::date() as formatter for created, modified and updated fields
|
||||
*
|
||||
* @param array $data Data to save
|
||||
* @param bool|array $validate Validate or options.
|
||||
* @param array $fieldList Whitelist of fields
|
||||
* @return mixed
|
||||
*/
|
||||
public function save($data = null, $validate = true, $fieldList = array()) {
|
||||
$db = $this->getDataSource();
|
||||
$db->columns['datetime']['formatter'] = 'CakeTestSuiteDispatcher::date';
|
||||
return parent::save($data, $validate, $fieldList);
|
||||
}
|
||||
|
||||
}
|
235
lib/Cake/TestSuite/Reporter/CakeBaseReporter.php
Normal file
235
lib/Cake/TestSuite/Reporter/CakeBaseReporter.php
Normal file
|
@ -0,0 +1,235 @@
|
|||
<?php
|
||||
/**
|
||||
* CakeBaseReporter contains common functionality to all cake test suite reporters.
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.3
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
require_once 'PHPUnit/TextUI/ResultPrinter.php';
|
||||
|
||||
/**
|
||||
* CakeBaseReporter contains common reporting features used in the CakePHP Test suite
|
||||
*
|
||||
* @package Cake.TestSuite.Reporter
|
||||
*/
|
||||
class CakeBaseReporter extends PHPUnit_TextUI_ResultPrinter {
|
||||
|
||||
/**
|
||||
* Headers sent
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_headerSent = false;
|
||||
|
||||
/**
|
||||
* Array of request parameters. Usually parsed GET params.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $params = array();
|
||||
|
||||
/**
|
||||
* Character set for the output of test reporting.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_characterSet;
|
||||
|
||||
/**
|
||||
* Does nothing yet. The first output will
|
||||
* be sent on the first test start.
|
||||
*
|
||||
* ### Params
|
||||
*
|
||||
* - show_passes - Should passes be shown
|
||||
* - plugin - Plugin test being run?
|
||||
* - core - Core test being run.
|
||||
* - case - The case being run
|
||||
* - codeCoverage - Whether the case/group being run is being code covered.
|
||||
*
|
||||
* @param string $charset The character set to output with. Defaults to UTF-8
|
||||
* @param array $params Array of request parameters the reporter should use. See above.
|
||||
*/
|
||||
public function __construct($charset = 'utf-8', $params = array()) {
|
||||
if (!$charset) {
|
||||
$charset = 'utf-8';
|
||||
}
|
||||
$this->_characterSet = $charset;
|
||||
$this->params = $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of test cases from the active Manager class,
|
||||
* displaying it in the correct format for the reporter subclass
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function testCaseList() {
|
||||
$testList = CakeTestLoader::generateTestList($this->params);
|
||||
return $testList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the start of the response from the test suite.
|
||||
* Used to paint things like head elements in an html page.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function paintDocumentStart() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the end of the response from the test suite.
|
||||
* Used to paint things like </body> in an html page.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function paintDocumentEnd() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Paint a list of test sets, core, app, and plugin test sets
|
||||
* available.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function paintTestMenu() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the baseUrl if one is available.
|
||||
*
|
||||
* @return string The base URL for the request.
|
||||
*/
|
||||
public function baseUrl() {
|
||||
if (!empty($_SERVER['PHP_SELF'])) {
|
||||
return $_SERVER['PHP_SELF'];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Print result
|
||||
*
|
||||
* @param PHPUnit_Framework_TestResult $result The result object
|
||||
* @return void
|
||||
*/
|
||||
public function printResult(PHPUnit_Framework_TestResult $result) {
|
||||
$this->paintFooter($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Paint result
|
||||
*
|
||||
* @param PHPUnit_Framework_TestResult $result The result object
|
||||
* @return void
|
||||
*/
|
||||
public function paintResult(PHPUnit_Framework_TestResult $result) {
|
||||
$this->paintFooter($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* An error occurred.
|
||||
*
|
||||
* @param PHPUnit_Framework_Test $test The test to add an error for.
|
||||
* @param Exception $e The exception object to add.
|
||||
* @param float $time The current time.
|
||||
* @return void
|
||||
*/
|
||||
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) {
|
||||
$this->paintException($e, $test);
|
||||
}
|
||||
|
||||
/**
|
||||
* A failure occurred.
|
||||
*
|
||||
* @param PHPUnit_Framework_Test $test The test that failed
|
||||
* @param PHPUnit_Framework_AssertionFailedError $e The assertion that failed.
|
||||
* @param float $time The current time.
|
||||
* @return void
|
||||
*/
|
||||
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {
|
||||
$this->paintFail($e, $test);
|
||||
}
|
||||
|
||||
/**
|
||||
* Incomplete test.
|
||||
*
|
||||
* @param PHPUnit_Framework_Test $test The test that was incomplete.
|
||||
* @param Exception $e The incomplete exception
|
||||
* @param float $time The current time.
|
||||
* @return void
|
||||
*/
|
||||
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) {
|
||||
$this->paintSkip($e, $test);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skipped test.
|
||||
*
|
||||
* @param PHPUnit_Framework_Test $test The test that failed.
|
||||
* @param Exception $e The skip object.
|
||||
* @param float $time The current time.
|
||||
* @return void
|
||||
*/
|
||||
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) {
|
||||
$this->paintSkip($e, $test);
|
||||
}
|
||||
|
||||
/**
|
||||
* A test suite started.
|
||||
*
|
||||
* @param PHPUnit_Framework_TestSuite $suite The suite to start
|
||||
* @return void
|
||||
*/
|
||||
public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {
|
||||
if (!$this->_headerSent) {
|
||||
echo $this->paintHeader();
|
||||
}
|
||||
echo __d('cake_dev', 'Running %s', $suite->getName()) . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* A test suite ended.
|
||||
*
|
||||
* @param PHPUnit_Framework_TestSuite $suite The suite that ended.
|
||||
* @return void
|
||||
*/
|
||||
public function endTestSuite(PHPUnit_Framework_TestSuite $suite) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A test started.
|
||||
*
|
||||
* @param PHPUnit_Framework_Test $test The test that started.
|
||||
* @return void
|
||||
*/
|
||||
public function startTest(PHPUnit_Framework_Test $test) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A test ended.
|
||||
*
|
||||
* @param PHPUnit_Framework_Test $test The test that ended
|
||||
* @param float $time The current time.
|
||||
* @return void
|
||||
*/
|
||||
public function endTest(PHPUnit_Framework_Test $test, $time) {
|
||||
$this->numAssertions += $test->getNumAssertions();
|
||||
if ($test->hasFailed()) {
|
||||
return;
|
||||
}
|
||||
$this->paintPass($test, $time);
|
||||
}
|
||||
|
||||
}
|
383
lib/Cake/TestSuite/Reporter/CakeHtmlReporter.php
Normal file
383
lib/Cake/TestSuite/Reporter/CakeHtmlReporter.php
Normal file
|
@ -0,0 +1,383 @@
|
|||
<?php
|
||||
/**
|
||||
* CakeHtmlReporter
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2.0.4433
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('CakeBaseReporter', 'TestSuite/Reporter');
|
||||
|
||||
/**
|
||||
* CakeHtmlReporter Reports Results of TestSuites and Test Cases
|
||||
* in an HTML format / context.
|
||||
*
|
||||
* @package Cake.TestSuite.Reporter
|
||||
*/
|
||||
class CakeHtmlReporter extends CakeBaseReporter {
|
||||
|
||||
/**
|
||||
* Paints the top of the web page setting the
|
||||
* title to the name of the starting test.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function paintHeader() {
|
||||
$this->_headerSent = true;
|
||||
$this->sendContentType();
|
||||
$this->sendNoCacheHeaders();
|
||||
$this->paintDocumentStart();
|
||||
$this->paintTestMenu();
|
||||
echo "<ul class='tests'>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the content-type header so it is in the correct encoding.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendContentType() {
|
||||
if (!headers_sent()) {
|
||||
header('Content-Type: text/html; charset=' . Configure::read('App.encoding'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the document start content contained in header.php
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function paintDocumentStart() {
|
||||
ob_start();
|
||||
$baseDir = $this->params['baseDir'];
|
||||
include CAKE . 'TestSuite' . DS . 'templates' . DS . 'header.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the menu on the left side of the test suite interface.
|
||||
* Contains all of the various plugin, core, and app buttons.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function paintTestMenu() {
|
||||
$cases = $this->baseUrl() . '?show=cases';
|
||||
$plugins = App::objects('plugin', null, false);
|
||||
sort($plugins);
|
||||
include CAKE . 'TestSuite' . DS . 'templates' . DS . 'menu.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves and paints the list of tests cases in an HTML format.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCaseList() {
|
||||
$testCases = parent::testCaseList();
|
||||
$core = $this->params['core'];
|
||||
$plugin = $this->params['plugin'];
|
||||
|
||||
$buffer = "<h3>App Test Cases:</h3>\n<ul>";
|
||||
$urlExtra = null;
|
||||
if ($core) {
|
||||
$buffer = "<h3>Core Test Cases:</h3>\n<ul>";
|
||||
$urlExtra = '&core=true';
|
||||
} elseif ($plugin) {
|
||||
$buffer = "<h3>" . Inflector::humanize($plugin) . " Test Cases:</h3>\n<ul>";
|
||||
$urlExtra = '&plugin=' . $plugin;
|
||||
}
|
||||
|
||||
if (count($testCases) < 1) {
|
||||
$buffer .= "<strong>EMPTY</strong>";
|
||||
}
|
||||
|
||||
foreach ($testCases as $testCase) {
|
||||
$title = explode(DS, str_replace('.test.php', '', $testCase));
|
||||
$title[count($title) - 1] = Inflector::camelize($title[count($title) - 1]);
|
||||
$title = implode(' / ', $title);
|
||||
$buffer .= "<li><a href='" . $this->baseUrl() . "?case=" . urlencode($testCase) . $urlExtra . "'>" . $title . "</a></li>\n";
|
||||
}
|
||||
$buffer .= "</ul>\n";
|
||||
echo $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the headers necessary to ensure the page is
|
||||
* reloaded on every request. Otherwise you could be
|
||||
* scratching your head over out of date test data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendNoCacheHeaders() {
|
||||
if (!headers_sent()) {
|
||||
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
|
||||
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
|
||||
header("Cache-Control: no-store, no-cache, must-revalidate");
|
||||
header("Cache-Control: post-check=0, pre-check=0", false);
|
||||
header("Pragma: no-cache");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the end of the test with a summary of
|
||||
* the passes and failures.
|
||||
*
|
||||
* @param PHPUnit_Framework_TestResult $result Result object
|
||||
* @return void
|
||||
*/
|
||||
public function paintFooter($result) {
|
||||
ob_end_flush();
|
||||
$colour = ($result->failureCount() + $result->errorCount() > 0 ? "red" : "green");
|
||||
echo "</ul>\n";
|
||||
echo "<div style=\"";
|
||||
echo "padding: 8px; margin: 1em 0; background-color: $colour; color: white;";
|
||||
echo "\">";
|
||||
echo ($result->count() - $result->skippedCount()) . "/" . $result->count();
|
||||
echo " test methods complete:\n";
|
||||
echo "<strong>" . count($result->passed()) . "</strong> passes, ";
|
||||
echo "<strong>" . $result->failureCount() . "</strong> fails, ";
|
||||
echo "<strong>" . $this->numAssertions . "</strong> assertions and ";
|
||||
echo "<strong>" . $result->errorCount() . "</strong> exceptions.";
|
||||
echo "</div>\n";
|
||||
echo '<div style="padding:0 0 5px;">';
|
||||
echo '<p><strong>Time:</strong> ' . $result->time() . ' seconds</p>';
|
||||
echo '<p><strong>Peak memory:</strong> ' . number_format(memory_get_peak_usage()) . ' bytes</p>';
|
||||
echo $this->_paintLinks();
|
||||
echo '</div>';
|
||||
if (isset($this->params['codeCoverage']) && $this->params['codeCoverage']) {
|
||||
$coverage = $result->getCodeCoverage();
|
||||
if (method_exists($coverage, 'getSummary')) {
|
||||
$report = $coverage->getSummary();
|
||||
echo $this->paintCoverage($report);
|
||||
}
|
||||
if (method_exists($coverage, 'getData')) {
|
||||
$report = $coverage->getData();
|
||||
echo $this->paintCoverage($report);
|
||||
}
|
||||
}
|
||||
$this->paintDocumentEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints a code coverage report.
|
||||
*
|
||||
* @param array $coverage The coverage data
|
||||
* @return void
|
||||
*/
|
||||
public function paintCoverage(array $coverage) {
|
||||
App::uses('HtmlCoverageReport', 'TestSuite/Coverage');
|
||||
|
||||
$reporter = new HtmlCoverageReport($coverage, $this);
|
||||
echo $reporter->report();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the links that for accessing things in the test suite.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _paintLinks() {
|
||||
$show = $query = array();
|
||||
if (!empty($this->params['case'])) {
|
||||
$show['show'] = 'cases';
|
||||
}
|
||||
|
||||
if (!empty($this->params['core'])) {
|
||||
$show['core'] = $query['core'] = 'true';
|
||||
}
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$show['plugin'] = $query['plugin'] = $this->params['plugin'];
|
||||
}
|
||||
if (!empty($this->params['case'])) {
|
||||
$query['case'] = $this->params['case'];
|
||||
}
|
||||
$show = $this->_queryString($show);
|
||||
$query = $this->_queryString($query);
|
||||
|
||||
echo "<p><a href='" . $this->baseUrl() . $show . "'>Run more tests</a> | <a href='" . $this->baseUrl() . $query . "&show_passes=1'>Show Passes</a> | \n";
|
||||
echo "<a href='" . $this->baseUrl() . $query . "&debug=1'>Enable Debug Output</a> | \n";
|
||||
echo "<a href='" . $this->baseUrl() . $query . "&code_coverage=true'>Analyze Code Coverage</a></p>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an array of parameters into a query string url
|
||||
*
|
||||
* @param array $url Url hash to be converted
|
||||
* @return string Converted url query string
|
||||
*/
|
||||
protected function _queryString($url) {
|
||||
$out = '?';
|
||||
$params = array();
|
||||
foreach ($url as $key => $value) {
|
||||
$params[] = "$key=$value";
|
||||
}
|
||||
$out .= implode('&', $params);
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the end of the document html.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function paintDocumentEnd() {
|
||||
$baseDir = $this->params['baseDir'];
|
||||
include CAKE . 'TestSuite' . DS . 'templates' . DS . 'footer.php';
|
||||
if (ob_get_length()) {
|
||||
ob_end_flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the test failure with a breadcrumbs
|
||||
* trail of the nesting test suites below the
|
||||
* top level test.
|
||||
*
|
||||
* @param PHPUnit_Framework_AssertionFailedError $message Failure object displayed in
|
||||
* the context of the other tests.
|
||||
* @param mixed $test The test case to paint a failure for.
|
||||
* @return void
|
||||
*/
|
||||
public function paintFail($message, $test) {
|
||||
$trace = $this->_getStackTrace($message);
|
||||
$testName = get_class($test) . '(' . $test->getName() . ')';
|
||||
|
||||
$actualMsg = $expectedMsg = null;
|
||||
if (method_exists($message, 'getComparisonFailure')) {
|
||||
$failure = $message->getComparisonFailure();
|
||||
if (is_object($failure)) {
|
||||
$actualMsg = $failure->getActualAsString();
|
||||
$expectedMsg = $failure->getExpectedAsString();
|
||||
}
|
||||
}
|
||||
|
||||
echo "<li class='fail'>\n";
|
||||
echo "<span>Failed</span>";
|
||||
echo "<div class='msg'><pre>" . $this->_htmlEntities($message->toString());
|
||||
|
||||
if ((is_string($actualMsg) && is_string($expectedMsg)) || (is_array($actualMsg) && is_array($expectedMsg))) {
|
||||
echo "<br />" . $this->_htmlEntities(PHPUnit_Util_Diff::diff($expectedMsg, $actualMsg));
|
||||
}
|
||||
|
||||
echo "</pre></div>\n";
|
||||
echo "<div class='msg'>" . __d('cake_dev', 'Test case: %s', $testName) . "</div>\n";
|
||||
echo "<div class='msg'>" . __d('cake_dev', 'Stack trace:') . '<br />' . $trace . "</div>\n";
|
||||
echo "</li>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the test pass with a breadcrumbs
|
||||
* trail of the nesting test suites below the
|
||||
* top level test.
|
||||
*
|
||||
* @param PHPUnit_Framework_Test $test Test method that just passed
|
||||
* @param float $time time spent to run the test method
|
||||
* @return void
|
||||
*/
|
||||
public function paintPass(PHPUnit_Framework_Test $test, $time = null) {
|
||||
if (isset($this->params['showPasses']) && $this->params['showPasses']) {
|
||||
echo "<li class='pass'>\n";
|
||||
echo "<span>Passed</span> ";
|
||||
|
||||
echo "<br />" . $this->_htmlEntities($test->getName()) . " ($time seconds)\n";
|
||||
echo "</li>\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints a PHP exception.
|
||||
*
|
||||
* @param Exception $message Exception to display.
|
||||
* @param mixed $test The test that failed.
|
||||
* @return void
|
||||
*/
|
||||
public function paintException($message, $test) {
|
||||
$trace = $this->_getStackTrace($message);
|
||||
$testName = get_class($test) . '(' . $test->getName() . ')';
|
||||
|
||||
echo "<li class='fail'>\n";
|
||||
echo "<span>" . get_class($message) . "</span>";
|
||||
|
||||
echo "<div class='msg'>" . $this->_htmlEntities($message->getMessage()) . "</div>\n";
|
||||
echo "<div class='msg'>" . __d('cake_dev', 'Test case: %s', $testName) . "</div>\n";
|
||||
echo "<div class='msg'>" . __d('cake_dev', 'Stack trace:') . '<br />' . $trace . "</div>\n";
|
||||
echo "</li>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the message for skipping tests.
|
||||
*
|
||||
* @param string $message Text of skip condition.
|
||||
* @param PHPUnit_Framework_TestCase $test the test method skipped
|
||||
* @return void
|
||||
*/
|
||||
public function paintSkip($message, $test) {
|
||||
echo "<li class='skipped'>\n";
|
||||
echo "<span>Skipped</span> ";
|
||||
echo $test->getName() . ': ' . $this->_htmlEntities($message->getMessage());
|
||||
echo "</li>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints formatted text such as dumped variables.
|
||||
*
|
||||
* @param string $message Text to show.
|
||||
* @return void
|
||||
*/
|
||||
public function paintFormattedMessage($message) {
|
||||
echo '<pre>' . $this->_htmlEntities($message) . '</pre>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Character set adjusted entity conversion.
|
||||
*
|
||||
* @param string $message Plain text or Unicode message.
|
||||
* @return string Browser readable message.
|
||||
*/
|
||||
protected function _htmlEntities($message) {
|
||||
return htmlentities($message, ENT_COMPAT, $this->_characterSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a formatted stack trace.
|
||||
*
|
||||
* @param Exception $e Exception to get a stack trace for.
|
||||
* @return string Generated stack trace.
|
||||
*/
|
||||
protected function _getStackTrace(Exception $e) {
|
||||
$trace = $e->getTrace();
|
||||
$out = array();
|
||||
foreach ($trace as $frame) {
|
||||
if (isset($frame['file']) && isset($frame['line'])) {
|
||||
$out[] = $frame['file'] . ' : ' . $frame['line'];
|
||||
} elseif (isset($frame['class']) && isset($frame['function'])) {
|
||||
$out[] = $frame['class'] . '::' . $frame['function'];
|
||||
} else {
|
||||
$out[] = '[internal]';
|
||||
}
|
||||
}
|
||||
return implode('<br />', $out);
|
||||
}
|
||||
|
||||
/**
|
||||
* A test suite started.
|
||||
*
|
||||
* @param PHPUnit_Framework_TestSuite $suite The test suite to start.
|
||||
* @return void
|
||||
*/
|
||||
public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {
|
||||
if (!$this->_headerSent) {
|
||||
echo $this->paintHeader();
|
||||
}
|
||||
echo '<h2>' . __d('cake_dev', 'Running %s', $suite->getName()) . '</h2>';
|
||||
}
|
||||
|
||||
}
|
183
lib/Cake/TestSuite/Reporter/CakeTextReporter.php
Normal file
183
lib/Cake/TestSuite/Reporter/CakeTextReporter.php
Normal file
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
/**
|
||||
* CakeTextReporter contains reporting features used for plain text based output
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.3
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('CakeBaseReporter', 'TestSuite/Reporter');
|
||||
App::uses('TextCoverageReport', 'TestSuite/Coverage');
|
||||
|
||||
/**
|
||||
* CakeTextReporter contains reporting features used for plain text based output
|
||||
*
|
||||
* @package Cake.TestSuite.Reporter
|
||||
*/
|
||||
class CakeTextReporter extends CakeBaseReporter {
|
||||
|
||||
/**
|
||||
* Sets the text/plain header if the test is not a CLI test.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function paintDocumentStart() {
|
||||
if (!headers_sent()) {
|
||||
header('Content-type: text/plain');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints a pass
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function paintPass() {
|
||||
echo '.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints a failing test.
|
||||
*
|
||||
* @param PHPUnit_Framework_AssertionFailedError $message Failure object displayed in
|
||||
* the context of the other tests.
|
||||
* @return void
|
||||
*/
|
||||
public function paintFail($message) {
|
||||
$context = $message->getTrace();
|
||||
$realContext = $context[3];
|
||||
$context = $context[2];
|
||||
|
||||
printf(
|
||||
"FAIL on line %s\n%s in\n%s %s()\n\n",
|
||||
$context['line'], $message->toString(), $context['file'], $realContext['function']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the end of the test with a summary of
|
||||
* the passes and failures.
|
||||
*
|
||||
* @param PHPUnit_Framework_TestResult $result Result object
|
||||
* @return void
|
||||
*/
|
||||
public function paintFooter($result) {
|
||||
if ($result->failureCount() + $result->errorCount()) {
|
||||
echo "FAILURES!!!\n";
|
||||
} else {
|
||||
echo "\nOK\n";
|
||||
}
|
||||
|
||||
echo "Test cases run: " . $result->count() .
|
||||
"/" . ($result->count() - $result->skippedCount()) .
|
||||
', Passes: ' . $this->numAssertions .
|
||||
', Failures: ' . $result->failureCount() .
|
||||
', Exceptions: ' . $result->errorCount() . "\n";
|
||||
|
||||
echo 'Time: ' . $result->time() . " seconds\n";
|
||||
echo 'Peak memory: ' . number_format(memory_get_peak_usage()) . " bytes\n";
|
||||
|
||||
if (isset($this->params['codeCoverage']) && $this->params['codeCoverage']) {
|
||||
$coverage = $result->getCodeCoverage()->getSummary();
|
||||
echo $this->paintCoverage($coverage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the title only.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function paintHeader() {
|
||||
$this->paintDocumentStart();
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints a PHP exception.
|
||||
*
|
||||
* @param Exception $exception Exception to describe.
|
||||
* @return void
|
||||
*/
|
||||
public function paintException($exception) {
|
||||
$message = 'Unexpected exception of type [' . get_class($exception) .
|
||||
'] with message [' . $exception->getMessage() .
|
||||
'] in [' . $exception->getFile() .
|
||||
' line ' . $exception->getLine() . ']';
|
||||
echo $message . "\n\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the message for skipping tests.
|
||||
*
|
||||
* @param string $message Text of skip condition.
|
||||
* @return void
|
||||
*/
|
||||
public function paintSkip($message) {
|
||||
printf("Skip: %s\n", $message->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints formatted text such as dumped variables.
|
||||
*
|
||||
* @param string $message Text to show.
|
||||
* @return void
|
||||
*/
|
||||
public function paintFormattedMessage($message) {
|
||||
echo "$message\n";
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a test case list in plain text.
|
||||
* Creates as series of URLs for tests that can be run.
|
||||
* One case per line.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCaseList() {
|
||||
$testCases = parent::testCaseList();
|
||||
$app = $this->params['app'];
|
||||
$plugin = $this->params['plugin'];
|
||||
|
||||
$buffer = "Core Test Cases:\n";
|
||||
if ($app) {
|
||||
$buffer = "App Test Cases:\n";
|
||||
} elseif ($plugin) {
|
||||
$buffer = Inflector::humanize($plugin) . " Test Cases:\n";
|
||||
}
|
||||
|
||||
if (count($testCases) < 1) {
|
||||
$buffer .= 'EMPTY';
|
||||
echo $buffer;
|
||||
}
|
||||
|
||||
foreach ($testCases as $testCase) {
|
||||
$buffer .= $_SERVER['SERVER_NAME'] . $this->baseUrl() . "?case=" . $testCase . "&output=text\n";
|
||||
}
|
||||
|
||||
$buffer .= "\n";
|
||||
echo $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a Text summary of the coverage data.
|
||||
*
|
||||
* @param array $coverage Array of coverage data.
|
||||
* @return void
|
||||
*/
|
||||
public function paintCoverage($coverage) {
|
||||
$reporter = new TextCoverageReport($coverage, $this);
|
||||
echo $reporter->report();
|
||||
}
|
||||
|
||||
}
|
35
lib/Cake/TestSuite/templates/footer.php
Normal file
35
lib/Cake/TestSuite/templates/footer.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
/**
|
||||
* Short description for file.
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
|
||||
* @package Cake.TestSuite.templates
|
||||
* @since CakePHP(tm) v 1.2.0.4433
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
?> </div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<p>
|
||||
<!--PLEASE USE ONE OF THE POWERED BY CAKEPHP LOGO-->
|
||||
<a href="http://www.cakephp.org/" target="_blank">
|
||||
<img src="<?php echo $baseDir; ?>img/cake.power.gif" alt="CakePHP(tm) :: Rapid Development Framework" /></a>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
App::uses('View', 'View');
|
||||
$null = null;
|
||||
$View = new View($null, false);
|
||||
echo $View->element('sql_dump');
|
||||
?>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
145
lib/Cake/TestSuite/templates/header.php
Normal file
145
lib/Cake/TestSuite/templates/header.php
Normal file
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
/**
|
||||
* Short description for file.
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
|
||||
* @package Cake.TestSuite.templates
|
||||
* @since CakePHP(tm) v 1.2.0.4433
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>CakePHP Test Suite <?php echo Configure::version(); ?></title>
|
||||
<style type="text/css">
|
||||
body h2 {color: #777;}
|
||||
h3 {font-size: 170%; padding-top: 1em}
|
||||
a {font-size: 120%}
|
||||
li {line-height: 140%}
|
||||
.test-menu {float:left; margin-right: 24px;}
|
||||
.test-results {float:left; width: 67%;}
|
||||
ul.tests {margin: 0; font-size:12px;}
|
||||
ul.tests li {
|
||||
list-style: none;
|
||||
margin: 14px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
ul.tests li span {
|
||||
font-size:14px;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
}
|
||||
ul.tests li.pass span, ul.tests li.skipped span { display:inline;}
|
||||
ul.tests li.fail span { color: red; }
|
||||
ul.tests li.pass span { color: green; }
|
||||
ul.tests li.skipped span { color: navy; }
|
||||
ul.tests li.error span { color : #d15d00; }
|
||||
|
||||
ul.tests li.pass,
|
||||
ul.tests li.error,
|
||||
ul.tests li.skipped,
|
||||
ul.tests li.fail {
|
||||
background: #fff2f2 url(<?php echo $baseDir; ?>img/test-fail-icon.png) 5px 5px no-repeat;
|
||||
border-top: 1px dotted red;
|
||||
border-bottom: 1px dotted red;
|
||||
padding:5px 10px 2px 25px;
|
||||
}
|
||||
ul.tests li.pass {
|
||||
background-color: #f2fff2;
|
||||
background-image: url(<?php echo $baseDir; ?>img/test-pass-icon.png);
|
||||
border-color:green;
|
||||
}
|
||||
ul.tests li.skipped {
|
||||
background-color: #edf1ff;
|
||||
background-image: url(<?php echo $baseDir; ?>img/test-skip-icon.png);
|
||||
border-color:navy;
|
||||
}
|
||||
ul.tests li.error {
|
||||
background-color: #ffffe5;
|
||||
background-image: url(<?php echo $baseDir; ?>img/test-error-icon.png);
|
||||
border-color: #DF6300;
|
||||
}
|
||||
ul.tests li div { margin: 5px 0 8px 0; }
|
||||
ul.tests li div.msg { font-weight: bold; }
|
||||
table caption { color:#fff; }
|
||||
|
||||
div.code-coverage-results div.code-line {
|
||||
padding-left:5px;
|
||||
display:block;
|
||||
margin-left:10px;
|
||||
}
|
||||
.coverage-toggle {
|
||||
float:right;
|
||||
margin-top:10px;
|
||||
font-size:12px;
|
||||
}
|
||||
.coverage-container {
|
||||
margin-top:1em;
|
||||
}
|
||||
div.code-coverage-results div.uncovered span.content { background:#ecc; }
|
||||
div.code-coverage-results div.covered span.content { background:#cec; }
|
||||
div.code-coverage-results div.ignored span.content { color:#aaa; }
|
||||
div.code-coverage-results div:hover {
|
||||
background:#e8e8e8;
|
||||
cursor: pointer;
|
||||
}
|
||||
div.code-coverage-results div.covered:hover span.content { background:#b4edb4;}
|
||||
div.code-coverage-results div.uncovered:hover span.content { background:#edb4b4;}
|
||||
div.code-coverage-results span.line-num {
|
||||
color:#666;
|
||||
display:block;
|
||||
float:left;
|
||||
width:20px;
|
||||
text-align:right;
|
||||
margin-right:5px;
|
||||
}
|
||||
div.code-coverage-results span.line-num strong { color:#666; }
|
||||
div.code-coverage-results div.start {
|
||||
border:1px solid #aaa;
|
||||
border-width:1px 1px 0px 1px;
|
||||
margin-top:30px;
|
||||
padding-top:5px;
|
||||
}
|
||||
div.code-coverage-results div.end {
|
||||
border:1px solid #aaa;
|
||||
border-width:0px 1px 1px 1px;
|
||||
margin-bottom:30px;
|
||||
padding-bottom:5px;
|
||||
}
|
||||
div.code-coverage-results div.realstart { margin-top:0px; }
|
||||
div.code-coverage-results p.note {
|
||||
color:#bbb;
|
||||
padding:5px;
|
||||
margin:5px 0 10px;
|
||||
font-size:10px;
|
||||
}
|
||||
div.code-coverage-results span.result-bad { color: #a00; }
|
||||
div.code-coverage-results span.result-ok { color: #fa0; }
|
||||
div.code-coverage-results span.result-good { color: #0a0; }
|
||||
|
||||
div#version {
|
||||
padding-top: 2px;
|
||||
float: right;
|
||||
padding-left: 20px;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="<?php echo $baseDir; ?>css/cake.generic.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="header">
|
||||
<div id="version">PHPUnit: <?php echo class_exists('PHPUnit_Runner_Version') ? PHPUnit_Runner_Version::id() : 'n/a'; ?></div>
|
||||
<h1>CakePHP: the rapid development php framework</h1>
|
||||
</div>
|
||||
<div id="content">
|
||||
<h2>CakePHP Test Suite <?php echo Configure::version(); ?></h2>
|
51
lib/Cake/TestSuite/templates/menu.php
Normal file
51
lib/Cake/TestSuite/templates/menu.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
// @codingStandardsIgnoreFile
|
||||
/**
|
||||
* Short description for file.
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
|
||||
* @package Cake.TestSuite.templates
|
||||
* @since CakePHP(tm) v 1.2.0.4433
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
?>
|
||||
<div class="test-menu">
|
||||
<ul>
|
||||
<li>
|
||||
<span style="font-size: 18px">App</span>
|
||||
<ul>
|
||||
<li><a href='<?php echo $cases; ?>'>Tests</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<?php if (!empty($plugins)): ?>
|
||||
<li style="padding-top: 10px">
|
||||
<span style="font-size: 18px">Plugins</span>
|
||||
<?php foreach ($plugins as $plugin) : ?>
|
||||
<ul>
|
||||
<li style="padding-top: 10px">
|
||||
<span style="font-size: 18px"><?php echo $plugin; ?></span>
|
||||
<ul>
|
||||
<li><?php printf('<a href="%s&plugin=%s">Tests</a>', $cases, $plugin); ?></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<?php endforeach; ?>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<li style="padding-top: 10px">
|
||||
<span style="font-size: 18px">Core</span>
|
||||
<ul>
|
||||
<li><a href='<?php echo $cases; ?>&core=true'>Tests</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="test-results">
|
26
lib/Cake/TestSuite/templates/missing_connection.php
Normal file
26
lib/Cake/TestSuite/templates/missing_connection.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/**
|
||||
* Missing Connection error page
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
|
||||
* @package Cake.TestSuite.templates
|
||||
* @since CakePHP(tm) v 1.2.0.4433
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
?>
|
||||
<?php include dirname(__FILE__) . DS . 'header.php'; ?>
|
||||
<div id="content">
|
||||
<h2>Missing Test Database Connection</h2>
|
||||
<h3><?php echo $exception->getMessage(); ?></h3>
|
||||
<pre><?php echo $exception->getTraceAsString(); ?></pre>
|
||||
</div>
|
||||
<?php
|
||||
include dirname(__FILE__) . DS . 'footer.php';
|
34
lib/Cake/TestSuite/templates/phpunit.php
Normal file
34
lib/Cake/TestSuite/templates/phpunit.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
/**
|
||||
* Missing PHPUnit error page.
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
|
||||
* @package Cake.TestSuite.templates
|
||||
* @since CakePHP(tm) v 1.2.0.4433
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
?>
|
||||
<?php include dirname(__FILE__) . DS . 'header.php'; ?>
|
||||
<div id="content">
|
||||
<h2>PHPUnit is not installed!</h2>
|
||||
<p>You must install PHPUnit to use the CakePHP(tm) Test Suite.</p>
|
||||
<p>PHPUnit can be installed with pear, using the pear installer.</p>
|
||||
<p>To install with the PEAR installer run the following commands:</p>
|
||||
<ul>
|
||||
<li><code>pear config-set auto_discover 1</code></li>
|
||||
<li><code>pear install pear.phpunit.de/PHPUnit</code></li>
|
||||
</ul>
|
||||
<p>Once PHPUnit is installed make sure its located on PHP's <code>include_path</code> by checking your php.ini</p>
|
||||
<p>For full instructions on how to <a href="http://www.phpunit.de/manual/current/en/installation.html" target="_blank">install PHPUnit, see the PHPUnit installation guide</a>.</p>
|
||||
<p><a href="https://github.com/sebastianbergmann/phpunit" target="_blank">Download PHPUnit</a></p>
|
||||
</div>
|
||||
<?php
|
||||
include dirname(__FILE__) . DS . 'footer.php';
|
26
lib/Cake/TestSuite/templates/xdebug.php
Normal file
26
lib/Cake/TestSuite/templates/xdebug.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/**
|
||||
* Xdebug error page
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
|
||||
* @package Cake.TestSuite.templates
|
||||
* @since CakePHP(tm) v 1.2.0.4433
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
?>
|
||||
<?php include dirname(__FILE__) . DS . 'header.php'; ?>
|
||||
<div id="content">
|
||||
<h2>Xdebug is not installed</h2>
|
||||
<p>You must install Xdebug to use the CakePHP(tm) Code Coverage Analyzation.</p>
|
||||
<p><a href="http://www.xdebug.org/docs/install" target="_blank">Learn How To Install Xdebug</a></p>
|
||||
</div>
|
||||
<?php
|
||||
include dirname(__FILE__) . DS . 'footer.php';
|
Loading…
Add table
Add a link
Reference in a new issue