~cnx/palace

ref: 708f23b35a7e232273ba0a63a65b3ef778a84540 palace/tests/unit/test_source.py -rw-r--r-- 12.2 KiB
708f23b3 — Ngô Ngọc Đức Huy Write tutorial for source effect 1 year, 8 months ago
                                                                                
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# Source pytest module
# Copyright (C) 2020  Nguyễn Gia Phong
#
# This file is part of palace.
#
# palace is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.
#
# palace is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with palace.  If not, see <https://www.gnu.org/licenses/>.

"""This pytest module tries to test the correctness of the class Source."""

from itertools import permutations, product, repeat
from math import inf, pi
from operator import is_
from random import random, shuffle

from palace import Buffer, BaseEffect, Source, SourceGroup
from pytest import raises

from fmath import FLT_MAX, allclose, isclose


def test_comparison(context):
    """Test basic comparisons."""
    with Source() as source0, Source() as source1, Source() as source2:
        assert source0 != source1
        sources = [source1, source1, source0, source2]
        sources.sort()
        sources.remove(source2)
        sources.remove(source0)
        assert sources[0] == sources[1]


def test_bool(context):
    """Test boolean value."""
    with Source() as source: assert source
    assert not source


def test_control(context, flac):
    """Test calling control methods."""
    with Buffer(flac) as buffer, buffer.play() as source:
        assert source.playing
        assert not source.paused
        source.pause()
        assert source.paused
        assert not source.playing
        source.resume()
        assert source.playing
        assert not source.paused
        source.stop()
        assert not source.playing
        assert not source.paused
        with raises(AttributeError): source.playing = True
        with raises(AttributeError): source.paused = True


def test_fade_out_to_stop(context, mp3):
    """Test calling method fade_out_to_stop."""
    with Buffer(mp3) as buffer, buffer.play() as source:
        source.fade_out_to_stop(5/7, buffer.length>>1)
        with raises(ValueError): source.fade_out_to_stop(0.42, -1)


def test_group(context):
    """Test read-write property group."""
    with Source(context) as source, SourceGroup(context) as source_group:
        assert source.group is None
        source.group = source_group
        assert source.group == source_group
        assert source in source_group.sources
        source.group = None
        assert source.group is None


def test_priority(context):
    """Test read-write property priority."""
    with Source(context) as source:
        assert source.priority == 0
        source.priority = 42
        assert source.priority == 42


def test_offset(context, ogg):
    """Test read-write property offset."""
    with Buffer(ogg) as buffer, buffer.play() as source:
        assert source.offset == 0
        length = buffer.length
        source.offset = length >> 1
        assert source.offset == length >> 1
        with raises(RuntimeError): source.offset = length
        with raises(OverflowError): source.offset = -1


def test_offset_seconds(context, flac):
    """Test read-only property offset_seconds."""
    with Buffer(flac) as buffer, buffer.play() as source:
        assert isinstance(source.offset_seconds, float)
        with raises(AttributeError):
            source.offset_seconds = buffer.length_seconds / 2


def test_latency(context, aiff):
    """Test read-only property latency."""
    with Buffer(aiff) as buffer, buffer.play() as source:
        assert isinstance(source.latency, int)
        with raises(AttributeError):
            source.latency = 42


def test_latency_seconds(context, mp3):
    """Test read-only property latency_seconds."""
    with Buffer(mp3) as buffer, buffer.play() as source:
        assert isinstance(source.latency_seconds, float)
        with raises(AttributeError):
            source.latency_seconds = buffer.length_seconds / 2


def test_looping(context):
    """Test read-write property looping."""
    with Source(context) as source:
        assert source.looping is False
        source.looping = True
        assert source.looping is True
        source.looping = False
        assert source.looping is False


def test_pitch(context):
    """Test read-write property pitch."""
    with Source(context) as source:
        assert isclose(source.pitch, 1)
        with raises(ValueError): source.pitch = -1
        source.pitch = 5 / 7
        assert isclose(source.pitch, 5/7)


def test_gain(context):
    """Test read-write property gain."""
    with Source(context) as source:
        assert isclose(source.gain, 1)
        with raises(ValueError): source.gain = -1
        source.gain = 5 / 7
        assert isclose(source.gain, 5/7)


def test_gain_range(context):
    """Test read-write property gain_range."""
    with Source(context) as source:
        assert allclose(source.gain_range, (0, 1))
        with raises(ValueError): source.gain_range = 9/11, 5/7
        with raises(ValueError): source.gain_range = 6/9, 420
        with raises(ValueError): source.gain_range = -420, 6/9
        source.gain_range = 5/7, 9/11
        assert allclose(source.gain_range, (5/7, 9/11))


def test_distance_range(context):
    """Test read-write property distance_range."""
    with Source(context) as source:
        assert allclose(source.distance_range, (1, FLT_MAX))
        with raises(ValueError): source.distance_range = 9/11, 5/7
        with raises(ValueError): source.distance_range = -420, 6/9
        with raises(ValueError): source.distance_range = 420, inf
        source.distance_range = 5/7, 9/11
        assert allclose(source.distance_range, (5/7, 9/11))
        source.distance_range = 1, FLT_MAX
        assert allclose(source.distance_range, (1, FLT_MAX))


def test_position(context):
    """Test read-write property position."""
    with Source(context) as source:
        assert allclose(source.position, (0, 0, 0))
        source.position = -1, 0, 1
        assert allclose(source.position, (-1, 0, 1))
        source.position = 4, 20, 69
        assert allclose(source.position, (4, 20, 69))


