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

/**
 * @category   Core
 * @package    Core_Module
 * @subpackage UnitTests
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 * @group      Core_Module
 */
class Core_Module_ResourceFinderTest extends CoreTest_Container_TestCase
{
    /**
     * @var Core_Module_ResourceFinder
     */
    protected $_object;

    protected function setUp()
    {
        $this->_object = new Core_Module_ResourceFinder;
    }

    protected function tearDown()
    {
        Core_Application::resetInstance();
    }

    /**
     * @covers Core_Module_ResourceFinder::setOptions
     */
    public function testCanSetOptions()
    {
        $modulesDir = dirname(__FILE__);
        $options    = array('modulesDir' => $modulesDir);
        $this->_object->setOptions($options);
        $this->assertEquals($modulesDir, $this->_object->getModulesDir());
    }

    /**
     * @covers Core_Module_ResourceFinder::setOptions
     */
    public function testSetOptionsWillSkipForbiddenKeysInOptions()
    {
        $modulesDir = dirname(__FILE__);
        $this->_object->setModulesDir($modulesDir);
        $options    = array('options' => array('modulesDir' => 'aaa'));
        $this->_object->setOptions($options);

        $this->assertEquals($modulesDir, $this->_object->getModulesDir());
    }

    /**
     * @covers Core_Module_ResourceFinder::setConfig
     */
    public function testCanSetConfig()
    {
        $modulesDir = dirname(__FILE__);
        $config     = $this->getMock('Zend_Config', null, array(array('modulesDir' => $modulesDir)));
        $this->_object->setConfig($config);
        $this->assertEquals($modulesDir, $this->_object->getModulesDir());
    }

    /**
     * @covers Core_Module_ResourceFinder::__construct
     */
    public function testCanSetOptionsViaConstructor()
    {
        $modulesDir = dirname(__FILE__);
        $options    = array('modulesDir' => $modulesDir);
        $object     = new Core_Module_ResourceFinder($options);
        $this->assertEquals($modulesDir, $object->getModulesDir());
    }

    /**
     * @covers Core_Module_ResourceFinder::__construct
     */
    public function testCanSetConfigViaConstructor()
    {
        $modulesDir = dirname(__FILE__);
        $config     = $this->getMock('Zend_Config', null, array(array('modulesDir' => $modulesDir)));
        $object     = new Core_Module_ResourceFinder($config);
        $this->assertEquals($modulesDir, $object->getModulesDir());
    }

    /**
     * @covers Core_Module_ResourceFinder::setOptions
     */
    public function testPassingAnUnknownOptionsThrowsAnInvalidArgumentException()
    {
        $this->setExpectedException('InvalidArgumentException');
        $this->_object->setOptions(array('no-such' => 'option'));
    }

    /**
     * @covers Core_Module_ResourceFinder::setPatterns
     * @covers Core_Module_ResourceFinder::getPatterns
     */
    public function testCanSetAndGetPatterns()
    {
        $this->_object->setPatterns(array('a' => 'b'));
        $this->assertArrayHasKey('a', $this->_object->getPatterns());
    }

    /**
     * @covers Core_Module_ResourceFinder::setPattern
     * @covers Core_Module_ResourceFinder::getPattern
     */
    public function testCanSetPattern()
    {
        $this->_object->setPattern('a', 'b');
        $this->assertEquals('b', $this->_object->getPattern('a'));
    }

    /**
     * @covers Core_Module_ResourceFinder::getPattern
     */
    public function testFetchingAnUnknownPatternThrowsAnInvalidArgumentException()
    {
        $this->setExpectedException('InvalidArgumentException');

        $this->assertEquals('b', $this->_object->getPattern('a'));
    }

    /**
     * @covers Core_Module_ResourceFinder::setModulesDir
     * @covers Core_Module_ResourceFinder::getModulesDir
     */
    public function testCanSetAndGetModulesDir()
    {
        $dir = dirname(__FILE__);
        $this->_object->setModulesDir($dir);
        $this->assertEquals($dir, $this->_object->getModulesDir());
    }

    /**
     * @covers Core_Module_ResourceFinder::setAppRootDir
     * @covers Core_Module_ResourceFinder::getAppRootDir
     */
    public function testCanSetAndGetAppRootDir()
    {
        $dir = dirname(__FILE__);
        $this->_object->setAppRootDir($dir);
        $this->assertEquals($dir, $this->_object->getAppRootDir());
    }

    /**
     * @covers Core_Module_ResourceFinder::getAppRootDir
     */
    public function testIfAppRootDirNotSetWillPullFromRootFromApplication()
    {
        Core_Application::getInstance()->setRoot('/my/root');
        $this->assertEquals('/my/root', $this->_object->getAppRootDir());
    }

