code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Event\Test\Unit;

use Hoa\Event as LUT;
use Hoa\Event\Event as SUT;
use Hoa\Test;

/**
 * Class \Hoa\Event\Test\Unit\Event.
 *
 * Test suite of the event class.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Event extends Test\Unit\Suite
{
    public function case_multiton()
    {
        $this
            ->given($eventId = 'hoa://Event/Test')
            ->when($result = SUT::getEvent($eventId))
            ->then
                ->object($result)
                    ->isInstanceOf('Hoa\Event\Event')
                ->object(SUT::getEvent($eventId))
                    ->isIdenticalTo($result);
    }

    public function case_register_source_instance()
    {
        $this
            ->given(
                $eventId = 'hoa://Event/Test',
                $source  = new \Mock\Hoa\Event\Source()
            )
            ->when($result = SUT::register($eventId, $source))
            ->then
                ->variable($result)
                    ->isNull()
                ->boolean(SUT::eventExists($eventId))
                    ->isTrue();
    }

    public function case_register_source_name()
    {
        $this
            ->given(
                $eventId = 'hoa://Event/Test',
                $source  = 'Mock\Hoa\Event\Source'
            )
            ->when($result = SUT::register($eventId, $source))
            ->then
                ->variable($result)
                    ->isNull()
                ->boolean(SUT::eventExists($eventId))
                    ->isTrue();
    }

    public function case_register_redeclare()
    {
        $this
            ->given(
                $eventId = 'hoa://Event/Test',
                $source  = new \Mock\Hoa\Event\Source(),
                SUT::register($eventId, $source)
            )
            ->exception(function () use ($eventId, $source) {
                SUT::register($eventId, $source);
            })
                ->isInstanceOf('Hoa\Event\Exception');
    }

    public function case_register_not_a_source_instance()
    {
        $this
            ->given(
                $eventId = 'hoa://Event/Test',
                $source  = new \StdClass()
            )
            ->exception(function () use ($eventId, $source) {
                $result = SUT::register($eventId, $source);
            })
                ->isInstanceOf('Hoa\Event\Exception');
    }

    public function case_register_not_a_source_name()
    {
        $this
            ->given(
                $eventId = 'hoa://Event/Test',
                $source  = 'StdClass'
            )
            ->exception(function () use ($eventId, $source) {
                $result = SUT::register($eventId, $source);
            })
                ->isInstanceOf('Hoa\Event\Exception');
    }

    public function case_unregister()
    {
        $this
            ->given(
                $eventId = 'hoa://Event/Test',
                $source  = new \Mock\Hoa\Event\Source(),
                SUT::register($eventId, $source)
            )
            ->when($result = SUT::unregister($eventId))
            ->then
                ->boolean(SUT::eventExists($eventId))
                    ->isFalse();
    }

    public function case_unregister_hard()
    {
        $this
            ->given(
                $eventId = 'hoa://Event/Test',
                $source  = new \Mock\Hoa\Event\Source(),
                SUT::register($eventId, $source),
                $event = SUT::getEvent($eventId)
            )
            ->when($result = SUT::unregister($eventId, true))
            ->then
                ->boolean(SUT::eventExists($eventId))
                    ->isFalse()
                ->object(SUT::getEvent($eventId))
                    ->isNotIdenticalTo($event);
    }

    public function case_unregister_not_registered()
    {
        $this
            ->given($eventId = 'hoa://Event/Test')
            ->when($result = SUT::unregister($eventId))
            ->then
                ->variable($result)
                    ->isNull();
    }

    public function case_attach()
    {
        $this
            ->given(
                $event    = SUT::getEvent('hoa://Event/Test'),
                $callable = function () { }
            )
            ->when($result = $event->attach($callable))
            ->then
                ->object($result)
                    ->isIdenticalTo($event)
                ->boolean($event->isListened())
                    ->isTrue();
    }

    public function case_detach()
    {
        $this
            ->given(
                $event    = SUT::getEvent('hoa://Event/Test'),
                $callable = function () { },
                $event->attach($callable)
            )
            ->when($result = $event->detach($callable))
            ->then
                ->object($result)
                    ->isIdenticalTo($event)
                ->boolean($event->isListened())
                    ->isFalse();
    }

    public function case_detach_unattached()
    {
        $this
            ->given(
                $event    = SUT::getEvent('hoa://Event/Test'),
                $callable = function () { }
            )
            ->when($result = $event->detach($callable))
            ->then
                ->object($result)
                    ->isIdenticalTo($event)
                ->boolean($event->isListened())
                    ->isFalse();
    }

    public function case_is_listened()
    {
        $this
            ->given($event = SUT::getEvent('hoa://Event/Test'))
            ->when($result = $event->isListened())
            ->then
                ->boolean($event->isListened())
                    ->isFalse();
    }

    public function case_notify()
    {
        $self = $this;

        $this
            ->given(
                $eventId = 'hoa://Event/Test',
                $source  = new \Mock\Hoa\Event\Source(),
                $bucket  = new LUT\Bucket(),

                SUT::register($eventId, $source),
                SUT::getEvent($eventId)->attach(
                    function (LUT\Bucket $receivedBucket) use ($self, $source, $bucket, &$called) {
                        $called = true;

                        $this
                            ->object($receivedBucket)
                                ->isIdenticalTo($bucket)
                            ->object($receivedBucket->getSource())
                                ->isIdenticalTo($source);
                    }
                )
            )
            ->when($result = SUT::notify($eventId, $source, $bucket))
            ->then
                ->variable($result)
                    ->isNull()
                ->boolean($called)
                    ->isTrue();
    }

    public function case_notify_unregistered_event_id()
    {
        $this
            ->given(
                $eventId = 'hoa://Event/Test',
                $source  = new \Mock\Hoa\Event\Source(),
                $data    = new LUT\Bucket()
            )
            ->exception(function () use ($eventId, $source, $data) {
                SUT::notify($eventId, $source, $data);
            })
                ->isInstanceOf('Hoa\Event\Exception');
    }

    public function case_event_exists()
    {
        $this
            ->given(
                $eventId = 'hoa://Event/Test',
                $source  = new \Mock\Hoa\Event\Source(),
                SUT::register($eventId, $source)
            )
            ->when($result = SUT::eventExists($eventId))
            ->then
                ->boolean($result)
                    ->isTrue();
    }

    public function case_event_not_exists()
    {
        $this
            ->given($eventId = 'hoa://Event/Test')
            ->when($result = SUT::eventExists($eventId))
            ->then
                ->boolean($result)
                    ->isFalse();
    }
}


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Event\Test\Unit;

use Hoa\Event\Exception as SUT;
use Hoa\Test;

/**
 * Class \Hoa\Event\Test\Unit\Exception.
 *
 * Test suite of the exception.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Exception extends Test\Unit\Suite
{
    public function case_hoa_exception()
    {
        $this
            ->when($result = new SUT('foo', 0))
            ->then
                ->object($result)
                    ->isInstanceOf('Hoa\Exception\Exception');
    }
}

license: mit


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Event\Test\Unit;

use Hoa\Test;

/**
 * Class \Hoa\Event\Test\Unit\Listenable.
 *
 * Test suite of the listenable interface.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Listenable extends Test\Unit\Suite
{
    public function case_interface()
    {
        $this
            ->when($result = new \Mock\Hoa\Event\Listenable())
            ->then
                ->object($result)
                    ->isInstanceOf('Hoa\Event\Listenable');
    }
}


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Event\Test\Unit;

use Hoa\Event as LUT;
use Hoa\Event\Listener as SUT;
use Hoa\Test;

/**
 * Class \Hoa\Event\Test\Unit\Listener.
 *
 * Test suite of the listener.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Listener extends Test\Unit\Suite
{
    public function case_constructor()
    {
        $this
            ->given(
                $source = new \Mock\Hoa\Event\Listenable(),
                $ids    = ['foo', 'bar', 'baz']
            )
            ->when($result = new SUT($source, $ids))
            ->then
                ->object($result)
                    ->isInstanceOf('Hoa\Event\Listener')
                ->boolean($result->listenerExists('foo'))
                    ->isTrue()
                ->boolean($result->listenerExists('bar'))
                    ->isTrue()
                ->boolean($result->listenerExists('baz'))
                    ->isTrue();
    }

    public function case_attach()
    {
        $this
            ->given(
                $source     = new \Mock\Hoa\Event\Listenable(),
                $listenerId = 'foo',
                $listener   = new SUT($source, ['foo', 'bar']),
                $callable   = function () {
                    return 42;
                }
            )
            ->when($result = $listener->attach($listenerId, $callable))
            ->then
                ->object($result)
                    ->isIdenticalTo($listener)
                ->array($listener->fire($listenerId, new LUT\Bucket()))
                    ->isEqualTo([42]);
    }

    public function case_attach_to_an_undefined_listener()
    {
        $this
            ->given(
                $source     = new \Mock\Hoa\Event\Listenable(),
                $listenerId = 'bar',
                $listener   = new SUT($source, ['foo', 'baz']),
                $callable   = function () { }
            )
            ->exception(function () use ($listener, $listenerId, $callable) {
                $listener->attach($listenerId, $callable);
            })
                ->isInstanceOf('Hoa\Event\Exception');
    }

    public function case_detach()
    {
        $this
            ->given(
                $source     = new \Mock\Hoa\Event\Listenable(),
                $listenerId = 'foo',
                $listener   = new SUT($source, ['foo', 'bar']),
                $callable   = function () {
                    return 42;
                },
                $listener->attach($listenerId, $callable)
            )
            ->when($result = $listener->detach($listenerId, $callable))
            ->then
                ->object($result)
                    ->isIdenticalTo($listener)
                ->array($listener->fire($listenerId, new LUT\Bucket()))
                    ->isEmpty();
    }

    public function case_detach_an_undefined_listener()
    {
        $this
            ->given(
                $source     = new \Mock\Hoa\Event\Listenable(),
                $listenerId = 'bar',
                $listener   = new SUT($source, ['foo', 'baz']),
                $callable   = function () { }
            )
            ->when($result = $listener->detach($listenerId, $callable))
            ->then
                ->object($result)
                    ->isIdenticalTo($listener);
    }

    public function case_detach_all()
    {
        $this
            ->given(
                $source     = new \Mock\Hoa\Event\Listenable(),
                $listenerId = 'foo',
                $listener   = new SUT($source, ['foo', 'bar'])
            )
            ->when($result = $listener->detachAll($listenerId))
            ->then
                ->object($result)
                    ->isIdenticalTo($listener)
                ->boolean($listener->listenerExists($listenerId))
                    ->isFalse();
    }

    public function case_detach_all_with_an_undefined_listener()
    {
        $this
            ->given(
                $source     = new \Mock\Hoa\Event\Listenable(),
                $listenerId = 'bar',
                $listener   = new SUT($source, ['foo', 'baz'])
            )
            ->when($result = $listener->detachAll($listenerId))
            ->then
                ->object($result)
                    ->isIdenticalTo($listener);
    }

    public function case_listener_exists()
    {
        $this
            ->given(
                $source   = new \Mock\Hoa\Event\Listenable(),
                $ids      = [],
                $listener = new SUT($source, $ids)
            )
            ->when($listener->addIds(['foo']))
            ->then
                ->boolean($listener->listenerExists('foo'))
                    ->isTrue()
                ->boolean($listener->listenerExists('bar'))
                    ->isFalse()

            ->when($listener->addIds(['bar']))
            ->then
                ->boolean($listener->listenerExists('bar'))
                    ->isTrue();
    }

    public function case_fire()
    {
        $self = $this;

        $this
            ->given(
                $source   = new \Mock\Hoa\Event\Listenable(),
                $ids      = ['foo', 'bar'],
                $listener = new SUT($source, $ids),

                $listenerId = 'foo',
                $bucket     = new LUT\Bucket(),
                $listener->attach(
                    $listenerId,
                    function (LUT\Bucket $receivedBucket) use ($self, $bucket, $source, &$called) {
                        $called = true;

                        $self
                            ->object($receivedBucket)
                                ->isIdenticalTo($bucket)
                            ->object($receivedBucket->getSource())
                                ->isIdenticalTo($source);

                        return 42;
                    }
                )
            )
            ->when($result = $listener->fire($listenerId, $bucket))
            ->then
                ->array($result)
                    ->isEqualTo([42])
                ->boolean($called)
                    ->isTrue();
    }

    public function case_fire_an_undefined_listenerId()
    {
        $this
            ->given(
                $source   = new \Mock\Hoa\Event\Listenable(),
                $ids      = [],
                $listener = new SUT($source, $ids)
            )
            ->exception(function () use ($listener) {
                $listener->fire('foo', new LUT\Bucket());
            })
                ->isInstanceOf('Hoa\Event\Exception');
    }
}


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Event\Test\Unit;

use Hoa\Event as LUT;
use Hoa\Event\Listens as SUT;
use Hoa\Test;

/**
 * Class \Hoa\Event\Test\Unit\Listens.
 *
 * Test suite of the listens trait.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Listens extends Test\Unit\Suite
{
    public function case_set_listener()
    {
        $this
            ->given(
                $listenable = new _Listenable(),
                $listener   = new LUT\Listener($listenable, ['foo'])
            )
            ->when($result = $listenable->_setListener($listener))
            ->then
                ->variable($result)
                    ->isNull();
    }

    public function case_get_listener()
    {
        $this
            ->given(
                $listenable = new _Listenable(),
                $listener   = new LUT\Listener($listenable, ['foo']),
                $listenable->_setListener($listener)
            )
            ->when($result = $listenable->_getListener())
            ->then
                ->object($result)
                    ->isIdenticalTo($listener);
    }

    public function case_on()
    {
        $this
            ->given(
                $listenable = new _Listenable(),
                $listener   = new LUT\Listener($listenable, ['foo']),
                $listenable->_setListener($listener),
                $callable   = function () use (&$called) {
                    $called = true;

                    return 42;
                }
            )
            ->when($result = $listenable->on('foo', $callable))
            ->then
                ->object($result)
                    ->isIdenticalTo($listenable)

            ->when($listenable->doSomethingThatFires())
            ->then
                ->boolean($called)
                    ->isTrue();
    }

    public function case_on_unregistered_listener()
    {
        $this
            ->given(
                $listenable = new _Listenable(),
                $listener   = new LUT\Listener($listenable, ['foo']),
                $listenable->_setListener($listener)
            )
            ->exception(function () use ($listenable) {
                $listenable->on('bar', null);
            })
                ->isInstanceOf('Hoa\Event\Exception');
    }
}

class _Listenable implements LUT\Listenable
{
    use SUT;

    public function _setListener(LUT\Listener $listener)
    {
        return $this->setListener($listener);
    }

    public function _getListener()
    {
        return $this->getListener();
    }

    public function doSomethingThatFires()
    {
        $this->getListener()->fire('foo', new LUT\Bucket('bar'));

        return;
    }
}


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Event\Test\Unit;

use Hoa\Test;

/**
 * Class \Hoa\Event\Test\Unit\Source.
 *
 * Test suite of the source interface.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Source extends Test\Unit\Suite
{
    public function case_interface()
    {
        $this
            ->when($result = new \Mock\Hoa\Event\Source())
            ->then
                ->object($result)
                    ->isInstanceOf('Hoa\Event\Source');
    }
}


hoa/event

description: The Hoa\Event library.

type: library

keywords: library event listener observer


code:
# 1.17.01.16

  * Quality: Happy new year! (Ivan Enderlin, 2017-01-16T08:52:04+01:00)

# 1.16.11.08

  * Documentation: New `README.md` file. (Ivan Enderlin, 2016-10-19T16:31:55+02:00)
  * Documentation: Update `support` properties. (Ivan Enderlin, 2016-10-05T20:39:14+02:00)

# 1.16.01.11

  * Quality: Drop PHP5.4. (Ivan Enderlin, 2016-01-11T09:15:26+01:00)
  * Quality: Run devtools:cs. (Ivan Enderlin, 2016-01-09T09:01:35+01:00)
  * Core: Remove `Hoa\Core`. (Ivan Enderlin, 2016-01-09T08:16:03+01:00)
  * Consistency: Use `Hoa\Consistency`. (Ivan Enderlin, 2015-12-08T11:11:27+01:00)

# 0.15.11.23

  * Fix phpDoc and a mistake on a static call. (Metalaka, 2015-11-21T17:44:35+01:00)
  * Add a `.gitignore` file. (Metalaka, 2015-11-21T17:39:21+01:00)
  * Test: Add test cases for the uncaught handler. (Ivan Enderlin, 2015-11-18T21:48:20+01:00)
  * Idle: Add uncaught handler. (Ivan Enderlin, 2015-11-18T21:47:40+01:00)
  * README: Add a better description and new usages. (Ivan Enderlin, 2015-11-18T21:36:03+01:00)
  * Test: Write test suite of `Hoa\Exception\Group`. (Ivan Enderlin, 2015-11-18T08:28:04+01:00)
  * Test: Write test suite of `Hoa\Exception\Error`. (Ivan Enderlin, 2015-11-17T22:30:14+01:00)
  * Test: Write test suite of `…\Exception\Exception`. (Ivan Enderlin, 2015-11-17T22:03:31+01:00)
  * Test: Write test suite of `Hoa\Exception\Idle`. (Ivan Enderlin, 2015-11-17T21:41:07+01:00)
  * Split from `Hoa\Core`. (Ivan Enderlin, 2015-11-13T08:58:45+01:00)

(first snapshot)


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Exception;

/**
 * Class \Hoa\Exception\Error.
 *
 * This exception is the equivalent representation of PHP errors.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Error extends Exception
{
    /**
     * Constructor.
     *
     * @param   string  $message    Message.
     * @param   int     $code       Code (the ID).
     * @param   string  $file       File.
     * @param   int     $line       Line.
     * @param   array   $trace      Trace.
     */
    public function __construct(
        $message,
        $code,
        $file,
        $line,
        array $trace = []
    ) {
        $this->file   = $file;
        $this->line   = $line;
        $this->_trace = $trace;

        parent::__construct($message, $code);

        return;
    }

    /**
     * Enable error handler: Transform PHP error into `\Hoa\Exception\Error`.
     *
     * @param   bool  $enable    Enable.
     * @return  mixed
     */
    public static function enableErrorHandler($enable = true)
    {
        if (false === $enable) {
            return restore_error_handler();
        }

        return set_error_handler(
            function ($no, $str, $file = null, $line = null, $ctx = null) {
                if (0 === ($no & error_reporting())) {
                    return;
                }

                $trace = debug_backtrace();
                array_shift($trace);
                array_shift($trace);

                throw new Error($str, $no, $file, $line, $trace);
            }
        );
    }
}


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Exception;

