File: test_mpris2widget.py

package info (click to toggle)
qtile 0.34.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,004 kB
  • sloc: python: 49,959; ansic: 4,371; xml: 324; sh: 260; makefile: 218
file content (190 lines) | stat: -rw-r--r-- 6,542 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
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
189
190
import sys
from importlib import reload
from types import ModuleType

import pytest

from test.widgets.conftest import FakeBar


def no_op(*args, **kwargs):
    pass


async def mock_signal_receiver(*args, **kwargs):
    return True


def fake_timer(interval, func, *args, **kwargs):
    class TimerObj:
        def cancel(self):
            pass

        @property
        def _scheduled(self):
            return False

    return TimerObj()


class MockConstants(ModuleType):
    class MessageType:
        SIGNAL = 1


class MockMessage:
    def __init__(self, is_signal=True, body=None):
        self.message_type = 1 if is_signal else 0
        self.body = body


# dbus_fast message data is stored in variants. The widget extracts the
# information via the `value` attribute so we just need to mock that here.
class obj:  # noqa: N801
    def __init__(self, value):
        self.value = value


# Creates a mock message body containing both metadata and playback status
def metadata_and_status(status):
    return MockMessage(
        body=(
            "",
            {
                "Metadata": obj(
                    {
                        "mpris:trackid": obj(1),
                        "xesam:url": obj("/path/to/rickroll.mp3"),
                        "xesam:title": obj("Never Gonna Give You Up"),
                        "xesam:artist": obj(["Rick Astley"]),
                        "xesam:album": obj("Whenever You Need Somebody"),
                        "mpris:length": obj(200000000),
                    }
                ),
                "PlaybackStatus": obj(status),
            },
            [],
        )
    )


# Creates a mock message body containing just playback status
def playback_status(status, signal=True):
    return MockMessage(is_signal=signal, body=("", {"PlaybackStatus": obj(status)}, []))


METADATA_PLAYING = metadata_and_status("Playing")
METADATA_PAUSED = metadata_and_status("Paused")
STATUS_PLAYING = playback_status("Playing")
STATUS_PAUSED = playback_status("Paused")
STATUS_STOPPED = playback_status("Stopped")
NON_SIGNAL = playback_status("Paused", False)


@pytest.fixture
def patched_module(monkeypatch):
    # Remove dbus_fast.constants entry from modules. If it's not there, don't raise error
    monkeypatch.delitem(sys.modules, "dbus_fast.constants", raising=False)
    monkeypatch.setitem(sys.modules, "dbus_fast.constants", MockConstants("dbus_fast.constants"))
    from libqtile.widget import mpris2widget

    # Need to force reload of the module to ensure patched module is loaded
    # This may only be needed if dbus_fast is installed on testing system so helpful for
    # local tests.
    reload(mpris2widget)
    monkeypatch.setattr("libqtile.widget.mpris2widget.add_signal_receiver", mock_signal_receiver)
    return mpris2widget


def test_mpris2_signal_handling(fake_qtile, patched_module, fake_window):
    mp = patched_module.Mpris2()
    fakebar = FakeBar([mp], window=fake_window)
    mp.timeout_add = fake_timer
    mp._configure(fake_qtile, fakebar)

    assert mp.displaytext == ""

    # No text will be displayed if widget is not configured
    mp.parse_message(*METADATA_PLAYING.body)
    assert mp.displaytext == ""

    # Set configured flag, create a message with the metadata and playback status
    mp.configured = True
    mp.parse_message(*METADATA_PLAYING.body)
    assert mp.text == "Never Gonna Give You Up - Whenever You Need Somebody - Rick Astley"

    # If widget receives "paused" signal it prefixes track with "Paused: "
    mp.parse_message(*STATUS_PAUSED.body)
    assert mp.text == "Paused: Never Gonna Give You Up - Whenever You Need Somebody - Rick Astley"

    # If widget receives "stopped" signal with no metadata then widget is blank
    mp.parse_message(*STATUS_STOPPED.body)
    assert mp.displaytext == ""

    # Reset to playing + metadata
    mp.parse_message(*METADATA_PLAYING.body)
    assert mp.text == "Never Gonna Give You Up - Whenever You Need Somebody - Rick Astley"

    # If widget receives "paused" signal with metadata then message is "Paused: {metadata}"
    mp.parse_message(*METADATA_PAUSED.body)
    assert mp.text == "Paused: Never Gonna Give You Up - Whenever You Need Somebody - Rick Astley"

    # If widget now receives "playing" signal with no metadata, "paused" word is removed
    mp.parse_message(*STATUS_PLAYING.body)
    assert mp.text == "Never Gonna Give You Up - Whenever You Need Somebody - Rick Astley"

    info = mp.info()
    assert info["text"] == "Never Gonna Give You Up - Whenever You Need Somebody - Rick Astley"
    assert info["isplaying"]


def test_mpris2_custom_stop_text(fake_qtile, patched_module, fake_window):
    mp = patched_module.Mpris2(stop_pause_text="Test Paused")
    fakebar = FakeBar([mp], window=fake_window)
    mp.timeout_add = fake_timer
    mp._configure(fake_qtile, fakebar)
    mp.configured = True

    mp.parse_message(*METADATA_PLAYING.body)
    assert mp.text == "Never Gonna Give You Up - Whenever You Need Somebody - Rick Astley"

    # Check our custom paused wording is shown
    mp.parse_message(*STATUS_PAUSED.body)
    assert mp.text == "Test Paused"


def test_mpris2_no_metadata(fake_qtile, patched_module, fake_window):
    mp = patched_module.Mpris2()
    fakebar = FakeBar([mp], window=fake_window)
    mp.timeout_add = fake_timer
    mp._configure(fake_qtile, fakebar)
    mp.configured = True

    mp.parse_message(*STATUS_PLAYING.body)
    assert mp.text == "No metadata for current track"


def test_mpris2_no_scroll(fake_qtile, patched_module, fake_window):
    # If no scrolling, then the update function creates the text to display
    # and draws the bar.
    mp = patched_module.Mpris2(scroll_chars=None)
    fakebar = FakeBar([mp], window=fake_window)
    mp.timeout_add = fake_timer
    mp._configure(fake_qtile, fakebar)
    mp.configured = True

    mp.parse_message(*METADATA_PLAYING.body)
    assert mp.text == "Never Gonna Give You Up - Whenever You Need Somebody - Rick Astley"

    mp.parse_message(*METADATA_PAUSED.body)
    assert mp.text == "Paused: Never Gonna Give You Up - Whenever You Need Somebody - Rick Astley"


def test_mpris2_deprecated_format(patched_module):
    """
    Previously, metadata was displayed by using a list of fields.
    Now, we use a `format` string. The widget should create this when a user
    provides `display_metadata` in their config.
    """
    mp = patched_module.Mpris2(display_metadata=["xesam:title", "xesam:artist"])
    assert mp.format == "{xesam:title} - {xesam:artist}"