Initial version of reorganized app startup and custom project dialog.

startup
Ben Niemann 4 years ago
parent fcf1db67e6
commit 7e0999fe96

@ -34,11 +34,14 @@ add_python_package(
misc.py
mute_button.py
object_list_editor.py
open_project_dialog.py
open_project_dialog_test.py
piano.py
piano_test.py
pipeline_perf_monitor.py
player_state.py
project_registry.py
project_registry_test.py
project_view.py
project_view_test.py
property_connector.py

@ -20,15 +20,17 @@
#
# @end:license
import asyncio
import logging
import os
import pprint
import sys
import traceback
import types
from typing import Any, Optional, Callable, Sequence, Type
from typing import Any, Optional, Callable, Sequence, List, Type
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from noisicaa import audioproc
@ -40,8 +42,7 @@ from noisicaa import editor_main_pb2
from noisicaa import runtime_settings as runtime_settings_lib
from ..exceptions import RestartAppException, RestartAppCleanException
from ..constants import EXIT_EXCEPTION, EXIT_RESTART, EXIT_RESTART_CLEAN
from .editor_window import EditorWindow
from . import editor_window
from . import audio_thread_profiler
from . import device_list
from . import project_registry
@ -115,13 +116,12 @@ class EditorApp(ui_base.AbstractEditorApp):
self.urid_mapper = None # type: lv2.ProxyURIDMapper
self.__clipboard = None # type: Any
self.__old_excepthook = None # type: Callable[[Type[BaseException], BaseException, types.TracebackType], None]
self.win = None # type: EditorWindow
self.__windows = [] # type: List[editor_window.EditorWindow]
self.pipeline_perf_monitor = None # type: pipeline_perf_monitor.PipelinePerfMonitor
self.stat_monitor = None # type: stat_monitor.StatMonitor
self.default_style = None # type: str
self.devices = None # type: device_list.DeviceList
self.setup_complete = None # type: asyncio.Event
self.__player_state_listeners = core.CallbackMap[str, audioproc.EngineNotification]()
@property
@ -133,83 +133,97 @@ class EditorApp(ui_base.AbstractEditorApp):
self.__old_excepthook = sys.excepthook
sys.excepthook = ExceptHook(self) # type: ignore
await self.createNodeDB()
await self.createInstrumentDB()
await self.createURIDMapper()
self.setup_complete = asyncio.Event(loop=self.process.event_loop)
self.default_style = self.qt_app.style().objectName()
self.project_registry = project_registry.ProjectRegistry(
self.process.event_loop,
self.process.server,
self.process.manager,
self.node_db,
self.urid_mapper,
self.process.tmp_dir)
style_name = self.settings.value('appearance/qtStyle', '')
if style_name:
# TODO: something's wrong with the QtWidgets stubs...
self.qt_app.setStyle(QtWidgets.QStyleFactory.create(style_name)) # type: ignore
self.devices = device_list.DeviceList()
logger.info("Creating initial window...")
win = await self.createWindow()
tab_page = win.addProjectTab("Open project")
# TODO: 'self' is not a QObject in this context.
self.__audio_thread_profiler = audio_thread_profiler.AudioThreadProfiler(
context=self.context)
self.profile_audio_thread_action = QtWidgets.QAction("Profile Audio Thread", self.qt_app)
self.profile_audio_thread_action.triggered.connect(self.onProfileAudioThread)
progress = win.createSetupProgress()
try:
progress.setNumSteps(4)
self.dump_audioproc = QtWidgets.QAction("Dump AudioProc", self.qt_app)
self.dump_audioproc.triggered.connect(self.onDumpAudioProc)
with progress.step("Scanning projects..."):
self.project_registry = project_registry.ProjectRegistry(self.process.event_loop)
await self.project_registry.setup()
tab_page.showOpenDialog(self.project_registry)
await self.createAudioProcProcess()
with progress.step("Scanning nodes and plugins..."):
await self.createNodeDB()
self.default_style = self.qt_app.style().objectName()
with progress.step("Scanning instruments..."):
await self.createInstrumentDB()
style_name = self.settings.value('appearance/qtStyle', '')
if style_name:
# TODO: something's wrong with the QtWidgets stubs...
self.qt_app.setStyle(QtWidgets.QStyleFactory.create(style_name)) # type: ignore
with progress.step("Creating URID mapper..."):
await self.createURIDMapper()
with progress.step("Setting up audio engine..."):
self.devices = device_list.DeviceList()
await self.createAudioProcProcess()
finally:
win.deleteSetupProgress()
self.setup_complete.set()
logger.info("Creating PipelinePerfMonitor.")
self.pipeline_perf_monitor = pipeline_perf_monitor.PipelinePerfMonitor(context=self.context)
# self.__audio_thread_profiler = audio_thread_profiler.AudioThreadProfiler(
# context=self.context)
# self.profile_audio_thread_action = QtWidgets.QAction("Profile Audio Thread", self.qt_app)
# self.profile_audio_thread_action.triggered.connect(self.onProfileAudioThread)
logger.info("Creating StatMonitor.")
self.stat_monitor = stat_monitor.StatMonitor(context=self.context)
# self.dump_audioproc = QtWidgets.QAction("Dump AudioProc", self.qt_app)
# self.dump_audioproc.triggered.connect(self.onDumpAudioProc)
await self.createEditorWindow()
if self.paths:
logger.info("Starting with projects from cmdline.")
for path in self.paths:
if path.startswith('+'):
await self.createProject(path[1:])
else:
await self.openProject(path)
else:
reopen_projects = self.settings.value('opened_projects', [])
for path in reopen_projects or []:
await self.openProject(path)
# logger.info("Creating PipelinePerfMonitor.")
# self.pipeline_perf_monitor = pipeline_perf_monitor.PipelinePerfMonitor(context=self.context)
# logger.info("Creating StatMonitor.")
# self.stat_monitor = stat_monitor.StatMonitor(context=self.context)
# if self.paths:
# logger.info("Starting with projects from cmdline.")
# for path in self.paths:
# if path.startswith('+'):
# await self.createProject(path[1:])
# else:
# await self.openProject(path)
# else:
# reopen_projects = self.settings.value('opened_projects', [])
# for path in reopen_projects or []:
# await self.openProject(path)
async def cleanup(self) -> None:
logger.info("Cleanup app...")
if self.stat_monitor is not None:
self.stat_monitor.storeState()
self.stat_monitor = None
# if self.stat_monitor is not None:
# self.stat_monitor.storeState()
# self.stat_monitor = None
if self.pipeline_perf_monitor is not None:
self.pipeline_perf_monitor.storeState()
self.pipeline_perf_monitor = None
# if self.pipeline_perf_monitor is not None:
# self.pipeline_perf_monitor.storeState()
# self.pipeline_perf_monitor = None
if self.__audio_thread_profiler is not None:
self.__audio_thread_profiler.hide()
self.__audio_thread_profiler = None
# if self.__audio_thread_profiler is not None:
# self.__audio_thread_profiler.hide()
# self.__audio_thread_profiler = None
if self.win is not None:
self.win.storeState()
await self.win.cleanup()
self.win = None
while self.__windows:
win = self.__windows.pop(0)
win.storeState()
await win.cleanup()
self.settings.sync()
self.dumpSettings()
# self.settings.sync()
# self.dumpSettings()
if self.project_registry is not None:
await self.project_registry.close_all()
await self.project_registry.cleanup()
self.project_registry = None
if self.audioproc_client is not None:
@ -241,6 +255,13 @@ class EditorApp(ui_base.AbstractEditorApp):
logger.info("Remove custom excepthook.")
sys.excepthook = self.__old_excepthook # type: ignore
async def createWindow(self) -> editor_window.EditorWindow:
win = editor_window.EditorWindow(context=self.context)
await win.setup()
win.show()
self.__windows.append(win)
return win
def quit(self, exit_code: int = 0) -> None:
# TODO: quit() is not a method of ProcessBase, only in UIProcess. Find some way to
# fix that without a cyclic import.
@ -302,12 +323,6 @@ class EditorApp(ui_base.AbstractEditorApp):
tmp_dir=self.process.tmp_dir)
await self.urid_mapper.setup(self.process.event_loop)
async def createEditorWindow(self) -> None:
logger.info("Creating EditorWindow.")
self.win = EditorWindow(context=self.context)
await self.win.setup()
self.win.show()
def dumpSettings(self) -> None:
for key in self.settings.allKeys():
value = self.settings.value(key)
@ -374,32 +389,32 @@ class EditorApp(ui_base.AbstractEditorApp):
def clipboardContent(self) -> Any:
return self.__clipboard
async def createProject(self, path: str) -> None:
project_connection = self.project_registry.add_project(path)
idx = self.win.addProjectSetupView(project_connection)
await project_connection.create()
await self.win.activateProjectView(idx, project_connection)
self._updateOpenedProjects()
async def openProject(self, path: str) -> None:
project_connection = self.project_registry.add_project(path)
idx = self.win.addProjectSetupView(project_connection)
await project_connection.open()
await self.win.activateProjectView(idx, project_connection)
self._updateOpenedProjects()
def _updateOpenedProjects(self) -> None:
self.settings.setValue(
'opened_projects',
sorted(
project.path
for project in self.project_registry.projects.values()
if project.path))
async def removeProject(self, project_connection: project_registry.Project) -> None:
await self.win.removeProjectView(project_connection)
await self.project_registry.close_project(project_connection)
self._updateOpenedProjects()
# async def createProject(self, path: str) -> None:
# project_connection = self.project_registry.add_project(path)
# idx = self.win.addProjectSetupView(project_connection)
# await project_connection.create()
# await self.win.activateProjectView(idx, project_connection)
# self._updateOpenedProjects()
# async def openProject(self, path: str) -> None:
# project_connection = self.project_registry.add_project(path)
# idx = self.win.addProjectSetupView(project_connection)
# await project_connection.open()
# await self.win.activateProjectView(idx, project_connection)
# self._updateOpenedProjects()
# def _updateOpenedProjects(self) -> None:
# self.settings.setValue(
# 'opened_projects',
# sorted(
# project.path
# for project in self.project_registry.projects.values()
# if project.path))
# async def removeProject(self, project_connection: project_registry.Project) -> None:
# await self.win.removeProjectView(project_connection)
# await self.project_registry.close_project(project_connection)
# self._updateOpenedProjects()
def crashWithMessage(self, title: str, msg: str) -> None:
logger.error('%s: %s', title, msg)

