Skip to content

Commit 1a7cd87

Browse files
committed
tests(hooks): add version compatibility tests
why: Document and test hook behavior differences between tmux < 3.0 and tmux 3.0+ to prevent regressions and help users understand the version requirements. what: - Add test_hooks_compat.py with detailed module docstring explaining the tmux hook architecture change in 3.0 - Test warning is emitted on < 3.0 using pytest.warns - Test no warning on 3.0+ using recwarn fixture - Test set_hooks array functionality on 3.0+ - Reference tmux commit dfb7bb68 in documentation Ref: tmux commit dfb7bb68 (April 2019) merged hooks into options as array options.
1 parent c4cbcd6 commit 1a7cd87

File tree

1 file changed

+203
-0
lines changed

1 file changed

+203
-0
lines changed

tests/test_hooks_compat.py

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
"""Tests for hook version compatibility.
2+
3+
This module tests hook behavior differences between tmux < 3.0 and tmux 3.0+.
4+
5+
tmux Hook Architecture Changes (3.0)
6+
------------------------------------
7+
Prior to tmux 3.0, hooks were stored in a red-black tree by exact name.
8+
Setting "session-renamed[0]" created a hook with that literal name (including
9+
brackets). The ``set-hook -u session-renamed`` command only removed a hook
10+
named exactly "session-renamed", NOT "session-renamed[0]".
11+
12+
In tmux 3.0 (commit dfb7bb68, April 2019), hooks were merged into the options
13+
tree as array options. Now "session-renamed[0]" is index 0 of the
14+
"session-renamed" array, and ``set-hook -u session-renamed`` clears all
15+
indices of that array.
16+
17+
This difference means that:
18+
- On tmux < 3.0: ``set_hooks()`` creates hooks with literal bracket names
19+
- On tmux < 3.0: ``unset_hook('session-renamed')`` does NOT clear indexed hooks
20+
- On tmux 3.0+: ``set_hooks()`` properly creates array entries
21+
- On tmux 3.0+: ``unset_hook('session-renamed')`` clears all array indices
22+
23+
References
24+
----------
25+
- tmux commit: dfb7bb68 (April 26, 2019)
26+
- tmux CHANGES 3.0: "Hooks are now stored in the options tree as array options"
27+
"""
28+
29+
from __future__ import annotations
30+
31+
import typing as t
32+
33+
import pytest
34+
35+
from libtmux._internal.sparse_array import SparseArray
36+
from libtmux.common import has_gte_version, has_lt_version
37+
38+
if t.TYPE_CHECKING:
39+
from libtmux.server import Server
40+
41+
42+
class TestSetHooksVersionWarning:
43+
"""Test set_hooks warning on tmux < 3.0."""
44+
45+
@pytest.mark.skipif(
46+
has_gte_version("3.0"),
47+
reason="Warning only emitted on tmux < 3.0",
48+
)
49+
def test_set_hooks_warns_on_old_tmux(self, server: Server) -> None:
50+
"""Verify set_hooks emits warning on tmux < 3.0.
51+
52+
On tmux < 3.0, hook arrays don't exist. The bracket syntax creates
53+
hooks with literal names like "session-renamed[0]" instead of array
54+
indices. This test verifies the warning is raised to alert users.
55+
"""
56+
session = server.new_session(session_name="test_warn")
57+
58+
with pytest.warns(
59+
UserWarning,
60+
match=r"Hook arrays require tmux 3\.0\+",
61+
):
62+
session.set_hooks("session-renamed", {0: "display-message 'test'"})
63+
64+
@pytest.mark.skipif(
65+
has_lt_version("3.0"),
66+
reason="Test requires tmux 3.0+ for proper array hook support",
67+
)
68+
def test_set_hooks_no_warning_on_new_tmux(
69+
self,
70+
server: Server,
71+
recwarn: pytest.WarningsRecorder,
72+
) -> None:
73+
"""Verify set_hooks does NOT warn on tmux 3.0+.
74+
75+
On tmux 3.0+, hooks are proper array options. No warning should be
76+
emitted when using set_hooks.
77+
"""
78+
session = server.new_session(session_name="test_no_warn")
79+
80+
# Call set_hooks - should not emit any warnings on 3.0+
81+
session.set_hooks("session-renamed", {0: "display-message 'test'"})
82+
83+
# Filter for our specific warning (ignore other warnings)
84+
hook_warnings = [
85+
w for w in recwarn if "Hook arrays require tmux" in str(w.message)
86+
]
87+
assert len(hook_warnings) == 0, "Should not warn on tmux 3.0+"
88+
89+
# Cleanup
90+
session.unset_hook("session-renamed")
91+
92+
93+
class TestSetHooksArrayBehavior:
94+
"""Test set_hooks array behavior on tmux 3.0+."""
95+
96+
@pytest.mark.skipif(
97+
has_lt_version("3.0"),
98+
reason="Hook arrays require tmux 3.0+",
99+
)
100+
def test_set_hooks_creates_array_indices(self, server: Server) -> None:
101+
"""Verify set_hooks creates proper array indices on tmux 3.0+.
102+
103+
On tmux 3.0+, set_hooks should create hooks as array indices that can
104+
be queried with show_hook and cleared with unset_hook.
105+
"""
106+
session = server.new_session(session_name="test_array")
107+
108+
# Suppress warning in case test runs on < 3.0 (though it's skipped)
109+
import warnings
110+
111+
with warnings.catch_warnings():
112+
warnings.simplefilter("ignore")
113+
session.set_hooks(
114+
"session-renamed",
115+
{
116+
0: 'display-message "hook 0"',
117+
1: 'display-message "hook 1"',
118+
},
119+
)
120+
121+
# Verify hooks were created as array
122+
hooks = session.show_hook("session-renamed")
123+
assert hooks is not None
124+
assert isinstance(hooks, SparseArray)
125+
assert sorted(hooks.keys()) == [0, 1]
126+
127+
# Cleanup
128+
session.unset_hook("session-renamed")
129+
130+
@pytest.mark.skipif(
131+
has_lt_version("3.0"),
132+
reason="Hook arrays require tmux 3.0+",
133+
)
134+
def test_set_hooks_clear_existing(self, server: Server) -> None:
135+
"""Verify clear_existing=True clears all indices on tmux 3.0+.
136+
137+
On tmux 3.0+, unset_hook('hook-name') clears all array indices.
138+
The clear_existing parameter uses this to replace all existing hooks.
139+
"""
140+
session = server.new_session(session_name="test_clear")
141+
142+
import warnings
143+
144+
with warnings.catch_warnings():
145+
warnings.simplefilter("ignore")
146+
# Set initial hooks
147+
session.set_hooks(
148+
"session-renamed",
149+
{0: 'display-message "old 0"', 1: 'display-message "old 1"'},
150+
)
151+
152+
# Verify initial hooks
153+
hooks = session.show_hook("session-renamed")
154+
assert hooks is not None
155+
assert isinstance(hooks, SparseArray)
156+
assert sorted(hooks.keys()) == [0, 1]
157+
158+
# Replace with new hooks using clear_existing
159+
session.set_hooks(
160+
"session-renamed",
161+
{0: 'display-message "new"'},
162+
clear_existing=True,
163+
)
164+
165+
# Verify only new hook exists
166+
hooks_after = session.show_hook("session-renamed")
167+
assert hooks_after is not None
168+
assert isinstance(hooks_after, SparseArray)
169+
assert sorted(hooks_after.keys()) == [0]
170+
171+
# Cleanup
172+
session.unset_hook("session-renamed")
173+
174+
@pytest.mark.skipif(
175+
has_lt_version("3.0"),
176+
reason="Hook arrays require tmux 3.0+",
177+
)
178+
def test_set_hooks_from_list(self, server: Server) -> None:
179+
"""Verify set_hooks accepts list input on tmux 3.0+.
180+
181+
Lists are converted to dicts with sequential indices starting at 0.
182+
"""
183+
session = server.new_session(session_name="test_list")
184+
185+
import warnings
186+
187+
with warnings.catch_warnings():
188+
warnings.simplefilter("ignore")
189+
session.set_hooks(
190+
"after-new-window",
191+
[
192+
"select-pane -t 0",
193+
'send-keys "clear" Enter',
194+
],
195+
)
196+
197+
hooks = session.show_hook("after-new-window")
198+
assert hooks is not None
199+
assert isinstance(hooks, SparseArray)
200+
assert sorted(hooks.keys()) == [0, 1]
201+
202+
# Cleanup
203+
session.unset_hook("after-new-window")

0 commit comments

Comments
 (0)