kdelibs/kdecore/tests/ksycocathreadtest.cpp
Ivailo Monev cdb0195dfd kdecore: testing build fix
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2021-06-16 04:05:54 +03:00

308 lines
10 KiB
C++

/* This file is part of the KDE project
Copyright 2009 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2 of the License or
( at your option ) version 3 or, at the discretion of KDE e.V.
( which shall act as a proxy as in section 14 of the GPLv3 ), any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <kcomponentdata.h>
#include <kservicegroup.h>
#include <kmimetype.h>
#include <qtest_kde.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kdesktopfile.h>
#include <ksycoca.h>
#include <kglobal.h>
#include <kservicetypetrader.h>
#include <kstandarddirs.h>
#include <kdebug.h>
#include <kservicetype.h>
#include <QtCore/qprocess.h>
#include <QtCore/qthread.h>
// Helper method for all the trader tests
static bool offerListHasService( const KService::List& offers,
const QString& entryPath )
{
bool found = false;
KService::List::const_iterator it = offers.begin();
for ( ; it != offers.end() ; ++it )
{
if ( (*it)->entryPath() == entryPath ) {
if( found ) { // should be there only once
qWarning( "ERROR: %s was found twice in the list", qPrintable( entryPath ) );
return false; // make test fail
}
found = true;
}
}
return found;
}
class WorkerObject : public QObject
{
Q_OBJECT
public:
WorkerObject() : QObject() {}
public slots:
void work() {
kDebug() << QThread::currentThread() << "working...";
const KServiceType::List allServiceTypes = KServiceType::allServiceTypes();
Q_ASSERT(!allServiceTypes.isEmpty());
const KMimeType::List allMimeTypes = KMimeType::allMimeTypes();
Q_ASSERT(!allMimeTypes.isEmpty());
const KService::List lst = KService::allServices();
Q_ASSERT(!lst.isEmpty());
for ( KService::List::ConstIterator it = lst.begin();
it != lst.end(); ++it ) {
const KService::Ptr service = (*it);
Q_ASSERT(service->isType(KST_KService));
const QString name = service->name();
const QString entryPath = service->entryPath();
//kDebug() << name << "entryPath=" << entryPath << "menuId=" << service->menuId();
Q_ASSERT(!name.isEmpty());
Q_ASSERT(!entryPath.isEmpty());
KService::Ptr lookedupService = KService::serviceByDesktopPath( entryPath );
Q_ASSERT( lookedupService ); // not null
QCOMPARE( lookedupService->entryPath(), entryPath );
if (service->isApplication()) {
const QString menuId = service->menuId();
if ( menuId.isEmpty() )
qWarning( "%s has an empty menuId!", qPrintable( entryPath ) );
Q_ASSERT( !menuId.isEmpty() );
lookedupService = KService::serviceByMenuId( menuId );
Q_ASSERT( lookedupService ); // not null
QCOMPARE( lookedupService->menuId(), menuId );
}
}
KService::List offers = KServiceTypeTrader::self()->query("KParts/ReadOnlyPart");
Q_ASSERT( offers.count() > 0 );
offers = KServiceTypeTrader::self()->query("KTextEditor/Plugin");
Q_ASSERT( offerListHasService( offers, "faketextplugin.desktop" ) );
offers = KServiceTypeTrader::self()->query("KTextEditor/Plugin", "Library == 'faketextplugin'");
Q_ASSERT(offers.count() == 1);
QVERIFY( offerListHasService( offers, "faketextplugin.desktop" ) );
KServiceGroup::Ptr root = KServiceGroup::root();
Q_ASSERT(root);
}
};
class WorkerThread : public QThread
{
Q_OBJECT
public:
WorkerThread() : QThread() {
m_stop = false;
}
virtual void run() {
WorkerObject wo;
while (!m_stop) {
wo.work();
}
}
virtual void stop() { m_stop = true; }
private:
bool m_stop;
};
/**
* Threads with an event loop will be able to process "database changed" signals.
* Threads without an event loop (like WorkerThread) cannot, so they will keep using
* the old data.
*/
class EventLoopThread : public WorkerThread
{
Q_OBJECT
public:
virtual void run() {
// WorkerObject must belong to this thread, this is why we don't
// have the slot work() in WorkerThread itself. Typical QThread trap!
WorkerObject wo;
QTimer timer;
connect(&timer, SIGNAL(timeout()), &wo, SLOT(work()));
timer.start(100);
exec();
}
virtual void stop() {
quit();
}
};
class KSycocaThreadTest : public QObject
{
Q_OBJECT
public:
KSycocaThreadTest() {}
void launch();
private slots:
// Note that this isn't a QTest based test.
// All these methods are called manually.
void cleanupTestCase();
void testCreateService();
void testDeleteService() {
deleteFakeService();
QTimer::singleShot(1000, this, SLOT(slotFinish()));
}
void slotFinish() {
kDebug() << "Terminating";
for (int i=0; i<threads.size(); i++) {
threads[i]->stop();
}
for (int i=0; i<threads.size(); i++) {
threads[i]->wait();
}
cleanupTestCase();
QCoreApplication::instance()->quit();
}
private:
void createFakeService();
void deleteFakeService();
QVector<WorkerThread*> threads;
};
static void runKBuildSycoca()
{
QProcess proc;
const QString kbuildsycoca = KStandardDirs::findExe(KBUILDSYCOCA_EXENAME);
QVERIFY(!kbuildsycoca.isEmpty());
QStringList args;
proc.setProcessChannelMode(QProcess::MergedChannels); // silence kbuildsycoca output
proc.start(kbuildsycoca, args);
kDebug() << "waiting for signal";
QVERIFY(QTest::kWaitForSignal(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), 10000));
kDebug() << "got signal";
proc.waitForFinished();
QCOMPARE(proc.exitStatus(), QProcess::NormalExit);
}
void KSycocaThreadTest::launch()
{
// This service is always there. Used in the trader queries from the thread.
const QString fakeTextPlugin = KStandardDirs::locateLocal("services", "faketextplugin.desktop");
if (!QFile::exists(fakeTextPlugin)) {
KDesktopFile file(fakeTextPlugin);
KConfigGroup group = file.desktopGroup();
group.writeEntry("Name", "FakeTextPlugin");
group.writeEntry("Type", "Service");
group.writeEntry("X-KDE-Library", "faketextplugin");
group.writeEntry("X-KDE-Protocols", "http,ftp");
group.writeEntry("ServiceTypes", "KTextEditor/Plugin");
group.writeEntry("MimeType", "text/plain;");
file.sync();
qDebug() << "Created" << fakeTextPlugin << ", running kbuilsycoca";
runKBuildSycoca();
// Process the event
int count = 0;
while (!KService::serviceByDesktopPath("faketextplugin.desktop")) {
qApp->processEvents();
if (++count == 20) {
qFatal("sycoca doesn't have faketextplugin.desktop");
}
}
}
// Start clean
const QString servPath = KStandardDirs::locateLocal("services", "fakeservice.desktop");
if (QFile::exists(servPath))
QFile::remove(servPath);
if (KService::serviceByDesktopPath("fakeservice.desktop"))
deleteFakeService();
threads.resize(5);
for (int i=0; i<threads.size(); i++) {
threads[i] = i < 3 ? new WorkerThread : new EventLoopThread;
threads[i]->start();
}
testCreateService();
}
void KSycocaThreadTest::cleanupTestCase()
{
const QString fakeTextPlugin = KStandardDirs::locateLocal("services", "faketextplugin.desktop");
QFile::remove(fakeTextPlugin);
}
void KSycocaThreadTest::testCreateService()
{
createFakeService();
const QString servPath = KStandardDirs::locateLocal("services", "fakeservice.desktop");
Q_ASSERT(QFile::exists(servPath));
kDebug() << "executing kbuildsycoca (1)";
runKBuildSycoca();
QTimer::singleShot(1000, this, SLOT(testDeleteService()));
}
void KSycocaThreadTest::deleteFakeService()
{
KService::Ptr fakeService = KService::serviceByDesktopPath("fakeservice.desktop");
Q_ASSERT(fakeService);
const QString servPath = KStandardDirs::locateLocal("services", "fakeservice.desktop");
QFile::remove(servPath);
QSignalSpy spy(KSycoca::self(), SIGNAL(databaseChanged(QStringList)));
Q_ASSERT(spy.isValid());
kDebug() << "executing kbuildsycoca (2)";
runKBuildSycoca();
Q_ASSERT(spy[0][0].toStringList().contains("services"));
Q_ASSERT(fakeService); // the whole point of refcounting is that this KService instance is still valid.
Q_ASSERT(!QFile::exists(servPath));
}
void KSycocaThreadTest::createFakeService()
{
const QString fakeService = KStandardDirs::locateLocal("services", "fakeservice.desktop");
KDesktopFile file(fakeService);
KConfigGroup group = file.desktopGroup();
group.writeEntry("Name", "FakeService");
group.writeEntry("Type", "Service");
group.writeEntry("X-KDE-Library", "fakeservice");
group.writeEntry("X-KDE-Protocols", "http,ftp");
group.writeEntry("ServiceTypes", "KTextEditor/Plugin");
group.writeEntry("MimeType", "text/plain;");
}
int main(int argc, char** argv)
{
setenv("KDEHOME", QFile::encodeName(QDir::homePath() + QString::fromLatin1("/.kde-unit-test")), 1);
setenv("XDG_DATA_HOME", QFile::encodeName(QDir::homePath() + QString::fromLatin1("/.kde-unit-test/xdg/local")), 1);
QCoreApplication app(argc, argv);
KComponentData cData("ksycocathreadtest");
KSycocaThreadTest mainObj;
mainObj.launch();
return app.exec();
}
#include "ksycocathreadtest.moc"