Overview

Namespaces

  • Budovy
  • Kdyby
    • BootstrapFormRenderer
      • DI
      • Latte
  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Diagnostics
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
      • Diagnostics
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • PhpGenerator
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
  • NetteModule
  • Nextras
    • Datagrid
  • None
  • PHP
  • Tester
    • CodeCoverage
    • Runner
      • Output
  • Vodacek
    • Forms
      • Controls
  • WebLoader
    • Filter
    • Nette

Classes

  • Assert
  • DataProvider
  • DomQuery
  • Dumper
  • Environment
  • Helpers
  • TestCase

Exceptions

  • AssertException
  • TestCaseException
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Tester.
  5:  * Copyright (c) 2009 David Grudl (http://davidgrudl.com)
  6:  */
  7: 
  8: namespace Tester;
  9: 
 10: 
 11: /**
 12:  * Assertion test helpers.
 13:  *
 14:  * @author     David Grudl
 15:  */
 16: class Assert
 17: {
 18:     /** used by equal() for comparing floats */
 19:     const EPSILON = 1e-10;
 20: 
 21:     /** used by match(); in values, each $ followed by number is backreference */
 22:     static public $patterns = array(
 23:         '%a%' => '[^\r\n]+',    // one or more of anything except the end of line characters
 24:         '%a\?%'=> '[^\r\n]*',   // zero or more of anything except the end of line characters
 25:         '%A%' => '.+',          // one or more of anything including the end of line characters
 26:         '%A\?%'=> '.*',         // zero or more of anything including the end of line characters
 27:         '%s%' => '[\t ]+',      // one or more white space characters except the end of line characters
 28:         '%s\?%'=> '[\t ]*',     // zero or more white space characters except the end of line characters
 29:         '%S%' => '\S+',         // one or more of characters except the white space
 30:         '%S\?%'=> '\S*',        // zero or more of characters except the white space
 31:         '%c%' => '[^\r\n]',     // a single character of any sort (except the end of line)
 32:         '%d%' => '[0-9]+',      // one or more digits
 33:         '%d\?%'=> '[0-9]*',     // zero or more digits
 34:         '%i%' => '[+-]?[0-9]+', // signed integer value
 35:         '%f%' => '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', // floating point number
 36:         '%h%' => '[0-9a-fA-F]+',// one or more HEX digits
 37:         '%ds%'=> '[\\\\/]', // directory separator
 38:         '%(\[.*\].*)%'=> '$1',  // range
 39:     );
 40: 
 41:     /** @var callable  function(AssertException $exception) */
 42:     public static $onFailure;
 43: 
 44: 
 45:     /**
 46:      * Checks assertion. Values must be exactly the same.
 47:      * @return void
 48:      */
 49:     public static function same($expected, $actual)
 50:     {
 51:         if ($actual !== $expected) {
 52:             self::fail('%1 should be %2', $actual, $expected);
 53:         }
 54:     }
 55: 
 56: 
 57:     /**
 58:      * Checks assertion. Values must not be exactly the same.
 59:      * @return void
 60:      */
 61:     public static function notSame($expected, $actual)
 62:     {
 63:         if ($actual === $expected) {
 64:             self::fail('%1 should not be %2', $actual, $expected);
 65:         }
 66:     }
 67: 
 68: 
 69:     /**
 70:      * Checks assertion. The identity of objects and the order of keys in the arrays are ignored.
 71:      * @return void
 72:      */
 73:     public static function equal($expected, $actual)
 74:     {
 75:         if (!self::isEqual($expected, $actual)) {
 76:             self::fail('%1 should be equal to %2', $actual, $expected);
 77:         }
 78:     }
 79: 
 80: 
 81:     /**
 82:      * Checks assertion. The identity of objects and the order of keys in the arrays are ignored.
 83:      * @return void
 84:      */
 85:     public static function notEqual($expected, $actual)
 86:     {
 87:         if (self::isEqual($expected, $actual)) {
 88:             self::fail('%1 should not be equal to %2', $actual, $expected);
 89:         }
 90:     }
 91: 
 92: 
 93:     /**
 94:      * Checks assertion. Values must contains expected needle.
 95:      * @return void
 96:      */
 97:     public static function contains($needle, $actual)
 98:     {
 99:         if (is_array($actual)) {
100:             if (!in_array($needle, $actual, TRUE)) {
101:                 self::fail('%1 should contain %2', $actual, $needle);
102:             }
103:         } elseif (is_string($actual)) {
104:             if (strpos($actual, $needle) === FALSE) {
105:                 self::fail('%1 should contain %2', $actual, $needle);
106:             }
107:         } else {
108:             self::fail('%1 should be string or array', $actual);
109:         }
110:     }
111: 
112: 
113:     /**
114:      * Checks assertion. Values must not contains expected needle.
115:      * @return void
116:      */
117:     public static function notContains($needle, $actual)
118:     {
119:         if (is_array($actual)) {
120:             if (in_array($needle, $actual, TRUE)) {
121:                 self::fail('%1 should not contain %2', $actual, $needle);
122:             }
123:         } elseif (is_string($actual)) {
124:             if (strpos($actual, $needle) !== FALSE) {
125:                 self::fail('%1 should not contain %2', $actual, $needle);
126:             }
127:         } else {
128:             self::fail('%1 should be string or array', $actual);
129:         }
130:     }
131: 
132: 
133:     /**
134:      * Checks TRUE assertion.
135:      * @param  mixed  actual
136:      * @return void
137:      */
138:     public static function true($actual)
139:     {
140:         if ($actual !== TRUE) {
141:             self::fail('%1 should be TRUE', $actual);
142:         }
143:     }
144: 
145: 
146:     /**
147:      * Checks FALSE assertion.
148:      * @param  mixed  actual
149:      * @return void
150:      */
151:     public static function false($actual)
152:     {
153:         if ($actual !== FALSE) {
154:             self::fail('%1 should be FALSE', $actual);
155:         }
156:     }
157: 
158: 
159:     /**
160:      * Checks NULL assertion.
161:      * @param  mixed  actual
162:      * @return void
163:      */
164:     public static function null($actual)
165:     {
166:         if ($actual !== NULL) {
167:             self::fail('%1 should be NULL', $actual);
168:         }
169:     }
170: 
171: 
172:     /**
173:      * Checks truthy assertion.
174:      * @param  mixed  actual
175:      * @return void
176:      */
177:     public static function truthy($actual)
178:     {
179:         if (!$actual) {
180:             self::fail('%1 should be truthy', $actual);
181:         }
182:     }
183: 
184: 
185:     /**
186:      * Checks falsey (empty) assertion.
187:      * @param  mixed  actual
188:      * @return void
189:      */
190:     public static function falsey($actual)
191:     {
192:         if ($actual) {
193:             self::fail('%1 should be falsey', $actual);
194:         }
195:     }
196: 
197: 
198:     /**
199:      * Checks assertion.
200:      * @return void
201:      */
202:     public static function type($type, $value)
203:     {
204:         if (!is_object($type) && !is_string($type)) {
205:             throw new \Exception('Type must be a object or a string.');
206: 
207:         } elseif ($type === 'list') {
208:             if (!is_array($value) || ($value && array_keys($value) !== range(0, count($value) - 1))) {
209:                 self::fail("%1 should be $type", $value);
210:             }
211: 
212:         } elseif (in_array($type, array('array', 'bool', 'callable', 'float',
213:             'int', 'integer', 'null', 'object', 'resource', 'scalar', 'string'), TRUE)
214:         ) {
215:             if (!call_user_func("is_$type", $value)) {
216:                 self::fail("%1 should be $type", $value);
217:             }
218: 
219:         } elseif (!$value instanceof $type) {
220:             self::fail("%1 should be instance of $type", $value);
221:         }
222:     }
223: 
224: 
225:     /**
226:      * Checks if the function throws exception.
227:      * @param  callable
228:      * @param  string class
229:      * @param  string message
230:      * @return Exception
231:      */
232:     public static function exception($function, $class, $message = NULL)
233:     {
234:         try {
235:             call_user_func($function);
236:         } catch (\Exception $e) {
237:         }
238:         if (!isset($e)) {
239:             self::fail("$class was expected, but none was thrown");
240: 
241:         } elseif (!$e instanceof $class) {
242:             self::fail("$class was expected but got " . get_class($e) . ($e->getMessage() ? " ({$e->getMessage()})" : ''));
243: 
244:         } elseif ($message && !self::isMatching($message, $e->getMessage())) {
245:             self::fail("$class with a message matching %2 was expected but got %1", $e->getMessage(), $message);
246:         }
247:         return $e;
248:     }
249: 
250: 
251:     /**
252:      * Checks if the function throws exception, alias for exception().
253:      * @return Exception
254:      */
255:     public static function throws($function, $class, $message = NULL)
256:     {
257:         return self::exception($function, $class, $message);
258:     }
259: 
260: 
261:     /**
262:      * Checks if the function generates PHP error or throws exception.
263:      * @param  callable
264:      * @param  int|string|array
265:      * @param  string message
266:      * @return null|Exception
267:      */
268:     public static function error($function, $expectedType, $expectedMessage = NULL)
269:     {
270:         if (is_string($expectedType) && !preg_match('#^E_[A-Z_]+\z#', $expectedType)) {
271:             return static::exception($function, $expectedType, $expectedMessage);
272:         }
273: 
274:         $expected = is_array($expectedType) ? $expectedType : array(array($expectedType, $expectedMessage));
275:         foreach ($expected as & $item) {
276:             list($expectedType, $expectedMessage) = $item;
277:             if (is_int($expectedType)) {
278:                 $item[2] = Helpers::errorTypeToString($expectedType);
279:             } elseif (is_string($expectedType)) {
280:                 $item[0] = constant($item[2] = $expectedType);
281:             } else {
282:                 throw new \Exception('Error type must be E_* constant.');
283:             }
284:         }
285: 
286:         set_error_handler(function($severity, $message, $file, $line) use (& $expected) {
287:             if (($severity & error_reporting()) !== $severity) {
288:                 return;
289:             }
290: 
291:             $errorStr = Helpers::errorTypeToString($severity) . ($message ? " ($message)" : '');
292:             list($expectedType, $expectedMessage, $expectedTypeStr) = array_shift($expected);
293:             if ($expectedType === NULL) {
294:                 restore_error_handler();
295:                 Assert::fail("Generated more errors than expected: $errorStr was generated in file $file on line $line");
296: 
297:             } elseif ($severity !== $expectedType) {
298:                 restore_error_handler();
299:                 Assert::fail("$expectedTypeStr was expected, but $errorStr was generated in file $file on line $line");
300: 
301:             } elseif ($expectedMessage && !Assert::isMatching($expectedMessage, $message)) {
302:                 restore_error_handler();
303:                 Assert::fail("$expectedTypeStr with a message matching %2 was expected but got %1", $message, $expectedMessage);
304:             }
305:         });
306: 
307:         reset($expected);
308:         call_user_func($function);
309:         restore_error_handler();
310: 
311:         if ($expected) {
312:             self::fail('Error was expected, but was not generated');
313:         }
314:     }
315: 
316: 
317:     /**
318:      * Compares result using regular expression or mask:
319:      *   %a%    one or more of anything except the end of line characters
320:      *   %a?%   zero or more of anything except the end of line characters
321:      *   %A%    one or more of anything including the end of line characters
322:      *   %A?%   zero or more of anything including the end of line characters
323:      *   %s%    one or more white space characters except the end of line characters
324:      *   %s?%   zero or more white space characters except the end of line characters
325:      *   %S%    one or more of characters except the white space
326:      *   %S?%   zero or more of characters except the white space
327:      *   %c%    a single character of any sort (except the end of line)
328:      *   %d%    one or more digits
329:      *   %d?%   zero or more digits
330:      *   %i%    signed integer value
331:      *   %f%    floating point number
332:      *   %h%    one or more HEX digits
333:      * @param  string  mask|regexp; only delimiters ~ and # are supported for regexp
334:      * @param  string
335:      * @return void
336:      */
337:     public static function match($pattern, $actual)
338:     {
339:         if (!is_string($pattern)) {
340:             throw new \Exception('Pattern must be a string.');
341: 
342:         } elseif (!is_scalar($actual) || !self::isMatching($pattern, $actual)) {
343:             self::fail('%1 should match %2', $actual, rtrim($pattern));
344:         }
345:     }
346: 
347: 
348:     /**
349:      * Compares results using mask sorted in file.
350:      * @return void
351:      */
352:     public static function matchFile($file, $actual)
353:     {
354:         $pattern = @file_get_contents($file);
355:         if ($pattern === FALSE) {
356:             throw new \Exception("Unable to read file '$file'.");
357: 
358:         } elseif (!is_scalar($actual) || !self::isMatching($pattern, $actual)) {
359:             self::fail('%1 should match %2', $actual, rtrim($pattern));
360:         }
361:     }
362: 
363: 
364:     /**
365:      * Failed assertion
366:      * @return void
367:      */
368:     public static function fail($message, $actual = NULL, $expected = NULL)
369:     {
370:         $e = new AssertException($message);
371:         $e->actual = $actual;
372:         $e->expected = $expected;
373:         if (self::$onFailure) {
374:             call_user_func(self::$onFailure, $e);
375:         } else {
376:             throw $e;
377:         }
378:     }
379: 
380: 
381:     public static function with($obj, \Closure $closure)
382:     {
383:         return $closure->bindTo($obj, $obj)->__invoke();
384:     }
385: 
386: 
387:     /********************* helpers ****************d*g**/
388: 
389: 
390:     /**
391:      * Compares using mask.
392:      * @return bool
393:      * @internal
394:      */
395:     public static function isMatching($pattern, $actual)
396:     {
397:         if (!is_string($pattern) && !is_scalar($actual)) {
398:             throw new \Exception('Value and pattern must be strings.');
399:         }
400: 
401:         $old = ini_set('pcre.backtrack_limit', '10000000');
402: 
403:         if (!preg_match('/^([~#]).+(\1)[imsxUu]*\z/s', $pattern)) {
404:             $utf8 = preg_match('#\x80-\x{10FFFF}]#u', $pattern) ? 'u' : '';
405:             $patterns = static::$patterns + array(
406:                 '[.\\\\+*?[^$(){|\x00\#]' => '\$0', // preg quoting
407:                 '[\t ]*\r?\n' => "[\\t ]*\n", // right trim
408:             );
409:             $pattern = '#^' . preg_replace_callback('#' . implode('|', array_keys($patterns)) . '#U' . $utf8, function($m) use ($patterns) {
410:                 foreach ($patterns as $re => $replacement) {
411:                     $s = preg_replace("#^$re\\z#", str_replace('\\', '\\\\', $replacement), $m[0], 1, $count);
412:                     if ($count) {
413:                         return $s;
414:                     }
415:                 }
416:             }, rtrim($pattern)) . '\s*$#sU' . $utf8;
417:             $actual = str_replace("\r\n", "\n", $actual);
418:         }
419: 
420:         $res = preg_match($pattern, $actual);
421:         ini_set('pcre.backtrack_limit', $old);
422:         if ($res === FALSE || preg_last_error()) {
423:             throw new \Exception('Error while executing regular expression. (PREG Error Code ' . preg_last_error() . ')');
424:         }
425:         return (bool) $res;
426:     }
427: 
428: 
429:     /**
430:      * Compares two structures. Ignores the identity of objects and the order of keys in the arrays.
431:      * @return bool
432:      * @internal
433:      */
434:     public static function isEqual($expected, $actual, $level = 0)
435:     {
436:         if ($level > 10) {
437:             throw new \Exception('Nesting level too deep or recursive dependency.');
438:         }
439: 
440:         if (is_float($expected) && is_float($actual) && is_finite($expected) && is_finite($actual)) {
441:             $diff = abs($expected - $actual);
442:             return ($diff < self::EPSILON) || ($diff / max(abs($expected), abs($actual)) < self::EPSILON);
443:         }
444: 
445:         if (is_object($expected) && is_object($actual) && get_class($expected) === get_class($actual)) {
446:             if ($expected === $actual) {
447:                 return TRUE;
448:             }
449:             $expected = (array) $expected;
450:             $actual = (array) $actual;
451:         }
452: 
453:         if (is_array($expected) && is_array($actual)) {
454:             ksort($expected, SORT_STRING);
455:             ksort($actual, SORT_STRING);
456:             if (array_keys($expected) !== array_keys($actual)) {
457:                 return FALSE;
458:             }
459: 
460:             foreach ($expected as $value) {
461:                 if (!self::isEqual($value, current($actual), $level + 1)) {
462:                     return FALSE;
463:                 }
464:                 next($actual);
465:             }
466:             return TRUE;
467:         }
468: 
469:         return $expected === $actual;
470:     }
471: 
472: }
473: 
474: 
475: /**
476:  * Assertion exception.
477:  *
478:  * @author     David Grudl
479:  */
480: class AssertException extends \Exception
481: {
482:     public $message;
483: 
484:     public $actual;
485: 
486:     public $expected;
487: 
488: }
489: 
API documentation generated by ApiGen 2.8.0