Config.php 11.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
<?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
14
 * @copyright  Copyright (c) 2013 EcomDev BV (http://www.ecomdev.org)
15 16 17 18 19
 * @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 * @author     Ivan Chepurnyi <ivan.chepurnyi@ecomdev.org>
 */

/**
20
 * Configuration model extended to make unit tests to be available
21 22 23 24 25
 * at separate configuration scope
 *
 */
class EcomDev_PHPUnit_Model_Config extends Mage_Core_Model_Config
{
26 27 28 29
    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]';
30
    /**
Ivan Chepurnyi's avatar
Ivan Chepurnyi committed
31
     * Scope snapshot with different levels of saving configuration
32 33 34
     *
     * @var Mage_Core_Model_Config_Base
     */
Ivan Chepurnyi's avatar
Ivan Chepurnyi committed
35 36 37 38 39 40 41 42
    protected $_scopeSnapshot = array();

    /**
     * Scope snapshot for a particular test case
     *
     * @var Mage_Core_Model_Config_Base
     */
    protected $_localScopeSnapshot = null;
43

44 45 46 47 48 49 50
    /**
     * List of replaced instance creation
     *
     * @return array
     */
    protected $_replaceInstanceCreation = array();

51 52 53 54 55 56 57 58
    /**
     * No cache sections should be cached,
     * in favor to get rid of buggy config set options
     *
     * @var array
     */
    protected $_cacheSections = array();

59 60 61 62 63 64 65
    /**
     * Object containing parsed local.xml.phpunit
     *
     * @var null
     */
    protected $_localXmlForTest = null;

66 67 68 69 70 71 72 73 74
	/**
     * Load config data from DB
     *
     * @return Mage_Core_Model_Config
     */
    public function loadDb()
    {
        if ($this->_isLocalConfigLoaded
            && Mage::isInstalled()
Ivan Chepurnyi's avatar
Ivan Chepurnyi committed
75
            && empty($this->_scopeSnapshot)) {
76 77 78 79 80 81
            $this->saveScopeSnapshot();
        }
        parent::loadDb();
        return $this;
    }

82 83 84 85 86 87 88 89 90 91 92 93
    /**
     * 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};
    }

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
    /**
     * 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;
    }

    /**
     * Overriden for test case model instance creation mocking
     *
     * (non-PHPdoc)
     * @see Mage_Core_Model_Config::getModelInstance()
     */
    public function getModelInstance($modelClass='', $constructArguments=array())
    {
        if (!isset($this->_replaceInstanceCreation['model'][$modelClass])) {
129
            return parent::getModelInstance($modelClass, $constructArguments);
130 131 132 133 134 135 136 137 138 139 140 141 142 143
        }

        return $this->_replaceInstanceCreation['model'][$modelClass];
    }

    /**
     * Overriden 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])) {
144
            return parent::getResourceModelInstance($modelClass, $constructArguments);
145 146 147 148 149
        }

        return $this->_replaceInstanceCreation['resource_model'][$modelClass];
    }

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
    /**
     * 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;
    }

170 171 172 173 174 175 176
    /**
     * Loads scope snapshot
     *
     * @return EcomDev_PHPUnit_Model_Config
     */
    public function loadScopeSnapshot()
    {
Ivan Chepurnyi's avatar
Ivan Chepurnyi committed
177
        if (empty($this->_scopeSnapshot)) {
178 179 180
            throw new RuntimeException('Cannot load scope snapshot, because it was not saved before');
        }

Ivan Chepurnyi's avatar
Ivan Chepurnyi committed
181
        $scope = clone end($this->_scopeSnapshot);
182

Ivan Chepurnyi's avatar
Ivan Chepurnyi committed
183
        $this->_xml = $scope;
184 185 186 187
        return $this;
    }

    /**
Ivan Chepurnyi's avatar
Ivan Chepurnyi committed
188
     * Flushes current scope snapshot level if it is not the last one
189 190 191
     *
     * @return EcomDev_PHPUnit_Model_Config
     */
Ivan Chepurnyi's avatar
Ivan Chepurnyi committed
192
    public function flushScopeSnapshot()
193
    {
Ivan Chepurnyi's avatar
Ivan Chepurnyi committed
194 195 196
        if (count($this->_scopeSnapshot) > 1) {
            array_pop($this->_scopeSnapshot);
            memory_get_usage(); // Memory GC
197
        }
Ivan Chepurnyi's avatar
Ivan Chepurnyi committed
198 199
        return $this;
    }
200

Ivan Chepurnyi's avatar
Ivan Chepurnyi committed
201 202 203 204 205 206 207 208 209
    /**
     * Saves current configuration snapshot,
     * for pussible restoring in feature
     *
     * @return EcomDev_PHPUnit_Model_Config
     */
    public function saveScopeSnapshot()
    {
        $this->_scopeSnapshot[] = clone $this->_xml;
210 211 212
        return $this;
    }

213 214 215 216 217 218 219 220 221 222 223 224
    /**
     * Loads additional configuration for unit tests
     * (non-PHPdoc)
     * @see Mage_Core_Model_Config::loadBase()
     */
    public function loadBase()
    {
        parent::loadBase();
        $this->_loadTestCacheConfig();
        return $this;
    }

225 226 227 228 229 230 231 232 233 234 235
    /**
     * 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
     */
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
    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;
    }


252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
    /**
     * (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()
    {
272
        try {
273
            if ($merge = $this->_loadLocalXmlForTest()) {
274 275 276 277 278 279 280 281 282
                $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);
283
        }
284

285 286 287
        return $this;
    }

288
    /**
289
     * Parse the phpunit specific local configuration.  This may be loaded by
290 291 292 293 294 295 296 297 298 299 300 301
     * 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;
    }

302 303 304 305 306 307 308 309 310
    /**
     * 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
Ivan Chepurnyi's avatar
Ivan Chepurnyi committed
311
        $this->setNode('global/cache/backend', '');
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
        $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');

331 332 333 334
        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.');
335 336 337 338
        }
        return $this;
    }

339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
    /**
     * 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) {
354 355
            throw new RuntimeException('The base url is not set for proper controller tests. '
                                        . 'Please run ecomdev-phpunit.php with magento-config action.');
356 357 358
        }
    }

359 360 361 362 363 364 365 366 367 368 369
    /**
     * 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)) {
370 371
            throw new RuntimeException('There is no local.xml.phpunit file. '
                                       . 'Try running ecomdev-phpunit.php with install action.');
372 373 374 375 376
        }

        return $filePath;
    }
}