Commit e1a9e49c authored by Ivan Chepurnyi's avatar Ivan Chepurnyi

+ Shared Fixtures

! Improved performance of EAV and scope fixtures loading
parent f3bb0dac
......@@ -48,6 +48,8 @@ class EcomDev_PHPUnit_Model_App extends Mage_Core_Model_App
const REGISTRY_PATH_LAYOUT_SINGLETON = '_singleton/core/layout';
const REGISTRY_PATH_DESIGN_PACKAGE_SINGLETON = '_singleton/core/design_package';
const REGISTRY_PATH_SHARED_STORAGE = 'test_suite_shared_storage';
const XML_PATH_LAYOUT_MODEL_FOR_TEST = 'phpunit/suite/layout/model';
const XML_PATH_DESIGN_PACKAGE_MODEL_FOR_TEST = 'phpunit/suite/design/package/model';
......@@ -221,6 +223,8 @@ class EcomDev_PHPUnit_Model_App extends Mage_Core_Model_App
$designPackageModel);
$this->loadAreaPart(self::AREA_TEST, self::AREA_PART_EVENTS);
$this->replaceRegistry(self::REGISTRY_PATH_SHARED_STORAGE, new Varien_Object());
return $this;
}
......
......@@ -28,12 +28,18 @@ class EcomDev_PHPUnit_Model_Config extends Mage_Core_Model_Config
const CHANGE_ME = '[change me]';
/**
* Scope snapshot without applied configurations,
* It is used for proper store/website/default loading on per store basis
* Scope snapshot with different levels of saving configuration
*
* @var Mage_Core_Model_Config_Base
*/
protected $_scopeSnapshot = null;
protected $_scopeSnapshot = array();
/**
* Scope snapshot for a particular test case
*
* @var Mage_Core_Model_Config_Base
*/
protected $_localScopeSnapshot = null;
/**
* List of replaced instance creation
......@@ -51,7 +57,7 @@ class EcomDev_PHPUnit_Model_Config extends Mage_Core_Model_Config
{
if ($this->_isLocalConfigLoaded
&& Mage::isInstalled()
&& $this->_scopeSnapshot === null) {
&& empty($this->_scopeSnapshot)) {
$this->saveScopeSnapshot();
}
parent::loadDb();
......@@ -141,19 +147,27 @@ class EcomDev_PHPUnit_Model_Config extends Mage_Core_Model_Config
*/
public function loadScopeSnapshot()
{
if ($this->_scopeSnapshot === null) {
if (empty($this->_scopeSnapshot)) {
throw new RuntimeException('Cannot load scope snapshot, because it was not saved before');
}
$scopeNode = $this->_scopeSnapshot->getNode();
foreach ($scopeNode->children() as $nodeName => $values) {
// Remove somehow modified before xml node
unset($this->getNode()->$nodeName);
// Add saved snapshot of configuration node
$this->getNode()->addChild($nodeName);
$this->getNode()->$nodeName->extend($values);
$scope = clone end($this->_scopeSnapshot);
$this->_xml = $scope;
return $this;
}
/**
* Flushes current scope snapshot level if it is not the last one
*
* @return EcomDev_PHPUnit_Model_Config
*/
public function flushScopeSnapshot()
{
if (count($this->_scopeSnapshot) > 1) {
array_pop($this->_scopeSnapshot);
memory_get_usage(); // Memory GC
}
return $this;
}
......@@ -161,23 +175,11 @@ class EcomDev_PHPUnit_Model_Config extends Mage_Core_Model_Config
* Saves current configuration snapshot,
* for pussible restoring in feature
*
* @param array $nodesToSave list of nodes for saving data, by default it is 'default', 'webistes', 'stores'
* @return EcomDev_PHPUnit_Model_Config
*/
public function saveScopeSnapshot($nodesToSave = array('default', 'websites', 'stores'))
public function saveScopeSnapshot()
{
$this->_scopeSnapshot = clone $this->_prototype;
$this->_scopeSnapshot->loadString('<config />');
$scopeNode = $this->_scopeSnapshot->getNode();
foreach ($nodesToSave as $node) {
$scopeNode->addChild($node);
$scopeNode->{$node}->extend(
$this->getNode($node),
true
);
}
$this->_scopeSnapshot[] = clone $this->_xml;
return $this;
}
......
......@@ -36,9 +36,24 @@ class EcomDev_PHPUnit_Model_Fixture
// Default eav loader class node in loaders configuration
const DEFAULT_EAV_LOADER_NODE = 'default';
// Default shared fixture name
const DEFAULT_SHARED_FIXTURE_NAME = 'default';
// Default eav loader class alias
const DEFAULT_EAV_LOADER_CLASS = 'ecomdev_phpunit/fixture_eav_default';
// Key for storing fixture data into storage
const STORAGE_KEY_FIXTURE = 'fixture';
// Key for loaded tables into database
const STORAGE_KEY_TABLES = 'tables';
// Key for loaded entities by EAV loaders
const STORAGE_KEY_ENTITIES = 'entities';
// Key for created scope models
const STORAGE_KEY_SCOPE = 'scope';
/**
* Fixtures array, contains config,
* table and eav keys.
......@@ -70,6 +85,20 @@ class EcomDev_PHPUnit_Model_Fixture
*/
protected $_fixture = array();
/**
* Storage object, for storing data between tests
*
* @var Varien_Object
*/
protected $_storage = null;
/**
* Scope of the fixture,
* used for different logic depending on
*
* @var string
*/
protected $_scope = self::SCOPE_LOCAL;
/**
* Fixture options
......@@ -78,9 +107,21 @@ class EcomDev_PHPUnit_Model_Fixture
*/
protected $_options = array();
/**
* List of scope model aliases by scope type
*
* @var array
*/
protected static $_scopeModelByType = array(
'store' => 'core/store',
'group' => 'core/store_group',
'website' => 'core/website'
);
/**
* Associative array of configuration nodes xml that was changed by fixture,
* it is used to preserve
* @deprecated since 0.2.1
*
* @var array
*/
......@@ -89,6 +130,7 @@ class EcomDev_PHPUnit_Model_Fixture
/**
* Hash of current scope instances (store, website, group)
*
* @deprecated since 0.2.1
* @return array
*/
protected $_currentScope = array();
......@@ -117,7 +159,112 @@ class EcomDev_PHPUnit_Model_Fixture
}
/**
* Loads fixture from test case annotations
* Sets storage for fixutures
*
* @param Varien_Object $storage
* @return EcomDev_PHPUnit_Model_Fixture
*/
public function setStorage(Varien_Object $storage)
{
$this->_storage = $storage;
return $this;
}
/**
* Retrieve fixture storage
*
* @return Varien_Object
*/
public function getStorage()
{
return $this->_storage;
}
/**
* Retrieves storage data for a particular fixture scope
*
* @param string $key
* @param string|null $scope
* @return mixed
*/
public function getStorageData($key, $scope = null)
{
if ($scope === null) {
$scope = $this->getScope();
}
$dataKey = sprintf('%s_%s', $scope, $key);
return $this->getStorage()->getData($dataKey);
}
/**
* Sets storage data for a particular fixture scope
*
* @param string $key
* @param mixed $value
* @param string|null $scope
* @return EcomDev_PHPUnit_Model_Fixture
*/
public function setStorageData($key, $value, $scope = null)
{
if ($scope === null) {
$scope = $this->getScope();
}
$dataKey = sprintf('%s_%s', $scope, $key);
$this->getStorage()->setData($dataKey, $value);
return $this;
}
/**
* Returns current fixture scope
*
* @return string
*/
public function getScope()
{
return $this->_scope;
}
/**
* Sets current fixture scope
*
* @param string $scope EcomDev_PHPUnit_Model_Fixture_Interface::SCOPE_LOCAL|EcomDev_PHPUnit_Model_Fixture_Interface::SCOPE_SHARED
* @return EcomDev_PHPUnit_Model_Fixture
*/
public function setScope($scope)
{
$this->_scope = $scope;
return $this;
}
/**
* Check that current fixture scope is equal to SCOPE_SHARED
*
* @return boolean
*/
public function isScopeShared()
{
return $this->getScope() === self::SCOPE_SHARED;
}
/**
* Check that current fixture scope is equal to SCOPE_LOCAL
*
* @return boolean
*/
public function isScopeLocal()
{
return $this->getScope() === self::SCOPE_LOCAL;
}
/**
* Loads fixture files from test case annotations
*
* @param EcomDev_PHPUnit_Test_Case $testCase
* @return EcomDev_PHPUnit_Model_Fixture
......@@ -129,12 +276,63 @@ class EcomDev_PHPUnit_Model_Fixture
array('class', 'method')
);
$this->_loadFixtureFiles($fixtures, $testCase);
return $this;
}
/**
* Loads fixture files from test class annotations
*
* @param string $className
* @return EcomDev_PHPUnit_Model_Fixture
*/
public function loadForClass($className)
{
$reflection = EcomDev_Utils_Reflection::getRelflection($className);
$method = $reflection->getMethod('getAnnotationByNameFromClass');
if (!$method instanceof ReflectionMethod) {
throw new RuntimeException('Unable to read class annotations, because it is not extended from EcomDev_PHPUnit_Test_Case');
}
$fixtures = $method->invokeArgs(
null, array($className, 'loadSharedFixture', 'class')
);
$this->_loadFixtureFiles($fixtures, $className);
return $this;
}
/**
* Loads fixture files
*
* @param string $fixtures
* @param string|EcomDev_PHPUnit_Test_Case $classOrInstance
* @return EcomDev_PHPUnit_Model_Fixture
*/
protected function _loadFixtureFiles(array $fixtures, $classOrInstance)
{
$isShared = ($this->isScopeShared() || !$classOrInstance instanceof EcomDev_PHPUnit_Test_Case);
foreach ($fixtures as $fixture) {
if (empty($fixture)) {
if (empty($fixture) && $isShared) {
$fixture = self::DEFAULT_SHARED_FIXTURE_NAME;
} elseif (empty($fixture)) {
$fixture = null;
}
$filePath = $testCase->getYamlFilePath('fixtures', $fixture);
$filePath = false;
if ($isShared) {
$reflection = EcomDev_Utils_Reflection::getRelflection($classOrInstance);
$method = $reflection->getMethod('getYamlFilePathByClass');
if ($method instanceof ReflectionMethod) {
$filePath = $method->invokeArgs(null, array($classOrInstance, 'fixtures', $fixture));
}
} else {
$filePath = $classOrInstance->getYamlFilePath('fixtures', $fixture);
}
if (!$filePath) {
throw new RuntimeException('Unable to load fixture for test');
......@@ -173,7 +371,9 @@ class EcomDev_PHPUnit_Model_Fixture
*/
public function apply()
{
$this->setStorageData(self::STORAGE_KEY_FIXTURE, $this->_fixture);
$reflection = EcomDev_Utils_Reflection::getRelflection($this);
foreach ($this->_fixture as $part => $data) {
$method = '_apply' . uc_words($part, '', '_');
if ($reflection->hasMethod($method)) {
......@@ -181,6 +381,8 @@ class EcomDev_PHPUnit_Model_Fixture
}
}
// Clear fixture for getting rid of duoble processing
$this->_fixture = array();
return $this;
}
......@@ -191,6 +393,14 @@ class EcomDev_PHPUnit_Model_Fixture
*/
public function discard()
{
$fixture = $this->getStorageData(self::STORAGE_KEY_FIXTURE);
if (!is_array($fixture)) {
$fixture = array();
}
$this->_fixture = $fixture;
$this->setStorageData(self::STORAGE_KEY_FIXTURE, null);
$reflection = EcomDev_Utils_Reflection::getRelflection($this);
foreach ($this->_fixture as $part => $data) {
$method = '_discard' . uc_words($part, '', '_');
......@@ -213,12 +423,14 @@ class EcomDev_PHPUnit_Model_Fixture
if (!is_array($configuration)) {
throw new InvalidArgumentException('Configuration part should be an associative list');
}
Mage::getConfig()->loadScopeSnapshot();
foreach ($configuration as $path => $value) {
$this->_setConfigNodeValue($path, $value);
}
Mage::getConfig()->loadDb();
Mage::app()->reinitStores();
return $this;
}
......@@ -250,7 +462,6 @@ class EcomDev_PHPUnit_Model_Fixture
throw new InvalidArgumentException('Configuration value should be a valid xml string');
}
$this->_originalConfigurationXml[$path] = $node->asNiceXml();
$node->extend($xmlElement, true);
}
......@@ -258,15 +469,25 @@ class EcomDev_PHPUnit_Model_Fixture
}
/**
* Reverts fixture configuration values in Mage_Core_Model_Config
* Restores config to a previous configuration scope
*
* @return EcomDev_PHPUnit_Model_Fixture
*/
protected function _discardConfig()
protected function _restoreConfig()
{
Mage::getConfig()->loadScopeSnapshot();
Mage::getConfig()->loadDb();
Mage::app()->reinitStores();
return $this;
}
/**
* Reverts fixture configuration values in Mage_Core_Model_Config
*
* @return EcomDev_PHPUnit_Model_Fixture
*/
protected function _discardConfig()
{
$this->_restoreConfig();
return $this;
}
......@@ -277,15 +498,9 @@ class EcomDev_PHPUnit_Model_Fixture
*/
protected function _discardConfigXml()
{
foreach ($this->_originalConfigurationXml as $path => $value) {
$node = Mage::getConfig()->getNode($path);
$parentNode = $node->getParent();
unset($parentNode->{$node->getName()});
$oldXml = new Varien_Simplexml_Element($value);
$parentNode->appendChild($oldXml);
if (!isset($this->_fixture['config'])) {
$this->_resetConfig();
}
$this->_originalConfigurationXml = array();
return $this;
}
......@@ -303,12 +518,24 @@ class EcomDev_PHPUnit_Model_Fixture
);
}
$ignoreCleanUp = array();
// Ignore cleaning of tables if shared fixture loaded something
if ($this->isScopeLocal() && $this->getStorageData(self::STORAGE_KEY_TABLES, self::SCOPE_SHARED)) {
$ignoreCleanUp = array_keys($this->getStorageData(self::STORAGE_KEY_TABLES, self::SCOPE_SHARED));
}
foreach ($tables as $tableEntity => $data) {
if (!in_array($tableEntity, $ignoreCleanUp)) {
$this->getResource()->cleanTable($tableEntity);
}
if (!empty($data)) {
$this->getResource()->loadTableData($tableEntity, $data);
}
}
$this->setStorageData(self::STORAGE_KEY_TABLES, $tables);
}
/**
......@@ -325,11 +552,24 @@ class EcomDev_PHPUnit_Model_Fixture
);
}
foreach ($tables as $tableEntity => $data) {
$restoreTableData = array();
// Data for tables used in shared fixture
if ($this->isScopeLocal() && $this->getStorageData(self::STORAGE_KEY_TABLES, self::SCOPE_SHARED)) {
$restoreTableData = $this->getStorageData(self::STORAGE_KEY_TABLES, self::SCOPE_SHARED);
}
foreach (array_keys($tables) as $tableEntity) {
$this->getResource()->cleanTable($tableEntity);
if (isset($restoreTableData[$tableEntity])) {
$this->getResource()->loadTableData($tableEntity, $restoreTableData[$tableEntity]);
}
}
$this->setStorageData(self::STORAGE_KEY_TABLES, null);
}
/**
* Setting config value with applying the values to stores and websites
*
......@@ -357,7 +597,7 @@ class EcomDev_PHPUnit_Model_Fixture
EcomDev_Utils_Reflection::setRestrictedPropertyValue(
$website, '_configCache', array()
);
// Should change value
default:
Mage::getConfig()->setNode($path, $value);
......@@ -402,10 +642,13 @@ class EcomDev_PHPUnit_Model_Fixture
foreach ($entities as $entityType => $values) {
$this->_getEavLoader($entityType)
->setFixture($this)
->setOptions($this->_options)
->loadEntity($entityType, $values);
}
$this->setStorageData(self::STORAGE_KEY_ENTITIES, array_keys($entities));
return $this;
}
......@@ -417,7 +660,17 @@ class EcomDev_PHPUnit_Model_Fixture
*/
protected function _discardEav($entities)
{
$ignoreCleanUp = array();
// Ignore cleaning of entities if shared fixture loaded something for them
if ($this->isScopeLocal() && $this->getStorageData(self::STORAGE_KEY_ENTITIES, self::SCOPE_SHARED)) {
$ignoreCleanUp = $this->getStorageData(self::STORAGE_KEY_ENTITIES, self::SCOPE_SHARED);
}
foreach (array_keys($entities) as $entityType) {
if (in_array($entityType, $ignoreCleanUp)) {
continue;
}
$this->_getEavLoader($entityType)
->cleanEntity($entityType);
}
......@@ -434,38 +687,99 @@ class EcomDev_PHPUnit_Model_Fixture
*/
protected function _applyScope($types)
{
$modelByType = array(
'store' => 'core/store',
'group' => 'core/store_group',
'website' => 'core/website'
);
Mage::app()->disableEvents();
// Validate received fixture data
$this->_validateScope($types);
foreach ($types as $type => $rows) {
if (!isset($modelByType[$type])) {
throw new RuntimeException(sprintf('Unknown "%s" scope type specified', $type));
if ($this->getStorageData(self::STORAGE_KEY_SCOPE) !== null) {
throw new RuntimeException('Scope data was not cleared after previous test');
}
$scopeModels = array();
foreach ($types as $type => $rows) {
foreach ($rows as $row) {
$scopeModel = Mage::getModel($modelByType[$type]);
$this->_currentScope[$type][] = $scopeModel;
$model = $this->_handleScopeRow($type, $row);
if ($model) {
$scopeModels[$type][$model->getId()] = $model;
}
}
}
$this->setStorageData(self::STORAGE_KEY_SCOPE, $scopeModels);
Mage::app()->enableEvents();
Mage::app()->reinitStores();
return $this;
}
/**
* Handle scope row data
*
* @param string $type
* @param array $row
* @return boolean|Mage_Core_Model_Abstract
*/
protected function _handleScopeRow($type, $row)
{
$previousScope = array();
if ($this->isScopeLocal() && $this->getStorageData(self::STORAGE_KEY_SCOPE, self::SCOPE_SHARED) !== null) {
$previousScope = $this->getStorageData(self::STORAGE_KEY_SCOPE, self::SCOPE_SHARED);
}
if (isset($previousScope[$type][$row[$type . '_id']])) {
return false;
}
$scopeModel = Mage::getModel(self::$_scopeModelByType[$type]);
$scopeModel->setData($row);
// Change property for saving new objects with specified ids
EcomDev_Utils_Reflection::setRestrictedPropertyValue(
$scopeModel->getResource(), '_useIsObjectNew', true
EcomDev_Utils_Reflection::setRestrictedPropertyValues(
$scopeModel->getResource(),
array(
'_useIsObjectNew' => true,
'_isPkAutoIncrement' => false
)
);
$scopeModel->isObjectNew(true);
$scopeModel->save();
// Revert changed property
EcomDev_Utils_Reflection::setRestrictedPropertyValue(
$scopeModel->getResource(), '_useIsObjectNew', false
EcomDev_Utils_Reflection::setRestrictedPropertyValues(
$scopeModel->getResource(),
array(
'_useIsObjectNew' => false,
'_isPkAutoIncrement' => true
)
);
return $scopeModel;
}
/**
* Validate scope data
*
* @param array $types
* @param array $modelByType
* @return EcomDev_PHPUnit_Model_Fixture
*/
protected function _validateScope($types)
{
foreach ($types as $type => $rows) {
if (!isset(self::$_scopeModelByType[$type])) {
throw new RuntimeException(sprintf('Unknown "%s" scope type specified', $type));
}
Mage::app()->enableEvents();
Mage::app()->reinitStores();
foreach ($rows as $rowNumber => $row) {
$scopeModel = Mage::getModel($modelByType[$type]);
if (!isset($row[$type . '_id'])) {
throw new RuntimeException(sprintf('Missing primary key for "%s" scope entity at #%d row', $type, $rowNumber + 1));
}
}
}
return $this;
}
......@@ -477,19 +791,29 @@ class EcomDev_PHPUnit_Model_Fixture
*/
protected function _discardScope()
{
if ($this->getStorageData(self::STORAGE_KEY_SCOPE) === null) {
return $this;
}
Mage::app()->disableEvents();
$scope = array_reverse($this->_currentScope);
$scope = array_reverse($this->getStorageData(self::STORAGE_KEY_SCOPE));
foreach ($scope as $models) {
foreach ($models as $model) {
$model->delete();
}
}
$this->_currentScope = array();
$this->setStorageData(self::STORAGE_KEY_SCOPE, null);
Mage::app()->getCache()->clean(
Zend_Cache::CLEANING_MODE_MATCHING_TAG,
array(Mage_Core_Model_Mysql4_Collection_Abstract::CACHE_TAG)
Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG,
array(
Mage_Core_Model_Store::CACHE_TAG,
Mage_Core_Model_Store_Group::CACHE_TAG,
Mage_Core_Model_Website::CACHE_TAG
)
);
Mage::app()->enableEvents();
Mage::app()->reinitStores();
return $this;
......
......@@ -25,6 +25,9 @@
*/
interface EcomDev_PHPUnit_Model_Fixture_Interface extends EcomDev_PHPUnit_Model_Test_Loadable_Interface
{
const SCOPE_LOCAL = 'local';
const SCOPE_SHARED = 'shared';
/**
* Sets fixture options
*
......@@ -32,4 +35,74 @@ interface EcomDev_PHPUnit_Model_Fixture_Interface extends EcomDev_PHPUnit_Model_
* @return EcomDev_PHPUnit_Model_Fixture_Interface
*/
public function setOptions(array $options);
/**
* Sets storage for fixutures
*
* @param Varien_Object $storage
* @return EcomDev_PHPUnit_Model_Fixture_Interface
*/
public function setStorage(Varien_Object $storage);
/**
* Retrieve fixture storage
*
* @return Varien_Object
*/
public function getStorage();
/**
* Retrieves storage data for a particular fixture scope
*
* @param string $key
* @param string|null $scope
*/
public function getStorageData($key, $scope = null);
/**
* Sets storage data for a particular fixture scope
*
* @param string $key
* @param mixed $value
* @param string|null $scope
*/
public function setStorageData($key, $value, $scope = null);
/**
* Returns current fixture scope
*
* @return string
*/
public function getScope();
/**
* Sets current fixture scope
*
*
* @param string $scope EcomDev_PHPUnit_Model_Fixture_Interface::SCOPE_LOCAL|EcomDev_PHPUnit_Model_Fixture_Interface::SCOPE_SHARED
*/
public function setScope($scope);
/**
* Check that current fixture scope is equal to SCOPE_SHARED
*
* @return boolean
*/
public function isScopeShared();
/**
* Check that current fixture scope is equal to SCOPE_LOCAL
*
* @return boolean
*/
public function isScopeLocal();
/**
* Loads fixture files from test class annotations
*
* @param string $className
* @return EcomDev_PHPUnit_Model_Fixture_Interface
*/
public function loadForClass($className);
}
\ No newline at end of file
......@@ -58,7 +58,7 @@ class EcomDev_PHPUnit_Model_Mysql4_Fixture extends Mage_Core_Model_Mysql4_Abstra
$records[] = $this->_getTableRecord($row, $tableColumns);
}
$this->_getWriteAdapter()->insertMultiple(
$this->_getWriteAdapter()->insertOnDuplicate(
$this->getTable($tableEntity),
$records
);
......
......@@ -36,6 +36,13 @@ abstract class EcomDev_PHPUnit_Model_Mysql4_Fixture_Eav_Abstract extends EcomDev
*/
protected $_options = array();
/**
* Fixture model
*
* @var EcomDev_PHPUnit_Model_Fixture_Interface
*/
protected $_fixture = null;
/**
* Retrieve required indexers for re-building
*
......@@ -46,6 +53,17 @@ abstract class EcomDev_PHPUnit_Model_Mysql4_Fixture_Eav_Abstract extends EcomDev
return $this->_requiredIndexers;
}
/**
* Sets fixture model to EAV loader
*
* @param EcomDev_PHPUnit_Model_Fixture_Interface $fixture
*/
public function setFixture($fixture)
{
$this->_fixture = $fixture;
return $this;
}
/**
* Set fixture options
*
......@@ -95,8 +113,19 @@ abstract class EcomDev_PHPUnit_Model_Mysql4_Fixture_Eav_Abstract extends EcomDev
*/
public function loadEntity($entityType, $values)
{
$originalRequiredIndexers = $this->_requiredIndexers;
if (!empty($this->_options['addRequiredIndex'])) {
foreach ($this->_options['addRequiredIndex'] as $data) {
if (preg_match('/^([a-z0-9_\\-])+\\s+([a-z0-9_\\-])\s*$/i', $data, $match)
&& $match[1] == $entityType) {
$this->_requiredIndexers[] = $match[2];
}
}
}
$entityTypeModel = Mage::getSingleton('eav/config')->getEntityType($entityType);
$entityTableColumns = $this->_getWriteAdapter()->describeTable(
$this->getTable($entityTypeModel->getEntityTable())
);
......@@ -197,6 +226,8 @@ abstract class EcomDev_PHPUnit_Model_Mysql4_Fixture_Eav_Abstract extends EcomDev
}
}
// Restoring original required indexers for making tests isolated
$this->_requiredIndexers = $originalRequiredIndexers;
return $this;
}
......
......@@ -25,7 +25,6 @@ class EcomDev_PHPUnit_Model_Mysql4_Fixture_Eav_Catalog_Product extends EcomDev_P
{
protected $_requiredIndexers = array(
'cataloginventory_stock',
'catalog_product_flat',
'catalog_product_attribute',
'catalog_product_price'
);
......
......@@ -271,12 +271,28 @@ abstract class EcomDev_PHPUnit_Test_Case extends PHPUnit_Framework_TestCase
* @return array
*/
public function getAnnotationByName($name, $sources = 'method')
{
return self::getAnnotationByNameFromClass(get_class($this), $name, $sources, $this->getName(false));
}
/**
* Retrieves annotation by its name from different sources (class, method) based on meta information
*
* @param string $className
* @param string $name annotation name
* @param array|string $sources
* @param string $testName test method name
*/
public static function getAnnotationByNameFromClass($className, $name, $sources = 'class', $testName = '')
{
if (is_string($sources)) {
$sources = array($sources);
}
$allAnnotations = $this->getAnnotations();
$allAnnotations = PHPUnit_Util_Test::parseTestMethodAnnotations(
$className, $testName
);
$annotation = array();
// Walkthrough sources for annotation retrieval
......@@ -627,12 +643,30 @@ abstract class EcomDev_PHPUnit_Test_Case extends PHPUnit_Framework_TestCase
*
* @return EcomDev_PHPUnit_Model_Fixture
*/
protected function getFixture()
protected static function getFixture()
{
return Mage::getSingleton($this->getLoadableClassAlias(
$fixture = Mage::getSingleton(
self::getLoadableClassAlias(
'fixture',
self::XML_PATH_DEFAULT_FIXTURE_MODEL
));;
)
);
if (!$fixture instanceof EcomDev_PHPUnit_Model_Fixture_Interface) {
throw new RuntimeException('Fixture model should implement EcomDev_PHPUnit_Model_Fixture_Interface interface');
}
$storage = Mage::registry(EcomDev_PHPUnit_Model_App::REGISTRY_PATH_SHARED_STORAGE);
if (!$storage instanceof Varien_Object) {
throw new RuntimeException('Fixture storage object was not initialized during test application setup');
}
$fixture->setStorage(
Mage::registry(EcomDev_PHPUnit_Model_App::REGISTRY_PATH_SHARED_STORAGE)
);
return $fixture;
}
/**
......@@ -642,10 +676,12 @@ abstract class EcomDev_PHPUnit_Test_Case extends PHPUnit_Framework_TestCase
*/
protected function getExpectation()
{
return Mage::getSingleton($this->getLoadableClassAlias(
return Mage::getSingleton(
self::getLoadableClassAlias(
'expectation',
self::XML_PATH_DEFAULT_EXPECTATION_MODEL
));
)
);
}
......@@ -656,14 +692,17 @@ abstract class EcomDev_PHPUnit_Test_Case extends PHPUnit_Framework_TestCase
* @param string $type
* @param string $configPath
*/
protected function getLoadableClassAlias($type, $configPath)
protected static function getLoadableClassAlias($type, $configPath)
{
$annotationValue = $this->getAnnotationByName($type .'Model' , 'class');
$annotationValue = self::getAnnotationByNameFromClass(
get_called_class(),
$type .'Model'
);
if (current($annotationValue)) {
$classAlias = current($annotationValue);
} else {
$classAlias = $this->app()->getConfig()->getNode($configPath);
$classAlias = self::app()->getConfig()->getNode($configPath);
}
return $classAlias;
......@@ -699,12 +738,25 @@ abstract class EcomDev_PHPUnit_Test_Case extends PHPUnit_Framework_TestCase
$name = $this->getName(false);
}
return self::getYamlFilePathByClass(get_called_class(), $type, $name);
}
/**
* Loads YAML file from directory inside of the unit test class
*
* @param string $className class name for looking fixture files
* @param string $type type of YAML data (fixtures,expectations,dataproviders)
* @param string $name the file name for loading
* @return string|boolean
*/
public static function getYamlFilePathByClass($className, $type, $name)
{
if (strrpos($name, '.yaml') !== strlen($name) - 5) {
$name .= '.yaml';
}
$classFileObject = new SplFileInfo(
EcomDev_Utils_Reflection::getRelflection($this)->getFileName()
EcomDev_Utils_Reflection::getRelflection($className)->getFileName()
);
$filePath = $classFileObject->getPath() . DS
......@@ -726,15 +778,38 @@ abstract class EcomDev_PHPUnit_Test_Case extends PHPUnit_Framework_TestCase
*/
protected function setUp()
{
$this->getFixture()->loadByTestCase($this);
self::getFixture()
->setScope(EcomDev_PHPUnit_Model_Fixture_Interface::SCOPE_LOCAL)
->loadByTestCase($this);
$annotations = $this->getAnnotations();
$this->getFixture()->setOptions($annotations['method']);
$this->getFixture()->apply();
self::getFixture()
->setOptions($annotations['method'])
->apply();
$this->app()->resetDispatchedEvents();
parent::setUp();
}
/**
* Initializes test environment for subset of tests
*
*/
public static function setUpBeforeClass()
{
self::getFixture()
->setScope(EcomDev_PHPUnit_Model_Fixture_Interface::SCOPE_SHARED)
->loadForClass(get_called_class());
$annotations = PHPUnit_Util_Test::parseTestMethodAnnotations(
get_called_class()
);
self::getFixture()
->setOptions($annotations['class'])
->apply();
parent::setUpBeforeClass();
}
/**
* Implements default data provider functionality,
* returns array data loaded from Yaml file with the same name as test method
......@@ -796,9 +871,24 @@ abstract class EcomDev_PHPUnit_Test_Case extends PHPUnit_Framework_TestCase
$this->app()->replaceRegistry($registryPath, $originalValue);
}
$this->getFixture()->discard(); // Clear applied fixture
self::getFixture()
->setScope(EcomDev_PHPUnit_Model_Fixture_Interface::SCOPE_LOCAL)
->discard(); // Clear applied fixture
parent::tearDown();
}
/**
* Clean up all the shared fixture data
*
* @return void
*/
public static function tearDownAfterClass()
{
self::getFixture()
->setScope(EcomDev_PHPUnit_Model_Fixture_Interface::SCOPE_SHARED)
->discard();
parent::tearDownAfterClass();
}
}
\ No newline at end of file
......@@ -44,6 +44,23 @@ class EcomDev_Utils_Reflection
$reflectionProperty->setValue((is_string($object) ? null : $object), $value);
}
/**
* Sets multiple restricted property values for an object
*
* @param string|object $object class name
* @param array $properties
*/
public static function setRestrictedPropertyValues($object, array $properties)
{
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
throw new RuntimeException('For setting of restricted properties via Reflection, PHP version should be 5.3.0 or later');
}
foreach ($properties as $property => $value) {
self::setRestrictedPropertyValue($object, $property, $value);
}
}
/**
* Gets protected or private property value
*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment