Store temp files (sockets, pipes) in a new directory and clean up on exit.

looper
Ben Niemann 2018-01-20 07:57:25 +01:00
parent 4ecce2673a
commit 825dbf5b58
25 changed files with 197 additions and 115 deletions

View File

@ -11,16 +11,6 @@
Sometimes still hangs on shutdown. Subprocess calls _exit(), but manager doesn't seem to get the
SIGCHLD. Non-deterministic and rare, so hard to debug...
* Cleanup temp files on shutdown :FR:
- store sockets, etc. in separate directory
- directory is chosen by ProcessManager and propagated to subprocesses
- cleaned up on exit
- still leaves a dead directory around on unclean shutdown, SIGKILL, etc.
- put in /tmp and rely on OS to cleanup junk
- on startup try to find dead directories and clean them up
- take a lock, which is automatically released by OS
- in tests, runtests manages directory and same principles apply
* Visual feedback when opening project :FR:
- Create tab first, show some spinning wheel, until project is setup completely.
@ -199,6 +189,12 @@ Is there some cross-compiler/-platform header to provide this functionality?
* Make sample rate configurable :FR:
- all processors need to cleanup/setup on changes
* Cleanup temp files on shutdown :FR:
- still leaves a dead directory around on unclean shutdown, SIGKILL, etc.
- put in /tmp and rely on OS to cleanup junk
- on startup try to find dead directories and clean them up
- take a lock, which is automatically released by OS
* crash on shutdown, when csound wants to log after LogSink has been destroyed :CRASH:
- is HostData properly cleaned up?
* turn off all notes when playback gets paused :FR:

View File

@ -24,6 +24,7 @@ import asyncio
from noisidev import unittest
from noisicaa import node_db
from noisicaa.constants import TEST_OPTS
from noisicaa.core import ipc
from . import audioproc_process
@ -34,7 +35,7 @@ class TestClientImpl(object):
def __init__(self, event_loop):
super().__init__()
self.event_loop = event_loop
self.server = ipc.Server(self.event_loop, 'client')
self.server = ipc.Server(self.event_loop, 'client', TEST_OPTS.TMP_DIR)
async def setup(self):
await self.server.setup()
@ -48,6 +49,13 @@ class TestClient(audioproc_client.AudioProcClientMixin, TestClientImpl):
class ProxyTest(unittest.AsyncTestCase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.client = None
self.audioproc_task = None
self.audioproc_process = None
async def setup_testcase(self):
self.passthru_description = node_db.ProcessorDescription(
processor_name='null',
@ -67,19 +75,22 @@ class ProxyTest(unittest.AsyncTestCase):
])
self.audioproc_process = audioproc_process.AudioProcProcess(
name='audioproc', event_loop=self.loop, manager=None)
name='audioproc', event_loop=self.loop, manager=None, tmp_dir=TEST_OPTS.TMP_DIR)
await self.audioproc_process.setup()
self.audioproc_task = self.loop.create_task(
self.audioproc_process.run())
self.audioproc_task = self.loop.create_task(self.audioproc_process.run())
self.client = TestClient(self.loop)
await self.client.setup()
await self.client.connect(self.audioproc_process.server.address)
async def cleanup_testcase(self):
await self.client.disconnect(shutdown=True)
await self.client.cleanup()
await asyncio.wait_for(self.audioproc_task, None)
await self.audioproc_process.cleanup()
if self.client is not None:
await self.client.disconnect(shutdown=True)
await self.client.cleanup()
if self.audioproc_process is not None:
if self.audioproc_task is not None:
await self.audioproc_process.shutdown()
await asyncio.wait_for(self.audioproc_task, None)
await self.audioproc_process.cleanup()
async def test_add_remove_node(self):
await self.client.add_node(id='test', description=self.passthru_description)

View File

@ -217,6 +217,13 @@ class AudioProcProcess(core.ProcessBase):
logger.info("Pipeline finished.")
self.__shutdown_complete.set()
async def shutdown(self):
logger.info("Shutdown received.")
self.__shutting_down.set()
logger.info("Waiting for shutdown to complete...")
await self.__shutdown_complete.wait()
logger.info("Shutdown complete.")
def get_session(self, session_id):
try:
return self.sessions[session_id]
@ -274,11 +281,7 @@ class AudioProcProcess(core.ProcessBase):
del self.sessions[session_id]
async def handle_shutdown(self):
logger.info("Shutdown received.")
self.__shutting_down.set()
logger.info("Waiting for shutdown to complete...")
await self.__shutdown_complete.wait()
logger.info("Shutdown complete.")
await self.shutdown()
async def handle_pipeline_mutation(self, session_id, mutation):
self.get_session(session_id)