use Hoa\Consistency;
use Hoa\Event;

/**
 * Class \Hoa\Exception\Exception.
 *
 * Each exception must extend \Hoa\Exception\Exception.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Exception extends Idle implements Event\Source
{
    /**
     * Create an exception.
     * An exception is built with a formatted message, a code (an ID), and an
     * array that contains the list of formatted string for the message. If
     * chaining, we can add a previous exception.
     *
     * @param   string      $message      Formatted message.
     * @param   int         $code         Code (the ID).
     * @param   array       $arguments    Arguments to format message.
     * @param   \Throwable  $previous     Previous exception in chaining.
     */
    public function __construct(
        $message,
        $code      = 0,
        $arguments = [],
        $previous  = null
    ) {
        parent::__construct($message, $code, $arguments, $previous);

        if (false === Event::eventExists('hoa://Event/Exception')) {
            Event::register('hoa://Event/Exception', __CLASS__);
        }

        $this->send();

        return;
    }

    /**
     * Send the exception on hoa://Event/Exception.
     *
     * @return  void
     */
    public function send()
    {
        Event::notify(
            'hoa://Event/Exception',
            $this,
            new Event\Bucket($this)
        );

        return;
    }
}

/**
 * Flex entity.
 */
Consistency::flexEntity('Hoa\Exception\Exception');


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Exception;

