pictcode / lib / Cake / TestSuite / Fixture / CakeFixtureManager.php @ 48ae03cf
履歴 | 表示 | アノテート | ダウンロード (8.581 KB)
| 1 | 635eef61 | spyder1211 | <?php
|
|---|---|---|---|
| 2 | /**
|
||
| 3 | * A factory class to manage the life cycle of test fixtures
|
||
| 4 | *
|
||
| 5 | * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||
| 6 | * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||
| 7 | *
|
||
| 8 | * Licensed under The MIT License
|
||
| 9 | * For full copyright and license information, please see the LICENSE.txt
|
||
| 10 | * Redistributions of files must retain the above copyright notice.
|
||
| 11 | *
|
||
| 12 | * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||
| 13 | * @link http://cakephp.org CakePHP(tm) Project
|
||
| 14 | * @package Cake.TestSuite.Fixture
|
||
| 15 | * @since CakePHP(tm) v 2.0
|
||
| 16 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||
| 17 | */
|
||
| 18 | |||
| 19 | App::uses('ConnectionManager', 'Model'); |
||
| 20 | App::uses('ClassRegistry', 'Utility'); |
||
| 21 | |||
| 22 | /**
|
||
| 23 | * A factory class to manage the life cycle of test fixtures
|
||
| 24 | *
|
||
| 25 | * @package Cake.TestSuite.Fixture
|
||
| 26 | */
|
||
| 27 | class CakeFixtureManager { |
||
| 28 | |||
| 29 | /**
|
||
| 30 | * Was this class already initialized?
|
||
| 31 | *
|
||
| 32 | * @var bool
|
||
| 33 | */
|
||
| 34 | protected $_initialized = false; |
||
| 35 | |||
| 36 | /**
|
||
| 37 | * Default datasource to use
|
||
| 38 | *
|
||
| 39 | * @var DataSource
|
||
| 40 | */
|
||
| 41 | protected $_db = null; |
||
| 42 | |||
| 43 | /**
|
||
| 44 | * Holds the fixture classes that where instantiated
|
||
| 45 | *
|
||
| 46 | * @var array
|
||
| 47 | */
|
||
| 48 | protected $_loaded = array(); |
||
| 49 | |||
| 50 | /**
|
||
| 51 | * Holds the fixture classes that where instantiated indexed by class name
|
||
| 52 | *
|
||
| 53 | * @var array
|
||
| 54 | */
|
||
| 55 | protected $_fixtureMap = array(); |
||
| 56 | |||
| 57 | /**
|
||
| 58 | * Inspects the test to look for unloaded fixtures and loads them
|
||
| 59 | *
|
||
| 60 | * @param CakeTestCase $test the test case to inspect
|
||
| 61 | * @return void
|
||
| 62 | */
|
||
| 63 | public function fixturize($test) { |
||
| 64 | if (!$this->_initialized) { |
||
| 65 | ClassRegistry::config(array('ds' => 'test', 'testing' => true)); |
||
| 66 | } |
||
| 67 | if (empty($test->fixtures) || !empty($this->_processed[get_class($test)])) { |
||
| 68 | $test->db = $this->_db; |
||
| 69 | return;
|
||
| 70 | } |
||
| 71 | $this->_initDb();
|
||
| 72 | $test->db = $this->_db; |
||
| 73 | if (!is_array($test->fixtures)) { |
||
| 74 | $test->fixtures = array_map('trim', explode(',', $test->fixtures)); |
||
| 75 | } |
||
| 76 | if (isset($test->fixtures)) { |
||
| 77 | $this->_loadFixtures($test->fixtures); |
||
| 78 | } |
||
| 79 | |||
| 80 | $this->_processed[get_class($test)] = true; |
||
| 81 | } |
||
| 82 | |||
| 83 | /**
|
||
| 84 | * Initializes this class with a DataSource object to use as default for all fixtures
|
||
| 85 | *
|
||
| 86 | * @return void
|
||
| 87 | */
|
||
| 88 | protected function _initDb() { |
||
| 89 | if ($this->_initialized) { |
||
| 90 | return;
|
||
| 91 | } |
||
| 92 | $db = ConnectionManager::getDataSource('test'); |
||
| 93 | $db->cacheSources = false; |
||
| 94 | $this->_db = $db; |
||
| 95 | $this->_initialized = true; |
||
| 96 | } |
||
| 97 | |||
| 98 | /**
|
||
| 99 | * Parse the fixture path included in test cases, to get the fixture class name, and the
|
||
| 100 | * real fixture path including sub-directories
|
||
| 101 | *
|
||
| 102 | * @param string $fixturePath the fixture path to parse
|
||
| 103 | * @return array containing fixture class name and optional additional path
|
||
| 104 | */
|
||
| 105 | protected function _parseFixturePath($fixturePath) { |
||
| 106 | $pathTokenArray = explode('/', $fixturePath); |
||
| 107 | $fixture = array_pop($pathTokenArray); |
||
| 108 | $additionalPath = ''; |
||
| 109 | foreach ($pathTokenArray as $pathToken) { |
||
| 110 | $additionalPath .= DS . $pathToken; |
||
| 111 | } |
||
| 112 | return array('fixture' => $fixture, 'additionalPath' => $additionalPath); |
||
| 113 | } |
||
| 114 | |||
| 115 | /**
|
||
| 116 | * Looks for fixture files and instantiates the classes accordingly
|
||
| 117 | *
|
||
| 118 | * @param array $fixtures the fixture names to load using the notation {type}.{name}
|
||
| 119 | * @return void
|
||
| 120 | * @throws UnexpectedValueException when a referenced fixture does not exist.
|
||
| 121 | */
|
||
| 122 | protected function _loadFixtures($fixtures) { |
||
| 123 | foreach ($fixtures as $fixture) { |
||
| 124 | $fixtureFile = null; |
||
| 125 | $fixtureIndex = $fixture; |
||
| 126 | if (isset($this->_loaded[$fixture])) { |
||
| 127 | continue;
|
||
| 128 | } |
||
| 129 | |||
| 130 | if (strpos($fixture, 'core.') === 0) { |
||
| 131 | $fixture = substr($fixture, strlen('core.')); |
||
| 132 | $fixturePaths[] = CAKE . 'Test' . DS . 'Fixture'; |
||
| 133 | } elseif (strpos($fixture, 'app.') === 0) { |
||
| 134 | $fixturePrefixLess = substr($fixture, strlen('app.')); |
||
| 135 | $fixtureParsedPath = $this->_parseFixturePath($fixturePrefixLess); |
||
| 136 | $fixture = $fixtureParsedPath['fixture']; |
||
| 137 | $fixturePaths = array( |
||
| 138 | TESTS . 'Fixture' . $fixtureParsedPath['additionalPath'] |
||
| 139 | ); |
||
| 140 | } elseif (strpos($fixture, 'plugin.') === 0) { |
||
| 141 | $explodedFixture = explode('.', $fixture, 3); |
||
| 142 | $pluginName = $explodedFixture[1]; |
||
| 143 | $fixtureParsedPath = $this->_parseFixturePath($explodedFixture[2]); |
||
| 144 | $fixture = $fixtureParsedPath['fixture']; |
||
| 145 | $fixturePaths = array( |
||
| 146 | CakePlugin::path(Inflector::camelize($pluginName)) . 'Test' . DS . 'Fixture' . $fixtureParsedPath['additionalPath'], |
||
| 147 | TESTS . 'Fixture' . $fixtureParsedPath['additionalPath'] |
||
| 148 | ); |
||
| 149 | } else {
|
||
| 150 | $fixturePaths = array( |
||
| 151 | TESTS . 'Fixture', |
||
| 152 | CAKE . 'Test' . DS . 'Fixture' |
||
| 153 | ); |
||
| 154 | } |
||
| 155 | |||
| 156 | $loaded = false; |
||
| 157 | foreach ($fixturePaths as $path) { |
||
| 158 | $className = Inflector::camelize($fixture); |
||
| 159 | if (is_readable($path . DS . $className . 'Fixture.php')) { |
||
| 160 | $fixtureFile = $path . DS . $className . 'Fixture.php'; |
||
| 161 | require_once $fixtureFile; |
||
| 162 | $fixtureClass = $className . 'Fixture'; |
||
| 163 | $this->_loaded[$fixtureIndex] = new $fixtureClass(); |
||
| 164 | $this->_fixtureMap[$fixtureClass] = $this->_loaded[$fixtureIndex]; |
||
| 165 | $loaded = true; |
||
| 166 | break;
|
||
| 167 | } |
||
| 168 | } |
||
| 169 | |||
| 170 | if (!$loaded) { |
||
| 171 | $firstPath = str_replace(array(APP, CAKE_CORE_INCLUDE_PATH, ROOT), '', $fixturePaths[0] . DS . $className . 'Fixture.php'); |
||
| 172 | throw new UnexpectedValueException(__d('cake_dev', 'Referenced fixture class %s (%s) not found', $className, $firstPath)); |
||
| 173 | } |
||
| 174 | } |
||
| 175 | } |
||
| 176 | |||
| 177 | /**
|
||
| 178 | * Runs the drop and create commands on the fixtures if necessary.
|
||
| 179 | *
|
||
| 180 | * @param CakeTestFixture $fixture the fixture object to create
|
||
| 181 | * @param DataSource $db the datasource instance to use
|
||
| 182 | * @param bool $drop whether drop the fixture if it is already created or not
|
||
| 183 | * @return void
|
||
| 184 | */
|
||
| 185 | protected function _setupTable($fixture, $db = null, $drop = true) { |
||
| 186 | if (!$db) { |
||
| 187 | if (!empty($fixture->useDbConfig)) { |
||
| 188 | $db = ConnectionManager::getDataSource($fixture->useDbConfig); |
||
| 189 | } else {
|
||
| 190 | $db = $this->_db; |
||
| 191 | } |
||
| 192 | } |
||
| 193 | if (!empty($fixture->created) && in_array($db->configKeyName, $fixture->created)) { |
||
| 194 | return;
|
||
| 195 | } |
||
| 196 | |||
| 197 | $sources = (array)$db->listSources(); |
||
| 198 | $table = $db->config['prefix'] . $fixture->table; |
||
| 199 | $exists = in_array($table, $sources); |
||
| 200 | |||
| 201 | if ($drop && $exists) { |
||
| 202 | $fixture->drop($db); |
||
| 203 | $fixture->create($db); |
||
| 204 | } elseif (!$exists) { |
||
| 205 | $fixture->create($db); |
||
| 206 | } else {
|
||
| 207 | $fixture->created[] = $db->configKeyName; |
||
| 208 | } |
||
| 209 | } |
||
| 210 | |||
| 211 | /**
|
||
| 212 | * Creates the fixtures tables and inserts data on them.
|
||
| 213 | *
|
||
| 214 | * @param CakeTestCase $test the test to inspect for fixture loading
|
||
| 215 | * @return void
|
||
| 216 | */
|
||
| 217 | public function load(CakeTestCase $test) { |
||
| 218 | if (empty($test->fixtures)) { |
||
| 219 | return;
|
||
| 220 | } |
||
| 221 | $fixtures = $test->fixtures; |
||
| 222 | if (empty($fixtures) || !$test->autoFixtures) { |
||
| 223 | return;
|
||
| 224 | } |
||
| 225 | |||
| 226 | foreach ($fixtures as $f) { |
||
| 227 | if (!empty($this->_loaded[$f])) { |
||
| 228 | $fixture = $this->_loaded[$f]; |
||
| 229 | $db = ConnectionManager::getDataSource($fixture->useDbConfig); |
||
| 230 | $db->begin();
|
||
| 231 | $this->_setupTable($fixture, $db, $test->dropTables); |
||
| 232 | $fixture->truncate($db); |
||
| 233 | $fixture->insert($db); |
||
| 234 | $db->commit();
|
||
| 235 | } |
||
| 236 | } |
||
| 237 | } |
||
| 238 | |||
| 239 | /**
|
||
| 240 | * Truncates the fixtures tables
|
||
| 241 | *
|
||
| 242 | * @param CakeTestCase $test the test to inspect for fixture unloading
|
||
| 243 | * @return void
|
||
| 244 | */
|
||
| 245 | public function unload(CakeTestCase $test) { |
||
| 246 | $fixtures = !empty($test->fixtures) ? $test->fixtures : array(); |
||
| 247 | foreach (array_reverse($fixtures) as $f) { |
||
| 248 | if (isset($this->_loaded[$f])) { |
||
| 249 | $fixture = $this->_loaded[$f]; |
||
| 250 | if (!empty($fixture->created)) { |
||
| 251 | foreach ($fixture->created as $ds) { |
||
| 252 | $db = ConnectionManager::getDataSource($ds); |
||
| 253 | $fixture->truncate($db); |
||
| 254 | } |
||
| 255 | } |
||
| 256 | } |
||
| 257 | } |
||
| 258 | } |
||
| 259 | |||
| 260 | /**
|
||
| 261 | * Creates a single fixture table and loads data into it.
|
||
| 262 | *
|
||
| 263 | * @param string $name of the fixture
|
||
| 264 | * @param DataSource $db DataSource instance or leave null to get DataSource from the fixture
|
||
| 265 | * @param bool $dropTables Whether or not tables should be dropped and re-created.
|
||
| 266 | * @return void
|
||
| 267 | * @throws UnexpectedValueException if $name is not a previously loaded class
|
||
| 268 | */
|
||
| 269 | public function loadSingle($name, $db = null, $dropTables = true) { |
||
| 270 | $name .= 'Fixture'; |
||
| 271 | if (isset($this->_fixtureMap[$name])) { |
||
| 272 | $fixture = $this->_fixtureMap[$name]; |
||
| 273 | if (!$db) { |
||
| 274 | $db = ConnectionManager::getDataSource($fixture->useDbConfig); |
||
| 275 | } |
||
| 276 | $this->_setupTable($fixture, $db, $dropTables); |
||
| 277 | $fixture->truncate($db); |
||
| 278 | $fixture->insert($db); |
||
| 279 | } else {
|
||
| 280 | throw new UnexpectedValueException(__d('cake_dev', 'Referenced fixture class %s not found', $name)); |
||
| 281 | } |
||
| 282 | } |
||
| 283 | |||
| 284 | /**
|
||
| 285 | * Drop all fixture tables loaded by this class
|
||
| 286 | *
|
||
| 287 | * This will also close the session, as failing to do so will cause
|
||
| 288 | * fatal errors with database sessions.
|
||
| 289 | *
|
||
| 290 | * @return void
|
||
| 291 | */
|
||
| 292 | public function shutDown() { |
||
| 293 | if (session_id()) {
|
||
| 294 | session_write_close(); |
||
| 295 | } |
||
| 296 | foreach ($this->_loaded as $fixture) { |
||
| 297 | if (!empty($fixture->created)) { |
||
| 298 | foreach ($fixture->created as $ds) { |
||
| 299 | $db = ConnectionManager::getDataSource($ds); |
||
| 300 | $fixture->drop($db); |
||
| 301 | } |
||
| 302 | } |
||
| 303 | } |
||
| 304 | } |
||
| 305 | |||
| 306 | } |