View File

@ -21,19 +21,17 @@
import os
import os.path
import uuid
import tempfile
import threading
from noisidev import unittest
from noisicaa.constants import TEST_OPTS
from . import audio_stream
class TestAudioStream(unittest.TestCase):
def setup_testcase(self):
self.address = os.fsencode(
os.path.join(
tempfile.gettempdir(),
'test.%s.pipe' % uuid.uuid4().hex))
os.path.join(TEST_OPTS.TMP_DIR, 'test.%s.pipe' % uuid.uuid4().hex))
def test_client_to_server(self):
server = audio_stream.AudioStream.create_server(self.address)

View File

@ -23,13 +23,13 @@ from libcpp.memory cimport unique_ptr
import os
import os.path
import struct
import tempfile
import uuid
import threading
import capnp
from noisidev import unittest
from noisicaa.constants import TEST_OPTS
from noisicaa.core.status cimport *
from noisicaa.core.status import *
from . import audio_stream
@ -52,9 +52,7 @@ class TestIPCBackend(unittest.TestCase):
vm.reset(new VM(host_data.get(), NULL))
cdef PyBackendSettings backend_settings = PyBackendSettings(
ipc_address=os.path.join(
tempfile.gettempdir(),
'test.%s.pipe' % uuid.uuid4().hex))
ipc_address=os.path.join(TEST_OPTS.TMP_DIR, 'test.%s.pipe' % uuid.uuid4().hex))
cdef StatusOr[Backend*] stor_backend = Backend.create(b"ipc", backend_settings.get())
check(stor_backend)

View File

@ -26,12 +26,12 @@ import threading
import uuid
import os
import os.path
import tempfile
import struct
import capnp
from noisidev import unittest
from noisicaa.constants import TEST_OPTS
from noisicaa.core.status cimport *
from noisicaa.core.status import ConnectionClosed
from . import block_data_capnp
@ -51,9 +51,7 @@ class TestProcessorIPC(unittest.TestCase):
host_data.reset(new HostData())
address = os.fsencode(
os.path.join(
tempfile.gettempdir(),
'test.%s.pipe' % uuid.uuid4().hex))
os.path.join(TEST_OPTS.TMP_DIR, 'test.%s.pipe' % uuid.uuid4().hex))
cdef StatusOr[Processor*] stor_processor = Processor.create(
b'test_node', host_data.get(), b'ipc')

View File

@ -59,6 +59,7 @@ class TEST_OPTS(object):
WRITE_PERF_STATS = False
ENABLE_PROFILER = False
PLAYBACK_BACKEND = 'null'
TMP_DIR = '/tmp'
# Cleanup namespace
del os

View File

@ -28,7 +28,6 @@ import os
import os.path
import pickle
import pprint
import tempfile
import traceback
import uuid
@ -138,17 +137,13 @@ class Server(object):
pickle.dumps, protocol=pickle.HIGHEST_PROTOCOL)
deserialize = pickle.loads
def __init__(self, event_loop, name, socket_dir=None):
def __init__(self, event_loop, name, socket_dir):
self.event_loop = event_loop
self.name = name
self.logger = logger.getChild(name)
if socket_dir is None:
socket_dir = tempfile.gettempdir()
self.address = os.path.join(
socket_dir, '%s.%s.sock' % (self.name, uuid.uuid4().hex))
self.address = os.path.join(socket_dir, '%s.%s.sock' % (self.name, uuid.uuid4().hex))
self._next_connection_id = 0
self._server = None

View File

@ -21,12 +21,13 @@
# @end:license
from noisidev import unittest
from noisicaa.constants import TEST_OPTS
from . import ipc
class IPCTest(unittest.AsyncTestCase):
async def test_ping(self):
async with ipc.Server(self.loop, name='test') as server:
async with ipc.Server(self.loop, name='test', socket_dir=TEST_OPTS.TMP_DIR) as server:
async with ipc.Stub(self.loop, server.address) as stub:
await stub.ping()
await stub.ping()
@ -36,7 +37,7 @@ class IPCTest(unittest.AsyncTestCase):
await stub.ping()
async def test_command(self):
async with ipc.Server(self.loop, name='test') as server:
async with ipc.Server(self.loop, name='test', socket_dir=TEST_OPTS.TMP_DIR) as server:
server.add_command_handler('foo', lambda: None)
server.add_command_handler('bar', lambda: 'yo')
server.add_command_handler('gnurz', lambda a: a + 1)
@ -47,14 +48,14 @@ class IPCTest(unittest.AsyncTestCase):
self.assertEqual(await stub.call('gnurz', 3), 4)
async def test_remote_exception(self):
async with ipc.Server(self.loop, name='test') as server:
async with ipc.Server(self.loop, name='test', socket_dir=TEST_OPTS.TMP_DIR) as server:
server.add_command_handler('foo', lambda: 1/0)
async with ipc.Stub(self.loop, server.address) as stub:
with self.assertRaises(ipc.RemoteException):
await stub.call('foo')
async def test_async_handler(self):
async with ipc.Server(self.loop, name='test') as server:
async with ipc.Server(self.loop, name='test', socket_dir=TEST_OPTS.TMP_DIR) as server:
async def handler(arg):
return arg + 1
server.add_command_handler('foo', handler)

View File

@ -28,9 +28,11 @@ import logging
import os
import pickle
import select
import shutil
import signal
import struct
import sys
import tempfile
import threading
import time
import traceback
@ -337,7 +339,8 @@ class ProcessManager(object):
self._processes = {}
self._sigchld_received = asyncio.Event()
self._server = ipc.Server(event_loop, 'manager')
self._tmp_dir = None
self._server = None
if collect_stats:
self._stats_collector = stats.Collector()
@ -355,6 +358,12 @@ class ProcessManager(object):
self._event_loop.add_signal_handler(
signal.SIGCHLD, self.sigchld_handler)
self._tmp_dir = tempfile.mkdtemp(
prefix='noisicaa-%s-%d-' % (time.strftime('%Y%m%d-%H%M%S'), os.getpid()))
logger.info("Using %s for temp files.", self._tmp_dir)
self._server = ipc.Server(self._event_loop, 'manager', socket_dir=self._tmp_dir)
self._server.add_command_handler(
'STATS_LIST', self.handle_stats_list)
self._server.add_command_handler(
@ -370,10 +379,17 @@ class ProcessManager(object):
self._child_collector.cleanup()
await self.terminate_all_children()
await self._server.cleanup()
if self._server is not None:
await self._server.cleanup()
self._server.remove_command_handler('STATS_LIST')
self._server.remove_command_handler('STATS_FETCH')
self._server.remove_command_handler('STATS_LIST')
self._server.remove_command_handler('STATS_FETCH')
self._server = None
if self._tmp_dir is not None:
shutil.rmtree(self._tmp_dir)
self._tmp_dir = None
async def __aenter__(self):
await self.setup()
@ -473,7 +489,8 @@ class ProcessManager(object):
mod = importlib.import_module(mod_name)
cls = getattr(mod, cls_name)
impl = cls(
name=name, manager_address=manager_address, **kwargs)
name=name, manager_address=manager_address, tmp_dir=self._tmp_dir,
**kwargs)
rc = impl.main(child_connection)
@ -662,14 +679,15 @@ class ChildConnectionHandler(object):
class ProcessBase(object):
def __init__(self, *, name, manager, event_loop):
def __init__(self, *, name, manager, event_loop, tmp_dir):
self.name = name
self.manager = manager
self.event_loop = event_loop
self.tmp_dir = tmp_dir
self.server = None
async def setup(self):
self.server = ipc.Server(self.event_loop, self.name)
self.server = ipc.Server(self.event_loop, self.name, socket_dir=self.tmp_dir)
await self.server.setup()
async def cleanup(self):

View File

