qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,27 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qlockfile Test:
#####################################################################
qt_internal_add_test(tst_qlockfile
SOURCES
tst_qlockfile.cpp
LIBRARIES
Qt::Concurrent
Qt::CorePrivate
)
## Scopes:
#####################################################################
qt_internal_extend_target(tst_qlockfile CONDITION WIN32
LIBRARIES
advapi32
)
add_subdirectory(qlockfiletesthelper)
if(QT_FEATURE_process AND NOT ANDROID)
add_dependencies(tst_qlockfile qlockfile_test_helper)
endif()

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## qlockfile_test_helper Binary:
#####################################################################
qt_internal_add_test_helper(qlockfile_test_helper
OVERRIDE_OUTPUT_DIRECTORY
OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
SOURCES
qlockfile_test_helper.cpp
)

View File

@ -0,0 +1,46 @@
// Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QDebug>
#include <QCoreApplication>
#include <QLockFile>
#include <QThread>
#ifdef Q_OS_UNIX
# include <unistd.h>
#else
# include <stdlib.h>
#endif
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
if (argc <= 1)
return -1;
const QString lockName = QString::fromLocal8Bit(argv[1]);
QString option;
if (argc > 2)
option = QString::fromLocal8Bit(argv[2]);
if (option == "-uncleanexit") {
QLockFile lockFile(lockName);
lockFile.lock();
// exit on purpose, so that the lock remains!
_exit(0);
} else if (option == "-busy") {
QLockFile lockFile(lockName);
lockFile.lock();
QThread::msleep(500);
return 0;
} else {
QLockFile lockFile(lockName);
if (lockFile.isLocked()) // cannot happen, before calling lock or tryLock
return QLockFile::UnknownError;
lockFile.tryLock();
return lockFile.error();
}
}

View File

