1from gi.repository import GLib
2import pytest
3
4from xl.player.track_fader import TrackFader, FadeState
5
6NoFade = FadeState.NoFade
7FadingIn = FadeState.FadingIn
8Normal = FadeState.Normal
9FadingOut = FadeState.FadingOut
10
11
12class FakeStream:
13    def __init__(self):
14        self.reset()
15
16    def reset(self):
17        self.position = 0
18        self.volume = 42
19        self.fadeout_begin = False
20        self.stopped = False
21
22    def get_position(self):
23        return self.position
24
25    def set_volume(self, value):
26        self.volume = int(value * 100)
27
28    def stop(self):
29        self.stopped = True
30
31    def on_fade_out(self):
32        self.fadeout_begin = True
33
34
35class FakeTrack:
36    def __init__(self, start_off, stop_off, tracklen):
37        self.tags = {
38            '__startoffset': start_off,
39            '__stopoffset': stop_off,
40            '__length': tracklen,
41        }
42
43    def get_tag_raw(self, t):
44        return self.tags[t]
45
46
47# TODO: monkeypatch instead
48timeout_args = [()]
49
50
51def glib_timeout_add(*args):
52    timeout_args[0] = args[2:]
53    return args[1]
54
55
56def glib_source_remove(src_id):
57    pass
58
59
60GLib.timeout_add = glib_timeout_add
61GLib.source_remove = glib_source_remove
62
63TmSt = 1
64TmEx = 2
65
66
67# Test data:
68#   Position, Volume, State, TmSt/TmEx/None, [call, [arg1...]]
69
70# fmt: off
71@pytest.mark.parametrize('test', [
72
73    # Test don't manage the volume
74    [
75        (0, 100, NoFade, None, 'play', None, None, None, None),
76        (1, 100, NoFade, None, 'pause'),
77        (2, 100, NoFade, None, 'unpause'),
78        (3, 100, NoFade, None, 'seek', 4),
79        (4, 100, NoFade, None, 'stop'),
80        (5, 100, NoFade, None),
81    ],
82
83    # Test fading in
84    [
85        (0, 0,  FadingIn, TmEx, 'play', 0, 2, None, None),
86        (1, 50, FadingIn, TmEx, 'execute'),
87        (3, 100, NoFade,  None, 'execute'),
88        (4, 100, NoFade,  None),
89        (5, 100, NoFade,  None, 'stop'),
90        (6, 100, NoFade,  None),
91    ],
92
93    # Test fading in: pause in middle
94    [
95        (0, 0,  FadingIn, TmEx, 'play', 0, 2, None, None),
96        (1, 50, FadingIn, TmEx, 'execute'),
97        (1, 50, FadingIn, None, 'pause'),
98        (1, 50, FadingIn, TmEx, 'unpause'),
99        (1, 50, FadingIn, TmEx, 'execute'),
100        (3, 100, NoFade,  None, 'execute'),
101        (4, 100, NoFade,  None),
102        (5, 100, NoFade,  None, 'stop'),
103        (6, 100, NoFade,  None),
104    ],
105
106    # Test fading in past the fade point
107    [
108        (3, 100, NoFade, None, 'play', 0, 2, None, None),
109        (4, 100, NoFade, None),
110        (5, 100, NoFade, None, 'stop'),
111        (6, 100, NoFade, None),
112    ],
113
114    # Test fading out
115    [
116        (3, 100, Normal,    TmSt, 'play', None, None, 4, 6),
117        (4, 100, FadingOut, TmEx, 'start'),
118        (5, 50,  FadingOut, TmEx, 'execute'),
119        (6, 0,   FadingOut, TmEx, 'execute'),
120        (6.1, 0, NoFade,    None, 'execute'),
121        (7,   0, NoFade,    None),
122    ],
123
124    # Test all of them
125    [
126        (0, 0,  FadingIn,   TmEx, 'play', 0, 2, 4, 6),
127        (1, 50, FadingIn,   TmEx, 'execute'),
128        (3, 100, Normal,    TmSt, 'execute'),
129        (4, 100, FadingOut, TmEx, 'start'),
130        (5, 50,  FadingOut, TmEx, 'execute'),
131        (6, 0,   FadingOut, TmEx, 'execute'),
132        (6.1, 0, NoFade,    None, 'execute'),
133        (7,   0, NoFade,    None),
134    ],
135
136    # Test fading in with startoffset
137    # [
138    #     (0, 0,  FadingIn,  TmEx, 'play', 60, 62, 64, 66),
139    #     (0, 0,  FadingIn,  TmEx, 'seek', 60),
140    #     (61, 50,  FadingIn,  TmEx, 'execute'),
141    # ],
142])
143# fmt: on
144def test_fader(test):
145
146    # Test fade_out_on_play
147
148    # Test setup_track
149
150    # Test setup_track is_update=True
151
152    # Test unexpected fading out
153
154    check_fader(test)
155
156
157def check_fader(test):
158    stream = FakeStream()
159    fader = TrackFader(stream, stream.on_fade_out, 'test')
160
161    for data in test:
162        print(data)
163        now = data[0]
164        stream.position = int(now * TrackFader.SECOND)
165        print(stream.position)
166        volume = data[1]
167        state = data[2]
168        timer_id = data[3]
169
170        if len(data) > 4:
171            action = data[4]
172            args = data[5:] if len(data) > 5 else ()
173
174            if action == 'start':
175                action = '_on_fade_start'
176            elif action == 'execute':
177                action = '_execute_fade'
178                args = timeout_args[0]
179                fader.now = now - 0.010
180
181            # Call the function
182            getattr(fader, action)(*args)
183
184        # Check to see if timer id exists
185        if timer_id is None:
186            assert fader.timer_id is None
187        elif timer_id == TmSt:
188            assert fader.timer_id == fader._on_fade_start
189        elif timer_id == TmEx:
190            assert fader.timer_id == fader._execute_fade
191        else:
192            assert False
193
194        assert fader.state == state
195        assert stream.volume == volume
196
197
198def test_calculate_fades():
199    fader = TrackFader(None, None, None)
200
201    # fin, fout, start_off, stop_off, tracklen;
202    # start, start+fade, end-fade, end
203    calcs = [
204        # fmt: off
205
206        # one is zero/none
207        (0, 4, 0, 0, 10,        0, 0, 6, 10),
208        (None, 4, 0, 0, 10,     0, 0, 6, 10),
209
210        # other is zero/none
211        (4, 0, 0, 0, 10,        0, 4, 10, 10),
212        (4, None, 0, 0, 10,     0, 4, 10, 10),
213
214        # both are equal
215        (4, 4, 0, 0, 10,        0, 4, 6, 10),
216
217        # both are none
218        (0, 0, 0, 0, 10,        0, 0, 10, 10),
219        (None, None, 0, 0, 10,  0, 0, 10, 10),
220
221        # Bigger than playlen: all three cases
222        (0, 4, 0, 0, 2,         0, 0, 0, 2),
223        (4, 0, 0, 0, 2,         0, 2, 2, 2),
224        (4, 4, 0, 0, 2,         0, 1, 1, 2),
225
226        # With start offset
227        (4, 4, 1, 0, 10,        1, 5, 6, 10),
228
229        # With stop offset
230        (4, 4, 0, 9, 10,        0, 4, 5, 9),
231
232        # With both
233        (2, 2, 1, 9, 10,        1, 3, 7, 9),
234
235        # With both, constrained
236        (4, 4, 4, 8, 10,        4, 6, 6, 8),
237        (2, 4, 4, 7, 10,        4, 5, 5, 7),
238        (4, 2, 4, 7, 10,        4, 6, 6, 7),
239        # fmt: on
240    ]
241
242    i = 0
243    for fin, fout, start, stop, tlen, t0, t1, t2, t3 in calcs:
244        print(
245            '%2d: Fade In: %s; Fade Out: %s; start: %s; stop: %s; Len: %s'
246            % (i, fin, fout, start, stop, tlen)
247        )
248        track = FakeTrack(start, stop, tlen)
249        assert fader.calculate_fades(track, fin, fout) == (t0, t1, t2, t3)
250        i += 1
251