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()
示例#2
0
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()
示例#3
0
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()
示例#6
0
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()
示例#7
0
async def signal_aio_support():
    Signal.setup_aio_support()
    yield
    Signal.teardown_aio_support()
示例#8
0
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