@ -49,17 +49,6 @@ class Editor(object):
self.event_loop = asyncio.get_event_loop()
self.manager = process_manager.ProcessManager(self.event_loop)
self.manager.server.add_command_handler(
'CREATE_PROJECT_PROCESS', self.handle_create_project_process)
self.manager.server.add_command_handler(
'CREATE_AUDIOPROC_PROCESS',
self.handle_create_audioproc_process)
self.manager.server.add_command_handler(
'CREATE_NODE_DB_PROCESS',
self.handle_create_node_db_process)
self.manager.server.add_command_handler(
'CREATE_INSTRUMENT_DB_PROCESS',
self.handle_create_instrument_db_process)
self.stop_event = asyncio.Event()
self.returncode = 0
@ -86,6 +75,18 @@ class Editor(object):
async def run_async(self):
async with self.manager:
self.manager.server.add_command_handler(
'CREATE_PROJECT_PROCESS', self.handle_create_project_process)
self.manager.server.add_command_handler(
'CREATE_AUDIOPROC_PROCESS',
self.handle_create_audioproc_process)
self.manager.server.add_command_handler(
'CREATE_NODE_DB_PROCESS',
self.handle_create_node_db_process)
self.manager.server.add_command_handler(
'CREATE_INSTRUMENT_DB_PROCESS',
self.handle_create_instrument_db_process)
task = self.event_loop.create_task(self.launch_ui())
task.add_done_callback(self.ui_closed)
await self.stop_event.wait()

View File

@ -24,6 +24,7 @@ import asyncio
from noisidev import unittest
from noisicaa.core import ipc
from noisicaa.constants import TEST_OPTS
from . import process
from . import client
@ -33,7 +34,7 @@ class TestClientImpl(object):
def __init__(self, event_loop):
super().__init__()
self.event_loop = event_loop
self.server = ipc.Server(self.event_loop, 'client')
self.server = ipc.Server(self.event_loop, 'client', socket_dir=TEST_OPTS.TMP_DIR)
async def setup(self):
await self.server.setup()
@ -47,22 +48,32 @@ class TestClient(client.InstrumentDBClientMixin, TestClientImpl):
class InstrumentDBClientTest(unittest.AsyncTestCase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.process = None
self.process_task = None
self.client = None
async def setup_testcase(self):
self.process = process.InstrumentDBProcess(
name='instrument_db', event_loop=self.loop, manager=None)
name='instrument_db', event_loop=self.loop, manager=None, tmp_dir=TEST_OPTS.TMP_DIR)
await self.process.setup()
self.process_task = self.loop.create_task(
self.process.run())
self.process_task = self.loop.create_task(self.process.run())
self.client = TestClient(self.loop)
await self.client.setup()
await self.client.connect(self.process.server.address)
async def cleanup_testcase(self):
await self.client.disconnect(shutdown=True)
await self.client.cleanup()
await asyncio.wait_for(self.process_task, None)
await self.process.cleanup()
if self.client is not None:
await self.client.disconnect(shutdown=True)
await self.client.cleanup()
if self.process is not None:
if self.process_task is not None:
await self.process.shutdown()
await asyncio.wait_for(self.process_task, None)
await self.process.cleanup()
async def test_start_scan(self):
await self.client.start_scan()

View File

@ -118,6 +118,13 @@ class InstrumentDBProcess(process_base.InstrumentDBProcessBase):
logger.info("Shutting down...")
self._shutdown_complete.set()
async def shutdown(self):
logger.info("Shutdown received.")
self._shutting_down.set()
logger.info("Waiting for shutdown to complete...")
await self._shutdown_complete.wait()
logger.info("Shutdown complete.")
def get_session(self, session_id):
try:
return self.sessions[session_id]
@ -157,11 +164,7 @@ class InstrumentDBProcess(process_base.InstrumentDBProcessBase):
del self.sessions[session_id]
async def handle_shutdown(self):
logger.info("Shutdown received.")
self._shutting_down.set()
logger.info("Waiting for shutdown to complete...")
await self._shutdown_complete.wait()
logger.info("Shutdown complete.")
await self.shutdown()
async def handle_start_scan(self, session_id):
self.get_session(session_id)

View File

