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

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

    protected function setUp()
    {
        $this->_object = $this->getMockForAbstractClass('Core_Router_Route_Abstract');
    }


    /**
     * @covers Core_Router_Route_Abstract::setRequest
     * @covers Core_Router_Route_Abstract::getRequest
     */
    public function testCanSetAndGetRequest()
    {
        $request = $this->getMock('Core_Request');
        $this->_object->setRequest($request);
        $this->assertSame($request, $this->_object->getRequest());
    }

    /**
     * @covers Core_Router_Route_Abstract::getRequest
     */
    public function testIfNoRequestIsSetGetRequestWillPullItFromApplicationInstanceAsRequest()
    {
        $request   = $this->getMock('Core_Request');
        $container = $this->_mockContainer('Request', $request);
        Core_Application::getInstance()->setContainer($container);

        $this->assertSame($request, $this->_object->getRequest());
    }


    /**
     * @covers Core_Router_Route_Abstract::setRouter
     * @covers Core_Router_Route_Abstract::getRouter
     */
    public function testCanSetAndGetRouter()
    {
        $router = $this->getMock('Core_Router');
        $this->_object->setRouter($router);
        $this->assertEquals($router, $this->_object->getRouter());
    }

    /**
     * @covers Core_Router_Route_Abstract::setName
     * @covers Core_Router_Route_Abstract::getName
     */
    public function testCanSetAndGetName()
    {
        $this->_object->setName('someName');
        $this->assertEquals('someName', $this->_object->getName());
    }

    /**
     * @covers Core_Router_Route_Abstract::setBase
     * @covers Core_Router_Route_Abstract::getBase
     */
    public function testSetAndGetBaseRoute()
    {
        $this->_object->setBase('/some/base/route');
        $this->assertEquals('/some/base/route', $this->_object->getBase());
    }

    /**
     * @covers Core_Router_Route_Abstract::getBase
     */
    public function testIfBaseNotSetWillPullBaseFromRouter()
    {
        $router = $this->getMock('Core_Router');
        $router
            ->expects($this->once())
            ->method('getBase')
            ->will($this->returnValue('/some/base/route'));
        $this->_object->setRouter($router);

        $this->assertEquals('/some/base/route', $this->_object->getBase());
    }


    /**
     * @covers Core_Router_Route_Abstract::getBase
     */
    public function testIfNoBaseAndRouterSetBaseIsNull()
    {
        $this->assertNull($this->_object->getBase());
    }

    /**
     * @covers Core_Router_Route_Abstract::getName
     */
    public function testIfGettingNameAndNoNameIsSetWillReturnClassName()
    {
        $object = $this->getMockForAbstractClass('Core_Router_Route_Abstract');

        // cannot use this as we're creating a mock (so, end type differs)
        // class is named like Mock_Core_Router_Route_Abstract_523ab70b
        // $this->assertEquals('Core_Router_Route_Abstract', $object->getName());

        // we'll assert that the class name contains our desired class name instead
        $this->assertContains('Core_Router_Route_Abstract', $object->getName());
    }

    /**
     * @covers Core_Router_Route_Abstract::__construct
     * @covers Core_Router_Route_Abstract<extended>
     */
    public function testCanNameViaConstructor()
    {
        $object = $this->getMockForAbstractClass('Core_Router_Route_Abstract', array('unusual'));
        $this->assertEquals('unusual', $object->getName());
    }

    /**
     * @covers Core_Router_Route_Abstract::setPattern
     * @covers Core_Router_Route_Abstract::getPattern
     */
    public function testCanSetAndGetPattern()
    {
        $this->_object->setPattern('/some/:route');
        $this->assertEquals('/some/:route', $this->_object->getPattern());
    }

    /**
     * @covers Core_Router_Route_Abstract::setMethod
     * @covers Core_Router_Route_Abstract::getMethod
     */
    public function testCanSetAndGetMethod()
    {
        $this->_object->setMethod('POST');
        $this->assertEquals('POST', $this->_object->getMethod());
    }

    /**
     * @covers Core_Router_Route_Abstract::setHostname
     * @covers Core_Router_Route_Abstract::getHostname
     */
    public function testCanSetAndGetHostname()
    {
        $this->_object->setHostname('example.com');
        $this->assertEquals('example.com', $this->_object->getHostname());
    }

    /**
     * @covers Core_Router_Route_Abstract::setOptions
     */
    public function testCanSetOptions()
    {
        $options = array(
            'pattern'   => '/foo/:bar',
        );
        $this->_object->setOptions($options);
        $this->assertEquals('/foo/:bar', $this->_object->getPattern());
    }

    /**
     * @covers Core_Router_Route_Abstract::__construct
     */
    public function testCanSetOptionsViaConstructor()
    {
        $options = array(
            'pattern'   => '/foo/:bar',
        );
        $object = $this->getMockForAbstractClass('Core_Router_Route_Abstract', array('name', $options));
        $this->assertEquals('/foo/:bar', $object->getPattern());
    }

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

    /**
     * @covers Core_Router_Route_Abstract::setDefaults
     * @covers Core_Router_Route_Abstract::setDefault
     * @covers Core_Router_Route_Abstract::getDefaults
     */
    public function testCanSetAndGetDefaults()
    {
        $this->_object->setDefault('bla', 99);
        $this->_object->setDefaults(array('abc' => 23, 'bla' => 1));

        $this->assertEquals(array('bla' => 1, 'abc' => 23), $this->_object->getDefaults());
    }

    /**
     * @covers Core_Router_Route_Abstract::setDefaults
     */
    public function testSettingDefaultsAddsToCurrentDefaults()
    {
        $this->_object->setDefaults(array('foo' => 'bar'));
        $this->_object->setDefaults(array('bar' => 'baz'));

        $this->assertEquals(array('foo' => 'bar', 'bar' => 'baz'), $this->_object->getDefaults());
    }

    /**
     * @covers Core_Router_Route_Abstract::getDefaults
     */
    public function testGetDefaultsWillMergeRouterDefaultsIfSet()
    {
        $router = $this->getMock('Core_Router');
        $router
            ->expects($this->once())
            ->method('getDefaults')
            ->will($this->returnValue(array('some' => 'defaults', 'value' => 'router')));
        $this->_object
            ->setRouter($router)
            ->setDefaults(array('bla' => 23, 'value' => 'route'));
        
        $defaults = $this->_object->getDefaults();
        $this->assertEquals(array('some' => 'defaults', 'bla' => 23, 'value' => 'route'), $defaults);
                
    }

    /**
     * @covers Core_Router_Route_Abstract::getValues
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testGetValuesWillMergeCurrentValuesAndDefaults()
    {
        $this->_object->setDefault('bla', 99);
        $this->assertEquals(array('bla' => 99), $this->_object->getValues());
    }

    /**
     * @covers Core_Router_Route_Abstract::setValue
     * @depends testGetValuesWillMergeCurrentValuesAndDefaults
     */
    public function testCanSetValue()
    {
        $this->_object->setValue('foo', 23);
        $this->assertArrayHasKey('foo', $this->_object->getValues());
    }

    /**
     * @covers Core_Router_Route_Abstract::addValues
     * @depends testCanSetValue
     */
    public function testCanAddValues()
    {
        $this->_object->addValues(array('foo' => 23, 'bar' => 24));
        $this->assertArrayHasKey('foo', $this->_object->getValues());
        $this->assertArrayHasKey('bar', $this->_object->getValues());
    }

    /**
     * @covers Core_Router_Route_Abstract::setPluginLoader
     * @covers Core_Router_Route_Abstract::getPluginLoader
     */
    public function testCanSetAndGetPluginLoader()
    {
        $pluginLoader = $this->getMock('Core_Loader_PluginLoader');
        $this->_object->setPluginLoader($pluginLoader);
        $this->assertSame($pluginLoader, $this->_object->getPluginLoader());
    }

    /**
     * @covers Core_Router_Route_Abstract::getPluginLoader
     */
    public function testIfNoRequestIsSetGetPluginLoaderWillPullItFromApplicationInstanceAsPluginLoader()
    {
        $pluginLoader = $this->getMock('Core_Loader_PluginLoader');
        $container    = $this->_mockContainer('PluginLoader', $pluginLoader);
        Core_Application::getInstance()->setContainer($container);
        $this->assertSame($pluginLoader, $this->_object->getPluginLoader());
    }

    /**
     * @covers Core_Router_Route_Abstract::addPlugin
     * @covers Core_Router_Route_Abstract::hasPlugin
     */
    public function testCanAddPlugin()
    {
        $plugin = $this->_mockPlugin('routePlugin');
        $this->_object->addPlugin($plugin);
        $this->assertTrue($this->_object->hasPlugin('routePlugin'));
    }

    /**
     * @covers Core_Router_Route_Abstract::addPlugin
     * @depends testCanAddPlugin
     */
    public function testWillSetRouteWhenAddingPlugin()
    {
        $plugin = $this->_mockPlugin('routePlugin', array('setRoute'));
        $plugin
            ->expects($this->once())
            ->method('setRoute')
            ->with($this->equalTo($this->_object));

        $this->_object->addPlugin($plugin);
    }

    /**
     * @covers Core_Router_Route_Abstract::getPlugins
     */
    public function testCanGetPlugins()
    {
        $plugin = $this->_mockPlugin('pluginName');
        $this->_object->addPlugin($plugin);
        $this->assertContains($plugin, $this->_object->getPlugins());
    }

    /**
     * @covers Core_Router_Route_Abstract::getPlugin
     */
    public function testCanGetPluginByName()
    {
        $plugin = $this->_mockPlugin('pluginName');
        $this->_object->addPlugin($plugin);
        $this->assertEquals($plugin, $this->_object->getPlugin('pluginname'));
    }

    /**
     * @covers Core_Router_Route_Abstract::getPlugin
     */
    public function testGettingUnknownPluginThrowsAnInvalidArgumentException()
    {
        $this->setExpectedException('InvalidArgumentException');
        $this->_object->getPlugin('noSuchPlugin');
    }

    /**
     * @covers Core_Router_Route_Abstract::createPlugin
     */
    public function testCanCreatePlugin()
    {
        $plugin = $this->_mockPlugin('routePlugin');
        $this->_object->setPluginLoader($this->_mockPluginLoader('Abstract', null, $plugin));

        $this->_object->createPlugin('Abstract', 'routePlugin', array('opt' => 'ions'));
        $this->assertTrue($this->_object->hasPlugin('routePlugin'));
    }

    /**
     * @covers Core_Router_Route_Abstract::addPlugins
     * @depends testCanAddPlugin
     */
    public function testCanAddArrayOfPluginInstancesAndSpecs()
    {
        $plugin       = $this->_mockPlugin('routePlugin2');
        $pluginLoader = $this->_mockPluginLoader(
            'Abstract', array('name' => 'routePlugin2', 'options' => array('opt' => 'ions')), $plugin
        );
        $this->_object->setPluginLoader($pluginLoader);

        $plugins = array(
            $this->_mockPlugin('routePlugin1'),
            'routePlugin2' => array(
                'type'    => 'Abstract',
                'options' => array(
                    'opt'     => 'ions',
                )
            )
        );
        $this->_object->addPlugins($plugins);

        $this->assertTrue($this->_object->hasPlugin('routePlugin1'));
        $this->assertTrue($this->_object->hasPlugin('routePlugin2'));
    }

    /**
     * @covers Core_Router_Route_Abstract::setOptions
     */
    public function testCanAddPluginsViaOptions()
    {
        $plugin = $this->_mockPlugin('routePlugin');
        $this->_object->setPluginLoader($this->_mockPluginLoader('Abstract', null, $plugin));

        $options = array(
            'plugins' => array(
                'routePlugin1' => array(
                    'type' => 'Abstract',
                )
            )
        );
        $this->_object->setOptions($options);
        $this->assertTrue($this->_object->hasPlugin('routePlugin'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     */
    public function testCanMatchPathWithRouteTypeSpecificMethod()
    {
        $object = $this->_mockSelf(array('_match'));
        $object
            ->expects($this->once())
            ->method('_match')
            ->with($this->equalTo('/some/path'))
            ->will($this->returnValue(true));

        $this->assertTrue($object->match('/some/path', '/'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     */
    public function testWillNotMatchIfRequestMethodDoesNotMatchRouteMethodIfSet()
    {
        $this->_object
            ->setPattern('/foo')
            ->setMethod('GET');

        // requested path matches, but the request method does not
        $this->assertFalse($this->_object->match('/foo', '/', 'POST'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testWillNotMatchIfCurrentHostnameDoesNotMatchRequestedRouteHostname()
    {
        $this->_object
            ->setPattern('/foo')
            ->setHostname('example.com');

        // requested path matches, but the request hostname
        $this->assertFalse($this->_object->match('/foo', '/', 'GET', 'http://foo.com'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testWillMatchHostnameWithoutScheme()
    {
        $object = $this->_mockSelf(array('_match'));
        $object
            ->setHostname('example.com')

            ->expects($this->once())
            ->method('_match')
            ->with($this->equalTo('/foo'))
            ->will($this->returnValue(true));

        // requested path matches, but the request hostname
        $this->assertTrue($object->match('/foo', '/', null, 'http://example.com'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testWillMatchHostnameWithCorrectScheme()
    {
        $object = $this->_mockSelf(array('_match'));
        $object
            ->setHostname('http://example.com')

            ->expects($this->once())
            ->method('_match')
            ->with($this->equalTo('/foo'))
            ->will($this->returnValue(true));

        // requested path matches, but the request hostname
        $this->assertTrue($object->match('/foo', '/', null, 'http://example.com'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testWillMatchHostnameWithCurrentScheme()
    {
        $object = $this->_mockSelf(array('_match'));
        $object
            ->setHostname('//example.com')

            ->expects($this->once())
            ->method('_match')
            ->with($this->equalTo('/foo'))
            ->will($this->returnValue(true));

        // requested path matches, but the request hostname
        $this->assertTrue($object->match('/foo', '/', null, 'http://example.com'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testWillNotMatchHostnameWithWrongScheme()
    {
        $object = $this->_mockSelf(array('_match'));
        $object
            ->setHostname('https://example.com')

            ->expects($this->never())
            ->method('_match');

        // requested path matches, but the request hostname
        $this->assertFalse($object->match('/foo', '/', null, 'http://example.com'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testWillMatchHostnameWithSamePort()
    {
        $object = $this->_mockSelf(array('_match'));
        $object
            ->setHostname('https://example.com:999')

            ->expects($this->once())
            ->method('_match')
            ->with($this->equalTo('/foo'))
            ->will($this->returnValue(true));

        // requested path matches, but the request hostname
        $this->assertTrue($object->match('/foo', '/', null, 'https://example.com:999'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testWillNotMatchHostnameWithDifferentPort()
    {
        $this->_object->setHostname('https://example.com:999');

        // requested path matches, but the request hostname
        $this->assertFalse($this->_object->match('/foo', '/', null, 'https://example.com:998'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testWillNotMatchHostnameByPassingNonFullyQualifiedHostName()
    {
        /* @var $object Core_Router_Route_Abstract */
        $object = $this->_mockSelf(array('_match'));
        $object
            ->setHostname('right.com')

            ->expects($this->never())
            ->method('_match');

        $this->assertFalse($object->match('/foo', '/', null, 'wrong.com'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testWillMatchHostnameWithCurrentSchemeByPassingNonFullyQualifiedHostName()
    {
        /* @var $object Core_Router_Route_Abstract */
        $object = $this->_mockSelf(array('_match'));
        $object
            ->setHostname('http://nonFQHN.com')

            ->expects($this->once())
            ->method('_match')
            ->with($this->equalTo('/foo'))
            ->will($this->returnValue(true));

        $this->assertTrue($object->match('/foo', '/', null, 'nonFQHN.com'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testWillMatchHostnameWithCurrentSchemeAndPortByPassingNonFullyQualifiedHostName()
    {
        /* @var $object Core_Router_Route_Abstract */
        $object = $this->_mockSelf(array('_match'));
        $object
            ->setHostname('https://nonFQHN.com:999')

            ->expects($this->once())
            ->method('_match')
            ->with($this->equalTo('/foo'))
            ->will($this->returnValue(true));

        $this->assertTrue($object->match('/foo', '/', null, 'nonFQHN.com'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testWillMatchHostnameByPassingNonFullyQualifiedHostName()
    {
        /* @var $object Core_Router_Route_Abstract */
        $object = $this->_mockSelf(array('_match'));
        $object
            ->setHostname('nonFQDN.com')

            ->expects($this->once())
            ->method('_match')
            ->with($this->equalTo('/foo'))
            ->will($this->returnValue(true));

        $this->assertTrue($object->match('/foo', '/', null, 'nonFQDN.com'));
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     */
    public function testWillMatchAgainstAttachedPluginsWithRouteValues()
    {
        $object = $this->_mockSelf(array('_match', 'getValues'));
        $object
            ->expects($this->once())
            ->method('_match')
            ->will($this->returnValue(true));
        $object
            ->expects($this->once())
            ->method('getValues')
            ->will($this->returnValue(array('some' => 'values')));

        $plugin = $this->_mockPlugin('routePlugin', array('match'));
        $plugin
            ->expects($this->once())
            ->method('match')
            ->with($this->equalTo(array('some' => 'values')));
        $object->addPlugin($plugin);

        $object->match('/foo/bar', '/');
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     * @covers Core_Router_Route_Abstract::<protected>
     */
    public function testWillInjectRequestedHeadersFromRequest()
    {
        $request = $this->getMock('Core_Request', array('getFoo', 'getHeader'));
        $request
            ->expects($this->once())
            ->method('getFoo')
            ->will($this->returnValue('aaa'));
        $request
            ->expects($this->once())
            ->method('getHeader')
            ->with($this->equalTo('Bar'))
            ->will($this->returnValue('bbb'));

        $plugin = $this->_mockPlugin('routePlugin', array('match', 'getHeadersRequest'));
        $plugin
            ->expects($this->once())
            ->method('getHeadersRequest')
            ->will($this->returnValue(array('Foo', 'Bar')));
        $plugin
            ->expects($this->once())
            ->method('match')
            ->with($this->equalTo(array('some' => 'values')), $this->equalTo(array('Foo' => 'aaa', 'Bar' => 'bbb')));

        $object = $this->_mockSelf(array('_match', 'getValues'));
        $object
            ->expects($this->once())
            ->method('_match')
            ->will($this->returnValue(true));
        $object
            ->expects($this->once())
            ->method('getValues')
            ->will($this->returnValue(array('some' => 'values')));

        $object
            ->setRequest($request)
            ->addPlugin($plugin);

        $object->match('/foo/bar', '/');
    }

    /**
     * @covers Core_Router_Route_Abstract::url
     */
    public function testCanAssembleUrl()
    {
        $object = $this->_mockSelf(array('path'));
        $object
            ->setHostname('http://example.com')
            ->expects($this->once())
            ->method('path')
            ->will($this->returnValue('/foo/bar'));

        $this->assertEquals('http://example.com/foo/bar', $object->url('/'));
    }

    /**
     * @covers Core_Router_Route_Abstract::url
     */
    public function testIfNoHostnameIsSetUrlWillUseDefaultRouterHostname()
    {
        $router = $this->getMock('Core_Router', array('getDefaultHostname'));
        $router
            ->expects($this->once())
            ->method('getDefaultHostname')
            ->will($this->returnValue('http://example.com'));
        $object = $this->_mockSelf(array('path'));
        $object
            ->setRouter($router)

            ->expects($this->once())
            ->method('path')
            ->will($this->returnValue('/foo/bar'));

        $this->assertEquals('http://example.com/foo/bar', $object->url('/'));
    }

    /**
     * @covers Core_Router_Route_Abstract::setUseQuery
     * @covers Core_Router_Route_Abstract::isUseQuery
     */
    public function testCanSetAndGetUseQuery()
    {
        $this->_object->setUseQuery(true);
        $this->assertTrue($this->_object->isUseQuery());
    }

    /**
     * @covers Core_Router_Route_Abstract::isUseQuery
     */
    public function testWillNotUseQueryByDefault()
    {
        $this->assertFalse($this->_object->isUseQuery());
    }

    /**
     * @covers Core_Router_Route_Abstract::match
     */
    public function testWillAddQueryVariablesToRouteIfUseQueryIsEnabled()
    {
        // values pulled from request
        $values  = array('param1' => 'a', 'param2' => 'b');

        $request = $this->getMock('Core_Request');
        $request
            ->expects($this->once())
            ->method('getQuery')
            ->will($this->returnValue($values));

        $object = $this->_mockSelf(array('_match'));
        $object
            ->setUseQuery(true)
            ->setRequest($request);
        $object->match('/foo', '/');

        $this->assertEquals($values, $object->getValues());
    }


    /**
     * @covers Core_Router_Route_Abstract::match
     */
    public function testValuesFromPathWillNotBeOverwrittenByValuesFromQuery()
    {
        // values pulled from request
        $request = $this->getMock('Core_Request');
        $request
            ->expects($this->once())
            ->method('getQuery')
            ->will($this->returnValue(array('param1' => 'a', 'param2' => 'b')));

        $object = $this->_mockSelf(array('_match'));
        $object
            ->setUseQuery(true)
            ->setRequest($request)
            ->addValues(array('param1' => 'c')); // this value was set from route path
        $object
            ->expects($this->once())
            ->method('_match')
            ->will($this->returnValue(true));
        $object->match('/foo', '/');

        $this->assertEquals(array('param1' => 'c', 'param2' => 'b'), $object->getValues());
    }

    protected function _mockSelf($methods = array())
    {
        $object = $this->getMockForAbstractClass('Core_Router_Route_Abstract', array(), '', true, true, true, $methods);

        return $object;
    }

    protected function _mockPlugin($name, $methods = array())
    {
        $methods[] = 'getName';
        $class     = 'Core_Router_Route_Plugin_Abstract';
        $plugin    = $this->getMockForAbstractClass($class, array(), '', true, true, true, $methods);
        $plugin
            ->expects($this->once())
            ->method('getName')
            ->will($this->returnValue($name));

        return $plugin;
    }

    protected function _mockContainer($name, $value)
    {
        // mock the container
        $container = $this->getMock('Core_Yadif_Container', array('getComponent'));
        $container
            ->expects($this->once())
            ->method('getComponent')
            ->with($this->equalTo($name))
            ->will($this->returnValue($value));

        return $container;
    }

    protected function _mockPluginLoader($type = null, $params = null, $plugin = null)
    {
        $options = array('initializeRouterRoutePluginPlugin');
        $pluginLoader = $this->getMock('Core_Loader_PluginLoader', $options);
        if (null !== $type) {
            $mocker = $pluginLoader
                ->expects($this->once())
                ->method(current($options));

            if (null !== $params) {
                if (is_string($params)) {
                    // $params is the name alone
                    $mocker
                        ->with($this->equalTo($type), $this->equalTo(array('name' => $params)));
                } else if (is_array($params)) {
                    // params should atleast have a name
                    if (!array_key_exists('name', $params)) {
                        // but it does not
                        throw new InvalidArgumentException('Mocking plugin loader without "name" specified in params');
                    }

                    // options are... well, optional
                    if (!array_key_exists('options', $params)) {
                        $mocker
                            ->with($this->equalTo($type), $this->equalTo(array('name' => $params['name'])));
                    } else {
                        $options         = $params['options'];
                        $options['name'] = $params['name'];
                        $mocker
                            ->with($this->equalTo($type), $this->equalTo($options));
                    }
                }
            }

            if (null !== $plugin) {
                $mocker
                    ->will($this->returnValue($plugin));
            }
        }
        return $pluginLoader;
    }
}
