Browse Source

Fix tests.

builtin-nodes
Ben Niemann 3 years ago
parent
commit
c54c4686b2
  1. 6
      noisicaa/audioproc/audioproc_client.py
  2. 18
      noisicaa/audioproc/audioproc_client_test.py
  3. 6
      noisicaa/builtin_nodes/control_track/server_impl_test.py
  4. 3
      noisicaa/builtin_nodes/midi_source/server_impl_test.py
  5. 6
      noisicaa/builtin_nodes/sample_track/server_impl_test.py
  6. 3
      noisicaa/builtin_nodes/score_track/server_impl_test.py
  7. 3
      noisicaa/core/__init__.py
  8. 56
      noisicaa/core/callbacks.py
  9. 1
      noisicaa/music/commands_test.py
  10. 2
      noisicaa/music/player.py
  11. 11
      noisicaa/music/player_test.py
  12. 2
      noisicaa/music/project_client_test.py
  13. 1
      noisicaa/music/project_integration_test.py
  14. 11
      noisicaa/music/project_process.py
  15. 2
      noisicaa/music/project_process_context.py
  16. 11
      noisicaa/music/render.py
  17. 16
      noisicaa/music/session_value_store.py
  18. 2
      noisicaa/ui/editor_app.py
  19. 3
      noisidev/uitest.py

6
noisicaa/audioproc/audioproc_client.py

@ -226,8 +226,10 @@ class AudioProcClient(AbstractAudioProcClient):
perf_stats.deserialize(request.perf_stats)
self.perf_stats.call(perf_stats)
except:
logger.error("Exception while processing engine notification:\n%s\n==========\n%s", request, traceback.format_exc())
except: # pylint: disable=bare-except
logger.error(
"Exception while processing engine notification:\n%s\n==========\n%s",
request, traceback.format_exc())
async def create_realm(
self, *, name: str, parent: Optional[str] = None, enable_player: bool = False,

18
noisicaa/audioproc/audioproc_client_test.py

@ -27,8 +27,10 @@ import async_generator
from noisidev import unittest
from noisidev import unittest_mixins
from noisicaa.constants import TEST_OPTS
from noisicaa import lv2
from noisicaa import node_db
from noisicaa import editor_main_pb2
from . import audioproc_client
from .public import engine_notification_pb2
@ -88,7 +90,17 @@ class AudioProcClientTest(
name='audioproc',
entry='noisicaa.audioproc.audioproc_process.AudioProcSubprocess')
client = audioproc_client.AudioProcClient(self.loop, self.server)
create_urid_mapper_response = editor_main_pb2.CreateProcessResponse()
await self.process_manager_client.call(
'CREATE_URID_MAPPER_PROCESS', None, create_urid_mapper_response)
urid_mapper_address = create_urid_mapper_response.address
urid_mapper = lv2.ProxyURIDMapper(
server_address=urid_mapper_address,
tmp_dir=TEST_OPTS.TMP_DIR)
await urid_mapper.setup(self.loop)
client = audioproc_client.AudioProcClient(self.loop, self.server, urid_mapper)
await client.setup()
await client.connect(proc.address)
try:
@ -99,6 +111,8 @@ class AudioProcClientTest(
await client.disconnect()
await client.cleanup()
await urid_mapper.cleanup(self.loop)
await proc.shutdown()
async def test_realms(self):

6
noisicaa/builtin_nodes/control_track/server_impl_test.py

@ -55,7 +55,8 @@ class ControlTrackConnectorTest(unittest_mixins.NodeDBMixin, unittest.AsyncTestC
self.messages.append(self.WhichOneof(msg))
def test_messages_on_mutations(self):
connector = self.track.create_node_connector(message_cb=self.message_cb)
connector = self.track.create_node_connector(
message_cb=self.message_cb, audioproc_client=None)
try:
self.assertEqual(connector.init(), [])
@ -110,7 +111,8 @@ class ControlTrackConnectorTest(unittest_mixins.NodeDBMixin, unittest.AsyncTestC
self.pool.create(
server_impl.ControlPoint, time=audioproc.MusicalTime(2, 4), value=0.8))
connector = self.track.create_node_connector(message_cb=self.message_cb)
connector = self.track.create_node_connector(
message_cb=self.message_cb, audioproc_client=None)
try:
messages = connector.init()

3
noisicaa/builtin_nodes/midi_source/server_impl_test.py

@ -41,7 +41,8 @@ class ConnectorTest(unittest_mixins.NodeDBMixin, unittest.AsyncTestCase):
self.messages.append(msg)
def test_messages_on_mutations(self):
connector = self.node.create_node_connector(message_cb=self.message_cb)
connector = self.node.create_node_connector(
message_cb=self.message_cb, audioproc_client=None)
try:
self.assertEqual(
connector.init(),

6
noisicaa/builtin_nodes/sample_track/server_impl_test.py

@ -66,7 +66,8 @@ class SampleTrackConnectorTest(unittest_mixins.NodeDBMixin, unittest.AsyncTestCa
self.messages.append(self.WhichOneof(msg))
def test_messages_on_mutations(self):
connector = self.track.create_node_connector(message_cb=self.message_cb)
connector = self.track.create_node_connector(
message_cb=self.message_cb, audioproc_client=None)
try:
self.assertEqual(connector.init(), [])
@ -125,7 +126,8 @@ class SampleTrackConnectorTest(unittest_mixins.NodeDBMixin, unittest.AsyncTestCa
server_impl.SampleRef,
time=audioproc.MusicalTime(2, 4), sample=self.sample2))
connector = self.track.create_node_connector(message_cb=self.message_cb)
connector = self.track.create_node_connector(
message_cb=self.message_cb, audioproc_client=None)
try:
messages = connector.init()

3
noisicaa/builtin_nodes/score_track/server_impl_test.py

@ -42,7 +42,8 @@ class ScoreTrackConnectorTest(unittest_mixins.NodeDBMixin, unittest.AsyncTestCas
messages = [] # type: List[str]
connector = tr.create_node_connector(message_cb=messages.append)
connector = tr.create_node_connector(
message_cb=messages.append, audioproc_client=None)
try:
messages.extend(connector.init())

3
noisicaa/core/__init__.py

@ -28,8 +28,11 @@ from .ipc_pb2 import (
)
from .callbacks import (
CallbackMap,
AsyncCallback,
Callback,
BaseListener,
Listener,
AsyncListener,
)
from .perf_stats import (
PyPerfStats as PerfStats,

56
noisicaa/core/callbacks.py

@ -24,20 +24,24 @@ import collections
import logging
import random
import threading
from typing import Any, Dict, Callable, Generic, TypeVar
from typing import Any, Dict, Callable, Awaitable, Generic, TypeVar
logger = logging.getLogger(__name__)
K = TypeVar('K')
T = TypeVar('T')
CB = TypeVar('CB')
L = TypeVar('L', bound='BaseListener')
R = TypeVar('R', bound='BaseCallback')
CallbackFunc = Callable[[T], None]
AsyncCallbackFunc = Callable[[T], Awaitable]
class Listener(Generic[T]):
class BaseListener(Generic[T, CB, R]):
"""Opaque container for a callback."""
def __init__(self, registry: 'Callback[T]', callback: CallbackFunc) -> None:
def __init__(self, registry: R, callback: CB) -> None:
self.__registry = registry
self.id = random.getrandbits(64)
self.callback = callback
@ -46,16 +50,24 @@ class Listener(Generic[T]):
self.__registry.remove(self)
class Callback(Generic[T]):
class Listener(Generic[T], BaseListener[T, CallbackFunc, 'Callback']):
pass
class AsyncListener(Generic[T], BaseListener[T, AsyncCallbackFunc, 'AsyncCallback']):
pass
class BaseCallback(Generic[T, CB, L]):
def __init__(self) -> None:
self.__lock = threading.RLock()
self.__listeners = collections.OrderedDict() # type: Dict[int, Listener]
self.__listeners = collections.OrderedDict() # type: Dict[int, L]
def clear(self) -> None:
with self.__lock:
self.__listeners.clear()
def add(self, callback: CallbackFunc) -> Listener[T]:
def add(self, callback: CB) -> L:
"""Register a new callback.
Args:
@ -65,20 +77,17 @@ class Callback(Generic[T]):
A listener, which should be used to unregister this callback.
"""
if not callable(callback):
raise TypeError(type(callback))
listener = Listener(self, callback)
listener = self._create_listener(callback)
with self.__lock:
self.__listeners[listener.id] = listener
return listener
def add_listener(self, listener: Listener[T]) -> None:
def add_listener(self, listener: L) -> None:
with self.__lock:
self.__listeners[listener.id] = listener
def remove(self, listener: Listener[T]) -> None:
def remove(self, listener: L) -> None:
"""Remove a callback.
Alternatively you can just call remove() on the listener returned by
@ -94,7 +103,10 @@ class Callback(Generic[T]):
with self.__lock:
del self.__listeners[listener.id]
def call(self, *args: Any, **kwargs: Any) -> None:
def _create_listener(self, callback: CB) -> L:
raise NotImplementedError
def _call_sync(self, *args: Any, **kwargs: Any) -> None:
"""Call all callbacks registered for a given target.
This method should only be called by the owner of the registry.
@ -108,7 +120,7 @@ class Callback(Generic[T]):
for listener in self.__listeners.values():
listener.callback(*args, **kwargs) # type: ignore
async def async_call(self, *args: Any, **kwargs: Any) -> None:
async def _call_async(self, *args: Any, **kwargs: Any) -> None:
"""Call all callbacks registered for a given target.
This method should only be called by the owner of the registry.
@ -123,6 +135,22 @@ class Callback(Generic[T]):
await listener.callback(*args, **kwargs) # type: ignore
class Callback(Generic[T], BaseCallback[T, CallbackFunc, Listener[T]]):
def _create_listener(self, callback: CallbackFunc) -> Listener[T]:
return Listener[T](self, callback)
def call(self, *args: Any, **kwargs: Any) -> None:
self._call_sync(*args, **kwargs)
class AsyncCallback(Generic[T], BaseCallback[T, AsyncCallbackFunc, AsyncListener[T]]):
def _create_listener(self, callback: AsyncCallbackFunc) -> AsyncListener[T]:
return AsyncListener[T](self, callback)
async def call(self, *args: Any, **kwargs: Any) -> None:
await self._call_async(*args, **kwargs)
class CallbackMap(Generic[K, T]):
def __init__(self) -> None:
self.__callbacks = {} # type: Dict[K, Callback[T]]

1
noisicaa/music/commands_test.py

@ -51,6 +51,7 @@ class CommandsTestMixin(
async def setup_testcase(self):
self.setup_node_db_process(inline=True)
self.setup_urid_mapper_process(inline=True)
self.setup_project_process(inline=True)
create_project_process_request = editor_main_pb2.CreateProjectProcessRequest(

2
noisicaa/music/player.py

@ -55,7 +55,7 @@ class Player(object):
self.realm = realm
self.session_values = session_values
self.__listeners = {} # type: Dict[str, core.Listener]
self.__listeners = {} # type: Dict[str, core.BaseListener]
self.id = uuid.uuid4().hex

11
noisicaa/music/player_test.py

@ -25,11 +25,14 @@ import logging
from noisidev import unittest
from noisidev import unittest_mixins
from noisicaa.constants import TEST_OPTS
from noisicaa import audioproc
from noisicaa.core import ipc
from noisicaa.core import session_data_pb2
from noisicaa.builtin_nodes.score_track import server_impl as score_track
from . import project
from . import player
from . import session_value_store
logger = logging.getLogger(__name__)
@ -51,6 +54,10 @@ class MockAudioProcClient(audioproc.AbstractAudioProcClient): # pylint: disable
assert realm == 'player'
assert isinstance(properties, audioproc.ProjectProperties)
async def set_session_values(self, realm, session_values):
assert realm == 'player'
assert all(isinstance(value, session_data_pb2.SessionValue) for value in session_values)
class PlayerTest(unittest_mixins.ServerMixin, unittest.AsyncTestCase):
async def setup_testcase(self):
@ -60,6 +67,9 @@ class PlayerTest(unittest_mixins.ServerMixin, unittest.AsyncTestCase):
cb_endpoint = ipc.ServerEndpoint('player_cb')
self.cb_endpoint_address = await self.server.add_endpoint(cb_endpoint)
self.session_values = session_value_store.SessionValueStore(self.loop, 'test')
await self.session_values.init(TEST_OPTS.TMP_DIR)
async def cleanup_testcase(self):
await self.server.remove_endpoint('player_cb')
@ -69,6 +79,7 @@ class PlayerTest(unittest_mixins.ServerMixin, unittest.AsyncTestCase):
callback_address=self.cb_endpoint_address,
event_loop=self.loop,
audioproc_client=MockAudioProcClient(),
session_values=self.session_values,
realm='player')
try:
await p.setup()

2
noisicaa/music/project_client_test.py

@ -51,6 +51,7 @@ class ProjectClientTestBase(
async def setup_testcase(self):
self.setup_node_db_process(inline=True)
self.setup_urid_mapper_process(inline=True)
self.setup_project_process(inline=True)
await self.connect_project_client()
@ -153,7 +154,6 @@ class RenderTest(ProjectClientTestBase):
response.status = True
async def setup_testcase(self):
self.setup_urid_mapper_process(inline=True)
self.setup_audioproc_process(inline=True)
await self.client.create_inmemory()

1
noisicaa/music/project_integration_test.py

@ -54,6 +54,7 @@ class ProjectIntegrationTest(
async def setup_testcase(self):
self.setup_node_db_process(inline=True)
self.setup_urid_mapper_process(inline=True)
self.setup_project_process(inline=True)
def create_pool_snapshot(self, pool):

11
noisicaa/music/project_process.py

@ -23,11 +23,9 @@
import asyncio
import copy
import logging
import os
import os.path
import traceback
import typing
from typing import Any, Type, Dict, Iterable, TypeVar
from typing import Any, Dict, Type, Iterable, TypeVar
from noisicaa import core
from noisicaa.core import empty_message_pb2
@ -101,17 +99,17 @@ class Session(ipc.CallbackSessionMixin, ipc.Session):
await self.callback('PROJECT_MUTATIONS', mutations)
async def init_session_data(self, data_dir: str) -> None:
initial_values = await self.session_values.init(data_dir)
await self.session_values.init(data_dir)
await self.callback(
'SESSION_DATA_MUTATION',
project_process_pb2.SessionDataMutation(session_values=initial_values))
project_process_pb2.SessionDataMutation(session_values=self.session_values.values()))
async def set_values(
self, session_values: Iterable[session_data_pb2.SessionValue],
from_client: bool = False
) -> None:
changes = await self.session_values.set_values(session_values)
await self.session_values.set_values(session_values)
if not from_client:
self.async_callback(
@ -526,6 +524,7 @@ class ProjectProcess(core.ProcessBase):
event_loop=self.event_loop,
callback_address=request.callback_address,
render_settings=request.settings,
urid_mapper=self.__ctxt.urid_mapper,
)
await renderer.run()

2
noisicaa/music/project_process_context.py

@ -31,6 +31,6 @@ if typing.TYPE_CHECKING:
class ProjectProcessContext(object):
def __init__(self) -> None:
self.node_db = None # type: node_db.NodeDBClient
self.urid_mapper = None # type: lv2.URIDMapper
self.urid_mapper = None # type: lv2.ProxyURIDMapper
self.pool = None # type: pmodel.Pool
self.project = None # type: project.BaseProject

11
noisicaa/music/render.py

@ -35,11 +35,13 @@ from typing import cast, Any, Union, Callable, Awaitable, List, Tuple, Text
from noisicaa.core.typing_extra import down_cast
from noisicaa.core import ipc
from noisicaa import audioproc
from noisicaa import lv2
from noisicaa import editor_main_pb2
from . import player
from . import render_settings_pb2
from . import pmodel
from . import project_process_pb2
from . import session_value_store
logger = logging.getLogger(__name__)
@ -364,6 +366,7 @@ class Renderer(object):
tmp_dir: str,
server: ipc.Server,
manager: ipc.Stub,
urid_mapper: lv2.URIDMapper,
event_loop: asyncio.AbstractEventLoop
) -> None:
self.__project = project
@ -372,6 +375,7 @@ class Renderer(object):
self.__tmp_dir = tmp_dir
self.__server = server
self.__manager = manager
self.__urid_mapper = urid_mapper
self.__event_loop = event_loop
self.__failed = asyncio.Event(loop=self.__event_loop)
@ -394,6 +398,7 @@ class Renderer(object):
self.__player = None # type: player.Player
self.__next_progress_update = None # type: Tuple[fractions.Fraction, float]
self.__progress_pump_task = None # type: asyncio.Task
self.__session_values = None # type: session_value_store.SessionValueStore
def __fail(self, msg: str) -> None:
logger.error("Encoding failed: %s", msg)
@ -535,7 +540,8 @@ class Renderer(object):
'CREATE_AUDIOPROC_PROCESS', create_audioproc_request, create_audioproc_response)
self.__audioproc_address = create_audioproc_response.address
self.__audioproc_client = audioproc.AudioProcClient(self.__event_loop, self.__server)
self.__audioproc_client = audioproc.AudioProcClient(
self.__event_loop, self.__server, self.__urid_mapper)
self.__audioproc_client.engine_notifications.add(self.__handle_engine_notification)
await self.__audioproc_client.setup()
@ -546,10 +552,13 @@ class Renderer(object):
'renderer',
audioproc.BackendSettings(datastream_address=self.__datastream_address))
self.__session_values = session_value_store.SessionValueStore(self.__event_loop, 'render')
self.__player = player.Player(
project=self.__project,
event_loop=self.__event_loop,
audioproc_client=self.__audioproc_client,
session_values=self.__session_values,
realm='root')
await self.__player.setup()

16
noisicaa/music/session_value_store.py

@ -40,9 +40,9 @@ class SessionValueStore(object):
self.__session_data = {} # type: Dict[str, session_data_pb2.SessionValue]
self.__session_data_path = None # type: str
self.values_changed = core.Callback[List[session_data_pb2.SessionValue]]()
self.values_changed = core.AsyncCallback[List[session_data_pb2.SessionValue]]()
async def init(self, data_dir: str) -> List[session_data_pb2.SessionValue]:
async def init(self, data_dir: str) -> None:
self.__session_data = {}
if data_dir is not None:
@ -63,16 +63,14 @@ class SessionValueStore(object):
for session_value in checkpoint.session_values:
self.__session_data[session_value.name] = session_value
result = list(self.__session_data.values())
await self.values_changed.async_call(result)
return result
await self.values_changed.call(self.values())
def values(self) -> List[session_data_pb2.SessionValue]:
return list(self.__session_data.values())
async def set_values(
self, session_values: Iterable[session_data_pb2.SessionValue],
) -> List[session_data_pb2.SessionValue]:
) -> None:
assert self.__session_data_path is not None
changes = {}
@ -83,7 +81,7 @@ class SessionValueStore(object):
changes[session_value.name] = session_value
if not changes:
return []
return
self.__session_data.update(changes)
@ -92,9 +90,7 @@ class SessionValueStore(object):
with open(os.path.join(self.__session_data_path, 'checkpoint'), 'wb') as fp:
fp.write(checkpoint.SerializeToString())
result = list(changes.values())
await self.values_changed.async_call(result)
return result
await self.values_changed.call(list(changes.values()))
async def set_value(self, session_value: session_data_pb2.SessionValue) -> None:
await self.set_values([session_value])

2
noisicaa/ui/editor_app.py

@ -26,7 +26,7 @@ import pprint
import sys
import traceback
import types
from typing import Any, Optional, Dict, Callable, Sequence, Type
from typing import Any, Optional, Callable, Sequence, Type
from PyQt5 import QtCore
from PyQt5 import QtWidgets

3
noisidev/uitest.py

@ -26,6 +26,7 @@ import inspect
import logging
import os.path
import uuid
from typing import Any, Dict
from PyQt5 import QtWidgets
import asynctest
@ -120,6 +121,7 @@ class TestContext(object):
class MockAudioProcClient(audioproc.AbstractAudioProcClient): # pylint: disable=abstract-method
def __init__(self):
super().__init__()
self.node_messages = core.CallbackMap[str, Dict[str, Any]]()
self.node_state_changed = core.CallbackMap[str, audioproc.NodeStateChange]()
@ -188,6 +190,7 @@ class UITestCase(unittest_mixins.ProcessManagerMixin, qttest.QtTestCase):
async def setup_testcase(self):
self.setup_node_db_process(inline=True)
self.setup_urid_mapper_process(inline=True)
self.setup_instrument_db_process(inline=True)
self.process = MockProcess(

Loading…
Cancel
Save