CakeTestFixture.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. <?php
  2. /**
  3. * CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
  4. * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * Redistributions of files must retain the above copyright notice
  8. *
  9. * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  10. * @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
  11. * @package Cake.TestSuite.Fixture
  12. * @since CakePHP(tm) v 1.2.0.4667
  13. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  14. */
  15. App::uses('CakeSchema', 'Model');
  16. /**
  17. * CakeTestFixture is responsible for building and destroying tables to be used
  18. * during testing.
  19. *
  20. * @package Cake.TestSuite.Fixture
  21. */
  22. class CakeTestFixture {
  23. /**
  24. * Name of the object
  25. *
  26. * @var string
  27. */
  28. public $name = null;
  29. /**
  30. * Cake's DBO driver (e.g: DboMysql).
  31. *
  32. * @var object
  33. */
  34. public $db = null;
  35. /**
  36. * Fixture Datasource
  37. *
  38. * @var string
  39. */
  40. public $useDbConfig = 'test';
  41. /**
  42. * Full Table Name
  43. *
  44. * @var string
  45. */
  46. public $table = null;
  47. /**
  48. * List of datasources where this fixture has been created
  49. *
  50. * @var array
  51. */
  52. public $created = array();
  53. /**
  54. * Fields / Schema for the fixture.
  55. * This array should match the output of Model::schema()
  56. *
  57. * @var array
  58. */
  59. public $fields = array();
  60. /**
  61. * Fixture records to be inserted.
  62. *
  63. * @var array
  64. */
  65. public $records = array();
  66. /**
  67. * The primary key for the table this fixture represents.
  68. *
  69. * @var string
  70. */
  71. public $primaryKey = null;
  72. /**
  73. * Instantiate the fixture.
  74. *
  75. * @throws CakeException on invalid datasource usage.
  76. */
  77. public function __construct() {
  78. if ($this->name === null) {
  79. if (preg_match('/^(.*)Fixture$/', get_class($this), $matches)) {
  80. $this->name = $matches[1];
  81. } else {
  82. $this->name = get_class($this);
  83. }
  84. }
  85. $connection = 'test';
  86. if (!empty($this->useDbConfig)) {
  87. $connection = $this->useDbConfig;
  88. if (strpos($connection, 'test') !== 0) {
  89. $message = __d(
  90. 'cake_dev',
  91. 'Invalid datasource name "%s" for "%s" fixture. Fixture datasource names must begin with "test".',
  92. $connection,
  93. $this->name
  94. );
  95. throw new CakeException($message);
  96. }
  97. }
  98. $this->Schema = new CakeSchema(array('name' => 'TestSuite', 'connection' => $connection));
  99. $this->init();
  100. }
  101. /**
  102. * Initialize the fixture.
  103. *
  104. * @return void
  105. * @throws MissingModelException Whe importing from a model that does not exist.
  106. */
  107. public function init() {
  108. if (isset($this->import) && (is_string($this->import) || is_array($this->import))) {
  109. $import = array_merge(
  110. array('connection' => 'default', 'records' => false),
  111. is_array($this->import) ? $this->import : array('model' => $this->import)
  112. );
  113. $this->Schema->connection = $import['connection'];
  114. if (isset($import['model'])) {
  115. list($plugin, $modelClass) = pluginSplit($import['model'], true);
  116. App::uses($modelClass, $plugin . 'Model');
  117. if (!class_exists($modelClass)) {
  118. throw new MissingModelException(array('class' => $modelClass));
  119. }
  120. $model = new $modelClass(null, null, $import['connection']);
  121. $db = $model->getDataSource();
  122. if (empty($model->tablePrefix)) {
  123. $model->tablePrefix = $db->config['prefix'];
  124. }
  125. $this->fields = $model->schema(true);
  126. $this->fields[$model->primaryKey]['key'] = 'primary';
  127. $this->table = $db->fullTableName($model, false, false);
  128. $this->primaryKey = $model->primaryKey;
  129. ClassRegistry::config(array('ds' => 'test'));
  130. ClassRegistry::flush();
  131. } elseif (isset($import['table'])) {
  132. $model = new Model(null, $import['table'], $import['connection']);
  133. $db = ConnectionManager::getDataSource($import['connection']);
  134. $db->cacheSources = false;
  135. $model->useDbConfig = $import['connection'];
  136. $model->name = Inflector::camelize(Inflector::singularize($import['table']));
  137. $model->table = $import['table'];
  138. $model->tablePrefix = $db->config['prefix'];
  139. $this->fields = $model->schema(true);
  140. $this->primaryKey = $model->primaryKey;
  141. ClassRegistry::flush();
  142. }
  143. if (!empty($db->config['prefix']) && strpos($this->table, $db->config['prefix']) === 0) {
  144. $this->table = str_replace($db->config['prefix'], '', $this->table);
  145. }
  146. if (isset($import['records']) && $import['records'] !== false && isset($model) && isset($db)) {
  147. $this->records = array();
  148. $query = array(
  149. 'fields' => $db->fields($model, null, array_keys($this->fields)),
  150. 'table' => $db->fullTableName($model),
  151. 'alias' => $model->alias,
  152. 'conditions' => array(),
  153. 'order' => null,
  154. 'limit' => null,
  155. 'group' => null
  156. );
  157. $records = $db->fetchAll($db->buildStatement($query, $model), false, $model->alias);
  158. if ($records !== false && !empty($records)) {
  159. $this->records = Hash::extract($records, '{n}.' . $model->alias);
  160. }
  161. }
  162. }
  163. if (!isset($this->table)) {
  164. $this->table = Inflector::underscore(Inflector::pluralize($this->name));
  165. }
  166. if (!isset($this->primaryKey) && isset($this->fields['id'])) {
  167. $this->primaryKey = 'id';
  168. }
  169. }
  170. /**
  171. * Run before all tests execute, should return SQL statement to create table for this fixture could be executed successfully.
  172. *
  173. * @param DboSource $db An instance of the database object used to create the fixture table
  174. * @return boolean True on success, false on failure
  175. */
  176. public function create($db) {
  177. if (!isset($this->fields) || empty($this->fields)) {
  178. return false;
  179. }
  180. if (empty($this->fields['tableParameters']['engine'])) {
  181. $canUseMemory = true;
  182. foreach ($this->fields as $args) {
  183. if (is_string($args)) {
  184. $type = $args;
  185. } elseif (!empty($args['type'])) {
  186. $type = $args['type'];
  187. } else {
  188. continue;
  189. }
  190. if (in_array($type, array('blob', 'text', 'binary'))) {
  191. $canUseMemory = false;
  192. break;
  193. }
  194. }
  195. if ($canUseMemory) {
  196. $this->fields['tableParameters']['engine'] = 'MEMORY';
  197. }
  198. }
  199. $this->Schema->build(array($this->table => $this->fields));
  200. try {
  201. $db->execute($db->createSchema($this->Schema), array('log' => false));
  202. $this->created[] = $db->configKeyName;
  203. } catch (Exception $e) {
  204. $msg = __d(
  205. 'cake_dev',
  206. 'Fixture creation for "%s" failed "%s"',
  207. $this->table,
  208. $e->getMessage()
  209. );
  210. CakeLog::error($msg);
  211. trigger_error($msg, E_USER_WARNING);
  212. return false;
  213. }
  214. return true;
  215. }
  216. /**
  217. * Run after all tests executed, should return SQL statement to drop table for this fixture.
  218. *
  219. * @param DboSource $db An instance of the database object used to create the fixture table
  220. * @return boolean True on success, false on failure
  221. */
  222. public function drop($db) {
  223. if (empty($this->fields)) {
  224. return false;
  225. }
  226. $this->Schema->build(array($this->table => $this->fields));
  227. try {
  228. $db->execute($db->dropSchema($this->Schema), array('log' => false));
  229. $this->created = array_diff($this->created, array($db->configKeyName));
  230. } catch (Exception $e) {
  231. return false;
  232. }
  233. return true;
  234. }
  235. /**
  236. * Run before each tests is executed, should return a set of SQL statements to insert records for the table
  237. * of this fixture could be executed successfully.
  238. *
  239. * @param DboSource $db An instance of the database into which the records will be inserted
  240. * @return boolean on success or if there are no records to insert, or false on failure
  241. */
  242. public function insert($db) {
  243. if (!isset($this->_insert)) {
  244. $values = array();
  245. if (isset($this->records) && !empty($this->records)) {
  246. $fields = array();
  247. foreach ($this->records as $record) {
  248. $fields = array_merge($fields, array_keys(array_intersect_key($record, $this->fields)));
  249. }
  250. $fields = array_unique($fields);
  251. $default = array_fill_keys($fields, null);
  252. foreach ($this->records as $record) {
  253. $fields = array_keys($record);
  254. $values[] = array_values(array_merge($default, $record));
  255. }
  256. $nested = $db->useNestedTransactions;
  257. $db->useNestedTransactions = false;
  258. $result = $db->insertMulti($this->table, $fields, $values);
  259. if ($this->primaryKey && in_array($this->fields[$this->primaryKey]['type'], array('integer', 'biginteger'))) {
  260. $db->resetSequence($this->table, $this->primaryKey);
  261. }
  262. $db->useNestedTransactions = $nested;
  263. return $result;
  264. }
  265. return true;
  266. }
  267. }
  268. /**
  269. * Truncates the current fixture. Can be overwritten by classes extending
  270. * CakeFixture to trigger other events before / after truncate.
  271. *
  272. * @param DboSource $db A reference to a db instance
  273. * @return boolean
  274. */
  275. public function truncate($db) {
  276. $fullDebug = $db->fullDebug;
  277. $db->fullDebug = false;
  278. $return = $db->truncate($this->table);
  279. $db->fullDebug = $fullDebug;
  280. return $return;
  281. }
  282. }