Ejemplo n.º 1
0
        def prepare_hooks():
            """Set up various hooks to test clearing only some of them."""
            callbacks = CallbackDict()
            sequence = []

            for when in ('before', 'after', 'around'):
                add_callback = CallbackDecorator(callbacks, when).all
                if when == 'around':
                    hook = self.around_hook(sequence)
                else:
                    hook = self.before_after_hook(sequence, when)

                # Default priority class
                add_callback(hook('Default'))

                # Default priority class, specifying a name
                add_callback(hook('Named'), name='named')

                # Different priority classes
                CallbackDecorator(callbacks, when,
                                  priority_class=-1).all(hook('Minus'))
                CallbackDecorator(callbacks, when,
                                  priority_class=1).all(hook('Plus'))

                # Different priority class, specifying a name
                CallbackDecorator(callbacks, when,
                                  priority_class=1).all(hook('PlusNamed'),
                                                        name='named')

            return callbacks, sequence
Ejemplo n.º 2
0
        def prepare_hooks():
            """Set up various hooks to test clearing only some of them."""
            callbacks = CallbackDict()
            sequence = []

            for when in ("before", "after", "around"):
                add_callback = CallbackDecorator(callbacks, when).all
                if when == "around":
                    hook = self.around_hook(sequence)
                else:
                    hook = self.before_after_hook(sequence, when)

                # Default priority class
                add_callback(hook("Default"))

                # Default priority class, specifying a name
                add_callback(hook("Named"), name="named")

                # Different priority classes
                CallbackDecorator(callbacks, when,
                                  priority_class=-1).all(hook("Minus"))
                CallbackDecorator(callbacks, when,
                                  priority_class=1).all(hook("Plus"))

                # Different priority class, specifying a name
                CallbackDecorator(callbacks, when,
                                  priority_class=1).all(hook("PlusNamed"),
                                                        name="named")

            return callbacks, sequence
Ejemplo n.º 3
0
    def setUp(self):
        self.callbacks = CallbackDict()

        self.before = CallbackDecorator(self.callbacks, 'before')
        self.around = CallbackDecorator(self.callbacks, 'around')
        self.after = CallbackDecorator(self.callbacks, 'after')