@ -26,7 +26,6 @@ import functools
import logging
import os
import os.path
import tempfile
import time
import uuid
@ -220,7 +219,7 @@ class AudioProcClient(
class Player(object):
def __init__(self, project, callback_address, manager, event_loop):
def __init__(self, *, project, callback_address, manager, event_loop, tmp_dir):
self.project = project
self.manager = manager
self.callback_address = callback_address
@ -230,7 +229,7 @@ class Player(object):
self.__listeners = {}
self.id = uuid.uuid4().hex
self.server = ipc.Server(self.event_loop, 'player')
self.server = ipc.Server(self.event_loop, 'player', socket_dir=tmp_dir)
self.callback_stub = None
@ -241,8 +240,7 @@ class Player(object):
self.audioproc_status_listener = None
self.audioproc_player_state_listener = None
self.audioproc_ready = None
self.audiostream_address = os.path.join(
tempfile.gettempdir(), 'audiostream.%s.pipe' % uuid.uuid4().hex)
self.audiostream_address = os.path.join(tmp_dir, 'audiostream.%s.pipe' % uuid.uuid4().hex)
self.pending_pipeline_mutations = None

View File

@ -37,6 +37,7 @@ from noisicaa.bindings import lv2
from noisicaa.core import ipc
from noisicaa.node_db.private import db as node_db
from noisidev import perf_stats
from noisicaa.constants import TEST_OPTS
from . import project
from . import player
@ -48,7 +49,7 @@ class TestAudioProcClientImpl(object):
def __init__(self, event_loop, name):
super().__init__()
self.event_loop = event_loop
self.server = ipc.Server(self.event_loop, name)
self.server = ipc.Server(self.event_loop, name, socket_dir=TEST_OPTS.tmp_dir)
async def setup(self):
await self.server.setup()

View File

@ -28,6 +28,7 @@ from noisidev import unittest
from noisicaa import core
from noisicaa import audioproc
from noisicaa.core import ipc
from noisicaa.constants import TEST_OPTS
from . import project
from . import player
@ -82,13 +83,13 @@ class PlayerTest(unittest.AsyncTestCase):
self.project = project.BaseProject()
self.player_status_calls = asyncio.Queue()
self.callback_server = ipc.Server(self.loop, 'callback')
self.callback_server = ipc.Server(self.loop, 'callback', socket_dir=TEST_OPTS.TMP_DIR)
self.callback_server.add_command_handler(
'PLAYER_STATUS',
lambda player_id, kwargs: self.player_status_calls.put_nowait(kwargs))
await self.callback_server.setup()
self.audioproc_server = ipc.Server(self.loop, 'audioproc')
self.audioproc_server = ipc.Server(self.loop, 'audioproc', socket_dir=TEST_OPTS.TMP_DIR)
await self.audioproc_server.setup()
self.mock_manager = mock.Mock()
@ -108,7 +109,12 @@ class PlayerTest(unittest.AsyncTestCase):
await self.callback_server.cleanup()
async def test_audio_stream_fails(self):
p = player.Player(self.project, self.callback_server.address, self.mock_manager, self.loop)
p = player.Player(
project=self.project,
callback_address=self.callback_server.address,
manager=self.mock_manager,
event_loop=self.loop,
tmp_dir=TEST_OPTS.TMP_DIR)
try:
with mock.patch('noisicaa.music.player.AudioProcClient', MockAudioProcClient):
await p.setup()

View File

@ -60,10 +60,10 @@ class ObjectProxy(core.ObjectBase):
class ProjectClientBase(object):
def __init__(self, event_loop):
def __init__(self, event_loop, tmp_dir):
super().__init__()
self.event_loop = event_loop
self.server = ipc.Server(self.event_loop, 'client')
self.server = ipc.Server(self.event_loop, 'client', socket_dir=tmp_dir)
async def setup(self):
await self.server.setup()

View File

@ -27,6 +27,7 @@ from noisidev import unittest
from noisicaa import node_db
from noisicaa.core import ipc
from noisicaa.ui import model
from noisicaa.constants import TEST_OPTS
from . import project_process
from . import project_client
@ -36,7 +37,7 @@ class TestClientImpl():
def __init__(self, event_loop):
super().__init__()
self.event_loop = event_loop
self.server = ipc.Server(self.event_loop, 'client')
self.server = ipc.Server(self.event_loop, 'client', socket_dir=TEST_OPTS.tmp_dir)
async def setup(self):
await self.server.setup()

View File

@ -433,7 +433,12 @@ class ProjectProcess(core.ProcessBase):
session = self.get_session(session_id)
assert self.project is not None
p = player.Player(self.project, client_address, self.manager, self.event_loop)
p = player.Player(
project=self.project,
callback_address=client_address,
manager=self.manager,
event_loop=self.event_loop,
tmp_dir=self.tmp_dir)
await p.setup()
session.add_player(p)

View File

@ -23,6 +23,7 @@
import asyncio
from noisidev import unittest
from noisicaa.constants import TEST_OPTS
from noisicaa.core import ipc
from . import process
@ -33,7 +34,7 @@ class TestClientImpl(object):
def __init__(self, event_loop):
super().__init__()
self.event_loop = event_loop
self.server = ipc.Server(self.event_loop, 'client')
self.server = ipc.Server(self.event_loop, 'client', socket_dir=TEST_OPTS.TMP_DIR)
async def setup(self):
await self.server.setup()
@ -47,9 +48,16 @@ class TestClient(client.NodeDBClientMixin, TestClientImpl):
class NodeDBClientTest(unittest.AsyncTestCase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.process = None
self.process_task = None
self.client = None
async def setup_testcase(self):
self.process = process.NodeDBProcess(
name='node_db', event_loop=self.loop, manager=None)
name='node_db', event_loop=self.loop, manager=None, tmp_dir=TEST_OPTS.TMP_DIR)
await self.process.setup()
self.process_task = self.loop.create_task(
self.process.run())
@ -59,10 +67,14 @@ class NodeDBClientTest(unittest.AsyncTestCase):
await self.client.connect(self.process.server.address)
async def cleanup_testcase(self):
await self.client.disconnect(shutdown=True)
await self.client.cleanup()
await asyncio.wait_for(self.process_task, None)
await self.process.cleanup()
if self.client is not None:
await self.client.disconnect(shutdown=True)
await self.client.cleanup()
if self.process is not None:
if self.process_task is not None:
await self.process.shutdown()
await asyncio.wait_for(self.process_task, None)
await self.process.cleanup()
async def test_start_scan(self):
pass #await self.client.start_scan()

View File

@ -76,6 +76,13 @@ class NodeDBProcess(process_base.NodeDBProcessBase):
logger.info("Shutting down...")
self._shutdown_complete.set()
async def shutdown(self):
logger.info("Shutdown received.")
self._shutting_down.set()
logger.info("Waiting for shutdown to complete...")
await self._shutdown_complete.wait()
logger.info("Shutdown complete.")
def get_session(self, session_id):
try:
return self.sessions[session_id]
@ -105,11 +112,7 @@ class NodeDBProcess(process_base.NodeDBProcessBase):
del self.sessions[session_id]
async def handle_shutdown(self):
logger.info("Shutdown received.")
self._shutting_down.set()
logger.info("Waiting for shutdown to complete...")
await self._shutdown_complete.wait()
logger.info("Shutdown complete.")
await self.shutdown()
async def handle_start_scan(self, session_id):
self.get_session(session_id)

View File

@ -151,7 +151,7 @@ class BaseEditorApp(object):
await self.createInstrumentDB()
self.project_registry = project_registry.ProjectRegistry(
self.process.event_loop, self.process.manager, self.node_db)
self.process.event_loop, self.process.tmp_dir, self.process.manager, self.node_db)
self.sequencer = self.createSequencer()

