File: test_context.py

package info (click to toggle)
app-model 0.5.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 700 kB
  • sloc: python: 5,484; makefile: 4
file content (147 lines) | stat: -rw-r--r-- 3,708 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
import gc
from unittest.mock import Mock

import pytest

from app_model.expressions import Context, create_context, get_context
from app_model.expressions._context import _OBJ_TO_CONTEXT


def test_create_context() -> None:
    """You can create a context for any object"""

    class T: ...

    t = T()
    tid = id(t)
    ctx = create_context(t)
    assert get_context(t) == ctx
    assert hash(ctx)  # hashable
    assert tid in _OBJ_TO_CONTEXT
    _OBJ_TO_CONTEXT.pop(tid)

    del t
    gc.collect()
    assert tid not in _OBJ_TO_CONTEXT

    # you can provide your own root, but it must be a context
    create_context(T(), root=Context())
    with pytest.raises(AssertionError):
        create_context(T(), root={})  # type: ignore


def test_create_and_get_scoped_contexts() -> None:
    """Test that objects created in the stack of another contexted object.

    likely the most common way that this API will be used:
    """
    before = len(_OBJ_TO_CONTEXT)

    class A:
        def __init__(self) -> None:
            create_context(self)
            self.b = B()

    class B:
        def __init__(self) -> None:
            create_context(self)

    obja = A()
    assert len(_OBJ_TO_CONTEXT) == before + 2
    ctxa = get_context(obja)
    ctxb = get_context(obja.b)
    assert ctxa is not None
    assert ctxb is not None
    ctxa["hi"] = "hi"
    assert ctxb["hi"] == "hi"

    # keys get deleted on object deletion
    del obja
    gc.collect()
    assert len(_OBJ_TO_CONTEXT) == before


def test_context_events() -> None:
    """Changing context keys emits an event"""
    mock = Mock()
    root = Context()
    scoped = root.new_child()
    scoped.changed.connect(mock)  # connect the mock to the child

    root["a"] = 1
    # child re-emits parent events
    assert mock.call_args[0][0] == {"a"}

    mock.reset_mock()
    scoped["b"] = 1
    # also emits own events
    assert mock.call_args[0][0] == {"b"}

    mock.reset_mock()
    del scoped["b"]
    assert mock.call_args[0][0] == {"b"}

    # but parent does not emit child events
    mock.reset_mock()
    mock2 = Mock()
    root.changed.connect(mock2)
    scoped["c"] = "c"
    mock.assert_called_once()
    mock2.assert_not_called()

    mock.reset_mock()
    with scoped.buffered_changes():
        scoped["d"] = "d"
        scoped["e"] = "f"
        scoped["f"] = "f"
    mock.assert_called_once_with({"d", "e", "f"})

    # check events properly propagated when adding already existing context as child
    mock3 = Mock()
    root2a = Context()
    root2b = Context()
    scoped2 = root2a.new_child(root2b)
    scoped2.changed.connect(mock3)  # connect the mock to the child

    root2a["a"] = 1
    # child re-emits parent events
    assert mock3.call_args[0][0] == {"a"}

    mock3.reset_mock()
    root2b["b"] = 1
    # child re-emits added events
    assert mock3.call_args[0][0] == {"b"}

    mock3.reset_mock()
    scoped2["c"] = 1
    # also emits own events
    assert mock3.call_args[0][0] == {"c"}

    # check events properly propagated when making a context of contexts
    mock4 = Mock()
    root3a = Context()
    root3b = Context()
    root3c = Context()
    root3d = Context()
    root3e = Context()
    combined = Context(root3a, root3b, root3c, root3d, root3e)
    combined.changed.connect(mock4)

    root3a["a"] = 1
    assert mock4.call_args[0][0] == {"a"}

    mock4.reset_mock()
    root3b["b"] = 1
    assert mock4.call_args[0][0] == {"b"}

    mock4.reset_mock()
    root3c["c"] = 1
    assert mock4.call_args[0][0] == {"c"}

    mock4.reset_mock()
    root3d["d"] = 1
    assert mock4.call_args[0][0] == {"d"}

    mock4.reset_mock()
    root3e["e"] = 1
    assert mock4.call_args[0][0] == {"e"}