TestCase.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @since 1.2.0
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace Cake\TestSuite;
  15. use Cake\Core\App;
  16. use Cake\Core\Configure;
  17. use Cake\Datasource\ConnectionManager;
  18. use Cake\Event\EventManager;
  19. use Cake\ORM\Exception\MissingTableClassException;
  20. use Cake\ORM\TableRegistry;
  21. use Cake\Routing\Router;
  22. use Cake\TestSuite\Constraint\EventFired;
  23. use Cake\TestSuite\Constraint\EventFiredWith;
  24. use Cake\Utility\Inflector;
  25. use Exception;
  26. use PHPUnit_Framework_TestCase;
  27. /**
  28. * Cake TestCase class
  29. */
  30. abstract class TestCase extends PHPUnit_Framework_TestCase
  31. {
  32. /**
  33. * The class responsible for managing the creation, loading and removing of fixtures
  34. *
  35. * @var \Cake\TestSuite\Fixture\FixtureManager|null
  36. */
  37. public $fixtureManager = null;
  38. /**
  39. * By default, all fixtures attached to this class will be truncated and reloaded after each test.
  40. * Set this to false to handle manually
  41. *
  42. * @var bool
  43. */
  44. public $autoFixtures = true;
  45. /**
  46. * Control table create/drops on each test method.
  47. *
  48. * If true, tables will still be dropped at the
  49. * end of each test runner execution.
  50. *
  51. * @var bool
  52. */
  53. public $dropTables = false;
  54. /**
  55. * Configure values to restore at end of test.
  56. *
  57. * @var array
  58. */
  59. protected $_configure = [];
  60. /**
  61. * Path settings to restore at the end of the test.
  62. *
  63. * @var array
  64. */
  65. protected $_pathRestore = [];
  66. /**
  67. * Overrides SimpleTestCase::skipIf to provide a boolean return value
  68. *
  69. * @param bool $shouldSkip Whether or not the test should be skipped.
  70. * @param string $message The message to display.
  71. * @return bool
  72. */
  73. public function skipIf($shouldSkip, $message = '')
  74. {
  75. if ($shouldSkip) {
  76. $this->markTestSkipped($message);
  77. }
  78. return $shouldSkip;
  79. }
  80. /**
  81. * Setup the test case, backup the static object values so they can be restored.
  82. * Specifically backs up the contents of Configure and paths in App if they have
  83. * not already been backed up.
  84. *
  85. * @return void
  86. */
  87. public function setUp()
  88. {
  89. parent::setUp();
  90. if (!$this->_configure) {
  91. $this->_configure = Configure::read();
  92. }
  93. if (class_exists('Cake\Routing\Router', false)) {
  94. Router::reload();
  95. }
  96. EventManager::instance(new EventManager());
  97. }
  98. /**
  99. * teardown any static object changes and restore them.
  100. *
  101. * @return void
  102. */
  103. public function tearDown()
  104. {
  105. parent::tearDown();
  106. if ($this->_configure) {
  107. Configure::clear();
  108. Configure::write($this->_configure);
  109. }
  110. TableRegistry::clear();
  111. }
  112. /**
  113. * Chooses which fixtures to load for a given test
  114. *
  115. * Each parameter is a model name that corresponds to a fixture, i.e. 'Posts', 'Authors', etc.
  116. *
  117. * @return void
  118. * @see \Cake\TestSuite\TestCase::$autoFixtures
  119. * @throws \Exception when no fixture manager is available.
  120. */
  121. public function loadFixtures()
  122. {
  123. if ($this->fixtureManager === null) {
  124. throw new Exception('No fixture manager to load the test fixture');
  125. }
  126. $args = func_get_args();
  127. foreach ($args as $class) {
  128. $this->fixtureManager->loadSingle($class, null, $this->dropTables);
  129. }
  130. }
  131. /**
  132. * Asserts that a global event was fired. You must track events in your event manager for this assertion to work
  133. *
  134. * @param string $name Event name
  135. * @param EventManager|null $eventManager Event manager to check, defaults to global event manager
  136. * @param string $message Assertion failure message
  137. * @return void
  138. */
  139. public function assertEventFired($name, $eventManager = null, $message = '')
  140. {
  141. if (!$eventManager) {
  142. $eventManager = EventManager::instance();
  143. }
  144. $this->assertThat($name, new EventFired($eventManager), $message);
  145. }
  146. /**
  147. * Asserts an event was fired with data
  148. *
  149. * If a third argument is passed, that value is used to compare with the value in $dataKey
  150. *
  151. * @param string $name Event name
  152. * @param string $dataKey Data key
  153. * @param string $dataValue Data value
  154. * @param EventManager|null $eventManager Event manager to check, defaults to global event manager
  155. * @param string $message Assertion failure message
  156. * @return void
  157. */
  158. public function assertEventFiredWith($name, $dataKey, $dataValue, $eventManager = null, $message = '')
  159. {
  160. if (!$eventManager) {
  161. $eventManager = EventManager::instance();
  162. }
  163. $this->assertThat($name, new EventFiredWith($eventManager, $dataKey, $dataValue), $message);
  164. }
  165. /**
  166. * Assert text equality, ignoring differences in newlines.
  167. * Helpful for doing cross platform tests of blocks of text.
  168. *
  169. * @param string $expected The expected value.
  170. * @param string $result The actual value.
  171. * @param string $message The message to use for failure.
  172. * @return void
  173. */
  174. public function assertTextNotEquals($expected, $result, $message = '')
  175. {
  176. $expected = str_replace(["\r\n", "\r"], "\n", $expected);
  177. $result = str_replace(["\r\n", "\r"], "\n", $result);
  178. $this->assertNotEquals($expected, $result, $message);
  179. }
  180. /**
  181. * Assert text equality, ignoring differences in newlines.
  182. * Helpful for doing cross platform tests of blocks of text.
  183. *
  184. * @param string $expected The expected value.
  185. * @param string $result The actual value.
  186. * @param string $message The message to use for failure.
  187. * @return void
  188. */
  189. public function assertTextEquals($expected, $result, $message = '')
  190. {
  191. $expected = str_replace(["\r\n", "\r"], "\n", $expected);
  192. $result = str_replace(["\r\n", "\r"], "\n", $result);
  193. $this->assertEquals($expected, $result, $message);
  194. }
  195. /**
  196. * Asserts that a string starts with a given prefix, ignoring differences in newlines.
  197. * Helpful for doing cross platform tests of blocks of text.
  198. *
  199. * @param string $prefix The prefix to check for.
  200. * @param string $string The string to search in.
  201. * @param string $message The message to use for failure.
  202. * @return void
  203. */
  204. public function assertTextStartsWith($prefix, $string, $message = '')
  205. {
  206. $prefix = str_replace(["\r\n", "\r"], "\n", $prefix);
  207. $string = str_replace(["\r\n", "\r"], "\n", $string);
  208. $this->assertStringStartsWith($prefix, $string, $message);
  209. }
  210. /**
  211. * Asserts that a string starts not with a given prefix, ignoring differences in newlines.
  212. * Helpful for doing cross platform tests of blocks of text.
  213. *
  214. * @param string $prefix The prefix to not find.
  215. * @param string $string The string to search.
  216. * @param string $message The message to use for failure.
  217. * @return void
  218. */
  219. public function assertTextStartsNotWith($prefix, $string, $message = '')
  220. {
  221. $prefix = str_replace(["\r\n", "\r"], "\n", $prefix);
  222. $string = str_replace(["\r\n", "\r"], "\n", $string);
  223. $this->assertStringStartsNotWith($prefix, $string, $message);
  224. }
  225. /**
  226. * Asserts that a string ends with a given prefix, ignoring differences in newlines.
  227. * Helpful for doing cross platform tests of blocks of text.
  228. *
  229. * @param string $suffix The suffix to find.
  230. * @param string $string The string to search.
  231. * @param string $message The message to use for failure.
  232. * @return void
  233. */
  234. public function assertTextEndsWith($suffix, $string, $message = '')
  235. {
  236. $suffix = str_replace(["\r\n", "\r"], "\n", $suffix);
  237. $string = str_replace(["\r\n", "\r"], "\n", $string);
  238. $this->assertStringEndsWith($suffix, $string, $message);
  239. }
  240. /**
  241. * Asserts that a string ends not with a given prefix, ignoring differences in newlines.
  242. * Helpful for doing cross platform tests of blocks of text.
  243. *
  244. * @param string $suffix The suffix to not find.
  245. * @param string $string The string to search.
  246. * @param string $message The message to use for failure.
  247. * @return void
  248. */
  249. public function assertTextEndsNotWith($suffix, $string, $message = '')
  250. {
  251. $suffix = str_replace(["\r\n", "\r"], "\n", $suffix);
  252. $string = str_replace(["\r\n", "\r"], "\n", $string);
  253. $this->assertStringEndsNotWith($suffix, $string, $message);
  254. }
  255. /**
  256. * Assert that a string contains another string, ignoring differences in newlines.
  257. * Helpful for doing cross platform tests of blocks of text.
  258. *
  259. * @param string $needle The string to search for.
  260. * @param string $haystack The string to search through.
  261. * @param string $message The message to display on failure.
  262. * @param bool $ignoreCase Whether or not the search should be case-sensitive.
  263. * @return void
  264. */
  265. public function assertTextContains($needle, $haystack, $message = '', $ignoreCase = false)
  266. {
  267. $needle = str_replace(["\r\n", "\r"], "\n", $needle);
  268. $haystack = str_replace(["\r\n", "\r"], "\n", $haystack);
  269. $this->assertContains($needle, $haystack, $message, $ignoreCase);
  270. }
  271. /**
  272. * Assert that a text doesn't contain another text, ignoring differences in newlines.
  273. * Helpful for doing cross platform tests of blocks of text.
  274. *
  275. * @param string $needle The string to search for.
  276. * @param string $haystack The string to search through.
  277. * @param string $message The message to display on failure.
  278. * @param bool $ignoreCase Whether or not the search should be case-sensitive.
  279. * @return void
  280. */
  281. public function assertTextNotContains($needle, $haystack, $message = '', $ignoreCase = false)
  282. {
  283. $needle = str_replace(["\r\n", "\r"], "\n", $needle);
  284. $haystack = str_replace(["\r\n", "\r"], "\n", $haystack);
  285. $this->assertNotContains($needle, $haystack, $message, $ignoreCase);
  286. }
  287. /**
  288. * Asserts HTML tags.
  289. *
  290. * @param string $string An HTML/XHTML/XML string
  291. * @param array $expected An array, see above
  292. * @param bool $fullDebug Whether or not more verbose output should be used.
  293. * @return void
  294. * @deprecated 3.0. Use assertHtml() instead.
  295. */
  296. public function assertTags($string, $expected, $fullDebug = false)
  297. {
  298. trigger_error(
  299. 'assertTags() is deprecated, use assertHtml() instead.',
  300. E_USER_DEPRECATED
  301. );
  302. static::assertHtml($expected, $string, $fullDebug);
  303. }
  304. /**
  305. * Asserts HTML tags.
  306. *
  307. * Takes an array $expected and generates a regex from it to match the provided $string.
  308. * Samples for $expected:
  309. *
  310. * Checks for an input tag with a name attribute (contains any non-empty value) and an id
  311. * attribute that contains 'my-input':
  312. *
  313. * ```
  314. * ['input' => ['name', 'id' => 'my-input']]
  315. * ```
  316. *
  317. * Checks for two p elements with some text in them:
  318. *
  319. * ```
  320. * [
  321. * ['p' => true],
  322. * 'textA',
  323. * '/p',
  324. * ['p' => true],
  325. * 'textB',
  326. * '/p'
  327. * ]
  328. * ```
  329. *
  330. * You can also specify a pattern expression as part of the attribute values, or the tag
  331. * being defined, if you prepend the value with preg: and enclose it with slashes, like so:
  332. *
  333. * ```
  334. * [
  335. * ['input' => ['name', 'id' => 'preg:/FieldName\d+/']],
  336. * 'preg:/My\s+field/'
  337. * ]
  338. * ```
  339. *
  340. * Important: This function is very forgiving about whitespace and also accepts any
  341. * permutation of attribute order. It will also allow whitespace between specified tags.
  342. *
  343. * @param array $expected An array, see above
  344. * @param string $string An HTML/XHTML/XML string
  345. * @param bool $fullDebug Whether or not more verbose output should be used.
  346. * @return bool
  347. */
  348. public function assertHtml($expected, $string, $fullDebug = false)
  349. {
  350. $regex = [];
  351. $normalized = [];
  352. foreach ((array)$expected as $key => $val) {
  353. if (!is_numeric($key)) {
  354. $normalized[] = [$key => $val];
  355. } else {
  356. $normalized[] = $val;
  357. }
  358. }
  359. $i = 0;
  360. foreach ($normalized as $tags) {
  361. if (!is_array($tags)) {
  362. $tags = (string)$tags;
  363. }
  364. $i++;
  365. if (is_string($tags) && $tags{0} === '<') {
  366. $tags = [substr($tags, 1) => []];
  367. } elseif (is_string($tags)) {
  368. $tagsTrimmed = preg_replace('/\s+/m', '', $tags);
  369. if (preg_match('/^\*?\//', $tags, $match) && $tagsTrimmed !== '//') {
  370. $prefix = [null, null];
  371. if ($match[0] === '*/') {
  372. $prefix = ['Anything, ', '.*?'];
  373. }
  374. $regex[] = [
  375. sprintf('%sClose %s tag', $prefix[0], substr($tags, strlen($match[0]))),
  376. sprintf('%s<[\s]*\/[\s]*%s[\s]*>[\n\r]*', $prefix[1], substr($tags, strlen($match[0]))),
  377. $i,
  378. ];
  379. continue;
  380. }
  381. if (!empty($tags) && preg_match('/^preg\:\/(.+)\/$/i', $tags, $matches)) {
  382. $tags = $matches[1];
  383. $type = 'Regex matches';
  384. } else {
  385. $tags = preg_quote($tags, '/');
  386. $type = 'Text equals';
  387. }
  388. $regex[] = [
  389. sprintf('%s "%s"', $type, $tags),
  390. $tags,
  391. $i,
  392. ];
  393. continue;
  394. }
  395. foreach ($tags as $tag => $attributes) {
  396. $regex[] = [
  397. sprintf('Open %s tag', $tag),
  398. sprintf('[\s]*<%s', preg_quote($tag, '/')),
  399. $i,
  400. ];
  401. if ($attributes === true) {
  402. $attributes = [];
  403. }
  404. $attrs = [];
  405. $explanations = [];
  406. $i = 1;
  407. foreach ($attributes as $attr => $val) {
  408. if (is_numeric($attr) && preg_match('/^preg\:\/(.+)\/$/i', $val, $matches)) {
  409. $attrs[] = $matches[1];
  410. $explanations[] = sprintf('Regex "%s" matches', $matches[1]);
  411. continue;
  412. }
  413. $quotes = '["\']';
  414. if (is_numeric($attr)) {
  415. $attr = $val;
  416. $val = '.+?';
  417. $explanations[] = sprintf('Attribute "%s" present', $attr);
  418. } elseif (!empty($val) && preg_match('/^preg\:\/(.+)\/$/i', $val, $matches)) {
  419. $val = str_replace(
  420. ['.*', '.+'],
  421. ['.*?', '.+?'],
  422. $matches[1]
  423. );
  424. $quotes = $val !== $matches[1] ? '["\']' : '["\']?';
  425. $explanations[] = sprintf('Attribute "%s" matches "%s"', $attr, $val);
  426. } else {
  427. $explanations[] = sprintf('Attribute "%s" == "%s"', $attr, $val);
  428. $val = preg_quote($val, '/');
  429. }
  430. $attrs[] = '[\s]+' . preg_quote($attr, '/') . '=' . $quotes . $val . $quotes;
  431. $i++;
  432. }
  433. if ($attrs) {
  434. $regex[] = [
  435. 'explains' => $explanations,
  436. 'attrs' => $attrs,
  437. ];
  438. }
  439. $regex[] = [
  440. sprintf('End %s tag', $tag),
  441. '[\s]*\/?[\s]*>[\n\r]*',
  442. $i,
  443. ];
  444. }
  445. }
  446. foreach ($regex as $i => $assertion) {
  447. $matches = false;
  448. if (isset($assertion['attrs'])) {
  449. $string = $this->_assertAttributes($assertion, $string, $fullDebug, $regex);
  450. if ($fullDebug === true && $string === false) {
  451. debug($string, true);
  452. debug($regex, true);
  453. }
  454. continue;
  455. }
  456. list($description, $expressions, $itemNum) = $assertion;
  457. foreach ((array)$expressions as $expression) {
  458. $expression = sprintf('/^%s/s', $expression);
  459. if (preg_match($expression, $string, $match)) {
  460. $matches = true;
  461. $string = substr($string, strlen($match[0]));
  462. break;
  463. }
  464. }
  465. if (!$matches) {
  466. if ($fullDebug === true) {
  467. debug($string);
  468. debug($regex);
  469. }
  470. $this->assertRegExp($expression, $string, sprintf('Item #%d / regex #%d failed: %s', $itemNum, $i, $description));
  471. return false;
  472. }
  473. }
  474. $this->assertTrue(true, '%s');
  475. return true;
  476. }
  477. /**
  478. * Check the attributes as part of an assertTags() check.
  479. *
  480. * @param array $assertions Assertions to run.
  481. * @param string $string The HTML string to check.
  482. * @param bool $fullDebug Whether or not more verbose output should be used.
  483. * @param array|string $regex Full regexp from `assertHtml`
  484. * @return string
  485. */
  486. protected function _assertAttributes($assertions, $string, $fullDebug = false, $regex = '')
  487. {
  488. $asserts = $assertions['attrs'];
  489. $explains = $assertions['explains'];
  490. do {
  491. $matches = false;
  492. foreach ($asserts as $j => $assert) {
  493. if (preg_match(sprintf('/^%s/s', $assert), $string, $match)) {
  494. $matches = true;
  495. $string = substr($string, strlen($match[0]));
  496. array_splice($asserts, $j, 1);
  497. array_splice($explains, $j, 1);
  498. break;
  499. }
  500. }
  501. if ($matches === false) {
  502. if ($fullDebug === true) {
  503. debug($string);
  504. debug($regex);
  505. }
  506. $this->assertTrue(false, 'Attribute did not match. Was expecting ' . $explains[$j]);
  507. }
  508. $len = count($asserts);
  509. } while ($len > 0);
  510. return $string;
  511. }
  512. /**
  513. * Normalize a path for comparison.
  514. *
  515. * @param string $path Path separated by "/" slash.
  516. * @return string Normalized path separated by DIRECTORY_SEPARATOR.
  517. */
  518. protected function _normalizePath($path)
  519. {
  520. return str_replace('/', DIRECTORY_SEPARATOR, $path);
  521. }
  522. // @codingStandardsIgnoreStart
  523. /**
  524. * Compatibility function to test if a value is between an acceptable range.
  525. *
  526. * @param float $expected
  527. * @param float $result
  528. * @param float $margin the rage of acceptation
  529. * @param string $message the text to display if the assertion is not correct
  530. * @return void
  531. */
  532. protected static function assertWithinRange($expected, $result, $margin, $message = '')
  533. {
  534. $upper = $result + $margin;
  535. $lower = $result - $margin;
  536. static::assertTrue((($expected <= $upper) && ($expected >= $lower)), $message);
  537. }
  538. /**
  539. * Compatibility function to test if a value is not between an acceptable range.
  540. *
  541. * @param float $expected
  542. * @param float $result
  543. * @param float $margin the rage of acceptation
  544. * @param string $message the text to display if the assertion is not correct
  545. * @return void
  546. */
  547. protected static function assertNotWithinRange($expected, $result, $margin, $message = '')
  548. {
  549. $upper = $result + $margin;
  550. $lower = $result - $margin;
  551. static::assertTrue((($expected > $upper) || ($expected < $lower)), $message);
  552. }
  553. /**
  554. * Compatibility function to test paths.
  555. *
  556. * @param string $expected
  557. * @param string $result
  558. * @param string $message the text to display if the assertion is not correct
  559. * @return void
  560. */
  561. protected static function assertPathEquals($expected, $result, $message = '')
  562. {
  563. $expected = str_replace(DIRECTORY_SEPARATOR, '/', $expected);
  564. $result = str_replace(DIRECTORY_SEPARATOR, '/', $result);
  565. static::assertEquals($expected, $result, $message);
  566. }
  567. /**
  568. * Compatibility function for skipping.
  569. *
  570. * @param bool $condition Condition to trigger skipping
  571. * @param string $message Message for skip
  572. * @return bool
  573. */
  574. protected function skipUnless($condition, $message = '')
  575. {
  576. if (!$condition) {
  577. $this->markTestSkipped($message);
  578. }
  579. return $condition;
  580. }
  581. // @codingStandardsIgnoreEnd
  582. /**
  583. * Mock a model, maintain fixtures and table association
  584. *
  585. * @param string $alias The model to get a mock for.
  586. * @param mixed $methods The list of methods to mock
  587. * @param array $options The config data for the mock's constructor.
  588. * @throws \Cake\ORM\Exception\MissingTableClassException
  589. * @return \Cake\ORM\Table|\PHPUnit_Framework_MockObject_MockObject
  590. */
  591. public function getMockForModel($alias, array $methods = [], array $options = [])
  592. {
  593. if (empty($options['className'])) {
  594. $class = Inflector::camelize($alias);
  595. $className = App::className($class, 'Model/Table', 'Table');
  596. if (!$className) {
  597. throw new MissingTableClassException([$alias]);
  598. }
  599. $options['className'] = $className;
  600. }
  601. $connectionName = $options['className']::defaultConnectionName();
  602. $connection = ConnectionManager::get($connectionName);
  603. list(, $baseClass) = pluginSplit($alias);
  604. $options += ['alias' => $baseClass, 'connection' => $connection];
  605. $options += TableRegistry::config($alias);
  606. $mock = $this->getMockBuilder($options['className'])
  607. ->setMethods($methods)
  608. ->setConstructorArgs([$options])
  609. ->getMock();
  610. if (empty($options['entityClass']) && $mock->entityClass() === '\Cake\ORM\Entity') {
  611. $parts = explode('\\', $options['className']);
  612. $entityAlias = Inflector::singularize(substr(array_pop($parts), 0, -5));
  613. $entityClass = implode('\\', array_slice($parts, 0, -1)) . '\Entity\\' . $entityAlias;
  614. if (class_exists($entityClass)) {
  615. $mock->entityClass($entityClass);
  616. }
  617. }
  618. if (stripos($mock->table(), 'mock') === 0) {
  619. $mock->table(Inflector::tableize($baseClass));
  620. }
  621. TableRegistry::set($baseClass, $mock);
  622. TableRegistry::set($alias, $mock);
  623. return $mock;
  624. }
  625. }