View File

@ -46,9 +46,10 @@ class ProjectClient(music.ProjectClient):
class Project(object):
def __init__(self, path, event_loop, process_manager, node_db):
def __init__(self, path, event_loop, tmp_dir, process_manager, node_db):
self.path = path
self.event_loop = event_loop
self.tmp_dir = tmp_dir
self.process_manager = process_manager
self.node_db = node_db
@ -63,7 +64,9 @@ class Project(object):
self.process_address = await self.process_manager.call(
'CREATE_PROJECT_PROCESS', self.path)
self.client = ProjectClient(
event_loop=self.event_loop, node_db=self.node_db)
event_loop=self.event_loop,
tmp_dir=self.tmp_dir,
node_db=self.node_db)
self.client.cls_map.update(model.cls_map)
await self.client.setup()
await self.client.connect(self.process_address)
@ -86,17 +89,18 @@ class Project(object):
class ProjectRegistry(QtCore.QObject):
projectListChanged = QtCore.pyqtSignal()
def __init__(self, event_loop, process_manager, node_db):
def __init__(self, event_loop, tmp_dir, process_manager, node_db):
super().__init__()
self.event_loop = event_loop
self.tmp_dir = tmp_dir
self.process_manager = process_manager
self.node_db = node_db
self.projects = {}
async def open_project(self, path):
project = Project(
path, self.event_loop, self.process_manager, self.node_db)
path, self.event_loop, self.tmp_dir, self.process_manager, self.node_db)
await project.open()
self.projects[path] = project
self.projectListChanged.emit()
@ -104,7 +108,7 @@ class ProjectRegistry(QtCore.QObject):
async def create_project(self, path):
project = Project(
path, self.event_loop, self.process_manager, self.node_db)
path, self.event_loop, self.tmp_dir, self.process_manager, self.node_db)
await project.create()
self.projects[path] = project
self.projectListChanged.emit()

