File: test_call_ordering.py

package info (click to toggle)
napari-plugin-engine 0.2.0-4
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 432 kB
  • sloc: python: 3,052; makefile: 21
file content (167 lines) | stat: -rw-r--r-- 5,142 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import pytest

from napari_plugin_engine import (
    HookImplementationMarker,
    HookSpecificationMarker,
    PluginManager,
)

dummy_hook_implementation = HookImplementationMarker("dummy")
dummy_hook_specification = HookSpecificationMarker("dummy")


class MySpec:
    @dummy_hook_specification
    def myhook(self):
        pass


class Plugin_1:
    @dummy_hook_implementation
    def myhook(self):
        return "p1"


class Plugin_2:
    @dummy_hook_implementation(tryfirst=True)
    def myhook(self):
        return "p2"


class Plugin_3:
    @dummy_hook_implementation
    def myhook(self):
        return "p3"


class Wrapper:
    @dummy_hook_implementation(hookwrapper=True)
    def myhook(self):
        yield


p1, p2, p3, wrapper = Plugin_1(), Plugin_2(), Plugin_3(), Wrapper()


@pytest.fixture
def dummy_plugin_manager():
    plugin_manager = PluginManager("dummy")
    plugin_manager.add_hookspecs(MySpec)
    plugin_manager.register(p1, name='p1')
    plugin_manager.register(p2, name='p2')
    plugin_manager.register(p3, name='p3')
    plugin_manager.register(wrapper, name='wrapper')
    return plugin_manager


# p2 is first because it was declared with tryfirst=True
# p3 is second because of "last-in-first-out" order
START_ORDER = ['p2', 'p3', 'p1']


@pytest.mark.parametrize(
    'order, expected_result',
    [
        ([], START_ORDER),
        (['p2'], START_ORDER),
        (['p2', 'p3'], START_ORDER),
        (['p1', 'p2', 'p3'], ['p1', 'p2', 'p3']),
        (['p1', 'p3', 'p2'], ['p1', 'p3', 'p2']),
        (['p1', 'p3'], ['p1', 'p3', 'p2']),
        (['p1'], ['p1', 'p2', 'p3']),
        (['p3'], ['p3', 'p2', 'p1']),
    ],
)
def test_reordering_hook_caller(dummy_plugin_manager, order, expected_result):
    """Test that the permute_hook_implementations function reorders hooks."""
    hook_caller = dummy_plugin_manager.hooks.myhook

    assert hook_caller() == START_ORDER
    hook_caller.bring_to_front(order)
    assert hook_caller() == expected_result
    # return to original order
    hook_caller.bring_to_front(START_ORDER)
    assert hook_caller() == START_ORDER

    # try again using HookImplementation INSTANCES instead of plugin names
    instances = [hook_caller.get_plugin_implementation(i) for i in order]
    hook_caller.bring_to_front(instances)
    assert hook_caller() == expected_result


def test_reordering_hook_caller_raises(dummy_plugin_manager):
    """Test that invalid calls to permute_hook_implementations raise errors."""
    hook_caller = dummy_plugin_manager.hooks.myhook

    with pytest.raises(TypeError):
        # all items must be the name of a plugin, or a HookImplementation instance
        hook_caller.bring_to_front([1, 2])

    with pytest.raises(ValueError):
        # 'wrapper' is the name of a plugin that provides an implementation...
        # but it is a hookwrappers which is not valid for `bring_to_front`
        hook_caller.bring_to_front(['p1', 'wrapper'])

    with pytest.raises(ValueError):
        # 'p4' is not in the list
        hook_caller.bring_to_front(['p1', 'p4'])

    with pytest.raises(ValueError):
        # duplicate entries are not allowed
        hook_caller.bring_to_front(['p1', 'p1', 'p2'])

    with pytest.raises(ValueError):
        # too many values
        hook_caller.bring_to_front(['p1', 'p1', 'p2', 'p4', 'p3', 'p1'])

    with pytest.raises(TypeError):
        # it has to be a list
        hook_caller.bring_to_front('p1')


def test_hook_caller_kwargs(dummy_plugin_manager):
    hook_caller = dummy_plugin_manager.hooks.myhook
    assert hook_caller() == ['p2', 'p3', 'p1']

    for p in ['p2', 'p3', 'p1']:
        # call with a specific plugin
        assert hook_caller(_plugin=p) == p
        impl = hook_caller.get_plugin_implementation(p)
        # call without specific plugins/impls
        expected = ['p2', 'p3', 'p1']
        expected.remove(p)
        assert hook_caller(_skip_impls=[impl]) == expected


def test_disable_impls(dummy_plugin_manager):
    hook_caller = dummy_plugin_manager.hooks.myhook
    assert hook_caller() == ['p2', 'p3', 'p1']

    for p in ['p2', 'p3', 'p1']:
        # call with a specific plugin
        expected = ['p2', 'p3', 'p1']
        assert hook_caller() == expected
        assert hook_caller(_plugin=p) == p
        assert hook_caller(_plugin=p) == hook_caller._call_plugin(p)
        impl = hook_caller.get_plugin_implementation(p)
        # call without specific plugins/impls
        expected.remove(p)
        assert hook_caller(_skip_impls=[impl]) == expected
        hook_caller.disable_plugin(p)
        assert hook_caller() == expected
        hook_caller.enable_plugin(p)
        assert set(hook_caller()) == set([p] + expected)

    with pytest.raises(TypeError):
        # cannot call wrappers directly
        hook_caller(_plugin='wrapper')


def test_get_plugin_implementation(dummy_plugin_manager):
    hook_caller = dummy_plugin_manager.hooks.myhook
    impl = hook_caller.get_plugin_implementation('p1')
    assert impl
    assert impl.plugin_name == 'p1'

    with pytest.raises(KeyError):
        hook_caller.get_plugin_implementation('pasdfsdf1')