123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058 |
- <?php
- /**
- * Lithium: the most rad php framework
- *
- * @copyright Copyright 2013, Union of RAD (http://union-of-rad.org)
- * @license http://opensource.org/licenses/bsd-license.php The BSD License
- */
- namespace lithium\test;
- use Exception;
- use ReflectionClass;
- use InvalidArgumentException;
- use lithium\util\String;
- use lithium\core\Libraries;
- use lithium\util\Validator;
- use lithium\analysis\Debugger;
- use lithium\analysis\Inspector;
- use RecursiveDirectoryIterator;
- use RecursiveIteratorIterator;
- /**
- * This is the base class for all test cases. Test are performed using an assertion method. If the
- * assertion is correct, the test passes, otherwise it fails. Most assertions take an expected
- * result, a received result, and a message (to describe the failure) as parameters.
- *
- * Unit tests are used to check a small unit of functionality, such as if a
- * method returns an expected result for a known input, or whether an adapter
- * can successfully open a connection.
- *
- * Available assertions are (see `assert<assertion-name>` methods for details): Equal, False,
- * Identical, NoPattern, NotEqual, Null, Pattern, Tags, True.
- *
- * If an assertion is expected to produce an exception, the `expectException` method should be
- * called before it.
- */
- class Unit extends \lithium\core\Object {
- /**
- * The Reference to a test reporter class.
- *
- * @var string
- */
- protected $_reporter = null;
- /**
- * The list of test results.
- *
- * @var string
- */
- protected $_results = array();
- /**
- * The list of expected exceptions.
- *
- * @var string
- */
- protected $_expected = array();
- /**
- * Internal types and how to test for them
- *
- * @var array
- */
- protected static $_internalTypes = array(
- 'array' => 'is_array',
- 'bool' => 'is_bool',
- 'callable' => 'is_callable',
- 'double' => 'is_double',
- 'float' => 'is_float',
- 'int' => 'is_int',
- 'integer' => 'is_integer',
- 'long' => 'is_long',
- 'null' => 'is_null',
- 'numeric' => 'is_numeric',
- 'object' => 'is_object',
- 'real' => 'is_real',
- 'resource' => 'is_resource',
- 'scalar' => 'is_scalar',
- 'string' => 'is_string'
- );
- /**
- * Finds the test case for the corresponding class name.
- *
- * @param string $class A fully-namespaced class reference for which to find a test case.
- * @return string Returns the class name of a test case for `$class`, or `null` if none exists.
- */
- public static function get($class) {
- $parts = explode('\\', $class);
- $library = array_shift($parts);
- $name = array_pop($parts);
- $type = 'tests.cases.' . implode('.', $parts);
- return Libraries::locate($type, $name, compact('library'));
- }
- /**
- * Setup method run before every test method. override in subclasses
- *
- * @return void
- */
- public function setUp() {}
- /**
- * Teardown method run after every test method. override in subclasses
- *
- * @return void
- */
- public function tearDown() {}
- /**
- * Subclasses should use this method to set conditions that, if failed, terminate further
- * testing.
- *
- * For example:
- * {{{
- * public function skip() {
- * $this->_dbConfig = Connections::get('default', array('config' => true));
- * $hasDb = (isset($this->_dbConfig['adapter']) && $this->_dbConfig['adapter'] == 'MySql');
- * $message = 'Test database is either unavailable, or not using a MySQL adapter';
- * $this->skipIf(!$hasDb, $message);
- * }
- * }}}
- *
- * @return void
- */
- public function skip() {}
- /**
- * Skips test(s) if the condition is met.
- *
- * When used within a subclass' `skip` method, all tests are ignored if the condition is met,
- * otherwise processing continues as normal.
- * For other methods, only the remainder of the method is skipped, when the condition is met.
- *
- * @throws Exception
- * @param boolean $condition
- * @param string|boolean $message Message to pass if the condition is met.
- * @return mixed
- */
- public function skipIf($condition, $message = false) {
- if ($condition) {
- throw new Exception(is_string($message) ? $message : null);
- }
- }
- /**
- * Returns the class name that is the subject under test for this test case.
- *
- * @return string
- */
- public function subject() {
- return preg_replace('/Test$/', '', str_replace('tests\\cases\\', '', get_class($this)));
- }
- /**
- * Return test methods to run
- *
- * @return array
- */
- public function methods() {
- static $methods;
- return $methods ?: $methods = array_values(preg_grep('/^test/', get_class_methods($this)));
- }
- /**
- * Runs the test methods in this test case, with the given options.
- *
- * @param array $options The options to use when running the test. Available options are:
- * - 'methods': An arbitrary array of method names to execute. If
- * unspecified, all methods starting with 'test' are run.
- * - 'reporter': A closure which gets called after each test result,
- * which may modify the results presented.
- * @return array
- */
- public function run(array $options = array()) {
- $defaults = array('methods' => array(), 'reporter' => null, 'handler' => null);
- $options += $defaults;
- $this->_results = array();
- $self = $this;
- try {
- $this->skip();
- } catch (Exception $e) {
- $this->_handleException($e);
- return $this->_results;
- }
- $h = function($code, $message, $file, $line = 0, $context = array()) use ($self) {
- $trace = debug_backtrace();
- $trace = array_slice($trace, 1, count($trace));
- $self->invokeMethod('_reportException', array(
- compact('code', 'message', 'file', 'line', 'trace', 'context')
- ));
- };
- $options['handler'] = $options['handler'] ?: $h;
- set_error_handler($options['handler']);
- $methods = $options['methods'] ?: $this->methods();
- $this->_reporter = $options['reporter'] ?: $this->_reporter;
- foreach ($methods as $method) {
- if ($this->_runTestMethod($method, $options) === false) {
- break;
- }
- }
- restore_error_handler();
- return $this->_results;
- }
- /**
- * General assert method used by others for common output.
- *
- * @param boolean $expression
- * @param string|boolean $message The message to output. If the message is not a string,
- * then it will be converted to '{:message}'. Use '{:message}' in the string and it
- * will use the `$data` to format the message with `String::insert()`.
- * @param array $data
- * @return void
- */
- public function assert($expression, $message = false, $data = array()) {
- if (!is_string($message)) {
- $message = '{:message}';
- }
- if (strpos($message, "{:message}") !== false) {
- $params = $data;
- $params['message'] = $this->_message($params);
- $message = String::insert($message, $params);
- }
- $trace = Debugger::trace(array(
- 'start' => 1, 'depth' => 4, 'format' => 'array', 'closures' => !$expression
- ));
- $methods = $this->methods();
- $i = 1;
- while ($i < count($trace)) {
- if (in_array($trace[$i]['function'], $methods) && $trace[$i - 1]['object'] == $this) {
- break;
- }
- $i++;
- }
- $class = isset($trace[$i - 1]['object']) ? get_class($trace[$i - 1]['object']) : null;
- $method = isset($trace[$i]) ? $trace[$i]['function'] : $trace[$i - 1]['function'];
- $result = compact('class', 'method', 'message', 'data') + array(
- 'file' => $trace[$i - 1]['file'],
- 'line' => $trace[$i - 1]['line'],
- 'assertion' => $trace[$i - 1]['function']
- );
- $this->_result($expression ? 'pass' : 'fail', $result);
- return $expression;
- }
- /**
- * Generates a failed test with the passed message.
- *
- * @param string $message
- */
- public function fail($message = false) {
- $this->assert(false, $message);
- }
- /**
- * Fixes some issues regarding the used EOL character(s).
- *
- * On linux EOL is LF, on Windows it is normally CRLF, but the latter may depend also
- * on the git config core.autocrlf setting. As some tests use heredoc style (<<<) to
- * specify multiline expectations, this EOL issue may cause tests to fail only because
- * of a difference in EOL's used.
- *
- * in assertEqual, assertNotEqual, assertPattern and assertNotPattern this function is
- * called to get rid of any EOL differences.
- *
- * @param mixed $expected
- * @param mixed $result
- */
- protected function _normalizeLineEndings($expected, $result) {
- if (is_string($expected) && is_string($result)) {
- $expected = preg_replace('/\r\n/', "\n", $expected);
- $result = preg_replace('/\r\n/', "\n", $result);
- }
- return array($expected, $result);
- }
- /**
- * Checks that the actual result is equal, but not neccessarily identical, to the expected
- * result.
- *
- * @param mixed $expected
- * @param mixed $result
- * @param string|boolean $message
- */
- public function assertEqual($expected, $result, $message = false) {
- list($expected, $result) = $this->_normalizeLineEndings($expected, $result);
- $data = ($expected != $result) ? $this->_compare('equal', $expected, $result) : null;
- return $this->assert($expected == $result, $message, $data);
- }
- /**
- * Checks that the actual result and the expected result are not equal to each other.
- *
- * @param mixed $expected
- * @param mixed $result
- * @param string|boolean $message
- */
- public function assertNotEqual($expected, $result, $message = false) {
- list($expected, $result) = $this->_normalizeLineEndings($expected, $result);
- return $this->assert($result != $expected, $message, compact('expected', 'result'));
- }
- /**
- * Checks that the actual result and the expected result are identical.
- *
- * @param mixed $expected
- * @param mixed $result
- * @param string|boolean $message
- */
- public function assertIdentical($expected, $result, $message = false) {
- $data = ($expected !== $result) ? $this->_compare('identical', $expected, $result) : null;
- return $this->assert($expected === $result, $message, $data);
- }
- /**
- * Checks that the result evaluates to true.
- *
- * For example:
- * {{{
- * $this->assertTrue('false', 'String has content');
- * }}}
- * {{{
- * $this->assertTrue(10, 'Non-Zero value');
- * }}}
- * {{{
- * $this->assertTrue(true, 'Boolean true');
- * }}}
- * all evaluate to true.
- *
- * @param mixed $result
- * @param string $message
- */
- public function assertTrue($result, $message = '{:message}') {
- $expected = true;
- return $this->assert(!empty($result), $message, compact('expected', 'result'));
- }
- /**
- * Checks that the result evaluates to false.
- *
- * For example:
- * {{{
- * $this->assertFalse('', 'String is empty');
- * }}}
- *
- * {{{
- * $this->assertFalse(0, 'Zero value');
- * }}}
- *
- * {{{
- * $this->assertFalse(false, 'Boolean false');
- * }}}
- * all evaluate to false.
- *
- * @param mixed $result
- * @param string $message
- */
- public function assertFalse($result, $message = '{:message}') {
- $expected = false;
- return $this->assert(empty($result), $message, compact('expected', 'result'));
- }
- /**
- * Checks if the result is null.
- *
- * @param mixed $result
- * @param string $message
- */
- public function assertNull($result, $message = '{:message}') {
- $expected = null;
- return $this->assert($result === null, $message, compact('expected', 'result'));
- }
- /**
- * Checks that the regular expression `$expected` is not matched in the result.
- *
- * @param mixed $expected
- * @param mixed $result
- * @param string $message
- */
- public function assertNoPattern($expected, $result, $message = '{:message}') {
- list($expected, $result) = $this->_normalizeLineEndings($expected, $result);
- $params = compact('expected', 'result');
- return $this->assert(!preg_match($expected, $result), $message, $params);
- }
- /**
- * Checks that the regular expression `$expected` is matched in the result.
- *
- * @param mixed $expected
- * @param mixed $result
- * @param string $message
- */
- public function assertPattern($expected, $result, $message = '{:message}') {
- list($expected, $result) = $this->_normalizeLineEndings($expected, $result);
- $params = compact('expected', 'result');
- return $this->assert(!!preg_match($expected, $result), $message, $params);
- }
- /**
- * 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 whitespaces between specified tags.
- *
- * @param string $string An HTML/XHTML/XML string
- * @param array $expected An array, see above
- * @return boolean
- */
- public function assertTags($string, $expected) {
- $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) {
- $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('/^regex\:\/(.+)\/$/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();
- foreach ($attributes as $attr => $val) {
- if (is_numeric($attr) && preg_match('/^regex\:\/(.+)\/$/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('/^regex\:\/(.+)\/$/i', $val, $matches)
- ) {
- $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}";
- }
- }
- if ($attrs) {
- $permutations = $this->_arrayPermute($attrs);
- $permutationTokens = array();
- foreach ($permutations as $permutation) {
- $permutationTokens[] = join('', $permutation);
- }
- $regex[] = array(
- sprintf('%s', join(', ', $explanations)),
- $permutationTokens,
- $i
- );
- }
- $regex[] = array(sprintf('End %s tag', $tag), '[\s]*\/?[\s]*>[\n\r]*', $i);
- }
- }
- foreach ($regex as $i => $assertation) {
- list($description, $expressions, $itemNum) = $assertation;
- $matches = false;
- 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->assert(false, sprintf(
- '- Item #%d / regex #%d failed: %s', $itemNum, $i, $description
- ));
- return false;
- }
- }
- return $this->assert(true);
- }
- /**
- * Assert that the code passed in a closure throws an exception matching the passed expected
- * exception.
- *
- * The value passed to `exepected` is either an exception class name or the expected message.
- *
- * @param mixed $expected A string indicating what the error text is expected to be. This can
- * be an exact string, a /-delimited regular expression, or true, indicating that
- * any error text is acceptable.
- * @param closure $closure A closure containing the code that should throw the exception.
- * @param string $message
- * @return boolean
- */
- public function assertException($expected, $closure, $message = '{:message}') {
- try {
- $closure();
- $message = sprintf('An exception "%s" was expected but not thrown.', $expected);
- return $this->assert(false, $message, compact('expected', 'result'));
- } catch (Exception $e) {
- $class = get_class($e);
- $eMessage = $e->getMessage();
- if (get_class($e) === $expected) {
- $result = $class;
- return $this->assert(true, $message, compact('expected', 'result'));
- }
- if ($eMessage === $expected) {
- $result = $eMessage;
- return $this->assert(true, $message, compact('expected', 'result'));
- }
- if (Validator::isRegex($expected) && preg_match($expected, $eMessage)) {
- $result = $eMessage;
- return $this->assert(true, $message, compact('expected', 'result'));
- }
- $message = sprintf(
- 'Exception "%s" was expected. Exception "%s" with message "%s" was thrown instead.',
- $expected, get_class($e), $eMessage);
- return $this->assert(false, $message);
- }
- }
- /**
- * Assert Cookie data is properly set in headers.
- *
- * The value passed to `exepected` is an array of the cookie data, with at least the key and
- * value expected, but can support any of the following keys:
- * - `key`: the expected key
- * - `value`: the expected value
- * - `path`: optionally specifiy a path
- * - `name`: optionally specify the cookie name
- * - `expires`: optionally assert a specific expire time
- *
- * @param array $expected
- * @param array $headers When empty, value of `headers_list()` is used.
- * @return boolean
- */
- public function assertCookie($expected, $headers = null) {
- $matched = $this->_cookieMatch($expected, $headers);
- if (!$matched['match']) {
- $message = sprintf('%s - Cookie not found in headers.', $matched['pattern']);
- return $this->assert(false, $message, compact('expected', 'result'));
- }
- return $this->assert(true, '%s');
- }
- /**
- * Assert Cookie data is *not* set in headers.
- *
- * The value passed to `expected` is an array of the cookie data, with at least the key and
- * value expected, but can support any of the following keys:
- * - `key`: the expected key
- * - `value`: the expected value
- * - `path`: optionally specify a path
- * - `name`: optionally specify the cookie name
- * - `expires`: optionally assert a specific expire time
- *
- * @param array $expected
- * @param array $headers When empty, value of `headers_list()` is used.
- * @return boolean
- */
- public function assertNoCookie($expected, $headers = null) {
- $matched = $this->_cookieMatch($expected, $headers);
- if ($matched['match']) {
- $message = sprintf('%s - Cookie found in headers.', $matched['pattern']);
- return $this->assert(false, $message, compact('expected', 'result'));
- }
- return $this->assert(true, '%s');
- }
- /**
- * Match an `$expected` cookie with the given headers. If no headers are provided, then
- * the value of `headers_list()` will be used.
- *
- * @param array $expected
- * @param array $headers When empty, value of `headers_list()` will be used.
- * @return boolean True if cookie is found, false otherwise.
- */
- protected function _cookieMatch($expected, $headers) {
- $defaults = array('path' => '/', 'name' => '[\w.-]+');
- $expected += $defaults;
- $headers = ($headers) ?: headers_list();
- $value = preg_quote(urlencode($expected['value']), '/');
- $key = explode('.', $expected['key']);
- $key = (count($key) === 1) ? '[' . current($key) . ']' : ('[' . join('][', $key) . ']');
- $key = preg_quote($key, '/');
- if (isset($expected['expires'])) {
- $date = gmdate('D, d-M-Y H:i:s \G\M\T', strtotime($expected['expires']));
- $expires = preg_quote($date, '/');
- } else {
- $expires = '(?:.+?)';
- }
- $path = preg_quote($expected['path'], '/');
- $pattern = "/^Set\-Cookie:\s{$expected['name']}$key=$value;";
- $pattern .= "\sexpires=$expires;\spath=$path/";
- $match = false;
- foreach ($headers as $header) {
- if (preg_match($pattern, $header)) {
- $match = true;
- continue;
- }
- }
- return compact('match', 'pattern');
- }
- /**
- * Used before a call to `assert*()` if you expect the test assertion to generate an exception
- * or PHP error. If no error or exception is thrown, a test failure will be reported. Can
- * be called multiple times per assertion, if more than one error is expected.
- *
- * @param mixed $message A string indicating what the error text is expected to be. This can
- * be an exact string, a /-delimited regular expression, or true, indicating that
- * any error text is acceptable.
- * @return void
- */
- public function expectException($message = true) {
- $this->_expected[] = $message;
- }
- /**
- * Reports test result messages.
- *
- * @param string $type The type of result being reported. Can be `'pass'`, `'fail'`, `'skip'`
- * or `'exception'`.
- * @param array $info An array of information about the test result. At a minimum, this should
- * contain a `'message'` key. Other possible keys are `'file'`, `'line'`,
- * `'class'`, `'method'`, `'assertion'` and `'data'`.
- * @param array $options Currently unimplemented.
- * @return void
- */
- protected function _result($type, $info, array $options = array()) {
- $info = (array('result' => $type) + $info);
- $defaults = array();
- $options += $defaults;
- if ($this->_reporter) {
- $filtered = $this->_reporter->__invoke($info);
- $info = is_array($filtered) ? $filtered : $info;
- }
- $this->_results[] = $info;
- }
- /**
- * Runs an individual test method, collecting results and catching exceptions along the way.
- *
- * @param string $method The name of the test method to run.
- * @param array $options
- * @return mixed
- * @filter
- */
- protected function _runTestMethod($method, $options) {
- try {
- $this->setUp();
- } catch (Exception $e) {
- $this->_handleException($e, __LINE__ - 2);
- return $this->_results;
- }
- $params = compact('options', 'method');
- $passed = $this->_filter(__CLASS__ . '::run', $params, function($self, $params, $chain) {
- try {
- $method = $params['method'];
- $lineFlag = __LINE__ + 1;
- $self->{$method}();
- } catch (Exception $e) {
- $self->invokeMethod('_handleException', array($e));
- }
- });
- foreach ($this->_expected as $expected) {
- $this->_result('fail', compact('method') + array(
- 'class' => get_class($this),
- 'message' => "Expected exception matching `{$expected}` uncaught.",
- 'data' => array(),
- 'file' => null,
- 'line' => null,
- 'assertion' => 'expectException'
- ));
- }
- $this->_expected = array();
- try {
- $this->tearDown();
- } catch (Exception $e) {
- $this->_handleException($e, __LINE__ - 2);
- }
- return $passed;
- }
- /**
- * Normalizes `Exception` objects and PHP error data into a single array format, and checks
- * each error against the list of expected errors (set using `expectException()`). If a match
- * is found, the expectation is removed from the stack and the error is ignored. If no match
- * is found, then the error data is logged to the test results.
- *
- * @see lithium\test\Unit::expectException()
- * @see lithium\test\Unit::_reportException()
- * @param mixed $exception An `Exception` object instance, or an array containing the following
- * keys: `'message'`, `'file'`, `'line'`, `'trace'` (in `debug_backtrace()`
- * format) and optionally `'code'` (error code number) and `'context'` (an array
- * of variables relevant to the scope of where the error occurred).
- * @param integer $lineFlag A flag used for determining the relevant scope of the call stack.
- * Set to the line number where test methods are called.
- * @return void
- */
- protected function _handleException($exception, $lineFlag = null) {
- $data = $exception;
- if (is_object($exception)) {
- $data = array();
- foreach (array('message', 'file', 'line', 'trace') as $key) {
- $method = 'get' . ucfirst($key);
- $data[$key] = $exception->{$method}();
- }
- $ref = $exception->getTrace();
- $ref = $ref[0] + array('class' => null);
- if ($ref['class'] == __CLASS__ && $ref['function'] == 'skipIf') {
- return $this->_result('skip', $data);
- }
- }
- return $this->_reportException($data, $lineFlag);
- }
- /**
- * Convert an exception object to an exception result array for test reporting.
- *
- * @param array $exception The exception data to report on. Statistics are gathered and
- * added to the reporting stack contained in `Unit::$_results`.
- * @param string $lineFlag
- * @return void
- * @todo Refactor so that reporters handle trace formatting.
- */
- protected function _reportException($exception, $lineFlag = null) {
- $message = $exception['message'];
- $isExpected = (($exp = end($this->_expected)) && ($exp === true || $exp === $message || (
- Validator::isRegex($exp) && preg_match($exp, $message)
- )));
- if ($isExpected) {
- return array_pop($this->_expected);
- }
- $initFrame = current($exception['trace']) + array('class' => '-', 'function' => '-');
- foreach ($exception['trace'] as $frame) {
- if (isset($scopedFrame)) {
- break;
- }
- if (!class_exists('lithium\analysis\Inspector')) {
- continue;
- }
- if (isset($frame['class']) && in_array($frame['class'], Inspector::parents($this))) {
- $scopedFrame = $frame;
- }
- }
- if (class_exists('lithium\analysis\Debugger')) {
- $exception['trace'] = Debugger::trace(array(
- 'trace' => $exception['trace'],
- 'format' => '{:functionRef}, line {:line}',
- 'includeScope' => false,
- 'scope' => array_filter(array(
- 'functionRef' => __NAMESPACE__ . '\{closure}',
- 'line' => $lineFlag
- ))
- ));
- }
- $this->_result('exception', $exception + array(
- 'class' => $initFrame['class'],
- 'method' => $initFrame['function']
- ));
- }
- /**
- * Compare the expected with the result. If `$result` is null `$expected` equals `$type`
- * and `$result` equals `$expected`.
- *
- * @param string $type The type of comparison either `'identical'` or `'equal'` (default).
- * @param mixed $expected The expected value.
- * @param mixed $result An optional result value, defaults to `null`
- * @param string $trace An optional trace used internally to track arrays and objects,
- * defaults to `null`.
- * @return array Data with the keys `trace'`, `'expected'` and `'result'`.
- */
- protected function _compare($type, $expected, $result = null, $trace = null) {
- $compareTypes = function($expected, $result, $trace) {
- $types = array('expected' => gettype($expected), 'result' => gettype($result));
- if ($types['expected'] !== $types['result']) {
- $expected = trim("({$types['expected']}) " . print_r($expected, true));
- $result = trim("({$types['result']}) " . print_r($result, true));
- return compact('trace', 'expected', 'result');
- }
- };
- if ($types = $compareTypes($expected, $result, $trace)) {
- return $types;
- }
- $data = array();
- if (!is_scalar($expected)) {
- foreach ($expected as $key => $value) {
- $newTrace = "{$trace}[{$key}]";
- $isObject = false;
- if (is_object($expected)) {
- $isObject = true;
- $expected = (array) $expected;
- $result = (array) $result;
- }
- if (!array_key_exists($key, $result)) {
- $trace = (!$key) ? null : $newTrace;
- $expected = (!$key) ? $expected : $value;
- $result = ($key) ? null : $result;
- return compact('trace', 'expected', 'result');
- }
- $check = $result[$key];
- if ($isObject) {
- $newTrace = ($trace) ? "{$trace}->{$key}" : $key;
- $expected = (object) $expected;
- $result = (object) $result;
- }
- if ($type === 'identical') {
- if ($value === $check) {
- if ($types = $compareTypes($value, $check, $trace)) {
- return $types;
- }
- continue;
- }
- if ($check === array()) {
- $trace = $newTrace;
- return compact('trace', 'expected', 'result');
- }
- if (is_string($check)) {
- $trace = $newTrace;
- $expected = $value;
- $result = $check;
- return compact('trace', 'expected', 'result');
- }
- } else {
- if ($value == $check) {
- if ($types = $compareTypes($value, $check, $trace)) {
- return $types;
- }
- continue;
- }
- if (!is_array($value)) {
- $trace = $newTrace;
- return compact('trace', 'expected', 'result');
- }
- }
- $compare = $this->_compare($type, $value, $check, $newTrace);
- if ($compare !== true) {
- $data[] = $compare;
- }
- }
- if (!empty($data)) {
- return $data;
- }
- } elseif (!is_scalar($result)) {
- $data = $this->_compare($type, $result, $expected);
- if (!empty($data)) {
- return array(
- 'trace' => $data['trace'],
- 'expected' => $data['result'],
- 'result' => $data['expected']
- );
- }
- }
- if ((($type === 'identical') ? $expected === $result : $expected == $result)) {
- if ($types = $compareTypes($expected, $result, $trace)) {
- return $types;
- }
- return true;
- }
- return compact('trace', 'expected', 'result');
- }
- /**
- * Returns a basic message for the data returned from `_result()`.
- *
- * @see lithium\test\Unit::assert()
- * @see lithium\test\Unit::_result()
- * @param array $data The data to use for creating the message.
- * @param string $message The string prepended to the generate message in the current scope.
- * @return string
- */
- protected function _message(&$data = array(), $message = null) {
- if (!empty($data[0])) {
- foreach ($data as $key => $value) {
- $message = (!empty($data[$key][0])) ? $message : null;
- $message .= $this->_message($value, $message);
- unset($data[$key]);
- }
- return $message;
- }
- $defaults = array('trace' => null, 'expected' => null, 'result' => null);
- $result = (array) $data + $defaults;
- $message = null;
- if (!empty($result['trace'])) {
- $message = sprintf("trace: %s\n", $result['trace']);
- }
- if (is_object($result['expected'])) {
- $result['expected'] = get_object_vars($result['expected']);
- }
- if (is_object($result['result'])) {
- $result['result'] = get_object_vars($result['result']);
- }
- return $message . sprintf("expected: %s\nresult: %s\n",
- var_export($result['expected'], true),
- var_export($result['result'], true)
- );
- }
- /**
- * Generates all permutation of an array $items and returns them in a new array.
- *
- * @param array $items An array of items
- * @param array $perms
- * @return array
- */
- protected function _arrayPermute($items, $perms = array()) {
- static $permuted;
- if (empty($perms)) {
- $permuted = array();
- }
- if (empty($items)) {
- $permuted[] = $perms;
- return;
- }
- $numItems = count($items) - 1;
- for ($i = $numItems; $i >= 0; --$i) {
- $newItems = $items;
- $newPerms = $perms;
- list($tmp) = array_splice($newItems, $i, 1);
- array_unshift($newPerms, $tmp);
- $this->_arrayPermute($newItems, $newPerms);
- }
- return $permuted;
- }
- /**
- * Removes everything from `resources/tmp/tests` directory. Call from inside of your test
- * method or `tearDown()`.
- *
- * Uses `DIRECTORY_SEPARATOR` as `getPathname()` is used in a a direct string comparison.
- * The method may contain slashes and backslashes.
- *
- * If the file to unlink is readonly, it throws a exception (Permission denied) on Windows.
- * So, the file is checked before an unlink is tried. (this will make the tests run slower
- * but is prefered over a if (!unlink { chmod; unlink }.
- * http://stringoftheseus.com/blog/2010/12/22/php-unlink-permisssion-denied-error-on-windows/
- *
- * @param string $path Path to directory with contents to remove. If first
- * character is NOT a slash (`/`) or a Windows drive letter (`C:`)
- * prepends `LITHIUM_APP_PATH/resources/tmp/`.
- * @return void
- */
- protected function _cleanUp($path = null) {
- $resources = Libraries::get(true, 'resources');
- $path = $path ?: $resources . '/tmp/tests';
- $path = preg_match('/^\w:|^\//', $path) ? $path : $resources . '/tmp/' . $path;
- if (!is_dir($path)) {
- return;
- }
- $dirs = new RecursiveDirectoryIterator($path);
- $iterator = new RecursiveIteratorIterator($dirs, RecursiveIteratorIterator::CHILD_FIRST);
- foreach ($iterator as $item) {
- $empty = $item->getPathname() === $path . DIRECTORY_SEPARATOR . 'empty';
- if ($empty || $iterator->isDot()) {
- continue;
- }
- if ($item->isDir()) {
- rmdir($item->getPathname());
- continue;
- }
- if (!$item->isWritable()) {
- chmod($item->getPathname(), 0777);
- }
- unlink($item->getPathname());
- }
- }
- /**
- * Returns the current results
- *
- * @return array The Results, currently
- */
- public function results() {
- return $this->_results;
- }
- /**
- * Checks for a working internet connection.
- *
- * This method is used to check for a working connection to google.com, both
- * testing for proper DNS resolution and reading the actual URL.
- *
- * @param array $config Override the default URL to check.
- * @return boolean True if a network connection is established, false otherwise.
- */
- protected function _hasNetwork($config = array()) {
- $defaults = array(
- 'scheme' => 'http',
- 'host' => 'google.com'
- );
- $config += $defaults;
- $url = "{$config['scheme']}://{$config['host']}";
- $failed = false;
- set_error_handler(function($errno, $errstr) use (&$failed) {
- $failed = true;
- });
- dns_check_record($config['host'], 'A');
- if ($handle = fopen($url, 'r')) {
- fclose($handle);
- }
- restore_error_handler();
- return !$failed;
- }
- /**
- * Will mark the test `true` if `$count` and `count($arr)` are equal.
- *
- * {{{
- * $this->assertCount(1, array('foo'));
- * }}}
- *
- * {{{
- * $this->assertCount(2, array('foo', 'bar', 'bar'));
- * }}}
- *
- * @param int $expected Expected count
- * @param array $array Result
- * @param string $message optional
- * @return bool
- */
- public function assertCount($expected, $array, $message = '{:message}') {
- return $this->assert($expected === ($result = count($array)), $message, array(
- 'expected' => $expected,
- 'result' => $result,
- ));
- }
- /**
- * Will mark the test `true` if `$count` and `count($arr)` are not equal.
- *
- * {{{
- * $this->assertNotCount(2, array('foo', 'bar', 'bar'));
- * }}}
- *
- * {{{
- * $this->assertNotCount(1, array('foo'));
- * }}}
- *
- * @param int $expected Expected count
- * @param array $array Result
- * @param string $message optional
- * @return bool
- */
- public function assertNotCount($expected, $array, $message = '{:message}') {
- return $this->assert($expected !== ($result = count($array)), $message, array(
- 'expected' => $expected,
- 'result' => $result,
- ));
- }
- /**
- * Will mark the test `true` if `$array` has key `$expected`.
- *
- * {{{
- * $this->assertArrayHasKey('foo', array('bar' => 'baz'));
- * }}}
- *
- * {{{
- * $this->assertArrayHasKey('bar', array('bar' => 'baz'));
- * }}}
- *
- * @param string $key Key you are looking for
- * @param array $array Array to search through
- * @param string $message optional
- * @return bool
- */
- public function assertArrayHasKey($key, $array, $message = '{:message}') {
- return $this->assert(isset($array[$key]), $message, array(
- 'expected' => $key,
- 'result' => $array
- ));
- }
- /**
- * Will mark the test `true` if `$array` does not have key `$expected`.
- *
- * {{{
- * $this->assertArrayNotHasKey('foo', array('bar' => 'baz'));
- * }}}
- *
- * {{{
- * $this->assertArrayNotHasKey('bar', array('bar' => 'baz'));
- * }}}
- *
- * @param int $key Expected count
- * @param array $array Array to search through
- * @param string $message optional
- * @return bool
- */
- public function assertArrayNotHasKey($key, $array, $message = '{:message}') {
- return $this->assert(!isset($array[$key]), $message, array(
- 'expected' => $key,
- 'result' => $array
- ));
- }
- /**
- * Will mark the test `true` if `$class` has an attribute `$attributeName`.
- *
- * {{{
- * $this->assertClassHasAttribute('name', '\ReflectionClass');
- * }}}
- *
- * {{{
- * $this->assertClassHasAttribute('__construct', '\ReflectionClass');
- * }}}
- *
- * @see lithium\test\Unit::assertObjectHasAttribute()
- * @throws InvalidArgumentException When $object is not an object
- * @throws ReflectionException If the given class does not exist
- * @param string $attributeName Attribute you wish to look for
- * @param string $class Class name
- * @param string $message optional
- * @return bool
- */
- public function assertClassHasAttribute($attributeName, $class, $message = '{:message}') {
- if (!is_string($class)) {
- throw new InvalidArgumentException('Argument $class must be a string');
- }
- $object = new ReflectionClass($class);
- return $this->assert($object->hasProperty($attributeName), $message, array(
- 'expected' => $attributeName,
- 'result' => $object->getProperties()
- ));
- }
- /**
- * Will mark the test `true` if `$class` has an attribute `$attributeName`.
- *
- * {{{
- * $this->assertClassNotHasAttribute('__construct', '\ReflectionClass');
- * }}}
- *
- * {{{
- * $this->assertClassNotHasAttribute('name', '\ReflectionClass');
- * }}}
- *
- * @see lithium\test\Unit::assertObjectNotHasAttribute()
- * @throws InvalidArgumentException When $object is not an object
- * @throws ReflectionException If the given class does not exist
- * @param string $attributeName Attribute you wish to look for
- * @param string $class Class name
- * @param string $message optional
- * @return bool
- */
- public function assertClassNotHasAttribute($attributeName, $class, $message = '{:message}') {
- if (!is_string($class)) {
- throw new InvalidArgumentException('Argument $class must be a string.');
- }
- $object = new ReflectionClass($class);
- return $this->assert(!$object->hasProperty($attributeName), $message, array(
- 'expected' => $attributeName,
- 'result' => $object->getProperties()
- ));
- }
- /**
- * Will mark the test `true` if `$class` has a static property `$attributeName`.
- *
- * {{{
- * $this->assertClassHasStaticAttribute('foobar', '\lithium\core\StaticObject');
- * }}}
- *
- * {{{
- * $this->assertClassHasStaticAttribute('_methodFilters', '\lithium\core\StaticObject');
- * }}}
- *
- * @throws ReflectionException If the given class does not exist
- * @param string $attributeName Attribute you wish to look for
- * @param string|object $class Class name or object
- * @param string $message optional
- * @return bool
- */
- public function assertClassHasStaticAttribute($attributeName, $class, $message = '{:message}') {
- $object = new ReflectionClass($class);
- if ($object->hasProperty($attributeName)) {
- $attribute = $object->getProperty($attributeName);
- return $this->assert($attribute->isStatic(), $message, array(
- 'expected' => $attributeName,
- 'result' => $object->getProperties()
- ));
- }
- return $this->assert(false, $message, array(
- 'expected' => $attributeName,
- 'result' => $object->getProperties()
- ));
- }
- /**
- * Will mark the test `true` if `$class` does not have a static property `$attrName`.
- *
- * {{{
- * $this->assertClassNotHasStaticAttribute('_methodFilters', '\lithium\core\StaticObject');
- * }}}
- *
- * {{{
- * $this->assertClassNotHasStaticAttribute('foobar', '\lithium\core\StaticObject')
- * }}}
- *
- * @throws ReflectionException If the given class does not exist
- * @param string $attrName Attribute you wish to look for
- * @param string|object $class Class name or object
- * @param string $message optional
- * @return bool
- */
- public function assertClassNotHasStaticAttribute($attrName, $class, $message = '{:message}') {
- $object = new ReflectionClass($class);
- if ($object->hasProperty($attrName)) {
- $attribute = $object->getProperty($attrName);
- return $this->assert(!$attribute->isStatic(), $message, array(
- 'expected' => $attrName,
- 'result' => $object->getProperties()
- ));
- }
- return $this->assert(true, $message, array(
- 'expected' => $attrName,
- 'result' => $object->getProperties()
- ));
- }
- /**
- * Will mark the test `true` if `$haystack` contains `$needle` as a value.
- *
- * {{{
- * $this->assertContains('foo', array('foo', 'bar', 'baz'));
- * }}}
- *
- * {{{
- * $this->assertContains(4, array(1,2,3));
- * }}}
- *
- * @param string $needle The needle you are looking for
- * @param mixed $haystack An array, iterable object, or string
- * @param string $message optional
- * @return bool
- */
- public function assertContains($needle, $haystack, $message = '{:message}') {
- if (is_string($haystack)) {
- return $this->assert(strpos($haystack, $needle) !== false, $message, array(
- 'expected' => $needle,
- 'result' => $haystack
- ));
- }
- foreach ($haystack as $key => $value) {
- if ($value === $needle) {
- return $this->assert(true, $message, array(
- 'expected' => $needle,
- 'result' => $haystack
- ));
- }
- }
- return $this->assert(false, $message, array(
- 'expected' => $needle,
- 'result' => $haystack
- ));
- }
- /**
- * Will mark the test `true` if `$haystack` does not contain `$needle` as a value.
- *
- * {{{
- * $this->assertNotContains(4, array(1,2,3));
- * }}}
- *
- * {{{
- * $this->assertNotContains('foo', array('foo', 'bar', 'baz'));
- * }}}
- *
- * @param string $needle Needle you are looking for
- * @param mixed $haystack Array, iterable object, or string
- * @param string $message optional
- * @return bool
- */
- public function assertNotContains($needle, $haystack, $message = '{:message}') {
- if (is_string($haystack)) {
- return $this->assert(strpos($haystack, $needle) === false, $message, array(
- 'expected' => $needle,
- 'result' => $haystack
- ));
- }
- foreach ($haystack as $key => $value) {
- if ($value === $needle) {
- return $this->assert(false, $message, array(
- 'expected' => $needle,
- 'result' => $haystack
- ));
- }
- }
- return $this->assert(true, $message, array(
- 'expected' => $needle,
- 'result' => $haystack
- ));
- }
- /**
- * Will mark the test `true` if `$haystack` contains only items of `$type`.
- *
- * {{{
- * $this->assertContainsOnly('int', array(1,2,3));
- * }}}
- *
- * {{{
- * $this->assertContainsOnly('int', array('foo', 'bar', 'baz'));
- * }}}
- *
- * @param string $type Data type to check for
- * @param mixed $haystack Array or iterable object
- * @param string $message optional
- * @return bool
- */
- public function assertContainsOnly($type, $haystack, $message = '{:message}') {
- $method = self::$_internalTypes[$type];
- foreach ($haystack as $key => $value) {
- if (!$method($value)) {
- return $this->assert(false, $message, array(
- 'expected' => $type,
- 'result' => $haystack
- ));
- }
- }
- return $this->assert(true, $message, array(
- 'expected' => $type,
- 'result' => $haystack
- ));
- }
- /**
- * Will mark the test `true` if `$haystack` does not have any of `$type`.
- *
- * {{{
- * $this->assertNotContainsOnly('int', array('foo', 'bar', 'baz'));
- * }}}
- *
- * {{{
- * $this->assertNotContainsOnly('int', array(1,2,3));
- * }}}
- *
- * @param string $type Data type to check for
- * @param mixed $haystack Array or iterable object
- * @param string $message optional
- * @return bool
- */
- public function assertNotContainsOnly($type, $haystack, $message = '{:message}') {
- $method = self::$_internalTypes[$type];
- foreach ($haystack as $key => $value) {
- if (!$method($value)) {
- return $this->assert(true, $message, array(
- 'expected' => $type,
- 'result' => $haystack
- ));
- }
- }
- return $this->assert(false, $message, array(
- 'expected' => $type,
- 'result' => $haystack
- ));
- }
- /**
- * Will mark the test `true` if `$haystack` contains only items of `$type`.
- *
- * {{{
- * $this->assertContainsOnlyInstancesOf('stdClass', array(new \stdClass));
- * }}}
- *
- * {{{
- * $this->assertContainsOnlyInstancesOf('stdClass', array(new \lithium\test\Unit));
- * }}}
- *
- * @param string $class Fully namespaced class name
- * @param mixed $haystack Array or iterable object
- * @param string $message optional
- * @return bool
- */
- public function assertContainsOnlyInstancesOf($class, $haystack, $message = '{:message}') {
- $result = array();
- foreach ($haystack as $key => &$value) {
- if (!is_a($value, $class)) {
- $result[$key] =& $value;
- break;
- }
- }
- return $this->assert(empty($result), $message, array(
- 'expected' => $class,
- 'result' => $result
- ));
- }
- /**
- * Will mark the test `true` if `$actual` is empty.
- *
- * {{{
- * $this->assertEmpty(1);
- * }}}
- *
- * {{{
- * $this->assertEmpty(array());
- * }}}
- *
- * @param string $actual Variable to check
- * @param string $message optional
- * @return bool
- */
- public function assertEmpty($actual, $message = '{:message}') {
- return $this->assert(empty($actual), $message, array(
- 'expected' => $actual,
- 'result' => empty($actual)
- ));
- }
- /**
- * Will mark the test `true` if `$actual` is not empty.
- *
- * {{{
- * $this->assertNotEmpty(array());
- * }}}
- *
- * {{{
- * $this->assertNotEmpty(1);
- * }}}
- *
- * @param string $actual Variable to check
- * @param string $message optional
- * @return bool
- */
- public function assertNotEmpty($actual, $message = '{:message}') {
- return $this->assert(!empty($actual), $message, array(
- 'expected' => $actual,
- 'result' => !empty($actual)
- ));
- }
- /**
- * Will mark the test `true` if the contents of `$expected` are equal to the
- * contents of `$actual`.
- *
- * {{{
- * $file1 = LITHIUM_APP_PATH . '/tests/mocks/md/file_1.md';
- * $file2 = LITHIUM_APP_PATH . '/tests/mocks/md/file_1.md.copy';
- * $this->assertFileEquals($file1, $file2);
- * }}}
- *
- * {{{
- * $file1 = LITHIUM_APP_PATH . '/tests/mocks/md/file_1.md';
- * $file2 = LITHIUM_APP_PATH . '/tests/mocks/md/file_2.md';
- * $this->assertFileEquals($file1, $file2);
- * }}}
- *
- * @param string $expected Path to the expected file
- * @param string $actual Path to the actual file
- * @param string $message optional
- * @return bool
- */
- public function assertFileEquals($expected, $actual, $message = '{:message}') {
- $expected = md5_file($expected);
- $result = md5_file($actual);
- return $this->assert($expected === $result, $message, compact('expected', 'result'));
- }
- /**
- * Will mark the test `true` if the contents of `$expected` are not equal to
- * the contents of `$actual`.
- *
- * {{{
- * $file1 = LITHIUM_APP_PATH . '/tests/mocks/md/file_1.md';
- * $file2 = LITHIUM_APP_PATH . '/tests/mocks/md/file_2.md';
- * $this->assertFileNotEquals($file1, $file2);
- * }}}
- *
- * {{{
- * $file1 = LITHIUM_APP_PATH . '/tests/mocks/md/file_1.md';
- * $file2 = LITHIUM_APP_PATH . '/tests/mocks/md/file_1.md.copy';
- * $this->assertFileNotEquals($file1, $file2);
- * }}}
- *
- * @param string $expected Path to the expected file
- * @param string $actual Path to the actual file
- * @param string $message optional
- * @return bool
- */
- public function assertFileNotEquals($expected, $actual, $message = '{:message}') {
- $expected = md5_file($expected);
- $result = md5_file($actual);
- return $this->assert($expected !== $result, $message, compact('expected', 'result'));
- }
- /**
- * Will mark the test `true` if the file `$actual` exists.
- *
- * {{{
- * $this->assertFileExists(LITHIUM_APP_PATH . '/readme.md');
- * }}}
- *
- * {{{
- * $this->assertFileExists(LITHIUM_APP_PATH . '/does/not/exist.txt');
- * }}}
- *
- * @param string $actual Path to the file you are asserting
- * @param string $message optional
- * @return bool
- */
- public function assertFileExists($actual, $message = '{:message}') {
- return $this->assert(file_exists($actual), $message, array(
- 'expected' => $actual,
- 'result' => file_exists($actual)
- ));
- }
- /**
- * Will mark the test `true` if the file `$actual` does not exist.
- *
- * {{{
- * $this->assertFileExists(LITHIUM_APP_PATH . '/does/not/exist.txt');
- * }}}
- *
- * {{{
- * $this->assertFileExists(LITHIUM_APP_PATH . '/readme.md');
- * }}}
- *
- * @param string $actual Path to the file you are asserting
- * @param string $message optional
- * @return bool
- */
- public function assertFileNotExists($actual, $message = '{:message}') {
- return $this->assert(!file_exists($actual), $message, array(
- 'expected' => $actual,
- 'result' => !file_exists($actual)
- ));
- }
- /**
- * Will mark the test `true` if `$expected` greater than `$actual`.
- *
- * {{{
- * $this->assertGreaterThan(5, 3);
- * }}}
- *
- * {{{
- * $this->assertGreaterThan(3, 5);
- * }}}
- *
- * @param float|int $expected
- * @param float|int $actual
- * @param string $message optional
- * @return bool
- */
- public function assertGreaterThan($expected, $actual, $message = '{:message}') {
- return $this->assert($expected > $actual, $message, array(
- 'expected' => $expected,
- 'result' => $actual
- ));
- }
- /**
- * Will mark the test `true` if `$expected` great than or equal to `$actual`.
- *
- * {{{
- * $this->assertGreaterThanOrEqual(5, 5);
- * }}}
- *
- * {{{
- * $this->assertGreaterThanOrEqual(3, 5);
- * }}}
- *
- * @param float|int $expected
- * @param float|int $actual
- * @param string $message optional
- * @return bool
- */
- public function assertGreaterThanOrEqual($expected, $actual, $message = '{:message}') {
- return $this->assert($expected >= $actual, $message, array(
- 'expected' => $expected,
- 'result' => $actual
- ));
- }
- /**
- * Will mark the test `true` if `$expected` less than `$actual`.
- *
- * {{{
- * $this->assertLessThan(3, 5);
- * }}}
- *
- * {{{
- * $this->assertLessThan(5, 3);
- * }}}
- *
- * @param float|int $expected
- * @param float|int $actual
- * @param string $message optional
- * @return bool
- */
- public function assertLessThan($expected, $actual, $message = '{:message}') {
- return $this->assert($expected < $actual, $message, array(
- 'expected' => $expected,
- 'result' => $actual
- ));
- }
- /**
- * Will mark the test `true` if `$expected` is less than or equal to `$actual`.
- *
- * {{{
- * $this->assertLessThanOrEqual(5, 5);
- * }}}
- *
- * {{{
- * $this->assertLessThanOrEqual(5, 3);
- * }}}
- *
- * @param float|int $expected
- * @param float|int $actual
- * @param string $message optional
- * @return bool
- */
- public function assertLessThanOrEqual($expected, $actual, $message = '{:message}') {
- return $this->assert($expected <= $actual, $message, array(
- 'expected' => $expected,
- 'result' => $actual
- ));
- }
- /**
- * Will mark the test `true` if `$actual` is a `$expected`.
- *
- * {{{
- * $this->assertInstanceOf('stdClass', new stdClass);
- * }}}
- *
- * {{{
- * $this->assertInstanceOf('ReflectionClass', new stdClass);
- * }}}
- *
- * @param string $expected Fully namespaced expected class
- * @param object $actual Object you are testing
- * @param string $message optional
- * @return bool
- */
- public function assertInstanceOf($expected, $actual, $message = '{:message}') {
- return $this->assert(is_a($actual, $expected), $message, array(
- 'expected' => $expected,
- 'result' => get_class($actual)
- ));
- }
- /**
- * Will mark the test `true` if `$actual` is not a `$expected`.
- *
- * {{{
- * $this->assertNotInstanceOf('ReflectionClass', new stdClass);
- * }}}
- *
- * {{{
- * $this->assertNotInstanceOf('stdClass', new stdClass);
- * }}}
- *
- * @param string $expected Fully namespaced expected class
- * @param object $actual Object you are testing
- * @param string $message optional
- * @return bool
- */
- public function assertNotInstanceOf($expected, $actual, $message = '{:message}') {
- return $this->assert(!is_a($actual, $expected), $message, array(
- 'expected' => $expected,
- 'result' => get_class($actual)
- ));
- }
- /**
- * Will mark the test `true` if `$actual` if of type $expected.
- *
- * {{{
- * $this->assertInternalType('string', 'foobar');
- * }}}
- *
- * {{{
- * $this->assertInternalType('int', 'foobar');
- * }}}
- *
- * @param string $expected Internal data type
- * @param object $actual Object you are testing
- * @param string $message optional
- * @return bool
- */
- public function assertInternalType($expected, $actual, $message = '{:message}') {
- $method = self::$_internalTypes[$expected];
- return $this->assert($method($actual), $message, array(
- 'expected' => $expected,
- 'result' => gettype($actual)
- ));
- }
- /**
- * Will mark the test `true` if `$actual` if not of type $expected.
- *
- * {{{
- * $this->assertNotInternalType('int', 'foobar');
- * }}}
- *
- * {{{
- * $this->assertNotInternalType('string', 'foobar');
- * }}}
- *
- * @param string $expected Internal data type
- * @param object $actual Object you are testing
- * @param string $message optional
- * @return bool
- */
- public function assertNotInternalType($expected, $actual, $message = '{:message}') {
- $method = self::$_internalTypes[$expected];
- return $this->assert(!$method($actual), $message, array(
- 'expected' => $expected,
- 'result' => gettype($actual)
- ));
- }
- /**
- * Will mark the test as true if `$actual` is not null.
- *
- * {{{
- * $this->assertNotNull(1);
- * }}}
- *
- * {{{
- * $this->assertNotNull(null);
- * }}}
- *
- * @param object $actual Variable you are testing
- * @param string $message optional
- * @return bool
- */
- public function assertNotNull($actual, $message = '{:message}') {
- return $this->assert(!is_null($actual), $message, array(
- 'expected' => null,
- 'actual' => gettype($actual)
- ));
- }
- /**
- * Will mark the test `true` if `$object` has an attribute `$attributeName`.
- *
- * {{{
- * $this->assertObjectHasAttribute('name', '\ReflectionClass');
- * }}}
- *
- * {{{
- * $this->assertObjectHasAttribute('__construct', '\ReflectionClass');
- * }}}
- *
- * @see lithium\test\Unit::assertClassHasAttribute()
- * @throws InvalidArgumentException When $object is not an object
- * @param string $attributeName Attribute you wish to look for
- * @param string $object Object to assert
- * @param string $message optional
- * @return bool
- */
- public function assertObjectHasAttribute($attributeName, $object, $message = '{:message}') {
- if (!is_object($object)) {
- throw new InvalidArgumentException('Second argument $object must be an object.');
- }
- $object = new ReflectionClass($object);
- return $this->assert($object->hasProperty($attributeName), $message, array(
- 'expected' => $attributeName,
- 'result' => $object->getProperties()
- ));
- }
- /**
- * Will mark the test `true` if `$object` has an attribute `$attributeName`.
- *
- * {{{
- * $this->assertObjectNotHasAttribute('__construct', '\ReflectionClass');
- * }}}
- *
- * {{{
- * $this->assertObjectNotHasAttribute('name', '\ReflectionClass');
- * }}}
- *
- * @see lithium\test\Unit::assertClassHasNotAttribute()
- * @throws InvalidArgumentException When $object is not an object
- * @param string $attributeName Attribute you wish to look for
- * @param string $object Object to assert
- * @param string $message optional
- * @return bool
- */
- public function assertObjectNotHasAttribute($attributeName, $object, $message = '{:message}') {
- if (!is_object($object)) {
- throw new InvalidArgumentException('Second argument $object must be an object');
- }
- $object = new ReflectionClass($object);
- return $this->assert(!$object->hasProperty($attributeName), $message, array(
- 'expected' => $attributeName,
- 'result' => $object->getProperties()
- ));
- }
- /**
- * Will mark the test `true` if `$actual` matches $expected using `preg_match`.
- *
- * {{{
- * $this->assertRegExp('/^foo/', 'foobar');
- * }}}
- *
- * {{{
- * $this->assertRegExp('/^foobar/', 'bar');
- * }}}
- *
- * @param string $expected Regex to match against $actual
- * @param string $actual String to be matched upon
- * @param string $message optional
- * @return bool
- */
- public function assertRegExp($expected, $actual, $message = '{:message}') {
- return $this->assert(preg_match($expected, $actual, $matches) === 1, $message, array(
- 'expected' => $expected,
- 'result' => $matches
- ));
- }
- /**
- * Will mark the test `true` if `$actual` does not match $expected using `preg_match`.
- *
- * {{{
- * $this->assertNotRegExp('/^foobar/', 'bar');
- * }}}
- *
- * {{{
- * $this->assertNotRegExp('/^foo/', 'foobar');
- * }}}
- *
- * @param string $expected Regex to match against $actual
- * @param string $actual String to be matched upon
- * @param string $message optional
- * @return bool
- */
- public function assertNotRegExp($expected, $actual, $message = '{:message}') {
- return $this->assert(preg_match($expected, $actual, $matches) === 0, $message, array(
- 'expected' => $expected,
- 'result' => $matches
- ));
- }
- /**
- * Will mark the test `true` if $actual matches $expected using `sprintf` format.
- *
- * {{{
- * $this->assertStringMatchesFormat('%d', '10')
- * }}}
- *
- * {{{
- * $this->assertStringMatchesFormat('%d', '10.555')
- * }}}
- *
- * @link http://php.net/sprintf
- * @link http://php.net/sscanf
- * @param string $expected Expected format using sscanf's format
- * @param string $actual Value to compare against
- * @param string $message optional
- * @return bool
- */
- public function assertStringMatchesFormat($expected, $actual, $message = '{:message}') {
- $result = sscanf($actual, $expected);
- return $this->assert($result[0] == $actual, $message, compact('expected', 'result'));
- }
- /**
- * Will mark the test `true` if $actual doesn't match $expected using `sprintf` format.
- *
- * {{{
- * $this->assertStringNotMatchesFormat('%d', '10.555')
- * }}}
- *
- * {{{
- * $this->assertStringNotMatchesFormat('%d', '10')
- * }}}
- *
- * @link http://php.net/sprintf
- * @link http://php.net/sscanf
- * @param string $expected Expected format using sscanf's format
- * @param string $actual Value to test against
- * @param string $message optional
- * @return bool
- */
- public function assertStringNotMatchesFormat($expected, $actual, $message = '{:message}') {
- $result = sscanf($actual, $expected);
- return $this->assert($result[0] != $actual, $message, compact('expected', 'result'));
- }
- /**
- * Will mark the test `true` if $actual ends with `$expected`.
- *
- * {{{
- * $this->assertStringEndsWith('bar', 'foobar');
- * }}}
- *
- * {{{
- * $this->assertStringEndsWith('foo', 'foobar');
- * }}}
- *
- * @param string $expected The suffix to check for
- * @param string $actual Value to test against
- * @param string $message optional
- * @return bool
- */
- public function assertStringEndsWith($expected, $actual, $message = '{:message}') {
- return $this->assert(preg_match("/$expected$/", $actual, $matches) === 1, $message, array(
- 'expected' => $expected,
- 'result' => $actual
- ));
- }
- /**
- * Will mark the test `true` if $actual starts with `$expected`.
- *
- * {{{
- * $this->assertStringStartsWith('foo', 'foobar');
- * }}}
- *
- * {{{
- * $this->assertStringStartsWith('bar', 'foobar');
- * }}}
- *
- * @param string $expected Prefix to check for
- * @param string $actual Value to test against
- * @param string $message optional
- * @return bool
- */
- public function assertStringStartsWith($expected, $actual, $message = '{:message}') {
- return $this->assert(preg_match("/^$expected/", $actual, $matches) === 1, $message, array(
- 'expected' => $expected,
- 'result' => $actual
- ));
- }
- }
- ?>
|