[Libreoffice-commits] core.git: Branch 'libreoffice-4-2' - configure.ac vcl/CustomTarget_kde4_moc.mk vcl/unx

Luboš Luňák l.lunak at collabora.com
Wed Apr 30 01:29:50 PDT 2014


 configure.ac                                  |   45 ++++++++-
 vcl/CustomTarget_kde4_moc.mk                  |    3 
 vcl/unx/kde4/KDE4FilePicker.cxx               |   21 +---
 vcl/unx/kde4/KDESalInstance.cxx               |    2 
 vcl/unx/kde4/KDEXLib.cxx                      |   21 ++--
 vcl/unx/kde4/KDEXLib.hxx                      |    4 
 vcl/unx/kde4/VCLKDEApplication.cxx            |   44 ++++++++
 vcl/unx/kde4/VCLKDEApplication.hxx            |    2 
 vcl/unx/kde4/tst_exclude_posted_events.hxx    |   72 ++++++++++++++
 vcl/unx/kde4/tst_exclude_socket_notifiers.hxx |  130 ++++++++------------------
 10 files changed, 231 insertions(+), 113 deletions(-)

New commits:
commit 80f113efa6f60c6b3aad33128921792451f114ce
Author: Luboš Luňák <l.lunak at collabora.com>
Date:   Fri Mar 28 15:09:13 2014 +0100

    fix KFileDialog crashes (fdo#69002)
    
    Requires Qt patches (or otherwise LO generic file dialog is used).
    Squashed from several master patches.
    
    (cherry picked from commit 2cd8a1e0f1e81efd15979953d7f274ab8a6806d6)
    (cherry picked from commit 474ad6b0e2fb18370be9d228456a2abbfc15bad2)
    (cherry picked from commit e809aa1e916e0f6d1a849d0374f59ef9619b1db7)
    (cherry picked from commit 65a3622148ea67744c9c1fc18c2b8d48e5f1c79f)
    (cherry picked from commit 508337db0c53caa5fb43ef26f781df159497a482)
    (cherry picked from commit 9aa32a34c7c7a2ee4da5f01983a0ed224c38c875)
    (cherry picked from commit 145f2e970f46a3a3e5456b122d71f17c3abe878f)
    (cherry picked from commit f09d4bc2853be2fa3faa0502b8efe94ad9719731)
    
    Change-Id: I6dba5da0170fb94bab928f71efc7dc8c03cc70d9
    Reviewed-on: https://gerrit.libreoffice.org/9206
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>
    Tested-by: Caolán McNamara <caolanm at redhat.com>

diff --git a/configure.ac b/configure.ac
index 5751f85..98e2a0e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11240,6 +11240,8 @@ int main(int argc, char **argv) {
             AC_DEFINE(KDE_HAVE_GLIB,1)
             KDE_GLIB_CFLAGS=$(printf '%s' "$KDE_GLIB_CFLAGS" | sed -e "s/-I/${ISYSTEM?}/g")
 
+            qt4_fix_warning=
+
             AC_LANG_PUSH([C++])
             save_CXXFLAGS=$CXXFLAGS
             CXXFLAGS="$CXXFLAGS $KDE4_CFLAGS"
@@ -11266,12 +11268,53 @@ int main(int argc, char *argv[])
                 AC_MSG_RESULT([yes])
             ],[
                 AC_MSG_RESULT([no])
-                AC_MSG_WARN([native KDE4 file pickers will be disabled at runtime - fix your Qt4 library!])
+                AC_MSG_WARN([native KDE4 file pickers will be disabled at runtime])
+                if test -z "$qt4_fix_warning"; then
+                    add_warning "native KDE4 file pickers will be disabled at runtime, Qt4 fixes needed"
+                fi
+                qt4_fix_warning=1
+                add_warning "  https://bugreports.qt-project.org/browse/QTBUG-37380 (needed)"
             ])
 
             # Remove meta object data
             rm -f "${TSTBASE}."*
 