View File

@ -35,6 +35,7 @@ from noisicaa import core
from noisicaa import instrument_db
from noisicaa import node_db
from noisicaa.runtime_settings import RuntimeSettings
from noisicaa.constants import TEST_OPTS
from .editor_app import BaseEditorApp
from . import model
from . import selection_set
@ -197,13 +198,20 @@ class UITest(unittest.AsyncTestCase):
# than fighting with the garbage collection in pyqt5.
app = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.node_db_process = None
self.instrument_db_process = None
self.process = None
async def setup_testcase(self):
self.node_db_process = TestNodeDBProcess(
name='node_db', event_loop=self.loop, manager=None)
name='node_db', event_loop=self.loop, manager=None, tmp_dir=TEST_OPTS.TMP_DIR)
await self.node_db_process.setup()
self.instrument_db_process = TestInstrumentDBProcess(
name='instrument_db', event_loop=self.loop, manager=None)
name='instrument_db', event_loop=self.loop, manager=None, tmp_dir=TEST_OPTS.TMP_DIR)
await self.instrument_db_process.setup()
self.manager = mock.Mock()
@ -218,7 +226,7 @@ class UITest(unittest.AsyncTestCase):
self.manager.call.side_effect = mock_call
self.process = MockProcess(
name='ui', event_loop=self.loop, manager=self.manager)
name='ui', event_loop=self.loop, manager=self.manager, tmp_dir=TEST_OPTS.TMP_DIR)
await self.process.setup()
if UITest.app is None:
@ -244,11 +252,15 @@ class UITest(unittest.AsyncTestCase):
self.commands = []
async def cleanup_testcase(self):
await UITest.app.cleanup()
UITest.app.process = None
await self.process.cleanup()
await self.instrument_db_process.cleanup()
await self.node_db_process.cleanup()
if UITest.app is not None and UITest.app.process is not None:
await UITest.app.cleanup()
UITest.app.process = None
if self.process is not None:
await self.process.cleanup()
if self.instrument_db_process is not None:
await self.instrument_db_process.cleanup()
if self.node_db_process is not None:
await self.node_db_process.cleanup()
_snapshot_numbers = {}

View File

@ -29,6 +29,7 @@ import os.path
import shutil
import subprocess
import sys
import tempfile
import textwrap
import unittest
@ -127,6 +128,7 @@ def main(argv):
parser.add_argument('--rebuild', nargs='?', type=bool_arg, const=True, default=True)
parser.add_argument('--pedantic', nargs='?', type=bool_arg, const=True, default=False)
parser.add_argument('--builtin-tests', nargs='?', type=bool_arg, const=True, default=True)
parser.add_argument('--keep-temp', nargs='?', type=bool_arg, const=True, default=False)
parser.add_argument('--playback-backend', type=str, default='null')
args = parser.parse_args(argv[1:])
@ -201,6 +203,7 @@ def main(argv):
constants.TEST_OPTS.WRITE_PERF_STATS = args.write_perf_stats
constants.TEST_OPTS.ENABLE_PROFILER = args.profile
constants.TEST_OPTS.PLAYBACK_BACKEND = args.playback_backend
constants.TEST_OPTS.TMP_DIR = tempfile.mkdtemp(prefix='noisicaa-tests-')
from noisicaa import core
core.init_pylogging()
@ -276,6 +279,9 @@ def main(argv):
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
if not args.keep_temp:
shutil.rmtree(constants.TEST_OPTS.TMP_DIR)
if args.coverage:
cov.stop()
cov_data = cov.get_data()