aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_bell.py
blob: 27307fcadf40a6dbb8113c6d1ad0961bfb68f358 (plain) (blame)
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#!/usr/bin/env python3
"""A pane that rang, and the several places that have to say so.

A bell exists for the pane you are *not* looking at, so most of what matters is
where it shows up when the pane is off-screen, and that looking at it is what
puts it out.
"""
import os
import sys
import tempfile

from harness import Session, check, report

SH = ["/bin/sh", "-c", 'printf "\\033]2;p\\007"; stty raw -echo; cat']
MARK = "\u2022"


def cfg(text):
    f = tempfile.NamedTemporaryFile("w", suffix=".kdl", delete=False)
    f.write(text)
    f.close()
    return f.name


def lay(text):
    f = tempfile.NamedTemporaryFile("w", suffix=".kdl", delete=False)
    f.write(text)
    f.close()
    return f.name


TWO_PANES = 'layout {\n tab name="api" {\n  pane\n  pane\n }\n}\n'
TWO_TABS = ('layout {\n tab name="api" {\n  pane\n  pane\n }\n'
            ' tab name="notes" {\n  pane\n }\n}\n')


def ring(s, pane_id, back_to):
    """Ring the bell in a pane while looking at another one."""
    s.api("focus", id=pane_id)
    s.settle(10)
    s.raw(r"\x07")
    s.settle(30)
    s.api("focus", id=back_to)
    s.settle(30)


def titlebar(snap, p, n=10):
    return snap.line(p["y"])[p["x"]:p["x"] + n]


def test_a_bel_marks_the_pane_that_rang():
    l = lay(TWO_PANES)
    with Session(SH, cols=90, rows=16, layout=l) as s:
        s.settle(30)
        panes = s.panes()
        mine = [p for p in panes if p["focused"]][0]
        other = [p for p in panes if not p["focused"]][0]

        check("no mark before anything rings",
              MARK not in titlebar(s.snapshot(), other), "")

        ring(s, other["id"], mine["id"])
        check("the pane that rang is marked",
              MARK in titlebar(s.snapshot(), other),
              repr(titlebar(s.snapshot(), other)))
        check("and the pane that did not is untouched",
              MARK not in titlebar(s.snapshot(), mine),
              repr(titlebar(s.snapshot(), mine)))
    os.unlink(l)


def test_looking_at_it_is_what_puts_it_out():
    l = lay(TWO_PANES)
    with Session(SH, cols=90, rows=16, layout=l) as s:
        s.settle(30)
        panes = s.panes()
        mine = [p for p in panes if p["focused"]][0]
        other = [p for p in panes if not p["focused"]][0]

        ring(s, other["id"], mine["id"])
        check("rung and unlooked-at", MARK in titlebar(s.snapshot(), other), "")

        s.api("focus", id=other["id"])
        s.settle(30)
        check("focusing it clears the mark",
              MARK not in titlebar(s.snapshot(), other),
              repr(titlebar(s.snapshot(), other)))

        # It must stay out; a bell is not a thing that comes back on its own.
        s.api("focus", id=mine["id"])
        s.settle(30)
        check("and it stays cleared once answered",
              MARK not in titlebar(s.snapshot(), other),
              repr(titlebar(s.snapshot(), other)))
    os.unlink(l)


def test_a_bell_in_another_tab_shows_on_the_strip():
    """The case the indicator exists for: the pane is not on screen at all."""
    l = lay(TWO_TABS)
    with Session(SH, cols=90, rows=16, layout=l) as s:
        s.settle(30)
        tabs = s.tabs()
        here = [p for p in s.panes() if p["focused"]][0]
        away = [p for p in s.panes() if p["tab"] == 2][0]

        check("the strip is unmarked to start",
              MARK not in s.snapshot().line(1), repr(s.snapshot().line(1)))

        s.api("select-tab", id=tabs[1]["id"])
        s.settle(20)
        s.api("focus", id=away["id"])
        s.settle(10)
        s.raw(r"\x07")
        s.settle(30)
        s.api("select-tab", id=tabs[0]["id"])
        s.api("focus", id=here["id"])
        s.settle(30)

        strip = s.snapshot().line(1)
        check("the tab holding the rung pane is marked", MARK in strip,
              repr(strip))
        check("and the mark sits with that tab's own label",
              strip.index(MARK) > strip.index("notes"), repr(strip))

        s.api("select-tab", id=tabs[1]["id"])
        s.settle(30)
        check("visiting the tab clears it",
              MARK not in s.snapshot().line(1), repr(s.snapshot().line(1)))
    os.unlink(l)


def test_the_strip_and_the_pane_clear_together():
    """The acknowledgement happens before the frame is drawn, so the strip --
    which is painted first -- cannot spend a frame reporting a stale bell."""
    l = lay(TWO_PANES)
    with Session(SH, cols=90, rows=16, layout=l) as s:
        s.settle(30)
        panes = s.panes()
        mine = [p for p in panes if p["focused"]][0]
        other = [p for p in panes if not p["focused"]][0]
        ring(s, other["id"], mine["id"])

        s.api("focus", id=other["id"])
        s.settle(30)
        snap = s.snapshot()
        check("neither the titlebar nor the strip is left marked",
              MARK not in titlebar(snap, other) and MARK not in snap.line(1),
              repr(titlebar(snap, other)) + " / " + repr(snap.line(1)))
    os.unlink(l)


def test_it_can_be_turned_off_and_the_mark_chosen():
    l = lay(TWO_PANES)
    off = cfg("bell_indicator false\n")
    with Session(SH, cols=90, rows=16, config=off, layout=l) as s:
        s.settle(30)
        panes = s.panes()
        mine = [p for p in panes if p["focused"]][0]
        other = [p for p in panes if not p["focused"]][0]
        ring(s, other["id"], mine["id"])
        snap = s.snapshot()
        check("bell_indicator false says nothing anywhere",
              MARK not in titlebar(snap, other) and MARK not in snap.line(1),
              repr(titlebar(snap, other)))
    os.unlink(off)

    custom = cfg('bell_mark "!"\n')
    with Session(SH, cols=90, rows=16, config=custom, layout=l) as s:
        s.settle(30)
        panes = s.panes()
        mine = [p for p in panes if p["focused"]][0]
        other = [p for p in panes if not p["focused"]][0]
        ring(s, other["id"], mine["id"])
        check("a chosen mark is the one drawn",
              "!" in titlebar(s.snapshot(), other),
              repr(titlebar(s.snapshot(), other)))
    os.unlink(custom)
    os.unlink(l)


if __name__ == "__main__":
    test_a_bel_marks_the_pane_that_rang()
    test_looking_at_it_is_what_puts_it_out()
    test_a_bell_in_another_tab_shows_on_the_strip()
    test_the_strip_and_the_pane_clear_together()
    test_it_can_be_turned_off_and_the_mark_chosen()
    sys.exit(report())