+            AC_MSG_CHECKING([whether Qt avoids QClipboard recursion caused by posted events])
+
+            # Prepare meta object data
+            TSTBASE="tst_exclude_posted_events"
+            TSTMOC="${SRC_ROOT}/vcl/unx/kde4/${TSTBASE}"
+            ln -fs "${TSTMOC}.hxx"
+            $MOC4 "${TSTBASE}.hxx" -o "${TSTBASE}.moc"
+
+            AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include "tst_exclude_posted_events.moc"
+
+int main(int argc, char *argv[])
+{
+    QCoreApplication app(argc, argv);
+    exit(tst_excludePostedEvents());
+    return 0;
+}
+            ]])],[
+                AC_MSG_RESULT([yes])
+            ],[
+                AC_MSG_RESULT([no])
+                AC_MSG_WARN([native KDE4 file pickers will be disabled at runtime])
+                if test -z "$qt4_fix_warning"; then
+                    add_warning "native KDE4 file pickers will be disabled at runtime, Qt4 fixes needed"
+                fi
+                qt4_fix_warning=1
+                add_warning "  https://bugreports.qt-project.org/browse/QTBUG-34614 (needed)"
+            ])
+
+            # Remove meta object data
+            rm -f "${TSTBASE}."*
+
+            if test -n "$qt4_fix_warning"; then
+                add_warning "  https://bugreports.qt-project.org/browse/QTBUG-38585 (recommended)"
+            fi
+
             LIBS=$save_LIBS
             CXXFLAGS=$save_CXXFLAGS
             AC_LANG_POP([C++])
diff --git a/vcl/CustomTarget_kde4_moc.mk b/vcl/CustomTarget_kde4_moc.mk
index 9e41754..16d1561 100644
--- a/vcl/CustomTarget_kde4_moc.mk
+++ b/vcl/CustomTarget_kde4_moc.mk
@@ -12,7 +12,8 @@ $(eval $(call gb_CustomTarget_CustomTarget,vcl/unx/kde4))
 $(call gb_CustomTarget_get_target,vcl/unx/kde4) : \
 	$(call gb_CustomTarget_get_workdir,vcl/unx/kde4)/KDEXLib.moc \
 	$(call gb_CustomTarget_get_workdir,vcl/unx/kde4)/KDE4FilePicker.moc \
-	$(call gb_CustomTarget_get_workdir,vcl/unx/kde4)/tst_exclude_socket_notifiers.moc
+	$(call gb_CustomTarget_get_workdir,vcl/unx/kde4)/tst_exclude_socket_notifiers.moc \
+	$(call gb_CustomTarget_get_workdir,vcl/unx/kde4)/tst_exclude_posted_events.moc
 
 $(call gb_CustomTarget_get_workdir,vcl/unx/kde4)/%.moc : \
 		$(SRCDIR)/vcl/unx/kde4/%.hxx \
diff --git a/vcl/unx/kde4/KDE4FilePicker.cxx b/vcl/unx/kde4/KDE4FilePicker.cxx
index 1cb1460..2d6bfba 100644
--- a/vcl/unx/kde4/KDE4FilePicker.cxx
+++ b/vcl/unx/kde4/KDE4FilePicker.cxx
@@ -38,6 +38,7 @@
 
 #include "KDE4FilePicker.hxx"
 #include "FPServiceInfo.hxx"
+#include "VCLKDEApplication.hxx"
 
 /* ********* Hack, but needed because of conflicting types... */
 #define Region QtXRegion
@@ -260,21 +261,11 @@ sal_Int16 SAL_CALL KDE4FilePicker::execute()
     _dialog->setFilter(_filter);
     _dialog->filterWidget()->setEditable(false);
 
-    // We're entering a nested loop.
-    int result;
-    {
-        // Release the yield mutex to prevent deadlocks.
-        SalYieldMutexReleaser aReleaser;
-        result = _dialog->exec();
-    }
-
-    // HACK: KFileDialog uses KConfig("kdeglobals") for saving some settings
-    // (such as the auto-extension flag), but that doesn't update KGlobal::config()
-    // (which is probably a KDE bug), so force reading the new configuration,
-    // otherwise the next opening of the dialog would use the old settings.
-    KGlobal::config()->reparseConfiguration();
-
-    if( result == KFileDialog::Accepted)
+    VCLKDEApplication::preDialogSetup();
+    //block and wait for user input
+    int result = _dialog->exec();
+    VCLKDEApplication::postDialogCleanup();
+    if( result == KFileDialog::Accepted )
         return ExecutableDialogResults::OK;
 
     return ExecutableDialogResults::CANCEL;