File diff suppressed because it is too large Load Diff

@ -0,0 +1,209 @@
#!/usr/bin/python3
# @begin:license
#
# Copyright (c) 2015-2019, Benjamin Niemann <pink@odahoda.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# @end:license
import logging
from typing import List
from PyQt5.QtCore import Qt
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from . import project_registry as project_registry_lib
from . import slots
logger = logging.getLogger(__name__)
class ProjectListItem(slots.SlotContainer, QtWidgets.QWidget):
selected, setSelected, selectedChanged = slots.slot(bool, 'selected')
def __init__(
self,
project: project_registry_lib.Project,
dialog: 'OpenProjectDialog',
parent: QtWidgets.QWidget = None
) -> None:
super().__init__(parent=parent)
self.__project = project
self.__dialog = dialog
self.__hovered = False
self.setBackgroundRole(QtGui.QPalette.Base)
self.setAutoFillBackground(True)
name = QtWidgets.QLabel(self)
name.setText(self.__project.name)
name_font = QtGui.QFont(name.font())
name_font.setPointSizeF(1.4 * name_font.pointSizeF())
name_font.setBold(True)
name.setFont(name_font)
path = QtWidgets.QLabel(self)
path.setText(self.__project.path)
path_font = QtGui.QFont(path.font())
path.setFont(path_font)
l1 = QtWidgets.QVBoxLayout()
l1.setContentsMargins(0, 0, 0, 0)
l1.addWidget(name)
l1.addWidget(path)
self.setLayout(l1)
self.selectedChanged.connect(lambda _: self.__updateBackgound())
def project(self) -> project_registry_lib.Project:
return self.__project
def enterEvent(self, evt: QtCore.QEvent) -> None:
self.__hovered = True
self.__updateBackgound()
super().enterEvent(evt)
def leaveEvent(self, evt: QtCore.QEvent) -> None:
self.__hovered = False
self.__updateBackgound()
super().leaveEvent(evt)
def __updateBackgound(self) -> None:
if self.selected():
self.setBackgroundRole(QtGui.QPalette.Highlight)
elif self.__hovered:
self.setBackgroundRole(QtGui.QPalette.AlternateBase)
else:
self.setBackgroundRole(QtGui.QPalette.Base)
def mousePressEvent(self, evt: QtGui.QMouseEvent) -> None:
if evt.button() == Qt.LeftButton:
if self.selected():
self.__dialog.selectProject(None)
else:
self.__dialog.selectProject(self)
return
super().mousePressEvent(evt)
def mouseDoubleClickEvent(self, evt: QtGui.QMouseEvent) -> None:
if evt.button() == Qt.LeftButton:
self.__dialog.openProject(self)
return
super().mouseDoubleClickEvent(evt)
class OpenProjectDialog(QtWidgets.QWidget):
projectSelected = QtCore.pyqtSignal(project_registry_lib.Project)
def __init__(
self,
parent: QtWidgets.QWidget = None,
*,
project_registry: project_registry_lib.ProjectRegistry
) -> None:
super().__init__(parent)
self.__project_registry = project_registry
self.__search = QtWidgets.QLineEdit(self)
self.__open_button = QtWidgets.QPushButton(self)
self.__open_button.setText("Open")
self.__open_button.setDisabled(True)
self.__delete_action = QtWidgets.QAction("Delete", self)
self.__archive_action = QtWidgets.QAction("Archive", self)
self.__more_menu = QtWidgets.QMenu()
self.__more_menu.addAction(self.__delete_action)
self.__more_menu.addAction(self.__archive_action)
self.__more_button = QtWidgets.QPushButton(self)
self.__more_button.setText("More")
self.__more_button.setMenu(self.__more_menu)
self.__more_button.setDisabled(True)
self.__list = QtWidgets.QWidget(self)
self.__list.setBackgroundRole(QtGui.QPalette.Base)
self.__list_items = [] # type: List[ProjectListItem]
self.__list_layout = QtWidgets.QVBoxLayout()
self.__list_layout.setContentsMargins(4, 2, 4, 2)
self.__list_layout.setSpacing(4)
self.__list_layout.addStretch(1)
self.__list.setLayout(self.__list_layout)
self.__list_view = QtWidgets.QScrollArea(self)
self.__list_view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.__list_view.setWidgetResizable(True)
self.__list_view.setWidget(self.__list)
l1 = QtWidgets.QVBoxLayout()
l1.setContentsMargins(0, 0, 0, 0)
l1.addWidget(self.__open_button)
l1.addWidget(self.__more_button)
l1.addStretch(1)
l2 = QtWidgets.QHBoxLayout()
l2.setContentsMargins(0, 0, 0, 0)
l2.addWidget(self.__list_view)
l2.addLayout(l1)
l3 = QtWidgets.QVBoxLayout()
l3.setContentsMargins(0, 0, 0, 0)
l3.addWidget(self.__search)
l3.addLayout(l2)
self.setLayout(l3)
for project in self.__project_registry.projects:
self.__addProject(project)
def cleanup(self) -> None:
pass
def selectProject(self, item: ProjectListItem) -> None:
if item is not None:
self.__open_button.setDisabled(False)
self.__more_button.setDisabled(False)
else:
self.__open_button.setDisabled(True)
self.__more_button.setDisabled(True)
for pitem in self.__list_items:
if pitem is item:
pitem.setSelected(True)
else:
pitem.setSelected(False)
def openProject(self, item: ProjectListItem) -> None:
self.projectSelected.emit(item.project())
def __addProject(self, project: project_registry_lib.Project) -> None:
item = ProjectListItem(project, self, self.__list)
self.__list_items.append(item)
if self.__list_layout.count() > 1:
sep = QtWidgets.QFrame()
sep.setFrameShape(QtWidgets.QFrame.HLine)
sep.setFrameShadow(QtWidgets.QFrame.Plain)
self.__list_layout.insertWidget(self.__list_layout.count() - 1, sep)
self.__list_layout.insertWidget(self.__list_layout.count() - 1, item)

