<?php /** * PHP Unit test suite for Magento * * NOTICE OF LICENSE * * This source file is subject to the Open Software License (OSL 3.0) * that is bundled with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://opensource.org/licenses/osl-3.0.php * * @category EcomDev * @package EcomDev_PHPUnit * @copyright Copyright (c) 2013 EcomDev BV (http://www.ecomdev.org) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * @author Ivan Chepurnyi <ivan.chepurnyi@ecomdev.org> */ /** * Configuration model extended to make unit tests to be available * at separate configuration scope * */ class EcomDev_PHPUnit_Model_Config extends Mage_Core_Model_Config { const XML_PATH_SECURE_BASE_URL = 'default/web/secure/base_url'; const XML_PATH_UNSECURE_BASE_URL = 'default/web/unsecure/base_url'; const CHANGE_ME = '[change me]'; /** * Scope snapshot with different levels of saving configuration * * @var Mage_Core_Model_Config_Base */ protected $_scopeSnapshot = array(); /** * Scope snapshot for a particular test case * * @var Mage_Core_Model_Config_Base */ protected $_localScopeSnapshot = null; /** * List of replaced instance creation * * @return array */ protected $_replaceInstanceCreation = array(); /** * No cache sections should be cached, * in favor to get rid of buggy config set options * * @var array */ protected $_cacheSections = array(); /** * Object containing parsed local.xml.phpunit * * @var null */ protected $_localXmlForTest = null; /** * Load config data from DB * * @return Mage_Core_Model_Config */ public function loadDb() { if ($this->_isLocalConfigLoaded && Mage::isInstalled() && empty($this->_scopeSnapshot)) { $this->saveScopeSnapshot(); } parent::loadDb(); return $this; } /** * Get events configuration * * @param string $area event area * @param string $eventName event name * @return Mage_Core_Model_Config_Element */ public function getEventConfig($area, $eventName) { return $this->getNode($area)->events->{$eventName}; } /** * Replaces creation of some instance by mock object * * * @param string $type * @param string $classAlias * @param PHPUnit_Framework_MockObject_MockObject|PHPUnit_Framework_MockObject_MockBuilder $mock * @return EcomDev_PHPUnit_Model_Config */ public function replaceInstanceCreation($type, $classAlias, $mock) { $this->_replaceInstanceCreation[$type][$classAlias] = $mock; return $this; } /** * Flushes instance creation instruction list * * @return EcomDev_PHPUnit_Model_Config */ public function flushReplaceInstanceCreation() { $this->_replaceInstanceCreation = array(); return $this; } /** * Overridden for test case model instance creation mocking * * @see Mage_Core_Model_Config::getModelInstance() */ public function getModelInstance($modelClass='', $constructArguments=array()) { if (!isset($this->_replaceInstanceCreation['model'][(string)$modelClass])) { return parent::getModelInstance((string)$modelClass, $constructArguments); } return $this->_replaceInstanceCreation['model'][(string)$modelClass]; } /** * Overridden for test case model instance creation mocking * * (non-PHPdoc) * @see Mage_Core_Model_Config::getModelInstance() */ public function getResourceModelInstance($modelClass='', $constructArguments=array()) { if (!isset($this->_replaceInstanceCreation['resource_model'][$modelClass])) { return parent::getResourceModelInstance($modelClass, $constructArguments); } return $this->_replaceInstanceCreation['resource_model'][$modelClass]; } /** * Retrieves real resource model class alias * * @param string $classAlias * @return string */ public function getRealResourceModelClassAlias($classAlias) { list($classAliasPrefix,) = explode('/', $classAlias, 2); if (isset($this->_xml->global->models->$classAliasPrefix->resourceModel)) { $realClassAliasPrefix = $this->_xml->global->models->$classAliasPrefix->resourceModel; $classAlias = $realClassAliasPrefix . substr( $classAlias, strlen($classAliasPrefix) ); } return $classAlias; } /** * Loads scope snapshot * * @throws RuntimeException * @return EcomDev_PHPUnit_Model_Config */ public function loadScopeSnapshot() { if (empty($this->_scopeSnapshot)) { throw new RuntimeException('Cannot load scope snapshot, because it was not saved before'); } $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; } /** * Saves current configuration snapshot, * for pussible restoring in feature * * @return EcomDev_PHPUnit_Model_Config */ public function saveScopeSnapshot() { $this->_scopeSnapshot[] = clone $this->_xml; return $this; } /** * Loads additional configuration for unit tests * (non-PHPdoc) * @see Mage_Core_Model_Config::loadBase() */ public function loadBase() { parent::loadBase(); $this->_loadTestCacheConfig(); return $this; } /** * Define if module is allowed * * Magento core allows use of a whitelist of modules supplied via the * addAllowedModules method. EcomDev_PHPUnit extends this to allow a * blacklist of modules to be supplied via local.xml.phpunit. * * @see Mage_Core_Model_Config::_isAllowedModule() * @param string $moduleName * @return bool */ protected function _isAllowedModule($moduleName) { if (!parent::_isAllowedModule($moduleName)) { return false; } $localXml = $this->_loadLocalXmlForTest(); if ($localXml) { $node = $localXml->getNode("phpunit/disable_modules/$moduleName"); return $node === false; } return true; } /** * (non-PHPdoc) * @see Mage_Core_Model_Config::loadModules() */ public function loadModules() { parent::loadModules(); $this->_loadTestConfig(); $this->_loadTestCacheConfig(); return $this; } /** * Loads local.xml.phpunit file * for overriding DB credentials * * @return EcomDev_PHPUnit_Model_Config */ protected function _loadTestConfig() { try { if ($merge = $this->_loadLocalXmlForTest()) { $this->_checkDbCredentialForDuplicate($this, $merge); $this->_checkBaseUrl($this, $merge); $this->extend($merge); } else { throw new RuntimeException('Unable to load local.xml.phpunit. Please run ecomdev-phpunit.php with install action.'); } } catch (RuntimeException $e) { echo $e->getMessage() . "\n"; exit(1); } return $this; } /** * Parse the phpunit specific local configuration. This may be loaded by * and used by _isAllowedModule before it's merged into the merged config. * * @return Mage_Core_Model_Config_Base|null */ protected function _loadLocalXmlForTest() { if ($this->_localXmlForTest === null) { $this->_localXmlForTest = clone $this->_prototype; $this->_localXmlForTest->loadFile($this->_getLocalXmlForTest()); } return $this->_localXmlForTest; } /** * Loads cache configuration for PHPUnit tests scope * * @return EcomDev_PHPUnit_Model_Config */ protected function _loadTestCacheConfig() { // Cache beckend initialization for unit tests, // because it should be separate from live one $this->setNode('global/cache/backend', ''); $this->getOptions()->setData('cache_dir', $this->getVarDir() . DS . 'phpunit.cache'); $this->getOptions()->setData('session_dir', $this->getVarDir() . DS . 'phpunit.session'); return $this; } /** * Checks DB credentials for phpunit test case. * They shouldn't be the same as live ones. * * @param Mage_Core_Model_Config_Base $original * @param Mage_Core_Model_Config_Base $test * @return EcomDev_PHPUnit_Model_Config * @throws RuntimeException */ protected function _checkDbCredentialForDuplicate($original, $test) { $originalDbName = (string) $original->getNode('global/resources/default_setup/connection/dbname'); $testDbName = (string) $test->getNode('global/resources/default_setup/connection/dbname'); if ($originalDbName == $testDbName && (string)$test->getNode('phpunit/allow_same_db') !== '1') { throw new RuntimeException('Test DB cannot be the same as the live one. ' . 'You can change this option by running ecomdev-phpunit.php with' . ' magento-config action.'); } return $this; } /** * Check base url settings, if not set it rises an exception * * @param Mage_Core_Model_Config_Base $original * @param Mage_Core_Model_Config_Base $test * @return EcomDev_PHPUnit_Model_Config * @throws RuntimeException */ protected function _checkBaseUrl($original, $test) { $baseUrlSecure = (string)$test->getNode(self::XML_PATH_SECURE_BASE_URL); $baseUrlUnsecure = (string)$test->getNode(self::XML_PATH_UNSECURE_BASE_URL); if (empty($baseUrlSecure) || empty($baseUrlUnsecure) || $baseUrlSecure == self::CHANGE_ME || $baseUrlUnsecure == self::CHANGE_ME) { throw new RuntimeException('The base url is not set for proper controller tests. ' . 'Please run ecomdev-phpunit.php with magento-config action.'); } } /** * Retrieves local.xml file path for tests, * If it is not found, method will rise an exception * * @return string * @throws RuntimeException */ protected function _getLocalXmlForTest() { $filePath = $this->getOptions()->getEtcDir() . DS . 'local.xml.phpunit'; if (!file_exists($filePath)) { throw new RuntimeException('There is no local.xml.phpunit file. ' . 'Try running ecomdev-phpunit.php with install action.'); } return $filePath; } }