Ejemplo n.º 4
0
class CallbackDictTest(unittest.TestCase):
    """
    Test callback dictionary.
    """

    def setUp(self):
        self.callbacks = CallbackDict()

        self.before = CallbackDecorator(self.callbacks, 'before')
        self.around = CallbackDecorator(self.callbacks, 'around')
        self.after = CallbackDecorator(self.callbacks, 'after')

    def test_wrap(self):
        """
        Test wrapping functions.
        """

        sequence = []

        self.before.all(appender(sequence, 'before'))

        self.around.all(before_after(
            appender(sequence, 'around_before'),
            appender(sequence, 'around_after')
        ))

        self.after.all(appender(sequence, 'after'))

        wrapped = appender(sequence, 'wrapped')

        wrap = self.callbacks.wrap('all', wrapped, 'hook_arg1', 'hook_arg2')

        wrap('wrap_arg1', 'wrap_arg2')

        self.assertEqual(sequence, [
            ('before', 'hook_arg1', 'hook_arg2'),
            ('around_before', 'hook_arg1', 'hook_arg2'),
            ('wrapped', 'wrap_arg1', 'wrap_arg2'),
            ('around_after', 'hook_arg1', 'hook_arg2'),
            ('after', 'hook_arg1', 'hook_arg2'),
        ])

    def test_before_after(self):
        """
        Test before_after.
        """

        sequence = []

        self.before.all(appender(sequence, 'before'))

        self.around.all(before_after(
            appender(sequence, 'around_before'),
            appender(sequence, 'around_after')
        ))

        self.after.all(appender(sequence, 'after'))

        before, after = self.callbacks.before_after('all')

        before('before_arg1', 'before_arg2')
        after('after_arg1', 'after_arg2')

        self.assertEqual(sequence, [
            ('before', 'before_arg1', 'before_arg2'),
            ('around_before', 'before_arg1', 'before_arg2'),
            ('around_after', 'before_arg1', 'before_arg2'),
            ('after', 'after_arg1', 'after_arg2'),
        ])

    @staticmethod
    def before_after_hook(sequence, when):
        """A before/after hook appending to a sequence."""
        return lambda name: appender(sequence, when + name)

    @classmethod
    def around_hook(cls, sequence):
        """An around hook appending to a sequence."""
        return lambda name: before_after(
            cls.before_after_hook(sequence, 'around_before')(name),
            cls.before_after_hook(sequence, 'around_after')(name)
        )

    def test_priority(self):
        """
        Test callback priority.
        """

        self.maxDiff = None

        sequence = []

        for when in ('before', 'after', 'around'):
            add_callback = getattr(self, when).all
            if when == 'around':
                hook = self.around_hook(sequence)
            else:
                hook = self.before_after_hook(sequence, when)

            # Default priority is 0
            add_callback(hook('B1'))
            add_callback(hook('B2'))

            # Explicit lower (=earlier) priority
            add_callback(hook('A1'), priority=-10)
            add_callback(hook('A2'), priority=-10)

            # Explicit higher (=later) priority
            add_callback(hook('C1'), priority=10)
            add_callback(hook('C2'), priority=10)

            # Add a callback with a different priority class
            CallbackDecorator(self.callbacks, when,
                              priority_class=-1).all(hook('Z1'))
            CallbackDecorator(self.callbacks, when,
                              priority_class=1).all(hook('D1'))

        wrap = self.callbacks.wrap('all', appender(sequence, 'wrapped'))

        wrap()

        self.assertEqual([item for (item,) in sequence], [
            'beforeZ1',
            'beforeA1',
            'beforeA2',
            'beforeB1',
            'beforeB2',
            'beforeC1',
            'beforeC2',
            'beforeD1',

            'around_beforeZ1',
            'around_beforeA1',
            'around_beforeA2',
            'around_beforeB1',
            'around_beforeB2',
            'around_beforeC1',
            'around_beforeC2',
            'around_beforeD1',

            'wrapped',

            'around_afterD1',
            'around_afterC2',
            'around_afterC1',
            'around_afterB2',
            'around_afterB1',
            'around_afterA2',
            'around_afterA1',
            'around_afterZ1',

            'afterD1',
            'afterC2',
            'afterC1',
            'afterB2',
            'afterB1',
            'afterA2',
            'afterA1',
            'afterZ1',
        ])

    def test_clear(self):
        """
        Test clearing the registry.
        """

        def prepare_hooks():
            """Set up various hooks to test clearing only some of them."""
            callbacks = CallbackDict()
            sequence = []

            for when in ('before', 'after', 'around'):
                add_callback = CallbackDecorator(callbacks, when).all
                if when == 'around':
                    hook = self.around_hook(sequence)
                else:
                    hook = self.before_after_hook(sequence, when)

                # Default priority class
                add_callback(hook('Default'))

                # Default priority class, specifying a name
                add_callback(hook('Named'), name='named')

                # Different priority classes
                CallbackDecorator(callbacks, when,
                                  priority_class=-1).all(hook('Minus'))
                CallbackDecorator(callbacks, when,
                                  priority_class=1).all(hook('Plus'))

                # Different priority class, specifying a name
                CallbackDecorator(callbacks, when,
                                  priority_class=1).all(hook('PlusNamed'),
                                                        name='named')

            return callbacks, sequence

        # Verify ordering without clearing anything
        callbacks, sequence = prepare_hooks()
        callbacks.wrap('all', appender(sequence, 'wrapped'))()

        self.assertEqual([item for (item,) in sequence], [
            'beforeMinus',
            'beforeDefault',
            'beforeNamed',
            'beforePlus',
            'beforePlusNamed',

            'around_beforeMinus',
            'around_beforeDefault',
            'around_beforeNamed',
            'around_beforePlus',
            'around_beforePlusNamed',

            'wrapped',

            'around_afterPlusNamed',
            'around_afterPlus',
            'around_afterNamed',
            'around_afterDefault',
            'around_afterMinus',

            'afterPlusNamed',
            'afterPlus',
            'afterNamed',
            'afterDefault',
            'afterMinus',
        ])

        # Only clear a particular name from the default priority class
        callbacks, sequence = prepare_hooks()
        callbacks.clear(priority_class=PriorityClass.USER,
                        name='named')
        callbacks.wrap('all', appender(sequence, 'wrapped'))()

        self.assertEqual([item for (item,) in sequence], [
            'beforeMinus',
            'beforeDefault',
            'beforePlus',
            'beforePlusNamed',

            'around_beforeMinus',
            'around_beforeDefault',
            'around_beforePlus',
            'around_beforePlusNamed',

            'wrapped',

            'around_afterPlusNamed',
            'around_afterPlus',
            'around_afterDefault',
            'around_afterMinus',

            'afterPlusNamed',
            'afterPlus',
            'afterDefault',
            'afterMinus',
        ])

        # Only clear the default priority class
        callbacks, sequence = prepare_hooks()
        callbacks.clear(priority_class=PriorityClass.USER)
        callbacks.wrap('all', appender(sequence, 'wrapped'))()

        self.assertEqual([item for (item,) in sequence], [
            'beforeMinus',
            'beforePlus',
            'beforePlusNamed',

            'around_beforeMinus',
            'around_beforePlus',
            'around_beforePlusNamed',

            'wrapped',

            'around_afterPlusNamed',
            'around_afterPlus',
            'around_afterMinus',

            'afterPlusNamed',
            'afterPlus',
            'afterMinus',
        ])

        # Clear all callbacks
        callbacks, sequence = prepare_hooks()
        callbacks.clear()
        callbacks.wrap('all', appender(sequence, 'wrapped'))()

        self.assertEqual([item for (item,) in sequence], [
            'wrapped',
        ])