/**
 * Class \Hoa\Exception\Group.
 *
 * This is an exception that contains a group of exceptions.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Group extends Exception implements \ArrayAccess, \IteratorAggregate, \Countable
{
    /**
     * All exceptions (stored in a stack for transactions).
     *
     * @var \SplStack
     */
    protected $_group = null;

    /**
     * Create an exception.
     *
     * @param   string      $message      Formatted message.
     * @param   int         $code         Code (the ID).
     * @param   array       $arguments    Arguments to format message.
     * @param   \Exception  $previous     Previous exception in chaining.
     */
    public function __construct(
        $message,
        $code                = 0,
        $arguments           = [],
        \Exception $previous = null
    ) {
        parent::__construct($message, $code, $arguments, $previous);
        $this->_group = new \SplStack();
        $this->beginTransaction();

        return;
    }

    /**
     * Raise an exception as a string.
     *
     * @param   bool    $previous    Whether raise previous exception if exists.
     * @return  string
     */
    public function raise($previous = false)
    {
        $out = parent::raise($previous);

        if (0 >= count($this)) {
            return $out;
        }

        $out .= "\n\n" . 'Contains the following exceptions:';

        foreach ($this as $exception) {
            $out .=
                "\n\n" . '  • ' .
                str_replace(
                    "\n",
                    "\n" . '    ',
                    $exception->raise($previous)
                );
        }

        return $out;
    }

    /**
     * Begin a transaction.
     *
     * @return  \Hoa\Exception\Group
     */
    public function beginTransaction()
    {
        $this->_group->push(new \ArrayObject());

        return $this;
    }

    /**
     * Rollback a transaction.
     *
     * @return  \Hoa\Exception\Group
     */
    public function rollbackTransaction()
    {
        if (1 >= count($this->_group)) {
            return $this;
        }

        $this->_group->pop();

        return $this;
    }

    /**
     * Commit a transaction.
     *
     * @return  \Hoa\Exception\Group
     */
    public function commitTransaction()
    {
        if (false === $this->hasUncommittedExceptions()) {
            $this->_group->pop();

            return $this;
        }

        foreach ($this->_group->pop() as $index => $exception) {
            $this[$index] = $exception;
        }

        return $this;
    }

    /**
     * Check if there is uncommitted exceptions.
     *
     * @return  bool
     */
    public function hasUncommittedExceptions()
    {
        return
            1 < count($this->_group) &&
            0 < count($this->_group->top());
    }

    /**
     * Check if an index in the group exists.
     *
     * @param   mixed  $index    Index.
     * @return  bool
     */
    public function offsetExists($index)
    {
        foreach ($this->_group as $group) {
            if (isset($group[$index])) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get an exception from the group.
     *
     * @param   mixed  $index    Index.
     * @return  Exception
     */
    public function offsetGet($index)
    {
        foreach ($this->_group as $group) {
            if (isset($group[$index])) {
                return $group[$index];
            }
        }

        return null;
    }

    /**
     * Set an exception in the group.
     *
     * @param   mixed       $index        Index.
     * @param   Exception  $exception    Exception.
     * @return  void
     */
    public function offsetSet($index, $exception)
    {
        if (!($exception instanceof \Exception)) {
            return null;
        }

        $group = $this->_group->top();

        if (null === $index ||
            true === is_int($index)) {
            $group[] = $exception;
        } else {
            $group[$index] = $exception;
        }

        return;
    }

    /**
     * Remove an exception in the group.
     *
     * @param   mixed  $index    Index.
     * @return  void
     */
    public function offsetUnset($index)
    {
        foreach ($this->_group as $group) {
            if (isset($group[$index])) {
                unset($group[$index]);
            }
        }

        return;
    }

    /**
     * Get committed exceptions in the group.
     *
     * @return  \ArrayObject
     */
    public function getExceptions()
    {
        return $this->_group->bottom();
    }

    /**
     * Get an iterator over all exceptions (committed or not).
     *
     * @return  \ArrayIterator
     */
    public function getIterator()
    {
        return $this->getExceptions()->getIterator();
    }

    /**
     * Count the number of committed exceptions.
     *
     * @return  int
     */
    public function count()
    {
        return count($this->getExceptions());
    }

    /**
     * Count the stack size, i.e. the number of opened transactions.
     *
     * @return  int
     */
    public function getStackSize()
    {
        return count($this->_group);
    }
}


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Exception;

/**
 * Class \Hoa\Exception\Idle.
 *
 * `\Hoa\Exception\Idle` is the mother exception class of libraries. The only
 * difference between `\Hoa\Exception\Idle` and its directly child
 * `\Hoa\Exception` is that the latter fires events after beeing constructed.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Idle extends \Exception
{
    /**
     * Delay processing on arguments.
     *
     * @var array
     */
    protected $_tmpArguments = null;

    /**
     * Arguments to format message.
     *
     * @var array
     */
    protected $_arguments    = null;

    /**
     * Backtrace.
     *
     * @var array
     */
    protected $_trace        = null;

    /**
     * Previous.
     *
     * @var \Exception
     */
    protected $_previous     = null;

    /**
     * Original message.
     *
     * @var string
     */
    protected $_rawMessage   = null;

    /**
     * Create an exception.
     * An exception is built with a formatted message, a code (an ID) and an
     * array that contains the list of formatted strings for the message. If
     * chaining, we can add a previous exception.
     *
     * @param   string      $message      Formatted message.
     * @param   int         $code         Code (the ID).
     * @param   array       $arguments    Arguments to format message.
     * @param   \Exception  $previous     Previous exception in chaining.
     */
    public function __construct(
        $message,
        $code = 0,
        $arguments = [],
        \Exception $previous = null
    ) {
        $this->_tmpArguments = $arguments;
        parent::__construct($message, $code, $previous);
        $this->_rawMessage   = $message;
        $this->message       = @vsprintf($message, $this->getArguments());

        return;
    }

    /**
     * Get the backtrace.
     * Do not use \Exception::getTrace() any more.
     *
     * @return  array
     */
    public function getBacktrace()
    {
        if (null === $this->_trace) {
            $this->_trace = $this->getTrace();
        }

        return $this->_trace;
    }

    /**
     * Get previous.
     * Do not use \Exception::getPrevious() any more.
     *
     * @return  \Exception
     */
    public function getPreviousThrow()
    {
        if (null === $this->_previous) {
            $this->_previous = $this->getPrevious();
        }

        return $this->_previous;
    }

    /**
     * Get arguments for the message.
     *
     * @return  array
     */
    public function getArguments()
    {
        if (null === $this->_arguments) {
            $arguments = $this->_tmpArguments;

            if (!is_array($arguments)) {
                $arguments = [$arguments];
            }

            foreach ($arguments as &$value) {
                if (null === $value) {
                    $value = '(null)';
                }
            }

            $this->_arguments = $arguments;
            unset($this->_tmpArguments);
        }

        return $this->_arguments;
    }

    /**
     * Get the raw message.
     *
     * @return  string
     */
    public function getRawMessage()
    {
        return $this->_rawMessage;
    }

    /**
     * Get the message already formatted.
     *
     * @return  string
     */
    public function getFormattedMessage()
    {
        return $this->getMessage();
    }

    /**
     * Get the source of the exception (class, method, function, main etc.).
     *
     * @return  string
     */
    public function getFrom()
    {
        $trace = $this->getBacktrace();
        $from  = '{main}';

        if (!empty($trace)) {
            $t    = $trace[0];
            $from = '';

            if (isset($t['class'])) {
                $from .= $t['class'] . '::';
            }

            if (isset($t['function'])) {
                $from .= $t['function'] . '()';
            }
        }

        return $from;
    }

    /**
     * Raise an exception as a string.
     *
     * @param   bool    $previous    Whether raise previous exception if exists.
     * @return  string
     */
    public function raise($previous = false)
    {
        $message = $this->getFormattedMessage();
        $trace   = $this->getBacktrace();
        $file    = '/dev/null';
        $line    = -1;
        $pre     = $this->getFrom();

        if (!empty($trace)) {
            $file = isset($trace['file']) ? $trace['file'] : null;
            $line = isset($trace['line']) ? $trace['line'] : null;
        }

        $pre .= ': ';

        try {
            $out =
                $pre . '(' . $this->getCode() . ') ' . $message . "\n" .
                'in ' . $this->getFile() . ' at line ' .
                $this->getLine() . '.';
        } catch (\Exception $e) {
            $out =
                $pre . '(' . $this->getCode() . ') ' . $message . "\n" .
                'in ' . $file . ' around line ' . $line . '.';
        }

        if (true === $previous &&
            null !== $previous = $this->getPreviousThrow()) {
            $out .=
                "\n\n" . '    ⬇' . "\n\n" .
                'Nested exception (' . get_class($previous) . '):' . "\n" .
                ($previous instanceof self
                    ? $previous->raise(true)
                    : $previous->getMessage());
        }

        return $out;
    }

    /**
     * Catch uncaught exception (only \Hoa\Exception\Idle and children).
     *
     * @param   \Throwable  $exception    The exception.
     * @return  void
     * @throws  \Throwable
     */
    public static function uncaught($exception)
    {
        if (!($exception instanceof self)) {
            throw $exception;
        }

        while (0 < ob_get_level()) {
            ob_end_flush();
        }

        echo
            'Uncaught exception (' . get_class($exception) . '):' . "\n" .
            $exception->raise(true);

        return;
    }

    /**
     * String representation of object.
     *
     * @return  string
     */
    public function __toString()
    {
        return $this->raise();
    }

    /**
     * Enable uncaught exception handler.
     * This is restricted to Hoa's exceptions only.
     *
     * @param   bool  $enable    Enable.
     * @return  mixed
     */
    public static function enableUncaughtHandler($enable = true)
    {
        if (false === $enable) {
            return restore_exception_handler();
        }

        return set_exception_handler(function ($exception) {
            return self::uncaught($exception);
        });
    }
}