@ -0,0 +1,660 @@
// Copyright (C) 2021 The Qt Company Ltd.
// Copyright (C) 2013 David Faure <faure+bluesystems@kde.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QtConcurrentRun>
#if QT_CONFIG(process)
#include <QProcess>
#endif
#include <QSemaphore>
#include <QFutureSynchronizer>
#include <qlockfile.h>
#include <qtemporarydir.h>
#include <qsysinfo.h>
#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
#include <unistd.h>
#include <sys/time.h>
#elif defined(Q_OS_WIN)
# include <qt_windows.h>
# include <QOperatingSystemVersion>
#endif
#include <private/qlockfile_p.h> // for getLockFileHandle()
class tst_QLockFile : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void lockUnlock();
void lockOutOtherProcess();
void lockOutOtherThread();
void raceWithOtherThread();
void waitForLock_data();
void waitForLock();
void staleLockFromCrashedProcess_data();
void staleLockFromCrashedProcess();
void staleLockFromCrashedProcessReusedPid();
void staleShortLockFromBusyProcess();
void staleLongLockFromBusyProcess();
void staleLockRace();
void noPermissions();
void noPermissionsWindows();
void corruptedLockFile();
void corruptedLockFileInTheFuture();
void hostnameChange();
void differentMachines();
void reboot();
private:
static bool overwriteLineInLockFile(QFile &f, int line, const QString &newLine);
static bool overwritePidInLockFile(const QString &filePath, qint64 pid);
public:
QString m_helperApp;
QTemporaryDir dir;
};
void tst_QLockFile::initTestCase()
{
#ifdef Q_OS_ANDROID
QSKIP("This test requires deploying and running external console applications");
#elif !QT_CONFIG(process)
QSKIP("This test requires QProcess support");
#else
QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
// chdir to our testdata path and execute helper apps relative to that.
QString testdata_dir = QFileInfo(QFINDTESTDATA("qlockfiletesthelper")).absolutePath();
QVERIFY2(QDir::setCurrent(testdata_dir), qPrintable("Could not chdir to " + testdata_dir));
m_helperApp = "qlockfiletesthelper/qlockfile_test_helper";
#endif // QT_CONFIG(process)
}
void tst_QLockFile::lockUnlock()
{
const QString fileName = dir.path() + "/lock1";
QVERIFY(!QFile(fileName).exists());
QLockFile lockFile(fileName);
QCOMPARE(lockFile.fileName(), fileName);
QVERIFY(lockFile.lock());
QVERIFY(lockFile.isLocked());
QCOMPARE(int(lockFile.error()), int(QLockFile::NoError));
QVERIFY(QFile::exists(fileName));
// Recursive locking is not allowed
// (can't test lock() here, it would wait forever)
QVERIFY(!lockFile.tryLock());
QCOMPARE(int(lockFile.error()), int(QLockFile::LockFailedError));
qint64 pid;
QString hostname, appname;
QVERIFY(lockFile.getLockInfo(&pid, &hostname, &appname));
QCOMPARE(pid, QCoreApplication::applicationPid());
QCOMPARE(appname, qAppName());
QVERIFY(!lockFile.tryLock(200));
QCOMPARE(int(lockFile.error()), int(QLockFile::LockFailedError));
// Unlock deletes the lock file
lockFile.unlock();
QCOMPARE(int(lockFile.error()), int(QLockFile::NoError));
QVERIFY(!lockFile.isLocked());
QVERIFY(!QFile::exists(fileName));
}
void tst_QLockFile::lockOutOtherProcess()
{
#if !QT_CONFIG(process)
QSKIP("This test requires QProcess support");
#else
// Lock
const QString fileName = dir.path() + "/lockOtherProcess";
QLockFile lockFile(fileName);
QVERIFY(lockFile.lock());
// Other process can't acquire lock
QProcess proc;
proc.start(m_helperApp, QStringList() << fileName);
QVERIFY2(proc.waitForStarted(), qPrintable(proc.errorString()));
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), int(QLockFile::LockFailedError));
// Unlock
lockFile.unlock();
QVERIFY(!QFile::exists(fileName));
// Other process can now acquire lock
int ret = QProcess::execute(m_helperApp, QStringList() << fileName);
QCOMPARE(ret, int(QLockFile::NoError));
// Lock doesn't survive process though (on clean exit)
QVERIFY(!QFile::exists(fileName));
#endif // QT_CONFIG(process)
}
static QLockFile::LockError tryLockFromThread(const QString &fileName)
{
QLockFile lockInThread(fileName);
lockInThread.tryLock();
return lockInThread.error();
}
void tst_QLockFile::lockOutOtherThread()
{
const QString fileName = dir.path() + "/lockOtherThread";
QLockFile lockFile(fileName);
QVERIFY(lockFile.lock());
// Other thread can't acquire lock
auto ret = QtConcurrent::run(tryLockFromThread, fileName);
QCOMPARE(ret.result(), QLockFile::LockFailedError);
lockFile.unlock();
// Now other thread can acquire lock
auto ret2 = QtConcurrent::run(tryLockFromThread, fileName);
QCOMPARE(ret2.result(), QLockFile::NoError);
}
static QLockFile::LockError lockFromThread(const QString &fileName)
{
QLockFile lockInThread(fileName);
lockInThread.lock();
return lockInThread.error();
}
// QTBUG-38853, best way to trigger it was to add a QThread::sleep(1) in QLockFilePrivate::getLockInfo() after the first readLine.
// Then (on Windows), the QFile::remove() in unlock() (called by the first thread who got the lock, in the destructor)
// would fail due to the existing reader on the file. Fixed by checking the return value of QFile::remove() in unlock().
void tst_QLockFile::raceWithOtherThread()
{
const QString fileName = dir.path() + "/raceWithOtherThread";
auto ret = QtConcurrent::run(lockFromThread, fileName);
auto ret2 = QtConcurrent::run(lockFromThread, fileName);
QCOMPARE(ret.result(), QLockFile::NoError);
QCOMPARE(ret2.result(), QLockFile::NoError);
}
static bool lockFromThreadAndWait(const QString &fileName, int sleepMs, QSemaphore *semThreadReady, QSemaphore *semMainThreadDone)
{
QLockFile lockFile(fileName);
if (!lockFile.lock()) {
qWarning() << "Locking failed" << lockFile.error();
return false;
}
semThreadReady->release();
QThread::msleep(sleepMs);
semMainThreadDone->acquire();
lockFile.unlock();
return true;
}
void tst_QLockFile::waitForLock_data()
{
QTest::addColumn<int>("testNumber");
QTest::addColumn<int>("threadSleepMs");
QTest::addColumn<bool>("releaseEarly");
QTest::addColumn<int>("tryLockTimeout");
QTest::addColumn<bool>("expectedResult");
int tn = 0; // test number
QTest::newRow("wait_forever_succeeds") << ++tn << 500 << true << -1 << true;
QTest::newRow("wait_longer_succeeds") << ++tn << 500 << true << 1000 << true;
QTest::newRow("wait_zero_fails") << ++tn << 500 << false << 0 << false;
QTest::newRow("wait_not_enough_fails") << ++tn << 500 << false << 100 << false;
}
void tst_QLockFile::waitForLock()
{
QFETCH(int, testNumber);
QFETCH(int, threadSleepMs);
QFETCH(bool, releaseEarly);
QFETCH(int, tryLockTimeout);
QFETCH(bool, expectedResult);
const QString fileName = dir.path() + "/waitForLock" + QString::number(testNumber);
QLockFile lockFile(fileName);
QSemaphore semThreadReady, semMainThreadDone;
// Lock file from a thread
auto ret = QtConcurrent::run(lockFromThreadAndWait, fileName, threadSleepMs, &semThreadReady, &semMainThreadDone);
semThreadReady.acquire();
if (releaseEarly) // let the thread release the lock after threadSleepMs
semMainThreadDone.release();
QCOMPARE(lockFile.tryLock(tryLockTimeout), expectedResult);
if (expectedResult)
QCOMPARE(int(lockFile.error()), int(QLockFile::NoError));
else
QCOMPARE(int(lockFile.error()), int(QLockFile::LockFailedError));
if (!releaseEarly) // only let the thread release the lock now
semMainThreadDone.release();
QVERIFY(ret.result()); // waits for the thread to finish
}
void tst_QLockFile::staleLockFromCrashedProcess_data()
{
QTest::addColumn<int>("staleLockTime");
// Test both use cases for QLockFile, should make no difference here.
QTest::newRow("short") << 30000;
QTest::newRow("long") << 0;
}
void tst_QLockFile::staleLockFromCrashedProcess()
{
#if !QT_CONFIG(process)
QSKIP("This test requires QProcess support");
#else
QFETCH(int, staleLockTime);
const QString fileName = dir.path() + "/staleLockFromCrashedProcess";
int ret = QProcess::execute(m_helperApp, QStringList() << fileName << "-uncleanexit");
QCOMPARE(ret, int(QLockFile::NoError));
QTRY_VERIFY(QFile::exists(fileName));
QLockFile secondLock(fileName);
secondLock.setStaleLockTime(staleLockTime);
// tryLock detects and removes the stale lock (since the PID is dead)
#ifdef Q_OS_WIN
// It can take a bit of time on Windows, though.
QVERIFY(secondLock.tryLock(30000));
#else
QVERIFY(secondLock.tryLock());
#endif
QCOMPARE(int(secondLock.error()), int(QLockFile::NoError));
#endif // QT_CONFIG(process)
}
void tst_QLockFile::staleLockFromCrashedProcessReusedPid()
{
#if !QT_CONFIG(process)
QSKIP("This test requires QProcess support");
#elif defined(QT_PLATFORM_UIKIT)
QSKIP("We cannot retrieve information about other processes on this platform.");
#else
const QString fileName = dir.path() + "/staleLockFromCrashedProcessReusedPid";
int ret = QProcess::execute(m_helperApp, QStringList() << fileName << "-uncleanexit");
QCOMPARE(ret, int(QLockFile::NoError));
QVERIFY(QFile::exists(fileName));
QVERIFY(overwritePidInLockFile(fileName, QCoreApplication::applicationPid()));
QLockFile secondLock(fileName);
qint64 pid = 0;
QVERIFY(secondLock.getLockInfo(&pid, 0, 0));
QCOMPARE(pid, QCoreApplication::applicationPid());
secondLock.setStaleLockTime(0);
QVERIFY(secondLock.tryLock());
QCOMPARE(int(secondLock.error()), int(QLockFile::NoError));
#endif // QT_CONFIG(process)
}
void tst_QLockFile::staleShortLockFromBusyProcess()
{
#if !QT_CONFIG(process)
QSKIP("This test requires QProcess support");
#else
const QString fileName = dir.path() + "/staleLockFromBusyProcess";
QProcess proc;
proc.start(m_helperApp, QStringList() << fileName << "-busy");
QVERIFY2(proc.waitForStarted(), qPrintable(proc.errorString()));
QTRY_VERIFY(QFile::exists(fileName));
QLockFile secondLock(fileName);
QVERIFY(!secondLock.tryLock()); // held by other process
QCOMPARE(int(secondLock.error()), int(QLockFile::LockFailedError));
qint64 pid;
QString hostname, appname;
QTRY_VERIFY(secondLock.getLockInfo(&pid, &hostname, &appname));
#ifdef Q_OS_UNIX
QCOMPARE(pid, proc.processId());
#endif
secondLock.setStaleLockTime(100);
QTest::qSleep(100); // make the lock stale
// We can't "steal" (delete+recreate) a lock file from a running process
// until the file descriptor is closed.
QVERIFY(!secondLock.tryLock());
proc.waitForFinished();
QVERIFY(secondLock.tryLock());
#endif // QT_CONFIG(process)
}
void tst_QLockFile::staleLongLockFromBusyProcess()
{
#if !QT_CONFIG(process)
QSKIP("This test requires QProcess support");
#else
const QString fileName = dir.path() + "/staleLockFromBusyProcess";
QProcess proc;
proc.start(m_helperApp, QStringList() << fileName << "-busy");
QVERIFY2(proc.waitForStarted(), qPrintable(proc.errorString()));
QTRY_VERIFY(QFile::exists(fileName));
QLockFile secondLock(fileName);
secondLock.setStaleLockTime(0);
QVERIFY(!secondLock.tryLock(100)); // never stale
QCOMPARE(int(secondLock.error()), int(QLockFile::LockFailedError));
qint64 pid;
QTRY_VERIFY(secondLock.getLockInfo(&pid, NULL, NULL));
QVERIFY(pid > 0);
// As long as the other process is running, we can't remove the lock file
QVERIFY(!secondLock.removeStaleLockFile());
proc.waitForFinished();
#endif // QT_CONFIG(process)
}
static QString tryStaleLockFromThread(const QString &fileName)
{
QLockFile lockInThread(fileName + ".lock");
lockInThread.setStaleLockTime(1000);
if (!lockInThread.lock())
return "Error locking: " + QString::number(lockInThread.error());
// The concurrent use of the file below (write, read, delete) is protected by the lock file above.
// (provided that it doesn't become stale due to this operation taking too long)
QFile theFile(fileName);
if (!theFile.open(QIODevice::WriteOnly))
return "Couldn't open for write";
theFile.write("Hello world");
theFile.flush();
theFile.close();
QFile reader(fileName);
if (!reader.open(QIODevice::ReadOnly))
return "Couldn't open for read";
const QByteArray read = reader.readAll();
if (read != "Hello world")
return "File didn't have the expected contents:" + read;
reader.remove();
return QString();
}
void tst_QLockFile::staleLockRace()
{
#if !QT_CONFIG(process)
QSKIP("This test requires QProcess support");
#else
// Multiple threads notice a stale lock at the same time
// Only one thread should delete it, otherwise a race will ensue
const QString fileName = dir.path() + "/sharedFile";
const QString lockName = fileName + ".lock";
int ret = QProcess::execute(m_helperApp, QStringList() << lockName << "-uncleanexit");
QCOMPARE(ret, int(QLockFile::NoError));
QTRY_VERIFY(QFile::exists(lockName));
QThreadPool::globalInstance()->setMaxThreadCount(10);
QFutureSynchronizer<QString> synchronizer;
for (int i = 0; i < 8; ++i)
synchronizer.addFuture(QtConcurrent::run(tryStaleLockFromThread, fileName));
synchronizer.waitForFinished();
foreach (const QFuture<QString> &future, synchronizer.futures())
QVERIFY2(future.result().isEmpty(), qPrintable(future.result()));
#endif // QT_CONFIG(process)
}
void tst_QLockFile::noPermissions()
{
#if defined(Q_OS_WIN)
// A readonly directory still allows us to create files, on Windows.
QSKIP("No permission testing on Windows");
#elif defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
if (::geteuid() == 0)
QSKIP("Test is not applicable with root privileges");
#endif
// Restore permissions so that the QTemporaryDir cleanup can happen
class PermissionRestorer
{
QString m_path;
public:
PermissionRestorer(const QString& path)
: m_path(path)
{}
~PermissionRestorer()
{
QFile file(m_path);
file.setPermissions(QFile::Permissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner));
}
};
const QString fileName = dir.path() + "/staleLock";
QFile dirAsFile(dir.path()); // I have to use QFile to change a dir's permissions...
QVERIFY2(dirAsFile.setPermissions(QFile::Permissions{}), qPrintable(dir.path())); // no permissions
PermissionRestorer permissionRestorer(dir.path());
QLockFile lockFile(fileName);
QVERIFY(!lockFile.lock());
QCOMPARE(int(lockFile.error()), int(QLockFile::PermissionError));
}
enum ProcessProperty {
ElevatedProcess = 0x1,
VirtualStore = 0x2
};
Q_DECLARE_FLAGS(ProcessProperties, ProcessProperty)
Q_DECLARE_OPERATORS_FOR_FLAGS(ProcessProperties)
static inline ProcessProperties processProperties()
{
ProcessProperties result;
#if defined(Q_OS_WIN)
HANDLE processToken = NULL;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &processToken)) {
DWORD elevation; // struct containing a DWORD, not present in some MinGW headers.
DWORD cbSize = sizeof(elevation);
if (GetTokenInformation(processToken, TokenElevation, &elevation, cbSize, &cbSize)
&& elevation) {
result |= ElevatedProcess;
}
// Check for UAC virtualization (compatibility mode for old software
// allowing it to write to system folders by mirroring them under
// "\Users\...\AppData\Local\VirtualStore\", which is typically the case
// for MinGW).
DWORD virtualStoreEnabled = 0;
cbSize = sizeof(virtualStoreEnabled);
if (GetTokenInformation(processToken, TokenVirtualizationEnabled, &virtualStoreEnabled, cbSize, &cbSize)
&& virtualStoreEnabled) {
result |= VirtualStore;
}
CloseHandle(processToken);
}
#endif
return result;
}
void tst_QLockFile::noPermissionsWindows()
{
// Windows: Do the permissions test in a system directory in which
// files cannot be created.
#if !defined(Q_OS_WIN)
QSKIP("This test is for desktop Windows only");
#endif
#ifdef Q_OS_WIN
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows7)
QSKIP("This test requires at least Windows 7");
#endif
if (const int p = processProperties()) {
const QByteArray message = "This test cannot be run (properties=0x"
+ QByteArray::number(p, 16) + ')';
QSKIP(message.constData());
}
const QString fileName = QFile::decodeName(qgetenv("ProgramFiles"))
+ QLatin1Char('/') + QCoreApplication::applicationName()
+ QDateTime::currentDateTime().toString(QStringLiteral("yyMMddhhmm"));
QLockFile lockFile(fileName);
QVERIFY(!lockFile.lock());
QCOMPARE(int(lockFile.error()), int(QLockFile::PermissionError));
}
void tst_QLockFile::corruptedLockFile()
{
const QString fileName = dir.path() + "/corruptedLockFile";
{
// Create a empty file. Typically the result of a computer crash or hard disk full.
QFile file(fileName);
QVERIFY(file.open(QFile::WriteOnly));
}
QLockFile secondLock(fileName);
secondLock.setStaleLockTime(100);
QVERIFY(secondLock.tryLock(10000));
QCOMPARE(int(secondLock.error()), int(QLockFile::NoError));
}
void tst_QLockFile::corruptedLockFileInTheFuture()
{
#if !defined(Q_OS_UNIX)
QSKIP("This tests needs utimes");
#else
// This test is the same as the previous one, but the corruption was so there is a corrupted
// .rmlock whose timestamp is in the future
const QString fileName = dir.path() + "/corruptedLockFile.rmlock";
{
QFile file(fileName);
QVERIFY(file.open(QFile::WriteOnly));
}
struct timeval times[2];
gettimeofday(times, 0);
times[1].tv_sec = (times[0].tv_sec += 600);
times[1].tv_usec = times[0].tv_usec;
utimes(fileName.toLocal8Bit(), times);
QTest::ignoreMessage(QtInfoMsg, "QLockFile: Lock file '" + fileName.toUtf8() + "' has a modification time in the future");
corruptedLockFile();
#endif
}
void tst_QLockFile::hostnameChange()
{
const QByteArray hostid = QSysInfo::machineUniqueId();
if (hostid.isEmpty())
QSKIP("Could not get a unique host ID on this machine");
QString lockFile = dir.path() + "/hostnameChangeLock";
QLockFile lock1(lockFile);
QVERIFY(lock1.lock());
{
// now modify it
QFile f;
QVERIFY(f.open(QLockFilePrivate::getLockFileHandle(&lock1),
QIODevice::ReadWrite | QIODevice::Text,
QFile::DontCloseHandle));
QVERIFY(overwriteLineInLockFile(f, 3, "this is not a hostname"));
}
{
// we should fail to lock
QLockFile lock2(lockFile);
QVERIFY(!lock2.tryLock(1000));
}
}
void tst_QLockFile::differentMachines()
{
const QByteArray hostid = QSysInfo::machineUniqueId();
if (hostid.isEmpty())
QSKIP("Could not get a unique host ID on this machine");
QString lockFile = dir.path() + "/differentMachinesLock";
QLockFile lock1(lockFile);
QVERIFY(lock1.lock());
{
// now modify it
QFile f;
QVERIFY(f.open(QLockFilePrivate::getLockFileHandle(&lock1),
QIODevice::ReadWrite | QIODevice::Text,
QFile::DontCloseHandle));
QVERIFY(overwriteLineInLockFile(f, 1, QT_STRINGIFY(INT_MAX)));
QVERIFY(overwriteLineInLockFile(f, 4, "this is not a UUID"));
}
{
// we should fail to lock
QLockFile lock2(lockFile);
QVERIFY(!lock2.tryLock(1000));
}
}
void tst_QLockFile::reboot()
{
const QByteArray bootid = QSysInfo::bootUniqueId();
if (bootid.isEmpty())
QSKIP("Could not get a unique boot ID on this machine");
// create a lock so we can get its contents
QString lockFile = dir.path() + "/rebootLock";
QLockFile lock1(lockFile);
QVERIFY(lock1.lock());
QFile f(lockFile);
QVERIFY(f.open(QFile::ReadOnly | QFile::Text));
auto lines = f.readAll().split('\n');
f.close();
lock1.unlock();
// now recreate the file simulating a reboot
QVERIFY(f.open(QFile::WriteOnly | QFile::Text));
lines[4] = "this is not a UUID";
f.write(lines.join('\n'));
f.close();
// we should succeed in locking
QVERIFY(lock1.tryLock(0));
}
bool tst_QLockFile::overwritePidInLockFile(const QString &filePath, qint64 pid)
{
QFile f(filePath);
if (!f.open(QFile::ReadWrite | QFile::Text)) {
qErrnoWarning("Cannot open %s", qPrintable(filePath));
return false;
}
return overwriteLineInLockFile(f, 1, QString::number(pid));
}
bool tst_QLockFile::overwriteLineInLockFile(QFile &f, int line, const QString &newLine)
{
f.seek(0);
QByteArray buf = f.readAll();
QStringList lines = QString::fromUtf8(buf).split('\n');
if (lines.size() < 3 && lines.size() < line - 1) {
qWarning("Unexpected lockfile content.");
return false;
}
lines[line - 1] = newLine;
f.seek(0);
buf = lines.join('\n').toUtf8();
f.resize(buf.size());
return f.write(buf) == buf.size();
}
struct LockFileUsageInGlobalDtor
{
~LockFileUsageInGlobalDtor() {
QLockFile lockFile(QDir::currentPath() + "/lastlock");
QVERIFY(lockFile.lock());
QVERIFY(lockFile.isLocked());
}
};
LockFileUsageInGlobalDtor s_instance;
QTEST_MAIN(tst_QLockFile)
#include "tst_qlockfile.moc"