Ejemplo n.º 5
0
from aloe.registry import (
    CallbackDecorator,
    CALLBACK_REGISTRY,
    PriorityClass,
)
from aloe.parser import replace_vars
from aloe.strings import ljust, represent_table
from aloe.tools import hook_not_reentrant
from aloe.utils import memoizedproperty, PY3
from nose.result import TextTestResult

# A decorator to add callbacks which wrap the steps looser than all the other
# callbacks.
# pylint:disable=invalid-name
outer_around = CallbackDecorator(CALLBACK_REGISTRY,
                                 'around',
                                 priority_class=PriorityClass.DISPLAY)
# pylint:enable=invalid-name

# Global reference to the Terminal:
# This exists because the hooks have to be registered before the test is
# started, which is when the stream is passed in.
TERMINAL = [None]


class Terminal(blessings.Terminal):
    """
    Wrapped Terminal object for display hooks.

     * Adds additional features: write and writeln.
     * Adds a decorator to require the terminal global being set and
Ejemplo n.º 6
0
            name: method
            for name, method in cls.__dict__.items()
            if getattr(method, 'is_example', False)
        }

        with_indices = [(method.scenario_index, name)
                        for name, method in scenarios.items()]

        return sorted(with_indices)


# A decorator to add callbacks which wrap the steps tighter than all the user
# callbacks.
# pylint:disable=invalid-name
inner_around = CallbackDecorator(CALLBACK_REGISTRY,
                                 'around',
                                 priority_class=PriorityClass.SYSTEM_INNER)
# pylint:enable=invalid-name


@inner_around.each_step
@contextmanager
def set_passed_failed(step):
    """
    Set the 'failed' property of the step.
    """

    try:
        yield
        step.passed = True
        step.failed = False
Ejemplo n.º 7
0
    def test_priority(self):
        """
        Test callback priority.
        """

        self.maxDiff = None

        sequence = []

        for when in ('before', 'after', 'around'):
            add_callback = getattr(self, when).all
            if when == 'around':
                hook = self.around_hook(sequence)
            else:
                hook = self.before_after_hook(sequence, when)

            # Default priority is 0
            add_callback(hook('B1'))
            add_callback(hook('B2'))

            # Explicit lower (=earlier) priority
            add_callback(hook('A1'), priority=-10)
            add_callback(hook('A2'), priority=-10)

            # Explicit higher (=later) priority
            add_callback(hook('C1'), priority=10)
            add_callback(hook('C2'), priority=10)

            # Add a callback with a different priority class
            CallbackDecorator(self.callbacks, when,
                              priority_class=-1).all(hook('Z1'))
            CallbackDecorator(self.callbacks, when,
                              priority_class=1).all(hook('D1'))

        wrap = self.callbacks.wrap('all', appender(sequence, 'wrapped'))

        wrap()

        self.assertEqual([item for (item,) in sequence], [
            'beforeZ1',
            'beforeA1',
            'beforeA2',
            'beforeB1',
            'beforeB2',
            'beforeC1',
            'beforeC2',
            'beforeD1',

            'around_beforeZ1',
            'around_beforeA1',
            'around_beforeA2',
            'around_beforeB1',
            'around_beforeB2',
            'around_beforeC1',
            'around_beforeC2',
            'around_beforeD1',

            'wrapped',

            'around_afterD1',
            'around_afterC2',
            'around_afterC1',
            'around_afterB2',
            'around_afterB1',
            'around_afterA2',
            'around_afterA1',
            'around_afterZ1',

            'afterD1',
            'afterC2',
            'afterC1',
            'afterB2',
            'afterB1',
            'afterA2',
            'afterA1',
            'afterZ1',
        ])
Ejemplo n.º 8
0
    def setUp(self):
        self.callbacks = CallbackDict()

        self.before = CallbackDecorator(self.callbacks, 'before')
        self.around = CallbackDecorator(self.callbacks, 'around')
        self.after = CallbackDecorator(self.callbacks, 'after')
Ejemplo n.º 9
0
class CallbackDictTest(unittest.TestCase):
    """
    Test callback dictionary.
    """

    def setUp(self):
        self.callbacks = CallbackDict()

        self.before = CallbackDecorator(self.callbacks, 'before')
        self.around = CallbackDecorator(self.callbacks, 'around')
        self.after = CallbackDecorator(self.callbacks, 'after')

    def test_wrap(self):
        """
        Test wrapping functions.
        """

        sequence = []

        self.before.all(appender(sequence, 'before'))

        self.around.all(before_after(
            appender(sequence, 'around_before'),
            appender(sequence, 'around_after')
        ))

        self.after.all(appender(sequence, 'after'))

        wrapped = appender(sequence, 'wrapped')

        wrap = self.callbacks.wrap('all', wrapped, 'hook_arg1', 'hook_arg2')

        wrap('wrap_arg1', 'wrap_arg2')

        self.assertEqual(sequence, [
            ('before', 'hook_arg1', 'hook_arg2'),
            ('around_before', 'hook_arg1', 'hook_arg2'),
            ('wrapped', 'wrap_arg1', 'wrap_arg2'),
            ('around_after', 'hook_arg1', 'hook_arg2'),
            ('after', 'hook_arg1', 'hook_arg2'),
        ])

    def test_before_after(self):
        """
        Test before_after.
        """

        sequence = []

        self.before.all(appender(sequence, 'before'))

        self.around.all(before_after(
            appender(sequence, 'around_before'),
            appender(sequence, 'around_after')
        ))

        self.after.all(appender(sequence, 'after'))

        before, after = self.callbacks.before_after('all')

        before('before_arg1', 'before_arg2')
        after('after_arg1', 'after_arg2')

        self.assertEqual(sequence, [
            ('before', 'before_arg1', 'before_arg2'),
            ('around_before', 'before_arg1', 'before_arg2'),
            ('around_after', 'before_arg1', 'before_arg2'),
            ('after', 'after_arg1', 'after_arg2'),
        ])

    @staticmethod
    def before_after_hook(sequence, when):
        """A before/after hook appending to a sequence."""
        return lambda name: appender(sequence, when + name)

    @classmethod
    def around_hook(cls, sequence):
        """An around hook appending to a sequence."""
        return lambda name: before_after(
            cls.before_after_hook(sequence, 'around_before')(name),
            cls.before_after_hook(sequence, 'around_after')(name)
        )

    def test_priority(self):
        """
        Test callback priority.
        """

        self.maxDiff = None

        sequence = []

        for when in ('before', 'after', 'around'):
            add_callback = getattr(self, when).all
            if when == 'around':
                hook = self.around_hook(sequence)
            else:
                hook = self.before_after_hook(sequence, when)

            # Default priority is 0
            add_callback(hook('B1'))
            add_callback(hook('B2'))

            # Explicit lower (=earlier) priority
            add_callback(hook('A1'), priority=-10)
            add_callback(hook('A2'), priority=-10)

            # Explicit higher (=later) priority
            add_callback(hook('C1'), priority=10)
            add_callback(hook('C2'), priority=10)

            # Add a callback with a different priority class
            CallbackDecorator(self.callbacks, when,
                              priority_class=-1).all(hook('Z1'))
            CallbackDecorator(self.callbacks, when,
                              priority_class=1).all(hook('D1'))

        wrap = self.callbacks.wrap('all', appender(sequence, 'wrapped'))

        wrap()

        self.assertEqual([item for (item,) in sequence], [
            'beforeZ1',
            'beforeA1',
            'beforeA2',
            'beforeB1',
            'beforeB2',
            'beforeC1',
            'beforeC2',
            'beforeD1',

            'around_beforeZ1',
            'around_beforeA1',
            'around_beforeA2',
            'around_beforeB1',
            'around_beforeB2',
            'around_beforeC1',
            'around_beforeC2',
            'around_beforeD1',

            'wrapped',

            'around_afterD1',
            'around_afterC2',
            'around_afterC1',
            'around_afterB2',
            'around_afterB1',
            'around_afterA2',
            'around_afterA1',
            'around_afterZ1',

            'afterD1',
            'afterC2',
            'afterC1',
            'afterB2',
            'afterB1',
            'afterA2',
            'afterA1',
            'afterZ1',
        ])

    def test_clear(self):
        """
        Test clearing the registry.
        """

        def prepare_hooks():
            """Set up various hooks to test clearing only some of them."""
            callbacks = CallbackDict()
            sequence = []

            for when in ('before', 'after', 'around'):
                add_callback = CallbackDecorator(callbacks, when).all
                if when == 'around':
                    hook = self.around_hook(sequence)
                else:
                    hook = self.before_after_hook(sequence, when)

                # Default priority class
                add_callback(hook('Default'))

                # Default priority class, specifying a name
                add_callback(hook('Named'), name='named')

                # Different priority classes
                CallbackDecorator(callbacks, when,
                                  priority_class=-1).all(hook('Minus'))
                CallbackDecorator(callbacks, when,
                                  priority_class=1).all(hook('Plus'))

                # Different priority class, specifying a name
                CallbackDecorator(callbacks, when,
                                  priority_class=1).all(hook('PlusNamed'),
                                                        name='named')

            return callbacks, sequence

        # Verify ordering without clearing anything
        callbacks, sequence = prepare_hooks()
        callbacks.wrap('all', appender(sequence, 'wrapped'))()

        self.assertEqual([item for (item,) in sequence], [
            'beforeMinus',
            'beforeDefault',
            'beforeNamed',
            'beforePlus',
            'beforePlusNamed',

            'around_beforeMinus',
            'around_beforeDefault',
            'around_beforeNamed',
            'around_beforePlus',
            'around_beforePlusNamed',

            'wrapped',

            'around_afterPlusNamed',
            'around_afterPlus',
            'around_afterNamed',
            'around_afterDefault',
            'around_afterMinus',

            'afterPlusNamed',
            'afterPlus',
            'afterNamed',
            'afterDefault',
            'afterMinus',
        ])

        # Only clear a particular name from the default priority class
        callbacks, sequence = prepare_hooks()
        callbacks.clear(priority_class=PriorityClass.USER,
                        name='named')
        callbacks.wrap('all', appender(sequence, 'wrapped'))()

        self.assertEqual([item for (item,) in sequence], [
            'beforeMinus',
            'beforeDefault',
            'beforePlus',
            'beforePlusNamed',

            'around_beforeMinus',
            'around_beforeDefault',
            'around_beforePlus',
            'around_beforePlusNamed',

            'wrapped',

            'around_afterPlusNamed',
            'around_afterPlus',
            'around_afterDefault',
            'around_afterMinus',

            'afterPlusNamed',
            'afterPlus',
            'afterDefault',
            'afterMinus',
        ])

        # Only clear the default priority class
        callbacks, sequence = prepare_hooks()
        callbacks.clear(priority_class=PriorityClass.USER)
        callbacks.wrap('all', appender(sequence, 'wrapped'))()

        self.assertEqual([item for (item,) in sequence], [
            'beforeMinus',
            'beforePlus',
            'beforePlusNamed',

            'around_beforeMinus',
            'around_beforePlus',
            'around_beforePlusNamed',

            'wrapped',

            'around_afterPlusNamed',
            'around_afterPlus',
            'around_afterMinus',

            'afterPlusNamed',
            'afterPlus',
            'afterMinus',
        ])

        # Clear all callbacks
        callbacks, sequence = prepare_hooks()
        callbacks.clear()
        callbacks.wrap('all', appender(sequence, 'wrapped'))()

        self.assertEqual([item for (item,) in sequence], [
            'wrapped',
        ])
Ejemplo n.º 10
0
    def test_priority(self):
        """
        Test callback priority.
        """

        self.maxDiff = None

        sequence = []

        for when in ("before", "after", "around"):
            add_callback = getattr(self, when).all
            if when == "around":
                hook = self.around_hook(sequence)
            else:
                hook = self.before_after_hook(sequence, when)

            # Default priority is 0
            add_callback(hook("B1"))
            add_callback(hook("B2"))

            # Explicit lower (=earlier) priority
            add_callback(hook("A1"), priority=-10)
            add_callback(hook("A2"), priority=-10)

            # Explicit higher (=later) priority
            add_callback(hook("C1"), priority=10)
            add_callback(hook("C2"), priority=10)

            # Add a callback with a different priority class
            CallbackDecorator(self.callbacks, when,
                              priority_class=-1).all(hook("Z1"))
            CallbackDecorator(self.callbacks, when,
                              priority_class=1).all(hook("D1"))

        wrap = self.callbacks.wrap("all", appender(sequence, "wrapped"))

        wrap()

        self.assertEqual(
            [item for (item, ) in sequence],
            [
                "beforeZ1",
                "beforeA1",
                "beforeA2",
                "beforeB1",
                "beforeB2",
                "beforeC1",
                "beforeC2",
                "beforeD1",
                "around_beforeZ1",
                "around_beforeA1",
                "around_beforeA2",
                "around_beforeB1",
                "around_beforeB2",
                "around_beforeC1",
                "around_beforeC2",
                "around_beforeD1",
                "wrapped",
                "around_afterD1",
                "around_afterC2",
                "around_afterC1",
                "around_afterB2",
                "around_afterB1",
                "around_afterA2",
                "around_afterA1",
                "around_afterZ1",
                "afterD1",
                "afterC2",
                "afterC1",
                "afterB2",
                "afterB1",
                "afterA2",
                "afterA1",
                "afterZ1",
            ],
        )
Ejemplo n.º 11
0
    def setUp(self):
        self.callbacks = CallbackDict()

        self.before = CallbackDecorator(self.callbacks, "before")
        self.around = CallbackDecorator(self.callbacks, "around")
        self.after = CallbackDecorator(self.callbacks, "after")
Ejemplo n.º 12
0
class CallbackDictTest(unittest.TestCase):
    """
    Test callback dictionary.
    """
    def setUp(self):
        self.callbacks = CallbackDict()

        self.before = CallbackDecorator(self.callbacks, "before")
        self.around = CallbackDecorator(self.callbacks, "around")
        self.after = CallbackDecorator(self.callbacks, "after")

    def test_wrap(self):
        """
        Test wrapping functions.
        """

        sequence = []

        self.before.all(appender(sequence, "before"))

        self.around.all(
            before_after(appender(sequence, "around_before"),
                         appender(sequence, "around_after")))

        self.after.all(appender(sequence, "after"))

        wrapped = appender(sequence, "wrapped")

        wrap = self.callbacks.wrap("all", wrapped, "hook_arg1", "hook_arg2")

        wrap("wrap_arg1", "wrap_arg2")

        self.assertEqual(
            sequence,
            [
                ("before", "hook_arg1", "hook_arg2"),
                ("around_before", "hook_arg1", "hook_arg2"),
                ("wrapped", "wrap_arg1", "wrap_arg2"),
                ("around_after", "hook_arg1", "hook_arg2"),
                ("after", "hook_arg1", "hook_arg2"),
            ],
        )

    def test_before_after(self):
        """
        Test before_after.
        """

        sequence = []

        self.before.all(appender(sequence, "before"))

        self.around.all(
            before_after(appender(sequence, "around_before"),
                         appender(sequence, "around_after")))

        self.after.all(appender(sequence, "after"))

        before, after = self.callbacks.before_after("all")

        before("before_arg1", "before_arg2")
        after("after_arg1", "after_arg2")

        self.assertEqual(
            sequence,
            [
                ("before", "before_arg1", "before_arg2"),
                ("around_before", "before_arg1", "before_arg2"),
                ("around_after", "before_arg1", "before_arg2"),
                ("after", "after_arg1", "after_arg2"),
            ],
        )

    @staticmethod
    def before_after_hook(sequence, when):
        """A before/after hook appending to a sequence."""
        return lambda name: appender(sequence, when + name)

    @classmethod
    def around_hook(cls, sequence):
        """An around hook appending to a sequence."""
        return lambda name: before_after(
            cls.before_after_hook(sequence, "around_before")(name),
            cls.before_after_hook(sequence, "around_after")(name),
        )

    def test_priority(self):
        """
        Test callback priority.
        """

        self.maxDiff = None

        sequence = []

        for when in ("before", "after", "around"):
            add_callback = getattr(self, when).all
            if when == "around":
                hook = self.around_hook(sequence)
            else:
                hook = self.before_after_hook(sequence, when)

            # Default priority is 0
            add_callback(hook("B1"))
            add_callback(hook("B2"))

            # Explicit lower (=earlier) priority
            add_callback(hook("A1"), priority=-10)
            add_callback(hook("A2"), priority=-10)

            # Explicit higher (=later) priority
            add_callback(hook("C1"), priority=10)
            add_callback(hook("C2"), priority=10)

            # Add a callback with a different priority class
            CallbackDecorator(self.callbacks, when,
                              priority_class=-1).all(hook("Z1"))
            CallbackDecorator(self.callbacks, when,
                              priority_class=1).all(hook("D1"))

        wrap = self.callbacks.wrap("all", appender(sequence, "wrapped"))

        wrap()

        self.assertEqual(
            [item for (item, ) in sequence],
            [
                "beforeZ1",
                "beforeA1",
                "beforeA2",
                "beforeB1",
                "beforeB2",
                "beforeC1",
                "beforeC2",
                "beforeD1",
                "around_beforeZ1",
                "around_beforeA1",
                "around_beforeA2",
                "around_beforeB1",
                "around_beforeB2",
                "around_beforeC1",
                "around_beforeC2",
                "around_beforeD1",
                "wrapped",
                "around_afterD1",
                "around_afterC2",
                "around_afterC1",
                "around_afterB2",
                "around_afterB1",
                "around_afterA2",
                "around_afterA1",
                "around_afterZ1",
                "afterD1",
                "afterC2",
                "afterC1",
                "afterB2",
                "afterB1",
                "afterA2",
                "afterA1",
                "afterZ1",
            ],
        )

    def test_clear(self):
        """
        Test clearing the registry.
        """
        def prepare_hooks():
            """Set up various hooks to test clearing only some of them."""
            callbacks = CallbackDict()
            sequence = []

            for when in ("before", "after", "around"):
                add_callback = CallbackDecorator(callbacks, when).all
                if when == "around":
                    hook = self.around_hook(sequence)
                else:
                    hook = self.before_after_hook(sequence, when)

                # Default priority class
                add_callback(hook("Default"))

                # Default priority class, specifying a name
                add_callback(hook("Named"), name="named")

                # Different priority classes
                CallbackDecorator(callbacks, when,
                                  priority_class=-1).all(hook("Minus"))
                CallbackDecorator(callbacks, when,
                                  priority_class=1).all(hook("Plus"))

                # Different priority class, specifying a name
                CallbackDecorator(callbacks, when,
                                  priority_class=1).all(hook("PlusNamed"),
                                                        name="named")

            return callbacks, sequence

        # Verify ordering without clearing anything
        callbacks, sequence = prepare_hooks()
        callbacks.wrap("all", appender(sequence, "wrapped"))()

        self.assertEqual(
            [item for (item, ) in sequence],
            [
                "beforeMinus",
                "beforeDefault",
                "beforeNamed",
                "beforePlus",
                "beforePlusNamed",
                "around_beforeMinus",
                "around_beforeDefault",
                "around_beforeNamed",
                "around_beforePlus",
                "around_beforePlusNamed",
                "wrapped",
                "around_afterPlusNamed",
                "around_afterPlus",
                "around_afterNamed",
                "around_afterDefault",
                "around_afterMinus",
                "afterPlusNamed",
                "afterPlus",
                "afterNamed",
                "afterDefault",
                "afterMinus",
            ],
        )

        # Only clear a particular name from the default priority class
        callbacks, sequence = prepare_hooks()
        callbacks.clear(priority_class=PriorityClass.USER, name="named")
        callbacks.wrap("all", appender(sequence, "wrapped"))()

        self.assertEqual(
            [item for (item, ) in sequence],
            [
                "beforeMinus",
                "beforeDefault",
                "beforePlus",
                "beforePlusNamed",
                "around_beforeMinus",
                "around_beforeDefault",
                "around_beforePlus",
                "around_beforePlusNamed",
                "wrapped",
                "around_afterPlusNamed",
                "around_afterPlus",
                "around_afterDefault",
                "around_afterMinus",
                "afterPlusNamed",
                "afterPlus",
                "afterDefault",
                "afterMinus",
            ],
        )

        # Only clear the default priority class
        callbacks, sequence = prepare_hooks()
        callbacks.clear(priority_class=PriorityClass.USER)
        callbacks.wrap("all", appender(sequence, "wrapped"))()

        self.assertEqual(
            [item for (item, ) in sequence],
            [
                "beforeMinus",
                "beforePlus",
                "beforePlusNamed",
                "around_beforeMinus",
                "around_beforePlus",
                "around_beforePlusNamed",
                "wrapped",
                "around_afterPlusNamed",
                "around_afterPlus",
                "around_afterMinus",
                "afterPlusNamed",
                "afterPlus",
                "afterMinus",
            ],
        )

        # Clear all callbacks
        callbacks, sequence = prepare_hooks()
        callbacks.clear()
        callbacks.wrap("all", appender(sequence, "wrapped"))()

        self.assertEqual([item for (item, ) in sequence], ["wrapped"])