@ -0,0 +1,49 @@
#!/usr/bin/python3
# @begin:license
#
# Copyright (c) 2015-2019, Benjamin Niemann <pink@odahoda.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# @end:license
from noisidev import uitest
from noisidev import unittest_mixins
from noisicaa.constants import TEST_OPTS
from . import open_project_dialog
from . import project_registry
class OpenProjectDialogTest(
unittest_mixins.NodeDBMixin,
unittest_mixins.URIDMapperMixin,
unittest_mixins.ServerMixin,
uitest.UITestCase):
async def test_foo(self):
registry = project_registry.ProjectRegistry(
event_loop=self.loop,
tmp_dir=TEST_OPTS.TMP_DIR,
server=self.server,
process_manager=self.process_manager_client,
node_db=self.node_db,
urid_mapper=self.urid_mapper,
)
await registry.setup()
try:
open_project_dialog.OpenProjectDialog(
project_registry=registry)
finally:
await registry.cleanup()

@ -23,7 +23,7 @@
import asyncio
import logging
import os.path
from typing import Dict
from typing import Dict, List, Iterable
from PyQt5 import QtCore
@ -39,44 +39,69 @@ class Project(object):
def __init__(
self,
path: str,
event_loop: asyncio.AbstractEventLoop,
server: ipc.Server,
process_manager: ipc.Stub,
node_db: node_db_lib.NodeDBClient,
urid_mapper: lv2.ProxyURIDMapper,
tmp_dir: str
) -> None:
self.path = path
self.event_loop = event_loop
self.server = server
self.process_manager = process_manager
self.node_db = node_db
self.urid_mapper = urid_mapper
self.tmp_dir = tmp_dir
self.client = None # type: music.ProjectClient
@property
def name(self) -> str:
return os.path.basename(self.path)
return os.path.splitext(os.path.basename(self.path))[0]
async def create_process(self) -> None:
async def __create_process(
self, *,
event_loop: asyncio.AbstractEventLoop,
server: ipc.Server,
process_manager: ipc.Stub,
node_db: node_db_lib.NodeDBClient,
urid_mapper: lv2.ProxyURIDMapper,
tmp_dir: str
) -> None:
self.client = music.ProjectClient(
event_loop=self.event_loop,
server=self.server,
node_db=self.node_db,
urid_mapper=self.urid_mapper,
manager=self.process_manager,
tmp_dir=self.tmp_dir,
event_loop=event_loop,
server=server,
node_db=node_db,
urid_mapper=urid_mapper,
manager=process_manager,
tmp_dir=tmp_dir,
)
await self.client.setup()
async def open(self) -> None:
await self.create_process()
async def open(
self, *,
event_loop: asyncio.AbstractEventLoop,
server: ipc.Server,
process_manager: ipc.Stub,
node_db: node_db_lib.NodeDBClient,
urid_mapper: lv2.ProxyURIDMapper,
tmp_dir: str
) -> None:
await self.__create_process(
event_loop=event_loop,
server=server,
node_db=node_db,
urid_mapper=urid_mapper,
process_manager=process_manager,
tmp_dir=tmp_dir,
)
await self.client.open(self.path)
async def create(self) -> None:
await self.create_process()
async def create(
self, *,
event_loop: asyncio.AbstractEventLoop,
server: ipc.Server,
process_manager: ipc.Stub,
node_db: node_db_lib.NodeDBClient,
urid_mapper: lv2.ProxyURIDMapper,
tmp_dir: str
) -> None:
await self.create_process(
event_loop=event_loop,
server=server,
node_db=node_db,
urid_mapper=urid_mapper,
process_manager=process_manager,
tmp_dir=tmp_dir,
)
await self.client.create(self.path)
async def close(self) -> None:
@ -87,45 +112,61 @@ class Project(object):
class ProjectRegistry(QtCore.QObject):
projectListChanged = QtCore.pyqtSignal()
def __init__(
self,
event_loop: asyncio.AbstractEventLoop,
server: ipc.Server,
process_manager: ipc.Stub,
node_db: node_db_lib.NodeDBClient,
urid_mapper: lv2.ProxyURIDMapper,
tmp_dir: str) -> None:
def __init__(self, event_loop: asyncio.AbstractEventLoop) -> None:
super().__init__()
self.event_loop = event_loop
self.server = server
self.process_manager = process_manager
self.node_db = node_db
self.urid_mapper = urid_mapper
self.tmp_dir = tmp_dir
self.projects = {} # type: Dict[str, Project]
def add_project(self, path: str) -> Project:
project = Project(
path,
self.event_loop,
self.server,
self.process_manager,
self.node_db,
self.urid_mapper,
self.tmp_dir)
self.projects[path] = project
self.projectListChanged.emit()
return project
async def close_project(self, project: Project) -> None:
await project.close()
del self.projects[project.path]
self.projectListChanged.emit()
async def close_all(self) -> None:
for project in list(self.projects.values()):
await self.close_project(project)
self.projectListChanged.emit()
self.__event_loop = event_loop
self.__projects = {} # type: Dict[str, Project]
@property
def projects(self) -> List[Project]:
return list(self.__projects.values())
async def setup(self) -> None:
# TODO: get list of directories from settings
directories = ['~/Music/Noisicaä', '/lala']
projects = await self.__event_loop.run_in_executor(None, self.__scan_projects, directories)
self.__projects = {project.path: project for project in projects}
async def cleanup(self) -> None:
while self.__projects:
_, project = self.__projects.popitem()
await project.close()
def __scan_projects(self, directories: Iterable[str]) -> List[Project]:
projects = [] # type: List[Project]
for directory in directories:
directory = os.path.expanduser(directory)
directory = os.path.abspath(directory)
for dirpath, dirnames, filenames in os.walk(directory):
for filename in filenames:
if filename.endswith('.noise'):
data_dir_name = filename[:-6] + '.data'
if data_dir_name in dirnames:
dirnames.remove(data_dir_name)
filepath = os.path.join(dirpath, filename)
projects.append(Project(filepath))
return projects
# def add_project(self, path: str) -> Project:
# project = Project(
# path,
# self.event_loop,
# self.server,
# self.process_manager,
# self.node_db,
# self.urid_mapper,
# self.tmp_dir)
# self.projects[path] = project
# return project
# async def close_project(self, project: Project) -> None:
# await project.close()
# del self.projects[project.path]
# async def close_all(self) -> None:
# for project in list(self.projects.values()):
# await self.close_project(project)

@ -0,0 +1,49 @@
#!/usr/bin/python3
# @begin:license
#
# Copyright (c) 2015-2019, Benjamin Niemann <pink@odahoda.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# @end:license
import os.path
from PyQt5.QtCore import Qt
from PyQt5 import QtCore
from noisidev import uitest
from noisidev import unittest_mixins
from noisicaa.constants import TEST_OPTS
from . import project_registry
class ProjectRegistryTest(
unittest_mixins.NodeDBMixin,
unittest_mixins.URIDMapperMixin,
unittest_mixins.ServerMixin,
uitest.UITestCase):
async def test_foo(self):
registry = project_registry.ProjectRegistry(
event_loop=self.loop,
tmp_dir=TEST_OPTS.TMP_DIR,
server=self.server,
process_manager=self.process_manager_client,
node_db=self.node_db,
urid_mapper=self.urid_mapper,
)
await registry.setup()
await registry.cleanup()
Loading…
Cancel
Save