<?php
/**
 * core.framework
 *
 * @category   Core
 * @package    Core_Loader
 * @subpackage UnitTests
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 */

/**
 * @category   Core
 * @package    Core_Loader
 * @subpackage UnitTests
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 * @group      Core_Loader
 *
 * @runTestsInSeparateProcesses
 */
class Core_LoaderTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var Core_Loader
     */
    protected $_object;
    protected $_includePath;

    public function setUp()
    {
        $this->_object      = new Core_Loader;
        $this->_includePath = get_include_path();

        set_include_path(TESTS_ROOT .'/resources/fixtures/Core/Loader' .PATH_SEPARATOR. get_include_path());
    }

    public function tearDown()
    {
        set_include_path($this->_includePath);
    }

    /**
     * @covers Core_Loader::registerBasicAutoloader
     */
    public function testCanRegisterAnAutoloadCallback()
    {
        // first, unregister existing autoload functions
        $callbacks = spl_autoload_functions();
        foreach ($callbacks as $callback) {
            spl_autoload_unregister($callback);
        }
        // and verify there are no more autoloaders
        $autoloadFunctions = spl_autoload_functions();

        // register our autoloader
        $this->_object->registerBasicAutoloader();

        // verify that the object is really registered
        $autoloaderRegistered = false;
        foreach (spl_autoload_functions() as $callback) {
            if ($callback[0] == get_class($this->_object)) {
                $autoloaderRegistered = true;
                break;
            }
        }

        // unregister SUT's callback
        foreach (spl_autoload_functions() as $callback) {
            spl_autoload_unregister($callback);
        }

        // re-register old callbacks
        foreach ($callbacks as $callback) {
            spl_autoload_register($callback);
        }

        // and now assert
        $this->assertTrue(empty($autoloadFunctions));
        $this->assertTrue($autoloaderRegistered);
    }

    /**
     * @covers Core_Loader::loadClass
     */
    public function testCanLoadClass()
    {
        $this->_object->loadClass('Invalid_Class');
        $this->assertTrue(class_exists('Invalid_Class', false));
    }

    /**
     * @covers Core_Loader::loadFile
     */
    public function testCanLoadFile()
    {
        $this->_object->loadFile(TESTS_ROOT .'/resources/fixtures/Core/Loader/Invalid/File.php');
        $this->assertTrue(class_exists('Invalid_File', false));
    }

    /**
     * @covers Core_Loader::setClassMap
     * @covers Core_Loader::<protected>
     */
    public function testCanSetClassMapForUnstandardPaths()
    {
        Core_Loader::setClassMap(
            array('Invalid_Weird' => TESTS_ROOT .'/resources/fixtures/Core/Loader/Invalid/WeirdPath.php')
        );
        $this->_object->loadClass('Invalid_Weird');
        $this->assertTrue(class_exists('Invalid_Weird', false));
    }

    /**
     * @covers Core_Loader::setClassMap
     * @covers Core_Loader::<protected>
     */
    public function testSetClassMapWillMergeExistingAndNewClassPath()
    {
        Core_Loader::setClassMap(
            array('Invalid_Weird' => TESTS_ROOT .'/resources/fixtures/Core/Loader/Invalid/WeirdPath.php')
        );
        Core_Loader::setClassMap(
            array('Invalid_StillWeird' => '/not/really/Used.php')
        );
        $this->_object->loadClass('Invalid_Weird');
        $this->assertTrue(class_exists('Invalid_Weird', false));
    }

    /**
     * @covers Core_Loader::loadClass
     * @covers Core_Loader::<protected>
     */
    public function testCanSafelyLoadAnAlreadyLoadedClass()
    {
        $load1 = $this->_object->loadClass('Invalid_Duplicate');
        $load2 = $this->_object->loadClass('Invalid_Duplicate');
        $this->assertTrue($load1);
        $this->assertTrue($load2);
    }

    /**
     * @depends testCanLoadClass
     * @covers Core_Loader::hasClass
     * @covers Core_Loader::<protected>
     */
    public function testCanTestIfAClassCanBeLoaded()
    {
        $this->assertTrue($this->_object->hasClass('Invalid_Class'));
        $this->assertFalse($this->_object->hasClass('Not_Existing_Class'));
    }

    /**
     * @depends testCanLoadClass
     * @covers Core_Loader::loadClass
     * @covers Core_Loader::<protected>
     */
    public function testLoadingAnNonExistingClassReturnsFalse()
    {
        $this->assertFalse($this->_object->loadClass('Not_Existing_Class'));
    }

    /**
     * @covers Core_Loader::loadClass
     * @covers Core_Loader::<protected>
     */
    public function testLoadingAClassNameWithPossibleExploitsInTheFileNameThrowsAnInvalidArgumentException()
    {
        $this->setExpectedException('InvalidArgumentException');
        $this->_object->loadClass('/etc_passwd');
    }

    /**
     * @covers Core_Loader::loadClass
     * @covers Core_Loader::<protected>
     */
    public function testCanLoadNamespacedClass()
    {
        if (-1 === version_compare(phpversion(), '5.3')) {
            $this->markTestSkipped('PHP prior to 5.3.x does not have namespace support');
        }
        $this->_object->loadClass('\Invalid\Namespaced');
        $this->assertTrue(class_exists('\Invalid\Namespaced', false));
    }
}