[vlc-commits] [Git][videolan/vlc][master] 3 commits: qt: split DialogModel
Steve Lhomme (@robUx4)
gitlab at videolan.org
Tue Oct 15 13:10:20 UTC 2024
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
f5255c06 by Pierre Lamot at 2024-10-15T12:40:21+00:00
qt: split DialogModel
This allows blocking early messages until the UI is ready to show the dialogs
- - - - -
db6dc2d0 by Pierre Lamot at 2024-10-15T12:40:21+00:00
qt: factorise module stubbing in tests
- - - - -
e4c67e4c by Pierre Lamot at 2024-10-15T12:40:21+00:00
qt: add DialogModel unit tests
- - - - -
11 changed files:
- modules/gui/qt/Makefile.am
- modules/gui/qt/dialogs/dialogs/dialogmodel.cpp
- modules/gui/qt/dialogs/dialogs/dialogmodel.hpp
- modules/gui/qt/dialogs/dialogs/qml/Dialogs.qml
- modules/gui/qt/maininterface/mainui.cpp
- modules/gui/qt/meson.build
- modules/gui/qt/qt.cpp
- modules/gui/qt/tests/test_renderer_manager_model.cpp
- + modules/gui/qt/tests/test_vlc_dialog_model.cpp
- + modules/gui/qt/tests/vlc_stub_modules.cpp
- + modules/gui/qt/tests/vlc_stub_modules.hpp
Changes:
=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -1541,8 +1541,15 @@ TESTS = base_model_test
if HAVE_QT_QTEST
LIBVLC = -L../../../lib -lvlc
+QT_QTEST_COMMON_cppflags = $(libqt_plugin_la_CPPFLAGS) -I$(builddir)/tests -DTOP_BUILDDIR=\"$(abs_top_builddir)\"
+QT_QTEST_COMMON_cxxflags = $(AM_CXXFLAGS) $(QT_CFLAGS) $(QT_QTEST_CFLAGS) -fPIC $(CXXFLAGS_qt)
+QT_QTEST_COMMON_ldadd = $(QT_LIBS) $(LIBS_qt) $(QT_QTEST_LIBS) $(LIBVLCCORE) $(LIBVLC)
+QT_QTEST_COMMON_ldflags = $(AM_LDFLAGS) $(QT_LDFLAGS) $(QT_QTEST_LDFLAGS)
+
+# test_renderer_manager_model
test_renderer_manager_model_SOURCES = \
+ tests/vlc_stub_modules.cpp \
tests/test_renderer_manager_model.cpp \
util/renderer_manager.hpp util/renderer_manager.cpp
@@ -1552,13 +1559,33 @@ nodist_test_renderer_manager_model_SOURCES = \
BUILT_SOURCES += tests/test_renderer_manager_model.moc
CLEANFILES += tests/test_renderer_manager_model.moc
-test_renderer_manager_model_CPPFLAGS = $(libqt_plugin_la_CPPFLAGS) -I$(builddir)/tests -DTOP_BUILDDIR=\"$(abs_top_builddir)\"
-test_renderer_manager_model_CXXFLAGS = $(AM_CXXFLAGS) $(QT_CFLAGS) $(QT_QTEST_CFLAGS) -fPIC $(CXXFLAGS_qt)
-test_renderer_manager_model_LDADD = $(QT_LIBS) $(LIBS_qt) $(QT_QTEST_LIBS) $(LIBVLCCORE) $(LIBVLC)
-test_renderer_manager_model_LDFLAGS = $(AM_LDFLAGS) $(QT_LDFLAGS) $(QT_QTEST_LDFLAGS)
+test_renderer_manager_model_CPPFLAGS = $(QT_QTEST_COMMON_cppflags)
+test_renderer_manager_model_CXXFLAGS = $(QT_QTEST_COMMON_cxxflags)
+test_renderer_manager_model_LDADD = $(QT_QTEST_COMMON_ldadd)
+test_renderer_manager_model_LDFLAGS = $(QT_QTEST_COMMON_ldflags)
check_PROGRAMS += test_renderer_manager_model
TESTS += test_renderer_manager_model
+# test_vlc_dialog_model
+
+test_vlc_dialog_model_SOURCES = \
+ tests/vlc_stub_modules.cpp \
+ tests/test_vlc_dialog_model.cpp \
+ dialogs/dialogs/dialogmodel.cpp
+
+nodist_test_vlc_dialog_model_SOURCES = \
+ tests/test_vlc_dialog_model.moc \
+ dialogs/dialogs/dialogmodel.moc.cpp
+
+BUILT_SOURCES += tests/test_vlc_dialog_model.moc
+CLEANFILES += tests/test_vlc_dialog_model.moc
+test_vlc_dialog_model_CPPFLAGS = $(QT_QTEST_COMMON_cppflags)
+test_vlc_dialog_model_CXXFLAGS = $(QT_QTEST_COMMON_cxxflags)
+test_vlc_dialog_model_LDADD = $(QT_QTEST_COMMON_ldadd)
+test_vlc_dialog_model_LDFLAGS = $(QT_QTEST_COMMON_ldflags)
+check_PROGRAMS += test_vlc_dialog_model
+TESTS += test_vlc_dialog_model
+
endif
QML_LOG_COMPILER = $(builddir)/qml_test -input
=====================================
modules/gui/qt/dialogs/dialogs/dialogmodel.cpp
=====================================
@@ -23,6 +23,7 @@
// VLC includes
#include <vlc_dialog.h>
#include "qt.hpp"
+#include <QThread>
#include "maininterface/mainctx.hpp"
@@ -155,115 +156,158 @@ void DialogErrorModel::resetRepeatedMessageCount()
// DialogModel
//=================================================================================================
-DialogModel::DialogModel(QObject* parent)
- : QObject(parent)
-{
-}
+//static functions
+namespace {
-DialogModel::~DialogModel()
+void onDialogLogin(void * p_data, vlc_dialog_id * dialogId,
+ const char * psz_title, const char * psz_text,
+ const char * psz_default_username, bool b_ask_store)
{
- if (m_ctx)
- vlc_dialog_provider_set_callbacks(m_ctx->getIntf(), nullptr, nullptr);
+ VLCDialogModel * model = static_cast<VLCDialogModel *>(p_data);
+ model->dialogCallback(dialogId, [=](VLCDialog* provider){
+ emit provider->login(dialogId, psz_title, psz_text, psz_default_username, b_ask_store);
+ });
+
}
-MainCtx* DialogModel::getCtx() const
+void onDialogQuestion(void * p_data, vlc_dialog_id * dialogId,
+ const char * psz_title, const char * psz_text,
+ vlc_dialog_question_type i_type,
+ const char * psz_cancel, const char * psz_action1,
+ const char * psz_action2)
{
- return m_ctx;
+ auto model = static_cast<VLCDialogModel *>(p_data);
+ model->dialogCallback(dialogId, [=](VLCDialog* provider){
+ emit provider->question(
+ dialogId, psz_title, psz_text,
+ static_cast<VLCDialog::QuestionType>(i_type), psz_cancel,
+ psz_action1, psz_action2);
+ });
}
-void DialogModel::setCtx(MainCtx* ctx)
+void onDialogProgress(void * p_data, vlc_dialog_id * dialogId,
+ const char * psz_title, const char * psz_text,
+ bool b_indeterminate, float f_position,
+ const char * psz_cancel)
{
- if (ctx == m_ctx)
- return;
- if (ctx) {
- m_ctx = ctx;
-
- const vlc_dialog_cbs cbs =
- {
- onLogin, onQuestion, onProgress, onCancelled, onProgressUpdated
- };
- vlc_dialog_provider_set_callbacks(ctx->getIntf(), &cbs, this);
- } else {
- if (m_ctx)
- vlc_dialog_provider_set_callbacks(m_ctx->getIntf(), nullptr, nullptr);
-
- m_ctx = nullptr;
- }
- emit ctxChanged();
+ auto model = static_cast<VLCDialogModel*>(p_data);
+ model->dialogCallback(dialogId, [=](VLCDialog* provider){
+ emit provider->progress(dialogId, psz_title, psz_text, b_indeterminate, f_position, psz_cancel);
+ });
}
-//-------------------------------------------------------------------------------------------------
-// Interface
-//-------------------------------------------------------------------------------------------------
-
-/* Q_INVOKABLE */ void DialogModel::post_login(DialogId dialogId, const QString & username,
- const QString & password, bool store)
+void onDialogProgressUpdated(void * p_data, vlc_dialog_id * dialogId,
+ float f_value, const char * psz_text)
{
- vlc_dialog_id_post_login(dialogId.m_id, qtu(username), qtu(password), store);
+ auto model = static_cast<VLCDialogModel*>(p_data);
+ model->dialogCallback(dialogId, [=](VLCDialog* provider){
+ emit provider->progressUpdated(dialogId, f_value, psz_text);
+ });
}
-/* Q_INVOKABLE */ void DialogModel::post_action1(DialogId dialogId)
+void onDialogCancelled(void * p_data, vlc_dialog_id * dialogId)
{
- vlc_dialog_id_post_action(dialogId.m_id, 1);
+ auto model = static_cast<VLCDialogModel*>(p_data);
+ model->dialogCallback(dialogId, [=](VLCDialog* provider){
+ emit provider->cancelled(dialogId);
+ });
}
-/* Q_INVOKABLE */ void DialogModel::post_action2(DialogId dialogId)
-{
- vlc_dialog_id_post_action(dialogId.m_id, 2);
}
-/* Q_INVOKABLE */ void DialogModel::dismiss(DialogId dialogId)
+VLCDialogModel::VLCDialogModel(qt_intf_t* intf, QObject* parent)
+ : QObject(parent)
+ , m_intf(intf)
{
- vlc_dialog_id_dismiss(dialogId.m_id);
+ const vlc_dialog_cbs cbs =
+ {
+ onDialogLogin, onDialogQuestion, onDialogProgress,
+ onDialogCancelled, onDialogProgressUpdated
+ };
+ vlc_dialog_provider_set_callbacks(intf, &cbs, this);
}
-//-------------------------------------------------------------------------------------------------
-// Private static functions
-//-------------------------------------------------------------------------------------------------
-
-
-/* static */ void DialogModel::onLogin(void * p_data, vlc_dialog_id * dialogId,
- const char * psz_title, const char * psz_text,
- const char * psz_default_username, bool b_ask_store)
+VLCDialogModel::~VLCDialogModel()
{
- DialogModel * model = static_cast<DialogModel *>(p_data);
+ m_shuttingDown = true;
+ m_providerWait.wakeAll();
+ vlc_dialog_provider_set_callbacks(m_intf, nullptr, nullptr);
- emit model->login(dialogId, psz_title, psz_text, psz_default_username, b_ask_store);
+ //ensure that we are not destroying ourselve before
+ {
+ QMutexLocker lock(&m_lock);
+ while (m_pendingDialog > 0)
+ m_pendingDialogCond.wait(&m_lock);
+ }
}
-/* static */ void DialogModel::onQuestion(void * p_data, vlc_dialog_id * dialogId,
- const char * psz_title, const char * psz_text,
- vlc_dialog_question_type i_type,
- const char * psz_cancel, const char * psz_action1,
- const char * psz_action2)
+VLCDialog* VLCDialogModel::getProvider() const
{
- DialogModel * model = static_cast<DialogModel *>(p_data);
-
- emit model->question(dialogId, psz_title, psz_text, static_cast<int>(i_type), psz_cancel,
- psz_action1, psz_action2);
+ return m_provider;
}
-/* static */ void DialogModel::onProgress(void * p_data, vlc_dialog_id * dialogId,
- const char * psz_title, const char * psz_text,
- bool b_indeterminate, float f_position,
- const char * psz_cancel)
+void VLCDialogModel::setProvider(VLCDialog* provider)
{
- DialogModel * model = static_cast<DialogModel *>(p_data);
+ if (m_provider == provider)
+ return;
+ if (m_provider)
+ {
+ disconnect(m_provider, nullptr, this, nullptr);
+ }
+ m_provider = provider;
+ if (m_provider)
+ {
+ connect(m_provider, &VLCDialog::post_login,
+ this, [](DialogId dialogId, const QString & username,
+ const QString & password, bool store){
+ vlc_dialog_id_post_login(dialogId.m_id, qtu(username), qtu(password), store);
+ });
+ connect(m_provider, &VLCDialog::post_action1,
+ this, [](DialogId dialogId){
+ vlc_dialog_id_post_action(dialogId.m_id, 1);
+ });
+ connect(m_provider, &VLCDialog::post_action2,
+ this, [](DialogId dialogId){
+ vlc_dialog_id_post_action(dialogId.m_id, 2);
+ });
+ connect(m_provider, &VLCDialog::dismiss,
+ this, [](DialogId dialogId){
+ vlc_dialog_id_dismiss(dialogId.m_id);
+ });
- emit model->progress(dialogId, psz_title, psz_text, b_indeterminate, f_position, psz_cancel);
+ }
+ m_providerWait.wakeAll();
+ emit providerChanged();
}
-/* static */ void DialogModel::onProgressUpdated(void * p_data, vlc_dialog_id * dialogId,
- float f_value, const char * psz_text)
+
+void VLCDialogModel::dialogCallback(vlc_dialog_id * dialogId, std::function<void(VLCDialog* provider)> callback)
{
- DialogModel * model = static_cast<DialogModel *>(p_data);
+ QMutexLocker lock(&m_lock);
+ m_pendingDialog++;
+ //dialogs are synchronous calls, if they are spawned from qt thread
+ //(which shouldn't happen), we can't wait for m_provider
+ if (QThread::currentThread() == this->thread() && m_provider == nullptr)
+ {
+ vlc_dialog_id_dismiss(dialogId);
+ m_pendingDialog--;
+ m_pendingDialogCond.wakeOne();
+ return;
+ }
- emit model->progressUpdated(dialogId, f_value, psz_text);
-}
+ while (m_provider == nullptr && !m_shuttingDown)
+ m_providerWait.wait(&m_lock);
-/* static */ void DialogModel::onCancelled(void * p_data, vlc_dialog_id * dialogId)
-{
- DialogModel * model = static_cast<DialogModel *>(p_data);
+ if (m_shuttingDown)
+ {
+ vlc_dialog_id_dismiss(dialogId);
+ m_pendingDialog--;
+ m_pendingDialogCond.wakeOne();
+ return;
+ }
+
+ callback(m_provider);
- emit model->cancelled(dialogId);
+ m_pendingDialog--;
+ m_pendingDialogCond.wakeOne();
}
=====================================
modules/gui/qt/dialogs/dialogs/dialogmodel.hpp
=====================================
@@ -31,6 +31,9 @@
// Qt includes
#include <QAbstractListModel>
+#include <QQmlEngine>
+#include <QMutex>
+#include <QWaitCondition>
#include "qt.hpp"
#include "util/singleton.hpp"
@@ -119,59 +122,40 @@ private: // Variables
friend class Singleton<DialogErrorModel>;
};
-class DialogModel : public QObject
+/**
+ * this class expose vlc_dialog events and allow to reply
+ * to use it, instantiate the object, connect the signals then
+ * register it in VLCDialogModel
+ */
+class VLCDialog: public QObject
{
Q_OBJECT
- Q_PROPERTY(MainCtx* ctx READ getCtx WRITE setCtx NOTIFY ctxChanged FINAL)
-
-public: // Enums
- // NOTE: Is it really useful to have this declared here ?
- enum QuestionType { QUESTION_NORMAL, QUESTION_WARNING, QUESTION_CRITICAL };
- Q_ENUM(QuestionType)
-
public:
- explicit DialogModel(QObject *parent = nullptr);
- ~DialogModel();
+ enum QuestionType {
+ QUESTION_NORMAL = VLC_DIALOG_QUESTION_NORMAL,
+ QUESTION_WARNING = VLC_DIALOG_QUESTION_WARNING,
+ QUESTION_CRITICAL = VLC_DIALOG_QUESTION_CRITICAL
+ };
+ Q_ENUM(QuestionType)
-public: // Interface
+signals:
+ //dialog user actions
Q_INVOKABLE void post_login(DialogId dialogId, const QString & username,
- const QString & password, bool store = false);
+ const QString & password, bool store = false);
Q_INVOKABLE void post_action1(DialogId dialogId);
Q_INVOKABLE void post_action2(DialogId dialogId);
Q_INVOKABLE void dismiss(DialogId dialogId);
-private: // Static functions
- static void onLogin(void * p_data, vlc_dialog_id * dialogId, const char * psz_title,
- const char * psz_text, const char * psz_default_username,
- bool b_ask_store);
-
- static void onQuestion(void * p_data, vlc_dialog_id * dialogId, const char * psz_title,
- const char * psz_text, vlc_dialog_question_type i_type,
- const char * psz_cancel, const char * psz_action1,
- const char * psz_action2);
+ //dialog request
- static void onProgress(void * p_data, vlc_dialog_id * dialogId, const char * psz_title,
- const char * psz_text, bool b_indeterminate, float f_position,
- const char *psz_cancel);
-
- static void onProgressUpdated(void * p_data, vlc_dialog_id * dialogId, float f_value,
- const char * psz_text);
-
- static void onCancelled(void * p_data, vlc_dialog_id * dialogId);
-
-public:
- MainCtx* getCtx() const;
- void setCtx(MainCtx*);
-
-signals:
void login(DialogId dialogId, const QString & title,
const QString & text, const QString & defaultUsername,
bool b_ask_store);
- void question(DialogId dialogId, const QString & title, const QString & text, int type,
+ void question(DialogId dialogId, const QString & title, const QString & text, QuestionType type,
const QString & cancel, const QString & action1, const QString & action2);
void progress(DialogId dialogId, const QString & title, const QString & text,
@@ -180,11 +164,46 @@ signals:
void progressUpdated(DialogId dialogId, float f_value, const QString & text);
void cancelled(DialogId dialogId);
+};
- void ctxChanged();
+/**
+ * This class listen to vlc_dialog_t events and forward them to VLCDialog
+ */
+class VLCDialogModel : public QObject, public Singleton<VLCDialogModel>
+{
+ Q_OBJECT
+
+ Q_PROPERTY(VLCDialog* provider READ getProvider WRITE setProvider NOTIFY providerChanged FINAL)
+
+public:
+ explicit VLCDialogModel(qt_intf_t* intf, QObject *parent = nullptr);
+ ~VLCDialogModel();
+
+public:
+ VLCDialog* getProvider() const;
+ void setProvider(VLCDialog*);
+
+public:
+ //block dialog until m_provider is available the call the callback on it
+ void dialogCallback(vlc_dialog_id*, std::function<void(VLCDialog* provider)>);
+
+signals:
+ void providerChanged();
private:
- MainCtx* m_ctx = nullptr;
+ qt_intf_t* m_intf = nullptr;
+ QMutex m_lock;
+ //waiting for the provider to be set
+ QWaitCondition m_providerWait;
+
+ //during destruction, waiting for dialogCallback to finish
+ QWaitCondition m_pendingDialogCond;
+ unsigned m_pendingDialog = 0;
+
+ VLCDialog* m_provider = nullptr;
+ bool m_shuttingDown = false;
+
+ friend class Singleton<VLCDialogModel>;
};
#endif // DIALOGMODEL_HPP
=====================================
modules/gui/qt/dialogs/dialogs/qml/Dialogs.qml
=====================================
@@ -53,10 +53,10 @@ Item {
Component.onDestruction: {
if (questionDialog.dialogId !== null) {
- dialogModel.dismiss(questionDialog.dialogId)
+ vlcDialog.dismiss(questionDialog.dialogId)
questionDialog.dialogId = null
} if (loginDialog.dialogId !== null) {
- dialogModel.dismiss(loginDialog.dialogId)
+ vlcDialog.dismiss(loginDialog.dialogId)
loginDialog.dialogId = null
}
}
@@ -73,18 +73,26 @@ Item {
// Connections
//---------------------------------------------------------------------------------------------
- Connections
+ VLCDialog
{
- target: dialogModel
+ id: vlcDialog
+
+ Component.onCompleted: {
+ VLCDialogModel.provider = vlcDialog
+ }
+
+ Component.onDestruction: {
+ VLCDialogModel.provider = null
+ }
- function onLogin(dialogId, title, text, defaultUsername, askStore) {
+ onLogin: (dialogId, title, text, defaultUsername, askStore) => {
loginDialog.dialogId = dialogId
loginDialog.title = title
loginDialog.defaultUsername = defaultUsername
loginDialog.open()
}
- function onQuestion(dialogId, title, text, type, cancel, action1, action2) {
+ onQuestion: (dialogId, title, text, type, cancel, action1, action2) => {
questionDialog.dialogId = dialogId
questionDialog.title = title
questionDialog.text = text
@@ -94,7 +102,7 @@ Item {
questionDialog.open()
}
- function onProgress(dialogId, title, text, indeterminate, position, cancel) {
+ onProgress: (dialogId, title, text, indeterminate, position, cancel) => {
progressDialog.dialogId = dialogId
progressDialog.title = title
progressDialog.text = text
@@ -104,7 +112,7 @@ Item {
progressDialog.open()
}
- function onProgressUpdated(dialogId, position, text) {
+ onProgressUpdated: (dialogId, position, text) => {
if (progressDialog.dialogId !== dialogId) {
console.warn("progress event on an inexisting dialog")
return
@@ -113,21 +121,21 @@ Item {
progressDialog.position = position
}
- function onCancelled(dialogId) {
+ onCancelled: (dialogId) => {
if (questionDialog.dialogId === dialogId) {
questionDialog.close()
questionDialog.dialogId = null
- dialogModel.dismiss(dialogId)
+ dismiss(dialogId)
} else if (loginDialog.dialogId === dialogId) {
loginDialog.close()
loginDialog.dialogId = null
- dialogModel.dismiss(dialogId)
+ dismiss(dialogId)
} else if (progressDialog.dialogId === dialogId) {
progressDialog.close()
progressDialog.dialogId = null
- dialogModel.dismiss(dialogId)
+ dismiss(dialogId)
} else {
- dialogModel.dismiss(dialogId)
+ dismiss(dialogId)
}
}
}
@@ -146,12 +154,6 @@ Item {
// Childs
//---------------------------------------------------------------------------------------------
- DialogModel {
- id: dialogModel
- ctx: MainCtx
- }
-
-
Widgets.DrawerExt {
id: errorPopup
@@ -391,13 +393,13 @@ Item {
onAccepted: {
if (loginDialog.dialogId !== null) {
- dialogModel.post_login(loginDialog.dialogId, username.text, password.text, savePassword.checked)
+ vlcDialog.post_login(loginDialog.dialogId, username.text, password.text, savePassword.checked)
loginDialog.dialogId = null
}
}
onRejected: {
if (loginDialog.dialogId !== null) {
- dialogModel.dismiss(loginDialog.dialogId)
+ vlcDialog.dismiss(loginDialog.dialogId)
loginDialog.dialogId = null
}
}
@@ -466,7 +468,7 @@ Item {
text: progressDialog.cancelTxt
onClicked: {
- dialogModel.dismiss(progressDialog.dialogId)
+ vlcDialog.dismiss(progressDialog.dialogId)
progressDialog.dialogId = null
progressDialog.close()
}
@@ -529,7 +531,7 @@ Item {
Keys.onPressed: (event) => Navigation.defaultKeyAction(event)
onClicked: {
- dialogModel.dismiss(questionDialog.dialogId)
+ vlcDialog.dismiss(questionDialog.dialogId)
questionDialog.dialogId = null
questionDialog.close()
}
@@ -548,7 +550,7 @@ Item {
Keys.onPressed: (event) => Navigation.defaultKeyAction(event)
onClicked: {
- dialogModel.post_action1(questionDialog.dialogId)
+ vlcDialog.post_action1(questionDialog.dialogId)
questionDialog.dialogId = null
questionDialog.close()
}
@@ -565,7 +567,7 @@ Item {
Keys.onPressed: (event) => Navigation.defaultKeyAction(event)
onClicked: {
- dialogModel.post_action2(questionDialog.dialogId)
+ vlcDialog.post_action2(questionDialog.dialogId)
questionDialog.dialogId = null
questionDialog.close()
}
=====================================
modules/gui/qt/maininterface/mainui.cpp
=====================================
@@ -128,6 +128,8 @@ MainUI::MainUI(qt_intf_t *p_intf, MainCtx *mainCtx, QWindow* interfaceWindow, Q
assert(m_intf->p_mainPlaylistController);
SingletonRegisterHelper<PlaylistController>::setInstance(m_intf->p_mainPlaylistController);
+ assert(VLCDialogModel::getInstance<false>());
+ SingletonRegisterHelper<VLCDialogModel>::setInstance(VLCDialogModel::getInstance<false>());
assert(DialogsProvider::getInstance());
SingletonRegisterHelper<DialogsProvider>::setInstance(DialogsProvider::getInstance());
@@ -252,7 +254,8 @@ void MainUI::registerQMLTypes()
// @uri VLC.Dialogs
qmlRegisterType<AboutModel>( uri, versionMajor, versionMinor, "AboutModel" );
- qmlRegisterType<DialogModel>(uri, versionMajor, versionMinor, "DialogModel");
+ qmlRegisterType<VLCDialog>( uri, versionMajor, versionMinor, "VLCDialog" );
+ qmlRegisterSingletonType<VLCDialogModel>(uri, versionMajor, versionMinor, "VLCDialogModel", SingletonRegisterHelper<VLCDialogModel>::callback);
qmlRegisterUncreatableType<DialogId>( uri, versionMajor, versionMinor, "dialogId", "");
qmlRegisterSingletonType<DialogsProvider>(uri, versionMajor, versionMinor, "DialogsProvider", SingletonRegisterHelper<DialogsProvider>::callback);
qmlRegisterSingletonType<DialogErrorModel>(uri, versionMajor, versionMinor, "DialogErrorModel", SingletonRegisterHelper<DialogErrorModel>::callback);
=====================================
modules/gui/qt/meson.build
=====================================
@@ -1107,6 +1107,7 @@ if qt6_dep.found()
vlc_tests += {
'name': 'test_qt_renderer_manager',
'sources': files(
+ 'tests/vlc_stub_modules.cpp',
'tests/test_renderer_manager_model.cpp',
'util/renderer_manager.hpp',
'util/renderer_manager.cpp'
@@ -1120,7 +1121,28 @@ if qt6_dep.found()
'suite': ['qt'],
'include_directories' : qt_include_dir,
'link_with': [libvlccore, libvlc],
+ 'dependencies': [qt6_dep, qt_extra_deps, qtest_qt6_dep]
+ }
+
+ vlc_tests += {
+ 'name': 'test_qt_dialog_model',
+ 'sources': files(
+ 'tests/vlc_stub_modules.cpp',
+ 'tests/test_vlc_dialog_model.cpp',
+ 'dialogs/dialogs/dialogmodel.hpp',
+ 'dialogs/dialogs/dialogmodel.cpp'
+ ),
+ 'moc_sources': files(
+ 'tests/test_vlc_dialog_model.cpp'
+ ),
+ 'moc_headers': files(
+ 'dialogs/dialogs/dialogmodel.hpp'
+ ),
+ 'suite': ['qt'],
+ 'include_directories' : qt_include_dir,
+ 'link_with': [libvlccore, libvlc],
'dependencies': [qt6_dep, qt_extra_deps, qtest_qt6_dep],
}
+
endif
endif
=====================================
modules/gui/qt/qt.cpp
=====================================
@@ -1010,6 +1010,7 @@ static void *Thread( void *obj )
app.setDesktopFileName( PACKAGE );
DialogErrorModel::getInstance( p_intf );
+ VLCDialogModel::getInstance( p_intf );
/* Initialize the Dialog Provider and the Main Input Manager */
DialogsProvider::getInstance( p_intf );
@@ -1143,6 +1144,7 @@ static void *ThreadCleanup( qt_intf_t *p_intf, CleanupReason cleanupReason )
*/
DialogsProvider::killInstance();
+ VLCDialogModel::killInstance();
DialogErrorModel::killInstance();
/* Destroy the main playlist controller */
=====================================
modules/gui/qt/tests/test_renderer_manager_model.cpp
=====================================
@@ -15,123 +15,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-/* Define a builtin module for mocked parts */
-#define MODULE_NAME renderer_manager_test
-#undef VLC_DYNAMIC_PLUGIN
-
-#include "../../../../test/libvlc/test.h"
-
-#include <vlc/vlc.h>
-
-#include <vlc_common.h>
-#include <vlc_plugin.h>
-#include <vlc_playlist.h>
-#include <vlc_services_discovery.h>
-#include <vlc_renderer_discovery.h>
-#include <vlc_probe.h>
-#include <vlc_interface.h>
-#include <vlc_player.h>
-
-#include "../../../../lib/libvlc_internal.h"
-
-#include "qt.hpp"
-
-namespace vlc {
-class Compositor {};
-}
-
-static vlc_renderer_discovery_t* g_rd = nullptr;
-static bool g_rd_probe_enabled = false;
-
-static int OpenRD( vlc_object_t* p_this )
-{
- g_rd = (vlc_renderer_discovery_t *)p_this;
- return VLC_SUCCESS;
-}
-
-static void CloseRD( vlc_object_t* )
-{
- g_rd = nullptr;
-
-}
-
-static int vlc_rd_probe_open(vlc_object_t *obj) {
- auto probe = (struct vlc_probe_t *)obj;
-
- if (g_rd_probe_enabled)
- vlc_rd_probe_add(probe, "rd", "a fake renderer for testing purpose");
- //only probe ourself
- return VLC_PROBE_STOP;
-}
-
-static qt_intf_t* g_intf = nullptr;
-
-static int OpenIntf(vlc_object_t* p_this)
-{
- auto intfThread = reinterpret_cast<intf_thread_t*>(p_this);
- libvlc_int_t* libvlc = vlc_object_instance( p_this );
-
- /* Ensure initialization of objects in qt_intf_t. */
- g_intf = vlc_object_create<qt_intf_t>( libvlc );
- if (!g_intf)
- return VLC_ENOMEM;
-
- g_intf->obj.logger = vlc_LogHeaderCreate(libvlc->obj.logger, "qt");
- if (!g_intf->obj.logger)
- {
- vlc_object_delete(g_intf);
- return VLC_EGENERIC;
- }
- g_intf->intf = intfThread;
- intfThread->p_sys = reinterpret_cast<intf_sys_t*>(g_intf);
- return VLC_SUCCESS;
-}
-
-static void CloseIntf( vlc_object_t *p_this )
-{
- intf_thread_t* intfThread = (intf_thread_t*)(p_this);
- auto p_intf = reinterpret_cast<qt_intf_t*>(intfThread->p_sys);
- if (!p_intf)
- return;
- vlc_LogDestroy(p_intf->obj.logger);
- vlc_object_delete(p_intf);
-}
-
-vlc_module_begin()
- set_callbacks(OpenIntf, CloseIntf)
- set_capability("interface", 0)
-add_submodule()
- set_capability("renderer_discovery", 0)
- add_shortcut("rd")
- set_callbacks(OpenRD, CloseRD)
-add_submodule()
- set_capability("renderer probe", 10000)
- set_callback(vlc_rd_probe_open)
-vlc_module_end()
-
-extern "C" {
-
-const char vlc_module_name[] = MODULE_STRING;
-VLC_EXPORT vlc_plugin_cb vlc_static_modules[] = {
- VLC_SYMBOL(vlc_entry),
- NULL
-};
-}
-
#include <QTest>
#include <QAbstractItemModelTester>
+
+#include "vlc_stub_modules.hpp"
+
#include "../util/renderer_manager.hpp"
-#include <vlc_cxx_helpers.hpp>
using RendererItemPtr = vlc_shared_data_ptr_type(vlc_renderer_item_t,
vlc_renderer_item_hold,
vlc_renderer_item_release);
-
class TestClass : public QObject
{
Q_OBJECT
@@ -143,7 +37,7 @@ private:
RendererItemPtr item(vlc_renderer_item_new(
"type", qtu(name), qtu(sout), "extra sout",
nullptr, "icon://", i ));
- vlc_rd_add_item( g_rd, item.get() );
+ vlc_rd_add_item( rd(), item.get() );
return item;
}
@@ -158,47 +52,49 @@ private:
QCOMPARE(m_model->data(idx, RendererManager::FLAGS), id);
}
+ vlc_renderer_discovery_t* rd() const
+ {
+ return m_env->renderDiscovery;
+ }
+
private slots:
void initTestCase() {
- test_init();
-
- m_vlc = libvlc_new(test_defaults_nargs, test_defaults_args);
- libvlc_InternalAddIntf(m_vlc->p_libvlc_int, MODULE_STRING);
- libvlc_InternalPlay(m_vlc->p_libvlc_int);
-
- m_playlist = vlc_intf_GetMainPlaylist(g_intf->intf);
- m_player = vlc_playlist_GetPlayer( m_playlist );
+ m_env = std::make_unique<VLCTestingEnv>();
+ QVERIFY(m_env->init());
+ m_player = m_env->intf->p_player;
}
void cleanupTestCase() {
- libvlc_release(m_vlc);
+ m_player = nullptr;
+ m_env.reset();
}
void init() {
- g_rd_probe_enabled = true;
- m_model = new RendererManager(g_intf, m_player);
+ m_env->renderDiscoveryProbeEnabled = true;
+
+ m_model = new RendererManager(m_env->intf, m_player);
//QAbstractItemModelTester checks that QAbstractItemModel events are coherents
- m_modelTester = new QAbstractItemModelTester(m_model);
- QVERIFY(g_rd == nullptr);
+ m_modelTester = std::make_unique<QAbstractItemModelTester>(m_model);
+ QVERIFY(rd() == nullptr);
}
void cleanup() {
- delete m_modelTester;
+ m_modelTester.reset();
delete m_model;
- QVERIFY(g_rd == nullptr);
+ QVERIFY(rd() == nullptr);
}
void testEmpty() {
- QVERIFY(g_rd == nullptr);
+ QVERIFY(rd() == nullptr);
//model is empty before scan
QCOMPARE(m_model->rowCount(), 0);
m_model->StartScan();
- QVERIFY(g_rd != nullptr);
+ QVERIFY(rd() != nullptr);
QCOMPARE(m_model->rowCount(), 0);
QCOMPARE(m_model->getStatus(), RendererManager::RUNNING);
//scan didn't find anything
m_model->StopScan();
- QVERIFY(g_rd == nullptr);
+ QVERIFY(rd() == nullptr);
QCOMPARE(m_model->rowCount(), 0);
QCOMPARE(m_model->getStatus(), RendererManager::IDLE);
}
@@ -209,7 +105,7 @@ private slots:
QCOMPARE(m_model->rowCount(), 0);
QCOMPARE(m_model->getStatus(), RendererManager::RUNNING);
- QVERIFY(g_rd != nullptr);
+ QVERIFY(rd() != nullptr);
for (int i = 0; i < 5; ++i) {
pushDummyRDItem(i);
@@ -227,12 +123,12 @@ private slots:
}
//module is closed
- QVERIFY(g_rd == nullptr);
+ QVERIFY(rd() == nullptr);
}
void testTwoPassesIdentical() {
m_model->StartScan();
- QVERIFY(g_rd != nullptr);
+ QVERIFY(rd() != nullptr);
QCOMPARE(m_model->rowCount(), 0);
for (int i = 0; i < 5; ++i) {
pushDummyRDItem(i);
@@ -290,7 +186,7 @@ private slots:
pushDummyRDItem(i);
}
QCOMPARE(m_model->rowCount(), 7);
- vlc_rd_remove_item(g_rd, item.get());
+ vlc_rd_remove_item(rd(), item.get());
QCOMPARE(m_model->rowCount(), 6);
m_model->StopScan();
QCOMPARE(m_model->rowCount(), 6);
@@ -423,7 +319,7 @@ private slots:
//selected item is removed
QCOMPARE(m_model->rowCount(), 5);
- vlc_rd_remove_item( g_rd, r3.get() );
+ vlc_rd_remove_item( rd(), r3.get() );
//item is held by model
QCOMPARE(m_model->useRenderer(), true);
@@ -439,11 +335,11 @@ private slots:
//item should be gone after next scan
m_model->StartScan();
- vlc_rd_add_item( g_rd, r0.get() );
- vlc_rd_add_item( g_rd, r1.get() );
- vlc_rd_add_item( g_rd, r2.get() );
+ vlc_rd_add_item( rd(), r0.get() );
+ vlc_rd_add_item( rd(), r1.get() );
+ vlc_rd_add_item( rd(), r2.get() );
//no r3
- vlc_rd_add_item( g_rd, r4.get() );
+ vlc_rd_add_item( rd(), r4.get() );
m_model->StopScan();
QCOMPARE(m_model->rowCount(), 4);
@@ -451,8 +347,8 @@ private slots:
selidx = m_model->index(2);
m_model->setData(selidx, true, RendererManager::SELECTED);
m_model->StartScan();
- vlc_rd_add_item( g_rd, r0.get() );
- vlc_rd_add_item( g_rd, r1.get() );
+ vlc_rd_add_item( rd(), r0.get() );
+ vlc_rd_add_item( rd(), r1.get() );
m_model->StopScan();
QCOMPARE(m_model->rowCount(), 3);
QCOMPARE(m_model->data(selidx, RendererManager::SELECTED), true);
@@ -472,20 +368,20 @@ private slots:
//failed state when no renderer manager is found
void testNoProbes() {
- g_rd_probe_enabled = false;
+ m_env->renderDiscoveryProbeEnabled = false;
QCOMPARE(m_model->getStatus(), RendererManager::IDLE);
m_model->StartScan();
QCOMPARE(m_model->getStatus(), RendererManager::FAILED);
QCOMPARE(m_model->rowCount(), 0);
- QVERIFY(g_rd == nullptr);
- g_rd_probe_enabled = true;
+ QVERIFY(rd() == nullptr);
+ m_env->renderDiscoveryProbeEnabled = true;
}
private:
- libvlc_instance_t* m_vlc = nullptr;
- vlc_playlist_t* m_playlist = nullptr;
+
+ std::unique_ptr<VLCTestingEnv> m_env;
vlc_player_t* m_player = nullptr;
- QAbstractItemModelTester* m_modelTester = nullptr;
- RendererManager* m_model;
+ std::unique_ptr<QAbstractItemModelTester> m_modelTester;
+ RendererManager* m_model = nullptr;
};
QTEST_GUILESS_MAIN(TestClass)
=====================================
modules/gui/qt/tests/test_vlc_dialog_model.cpp
=====================================
@@ -0,0 +1,370 @@
+/*****************************************************************************
+ * Copyright (C) 2024 VLC authors and VideoLAN
+ *
+ * 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.
+ *****************************************************************************/
+#include "vlc_stub_modules.hpp"
+
+#include <QTest>
+#include <QSignalSpy>
+#include <QThreadPool>
+#include "dialogs/dialogs/dialogmodel.hpp"
+#include <vlc_cxx_helpers.hpp>
+
+#include "../util/renderer_manager.hpp"
+
+class TestVLCDialogModel : public QObject
+{
+ Q_OBJECT
+
+ enum UserAnswer {
+ Dismiss,
+ PostLogin,
+ PostAction1,
+ PostAction2
+ };
+
+private slots:
+ void initTestCase() {
+ m_env = std::make_unique<VLCTestingEnv>();
+ QVERIFY(m_env->init());
+ m_intf = m_env->intf;
+ }
+
+ void cleanupTestCase() {
+ m_intf = nullptr;
+ m_env.reset();
+ }
+
+ void init() {
+ VLCDialogModel::getInstance(m_intf);
+ m_dialog = new VLCDialog();
+ m_loginSpy = std::make_unique<QSignalSpy>(m_dialog, &VLCDialog::login);
+ m_questionSpy = std::make_unique<QSignalSpy>(m_dialog, &VLCDialog::question);
+ m_progressSpy = std::make_unique<QSignalSpy>(m_dialog, &VLCDialog::progress);
+ m_progressUpdatedSpy = std::make_unique<QSignalSpy>(m_dialog, &VLCDialog::progressUpdated);
+ m_canceledSpy = std::make_unique<QSignalSpy>(m_dialog, &VLCDialog::cancelled);
+ }
+
+ void cleanup() {
+ m_loginSpy.reset();
+ m_questionSpy.reset();
+ m_progressSpy.reset();
+ m_progressUpdatedSpy.reset();
+ m_canceledSpy.reset();
+ VLCDialogModel* model = VLCDialogModel::getInstance<false>();
+ if (model) {
+ model->setProvider(nullptr);
+ VLCDialogModel::killInstance();
+ }
+ delete m_dialog;
+ }
+
+ void testLogin_data() {
+ QTest::addColumn<UserAnswer>("userReply");
+ QTest::addColumn<int>("expectedReply");
+
+ QTest::newRow("login") << PostLogin << 1;
+ QTest::newRow("dismiss") << Dismiss << 0;
+ QTest::newRow("wrongAnswer") << PostAction1 << VLC_EGENERIC;
+ }
+
+ //failed state when no renderer manager is found
+ void testLogin() {
+ QFETCH(UserAnswer, userReply);
+ QFETCH(int, expectedReply);
+
+ QObject::connect(m_dialog, &VLCDialog::login, this, [&](
+ DialogId dialogId, const QString & title,
+ const QString & text, const QString & defaultUsername,
+ bool){
+
+ QCOMPARE(defaultUsername, "default username");
+ QCOMPARE(title, "login title");
+ QCOMPARE(text, "login message");
+
+ switch(userReply)
+ {
+ case Dismiss:
+ m_dialog->dismiss(dialogId);
+ break;
+ case PostLogin:
+ m_dialog->post_login(dialogId, "username", "hunter2", false);
+ break;
+ case PostAction1:
+ default:
+ //bad behavior
+ m_dialog->post_action1(dialogId);
+ break;
+
+ }
+ });
+
+ VLCDialogModel::getInstance<false>()->setProvider(m_dialog);
+
+ std::atomic<bool> replied = false;
+ int reply = -1;
+ QString repliedUsername;
+ QString repliedPassword;
+ bool repliedStore = true;
+ QThreadPool::globalInstance()->start([&](){
+ char* username = nullptr;
+ char* password = nullptr;
+ reply = vlc_dialog_wait_login(m_intf, &username, &password, &repliedStore,
+ "default username", "login title",
+ "login message");
+ if (reply == 1) {
+ repliedUsername = username;
+ repliedPassword = password;
+ free(username);
+ free(password);
+ }
+
+ replied = true;
+ });
+
+ QVERIFY(QTest::qWaitFor([&replied](){ return replied == true; }));
+ if (expectedReply == VLC_EGENERIC)
+ QCOMPARE_LT(reply, 0);
+ else
+ QCOMPARE(reply, expectedReply);
+
+ if (reply == 1) {
+ QCOMPARE(repliedUsername, "username");
+ QCOMPARE(repliedPassword, "hunter2");
+ QCOMPARE(repliedStore, false);
+ }
+
+ QCOMPARE(m_questionSpy->count(), 0);
+ QCOMPARE(m_progressSpy->count(), 0);
+ QCOMPARE(m_progressUpdatedSpy->count(), 0);
+ QCOMPARE(m_canceledSpy->count(), 0);
+ }
+
+ void testQuestion_data() {
+ QTest::addColumn<UserAnswer>("userReply");
+ QTest::addColumn<int>("expectedReply");
+
+ QTest::newRow("action1") << PostAction1 << 1;
+ QTest::newRow("action2") << PostAction2 << 2;
+ QTest::newRow("dismiss") << Dismiss << 0;
+ QTest::newRow("wrongAnswer") << PostLogin << VLC_EGENERIC;
+ }
+
+ //failed state when no renderer manager is found
+ void testQuestion() {
+ QFETCH(UserAnswer, userReply);
+ QFETCH(int, expectedReply);
+
+ QObject::connect(m_dialog, &VLCDialog::question, this,
+ [&](DialogId dialogId, const QString& title,
+ const QString& text, VLCDialog::QuestionType type,
+ const QString& cancel, const QString& action1, const QString& action2){
+
+ QCOMPARE(action1, "action1 txt");
+ QCOMPARE(action2, "action2 txt");
+ QCOMPARE(cancel, "cancel txt");
+ QCOMPARE(title, "title");
+ QCOMPARE(text, "message");
+ QCOMPARE(type, VLCDialog::QuestionType::QUESTION_WARNING);
+
+ switch(userReply)
+ {
+ case PostAction1:
+ m_dialog->post_action1(dialogId);
+ break;
+ case PostAction2:
+ m_dialog->post_action2(dialogId);
+ break;
+ case Dismiss:
+ m_dialog->dismiss(dialogId);
+ break;
+ case PostLogin:
+ m_dialog->post_login(dialogId, "not", "expected"); //user answer the type
+ break;
+ }
+ });
+
+ VLCDialogModel::getInstance<false>()->setProvider(m_dialog);
+
+ std::atomic<bool> replied = false;
+ int reply = -1;
+ QThreadPool::globalInstance()->start([&](){
+ reply = vlc_dialog_wait_question(m_intf, VLC_DIALOG_QUESTION_WARNING,
+ "cancel txt", "action1 txt",
+ "action2 txt", "title", "message");
+ replied = true;
+ });
+ QVERIFY(QTest::qWaitFor([&replied](){ return replied == true; }));
+ if (expectedReply == VLC_EGENERIC)
+ QCOMPARE_LT(reply, 0);
+ else
+ QCOMPARE(reply, expectedReply);
+
+ QCOMPARE(m_loginSpy->count(), 0);
+ QCOMPARE(m_progressSpy->count(), 0);
+ QCOMPARE(m_progressUpdatedSpy->count(), 0);
+ QCOMPARE(m_canceledSpy->count(), 0);
+ }
+
+ //failed state when no renderer manager is found
+ void testProgress() {
+ QObject::connect(m_dialog, &VLCDialog::progress, this,
+ [&](DialogId, const QString& title, const QString& text,
+ bool b_indeterminate, float f_position, const QString& cancel){
+
+ //QCOMPARE performs is fuzzy for floats
+ QCOMPARE(b_indeterminate, false);
+ QCOMPARE(f_position, 0.f);
+ QCOMPARE(cancel, "cancel txt");
+ QCOMPARE(title, "title");
+ QCOMPARE(text, "message");
+ });
+
+ QObject::connect(m_dialog, &VLCDialog::progressUpdated, this,
+ [&](DialogId, float f_position, const QString& text){
+
+ //QCOMPARE performs is fuzzy for floats
+ QCOMPARE(f_position, 0.3f);
+ QCOMPARE(text, "updated text");
+ });
+
+ VLCDialogModel::getInstance<false>()->setProvider(m_dialog);
+
+ std::atomic<bool> replied = false;
+ vlc_dialog_id* dialogId;
+ QThreadPool::globalInstance()->start([&](){
+ dialogId = vlc_dialog_display_progress(m_intf,
+ false, 0.f,
+ "cancel txt", "title", "message");
+ replied = true;
+ });
+ QVERIFY(QTest::qWaitFor([&replied](){ return replied == true; }));
+ QVERIFY(dialogId != nullptr);
+
+ replied = false;
+ int reply;
+ QThreadPool::globalInstance()->start([&](){
+ reply = vlc_dialog_update_progress_text(m_intf, dialogId, 0.3f, "updated text");
+ replied = true;
+ });
+ QVERIFY(QTest::qWaitFor([&replied](){ return replied == true; }));
+ QCOMPARE(reply, VLC_SUCCESS);
+ QCOMPARE(m_canceledSpy->count(), 0);
+
+ replied = false;
+ QThreadPool::globalInstance()->start([&](){
+ vlc_dialog_release(m_intf, dialogId);
+ replied = true;
+ });
+ QVERIFY(QTest::qWaitFor([&replied](){ return replied == true; }));
+ QCOMPARE(m_canceledSpy->count(), 1);
+
+ QCOMPARE(m_loginSpy->count(), 0);
+ QCOMPARE(m_questionSpy->count(), 0);
+ }
+
+ void testLateBinding() {
+ QObject::connect(m_dialog, &VLCDialog::question, this,
+ [&](DialogId dialogId, const QString& ,
+ const QString& , VLCDialog::QuestionType ,
+ const QString& , const QString& , const QString& ){
+ m_dialog->post_action1(dialogId);
+ });
+
+ std::atomic<bool> replied = false;
+ int reply = -1;
+ QThreadPool::globalInstance()->start([&](){
+ reply = vlc_dialog_wait_question(m_intf, VLC_DIALOG_QUESTION_WARNING,
+ "cancel txt", "action1 txt",
+ "action2 txt", "title", "message");
+ replied = true;
+ });
+ //wait for the question to be asked
+ //the dialog should block until VLCDialog is provided
+ QTest::qWait(20);
+
+ //set the dialog afterwards
+ VLCDialogModel::getInstance<false>()->setProvider(m_dialog);
+
+ QVERIFY(QTest::qWaitFor([&replied](){ return replied == true; }));
+ //user replied with Action1
+ QCOMPARE(reply, 1);
+
+ QCOMPARE(m_loginSpy->count(), 0);
+ QCOMPARE(m_progressSpy->count(), 0);
+ QCOMPARE(m_progressUpdatedSpy->count(), 0);
+ QCOMPARE(m_canceledSpy->count(), 0);
+ }
+
+ void testModelDestruction() {
+ std::atomic<bool> replied = false;
+ int reply = -1;
+ QThreadPool::globalInstance()->start([&](){
+ reply = vlc_dialog_wait_question(m_intf, VLC_DIALOG_QUESTION_WARNING,
+ "cancel txt", "action1 txt",
+ "action2 txt", "title", "message");
+ replied = true;
+ });
+ //wait for the question to be asked
+ //the dialog should block until VLCDialog is provided
+ QTest::qWait(20);
+
+ VLCDialogModel::killInstance();
+
+ QVERIFY(QTest::qWaitFor([&replied](){ return replied == true; }));
+
+ QCOMPARE(reply, 0);
+ }
+
+ void testSameThreadWithoutDialog()
+ {
+ int reply = vlc_dialog_wait_question(m_intf, VLC_DIALOG_QUESTION_WARNING,
+ "cancel txt", "action1 txt",
+ "action2 txt", "title", "message");
+ QCOMPARE(reply, 0);
+ }
+
+ void testSameThreadWithDialog()
+ {
+ QObject::connect(m_dialog, &VLCDialog::question, this,
+ [&](DialogId dialogId, const QString& ,
+ const QString& , VLCDialog::QuestionType ,
+ const QString& , const QString& , const QString& ){
+ m_dialog->post_action1(dialogId);
+ });
+ VLCDialogModel::getInstance<false>()->setProvider(m_dialog);
+
+ int reply = vlc_dialog_wait_question(m_intf, VLC_DIALOG_QUESTION_WARNING,
+ "cancel txt", "action1 txt",
+ "action2 txt", "title", "message");
+ QCOMPARE(reply, 1);
+ }
+
+private:
+ std::unique_ptr<VLCTestingEnv> m_env;
+ qt_intf_t* m_intf = nullptr;
+
+ VLCDialog* m_dialog = nullptr;
+
+ std::unique_ptr<QSignalSpy> m_loginSpy;
+ std::unique_ptr<QSignalSpy> m_questionSpy;
+ std::unique_ptr<QSignalSpy> m_progressSpy;
+ std::unique_ptr<QSignalSpy> m_progressUpdatedSpy;
+ std::unique_ptr<QSignalSpy> m_canceledSpy;
+};
+
+QTEST_GUILESS_MAIN(TestVLCDialogModel)
+#include "test_vlc_dialog_model.moc"
=====================================
modules/gui/qt/tests/vlc_stub_modules.cpp
=====================================
@@ -0,0 +1,182 @@
+/*****************************************************************************
+ * Copyright (C) 2024 VLC authors and VideoLAN
+ *
+ * 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.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Define builtin modules for mocked parts */
+#define MODULE_NAME module_faker
+#undef VLC_DYNAMIC_PLUGIN
+
+#include <vlc/vlc.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_playlist.h>
+#include <vlc_services_discovery.h>
+#include <vlc_renderer_discovery.h>
+#include <vlc_probe.h>
+#include <vlc_interface.h>
+#include <vlc_player.h>
+
+#include "../../../../lib/libvlc_internal.h"
+
+#include "qt.hpp"
+
+
+#include "vlc_stub_modules.hpp"
+
+static VLCTestingEnv* testenv = nullptr;
+
+///RenderDiscovery module
+
+static int OpenRD( vlc_object_t* p_this )
+{
+ testenv->renderDiscovery = (vlc_renderer_discovery_t *)p_this;
+ return VLC_SUCCESS;
+}
+
+static void CloseRD( vlc_object_t* )
+{
+ testenv->renderDiscovery = nullptr;
+}
+
+static int vlc_rd_probe_open(vlc_object_t *obj) {
+ auto probe = (struct vlc_probe_t *)obj;
+
+ if (testenv->renderDiscoveryProbeEnabled)
+ vlc_rd_probe_add(probe, "rd", "a fake renderer for testing purpose");
+ //only probe ourself
+ return VLC_PROBE_STOP;
+}
+
+///Interface module
+
+namespace vlc {
+class Compositor {};
+}
+
+static int OpenIntf(vlc_object_t* p_this)
+{
+ auto intfThread = reinterpret_cast<intf_thread_t*>(p_this);
+ libvlc_int_t* libvlc = vlc_object_instance( p_this );
+
+ /* Ensure initialization of objects in qt_intf_t. */
+ qt_intf_t* qt_intf = vlc_object_create<qt_intf_t>( libvlc );
+ if (!qt_intf)
+ return VLC_ENOMEM;
+
+ qt_intf->obj.logger = vlc_LogHeaderCreate(libvlc->obj.logger, "qt");
+ if (!qt_intf->obj.logger)
+ {
+ vlc_object_delete(qt_intf);
+ return VLC_EGENERIC;
+ }
+
+ qt_intf->p_playlist = vlc_intf_GetMainPlaylist(intfThread);
+ qt_intf->p_player = vlc_playlist_GetPlayer(qt_intf->p_playlist);
+ qt_intf->intf = intfThread;
+
+ intfThread->p_sys = reinterpret_cast<intf_sys_t*>(qt_intf);
+
+ testenv->intf = qt_intf;
+
+ return VLC_SUCCESS;
+}
+
+static void CloseIntf( vlc_object_t *p_this )
+{
+ intf_thread_t* intfThread = (intf_thread_t*)(p_this);
+ auto qt_intf = reinterpret_cast<qt_intf_t*>(intfThread->p_sys);
+ if (!qt_intf)
+ return;
+ vlc_LogDestroy(qt_intf->obj.logger);
+ vlc_object_delete(qt_intf);
+
+ testenv->intf = nullptr;
+}
+
+//module declaration
+
+vlc_module_begin()
+ set_callbacks(OpenIntf, CloseIntf)
+ set_capability("interface", 0)
+add_submodule()
+ set_capability("renderer_discovery", 0)
+ add_shortcut("rd")
+ set_callbacks(OpenRD, CloseRD)
+add_submodule()
+ set_capability("renderer probe", 10000)
+ set_callback(vlc_rd_probe_open)
+vlc_module_end()
+
+extern "C" {
+
+const char vlc_module_name[] = MODULE_STRING;
+VLC_EXPORT vlc_plugin_cb vlc_static_modules[] = {
+ VLC_SYMBOL(vlc_entry),
+ NULL
+};
+}
+
+VLCTestingEnv::VLCTestingEnv()
+{
+ assert(testenv == nullptr);
+ testenv = this;
+
+}
+
+VLCTestingEnv::~VLCTestingEnv()
+{
+ libvlc_release(libvlc);
+ testenv = nullptr;
+}
+
+static const char * test_defaults_args[] = {
+ "-v", "--vout=vdummy", "--aout=adummy", "--text-renderer=tdummy",
+};
+
+static const int test_defaults_nargs =
+ sizeof (test_defaults_args) / sizeof (test_defaults_args[0]);
+
+
+bool VLCTestingEnv::init()
+{
+ //see test/libvlc/test.h
+ QByteArray alarm_timeout = qgetenv("VLC_TEST_TIMEOUT");
+ if (alarm_timeout.isEmpty())
+ qputenv("QTEST_FUNCTION_TIMEOUT", "5000");
+ else
+ qputenv("QTEST_FUNCTION_TIMEOUT", alarm_timeout);
+
+ setenv("VLC_PLUGIN_PATH", TOP_BUILDDIR"/modules", 1);
+ setenv("VLC_LIB_PATH", TOP_BUILDDIR, 1);
+
+ libvlc = libvlc_new(test_defaults_nargs, test_defaults_args);
+ if (!libvlc)
+ return false;
+ libvlc_InternalAddIntf(libvlc->p_libvlc_int, MODULE_STRING);
+ libvlc_InternalPlay(libvlc->p_libvlc_int);
+
+ if (!intf)
+ return false;
+
+ return true;
+}
+
+
=====================================
modules/gui/qt/tests/vlc_stub_modules.hpp
=====================================
@@ -0,0 +1,75 @@
+/*****************************************************************************
+ * Copyright (C) 2024 VLC authors and VideoLAN
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifndef QT_VLC_STUB_MODULES
+#define QT_VLC_STUB_MODULES
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <vlc/vlc.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_player.h>
+#include <vlc_playlist.h>
+#include <vlc_services_discovery.h>
+#include <vlc_renderer_discovery.h>
+#include <vlc_interface.h>
+#include <vlc_cxx_helpers.hpp>
+
+#include "qt.hpp"
+
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+#define QCOMPARE_LT(a, b) QVERIFY((a) < (b))
+#define QCOMPARE_GT(a, b) QVERIFY((a) > (b))
+#define QCOMPARE_LE(a, b) QVERIFY((a) <= (b))
+#define QCOMPARE_GE(a, b) QVERIFY((a) >= (b))
+#endif
+
+/**
+ * This class allows to instanciate a libvlc instance with stubbed modules
+ * the modules instance can be reteived and manipulated using their pointer
+ * This allows testing models depending on vlc in a controlled environment
+ * validity of the pointers depends of the module usage
+ */
+struct VLCTestingEnv
+{
+ VLCTestingEnv();
+ ~VLCTestingEnv();
+
+ bool init();
+
+ //should be valid after init
+ libvlc_instance_t* libvlc = nullptr;
+
+ //pointer to the qt interface pointer
+ //no qt related objects are defined in it (no MainCtx, no Compositor ...)
+ //should be valid after init
+ qt_intf_t* intf = nullptr;
+
+ //render discovery instance (valid once created)
+ vlc_renderer_discovery_t* renderDiscovery = nullptr;
+
+ //if set to false no render discovery will be found
+ bool renderDiscoveryProbeEnabled = true;
+};
+
+#endif /* QT_VLC_STUB_MODULES */
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/e85ead7660e52ee721607b7bed7fa8a2e7696292...e4c67e4cec31015e80174e09ef880b18cfdfba49
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/e85ead7660e52ee721607b7bed7fa8a2e7696292...e4c67e4cec31015e80174e09ef880b18cfdfba49
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list