    /**
     * @covers Core_Module_ResourceFinder::setModulesDir
     */
    public function testWillStripTrailingDirectorySeparatorFromModulesDir()
    {
        $dir = dirname(__FILE__);
        $this->_object->setModulesDir($dir . DIRECTORY_SEPARATOR);
        $this->assertEquals($dir, $this->_object->getModulesDir());
    }

    /**
     * @covers Core_Module_ResourceFinder::setModulesDir
     */
    public function testModulesDirMustBeAValidDirectory()
    {
        $this->setExpectedException('InvalidArgumentException');
        $this->_object->setModulesDir('/some/invalid/dir');
    }

    /**
     * @covers Core_Module_ResourceFinder::setModulesDir
     */
    public function testModulesDirCannotBeAFile()
    {
        $this->setExpectedException('InvalidArgumentException');
        $this->_object->setModulesDir(__FILE__);
    }

    /**
     * @covers Core_Module_ResourceFinder::getModulesDir
     */
    public function testIfNoModulesDirIsSetGetModulesDirWillThrowAnInvalidArgumentException()
    {
        $this->setExpectedException('InvalidArgumentException');
        $this->_object->getModulesDir();
    }

    /**
     * @covers Core_Module_ResourceFinder::getPattern
     * @covers Core_Module_ResourceFinder::setPattern
     * @covers Core_Module_ResourceFinder::__call
     */
    public function testCanSetAndGetModulePathPattern()
    {
        $this->_object->setModulePathPattern('/foo/ha/{raw:module}');
        $this->assertEquals('/foo/ha/{raw:module}', $this->_object->getModulePathPattern());
    }

    /**
     * @covers Core_Module_ResourceFinder::getPattern
     * @covers Core_Module_ResourceFinder::__call
     */
    public function testDefaultModulePathPatternIsDefined()
    {
        $this->assertEquals('{modulesDir|raw}/{module|normalize}', $this->_object->getModulePathPattern());
    }

    /**
     * @covers Core_Module_ResourceFinder::getPattern
     * @covers Core_Module_ResourceFinder::__call
     */
    public function testDefaultControllerPathPatternIsDefined()
    {
        $format = '{modulePath|raw}/{controller}Controller.php';
        $this->assertEquals($format, $this->_object->getControllerPathPattern());
    }

    /**
     * @covers Core_Module_ResourceFinder::getPattern
     * @covers Core_Module_ResourceFinder::__call
     */
    public function testDefaultControllerClassIsDefined()
    {
        $this->assertEquals('{module}_{controller}Controller', $this->_object->getControllerClassPattern());
    }

    /**
     * @covers Core_Module_ResourceFinder::getPattern
     * @covers Core_Module_ResourceFinder::__call
     */
    public function testDefaultActionIsDefined()
    {
        $this->assertEquals('{action}Action', $this->_object->getControllerActionPattern());
    }

    /**
     * @covers Core_Module_ResourceFinder::getPattern
     * @covers Core_Module_ResourceFinder::__call
     */
    public function testDefaultTemplatesPathPatternIsDefined()
    {
        $this->assertEquals('{modulePath|raw}/templates', $this->_object->getTemplatesPathPattern());
    }

