diff options
author | Ben Connors <benconnors@outlook.com> | 2019-09-27 14:57:46 -0400 |
---|---|---|
committer | Ben Connors <benconnors@outlook.com> | 2019-09-27 14:57:46 -0400 |
commit | 0fc3371d48d7e87a5628b16c2bbd09c3fb15cd8e (patch) | |
tree | 64dd6994f04a08b269dcf14ff5c26b2d2b542db5 | |
parent | dfe20c0430c7d58b57c44026102cf8b3c52ac1b3 (diff) |
Bugfixes
- Add some new tests
- Run some basic (interactive) chaser tests, they work-ish now
-rw-r--r-- | .coveragerc | 21 | ||||
-rw-r--r-- | blc2/constants.py | 14 | ||||
-rw-r--r-- | blc2/functions/audio.py | 1 | ||||
-rw-r--r-- | blc2/functions/chaser.py | 14 | ||||
-rw-r--r-- | blc2/functions/chaserstep.py | 15 | ||||
-rw-r--r-- | blc2/workspace.py | 7 | ||||
-rw-r--r-- | examples/workspace.xml | 6 | ||||
-rw-r--r-- | tests/conftest.py | 15 | ||||
-rw-r--r-- | tests/test_constants.py | 20 | ||||
-rw-r--r-- | tests/test_functions_audio.py | 27 | ||||
-rw-r--r-- | tests/test_functions_scene.py | 38 |
11 files changed, 155 insertions, 23 deletions
diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..088c2da --- /dev/null +++ b/.coveragerc @@ -0,0 +1,21 @@ +# .coveragerc to control coverage.py +[run] +branch = True + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain about missing debug-only code: + def __repr__ + if self\.debug + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + + # Don't complain if non-runnable code isn't run: + if 0: + if __name__ == .__main__.: diff --git a/blc2/constants.py b/blc2/constants.py index ad6d174..bb207f9 100644 --- a/blc2/constants.py +++ b/blc2/constants.py @@ -18,8 +18,16 @@ class _Infinity: return "infty" def __gt__(self, other): + if other == INFTY: + return False return True + def __ge__(self, other): + return True + + def __le__(self, other): + return other == INFTY + def __lt__(self, other): return False @@ -37,13 +45,11 @@ class _Infinity: def __mul__(self, other): if other == 0: - return 0 + raise ValueError("Undefined") return self def __rmul__(self, other): - if other == 0: - return 0 - return self + return self * other def __init__(self): if _Infinity.INFTY is not None: diff --git a/blc2/functions/audio.py b/blc2/functions/audio.py index 42cd3f6..9b22ba9 100644 --- a/blc2/functions/audio.py +++ b/blc2/functions/audio.py @@ -30,6 +30,7 @@ class Audio(Function): self._audio_id = self.id if fade_in < 0 or fade_out < 0: + self.w.function_deleted(self) raise ValueError("Fades must be nonnegative") self._fade_out = fade_out self._fade_in = fade_in diff --git a/blc2/functions/chaser.py b/blc2/functions/chaser.py index a6699f8..fe6fc17 100644 --- a/blc2/functions/chaser.py +++ b/blc2/functions/chaser.py @@ -112,6 +112,7 @@ class Chaser(Function): def next_steps(self, data): """Iterate over the next steps in the chaser.""" n = data.steps[-1].index + print("Last index", n) if self._advance_mode == RANDOM: while True: yield random.randint(0, len(self.steps)-1) @@ -214,31 +215,30 @@ class Chaser(Function): data.audio_id += 1 ## Make sure we have all the steps we need - st = data.steps[-1].duration + data.steps[-1].start_time + st = data.steps[-1].step.duration + data.steps[-1].start_time if st < INFTY and st <= t: for n in self.next_steps(data): s = self.steps[n] - st += s.duration - et += s.actual_duration + et = st + s.actual_duration if et >= t: ## We need this one - data.steps.append(self.steps[n]._get_data(0, data.audio_id)) #pylint: disable=protected-access + data.steps.append(self.steps[n]._get_data(st, data.audio_id)) #pylint: disable=protected-access data.audio_id += 1 + i = data.steps[-1] + st += s.duration if st > t: ## We're done break ## Clean up the old steps data.steps = [i for i in data.steps if i.start_time+i.actual_duration >= t and i.step.index != -1] - if not data.steps: - return (), (), data ## Render lc = {c: 0 for c in self._scope} ac = [] for n, d in enumerate(data.steps): s = d.step - slc, sac, data.steps[n] = s.render(t, data=data.steps[n]) + slc, sac, data.steps[n] = s.render(t, data=d) lc.update(slc) ac.extend(sac) diff --git a/blc2/functions/chaserstep.py b/blc2/functions/chaserstep.py index 005f76e..597b760 100644 --- a/blc2/functions/chaserstep.py +++ b/blc2/functions/chaserstep.py @@ -231,11 +231,10 @@ class ChaserStep(Function): if data.index != self._index: data.index = self._index - if t > min(self._actual_duration, data.end_time+self._fade_out): - return (), (), data - fade_start = min(data.end_time, self._duration) t -= data.start_time + if t > min(self._actual_duration, data.end_time+self._fade_out): + return (), (), data ## Compute lighting multiplier mul = 1 @@ -282,7 +281,15 @@ class ChaserStep(Function): duration_mode = e.get("duration-mode") if duration_mode not in (MANUAL, INHERIT): raise LoadError("Invalid duration mode") - duration = int(e.get("duration")) if duration_mode == MANUAL else 0 + if duration_mode == INHERIT: + duration = 0 + else: + duration = e.get("duration") + if duration.strip().lower() == "infty": + duration = INFTY + else: + duration = int(duration) + function = int(e.get("function")) return cls(c, id_=id_, name=name, fade_in=fade_in, fade_out=fade_out, diff --git a/blc2/workspace.py b/blc2/workspace.py index 83dba5b..2f2cf79 100644 --- a/blc2/workspace.py +++ b/blc2/workspace.py @@ -195,10 +195,9 @@ class Workspace(XMLSerializable): This also handles removing the function from the Workspace. """ - if f.id not in self._change_callbacks: - return - for _, callback in self._delete_callbacks[f.id]: - callback(f) + if f.id in self._change_callbacks: + for _, callback in self._delete_callbacks[f.id]: + callback(f) self.delete_callbacks(f) diff --git a/examples/workspace.xml b/examples/workspace.xml index c0161fb..3fa2d70 100644 --- a/examples/workspace.xml +++ b/examples/workspace.xml @@ -32,11 +32,13 @@ </function> <function type="Audio" id="1" name="Intro" fade-in="0" fade-out="0"> - <filename>test.wav</filename> + <filename>tests/silence.m4a</filename> </function> <function type="Chaser" id="2" name="Chaser 1" advance-mode="Loop"> - <step id="3" name="Step 1" fade-in="0" fade-out="0" duration-mode="Manual" duration="123" function="0"/> + <step id="3" name="Step 1" fade-in="10" fade-out="100" duration-mode="Manual" duration="infty" function="0"/> + <step id="4" name="Step 2" fade-in="500" fade-out="500" duration-mode="Inherit" function="1"/> </function> + </functions> </workspace> diff --git a/tests/conftest.py b/tests/conftest.py index 71bd3ac..1a9af58 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ import datetime as dt +import xml.etree.ElementTree as et import pytest @@ -16,3 +17,17 @@ def aws(): w = Workspace("", "", "", dt.datetime.now()) Fixture(w, id_=0, channel_count=4) return w + +def elements_equal(e1, e2): + if e1.tag != e2.tag: return False + if e1.text != e2.text: return False + if e1.tail.strip() != e2.tail.strip(): return False + if e1.attrib != e2.attrib: return False + if len(e1) != len(e2): return False + return all(elements_equal(c1, c2) for c1, c2 in zip(e1, e2)) + +@pytest.fixture +def test_xml_eq(): + def inner(e: et.Element, s: str): + return elements_equal(e, et.fromstring(s)) + return inner diff --git a/tests/test_constants.py b/tests/test_constants.py new file mode 100644 index 0000000..5ce0b1a --- /dev/null +++ b/tests/test_constants.py @@ -0,0 +1,20 @@ +"""Tests for the constants used. + +Mainly just tests the INFTY object. +""" + +import pytest + +from blc2.constants import INFTY, _Infinity + +def test_infty(): + assert str(INFTY) == "infty" + assert 1+INFTY == INFTY+1 == INFTY + assert 1-INFTY == INFTY-1 == INFTY + assert 4*INFTY == INFTY*4 == INFTY*0.4 == INFTY + + with pytest.raises(Exception): + _ = INFTY * 0 + + with pytest.raises(Exception): + infty2 = _Infinity() diff --git a/tests/test_functions_audio.py b/tests/test_functions_audio.py index de6aa2a..35d548a 100644 --- a/tests/test_functions_audio.py +++ b/tests/test_functions_audio.py @@ -1,4 +1,6 @@ -import os +"""Tests for the audio primitive.""" + +import pytest from blc2.functions.audio import Audio @@ -15,6 +17,7 @@ def test_audio(aws): assert a.actual_duration == 0 a.filename = "tests/silence.m4a" + assert a.filename == "tests/silence.m4a" assert a.audio_scope == {"tests/silence.m4a"} assert a.duration == 2024 assert a.actual_duration == 3024 @@ -34,3 +37,25 @@ def test_audio(aws): _, ac, _ = a.render(5000) assert not ac + + with pytest.raises(ValueError): + _ = Audio(aws, id_=101, fade_in=-10) + + assert 101 not in aws.functions + + fout = a.fade_out + with pytest.raises(ValueError): + a.fade_out = -50 + assert a.fade_out == fout + + fin = a.fade_in + with pytest.raises(ValueError): + a.fade_in = -50 + assert a.fade_in == fin + + a._set_duration(10000) + assert a.copy_data(a.get_data()) is None + + a.filename = None + assert a.duration == 0 + assert a.actual_duration == 0 diff --git a/tests/test_functions_scene.py b/tests/test_functions_scene.py index 7a5b78b..77b2d6f 100644 --- a/tests/test_functions_scene.py +++ b/tests/test_functions_scene.py @@ -1,7 +1,9 @@ import pytest +import xml.etree.ElementTree as et + from blc2.functions.scene import Scene -from blc2.constants import INFTY +from blc2.constants import INFTY, BXW def test_scene(aws): """Test creation and modification of scenes.""" @@ -74,3 +76,37 @@ def test_scene(aws): lc, ac, _ = s1.render(1) assert dict(lc)[c1] == 10 assert False not in (v == values[c] for c, v in lc if c != c1) + + with pytest.raises(ValueError): + s1[c1] = -10 + assert s1[c1] == 10 + + with pytest.raises(ValueError): + s1[c1] = -256 + assert s1[c1] == 10 + + del s1[c4] + del s1[c4] + +def test_scene_serialize(aws, test_xml_eq): + return + c1, c2, c3, c4 = aws.fixtures[0].channels + s = Scene(aws, id_=123, name="Test Scene", values={c1: 1, c2: 2, c3: 3, c4: 4}) + + (f1, i1), (f2, i2), (f3, i3), (f4, i4) = ((i.f.id, i.id) for i in (c1, c2, c3, c4)) + + e = s.serialize() + expected = """ +<function xmlns="{bxw}" type="Scene" id="123" name="Test Scene"> + <value fixture="{f1}" channel="{i1}">1</value> + <value fixture="{f2}" channel="{i2}">2</value> + <value fixture="{f3}" channel="{i3}">3</value> + <value fixture="{f4}" channel="{i4}">4</value> +</function> +""".format(bxw=BXW.strip("{}"), f1=f1, f2=f2, f3=f3, f4=f4, i1=i1,i2=i2,i3=i3,i4=i4) + + et.register_namespace("", BXW) + print(et.tostring(e, encoding="utf-8")) + print(expected) + + assert test_xml_eq(e, expected) |