def test_velocity(context):
    """Test read-write property velocity."""
    with Source(context) as source:
        assert allclose(source.velocity, (0, 0, 0))
        source.velocity = -1, 0, 1
        assert allclose(source.velocity, (-1, 0, 1))
        source.velocity = 4, 20, 69
        assert allclose(source.velocity, (4, 20, 69))


def test_orientation(context):
    """Test read-write property orientation."""
    with Source(context) as source:
        assert allclose(source.orientation, ((0, 0, -1), (0, 1, 0)), allclose)
        source.orientation = (1, 1, -2), (3, -5, 8)
        assert allclose(source.orientation, ((1, 1, -2), (3, -5, 8)), allclose)


def test_cone_angles(context):
    """Test read-write property cone_angles."""
    with Source(context) as source:
        assert allclose(source.cone_angles, (360, 360))
        with raises(ValueError): source.cone_angles = 420, 69
        with raises(ValueError): source.cone_angles = -4.20, 69
        with raises(ValueError): source.cone_angles = 4.20, -69
        source.cone_angles = 4.20, 69
        assert allclose(source.cone_angles, (4.20, 69))


def test_outer_cone_gains(context):
    """Test read-write property outer_cone_gains."""
    with Source(context) as source:
        assert allclose(source.outer_cone_gains, (0, 1))
        with raises(ValueError): source.outer_cone_gains = 6/9, -420
        with raises(ValueError): source.outer_cone_gains = 6/9, 420
        with raises(ValueError): source.outer_cone_gains = -420, 6/9
        with raises(ValueError): source.outer_cone_gains = 420, 6/9
        source.outer_cone_gains = 5/7, 9/11
        assert allclose(source.outer_cone_gains, (5/7, 9/11))


def test_rolloff_factors(context):
    """Test read-write property rolloff_factors."""
    with Source(context) as source:
        assert allclose(source.rolloff_factors, (1, 0))
        with raises(ValueError): source.rolloff_factors = -6, 9
        with raises(ValueError): source.rolloff_factors = 6, -9
        source.rolloff_factors = 6, 9
        assert allclose(source.rolloff_factors, (6, 9))


def test_doppler_factor(context):
    """Test read-write property doppler_factor."""
    with Source(context) as source:
        assert isclose(source.doppler_factor, 1)
        with raises(ValueError): source.doppler_factor = -6.9
        with raises(ValueError): source.doppler_factor = 4.20
        source.doppler_factor = 5 / 7
        assert isclose(source.doppler_factor, 5/7)


def test_relative(context):
    """Test read-write property relative."""
    with Source(context) as source:
        assert source.relative is False
        source.relative = True
        assert source.relative is True
        source.relative = False
        assert source.relative is False


def test_radius(context):
    """Test read-write property radius."""
    with Source(context) as source:
        assert isclose(source.radius, 0)
        with raises(ValueError): source.radius = -1
        source.radius = 5 / 7
        assert isclose(source.radius, 5/7)


def test_stereo_angles(context):
    """Test read-write property stereo_angles."""
    with Source(context) as source:
        assert allclose(source.stereo_angles, (pi/6, -pi/6))
        source.stereo_angles = 420, -69
        assert allclose(source.stereo_angles, (420, -69))
        source.stereo_angles = -5/7, 9/11
        assert allclose(source.stereo_angles, (-5/7, 9/11))


def test_spatialize(context):
    """Test read-write property spatialize."""
    with Source(context) as source:
        assert source.spatialize is None
        source.spatialize = False
        assert source.spatialize is False
        source.spatialize = True
        assert source.spatialize is True
        source.spatialize = None
        assert source.spatialize is None


def test_resampler_index(context):
    """Test read-write property resampler_index."""
    with Source() as source:
        assert source.resampler_index == context.default_resampler_index
        with raises(ValueError): source.resampler_index = -1
        source.resampler_index = 69
        assert source.resampler_index == 69


def test_air_absorption_factor(context):
    """Test read-write property air_absorption_factor."""
    with Source(context) as source:
        assert isclose(source.air_absorption_factor, 0)
        with raises(ValueError): source.air_absorption_factor = -1
        with raises(ValueError): source.air_absorption_factor = 11
        source.air_absorption_factor = 420 / 69
        assert isclose(source.air_absorption_factor, 420/69)


def test_gain_auto(context):
    """Test read-write property gain_auto."""
    with Source(context) as source:
        assert all(gain is True for gain in source.gain_auto)
        for gain_auto in product(*repeat((False, True), 3)):
            source.gain_auto = gain_auto
            assert all(map(is_, source.gain_auto, gain_auto))


def tests_sends(device, context):
    """Test send paths assignment."""
    with Source() as source, BaseEffect() as effect:
        invalid_filter = [-1, 0, 1]
        for i in range(device.max_auxiliary_sends):
            source.sends[i].effect = effect
            source.sends[i].filter = random(), random(), random()
            shuffle(invalid_filter)
            with raises(ValueError): source.sends[i].filter = invalid_filter
            with raises(AttributeError): source.sends[i].effect
            with raises(AttributeError): source.sends[i].filter
        with raises(IndexError): source.sends[-1]
        with raises(TypeError): source.sends[4.2]
        with raises(TypeError): source.sends['0']
        with raises(TypeError): source.sends[6:9]
        with raises(AttributeError): source.sends = ...


def test_filter(context):
    """Test write-only property filter."""
    with Source() as source:
        with raises(AttributeError): source.filter
        source.filter = 1, 6.9, 5/7
        source.filter = 0, 0, 0
        for gain, gain_hf, gain_lf in permutations([4, -2, 0]):
            with raises(ValueError): source.filter = gain, gain_hf, gain_lf