diff --git a/vcl/unx/kde4/KDESalInstance.cxx b/vcl/unx/kde4/KDESalInstance.cxx
index 023d790..094cd20 100644
--- a/vcl/unx/kde4/KDESalInstance.cxx
+++ b/vcl/unx/kde4/KDESalInstance.cxx
@@ -35,7 +35,7 @@ uno::Reference< ui::dialogs::XFilePicker2 > KDESalInstance::createFilePicker(
     const uno::Reference< uno::XComponentContext >& xMSF )
 {
     KDEXLib* kdeXLib = static_cast<KDEXLib*>( mpXLib );
-    if (kdeXLib->haveQt4SocketExcludeFix())
+    if (kdeXLib->allowKdeDialogs())
         return uno::Reference< ui::dialogs::XFilePicker2 >(
             kdeXLib->createFilePicker(xMSF) );
     else
diff --git a/vcl/unx/kde4/KDEXLib.cxx b/vcl/unx/kde4/KDEXLib.cxx
index e4900a7..2630c0c 100644
--- a/vcl/unx/kde4/KDEXLib.cxx
+++ b/vcl/unx/kde4/KDEXLib.cxx
@@ -47,13 +47,14 @@
 #if KDE_HAVE_GLIB
 #include "KDE4FilePicker.hxx"
 #include "tst_exclude_socket_notifiers.moc"
+#include "tst_exclude_posted_events.moc"
 #endif
 
 KDEXLib::KDEXLib() :
     SalXLib(),  m_bStartupDone(false), m_pApplication(0),
     m_pFreeCmdLineArgs(0), m_pAppCmdLineArgs(0), m_nFakeCmdLineArgs( 0 ),
     m_frameWidth( -1 ), m_isGlibEventLoopType(false),
-    m_haveQt4SocketExcludeFix(false)
+    m_allowKdeDialogs(false)
 {
     // the timers created here means they belong to the main thread.
     // As the timeoutTimer runs the LO event queue, which may block on a dialog,
@@ -187,9 +188,14 @@ void KDEXLib::Init()
 
 #if KDE_HAVE_GLIB
     m_isGlibEventLoopType = QAbstractEventDispatcher::instance()->inherits( "QEventDispatcherGlib" );
-    if (m_isGlibEventLoopType && (0 == tst_processEventsExcludeSocket()))
-        // See http://bugreports.qt.nokia.com/browse/QTBUG-37380
-        m_haveQt4SocketExcludeFix = true;
+    // Using KDE dialogs (and their nested event loops) works only with a proper event loop integration
+    // that will release SolarMutex when waiting for more events.
+    // Moreover there are bugs in Qt event loop code that allow QClipboard recursing because the event
+    // loop processes also events that it should not at that point, so no dialogs in that case either.
+    // https://bugreports.qt-project.org/browse/QTBUG-37380
+    // https://bugreports.qt-project.org/browse/QTBUG-34614
+    if (m_isGlibEventLoopType && (0 == tst_processEventsExcludeSocket()) && tst_excludePostedEvents() == 0 )
+        m_allowKdeDialogs = true;
 #endif
 
     setupEventLoop();
@@ -238,7 +244,7 @@ void KDEXLib::setupEventLoop()
     {
         old_gpoll = g_main_context_get_poll_func( NULL );
         g_main_context_set_poll_func( NULL, gpoll_wrapper );
-        if( m_haveQt4SocketExcludeFix )
+        if( m_allowKdeDialogs )
             m_pApplication->clipboard()->setProperty( "useEventLoopWhenWaiting", true );
         return;
     }
@@ -286,7 +292,6 @@ void KDEXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
         }
         return SalXLib::Yield( bWait, bHandleAllCurrentEvents );
     }
