1import libqtile.config
2from libqtile.bar import Bar
3from libqtile.widget.check_updates import CheckUpdates, Popen  # noqa: F401
4
5
6def no_op(*args, **kwargs):
7    pass
8
9
10wrong_distro = "Barch"
11good_distro = "Arch"
12cmd_0_line = "export toto"   # quick "monkeypatch" simulating 0 output, ie 0 update
13cmd_1_line = "echo toto"     # quick "monkeypatch" simulating 1 output, ie 1 update
14cmd_error = "false"
15nus = "No Update Avalaible"
16
17
18def test_unknown_distro():
19    """ test an unknown distribution """
20    cu = CheckUpdates(distro=wrong_distro)
21    text = cu.poll()
22    assert text == "N/A"
23
24
25def test_update_available(fake_qtile, fake_window):
26    """ test output with update (check number of updates and color) """
27    cu2 = CheckUpdates(distro=good_distro,
28                       custom_command=cmd_1_line,
29                       colour_have_updates="#123456"
30                       )
31    fakebar = Bar([cu2], 24)
32    fakebar.window = fake_window
33    fakebar.width = 10
34    fakebar.height = 10
35    fakebar.draw = no_op
36    cu2._configure(fake_qtile, fakebar)
37    text = cu2.poll()
38    assert text == "Updates: 1"
39    assert cu2.layout.colour == cu2.colour_have_updates
40
41
42def test_no_update_available_without_no_update_string(fake_qtile, fake_window):
43    """ test output with no update (without dedicated string nor color) """
44    cu3 = CheckUpdates(distro=good_distro, custom_command=cmd_0_line)
45    fakebar = Bar([cu3], 24)
46    fakebar.window = fake_window
47    fakebar.width = 10
48    fakebar.height = 10
49    fakebar.draw = no_op
50    cu3._configure(fake_qtile, fakebar)
51    text = cu3.poll()
52    assert text == ""
53
54
55def test_no_update_available_with_no_update_string_and_color_no_updates(
56    fake_qtile, fake_window
57):
58    """ test output with no update (with dedicated string and color) """
59    cu4 = CheckUpdates(distro=good_distro,
60                       custom_command=cmd_0_line,
61                       no_update_string=nus,
62                       colour_no_updates="#654321"
63                       )
64    fakebar = Bar([cu4], 24)
65    fakebar.window = fake_window
66    fakebar.width = 10
67    fakebar.height = 10
68    fakebar.draw = no_op
69    cu4._configure(fake_qtile, fakebar)
70    text = cu4.poll()
71    assert text == nus
72    assert cu4.layout.colour == cu4.colour_no_updates
73
74
75def test_update_available_with_restart_indicator(monkeypatch, fake_qtile, fake_window):
76    """ test output with no indicator where restart needed """
77    cu5 = CheckUpdates(distro=good_distro,
78                       custom_command=cmd_1_line,
79                       restart_indicator="*",
80                       )
81    monkeypatch.setattr("os.path.exists", lambda x: True)
82    fakebar = Bar([cu5], 24)
83    fakebar.window = fake_window
84    fakebar.width = 10
85    fakebar.height = 10
86    fakebar.draw = no_op
87    cu5._configure(fake_qtile, fakebar)
88    text = cu5.poll()
89    assert text == "Updates: 1*"
90
91
92def test_update_available_with_execute(manager_nospawn, minimal_conf_noscreen, monkeypatch):
93    """ test polling after executing command """
94
95    # Use monkeypatching to patch both Popen (for execute command) and call_process
96
97    # This class returns None when first polled (to simulate that the task is still running)
98    # and then 0 on the second call.
99    class MockPopen:
100        def __init__(self, *args, **kwargs):
101            self.call_count = 0
102
103        def poll(self):
104            if self.call_count == 0:
105                self.call_count += 1
106                return None
107            return 0
108
109    # Bit of an ugly hack to replicate the above functionality but for a method.
110    class MockSpawn:
111        call_count = 0
112
113        @classmethod
114        def call_process(cls, *args, **kwargs):
115            if cls.call_count == 0:
116                cls.call_count += 1
117                return "Updates"
118            return ""
119
120    cu6 = CheckUpdates(distro=good_distro,
121                       custom_command="dummy",
122                       execute="dummy",
123                       no_update_string=nus,
124                       )
125
126    # Patch the necessary object
127    monkeypatch.setattr(cu6, "call_process", MockSpawn.call_process)
128    monkeypatch.setattr("libqtile.widget.check_updates.Popen", MockPopen)
129
130    config = minimal_conf_noscreen
131    config.screens = [
132        libqtile.config.Screen(
133            top=libqtile.bar.Bar([cu6], 10)
134        )
135    ]
136
137    manager_nospawn.start(config)
138
139    topbar = manager_nospawn.c.bar["top"]
140
141    assert topbar.info()["widgets"][0]["text"] == "Updates: 1"
142
143    # Clicking the widget triggers the execute command
144    topbar.fake_button_press(0, "top", 0, 0, button=1)
145
146    # The second time we poll the widget, the update process is complete
147    # and there are no more updates
148    _, result = manager_nospawn.c.widget["checkupdates"].eval("self.poll()")
149    assert result == nus
150
151
152def test_update_process_error(fake_qtile, fake_window):
153    """ test output where update check gives error"""
154    cu7 = CheckUpdates(distro=good_distro,
155                       custom_command=cmd_error,
156                       no_update_string="ERROR",
157                       )
158    fakebar = Bar([cu7], 24)
159    fakebar.window = fake_window
160    fakebar.width = 10
161    fakebar.height = 10
162    fakebar.draw = no_op
163    cu7._configure(fake_qtile, fakebar)
164    text = cu7.poll()
165    assert text == "ERROR"
166
167
168def test_line_truncations(fake_qtile, monkeypatch, fake_window):
169    """ test update count is reduced"""
170
171    # Mock output to return 5 lines of text
172    def mock_process(*args, **kwargs):
173        return "1\n2\n3\n4\n5\n"
174
175    # Fedora is set up to remove 1 from line count
176    cu8 = CheckUpdates(distro="Fedora")
177
178    monkeypatch.setattr(cu8, "call_process", mock_process)
179    fakebar = Bar([cu8], 24)
180    fakebar.window = fake_window
181    fakebar.width = 10
182    fakebar.height = 10
183    fakebar.draw = no_op
184    cu8._configure(fake_qtile, fakebar)
185    text = cu8.poll()
186
187    # Should have 4 updates
188    assert text == "Updates: 4"
189