aboutsummaryrefslogtreecommitdiff
path: root/tests/waybar-touchpad/test_waybar_touchpad.py
blob: 3133bb4dd5f37153f90aa09f218bada58d8007c6 (plain)
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
"""Tests for dotfiles/hyprland/.local/bin/waybar-touchpad.

The script emits one JSON line for waybar's custom/touchpad module, derived
from the touchpad state file that toggle-touchpad / touchpad-auto maintain
at $XDG_RUNTIME_DIR/touchpad-state. Tests point XDG_RUNTIME_DIR at a temp
dir and assert on the emitted JSON for each state — enabled, disabled, and
the missing-file default. No mocking: the real script runs against a real
state file.

Run from repo root:
    python3 -m unittest tests.waybar-touchpad.test_waybar_touchpad
"""

import json
import os
import shutil
import subprocess
import tempfile
import unittest


REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
SCRIPT = os.path.join(REPO_ROOT, "dotfiles/hyprland/.local/bin/waybar-touchpad")


class WaybarTouchpadHarness(unittest.TestCase):

    def setUp(self):
        self.tmp = tempfile.mkdtemp(prefix="waybar-touchpad-test-")
        self.state_file = os.path.join(self.tmp, "touchpad-state")

    def tearDown(self):
        shutil.rmtree(self.tmp, ignore_errors=True)

    def set_state(self, value):
        with open(self.state_file, "w") as f:
            f.write(value)

    def run_script(self):
        env = os.environ.copy()
        env["XDG_RUNTIME_DIR"] = self.tmp
        return subprocess.run(
            [SCRIPT], env=env, capture_output=True, text=True, timeout=10,
        )

    def emitted_json(self):
        result = self.run_script()
        self.assertEqual(result.returncode, 0, msg=result.stderr)
        return json.loads(result.stdout)


# -----------------------------------------------------------------------------
# Normal cases
# -----------------------------------------------------------------------------

class TestWaybarTouchpadNormal(WaybarTouchpadHarness):

    def test_enabled_state_emits_enabled_class_and_mouse_icon(self):
        self.set_state("enabled")
        data = self.emitted_json()
        self.assertEqual(data["class"], "enabled")
        self.assertIn("\U000f037d", data["text"])  # 󰍽 mouse
        self.assertIn("enabled", data["tooltip"].lower())

    def test_disabled_state_emits_disabled_class_and_mouse_off_icon(self):
        self.set_state("disabled")
        data = self.emitted_json()
        self.assertEqual(data["class"], "disabled")
        self.assertIn("\U000f037e", data["text"])  # 󰍾 mouse-off
        self.assertIn("disabled", data["tooltip"].lower())


# -----------------------------------------------------------------------------
# Boundary cases
# -----------------------------------------------------------------------------

class TestWaybarTouchpadBoundary(WaybarTouchpadHarness):

    def test_missing_state_file_defaults_to_enabled(self):
        # No state file written → script should treat the touchpad as enabled.
        data = self.emitted_json()
        self.assertEqual(data["class"], "enabled")

    def test_state_with_trailing_newline_is_handled(self):
        self.set_state("disabled\n")
        data = self.emitted_json()
        self.assertEqual(data["class"], "disabled")

    def test_unknown_state_value_treated_as_enabled(self):
        # Anything that isn't "disabled" reads as enabled (fail-safe: the
        # pointer is usable rather than hidden).
        self.set_state("garbage")
        data = self.emitted_json()
        self.assertEqual(data["class"], "enabled")

    def test_output_is_a_single_json_object(self):
        self.set_state("enabled")
        result = self.run_script()
        lines = [ln for ln in result.stdout.splitlines() if ln.strip()]
        self.assertEqual(len(lines), 1, msg=f"expected one line, got {lines!r}")


if __name__ == "__main__":
    unittest.main()