-
     // if we are the main thread (which is where the event processing is done),
     // good, just do it
     if( qApp->thread() == QThread::currentThread())
@@ -294,7 +299,9 @@ void KDEXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
     else
     {
         // we were called from another thread;
-        // release the yield lock to prevent deadlock.
+        // release the yield lock to prevent deadlock with the main thread
+        // (it's ok to release it here, since even normal processYield() would
+        // temporarily do it while checking for new events)
         SalYieldMutexReleaser aReleaser;
         Q_EMIT processYieldSignal( bWait, bHandleAllCurrentEvents );
     }
diff --git a/vcl/unx/kde4/KDEXLib.hxx b/vcl/unx/kde4/KDEXLib.hxx
index a88258c..53e01c0 100644
--- a/vcl/unx/kde4/KDEXLib.hxx
+++ b/vcl/unx/kde4/KDEXLib.hxx
@@ -53,7 +53,7 @@ class KDEXLib : public QObject, public SalXLib
         QTimer userEventTimer;
         int m_frameWidth;
         bool m_isGlibEventLoopType;
-        bool m_haveQt4SocketExcludeFix;
+        bool m_allowKdeDialogs;
 
     private:
         void setupEventLoop();
@@ -88,7 +88,7 @@ class KDEXLib : public QObject, public SalXLib
         virtual void PostUserEvent();
 
         void doStartup();
-        bool haveQt4SocketExcludeFix() { return m_haveQt4SocketExcludeFix; }
+        bool allowKdeDialogs() { return m_allowKdeDialogs; }
 
     public Q_SLOTS:
         com::sun::star::uno::Reference< com::sun::star::ui::dialogs::XFilePicker2 >
diff --git a/vcl/unx/kde4/VCLKDEApplication.cxx b/vcl/unx/kde4/VCLKDEApplication.cxx
index e196ff3..65f6999 100644
--- a/vcl/unx/kde4/VCLKDEApplication.cxx
+++ b/vcl/unx/kde4/VCLKDEApplication.cxx
@@ -19,6 +19,7 @@
 
 #include "VCLKDEApplication.hxx"
 
+#include <QClipboard>
 #include <QEvent>
 
 #include "KDESalDisplay.hxx"
@@ -40,4 +41,47 @@ bool VCLKDEApplication::x11EventFilter(XEvent* ev)
     return false;
 }
 
