diff --git a/.gitignore b/.gitignore index b6e4761..ac8ab05 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +config.ini + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index a16ade0..24fc759 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,49 @@ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/onyx-and-iris/xair-api-python/blob/dev/LICENSE) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +![Tests Status](./tests/MR18.svg?dummy=8484744) + # Mair Remote -This package offers a python interface to the Midas MR18 digital rack mixer. -All testing was done using a Midas MR18 but I've been informed that the software for XR18 is identical. -Midas are not affiliated with/nor do they support this package in any way. +This package offers a python interface for the [Behringer XAir](https://www.behringer.com/series.html?category=R-BEHRINGER-XAIRSERIES), [Midas MR](https://www.midasconsoles.com/catalog.html?catalog=Category&category=C-MIDAS-MIXERS-DIGITALSTAGEBOXMIXERS) series of digital rack mixers. I only have access to an MR18 for testing so if there is an error in the kind maps feel free to raise an issue or PR. ## Prerequisites -- Python 3.9+ + +- Python 3.9+ ## Installation + ``` -git clone https://github.com/onyx-and-iris/mair-api-python -cd mair-api-python +git clone https://github.com/onyx-and-iris/xair-api-python +cd xair-api-python ``` Just the interface: + ``` pip install . ``` With development dependencies: + ``` pip install -e .['development'] ``` ## Usage + ### Connection + An ini file named config.ini, placed into the current working directory of your code may be used to configure the mixers ip. It's contents should resemble: + ``` [connection] ip= ``` + Alternatively you may state it explicitly as an argument to mair.connect() ### Example 1 + ```python import mair @@ -52,13 +61,16 @@ if __name__ == '__main__': ``` ## API -Currently the following devices are support: -- `XR18` -- `MR18` -However, this interface can be expanded upon to support other devices. +Currently the following devices are support: + +- `XR18` +- `MR18` +- `XR16` +- `XR12` ### MAirRemote (higher level) + `mixer.lr` A class representing Main LR channel @@ -95,163 +107,183 @@ An RTN tuple containing a class for each rtn channel A class representing the main config settings - ### `LR` + Contains the subclasses: (`Config`, `Dyn`, `Insert`, `EQ`, `Mix`) ### `Strip` + Contains the subclasses: (`Config`, `Preamp`, `Gate`, `Dyn`, `Insert`, `GEQ`, `EQ`, `Mix`, `Group`, `Automix`) ### `Bus` + Contains the subclasses: (`Config`, `Dyn`, `Insert`, `EQ`, `Mix`, `Group`) ### `FXSend` + Contains the subclasses: (`Config`, `Mix`, `Group`) ### `Aux` + Contains the subclasses: (`Config`, `Preamp`, `EQ`, `Mix`, `Group`) ### `Rtn` + Contains the subclasses: (`Config`, `Preamp`, `EQ`, `Mix`, `Group`) - ### `Subclasses` + For each subclass the corresponding properties are available. `Config` -- `name`: string -- `color`: int, from 0, 16 -- `inputsource`: int -- `usbreturn`: int + +- `name`: string +- `color`: int, from 0, 16 +- `inputsource`: int +- `usbreturn`: int `Preamp` -- `on`: bool -- `usbtrim`: float, from -18.0 to 18.0 -- `usbinput`: bool -- `invert`: bool -- `highpasson`: bool -- `highpassfilter`: int, from 20 to 400 + +- `on`: bool +- `usbtrim`: float, from -18.0 to 18.0 +- `usbinput`: bool +- `invert`: bool +- `highpasson`: bool +- `highpassfilter`: int, from 20 to 400 `Gate` -- `on`: bool -- `mode`: str, one of ('gate', 'exp2', 'exp3', 'exp4', 'duck') -- `threshold`: float, from -80.0 to 0.0 -- `range`: int, from 3 to 60 -- `attack`: int, from 0 to 120 -- `hold`: float, from 0.02 to 2000 -- `release`: int, from 5 to 4000 -- `keysource`, from 0 to 22 -- `filteron`: bool -- `filtertype`: int, from 0 to 8 -- `filterfreq`: float, from 20 to 20000 + +- `on`: bool +- `mode`: str, one of ('gate', 'exp2', 'exp3', 'exp4', 'duck') +- `threshold`: float, from -80.0 to 0.0 +- `range`: int, from 3 to 60 +- `attack`: int, from 0 to 120 +- `hold`: float, from 0.02 to 2000 +- `release`: int, from 5 to 4000 +- `keysource`, from 0 to 22 +- `filteron`: bool +- `filtertype`: int, from 0 to 8 +- `filterfreq`: float, from 20 to 20000 `Dyn` -- `on`: bool -- `mode`: str, one of ('comp', 'exp') -- `det`: str, one of ('peak', 'rms') -- `env`: str, one of ('lin', 'log') -- `threshold`: float, from -60.0 to 0.0 -- `ratio`: int, from 0 to 11 -- `knee`: int, from 0 to 5 -- `mgain`: float, from 0.0 to 24.0 -- `attack`: int, from 0 to 120 -- `hold`: float, from 0.02 to 2000 -- `release`: int, from 5 to 4000 -- `mix`: int, from 0 to 100 -- `keysource`: int, from 0 to 22 -- `auto`: bool -- `filteron`: bool -- `filtertype`: int, from 0 to 8 -- `filterfreq`: float, from 20 to 20000 + +- `on`: bool +- `mode`: str, one of ('comp', 'exp') +- `det`: str, one of ('peak', 'rms') +- `env`: str, one of ('lin', 'log') +- `threshold`: float, from -60.0 to 0.0 +- `ratio`: int, from 0 to 11 +- `knee`: int, from 0 to 5 +- `mgain`: float, from 0.0 to 24.0 +- `attack`: int, from 0 to 120 +- `hold`: float, from 0.02 to 2000 +- `release`: int, from 5 to 4000 +- `mix`: int, from 0 to 100 +- `keysource`: int, from 0 to 22 +- `auto`: bool +- `filteron`: bool +- `filtertype`: int, from 0 to 8 +- `filterfreq`: float, from 20 to 20000 `Insert` -- `on`: bool -- `sel`: int + +- `on`: bool +- `sel`: int `GEQ` The following method names preceded by `slider_` -- `20`, `25`, `31_5`, `40`, `50`, `63`, `80`, `100`, `125`, `160`, -- `200`, `250`, `315`, `400`, `500`, `630`, `800`, `1k`, `1k25`, `1k6`, `2k`, -- `2k5`, `3k15`, `4k`, `5k`, `6k3`, `8k`, `10k`, `12k5`, `16k`, `20k`: float, from -15.0 to 15.0 + +- `20`, `25`, `31_5`, `40`, `50`, `63`, `80`, `100`, `125`, `160`, +- `200`, `250`, `315`, `400`, `500`, `630`, `800`, `1k`, `1k25`, `1k6`, `2k`, +- `2k5`, `3k15`, `4k`, `5k`, `6k3`, `8k`, `10k`, `12k5`, `16k`, `20k`: float, from -15.0 to 15.0 for example: `slider_20`, `slider_6k3` etc.. `EQ` -- `on`: bool -- `mode`: str, one of ('peq', 'geq', 'teq') + +- `on`: bool +- `mode`: str, one of ('peq', 'geq', 'teq') For the subclasses: `low`, `low2`, `lomid`, `himid`, `high2`, `high` the following properties are available: -- `type`: int, from 0 to 5 -- `frequency`: float, from 20.0 to 20000.0 -- `gain`: float, -15.0 to 15.0 -- `quality`: float, from 0.3 to 10.0 + +- `type`: int, from 0 to 5 +- `frequency`: float, from 20.0 to 20000.0 +- `gain`: float, -15.0 to 15.0 +- `quality`: float, from 0.3 to 10.0 for example: `eq.low2.type` `Mix` -- `on`: bool -- `fader`: float, -inf, to 10.0 -- `lr`: bool + +- `on`: bool +- `fader`: float, -inf, to 10.0 +- `lr`: bool `Group` -- `dca`: int, from 0 to 15 -- `mute`: int, from 0 to 15 + +- `dca`: int, from 0 to 15 +- `mute`: int, from 0 to 15 `Automix` -- `group`: int, from 0 to 2 -- `weight`: float, from -12.0 to 12.0 +- `group`: int, from 0 to 2 +- `weight`: float, from -12.0 to 12.0 ### `DCA` -- `on`: bool -- `name`: str -- `color`: int, from 0 to 15 +- `on`: bool +- `name`: str +- `color`: int, from 0 to 15 ### `Config` + The following method names preceded by `chlink` -- `1_2`, `3_4`, `5_6`, `7_8`, `9_10`, `11_12`, `13_14`, `15_16` + +- `1_2`, `3_4`, `5_6`, `7_8`, `9_10`, `11_12`, `13_14`, `15_16` The following method names preceded by `buslink` -- `1_2`, `3_4`, `5_6` + +- `1_2`, `3_4`, `5_6` for example: `chlink1_2`, `buslink5_6` etc.. -- `link_eq`: bool -- `link_dyn`: bool -- `link_fader_mute`: bool -- `amixenable`: bool -- `amixlock`: bool -- `mute_group`: bool +- `link_eq`: bool +- `link_dyn`: bool +- `link_fader_mute`: bool +- `amixenable`: bool +- `amixlock`: bool +- `mute_group`: bool For the subclass `monitor` the following properties are available -- `level`: float, -inf to 10.0 -- `source`: int, from 0 to 14 -- `chmode` bool -- `busmode` bool -- `dim` bool -- `mono` bool -- `mute` bool -- `dimfpl` bool + +- `level`: float, -inf to 10.0 +- `source`: int, from 0 to 14 +- `chmode` bool +- `busmode` bool +- `dim` bool +- `mono` bool +- `mute` bool +- `dimfpl` bool for example: `config.monitor.chmode` - ### `Tests` -First make sure you installed the [development dependencies](https://github.com/onyx-and-iris/mair-api-python#installation) + +People plug expensive equipment into these mixers, the unit tests adjust parameter values such as gain sliders etc. My advice is +to unplug all equipment from the mixer before running these tests. No tests alter phantom power state. +Save your current settings to a snapshot first. + +First make sure you installed the [development dependencies](https://github.com/onyx-and-iris/xair-api-python#installation) To run the tests from tests directory: -WARNING: First save your settings and make sure your equipment is safe from damage. -Run tests at your own risk. - -`nosetests --r test -v`. +`pytest -v`. ## License @@ -259,4 +291,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file ## Special Thanks -Peter Dikant for writing the base class +[Peter Dikant](https://github.com/peterdikant) for writing the base class diff --git a/__main__.py b/__main__.py index 21c3ebe..8b3b7ed 100644 --- a/__main__.py +++ b/__main__.py @@ -1,13 +1,17 @@ import mair + def main(): with mair.connect(kind_id, ip=ip) as mixer: - mixer.strip[8].config.name = 'sm7b' + mixer.strip[8].config.name = "sm7b" mixer.strip[8].config.on = True - print(f'strip 09 ({mixer.strip[8].config.name}) has been set to {mixer.strip[8].config.on}') + print( + f"strip 09 ({mixer.strip[8].config.name}) has been set to {mixer.strip[8].config.on}" + ) -if __name__ == '__main__': - kind_id = 'MR18' - ip = '' + +if __name__ == "__main__": + kind_id = "MR18" + ip = "mixer.local" main() diff --git a/mair/kinds.py b/mair/kinds.py index 9e75946..eeb7669 100644 --- a/mair/kinds.py +++ b/mair/kinds.py @@ -1,9 +1,23 @@ from dataclasses import dataclass +""" +# osc slightly different, interface would need adjusting to support this mixer. + +@dataclass +class X32KindMap: + id_: str = "X32" + num_dca: int = 8 + num_strip: int = 32 + num_bus: int = 16 + num_fx: int = 8 + num_rtn: int = 6 +""" + @dataclass class MR18KindMap: - id_: str = "MR18" + # note ch 17-18 defined as aux rtn + id_: str num_dca: int = 4 num_strip: int = 16 num_bus: int = 6 @@ -11,9 +25,36 @@ class MR18KindMap: num_rtn: int = 4 +@dataclass +class XR16KindMap: + id_: str + num_dca: int = 4 + num_strip: int = 16 + num_bus: int = 4 + num_fx: int = 4 + num_rtn: int = 4 + + +@dataclass +class XR12KindMap: + id_: str + num_dca: int = 4 + num_strip: int = 12 + num_bus: int = 2 + num_fx: int = 4 + num_rtn: int = 4 + + _kinds = { - "XR18": MR18KindMap(), - "MR18": MR18KindMap(), + "XR18": MR18KindMap(id_="XR18"), + "MR18": MR18KindMap(id_="MR18"), + "XR16": XR16KindMap(id_="XR16"), + "XR12": XR12KindMap(id_="XR12"), } + +def get(kind_id): + return _kinds[kind_id] + + all = list(kind for kind in _kinds.values()) diff --git a/mair/mair.py b/mair/mair.py index 64a089a..feb2389 100644 --- a/mair/mair.py +++ b/mair/mair.py @@ -54,7 +54,8 @@ class MAirRemote(abc.ABC): dispatcher = Dispatcher() dispatcher.set_default_handler(self.msg_handler) self.xair_ip = kwargs["ip"] or self._ip_from_ini() - self.server = OSCClientServer((self.xair_ip, self.XAIR_PORT), dispatcher) + self.xair_port = kwargs["port"] or self.XAIR_PORT + self.server = OSCClientServer((self.xair_ip, self.xair_port), dispatcher) def __enter__(self): self.worker = threading.Thread(target=self.run_server) @@ -107,7 +108,7 @@ def _make_remote(kind: kinds.MR18KindMap) -> MAirRemote: """ def init(self, *args, **kwargs): - defaultkwargs = {"ip": None} + defaultkwargs = {"ip": None, "port": None} kwargs = defaultkwargs | kwargs MAirRemote.__init__(self, *args, **kwargs) self.kind = kind diff --git a/setup.py b/setup.py index 79c1a55..4d08d90 100644 --- a/setup.py +++ b/setup.py @@ -1,18 +1,10 @@ from setuptools import setup setup( - name='mair_remote', - version='0.1', - description='MAIR Remote Python API', - packages=['mair'], - install_requires=[ - 'python-osc' - ], - extras_require={ - 'development': [ - 'nose', - 'randomize', - 'parameterized' - ] - } + name="mair_remote", + version="0.1", + description="MAIR Remote Python API", + packages=["mair"], + install_requires=["python-osc"], + extras_require={"development": ["pytest", "pytest-randomly", "genbadge[tests]"]}, ) diff --git a/tests/MR18.svg b/tests/MR18.svg new file mode 100644 index 0000000..08a5179 --- /dev/null +++ b/tests/MR18.svg @@ -0,0 +1 @@ +tests: 67tests67 \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py index b85a1dc..4d0b5aa 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,17 +1,39 @@ import mair from mair import kinds import threading +from dataclasses import dataclass +import sys -_kind = 'MR18' +kind_id = "MR18" +ip = "mixer.local" -mars = {kind.id_: mair.connect(_kind) for kind in kinds.all} -tests = mars[_kind] +tests = mair.connect(kind_id, ip=ip) -def setup_package(): - tests.worker = threading.Thread(target = tests.run_server) +kind = kinds.get(kind_id) + + +@dataclass +class Data: + """bounds data to map tests to a kind""" + + name: str = kind.id_ + dca: int = kind.num_dca - 1 + strip: int = kind.num_strip - 1 + bus: int = kind.num_bus - 1 + fx: int = kind.num_fx - 1 + rtn: int = kind.num_rtn - 1 + + +data = Data() + + +def setup_module(): + print(f"\nRunning tests for kind [{data.name}]\n", file=sys.stdout) + tests.worker = threading.Thread(target=tests.run_server) tests.worker.daemon = True tests.worker.start() tests.validate_connection() -def teardown_package(): + +def teardown_module(): tests.server.shutdown() diff --git a/tests/pre-commit.ps1 b/tests/pre-commit.ps1 new file mode 100644 index 0000000..e2c8e27 --- /dev/null +++ b/tests/pre-commit.ps1 @@ -0,0 +1,31 @@ +Function RunTests { + $coverage = "./tests/pytest_coverage.log" + $run_tests = "pytest -v --capture=tee-sys --junitxml=./tests/.coverage.xml" + $match_pattern = "^=|^\s*$|^Running|^Using|^plugins|^collecting|^tests" + + if ( Test-Path $coverage ) { Clear-Content $coverage } + + ForEach ($line in $(Invoke-Expression $run_tests)) { + If ( $line -Match $match_pattern ) { + if ( $line -Match "^Running tests for kind \[(\w+)\]" ) { $kind = $Matches[1] } + $line | Tee-Object -FilePath $coverage -Append + } + } + Write-Output "$(Get-TimeStamp)" | Out-file $coverage -Append + + Invoke-Expression "genbadge tests -t 90 -i ./tests/.coverage.xml -o ./tests/$kind.svg" +} + +Function Get-TimeStamp { + + return "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date) + +} + +if ($MyInvocation.InvocationName -ne ".") { + Invoke-Expression ".\venv\Scripts\Activate.ps1" + + RunTests + + Invoke-Expression "deactivate" +} \ No newline at end of file diff --git a/tests/shared_tests.py b/tests/shared_tests.py deleted file mode 100644 index 0808bfb..0000000 --- a/tests/shared_tests.py +++ /dev/null @@ -1,332 +0,0 @@ -from nose.tools import assert_equal, nottest -from parameterized import parameterized, parameterized_class - -import unittest -from tests import tests - -""" -Not every subclass is tested for every superclass to avoid redundancy. -LR: mix, config, insert, geq -Strip: mix, preamp, config, gate, automix -Bus: config, dyn, eq -FXSend: group -""" - -""" LR TESTS """ -#@nottest -class TestSetAndGetLRMixHigher(unittest.TestCase): - """ Mix """ - def setUp(self): - self.target = getattr(tests, 'lr') - self.target = getattr(self.target, 'mix') - - @parameterized.expand([ - ('on', True), ('on', False) - ]) - def test_it_sets_and_gets_lr_bool_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - self.assertTrue(isinstance(retval, bool)) - assert_equal(retval, val) - - @parameterized.expand([ - ('fader', -80.6), ('fader', -67.0) - ]) - def test_it_sets_and_gets_lr_float_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - -#@nottest -class TestSetAndGetLRConfigHigher(unittest.TestCase): - """ Config """ - def setUp(self): - self.target = getattr(tests, 'lr') - self.target = getattr(self.target, 'config') - - @parameterized.expand([ - ('name', 'test0'), ('name', 'test1') - ]) - def test_it_sets_and_gets_lr_string_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - -#@nottest -class TestSetAndGetLRInsertHigher(unittest.TestCase): - """ Insert """ - def setUp(self): - self.target = getattr(tests, 'lr') - self.target = getattr(self.target, 'insert') - - @parameterized.expand([ - ('on', True), ('on', False) - ]) - def test_it_sets_and_gets_lr_bool_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - self.assertTrue(isinstance(retval, bool)) - assert_equal(retval, val) - - @parameterized.expand([ - ('sel', 0), ('sel', 4) - ]) - def test_it_sets_and_gets_lr_int_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - -#@nottest -class TestSetAndGetLRGEQHigher(unittest.TestCase): - """ GEQ """ - def setUp(self): - self.target = getattr(tests, 'lr') - self.target = getattr(self.target, 'geq') - - @parameterized.expand([ - ('slider_20', -13.5), ('slider_20', 5.5), ('slider_6k3', -8.5), ('slider_6k3', 8.5) - ]) - def test_it_sets_and_gets_lr_int_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - - -""" STRIP TESTS """ -#@nottest -@parameterized_class([ - { 'i': 15 } -]) -class TestSetAndGetStripMixHigher(unittest.TestCase): - """ Mix """ - def setUp(self): - self.target = getattr(tests, 'strip') - self.target = getattr(self.target[self.i], 'mix') - - @parameterized.expand([ - ('on', True), ('on', False), ('lr', True), ('lr', False) - ]) - def test_it_sets_and_gets_strip_bool_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - self.assertTrue(isinstance(retval, bool)) - assert_equal(retval, val) - -#@nottest -@parameterized_class([ - { 'i': 8 } -]) -class TestSetAndGetStripPreampHigher(unittest.TestCase): - """ Preamp """ - def setUp(self): - self.target = getattr(tests, 'strip') - self.target = getattr(self.target[self.i], 'preamp') - - @parameterized.expand([ - ('highpasson', True), ('highpasson', False), ('usbinput', True), ('usbinput', False) - ]) - def test_it_sets_and_gets_strip_bool_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - self.assertTrue(isinstance(retval, bool)) - assert_equal(retval, val) - - @parameterized.expand([ - ('highpassfilter', 20), ('highpassfilter', 399) - ]) - def test_it_sets_and_gets_strip_int_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - - @parameterized.expand([ - ('usbtrim', -16.5), ('usbtrim', 5.5) - ]) - def test_it_sets_and_gets_strip_float_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - -#@nottest -@parameterized_class([ - { 'i': 3 } -]) -class TestSetAndGetStripConfigHigher(unittest.TestCase): - """ Config """ - def setUp(self): - self.target = getattr(tests, 'strip') - self.target = getattr(self.target[self.i], 'config') - - @parameterized.expand([ - ('inputsource', 0), ('inputsource', 18), ('usbreturn', 3), ('usbreturn', 12) - ]) - def test_it_sets_and_gets_strip_int_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - -@parameterized_class([ - { 'i': 12 } -]) -class TestSetAndGetStripGateHigher(unittest.TestCase): - """ Gate """ - def setUp(self): - self.target = getattr(tests, 'strip') - self.target = getattr(self.target[self.i], 'gate') - - @parameterized.expand([ - ('on', True), ('on', False), ('invert', True), ('invert', False), - ('filteron', True), ('filteron', False) - ]) - def test_it_sets_and_gets_strip_bool_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - self.assertTrue(isinstance(retval, bool)) - assert_equal(retval, val) - - @parameterized.expand([ - ('range', 11), ('range', 48), ('attack', 5), ('attack', 110), - ('release', 360), ('release', 2505), ('filtertype', 0), ('filtertype', 8) - ]) - def test_it_sets_and_gets_strip_int_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - - @parameterized.expand([ - ('mode', 'exp2'), ('mode', 'duck') - ]) - def test_it_sets_and_gets_strip_string_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - - @parameterized.expand([ - ('threshold', -80.0), ('threshold', 0.0), ('hold', 355), ('hold', 63.2), - ('filterfreq', 37.2), ('filterfreq', 12765) - ]) - def test_it_sets_and_gets_strip_float_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - -#@nottest -@parameterized_class([ - { 'i': 6 } -]) -class TestSetAndGetStripAutomixHigher(unittest.TestCase): - """ Automix """ - def setUp(self): - self.target = getattr(tests, 'strip') - self.target = getattr(self.target[self.i], 'automix') - - @parameterized.expand([ - ('group', 0), ('group', 2) - ]) - def test_it_sets_and_gets_fxsend_int_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - - @parameterized.expand([ - ('weight', -10.5), ('weight', 3.5) - ]) - def test_it_sets_and_gets_fxsend_float_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - - -""" BUS TESTS """ -#@nottest -@parameterized_class([ - { 'i': 1 } -]) -class TestSetAndGetBusConfigHigher(unittest.TestCase): - """ Config """ - def setUp(self): - self.target = getattr(tests, 'bus') - self.target = getattr(self.target[self.i], 'config') - - @parameterized.expand([ - ('color', 0), ('color', 15) - ]) - def test_it_sets_and_gets_bus_bool_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - -#@nottest -@parameterized_class([ - { 'i': 2 } -]) -class TestSetAndGetBusDynHigher(unittest.TestCase): - """ Dyn """ - def setUp(self): - self.target = getattr(tests, 'bus') - self.target = getattr(self.target[self.i], 'dyn') - - @parameterized.expand([ - ('on', True), ('on', False) - ]) - def test_it_sets_and_gets_bus_bool_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - self.assertTrue(isinstance(retval, bool)) - assert_equal(retval, val) - - @parameterized.expand([ - ('mode', 'comp'), ('mode', 'exp'), ('env', 'lin'), ('env', 'log'), - ('det', 'peak'), ('det', 'rms') - ]) - def test_it_sets_and_gets_bus_string_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - -#@nottest -@parameterized_class([ - { 'i': 0 } -]) -class TestSetAndGetBusEQHigher(unittest.TestCase): - """ EQ """ - def setUp(self): - self.target = getattr(tests, 'bus') - self.target = getattr(self.target[self.i], 'eq') - - @parameterized.expand([ - ('on', True), ('on', False) - ]) - def test_it_sets_and_gets_bus_bool_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - self.assertTrue(isinstance(retval, bool)) - assert_equal(retval, val) - - @parameterized.expand([ - ('mode', 'peq'), ('mode', 'geq'), ('mode', 'teq') - ]) - def test_it_sets_and_gets_bus_string_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) - - -""" FXSEND TESTS """ -#@nottest -@parameterized_class([ - { 'i': 1 } -]) -class TestSetAndGetFXSendGroupHigher(unittest.TestCase): - """ Group """ - def setUp(self): - self.target = getattr(tests, 'fxsend') - self.target = getattr(self.target[self.i], 'group') - - @parameterized.expand([ - ('dca', 0), ('dca', 12), ('mute', 3), ('mute', 8) - ]) - def test_it_sets_and_gets_fxsend_int_params(self, param, val): - setattr(self.target, param, val) - retval = getattr(self.target, param) - assert_equal(retval, val) diff --git a/tests/test_shared.py b/tests/test_shared.py new file mode 100644 index 0000000..926a0ce --- /dev/null +++ b/tests/test_shared.py @@ -0,0 +1,372 @@ +import pytest +from tests import tests, data + +""" +Not every subclass is tested for every superclass to avoid redundancy. +LR: mix, config, insert, geq +Strip: mix, preamp, config, gate, automix +Bus: config, dyn, eq +FXSend: group +""" + +""" Main LR TESTS """ + + +class TestSetAndGetLRMixHigher: + """Mix""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "lr") + self.target = getattr(self.target, "mix") + + @pytest.mark.parametrize( + "param,value", + [("on", True), ("on", False)], + ) + def test_it_sets_and_gets_lr_bool_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + @pytest.mark.parametrize( + "param,value", + [("fader", -80.6), ("fader", -67.0)], + ) + def test_it_sets_and_gets_lr_float_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + +class TestSetAndGetLRConfigHigher: + """Config""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "lr") + self.target = getattr(self.target, "config") + + @pytest.mark.parametrize("param,value", [("name", "test0"), ("name", "test1")]) + def test_it_sets_and_gets_lr_string_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + +class TestSetAndGetLRInsertHigher: + """Insert""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "lr") + self.target = getattr(self.target, "insert") + + @pytest.mark.parametrize( + "param,value", + [("on", True), ("on", False)], + ) + def test_it_sets_and_gets_lr_bool_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + @pytest.mark.parametrize( + "param,value", + [("sel", 0), ("sel", 4)], + ) + def test_it_sets_and_gets_lr_int_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + +class TestSetAndGetLRGEQHigher: + """GEQ""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "lr") + self.target = getattr(self.target, "geq") + + @pytest.mark.parametrize( + "param,value", + [ + ("slider_20", -13.5), + ("slider_20", 5.5), + ("slider_6k3", -8.5), + ("slider_6k3", 8.5), + ], + ) + def test_it_sets_and_gets_lr_int_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + +""" STRIP TESTS """ + + +class TestSetAndGetStripMixHigher: + """Mix""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "strip") + self.target = getattr(self.target[data.strip], "mix") + + @pytest.mark.parametrize( + "param,value", + [("on", True), ("on", False), ("lr", True), ("lr", False)], + ) + def test_it_sets_and_gets_strip_bool_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + +class TestSetAndGetStripPreampHigher: + """Preamp""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "strip") + self.target = getattr(self.target[data.strip], "preamp") + + @pytest.mark.parametrize( + "param,value", + [ + ("highpasson", True), + ("highpasson", False), + ("usbinput", True), + ("usbinput", False), + ], + ) + def test_it_sets_and_gets_strip_bool_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + @pytest.mark.parametrize( + "param,value", + [("highpassfilter", 20), ("highpassfilter", 399)], + ) + def test_it_sets_and_gets_strip_int_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + @pytest.mark.parametrize( + "param,value", + [("usbtrim", -16.5), ("usbtrim", 5.5)], + ) + def test_it_sets_and_gets_strip_float_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + +class TestSetAndGetStripConfigHigher: + """Config""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "strip") + self.target = getattr(self.target[data.strip], "config") + + @pytest.mark.parametrize( + "param,value", + [("inputsource", 0), ("inputsource", 18), ("usbreturn", 3), ("usbreturn", 12)], + ) + def test_it_sets_and_gets_strip_int_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + +class TestSetAndGetStripGateHigher: + """Gate""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "strip") + self.target = getattr(self.target[data.strip], "gate") + + @pytest.mark.parametrize( + "param,value", + [ + ("on", True), + ("on", False), + ("invert", True), + ("invert", False), + ("filteron", True), + ("filteron", False), + ], + ) + def test_it_sets_and_gets_strip_bool_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + @pytest.mark.parametrize( + "param,value", + [ + ("range", 11), + ("range", 48), + ("attack", 5), + ("attack", 110), + ("release", 360), + ("release", 2505), + ("filtertype", 0), + ("filtertype", 8), + ], + ) + def test_it_sets_and_gets_strip_int_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + @pytest.mark.parametrize( + "param,value", + [("mode", "exp2"), ("mode", "duck")], + ) + def test_it_sets_and_gets_strip_string_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + @pytest.mark.parametrize( + "param,value", + [ + ("threshold", -80.0), + ("threshold", 0.0), + ("hold", 355), + ("hold", 63.2), + ("filterfreq", 37.2), + ("filterfreq", 12765), + ], + ) + def test_it_sets_and_gets_strip_float_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + +class TestSetAndGetStripAutomixHigher: + """Automix""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "strip") + self.target = getattr(self.target[data.strip], "automix") + + @pytest.mark.parametrize( + "param,value", + [("group", 0), ("group", 2)], + ) + def test_it_sets_and_gets_fxsend_int_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + @pytest.mark.parametrize( + "param,value", + [("weight", -10.5), ("weight", 3.5)], + ) + def test_it_sets_and_gets_fxsend_float_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + +""" BUS TESTS """ + + +class TestSetAndGetBusConfigHigher: + """Config""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "bus") + self.target = getattr(self.target[data.bus], "config") + + @pytest.mark.parametrize( + "param,value", + [("color", 0), ("color", 15)], + ) + def test_it_sets_and_gets_bus_bool_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + +class TestSetAndGetBusDynHigher: + """Dyn""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "bus") + self.target = getattr(self.target[data.bus], "dyn") + + @pytest.mark.parametrize( + "param,value", + [("on", True), ("on", False)], + ) + def test_it_sets_and_gets_bus_bool_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + @pytest.mark.parametrize( + "param,value", + [ + ("mode", "comp"), + ("mode", "exp"), + ("env", "lin"), + ("env", "log"), + ("det", "peak"), + ("det", "rms"), + ], + ) + def test_it_sets_and_gets_bus_string_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + +class TestSetAndGetBusDynHigher: + """EQ""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "bus") + self.target = getattr(self.target[data.bus], "eq") + + @pytest.mark.parametrize( + "param,value", + [("on", True), ("on", False)], + ) + def test_it_sets_and_gets_bus_bool_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + @pytest.mark.parametrize( + "param,value", + [("mode", "peq"), ("mode", "geq"), ("mode", "teq")], + ) + def test_it_sets_and_gets_bus_string_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value + + +""" FXSEND TESTS """ + + +class TestSetAndGetFXSendGroupHigher: + """Group""" + + __test__ = True + + def setup_class(self): + self.target = getattr(tests, "fxsend") + self.target = getattr(self.target[data.fx], "group") + + @pytest.mark.parametrize( + "param,value", + [("dca", 0), ("dca", 12), ("mute", 3), ("mute", 8)], + ) + def test_it_sets_and_gets_bus_bool_params(self, param, value): + setattr(self.target, param, value) + assert getattr(self.target, param) == value