async def test_add_and_remove_symbol(app_mock, signal_mgr, signal, mocker): """ test add slot symbol """ signal_mgr.initialize(app_mock) Signal.setup_aio_support() app_mock.test_signal = signal func = mock.MagicMock() func.__name__ = 'fake_func' mock_F = mocker.patch('feeluown.fuoexec.signal_manager.fuoexec_F', return_value=func) signal_mgr.add('app.test_signal', func, use_symbol=True) signal.emit() await asyncio.sleep(0.1) mock_F.assert_called_once_with('fake_func') # fuoexec_F should be called once. assert func.called assert func.call_count == 1 signal_mgr.remove('app.test_signal', func, use_symbol=True) signal.emit() await asyncio.sleep(0.1) # fuoexec_F should not be called anymore assert func.call_count == 1 Signal.teardown_aio_support()
async def test_signals_slots_mgr(app_mock, signal): signals_slots_mgr = SignalsSlotsManager() Signal.setup_aio_support() func = mock.MagicMock() app_mock.test_signal = signal # test if add method works signals_slots_mgr.add('app.test_signal', func) signals_slots_mgr.initialize(app_mock) signal.emit() await asyncio.sleep(0.1) # schedule signal callbacks with aioqueue enabled assert func.called is True # test if add method works after initialized func_lator = mock.MagicMock() signals_slots_mgr.add('app.test_signal', func_lator) signal.emit() await asyncio.sleep(0.1) assert func_lator.called is True # test if remove method works signals_slots_mgr.remove('app.test_signal', func_lator) signal.emit() await asyncio.sleep(0.1) assert func_lator.call_count == 1 assert func.call_count == 3 Signal.teardown_aio_support()
async def start_app(args, config, sentinal=None): """ The param sentinal is currently only used for unittest. """ Signal.setup_aio_support() app = create_app(args, config) # Do fuoexec initialization before app initialization. fuoexec_init(app) # Initialize app with config. # # all objects can do initialization here. some objects may emit signal, # some objects may connect with others signals. app.initialize() app.initialized.emit(app) # Load last state. app.load_state() def sighanlder(signum, _): logger.info('Signal %d is received', signum) app.exit() # Handle signals. signal.signal(signal.SIGTERM, sighanlder) signal.signal(signal.SIGINT, sighanlder) if sentinal is None: sentinal: asyncio.Future = asyncio.Future() def shutdown(_): # Since about_to_shutdown signal may emit multiple times # (QApplication.aboutToQuit emits multiple times), # we should check if it is already done firstly. if not sentinal.done(): sentinal.set_result(0) app.about_to_shutdown.connect(shutdown, weak=False) # App can exit in several ways. # # GUI mode: # 1. QApplication.quit. QApplication.quit can be called under several circumstances # 1. User press CMD-Q on macOS. # 2. User clicks the tray icon exit button. # 2. SIGTERM is received. # # Daemon mode: # 1. Ctrl-C # 2. SIGTERM app.run() await sentinal Signal.teardown_aio_support()
async def test_add_and_remove(app_mock, signal_mgr, signal): Signal.setup_aio_support() func = mock.MagicMock() app_mock.test_signal = signal # test if add method works signal_mgr.add('app.test_signal', func, use_symbol=False) signal_mgr.initialize(app_mock) signal.emit() await asyncio.sleep(0.1) # schedule signal callbacks with aioqueue enabled assert func.called is True
async def test_add_and_remove_after_initialization(app_mock, signal_mgr, signal): Signal.setup_aio_support() # test if add method works after initialized func_lator = mock.MagicMock() app_mock.test_signal = signal signal_mgr.initialize(app_mock) signal_mgr.add('app.test_signal', func_lator, use_symbol=False) signal.emit() await asyncio.sleep(0.1) assert func_lator.called is True # test if remove method works signal_mgr.remove('app.test_signal', func_lator, use_symbol=False) signal.emit() await asyncio.sleep(0.1) assert func_lator.call_count == 1 Signal.teardown_aio_support()
async def test_signals_slots_mgr_add_slot_symbol(app_mock, signal, mocker): """ test add slot symbol """ signals_slots_mgr = SignalsSlotsManager() signals_slots_mgr.initialize(app_mock) Signal.setup_aio_support() app_mock.test_signal = signal func = mock.MagicMock() func.__name__ = 'fake_func' mock_F = mocker.patch('feeluown.fuoexec.fuoexec_F', return_value=func) signals_slots_mgr.add('app.test_signal', 'fake_func') signal.emit() await asyncio.sleep(0.1) mock_F.assert_called_once_with('fake_func') assert func.called is True Signal.teardown_aio_support()
async def signal_aio_support(): Signal.setup_aio_support() yield Signal.teardown_aio_support()
def create_app(config): mode = config.MODE if mode & App.GuiMode: from PyQt5.QtCore import Qt, QDir from PyQt5.QtGui import QIcon, QPixmap from PyQt5.QtWidgets import QApplication, QWidget try: # HELP: QtWebEngineWidgets must be imported before a # QCoreApplication instance is created # TODO: add a command line option to control this import import PyQt5.QtWebEngineWidgets # noqa except ImportError: logger.info('import QtWebEngineWidgets failed') from feeluown.utils.compat import QEventLoop pkg_root_dir = os.path.dirname(__file__) icons_dir = os.path.join(pkg_root_dir, 'icons') q_app = QApplication(sys.argv) QDir.addSearchPath('icons', icons_dir) q_app.setQuitOnLastWindowClosed(not config.ENABLE_TRAY) q_app.setApplicationName('FeelUOwn') app_event_loop = QEventLoop(q_app) asyncio.set_event_loop(app_event_loop) class GuiApp(QWidget): mode = App.GuiMode def __init__(self): super().__init__() self.setObjectName('app') QApplication.setWindowIcon(QIcon( QPixmap('icons:feeluown.png'))) def closeEvent(self, _): if not self.config.ENABLE_TRAY: self.exit() def mouseReleaseEvent(self, e): if not self.rect().contains(e.pos()): return if e.button() == Qt.BackButton: self.browser.back() elif e.button() == Qt.ForwardButton: self.browser.forward() class FApp(App, GuiApp): def __init__(self, config): App.__init__(self, config) GuiApp.__init__(self) def exit_player(self): # If mpv use render api to show video, we should # free resource explicitly if not self.player.use_opengl_cb: mpv_render_ctx = self.ui.mpv_widget.ctx if mpv_render_ctx is not None: mpv_render_ctx.free() self.ui.mpv_widget.close() self.player.shutdown() def exit(self): QApplication.exit() else: FApp = App Signal.setup_aio_support() Resolver.setup_aio_support() app = FApp(config) attach_attrs(app) if mode & App.GuiMode: q_app.aboutToQuit.connect(app.about_to_exit) Resolver.library = app.library return app