    /**
     * @covers Core_Module_ResourceFinder::getPattern
     * @covers Core_Module_ResourceFinder::__call
     */
    public function testDefaultTemplatePatternIsDefined()
    {
        $this->assertEquals('{controller|normalize}/{action|normalize}.tpl', $this->_object->getTemplatePattern());
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testSettingUnparsablePatternWillReturnThePattern()
    {
        $this->_object->setControllerPathPattern('SomethingUnparsable');

        $this->assertEquals('SomethingUnparsable', $this->_object->find('ControllerPath', $this->_mockLocation()));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testSettingAnUnknownFilterWillThrowAnInvalidArgumentException()
    {
        $this->_object->setControllerPathPattern('{controller|unknown}');
        $this->setExpectedException('InvalidArgumentException');

        $this->_object->find('ControllerPath', $this->_mockLocation());
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testCanFindControllerPath()
    {
        $dir      = dirname(__FILE__);
        $this->_object->setModulesDir($dir);
        $location = $this->_mockLocation('some Modul:E', 'foO-bAr a:a');
        $path     = $dir . DIRECTORY_SEPARATOR. 'some-module'. DIRECTORY_SEPARATOR .'FooBarAaController.php';

        $this->assertEquals($path, $this->_object->find('ControllerPath', $location));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testCanFindFormPath()
    {
        $dir      = dirname(__FILE__);
        $this->_object->setModulesDir($dir);
        $location = $this->_mockLocation('some Modul:E', 'articles');
        $path     = implode(DIRECTORY_SEPARATOR, array($dir, 'some-module', 'forms', 'articles', 'name.ini'));

        $this->assertEquals($path, $this->_object->find('Form', $location, array('name' => 'name.ini')));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testCanFindSharedFormPath()
    {
        $dir      = dirname(__FILE__);
        $this->_object->setAppRootDir($dir);
        $location = $this->_mockLocation();
        $path     = implode(DIRECTORY_SEPARATOR, array($dir, 'shared', 'forms', 'name.ini'));

        $this->assertEquals($path, $this->_object->find('SharedForm', $location, array('name' => 'name.ini')));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
    */
    public function testCanFindControllerClass()
    {
        $location = $this->_mockLocation('some-module', 'foO-bAr a:a');

        $this->assertEquals('SomeModule_FooBarAaController', $this->_object->find('ControllerClass', $location));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testCanFindControllerAction()
    {
        $location = $this->_mockLocation(null, null, 'woop-"*+&woop');

        $this->assertEquals('WoopWoopAction', $this->_object->find('controllerAction', $location));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testCanFindTemplate()
    {
        $location = $this->_mockLocation(null, 'foO-bAr a:a', 'woop-"*+&woop');

        $this->assertEquals('foo-bar-aa/woop-woop.tpl', $this->_object->find('template', $location));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testCanFindAngularResource()
    {
        $location = $this->_mockLocation(null, 'foO-bAr a:a', null);
        $params   = array('ngmodule' => 'articles', 'resource' => 'partials/view.html');
        $this->assertEquals('foo-bar-aa/articles/partials/view.html.tpl', $this->_object->find('angularResource', $location, $params));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testCanFindAngularModules()
    {
        $location = $this->_mockLocation(null, 'foO-bAr a:a', null);
        $params   = array('templatesPath' => 'some-weird/path');
        $this->assertEquals('some-weird/path/foo-bar-aa', $this->_object->find('angularModules', $location, $params));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testCanFindAngularControllerSuffix()
    {
        $location = $this->_mockLocation();
        $this->assertEquals('Controller.js.tpl', $this->_object->find('angularControllerSuffix', $location));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testCanFindAngularControllerGlob()
    {
        $location = $this->_mockLocation();
        $params   = array('AngularModules' => 'foo/eee', 'ngmodule' => 'articles');
        $glob     = 'foo/eee/articles/*Controller.js.tpl';
        $this->assertEquals($glob, $this->_object->find('angularControllerGlob', $location, $params));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testCanFindAngularDirectiveSuffix()
    {
        $location = $this->_mockLocation();
        $this->assertEquals('Directive.js.tpl', $this->_object->find('angularDirectiveSuffix', $location));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     * @covers Core_Module_ResourceFinder::<protected>
     */
    public function testCanFindAngularDirectiveGlob()
    {
        $location = $this->_mockLocation();
        $params   = array('AngularModules' => 'foo/iii', 'ngmodule' => 'products');
        $glob     = 'foo/iii/products/*Directive.js.tpl';
        $this->assertEquals($glob, $this->_object->find('angularDirectiveGlob', $location, $params));
    }

    /**
     * @covers Core_Module_ResourceFinder::find
     */
    public function testTryingToFindUnknownResourceTypeWillThrowAnInvalidArgumentException()
    {
        $location = $this->_mockLocation();

        $this->setExpectedException('InvalidArgumentException');
        $this->_object->find('no-such-resource', $location);
    }

    /**
     * @covers Core_Module_ResourceFinder::__call
     */
    public function testWillProxyMethodCallsToFind()
    {
        $location = $this->_mockLocation();

        $object   = $this->getMock(get_class($this->_object), array('find'));
        $object
            ->expects($this->once())
            ->method('find')
            ->with($this->equalTo('template'), $this->equalTo($location))
            ->will($this->returnValue('foo.tpl'));

        $this->assertEquals('foo.tpl', $object->findTemplate($location));
    }

    /**
     * @covers Core_Module_ResourceFinder::__call
     */
    public function testUnknownMethodCallsNotPrefixedWithFindWillThrowAnInvalidArgumentException()
    {
        $this->setExpectedException('BadMethodCallException');

        $this->_object->fetchTemplate();
    }

    protected function _mockLocation($module = null, $controller = null, $action = null)
    {
        $location = $this->getMock('Core_Dispatcher_Location_Interface');
        if ($module) {
            $location
                ->expects($this->once())
                ->method('getModule')
                ->will($this->returnValue($module));
        }
        if ($controller) {
            $location
                ->expects($this->once())
                ->method('getController')
                ->will($this->returnValue($controller));
        }
        if ($action) {
            $location
                ->expects($this->once())
                ->method('getAction')
                ->will($this->returnValue($action));
        }
        return $location;
    }
}