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