OrmFunctionalTestCase.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. <?php
  2. namespace Doctrine\Tests;
  3. /**
  4. * Base testcase class for all functional ORM testcases.
  5. *
  6. * @since 2.0
  7. */
  8. abstract class OrmFunctionalTestCase extends OrmTestCase
  9. {
  10. /* The metadata cache shared between all functional tests. */
  11. private static $_metadataCacheImpl = null;
  12. /* The query cache shared between all functional tests. */
  13. private static $_queryCacheImpl = null;
  14. /* Shared connection when a TestCase is run alone (outside of it's functional suite) */
  15. protected static $_sharedConn;
  16. /**
  17. * @var \Doctrine\ORM\EntityManager
  18. */
  19. protected $_em;
  20. /**
  21. * @var \Doctrine\ORM\Tools\SchemaTool
  22. */
  23. protected $_schemaTool;
  24. /**
  25. * @var \Doctrine\DBAL\Logging\DebugStack
  26. */
  27. protected $_sqlLoggerStack;
  28. /** The names of the model sets used in this testcase. */
  29. protected $_usedModelSets = array();
  30. /** Whether the database schema has already been created. */
  31. protected static $_tablesCreated = array();
  32. /**
  33. * Array of entity class name to their tables that were created.
  34. * @var array
  35. */
  36. protected static $_entityTablesCreated = array();
  37. /** List of model sets and their classes. */
  38. protected static $_modelSets = array(
  39. 'cms' => array(
  40. 'Doctrine\Tests\Models\CMS\CmsUser',
  41. 'Doctrine\Tests\Models\CMS\CmsPhonenumber',
  42. 'Doctrine\Tests\Models\CMS\CmsAddress',
  43. 'Doctrine\Tests\Models\CMS\CmsEmail',
  44. 'Doctrine\Tests\Models\CMS\CmsGroup',
  45. 'Doctrine\Tests\Models\CMS\CmsArticle',
  46. 'Doctrine\Tests\Models\CMS\CmsComment',
  47. ),
  48. 'forum' => array(),
  49. 'company' => array(
  50. 'Doctrine\Tests\Models\Company\CompanyPerson',
  51. 'Doctrine\Tests\Models\Company\CompanyEmployee',
  52. 'Doctrine\Tests\Models\Company\CompanyManager',
  53. 'Doctrine\Tests\Models\Company\CompanyOrganization',
  54. 'Doctrine\Tests\Models\Company\CompanyEvent',
  55. 'Doctrine\Tests\Models\Company\CompanyAuction',
  56. 'Doctrine\Tests\Models\Company\CompanyRaffle',
  57. 'Doctrine\Tests\Models\Company\CompanyCar',
  58. 'Doctrine\Tests\Models\Company\CompanyContract',
  59. ),
  60. 'ecommerce' => array(
  61. 'Doctrine\Tests\Models\ECommerce\ECommerceCart',
  62. 'Doctrine\Tests\Models\ECommerce\ECommerceCustomer',
  63. 'Doctrine\Tests\Models\ECommerce\ECommerceProduct',
  64. 'Doctrine\Tests\Models\ECommerce\ECommerceShipping',
  65. 'Doctrine\Tests\Models\ECommerce\ECommerceFeature',
  66. 'Doctrine\Tests\Models\ECommerce\ECommerceCategory'
  67. ),
  68. 'generic' => array(
  69. 'Doctrine\Tests\Models\Generic\BooleanModel',
  70. 'Doctrine\Tests\Models\Generic\DateTimeModel',
  71. 'Doctrine\Tests\Models\Generic\DecimalModel',
  72. 'Doctrine\Tests\Models\Generic\SerializationModel',
  73. ),
  74. 'routing' => array(
  75. 'Doctrine\Tests\Models\Routing\RoutingLeg',
  76. 'Doctrine\Tests\Models\Routing\RoutingLocation',
  77. 'Doctrine\Tests\Models\Routing\RoutingRoute',
  78. 'Doctrine\Tests\Models\Routing\RoutingRouteBooking',
  79. ),
  80. 'navigation' => array(
  81. 'Doctrine\Tests\Models\Navigation\NavUser',
  82. 'Doctrine\Tests\Models\Navigation\NavCountry',
  83. 'Doctrine\Tests\Models\Navigation\NavPhotos',
  84. 'Doctrine\Tests\Models\Navigation\NavTour',
  85. 'Doctrine\Tests\Models\Navigation\NavPointOfInterest',
  86. ),
  87. 'directorytree' => array(
  88. 'Doctrine\Tests\Models\DirectoryTree\AbstractContentItem',
  89. 'Doctrine\Tests\Models\DirectoryTree\File',
  90. 'Doctrine\Tests\Models\DirectoryTree\Directory',
  91. ),
  92. 'ddc117' => array(
  93. 'Doctrine\Tests\Models\DDC117\DDC117Article',
  94. 'Doctrine\Tests\Models\DDC117\DDC117Reference',
  95. 'Doctrine\Tests\Models\DDC117\DDC117Translation',
  96. 'Doctrine\Tests\Models\DDC117\DDC117ArticleDetails',
  97. 'Doctrine\Tests\Models\DDC117\DDC117ApproveChanges',
  98. 'Doctrine\Tests\Models\DDC117\DDC117Editor',
  99. 'Doctrine\Tests\Models\DDC117\DDC117Link',
  100. ),
  101. 'stockexchange' => array(
  102. 'Doctrine\Tests\Models\StockExchange\Bond',
  103. 'Doctrine\Tests\Models\StockExchange\Stock',
  104. 'Doctrine\Tests\Models\StockExchange\Market',
  105. ),
  106. 'legacy' => array(
  107. 'Doctrine\Tests\Models\Legacy\LegacyUser',
  108. 'Doctrine\Tests\Models\Legacy\LegacyUserReference',
  109. 'Doctrine\Tests\Models\Legacy\LegacyArticle',
  110. 'Doctrine\Tests\Models\Legacy\LegacyCar',
  111. ),
  112. 'customtype' => array(
  113. 'Doctrine\Tests\Models\CustomType\CustomTypeChild',
  114. 'Doctrine\Tests\Models\CustomType\CustomTypeParent',
  115. 'Doctrine\Tests\Models\CustomType\CustomTypeUpperCase',
  116. ),
  117. );
  118. protected function useModelSet($setName)
  119. {
  120. $this->_usedModelSets[$setName] = true;
  121. }
  122. /**
  123. * Sweeps the database tables and clears the EntityManager.
  124. */
  125. protected function tearDown()
  126. {
  127. $conn = static::$_sharedConn;
  128. $this->_sqlLoggerStack->enabled = false;
  129. if (isset($this->_usedModelSets['cms'])) {
  130. $conn->executeUpdate('DELETE FROM cms_users_groups');
  131. $conn->executeUpdate('DELETE FROM cms_groups');
  132. $conn->executeUpdate('DELETE FROM cms_addresses');
  133. $conn->executeUpdate('DELETE FROM cms_phonenumbers');
  134. $conn->executeUpdate('DELETE FROM cms_comments');
  135. $conn->executeUpdate('DELETE FROM cms_articles');
  136. $conn->executeUpdate('DELETE FROM cms_users');
  137. $conn->executeUpdate('DELETE FROM cms_emails');
  138. }
  139. if (isset($this->_usedModelSets['ecommerce'])) {
  140. $conn->executeUpdate('DELETE FROM ecommerce_carts_products');
  141. $conn->executeUpdate('DELETE FROM ecommerce_products_categories');
  142. $conn->executeUpdate('DELETE FROM ecommerce_products_related');
  143. $conn->executeUpdate('DELETE FROM ecommerce_carts');
  144. $conn->executeUpdate('DELETE FROM ecommerce_customers');
  145. $conn->executeUpdate('DELETE FROM ecommerce_features');
  146. $conn->executeUpdate('DELETE FROM ecommerce_products');
  147. $conn->executeUpdate('DELETE FROM ecommerce_shippings');
  148. $conn->executeUpdate('UPDATE ecommerce_categories SET parent_id = NULL');
  149. $conn->executeUpdate('DELETE FROM ecommerce_categories');
  150. }
  151. if (isset($this->_usedModelSets['company'])) {
  152. $conn->executeUpdate('DELETE FROM company_contract_employees');
  153. $conn->executeUpdate('DELETE FROM company_contract_managers');
  154. $conn->executeUpdate('DELETE FROM company_contracts');
  155. $conn->executeUpdate('DELETE FROM company_persons_friends');
  156. $conn->executeUpdate('DELETE FROM company_managers');
  157. $conn->executeUpdate('DELETE FROM company_employees');
  158. $conn->executeUpdate('UPDATE company_persons SET spouse_id = NULL');
  159. $conn->executeUpdate('DELETE FROM company_persons');
  160. $conn->executeUpdate('DELETE FROM company_raffles');
  161. $conn->executeUpdate('DELETE FROM company_auctions');
  162. $conn->executeUpdate('UPDATE company_organizations SET main_event_id = NULL');
  163. $conn->executeUpdate('DELETE FROM company_events');
  164. $conn->executeUpdate('DELETE FROM company_organizations');
  165. }
  166. if (isset($this->_usedModelSets['generic'])) {
  167. $conn->executeUpdate('DELETE FROM boolean_model');
  168. $conn->executeUpdate('DELETE FROM date_time_model');
  169. $conn->executeUpdate('DELETE FROM decimal_model');
  170. $conn->executeUpdate('DELETE FROM serialize_model');
  171. }
  172. if (isset($this->_usedModelSets['routing'])) {
  173. $conn->executeUpdate('DELETE FROM RoutingRouteLegs');
  174. $conn->executeUpdate('DELETE FROM RoutingRouteBooking');
  175. $conn->executeUpdate('DELETE FROM RoutingRoute');
  176. $conn->executeUpdate('DELETE FROM RoutingLeg');
  177. $conn->executeUpdate('DELETE FROM RoutingLocation');
  178. }
  179. if(isset($this->_usedModelSets['navigation'])) {
  180. $conn->executeUpdate('DELETE FROM navigation_tour_pois');
  181. $conn->executeUpdate('DELETE FROM navigation_photos');
  182. $conn->executeUpdate('DELETE FROM navigation_pois');
  183. $conn->executeUpdate('DELETE FROM navigation_tours');
  184. $conn->executeUpdate('DELETE FROM navigation_countries');
  185. }
  186. if (isset($this->_usedModelSets['directorytree'])) {
  187. $conn->executeUpdate('DELETE FROM ' . $this->_em->getConnection()->getDatabasePlatform()->quoteIdentifier("file"));
  188. // MySQL doesnt know deferred deletions therefore only executing the second query gives errors.
  189. $conn->executeUpdate('DELETE FROM Directory WHERE parentDirectory_id IS NOT NULL');
  190. $conn->executeUpdate('DELETE FROM Directory');
  191. }
  192. if (isset($this->_usedModelSets['ddc117'])) {
  193. $conn->executeUpdate('DELETE FROM ddc117editor_ddc117translation');
  194. $conn->executeUpdate('DELETE FROM DDC117Editor');
  195. $conn->executeUpdate('DELETE FROM DDC117ApproveChanges');
  196. $conn->executeUpdate('DELETE FROM DDC117Link');
  197. $conn->executeUpdate('DELETE FROM DDC117Reference');
  198. $conn->executeUpdate('DELETE FROM DDC117ArticleDetails');
  199. $conn->executeUpdate('DELETE FROM DDC117Translation');
  200. $conn->executeUpdate('DELETE FROM DDC117Article');
  201. }
  202. if (isset($this->_usedModelSets['stockexchange'])) {
  203. $conn->executeUpdate('DELETE FROM exchange_bonds_stocks');
  204. $conn->executeUpdate('DELETE FROM exchange_bonds');
  205. $conn->executeUpdate('DELETE FROM exchange_stocks');
  206. $conn->executeUpdate('DELETE FROM exchange_markets');
  207. }
  208. if (isset($this->_usedModelSets['legacy'])) {
  209. $conn->executeUpdate('DELETE FROM legacy_users_cars');
  210. $conn->executeUpdate('DELETE FROM legacy_users_reference');
  211. $conn->executeUpdate('DELETE FROM legacy_articles');
  212. $conn->executeUpdate('DELETE FROM legacy_cars');
  213. $conn->executeUpdate('DELETE FROM legacy_users');
  214. }
  215. if (isset($this->_usedModelSets['customtype'])) {
  216. $conn->executeUpdate('DELETE FROM customtype_parent_friends');
  217. $conn->executeUpdate('DELETE FROM customtype_parents');
  218. $conn->executeUpdate('DELETE FROM customtype_children');
  219. $conn->executeUpdate('DELETE FROM customtype_uppercases');
  220. }
  221. $this->_em->clear();
  222. }
  223. protected function setUpEntitySchema(array $classNames)
  224. {
  225. if ($this->_em === null) {
  226. throw new \RuntimeException("EntityManager not set, you have to call parent::setUp() before invoking this method.");
  227. }
  228. $classes = array();
  229. foreach ($classNames as $className) {
  230. if ( ! isset(static::$_entityTablesCreated[$className])) {
  231. static::$_entityTablesCreated[$className] = true;
  232. $classes[] = $this->_em->getClassMetadata($className);
  233. }
  234. }
  235. if ($classes) {
  236. $this->_schemaTool->createSchema($classes);
  237. }
  238. }
  239. /**
  240. * Creates a connection to the test database, if there is none yet, and
  241. * creates the necessary tables.
  242. */
  243. protected function setUp()
  244. {
  245. $forceCreateTables = false;
  246. if ( ! isset(static::$_sharedConn)) {
  247. static::$_sharedConn = TestUtil::getConnection();
  248. if (static::$_sharedConn->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) {
  249. $forceCreateTables = true;
  250. }
  251. }
  252. if (isset($GLOBALS['DOCTRINE_MARK_SQL_LOGS'])) {
  253. if (in_array(static::$_sharedConn->getDatabasePlatform()->getName(), array("mysql", "postgresql"))) {
  254. static::$_sharedConn->executeQuery('SELECT 1 /*' . get_class($this) . '*/');
  255. } else if (static::$_sharedConn->getDatabasePlatform()->getName() == "oracle") {
  256. static::$_sharedConn->executeQuery('SELECT 1 /*' . get_class($this) . '*/ FROM dual');
  257. }
  258. }
  259. if ( ! $this->_em) {
  260. $this->_em = $this->_getEntityManager();
  261. $this->_schemaTool = new \Doctrine\ORM\Tools\SchemaTool($this->_em);
  262. }
  263. $classes = array();
  264. foreach ($this->_usedModelSets as $setName => $bool) {
  265. if ( ! isset(static::$_tablesCreated[$setName])/* || $forceCreateTables*/) {
  266. foreach (static::$_modelSets[$setName] as $className) {
  267. $classes[] = $this->_em->getClassMetadata($className);
  268. }
  269. static::$_tablesCreated[$setName] = true;
  270. }
  271. }
  272. if ($classes) {
  273. $this->_schemaTool->createSchema($classes);
  274. }
  275. $this->_sqlLoggerStack->enabled = true;
  276. }
  277. /**
  278. * Gets an EntityManager for testing purposes.
  279. *
  280. * @param Configuration $config The Configuration to pass to the EntityManager.
  281. * @param EventManager $eventManager The EventManager to pass to the EntityManager.
  282. * @return EntityManager
  283. */
  284. protected function _getEntityManager($config = null, $eventManager = null) {
  285. // NOTE: Functional tests use their own shared metadata cache, because
  286. // the actual database platform used during execution has effect on some
  287. // metadata mapping behaviors (like the choice of the ID generation).
  288. if (is_null(self::$_metadataCacheImpl)) {
  289. if (isset($GLOBALS['DOCTRINE_CACHE_IMPL'])) {
  290. self::$_metadataCacheImpl = new $GLOBALS['DOCTRINE_CACHE_IMPL'];
  291. } else {
  292. self::$_metadataCacheImpl = new \Doctrine\Common\Cache\ArrayCache;
  293. }
  294. }
  295. if (is_null(self::$_queryCacheImpl)) {
  296. self::$_queryCacheImpl = new \Doctrine\Common\Cache\ArrayCache;
  297. }
  298. $this->_sqlLoggerStack = new \Doctrine\DBAL\Logging\DebugStack();
  299. $this->_sqlLoggerStack->enabled = false;
  300. //FIXME: two different configs! $conn and the created entity manager have
  301. // different configs.
  302. $config = new \Doctrine\ORM\Configuration();
  303. $config->setMetadataCacheImpl(self::$_metadataCacheImpl);
  304. $config->setQueryCacheImpl(self::$_queryCacheImpl);
  305. $config->setProxyDir(__DIR__ . '/Proxies');
  306. $config->setProxyNamespace('Doctrine\Tests\Proxies');
  307. $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver(array(), true));
  308. $conn = static::$_sharedConn;
  309. $conn->getConfiguration()->setSQLLogger($this->_sqlLoggerStack);
  310. // get rid of more global state
  311. $evm = $conn->getEventManager();
  312. foreach ($evm->getListeners() AS $event => $listeners) {
  313. foreach ($listeners AS $listener) {
  314. $evm->removeEventListener(array($event), $listener);
  315. }
  316. }
  317. if (isset($GLOBALS['db_event_subscribers'])) {
  318. foreach (explode(",", $GLOBALS['db_event_subscribers']) AS $subscriberClass) {
  319. $subscriberInstance = new $subscriberClass();
  320. $evm->addEventSubscriber($subscriberInstance);
  321. }
  322. }
  323. if (isset($GLOBALS['debug_uow_listener'])) {
  324. $evm->addEventListener(array('onFlush'), new \Doctrine\ORM\Tools\DebugUnitOfWorkListener());
  325. }
  326. return \Doctrine\ORM\EntityManager::create($conn, $config);
  327. }
  328. protected function onNotSuccessfulTest(\Exception $e)
  329. {
  330. if ($e instanceof \PHPUnit_Framework_AssertionFailedError) {
  331. throw $e;
  332. }
  333. if(isset($this->_sqlLoggerStack->queries) && count($this->_sqlLoggerStack->queries)) {
  334. $queries = "";
  335. for($i = count($this->_sqlLoggerStack->queries)-1; $i > max(count($this->_sqlLoggerStack->queries)-25, 0) && isset($this->_sqlLoggerStack->queries[$i]); $i--) {
  336. $query = $this->_sqlLoggerStack->queries[$i];
  337. $params = array_map(function($p) { if (is_object($p)) return get_class($p); else return "'".$p."'"; }, $query['params'] ?: array());
  338. $queries .= ($i+1).". SQL: '".$query['sql']."' Params: ".implode(", ", $params).PHP_EOL;
  339. }
  340. $trace = $e->getTrace();
  341. $traceMsg = "";
  342. foreach($trace AS $part) {
  343. if(isset($part['file'])) {
  344. if(strpos($part['file'], "PHPUnit/") !== false) {
  345. // Beginning with PHPUnit files we don't print the trace anymore.
  346. break;
  347. }
  348. $traceMsg .= $part['file'].":".$part['line'].PHP_EOL;
  349. }
  350. }
  351. $message = "[".get_class($e)."] ".$e->getMessage().PHP_EOL.PHP_EOL."With queries:".PHP_EOL.$queries.PHP_EOL."Trace:".PHP_EOL.$traceMsg;
  352. throw new \Exception($message, (int)$e->getCode(), $e);
  353. }
  354. throw $e;
  355. }
  356. /**
  357. * Using the SQL Logger Stack this method retrieves the current query count executed in this test.
  358. *
  359. * @return int
  360. */
  361. protected function getCurrentQueryCount()
  362. {
  363. return count($this->_sqlLoggerStack->queries);
  364. }
  365. }