code:
---

  Hoa is a modular, extensible and
  structured set of PHP libraries.

  Moreover, Hoa aims at being a bridge between industrial and research worlds.

# Hoa\Exception

[![Help on IRC](https://img.shields.io/badge/help-%23hoaproject-ff0066.svg)](https://webchat.freenode.net/?channels=#hoaproject)
[![Help on Gitter](https://img.shields.io/badge/help-gitter-ff0066.svg)](https://gitter.im/hoaproject/central)
[![Documentation](https://img.shields.io/badge/documentation-hack_book-ff0066.svg)](https://central.hoa-project.net/Documentation/Library/Exception)
[![Board](https://img.shields.io/badge/organisation-board-ff0066.svg)](https://waffle.io/hoaproject/exception)

This library allows to use advanced exceptions. It provides generic exceptions
(that are sent over the `hoa://Event/Exception` event channel), idle exceptions
(that are not sent over an event channel), uncaught exception handlers, errors
to exceptions handler and group of exceptions (with transactions).

[Learn more](https://central.hoa-project.net/Documentation/Library/Exception).

## Installation

With [Composer](https://getcomposer.org/), to include this library into
your dependencies, you need to
require [`hoa/exception`](https://packagist.org/packages/hoa/exception):

```sh
$ composer require hoa/exception '~1.0'
```

For more installation procedures, please read [the Source
page](https://hoa-project.net/Source.html).

## Testing

Before running the test suites, the development dependencies must be installed:

```sh
$ composer install
```

Then, to run all the test suites:

```sh
$ vendor/bin/hoa test:run
```

For more information, please read the [contributor
guide](https://hoa-project.net/Literature/Contributor/Guide.html).

## Quick usage

We propose a quick overview of how to use generic exceptions, how to listen all
thrown exceptions through events and how to use group of exceptions.

### Generic exceptions

An exception is constitued of:
  * A message,
  * A code (optional),
  * A list of arguments for the message (à la `printf`, optional),
  * A previous exception (optional).

Thus, the following example builds an exception:

```php
$exception = new Hoa\Exception\Exception('Hello %s!', 0, 'world');
```

The exception message will be: `Hello world!`. The “raise” message (with all
information, not only the message) is:

```
{main}: (0) Hello world!
in … at line ….
```

Previous exceptions are shown too, for instance:

```php
$previous  = new Hoa\Exception\Exception('Hello previous.');
$exception = new Hoa\Exception\Exception('Hello %s!', 0, 'world', $previous);

echo $exception->raise(true);

/**
 * Will output:
 *     {main}: (0) Hello world!
 *     in … at line ….
 *
 *         ⬇
 *
 *     Nested exception (Hoa\Exception\Exception):
 *     {main}: (0) Hello previous.
 *     in … at line ….
 */
```

### Listen exceptions through events

Most exceptions in Hoa extend `Hoa\Exception\Exception`, which fire themselves
on the `hoa://Event/Exception` event channel (please, see [the `Hoa\Event`
library](http://central.hoa-project.net/Resource/Library/Event)). Consequently,
we can listen for all exceptions that are thrown in the application by writing:

```php
Hoa\Event\Event::getEvent('hoa://Event/Exception')->attach(
    function (Hoa\Event\Bucket $bucket) {
        $exception = $bucket->getData();
        // …
    }
);
```

Only the `Hoa\Exception\Idle` exceptions are not fired on the channel event.

### Group and transactions

Groups of exceptions are represented by the `Hoa\Exception\Group`. A group is an
exception that contains one or many exceptions. A transactional API is provided
to add more exceptions in the group with the following methods:
  * `beginTransaction` to start a transaction,
  * `rollbackTransaction` to remove all newly added exceptions since
    `beginTransaction` call,
  * `commitTransaction` to merge all newly added exceptions in the previous
    transaction,
  * `hasUncommittedExceptions` to check whether they are pending exceptions or
    not.

For instance, if an exceptional behavior is due to several reasons, a group of
exceptions can be thrown instead of one exception. Group can be nested too,
which is useful to represent a tree of exceptions. Thus:

```php
// A group of exceptions.
$group           = new Hoa\Exception\Group('Failed because of several reasons.');
$group['first']  = new Hoa\Exception\Exception('First reason');
$group['second'] = new Hoa\Exception\Exception('Second reason');

// Can nest another group.
$group['third']           = new Hoa\Exception\Group('Third reason');
$group['third']['fourth'] = new Hoa\Exception\Exception('Fourth reason');

echo $group->raise(true);

/**
 * Will output:
 *     {main}: (0) Failed because of several reasons.
 *     in … at line ….
 *
 *     Contains the following exceptions:
 *
 *       • {main}: (0) First reason
 *         in … at line ….
 *
 *       • {main}: (0) Second reason
 *         in … at line ….
 *
 *       • {main}: (0) Third reason
 *         in … at line ….
 *
 *         Contains the following exceptions:
 *
 *           • {main}: (0) Fourth reason
 *             in … at line ….
 */
```

The following example uses a transaction to add new exceptions in the group:

```php
$group   = new Hoa\Exception\Group('Failed because of several reasons.');
$group[] = new Hoa\Exception\Exception('Always present.');

$group->beginTransaction();

$group[] = new Hoa\Exception\Exception('Might be present.');

if (true === $condition) {
    $group->commitTransaction();
} else {
    $group->rollbackTransaction();
}
```

## Documentation

The
[hack book of `Hoa\Exception`](https://central.hoa-project.net/Documentation/Library/Exception)
contains detailed information about how to use this library and how it works.

To generate the documentation locally, execute the following commands:

```sh
$ composer require --dev hoa/devtools
$ vendor/bin/hoa devtools:documentation --open
```

More documentation can be found on the project's website:
[hoa-project.net](https://hoa-project.net/).

## Getting help

There are mainly two ways to get help:

  * On the [`#hoaproject`](https://webchat.freenode.net/?channels=#hoaproject)
    IRC channel,
  * On the forum at [users.hoa-project.net](https://users.hoa-project.net).

## Contribution

Do you want to contribute? Thanks! A detailed [contributor
guide](https://hoa-project.net/Literature/Contributor/Guide.html) explains
everything you need to know.

## License

Hoa is under the New BSD License (BSD-3-Clause). Please, see
[`LICENSE`](https://hoa-project.net/LICENSE) for details.


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Exception\Test\Unit;

use Hoa\Exception\Error as SUT;
use Hoa\Test;

/**
 * Class \Hoa\Exception\Test\Unit\Error.
 *
 * Test suite of the error class.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Error extends Test\Unit\Suite
{
    public function case_is_an_exception()
    {
        $this
            ->when($result = new SUT('foo', 42, '/hoa/flatland', 153))
            ->then
                ->object($result)
                    ->isInstanceOf('Hoa\Exception\Exception');
    }

    public function case_get_message()
    {
        $this
            ->given($exception = new SUT('foo', 42, '/hoa/flatland', 153))
            ->when($result = $exception->raise())
            ->then
                ->string($result)
                    ->isEqualTo(
                        '{main}: (42) foo' . "\n" .
                        'in /hoa/flatland at line 153.'
                    );
    }

    public function case_disable_error_handler()
    {
        $this
            ->given(
                $this->function->restore_error_handler = function () use (&$called) {
                    $called = true;

                    return null;
                }
            )
            ->when($result = SUT::enableErrorHandler(false))
            ->then
                ->variable($result)
                    ->isNull()
                ->boolean($called)
                    ->isTrue();
    }

    public function case_enable_error_handler()
    {
        $self = $this;

        $this
            ->given(
                $this->function->set_error_handler = function ($handler) use ($self, &$called) {
                    $called = true;

                    $self
                        ->object($handler)
                            ->isInstanceOf('Closure')
                        ->let($reflection = new \ReflectionObject($handler))
                        ->array($invokeParameters = $reflection->getMethod('__invoke')->getParameters())
                            ->hasSize(5)
                        ->string($invokeParameters[0]->getName())
                            ->isEqualTo('no')
                        ->string($invokeParameters[1]->getName())
                            ->isEqualTo('str')
                        ->string($invokeParameters[2]->getName())
                            ->isEqualTo('file')
                        ->boolean($invokeParameters[2]->isOptional())
                            ->isTrue()
                        ->string($invokeParameters[3]->getName())
                            ->isEqualTo('line')
                        ->boolean($invokeParameters[3]->isOptional())
                            ->isTrue()
                        ->string($invokeParameters[4]->getName())
                            ->isEqualTo('ctx')
                        ->boolean($invokeParameters[4]->isOptional())
                            ->isTrue();

                    return null;
                }
            )
            ->when($result = SUT::enableErrorHandler())
            ->then
                ->variable($result)
                    ->isNull()
                ->boolean($called)
                    ->isTrue();
    }

    public function case_error_handler()
    {
        $this
            ->given(SUT::enableErrorHandler())
            ->exception(function () {
                ++$i;
            })
                ->isInstanceOf('Hoa\Exception\Error')
                ->hasMessage('Undefined variable: i');
    }
}


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Exception\Test\Unit;

use Hoa\Event;
use Hoa\Exception\Exception as SUT;
use Hoa\Test;

/**
 * Class \Hoa\Exception\Test\Unit\Exception.
 *
 * Test suite of the exception class.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Exception extends Test\Unit\Suite
{
    public function case_is_an_idle_exception()
    {
        $this
            ->when($result = new SUT('foo'))
            ->then
                ->object($result)
                    ->isInstanceOf('Hoa\Exception\Idle');
    }

    public function case_event_is_registered()
    {
        $this
            ->given(new SUT('foo'))
            ->when($result = Event::eventExists('hoa://Event/Exception'))
            ->then
                ->boolean($result)
                    ->isTrue();
    }

    public function case_event_is_sent()
    {
        $self = $this;

        $this
            ->given(
                Event::getEvent('hoa://Event/Exception')->attach(
                    function (Event\Bucket $bucket) use ($self, &$called) {
                        $called = true;

                        $self
                            ->object($bucket->getSource())
                                ->isInstanceOf('Hoa\Exception\Exception')
                            ->string($bucket->getSource()->getMessage())
                                ->isEqualTo('foo')
                            ->object($bucket->getData())
                                ->isIdenticalTo($bucket->getSource());
                    }
                )
            )
            ->when(new SUT('foo'))
            ->then
                ->boolean($called)
                    ->isTrue();
    }
}


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Exception\Test\Unit;

use Hoa\Exception\Group as SUT;
use Hoa\Test;

/**
 * Class \Hoa\Exception\Test\Unit\Group.
 *
 * Test suite of the group class.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Group extends Test\Unit\Suite
{
    public function case_is_an_exception_arrayaccess_iteratoraggregate_countable()
    {
        $this
            ->when($result = new SUT('foo'))
            ->then
                ->object($result)
                    ->isInstanceOf('Hoa\Exception\Exception')
                    ->isInstanceOf('ArrayAccess')
                    ->isInstanceOf('IteratorAggregate')
                    ->isInstanceOf('Countable');
    }

    public function case_constructor()
    {
        $this
            ->given(
                $message   = 'foo %s %d %s',
                $code      = 7,
                $arguments = ['arg', 42, null],
                $previous  = new SUT('previous')
            )
            ->when($result = new SUT($message, $code, $arguments, $previous), $line = __LINE__)
            ->then
                ->string($result->getMessage())
                    ->isEqualTo('foo arg 42 (null)')
                ->integer($result->getCode())
                    ->isEqualTo(7)
                ->array($result->getArguments())
                    ->isEqualTo(['arg', 42, '(null)'])
                ->object($result->getPreviousThrow())
                    ->isIdenticalTo($previous)
                ->boolean($result->hasUncommittedExceptions())
                    ->isFalse();
    }

    public function case_raise_zero_exception()
    {
        $this
            ->given($group = new SUT('foo'), $line = __LINE__)
            ->when($result = $group->raise())
            ->then
                ->string($result)
                    ->isEqualTo(
                        __METHOD__ . '(): (0) foo' . "\n" .
                        'in ' . __FILE__ . ' at line ' . $line . '.'
                    );
    }

    public function case_raise_one_exception()
    {
        $this
            ->given(
                $exception1 = new SUT('bar'), $barLine = __LINE__,
                $group      = new SUT('foo'), $fooLine = __LINE__,
                $group[]    = $exception1
            )
            ->when($result = $group->raise())
            ->then
                ->string($result)
                    ->isEqualTo(
                        __METHOD__ . '(): (0) foo' . "\n" .
                        'in ' . __FILE__ . ' at line ' . $fooLine . '.' . "\n\n" .
                        'Contains the following exceptions:' . "\n\n" .
                        '  • ' . __METHOD__ . '(): (0) bar' . "\n" .
                        '    in ' . __FILE__ . ' at line ' . $barLine . '.'
                    );
    }

    public function case_raise_more_exceptions()
    {
        $this
            ->given(
                $exception1 = new SUT('bar'), $barLine = __LINE__,
                $exception2 = new SUT('baz'), $bazLine = __LINE__,
                $group      = new SUT('foo'), $fooLine = __LINE__,
                $group[]    = $exception1,
                $group[]    = $exception2
            )
            ->when($result = $group->raise())
            ->then
                ->string($result)
                    ->isEqualTo(
                        __METHOD__ . '(): (0) foo' . "\n" .
                        'in ' . __FILE__ . ' at line ' . $fooLine . '.' . "\n\n" .
                        'Contains the following exceptions:' . "\n\n" .
                        '  • ' . __METHOD__ . '(): (0) bar' . "\n" .
                        '    in ' . __FILE__ . ' at line ' . $barLine . '.' . "\n\n" .
                        '  • ' . __METHOD__ . '(): (0) baz' . "\n" .
                        '    in ' . __FILE__ . ' at line ' . $bazLine . '.'
                    );
    }

    public function case_begin_transaction()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $oldStackSize = $group->getStackSize()
            )
            ->when(
                $result    = $group->beginTransaction(),
                $stackSize = $group->getStackSize()
            )
            ->then
                ->integer($oldStackSize)
                    ->isEqualTo(1)
                ->object($result)
                    ->isIdenticalTo($group)
                ->integer($stackSize)
                    ->isEqualTo($oldStackSize + 1);
    }

    public function case_rollback_transaction_with_an_empty_stack()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $oldStackSize = $group->getStackSize()
            )
            ->when(
                $result    = $group->rollbackTransaction(),
                $stackSize = $group->getStackSize()
            )
            ->then
                ->integer($oldStackSize)
                    ->isEqualTo(1)
                ->object($result)
                    ->isIdenticalTo($group)
                ->integer($stackSize)
                    ->isEqualTo($oldStackSize);
    }

    public function case_rollback_transaction()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $group->beginTransaction(),
                $group->beginTransaction(),
                $oldStackSize = $group->getStackSize(),
                $group->rollbackTransaction()
            )
            ->when(
                $result    = $group->rollbackTransaction(),
                $stackSize = $group->getStackSize()
            )
            ->then
                ->integer($oldStackSize)
                    ->isEqualTo(3)
                ->object($result)
                    ->isIdenticalTo($group)
                ->integer($stackSize)
                    ->isEqualTo($oldStackSize - 2);
    }

    public function case_commit_transaction_with_an_empty_stack()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $group->beginTransaction(),
                $oldCount     = count($group),
                $oldStackSize = $group->getStackSize()
            )
            ->when(
                $result    = $group->commitTransaction(),
                $count     = count($group),
                $stackSize = $group->getStackSize()
            )
            ->then
                ->integer($oldCount)
                    ->isEqualTo(0)
                ->integer($oldStackSize)
                    ->isEqualTo(2)
                ->object($result)
                    ->isIdenticalTo($group)
                ->integer($count)
                    ->isEqualTo($oldCount)
                ->integer($stackSize)
                    ->isEqualTo($oldStackSize - 1);
    }

    public function case_commit_transaction()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $group->beginTransaction(),
                $exception1   = new SUT('bar'),
                $exception2   = new SUT('baz'),
                $group[]      = $exception1,
                $group[]      = $exception2,
                $oldCount     = count($group),
                $oldStackSize = $group->getStackSize()
            )
            ->when(
                $result    = $group->commitTransaction(),
                $count     = count($group),
                $stackSize = $group->getStackSize()
            )
            ->then
                ->integer($oldCount)
                    ->isEqualTo(0)
                ->integer($oldStackSize)
                    ->isEqualTo(2)
                ->object($result)
                    ->isIdenticalTo($group)
                ->integer($count)
                    ->isEqualTo($oldCount + 2)
                ->integer($stackSize)
                    ->isEqualTo($oldStackSize - 1)
                ->array(iterator_to_array($group->getIterator()))
                    ->isEqualTo([
                        0 => $exception1,
                        1 => $exception2
                    ]);
    }

    public function case_has_uncommitted_exceptions()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $group->beginTransaction(),
                $group[] = new SUT('bar')
            )
            ->when($result = $group->hasUncommittedExceptions())
            ->then
                ->boolean($result)
                    ->isTrue();
    }

    public function case_has_no_uncommitted_exceptions()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $group->beginTransaction()
            )
            ->when($result = $group->hasUncommittedExceptions())
            ->then
                ->boolean($result)
                    ->isFalse();
    }

    public function case_has_no_uncommitted_exceptions_with_empty_stack()
    {
        $this
            ->given(
                $group   = new SUT('foo'),
                $group[] = new SUT('bar')
            )
            ->when($result = $group->hasUncommittedExceptions())
            ->then
                ->boolean($result)
                    ->isFalse();
    }

    public function case_offset_exists_with_no_uncommited_exceptions()
    {
        $this
            ->given(
                $group        = new SUT('foo'),
                $group['bar'] = new SUT('bar')
            )
            ->when($result = $group->offsetExists('bar'))
            ->then
                ->boolean($result)
                    ->isTrue();
    }

    public function case_offset_does_not_exist_with_no_uncommited_exceptions()
    {
        $this
            ->given(
                $group        = new SUT('foo'),
                $group['bar'] = new SUT('bar')
            )
            ->when($result = $group->offsetExists('baz'))
            ->then
                ->boolean($result)
                    ->isFalse();
    }

    public function case_offset_exists()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $group->beginTransaction(),
                $group->beginTransaction(),
                $group['bar'] = new SUT('bar')
            )
            ->when($result = $group->offsetExists('bar'))
            ->then
                ->boolean($result)
                    ->isTrue();
    }

    public function case_offset_does_not_exist()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $group->beginTransaction(),
                $group->beginTransaction(),
                $group['bar'] = new SUT('bar')
            )
            ->when($result = $group->offsetExists('baz'))
            ->then
                ->boolean($result)
                    ->isFalse();
    }

    public function case_offset_get_with_no_uncommited_exceptions()
    {
        $this
            ->given(
                $group        = new SUT('foo'),
                $exception1   = new SUT('bar'),
                $group['bar'] = $exception1
            )
            ->when($result = $group->offsetGet('bar'))
            ->then
                ->object($result)
                    ->isIdenticalTo($exception1);
    }

    public function case_offset_get_does_not_exist_with_no_uncommited_exceptions()
    {
        $this
            ->given(
                $group        = new SUT('foo'),
                $exception1   = new SUT('bar'),
                $group['bar'] = $exception1
            )
            ->when($result = $group->offsetGet('baz'))
            ->then
                ->variable($result)
                    ->isNull();
    }

    public function case_offset_get()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $group->beginTransaction(),
                $group->beginTransaction(),
                $exception1   = new SUT('bar'),
                $group['bar'] = $exception1
            )
            ->when($result = $group->offsetGet('bar'))
            ->then
                ->object($result)
                    ->isIdenticalTo($exception1);
    }

    public function case_offset_get_does_not_exist()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $group->beginTransaction(),
                $group->beginTransaction(),
                $exception1   = new SUT('bar'),
                $group['bar'] = $exception1
            )
            ->when($result = $group->offsetGet('baz'))
            ->then
                ->variable($result)
                    ->isNull();
    }

    public function case_offset_set_not_an_exception()
    {
        $this
            ->given($group = new SUT('foo'))
            ->when($group->offsetSet('bar', new \StdClass()))
            ->then
                ->boolean($group->offsetExists('bar'))
                    ->isFalse();
    }

    public function case_offset_set()
    {
        $this
            ->given(
                $group      = new SUT('foo'),
                $exception1 = new SUT('bar')
            )
            ->when($result = $group->offsetExists('bar'))
            ->then
                ->boolean($result)
                    ->isFalse()

            ->when($group->offsetSet('bar', $exception1))
            ->then
                ->boolean($group->offsetExists('bar'))
                    ->isTrue()
                ->object($group->offsetGet('bar'))
                    ->isIdenticalTo($exception1);
    }

    public function case_offset_set_with_a_null_index()
    {
        $this
            ->given(
                $group      = new SUT('foo'),
                $exception1 = new SUT('bar')
            )
            ->when($group->offsetSet(null, $exception1))
            ->then
                ->boolean($group->offsetExists(0))
                    ->isTrue()
                ->object($group->offsetGet(0))
                    ->isIdenticalTo($exception1);
    }

    public function case_offset_set_with_an_integer_index()
    {
        $this
            ->given(
                $group      = new SUT('foo'),
                $exception1 = new SUT('bar')
            )
            ->when($group->offsetSet(42, $exception1))
            ->then
                ->boolean($group->offsetExists(42))
                    ->isFalse()
                ->boolean($group->offsetExists(0))
                    ->isTrue()
                ->object($group->offsetGet(0))
                    ->isIdenticalTo($exception1);
    }

    public function case_offset_unset_with_no_uncommited_exceptions()
    {
        $this
            ->given(
                $group        = new SUT('foo'),
                $group['bar'] = new SUT('bar')
            )
            ->when($group->offsetUnset('bar'))
            ->then
                ->boolean($group->offsetExists('bar'))
                    ->isFalse();
    }

    public function case_offset_unset_does_not_exist_with_no_uncommited_exceptions()
    {
        $this
            ->given($group = new SUT('foo'))
            ->when($group->offsetUnset('bar'))
            ->then
                ->boolean($group->offsetExists('bar'))
                    ->isFalse();
    }

    public function case_offset_unset()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $group->beginTransaction(),
                $group->beginTransaction(),
                $group['bar'] = new SUT('bar')
            )
            ->when($result = $group->offsetUnset('bar'))
            ->then
                ->boolean($group->offsetExists('bar'))
                    ->isFalse();
    }

    public function case_offset_unset_does_not_exist()
    {
        $this
            ->given(
                $group = new SUT('foo'),
                $group->beginTransaction(),
                $group->beginTransaction()
            )
            ->when($result = $group->offsetUnset('bar'))
            ->then
                ->boolean($group->offsetExists('bar'))
                    ->isFalse();
    }

    public function case_get_exceptions()
    {
        $this
            ->given(
                $group        = new SUT('foo'),
                $exception1   = new SUT('bar'),
                $exception2   = new SUT('baz'),
                $group['bar'] = $exception1,
                $group->beginTransaction(),
                $group['baz'] = $exception2
            )
            ->when($result = $group->getExceptions())
            ->then
                ->object($result)
                    ->isInstanceOf('ArrayObject')
                ->object($result['bar'])
                    ->isIdenticalTo($exception1);
    }

    public function case_get_iterator()
    {
        $this
            ->given(
                $group        = new SUT('foo'),
                $exception1   = new SUT('bar'),
                $group['bar'] = $exception1
            )
            ->when($result = $group->getIterator())
            ->then
                ->object($result)
                    ->isInstanceOf('ArrayIterator')
                ->array(iterator_to_array($result))
                    ->isEqualTo([
                        'bar' => $exception1
                    ]);
    }

    public function case_count()
    {
        $this
            ->given(
                $group        = new SUT('foo'),
                $exception1   = new SUT('bar'),
                $exception2   = new SUT('baz'),
                $group['bar'] = $exception1,
                $group->beginTransaction(),
                $group['baz'] = $exception2
            )
            ->when($result = count($group))
            ->then
                ->integer($result)
                    ->isEqualTo(1);
    }

    public function get_get_stack_size()
    {
        $this
            ->given(
                $group        = new SUT('foo'),
                $exception1   = new SUT('bar'),
                $exception2   = new SUT('baz'),
                $group['bar'] = $exception1,
                $group->beginTransaction(),
                $group['baz'] = $exception2
            )
            ->when($result = $group->getStackSize())
            ->then
                ->integer($result)
                    ->isEqualTo(2);
    }
}


code:
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace Hoa\Exception\Test\Unit;

use Hoa\Exception\Idle as SUT;
use Hoa\Test;

/**
 * Class \Hoa\Exception\Test\Unit\Idle.
 *
 * Test suite of the idle exception.
 *
 * @copyright  Copyright © 2007-2017 Hoa community
 * @license    New BSD License
 */
class Idle extends Test\Unit\Suite
{
    public function case_is_a_real_exception()
    {
        $this
            ->when($result = new SUT('foo'))
            ->then
                ->object($result)
                    ->isInstanceOf('Exception');
    }

    public function case_get_backtrace()
    {
        $this
            ->given($exception = new SUT('foo'))
            ->when($result = $exception->getBacktrace())
            ->then
                ->array($result)
                    ->hasKey(0)
                ->array($result[0])
                    ->hasKey('file')
                    ->hasKey('line')
                    ->hasKey('function')
                    ->hasKey('class')
                    ->hasKey('type')
                    ->hasKey('args');
    }

    public function case_get_previous_throw()
    {
        $this
            ->given(
                $previous  = new SUT('previous'),
                $exception = new SUT('foo', 0, [], $previous)
            )
            ->when($result = $exception->getPreviousThrow())
            ->then
                ->object($result)
                    ->isIdenticalTo($previous);
    }

    public function case_get_arguments()
    {
        $this
            ->given($exception = new SUT('foo', 0, ['arg', 42, null]))
            ->when($result = $exception->getArguments())
            ->then
                ->array($result)
                    ->isEqualTo(['arg', 42, '(null)']);
    }

    public function case_get_arguments_from_a_string()
    {
        $this
            ->given($exception = new SUT('foo', 0, 'arg'))
            ->when($result = $exception->getArguments())
            ->then
                ->array($result)
                    ->isEqualTo(['arg']);
    }

    public function case_get_raw_message()
    {
        $this
            ->given(
                $message   = 'foo %s',
                $exception = new SUT($message)
            )
            ->when($result = $exception->getRawMessage())
            ->then
                ->string($result)
                    ->isEqualTo($message);
    }

    public function case_get_formatted_message()
    {
        $this
            ->given(
                $message   = 'foo %s',
                $exception = new SUT($message, 0, 'bar')
            )
            ->when($result = $exception->getFormattedMessage())
            ->then
                ->string($result)
                    ->isEqualTo($exception->getMessage())
                    ->isEqualTo('foo bar');
    }

    public function case_get_from_object()
    {
        $this
            ->given($exception = new SUT('foo'))
            ->when($result = $exception->getFrom())
            ->then
                ->string($result)
                    ->isEqualTo(__METHOD__ . '()');
    }

    public function case_raise()
    {
        $this
            ->given($exception = new SUT('foo'), $line = __LINE__)
            ->when($result = $exception->raise())
            ->then
                ->string($result)
                    ->isEqualTo(
                        __METHOD__ . '(): (0) foo' . "\n" .
                        'in ' . __FILE__ . ' at line ' . $line . '.'
                    );
    }

    public function case_raise_with_previous()
    {
        $this
            ->given(
                $previous  = new SUT('previous'), $previousLine = __LINE__,
                $exception = new SUT('foo', 0, [], $previous), $line = __LINE__
            )
            ->when($result = $exception->raise(true))
            ->then
                ->string($result)
                    ->isEqualTo(
                        __METHOD__ . '(): (0) foo' . "\n" .
                        'in ' . __FILE__ . ' at line ' . $line . '.' . "\n\n" .
                        '    ⬇' . "\n\n" .
                        'Nested exception (' . get_class($previous) . '):' . "\n" .
                        __METHOD__ . '(): (0) previous' . "\n" .
                        'in ' . __FILE__ . ' at line ' . $previousLine . '.'
                    );
    }

    public function case_uncaught()
    {
        $this
            ->given(
                $this->function->ob_get_level = 0,
                $exception = new SUT('foo'), $line = __LINE__
            )
            ->when($result = SUT::uncaught($exception))
            ->then
                ->variable($result)
                    ->isNull()
                ->output
                    ->isEqualTo(
                        'Uncaught exception (' . get_class($exception) . '):' . "\n" .
                        __METHOD__ . '(): (0) foo' . "\n" .
                        'in ' . __FILE__ . ' at line ' . $line . '.'
                    );
    }

    public function case_uncaught_not_Hoa()
    {
        $this
            ->exception(function () {
                SUT::uncaught(new \Exception('foo'));
            })
                ->isInstanceOf('Exception')
            ->output
                ->isEmpty();
    }

    public function case_to_string()
    {
        $this
            ->given($exception = new SUT('foo'))
            ->when($result = $exception->__toString())
            ->then
                ->string($result)
                    ->isEqualTo($exception->raise());
    }

    public function case_disable_uncaught_handler()
    {
        $this
            ->given(
                $this->function->restore_exception_handler = function () use (&$called) {
                    $called = true;

                    return null;
                }
            )
            ->when($result = SUT::enableUncaughtHandler(false))
            ->then
                ->variable($result)
                    ->isNull()
                ->boolean($called)
                    ->isTrue();
    }

    public function case_enable_uncaught_handler()
    {
        $self = $this;

        $this
            ->given(
                $this->function->set_exception_handler = function ($handler) use ($self, &$called) {
                    $called = true;

                    $self
                        ->object($handler)
                            ->isInstanceOf('Closure')
                        ->let($reflection = new \ReflectionObject($handler))
                        ->array($invokeParameters = $reflection->getMethod('__invoke')->getParameters())
                            ->hasSize(1)
                        ->string($invokeParameters[0]->getName())
                            ->isEqualTo('exception');

                    return null;
                }
            )
            ->when($result = SUT::enableUncaughtHandler())
            ->then
                ->variable($result)
                    ->isNull()
                ->boolean($called)
                    ->isTrue();
    }
}


hoa/exception

description: The Hoa\Exception library.

keywords: library exception


intervention/image

description: Image handling and manipulation library with support for Laravel integration

keywords: image gd imagick laravel watermark thumbnail

license: MIT

ext-gd: to use GD library based image processing.

ext-imagick: to use Imagick based image processing.

intervention/imagecache: Caching extension for the Intervention Image library

Intervention\Image\: src/Intervention/Image

Image: Intervention\Image\Facades\Image

minimum-stability: stable


alias: Image

facade: Intervention\Image\Facades\Image