+// various hacks to be performed before re-entering Qt's event loop
+// because of showing a Qt dialog
+void VCLKDEApplication::preDialogSetup()
+{
+    // KFileDialog intergration requires using event loop with QClipboard.
+    // Opening the KDE file dialog here can lead to QClipboard
+    // asking for clipboard contents. If LO core is the owner of the clipboard
+    // content, without event loop use this will block for 5 seconds and timeout,
+    // since the clipboard thread will not be able to acquire SolarMutex
+    // and thus won't be able to respond. If the event loops
+    // are properly integrated and QClipboard can use a nested event loop
+    // (see the KDE VCL plug), then this won't happen.
+    // We cannot simply release SolarMutex here, because the event loop started
+    // by the file dialog would also call back to LO code.
+    assert( qApp->clipboard()->property( "useEventLoopWhenWaiting" ).toBool() == true );
+}
+
+// various hacks to be performed after a Qt dialog has been closed
+void VCLKDEApplication::postDialogCleanup()
+{
+    // HACK: KFileDialog uses KConfig("kdeglobals") for saving some settings
+    // (such as the auto-extension flag), but that doesn't update KGlobal::config()
+    // (which is probably a KDE bug), so force reading the new configuration,
+    // otherwise the next opening of the dialog would use the old settings.
+    KGlobal::config()->reparseConfiguration();
+    // HACK: If Qt owns clipboard or selection, give up on their ownership now. Otherwise
+    // LO core might ask for the contents, but it would block while doing so (i.e. it
+    // doesn't seem to have an equivalent of QClipboard's "useEventLoopWhenWaiting"),
+    // therefore QClipboard wouldn't be able to respond, and whole LO would block until
+    // a timeout. Given that Klipper is most probably running, giving up clipboard/selection
+    // ownership will not only avoid the blocking, but even pasting that content in LO
+    // will in fact work, if Klipper can handle it.
+    // Technically proper solution would be of course to allow Qt to process QClipboard
+    // events while LO waits for clipboard contents, or short-circuit to QClipboard somehow
+    // (it's a mystery why LO's clipboard handling has its own thread when whole LO can
+    // get blocked by both trying to send and receive clipboard contents anyway).
+    QClipboard* clipboard = QApplication::clipboard();
+    if( clipboard->ownsSelection())
+        clipboard->clear( QClipboard::Selection );
+    if( clipboard->ownsClipboard())
+        clipboard->clear( QClipboard::Clipboard );
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde4/VCLKDEApplication.hxx b/vcl/unx/kde4/VCLKDEApplication.hxx
index 4ce0b2c..d31f4e8 100644
--- a/vcl/unx/kde4/VCLKDEApplication.hxx
+++ b/vcl/unx/kde4/VCLKDEApplication.hxx
@@ -30,6 +30,8 @@ class VCLKDEApplication : public KApplication
     public:
         VCLKDEApplication();
         virtual bool x11EventFilter(XEvent* event);
+        static void preDialogSetup();
+        static void postDialogCleanup();
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde4/tst_exclude_posted_events.hxx b/vcl/unx/kde4/tst_exclude_posted_events.hxx
new file mode 100644
index 0000000..7127505
--- /dev/null
+++ b/vcl/unx/kde4/tst_exclude_posted_events.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ *
+ * This code is based on the SocketEventsTester from the Qt4 test suite.
+ */
+
+#pragma once
+
+#include <qcoreapplication.h>
+#include <qeventloop.h>
+
+namespace
+{
+
+const QEvent::Type eventType = QEvent::User;
+
+class TestExcludePostedEvents
+    : public QObject
+{
+    Q_OBJECT
+    public:
+        TestExcludePostedEvents();
+        virtual bool event( QEvent* e );
+        bool processed;
+};
+
+TestExcludePostedEvents::TestExcludePostedEvents()
+    : processed( false )
+{
+}
+
+bool TestExcludePostedEvents::event( QEvent* e )
+{
+    if( e->type() == eventType )
+        processed = true;
+    return QObject::event( e );
+}
+
+}
+
+#define QVERIFY(a) \
+    if (!a) return 1;
+
+static int tst_excludePostedEvents()
+{
+    TestExcludePostedEvents test;
+    QCoreApplication::postEvent( &test, new QEvent( eventType ));
+    QEventLoop loop;
+    loop.processEvents(QEventLoop::ExcludeUserInputEvents
+        | QEventLoop::ExcludeSocketNotifiers
+//        | QEventLoop::WaitForMoreEvents
+        | QEventLoop::X11ExcludeTimers);
+    QVERIFY( !test.processed );
+    loop.processEvents();
+    QVERIFY( test.processed );
+    return 0;
+}
diff --git a/vcl/unx/kde4/tst_exclude_socket_notifiers.hxx b/vcl/unx/kde4/tst_exclude_socket_notifiers.hxx
index 0c874fd..acf4d36 100644
--- a/vcl/unx/kde4/tst_exclude_socket_notifiers.hxx
+++ b/vcl/unx/kde4/tst_exclude_socket_notifiers.hxx
@@ -23,104 +23,62 @@
 
 #include <qcoreapplication.h>
 #include <qeventloop.h>
-#include <qthread.h>
-#include <qtimer.h>
-#include <QtNetwork/qtcpserver.h>
-#include <QtNetwork/qtcpsocket.h>
+#include <qsocketnotifier.h>
+#include <unistd.h>
 
-class SocketEventsTester: public QObject
+namespace
+{
+
+class TestExcludeSocketNotifiers
+    : public QObject
 {
     Q_OBJECT
-public:
-    SocketEventsTester()
-    {
-        socket = 0;
-        server = 0;
-        dataSent = false;
-        testResult = false;
-        dataArrived = false;
-    }
-    ~SocketEventsTester()
-    {
-        delete socket;
-        delete server;
-    }
-    bool init()
-    {
-        bool ret = false;
-        server = new QTcpServer();
-        socket = new QTcpSocket();
-        connect(server, SIGNAL(newConnection()), this, SLOT(sendHello()));
-        connect(socket, SIGNAL(readyRead()), this, SLOT(sendAck()), Qt::DirectConnection);
-        if((ret = server->listen(QHostAddress::LocalHost, 0))) {
-            socket->connectToHost(server->serverAddress(), server->serverPort());
-            socket->waitForConnected();
-        }
-        return ret;
-    }
+    public:
+        TestExcludeSocketNotifiers( const int* pipes );
+        ~TestExcludeSocketNotifiers();
+        bool received;
+    public slots:
+        void slotReceived();
+    private:
+        const int* pipes;
+};
 
-    QTcpSocket *socket;
-    QTcpServer *server;
-    bool dataSent;
-    bool testResult;
-    bool dataArrived;
-public slots:
-    void sendAck()
-    {
-        dataArrived = true;
-    }
-    void sendHello()
-    {
-        char data[10] ="HELLO";
-        qint64 size = sizeof(data);
+TestExcludeSocketNotifiers::TestExcludeSocketNotifiers( const int* pipes )
+    : received( false )
+    , pipes( pipes )
+{
+}
 
-        QTcpSocket *serverSocket = server->nextPendingConnection();
-        serverSocket->write(data, size);
-        dataSent = serverSocket->waitForBytesWritten(-1);
-        QEventLoop loop;
-        //allow the TCP/IP stack time to loopback the data, so our socket is ready to read
-        QTimer::singleShot(200, &loop, SLOT(quit()));
-        loop.exec(QEventLoop::ExcludeSocketNotifiers);
-        testResult = dataArrived;
-        //check the deferred event is processed
-        QTimer::singleShot(200, &loop, SLOT(quit()));
-        loop.exec();
-        serverSocket->close();
-        QThread::currentThread()->exit(0);
-    }
-};
+TestExcludeSocketNotifiers::~TestExcludeSocketNotifiers()
+{
+    close( pipes[ 0 ] );
+    close( pipes[ 1 ] );
+}
 
-class SocketTestThread : public QThread
+void TestExcludeSocketNotifiers::slotReceived()
 {
-    Q_OBJECT
-public:
-    SocketTestThread():QThread(0),testResult(false){};
-    void run()
-    {
-        SocketEventsTester *tester = new SocketEventsTester();
-        if (tester->init())
-            exec();
-        dataSent = tester->dataSent;
-        testResult = tester->testResult;
-        dataArrived = tester->dataArrived;
-        delete tester;
-    }
-    bool dataSent;
-    bool testResult;
-    bool dataArrived;
-};
+    received = true;
+}
+
+}
 
 #define QVERIFY(a) \
     if (!a) return 1;
 
 static int tst_processEventsExcludeSocket()
 {
-    SocketTestThread thread;
-    thread.start();
-    QVERIFY(thread.wait());
-    QVERIFY(thread.dataSent);
-    QVERIFY(!thread.testResult);
-    QVERIFY(thread.dataArrived);
+    int pipes[ 2 ];
+    if( pipe( pipes ) < 0 )
+        return 1;
+    TestExcludeSocketNotifiers test( pipes );
+    QSocketNotifier notifier( pipes[ 0 ], QSocketNotifier::Read );
+    QObject::connect( &notifier, SIGNAL( activated( int )), &test, SLOT( slotReceived()));
+    char dummy = 'a';
+    write( pipes[ 1 ], &dummy, 1 );
+    QEventLoop loop;
+    loop.processEvents( QEventLoop::ExcludeSocketNotifiers );
+    QVERIFY( !test.received );
+    loop.processEvents();
+    QVERIFY( test.received );
     return 0;
 }
-


More information about the Libreoffice-commits mailing list