move QSharedMemory autotest from qtscript to qtbase
As the script dependency for that autotest is not really needed it should be moved to qtbase. Task-number: QTBUG-27706 Change-Id: Ieda8b2182a20a77f53a0be9878e82e3236c79c2b Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com> Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
This commit is contained in:
parent
9d0afa2ab0
commit
dd904c3c48
@ -12,6 +12,7 @@ SUBDIRS=\
|
||||
qmimedata \
|
||||
qobject \
|
||||
qpointer \
|
||||
qsharedmemory \
|
||||
qsignalmapper \
|
||||
qsocketnotifier \
|
||||
qtimer \
|
||||
@ -20,7 +21,8 @@ SUBDIRS=\
|
||||
qwineventnotifier
|
||||
|
||||
!contains(QT_CONFIG, private_tests): SUBDIRS -= \
|
||||
qsocketnotifier
|
||||
qsocketnotifier \
|
||||
qsharedmemory
|
||||
|
||||
# This test is only applicable on Windows
|
||||
!win32*:SUBDIRS -= qwineventnotifier
|
||||
|
@ -0,0 +1,3 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = sharedmemoryhelper test
|
@ -0,0 +1,208 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QSharedMemory>
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
#include <QTest>
|
||||
|
||||
void set(QSharedMemory &sm, int pos, QChar value)
|
||||
{
|
||||
((char*)sm.data())[pos] = value.toLatin1();
|
||||
}
|
||||
|
||||
QChar get(QSharedMemory &sm, int i)
|
||||
{
|
||||
return QChar::fromLatin1(((char*)sm.data())[i]);
|
||||
}
|
||||
|
||||
int readonly_segfault()
|
||||
{
|
||||
QSharedMemory sharedMemory;
|
||||
sharedMemory.setKey("readonly_segfault");
|
||||
sharedMemory.create(1024, QSharedMemory::ReadOnly);
|
||||
sharedMemory.lock();
|
||||
set(sharedMemory, 0, 'a');
|
||||
sharedMemory.unlock();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int producer()
|
||||
{
|
||||
QSharedMemory producer;
|
||||
producer.setKey("market");
|
||||
|
||||
int size = 1024;
|
||||
if (!producer.create(size)) {
|
||||
if (producer.error() == QSharedMemory::AlreadyExists) {
|
||||
if (!producer.attach()) {
|
||||
qWarning() << "Could not attach to" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Could not create" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
//qDebug("producer created and attached");
|
||||
|
||||
if (!producer.lock()) {
|
||||
qWarning() << "Could not lock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
set(producer, 0, 'Q');
|
||||
if (!producer.unlock()) {
|
||||
qWarning() << "Could not lock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
while (i < 5) {
|
||||
if (!producer.lock()) {
|
||||
qWarning() << "Could not lock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (get(producer, 0) == 'Q') {
|
||||
if (!producer.unlock()) {
|
||||
qWarning() << "Could not unlock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
QTest::qSleep(1);
|
||||
continue;
|
||||
}
|
||||
//qDebug() << "producer:" << i);
|
||||
++i;
|
||||
set(producer, 0, 'Q');
|
||||
if (!producer.unlock()) {
|
||||
qWarning() << "Could not unlock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
QTest::qSleep(1);
|
||||
}
|
||||
if (!producer.lock()) {
|
||||
qWarning() << "Could not lock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
set(producer, 0, 'E');
|
||||
if (!producer.unlock()) {
|
||||
qWarning() << "Could not unlock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
//qDebug("producer done");
|
||||
|
||||
// Sleep for a bit to let all consumers start, otherwise they will get stuck in the attach loop,
|
||||
// because at least in Symbian the shared memory will be destroyed if there are no active handles to it.
|
||||
QTest::qSleep(3000);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int consumer()
|
||||
{
|
||||
QSharedMemory consumer;
|
||||
consumer.setKey("market");
|
||||
|
||||
//qDebug("consumer starting");
|
||||
int tries = 0;
|
||||
while (!consumer.attach()) {
|
||||
if (tries == 5000) {
|
||||
qWarning() << "consumer exiting, waiting too long";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
++tries;
|
||||
QTest::qSleep(1);
|
||||
}
|
||||
//qDebug("consumer attached");
|
||||
|
||||
|
||||
int i = 0;
|
||||
while (true) {
|
||||
if (!consumer.lock()) {
|
||||
qWarning() << "Could not lock" << consumer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (get(consumer, 0) == 'Q') {
|
||||
set(consumer, 0, ++i);
|
||||
//qDebug() << "consumer sets" << i;
|
||||
}
|
||||
if (get(consumer, 0) == 'E') {
|
||||
if (!consumer.unlock()) {
|
||||
qWarning() << "Could not unlock" << consumer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!consumer.unlock()) {
|
||||
qWarning() << "Could not unlock" << consumer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
QTest::qSleep(10);
|
||||
}
|
||||
|
||||
//qDebug("consumer detaching");
|
||||
if (!consumer.detach()) {
|
||||
qWarning() << "Could not detach" << consumer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
QStringList arguments = app.arguments();
|
||||
if (app.arguments().count() != 2) {
|
||||
qWarning("Please call the helper with the function to call as argument");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
QString function = arguments.at(1);
|
||||
if (function == QLatin1String("readonly_segfault"))
|
||||
return readonly_segfault();
|
||||
else if (function == QLatin1String("producer"))
|
||||
return producer();
|
||||
else if (function == QLatin1String("consumer"))
|
||||
return consumer();
|
||||
else
|
||||
qWarning() << "Unknown function" << arguments.at(1);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
QT = core testlib
|
||||
|
||||
win32: CONFIG += console
|
||||
mac:CONFIG -= app_bundle
|
||||
|
||||
SOURCES += main.cpp
|
||||
TARGET = helperbinary
|
||||
|
||||
CONFIG(debug_and_release) {
|
||||
CONFIG(debug, debug|release) {
|
||||
DESTDIR = ../debug
|
||||
} else {
|
||||
DESTDIR = ../release
|
||||
}
|
||||
} else {
|
||||
DESTDIR = ..
|
||||
}
|
18
tests/auto/corelib/kernel/qsharedmemory/test/test.pro
Normal file
18
tests/auto/corelib/kernel/qsharedmemory/test/test.pro
Normal file
@ -0,0 +1,18 @@
|
||||
CONFIG += testcase
|
||||
|
||||
QT = core-private testlib
|
||||
|
||||
mac:CONFIG -= app_bundle
|
||||
|
||||
SOURCES += tst_qsharedmemory.cpp
|
||||
TARGET = tst_qsharedmemory
|
||||
|
||||
CONFIG(debug_and_release) {
|
||||
CONFIG(debug, debug|release) {
|
||||
DESTDIR = ../debug
|
||||
} else {
|
||||
DESTDIR = ../release
|
||||
}
|
||||
} else {
|
||||
DESTDIR = ..
|
||||
}
|
@ -0,0 +1,809 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QSharedMemory>
|
||||
#include <QTest>
|
||||
#include <QThread>
|
||||
|
||||
#define EXISTING_SHARE "existing"
|
||||
#define EXISTING_SIZE 1024
|
||||
|
||||
Q_DECLARE_METATYPE(QSharedMemory::SharedMemoryError)
|
||||
Q_DECLARE_METATYPE(QSharedMemory::AccessMode)
|
||||
|
||||
class tst_QSharedMemory : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QSharedMemory();
|
||||
virtual ~tst_QSharedMemory();
|
||||
|
||||
public Q_SLOTS:
|
||||
void init();
|
||||
void initTestCase();
|
||||
void cleanup();
|
||||
|
||||
|
||||
private slots:
|
||||
// basics
|
||||
void constructor();
|
||||
void key_data();
|
||||
void key();
|
||||
void create_data();
|
||||
void create();
|
||||
void attach_data();
|
||||
void attach();
|
||||
void lock();
|
||||
|
||||
// custom edge cases
|
||||
#ifndef Q_OS_HPUX
|
||||
void removeWhileAttached();
|
||||
#endif
|
||||
void emptyMemory();
|
||||
#ifndef Q_OS_WIN
|
||||
void readOnly();
|
||||
#endif
|
||||
|
||||
// basics all together
|
||||
#ifndef Q_OS_HPUX
|
||||
void simpleProducerConsumer_data();
|
||||
void simpleProducerConsumer();
|
||||
void simpleDoubleProducerConsumer();
|
||||
#endif
|
||||
|
||||
// with threads
|
||||
void simpleThreadedProducerConsumer_data();
|
||||
void simpleThreadedProducerConsumer();
|
||||
|
||||
// with processes
|
||||
void simpleProcessProducerConsumer_data();
|
||||
void simpleProcessProducerConsumer();
|
||||
|
||||
// extreme cases
|
||||
void useTooMuchMemory();
|
||||
#if !defined(Q_OS_HPUX) && !defined(Q_OS_WINCE)
|
||||
void attachTooMuch();
|
||||
#endif
|
||||
|
||||
// unique keys
|
||||
void uniqueKey_data();
|
||||
void uniqueKey();
|
||||
|
||||
protected:
|
||||
QString helperBinary();
|
||||
int remove(const QString &key);
|
||||
|
||||
QString rememberKey(const QString &key)
|
||||
{
|
||||
if (key == EXISTING_SHARE)
|
||||
return key;
|
||||
if (!keys.contains(key)) {
|
||||
keys.append(key);
|
||||
remove(key);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
QStringList keys;
|
||||
QList<QSharedMemory*> jail;
|
||||
QSharedMemory *existingSharedMemory;
|
||||
};
|
||||
|
||||
tst_QSharedMemory::tst_QSharedMemory() :
|
||||
existingSharedMemory(0)
|
||||
{
|
||||
}
|
||||
|
||||
tst_QSharedMemory::~tst_QSharedMemory()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::initTestCase()
|
||||
{
|
||||
QVERIFY2(!helperBinary().isEmpty(), "Could not find helper binary");
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::init()
|
||||
{
|
||||
existingSharedMemory = new QSharedMemory(EXISTING_SHARE);
|
||||
if (!existingSharedMemory->create(EXISTING_SIZE)) {
|
||||
QVERIFY(existingSharedMemory->error() == QSharedMemory::AlreadyExists);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::cleanup()
|
||||
{
|
||||
delete existingSharedMemory;
|
||||
qDeleteAll(jail.begin(), jail.end());
|
||||
jail.clear();
|
||||
|
||||
keys.append(EXISTING_SHARE);
|
||||
for (int i = 0; i < keys.count(); ++i) {
|
||||
QSharedMemory sm(keys.at(i));
|
||||
if (!sm.create(1024)) {
|
||||
//if (sm.error() != QSharedMemory::KeyError)
|
||||
// qWarning() << "test cleanup: remove failed:" << keys.at(i) << sm.error() << sm.errorString();
|
||||
sm.attach();
|
||||
sm.detach();
|
||||
remove(keys.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#include <private/qsharedmemory_p.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
|
||||
QString tst_QSharedMemory::helperBinary()
|
||||
{
|
||||
QString binary = QStringLiteral("helperbinary");
|
||||
#ifdef Q_OS_WIN
|
||||
binary += QStringLiteral(".exe");
|
||||
#endif
|
||||
return QFINDTESTDATA(binary);
|
||||
}
|
||||
|
||||
int tst_QSharedMemory::remove(const QString &key)
|
||||
{
|
||||
#ifndef Q_OS_WIN
|
||||
// On unix the shared memory might exists from a previously failed test
|
||||
// or segfault, remove it it does
|
||||
if (key.isEmpty())
|
||||
return -1;
|
||||
|
||||
// ftok requires that an actual file exists somewhere
|
||||
QString fileName = QSharedMemoryPrivate::makePlatformSafeKey(key);
|
||||
if (!QFile::exists(fileName)) {
|
||||
//qDebug() << "exits failed";
|
||||
return -2;
|
||||
}
|
||||
|
||||
int unix_key = ftok(fileName.toLatin1().constData(), 'Q');
|
||||
if (-1 == unix_key) {
|
||||
qDebug() << "ftok failed";
|
||||
return -3;
|
||||
}
|
||||
|
||||
int id = shmget(unix_key, 0, 0660);
|
||||
if (-1 == id) {
|
||||
qDebug() << "shmget failed";
|
||||
return -4;
|
||||
}
|
||||
|
||||
struct shmid_ds shmid_ds;
|
||||
if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) {
|
||||
qDebug() << "shmctl failed";
|
||||
return -5;
|
||||
}
|
||||
return QFile::remove(fileName);
|
||||
#else
|
||||
Q_UNUSED(key);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Tests the default values
|
||||
*/
|
||||
void tst_QSharedMemory::constructor()
|
||||
{
|
||||
QSharedMemory sm;
|
||||
QCOMPARE(sm.key(), QString());
|
||||
QVERIFY(!sm.isAttached());
|
||||
QVERIFY(sm.data() == 0);
|
||||
QCOMPARE(sm.size(), 0);
|
||||
QCOMPARE(sm.error(), QSharedMemory::NoError);
|
||||
QVERIFY(sm.errorString() == QString());
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::key_data()
|
||||
{
|
||||
QTest::addColumn<QString>("constructorKey");
|
||||
QTest::addColumn<QString>("setKey");
|
||||
QTest::addColumn<QString>("setNativeKey");
|
||||
|
||||
QTest::newRow("null, null, null") << QString() << QString() << QString();
|
||||
QTest::newRow("one, null, null") << QString("one") << QString() << QString();
|
||||
QTest::newRow("null, one, null") << QString() << QString("one") << QString();
|
||||
QTest::newRow("null, null, one") << QString() << QString() << QString("one");
|
||||
QTest::newRow("one, two, null") << QString("one") << QString("two") << QString();
|
||||
QTest::newRow("one, null, two") << QString("one") << QString() << QString("two");
|
||||
QTest::newRow("null, one, two") << QString() << QString("one") << QString("two");
|
||||
QTest::newRow("one, two, three") << QString("one") << QString("two") << QString("three");
|
||||
QTest::newRow("invalid") << QString("o/e") << QString("t/o") << QString("|x");
|
||||
}
|
||||
|
||||
/*!
|
||||
Basic key testing
|
||||
*/
|
||||
void tst_QSharedMemory::key()
|
||||
{
|
||||
QFETCH(QString, constructorKey);
|
||||
QFETCH(QString, setKey);
|
||||
QFETCH(QString, setNativeKey);
|
||||
|
||||
QSharedMemory sm(constructorKey);
|
||||
QCOMPARE(sm.key(), constructorKey);
|
||||
QCOMPARE(sm.nativeKey().isEmpty(), constructorKey.isEmpty());
|
||||
sm.setKey(setKey);
|
||||
QCOMPARE(sm.key(), setKey);
|
||||
QCOMPARE(sm.nativeKey().isEmpty(), setKey.isEmpty());
|
||||
sm.setNativeKey(setNativeKey);
|
||||
QVERIFY(sm.key().isNull());
|
||||
QCOMPARE(sm.nativeKey(), setNativeKey);
|
||||
QCOMPARE(sm.isAttached(), false);
|
||||
|
||||
QCOMPARE(sm.error(), QSharedMemory::NoError);
|
||||
QVERIFY(sm.errorString() == QString());
|
||||
QVERIFY(sm.data() == 0);
|
||||
QCOMPARE(sm.size(), 0);
|
||||
|
||||
QCOMPARE(sm.detach(), false);
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::create_data()
|
||||
{
|
||||
QTest::addColumn<QString>("key");
|
||||
QTest::addColumn<int>("size");
|
||||
QTest::addColumn<bool>("canCreate");
|
||||
QTest::addColumn<QSharedMemory::SharedMemoryError>("error");
|
||||
|
||||
QTest::newRow("null key") << QString() << 1024
|
||||
<< false << QSharedMemory::KeyError;
|
||||
QTest::newRow("-1 size") << QString("negsize") << -1
|
||||
<< false << QSharedMemory::InvalidSize;
|
||||
QTest::newRow("nor size") << QString("norsize") << 1024
|
||||
<< true << QSharedMemory::NoError;
|
||||
QTest::newRow("already exists") << QString(EXISTING_SHARE) << EXISTING_SIZE
|
||||
<< false << QSharedMemory::AlreadyExists;
|
||||
}
|
||||
|
||||
/*!
|
||||
Basic create testing
|
||||
*/
|
||||
void tst_QSharedMemory::create()
|
||||
{
|
||||
QFETCH(QString, key);
|
||||
QFETCH(int, size);
|
||||
QFETCH(bool, canCreate);
|
||||
QFETCH(QSharedMemory::SharedMemoryError, error);
|
||||
|
||||
QSharedMemory sm(rememberKey(key));
|
||||
QCOMPARE(sm.create(size), canCreate);
|
||||
if (sm.error() != error)
|
||||
qDebug() << sm.errorString();
|
||||
QCOMPARE(sm.key(), key);
|
||||
if (canCreate) {
|
||||
QVERIFY(sm.errorString() == QString());
|
||||
QVERIFY(sm.data() != 0);
|
||||
QVERIFY(sm.size() != 0);
|
||||
} else {
|
||||
QVERIFY(sm.data() == 0);
|
||||
QVERIFY(sm.errorString() != QString());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::attach_data()
|
||||
{
|
||||
QTest::addColumn<QString>("key");
|
||||
QTest::addColumn<bool>("exists");
|
||||
QTest::addColumn<QSharedMemory::SharedMemoryError>("error");
|
||||
|
||||
QTest::newRow("null key") << QString() << false << QSharedMemory::KeyError;
|
||||
QTest::newRow("doesn't exists") << QString("doesntexists") << false << QSharedMemory::NotFound;
|
||||
|
||||
// HPUX doesn't allow for multiple attaches per process.
|
||||
#ifndef Q_OS_HPUX
|
||||
QTest::newRow("already exists") << QString(EXISTING_SHARE) << true << QSharedMemory::NoError;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Basic attach/detach testing
|
||||
*/
|
||||
void tst_QSharedMemory::attach()
|
||||
{
|
||||
QFETCH(QString, key);
|
||||
QFETCH(bool, exists);
|
||||
QFETCH(QSharedMemory::SharedMemoryError, error);
|
||||
|
||||
QSharedMemory sm(key);
|
||||
QCOMPARE(sm.attach(), exists);
|
||||
QCOMPARE(sm.isAttached(), exists);
|
||||
QCOMPARE(sm.error(), error);
|
||||
QCOMPARE(sm.key(), key);
|
||||
if (exists) {
|
||||
QVERIFY(sm.data() != 0);
|
||||
QVERIFY(sm.size() != 0);
|
||||
QCOMPARE(sm.errorString(), QString());
|
||||
QVERIFY(sm.detach());
|
||||
// Make sure detach doesn't screw up something and we can't re-attach.
|
||||
QVERIFY(sm.attach());
|
||||
QVERIFY(sm.data() != 0);
|
||||
QVERIFY(sm.size() != 0);
|
||||
QVERIFY(sm.detach());
|
||||
QCOMPARE(sm.size(), 0);
|
||||
QVERIFY(sm.data() == 0);
|
||||
} else {
|
||||
QVERIFY(sm.data() == 0);
|
||||
QVERIFY(sm.size() == 0);
|
||||
QVERIFY(sm.errorString() != QString());
|
||||
QVERIFY(!sm.detach());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::lock()
|
||||
{
|
||||
QSharedMemory shm;
|
||||
QVERIFY(!shm.lock());
|
||||
QCOMPARE(shm.error(), QSharedMemory::LockError);
|
||||
|
||||
shm.setKey(QLatin1String("qsharedmemory"));
|
||||
|
||||
QVERIFY(!shm.lock());
|
||||
QCOMPARE(shm.error(), QSharedMemory::LockError);
|
||||
|
||||
QVERIFY(shm.create(100));
|
||||
QVERIFY(shm.lock());
|
||||
QTest::ignoreMessage(QtWarningMsg, "QSharedMemory::lock: already locked");
|
||||
QVERIFY(shm.lock());
|
||||
// we didn't unlock(), so ignore the warning from auto-detach in destructor
|
||||
QTest::ignoreMessage(QtWarningMsg, "QSharedMemory::lock: already locked");
|
||||
}
|
||||
|
||||
/*!
|
||||
Other shared memory are allowed to be attached after we remove,
|
||||
but new shared memory are not allowed to attach after a remove.
|
||||
*/
|
||||
// HPUX doesn't allow for multiple attaches per process.
|
||||
#ifndef Q_OS_HPUX
|
||||
void tst_QSharedMemory::removeWhileAttached()
|
||||
{
|
||||
rememberKey("one");
|
||||
|
||||
// attach 1
|
||||
QSharedMemory *smOne = new QSharedMemory(QLatin1String("one"));
|
||||
QVERIFY(smOne->create(1024));
|
||||
QVERIFY(smOne->isAttached());
|
||||
|
||||
// attach 2
|
||||
QSharedMemory *smTwo = new QSharedMemory(QLatin1String("one"));
|
||||
QVERIFY(smTwo->attach());
|
||||
QVERIFY(smTwo->isAttached());
|
||||
|
||||
// detach 1 and remove, remove one first to catch another error.
|
||||
delete smOne;
|
||||
delete smTwo;
|
||||
|
||||
// three shouldn't be able to attach
|
||||
QSharedMemory smThree(QLatin1String("one"));
|
||||
QVERIFY(!smThree.attach());
|
||||
QCOMPARE(smThree.error(), QSharedMemory::NotFound);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
The memory should be set to 0 after created.
|
||||
*/
|
||||
void tst_QSharedMemory::emptyMemory()
|
||||
{
|
||||
QSharedMemory sm(rememberKey(QLatin1String("voidland")));
|
||||
int size = 1024;
|
||||
QVERIFY(sm.create(size, QSharedMemory::ReadOnly));
|
||||
char *get = (char*)sm.data();
|
||||
char null = 0;
|
||||
for (int i = 0; i < size; ++i)
|
||||
QCOMPARE(get[i], null);
|
||||
}
|
||||
|
||||
/*!
|
||||
Verify that attach with ReadOnly is actually read only
|
||||
by writing to data and causing a segfault.
|
||||
*/
|
||||
// This test opens a crash dialog on Windows.
|
||||
#ifndef Q_OS_WIN
|
||||
void tst_QSharedMemory::readOnly()
|
||||
{
|
||||
rememberKey("readonly_segfault");
|
||||
// ### on windows disable the popup somehow
|
||||
QProcess p;
|
||||
p.start(helperBinary(), QStringList("readonly_segfault"));
|
||||
p.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
p.waitForFinished();
|
||||
QCOMPARE(p.error(), QProcess::Crashed);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
Keep making shared memory until the kernel stops us.
|
||||
*/
|
||||
void tst_QSharedMemory::useTooMuchMemory()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
bool success = true;
|
||||
int count = 0;
|
||||
while (success) {
|
||||
QString key = QString("maxmemorytest_%1").arg(count++);
|
||||
QSharedMemory *sm = new QSharedMemory(rememberKey(key));
|
||||
QVERIFY(sm);
|
||||
jail.append(sm);
|
||||
int size = 32768 * 1024;
|
||||
success = sm->create(size);
|
||||
if (!success && sm->error() == QSharedMemory::AlreadyExists) {
|
||||
// left over from a crash, clean it up
|
||||
sm->attach();
|
||||
sm->detach();
|
||||
success = sm->create(size);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
QVERIFY(!sm->isAttached());
|
||||
QCOMPARE(sm->key(), key);
|
||||
QCOMPARE(sm->size(), 0);
|
||||
QVERIFY(sm->data() == 0);
|
||||
if (sm->error() != QSharedMemory::OutOfResources)
|
||||
qDebug() << sm->error() << sm->errorString();
|
||||
// ### Linux wont return OutOfResources if there are not enough semaphores to use.
|
||||
QVERIFY(sm->error() == QSharedMemory::OutOfResources
|
||||
|| sm->error() == QSharedMemory::LockError);
|
||||
QVERIFY(sm->errorString() != QString());
|
||||
QVERIFY(!sm->attach());
|
||||
QVERIFY(!sm->detach());
|
||||
} else {
|
||||
QVERIFY(sm->isAttached());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Create one shared memory (government) and see how many other shared memories (wars) we can
|
||||
attach before the system runs out of resources.
|
||||
*/
|
||||
// HPUX doesn't allow for multiple attaches per process.
|
||||
// For WinCE, this test nearly kills the system, so skip it.
|
||||
#if !defined(Q_OS_HPUX) && !defined(Q_OS_WINCE)
|
||||
void tst_QSharedMemory::attachTooMuch()
|
||||
{
|
||||
QSKIP("disabled");
|
||||
|
||||
QSharedMemory government(rememberKey("government"));
|
||||
QVERIFY(government.create(1024));
|
||||
while (true) {
|
||||
QSharedMemory *war = new QSharedMemory(government.key());
|
||||
QVERIFY(war);
|
||||
jail.append(war);
|
||||
if (!war->attach()) {
|
||||
QVERIFY(!war->isAttached());
|
||||
QCOMPARE(war->key(), government.key());
|
||||
QCOMPARE(war->size(), 0);
|
||||
QVERIFY(war->data() == 0);
|
||||
QCOMPARE(war->error(), QSharedMemory::OutOfResources);
|
||||
QVERIFY(war->errorString() != QString());
|
||||
QVERIFY(!war->detach());
|
||||
break;
|
||||
} else {
|
||||
QVERIFY(war->isAttached());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPUX doesn't allow for multiple attaches per process.
|
||||
#ifndef Q_OS_HPUX
|
||||
void tst_QSharedMemory::simpleProducerConsumer_data()
|
||||
{
|
||||
QTest::addColumn<QSharedMemory::AccessMode>("mode");
|
||||
|
||||
QTest::newRow("readonly") << QSharedMemory::ReadOnly;
|
||||
QTest::newRow("readwrite") << QSharedMemory::ReadWrite;
|
||||
}
|
||||
|
||||
/*!
|
||||
The basic consumer producer that rounds out the basic testing.
|
||||
If this fails then any muli-threading/process might fail (but be
|
||||
harder to debug)
|
||||
|
||||
This doesn't require nor test any locking system.
|
||||
*/
|
||||
void tst_QSharedMemory::simpleProducerConsumer()
|
||||
{
|
||||
QFETCH(QSharedMemory::AccessMode, mode);
|
||||
|
||||
rememberKey(QLatin1String("market"));
|
||||
QSharedMemory producer(QLatin1String("market"));
|
||||
QSharedMemory consumer(QLatin1String("market"));
|
||||
int size = 512;
|
||||
QVERIFY(producer.create(size));
|
||||
QVERIFY(consumer.attach(mode));
|
||||
|
||||
char *put = (char*)producer.data();
|
||||
char *get = (char*)consumer.data();
|
||||
// On Windows CE you always have ReadWrite access. Thus
|
||||
// ViewMapOfFile returns the same pointer
|
||||
#if !defined(Q_OS_WINCE)
|
||||
QVERIFY(put != get);
|
||||
#endif
|
||||
for (int i = 0; i < size; ++i) {
|
||||
put[i] = 'Q';
|
||||
QCOMPARE(get[i], 'Q');
|
||||
}
|
||||
QVERIFY(consumer.detach());
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPUX doesn't allow for multiple attaches per process.
|
||||
#ifndef Q_OS_HPUX
|
||||
void tst_QSharedMemory::simpleDoubleProducerConsumer()
|
||||
{
|
||||
rememberKey(QLatin1String("market"));
|
||||
QSharedMemory producer(QLatin1String("market"));
|
||||
int size = 512;
|
||||
QVERIFY(producer.create(size));
|
||||
QVERIFY(producer.detach());
|
||||
QVERIFY(producer.create(size));
|
||||
|
||||
{
|
||||
QSharedMemory consumer(QLatin1String("market"));
|
||||
QVERIFY(consumer.attach());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
class Consumer : public QThread
|
||||
{
|
||||
|
||||
public:
|
||||
void run()
|
||||
{
|
||||
QSharedMemory consumer(QLatin1String("market"));
|
||||
while (!consumer.attach()) {
|
||||
if (consumer.error() != QSharedMemory::NotFound)
|
||||
qDebug() << "consumer: failed to connect" << consumer.error() << consumer.errorString();
|
||||
QVERIFY(consumer.error() == QSharedMemory::NotFound || consumer.error() == QSharedMemory::KeyError);
|
||||
QTest::qWait(1);
|
||||
}
|
||||
|
||||
char *memory = (char*)consumer.data();
|
||||
|
||||
int i = 0;
|
||||
while (true) {
|
||||
if (!consumer.lock())
|
||||
break;
|
||||
if (memory[0] == 'Q')
|
||||
memory[0] = ++i;
|
||||
if (memory[0] == 'E') {
|
||||
memory[1]++;
|
||||
QVERIFY(consumer.unlock());
|
||||
break;
|
||||
}
|
||||
QVERIFY(consumer.unlock());
|
||||
QTest::qWait(1);
|
||||
}
|
||||
|
||||
QVERIFY(consumer.detach());
|
||||
}
|
||||
};
|
||||
|
||||
class Producer : public QThread
|
||||
{
|
||||
|
||||
public:
|
||||
void run()
|
||||
{
|
||||
QSharedMemory producer(QLatin1String("market"));
|
||||
int size = 1024;
|
||||
if (!producer.create(size)) {
|
||||
// left over from a crash...
|
||||
if (producer.error() == QSharedMemory::AlreadyExists) {
|
||||
producer.attach();
|
||||
producer.detach();
|
||||
QVERIFY(producer.create(size));
|
||||
}
|
||||
}
|
||||
QVERIFY(producer.isAttached());
|
||||
char *memory = (char*)producer.data();
|
||||
memory[1] = '0';
|
||||
QTime timer;
|
||||
timer.start();
|
||||
int i = 0;
|
||||
while (i < 5 && timer.elapsed() < 5000) {
|
||||
QVERIFY(producer.lock());
|
||||
if (memory[0] == 'Q') {
|
||||
QVERIFY(producer.unlock());
|
||||
QTest::qWait(1);
|
||||
continue;
|
||||
}
|
||||
++i;
|
||||
memory[0] = 'Q';
|
||||
QVERIFY(producer.unlock());
|
||||
QTest::qWait(1);
|
||||
}
|
||||
|
||||
// tell everyone to quit
|
||||
QVERIFY(producer.lock());
|
||||
memory[0] = 'E';
|
||||
QVERIFY(producer.unlock());
|
||||
|
||||
}
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
void tst_QSharedMemory::simpleThreadedProducerConsumer_data()
|
||||
{
|
||||
QTest::addColumn<bool>("producerIsThread");
|
||||
QTest::addColumn<int>("threads");
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
QTest::newRow("1 consumer, producer is thread") << true << 1;
|
||||
QTest::newRow("1 consumer, producer is this") << false << 1;
|
||||
QTest::newRow("5 consumers, producer is thread") << true << 5;
|
||||
QTest::newRow("5 consumers, producer is this") << false << 5;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
The basic producer/consumer, but this time using threads.
|
||||
*/
|
||||
void tst_QSharedMemory::simpleThreadedProducerConsumer()
|
||||
{
|
||||
QFETCH(bool, producerIsThread);
|
||||
QFETCH(int, threads);
|
||||
rememberKey(QLatin1String("market"));
|
||||
|
||||
#if defined Q_OS_HPUX && defined __ia64
|
||||
QSKIP("This test locks up on gravlaks.troll.no");
|
||||
#endif
|
||||
|
||||
Producer p;
|
||||
if (producerIsThread)
|
||||
p.start();
|
||||
|
||||
QList<Consumer*> consumers;
|
||||
for (int i = 0; i < threads; ++i) {
|
||||
consumers.append(new Consumer());
|
||||
consumers.last()->start();
|
||||
}
|
||||
|
||||
if (!producerIsThread)
|
||||
p.run();
|
||||
|
||||
p.wait(5000);
|
||||
while (!consumers.isEmpty()) {
|
||||
Consumer *c = consumers.first();
|
||||
QVERIFY(c->isFinished() || c->wait(5000));
|
||||
delete consumers.takeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::simpleProcessProducerConsumer_data()
|
||||
{
|
||||
QTest::addColumn<int>("processes");
|
||||
int tries = 5;
|
||||
for (int i = 0; i < tries; ++i) {
|
||||
QTest::newRow("1 process") << 1;
|
||||
QTest::newRow("5 processes") << 5;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Create external processes that produce and consume.
|
||||
*/
|
||||
void tst_QSharedMemory::simpleProcessProducerConsumer()
|
||||
{
|
||||
QFETCH(int, processes);
|
||||
|
||||
rememberKey("market");
|
||||
|
||||
QProcess producer;
|
||||
producer.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
producer.start(helperBinary(), QStringList("producer"));
|
||||
QVERIFY2(producer.waitForStarted(), "Could not start helper binary");
|
||||
|
||||
QList<QProcess*> consumers;
|
||||
unsigned int failedProcesses = 0;
|
||||
const QStringList consumerArguments = QStringList("consumer");
|
||||
for (int i = 0; i < processes; ++i) {
|
||||
QProcess *p = new QProcess;
|
||||
p->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
p->start(helperBinary(), consumerArguments);
|
||||
if (p->waitForStarted(2000))
|
||||
consumers.append(p);
|
||||
else
|
||||
++failedProcesses;
|
||||
}
|
||||
|
||||
QVERIFY(producer.waitForFinished(5000));
|
||||
|
||||
bool consumerFailed = false;
|
||||
|
||||
while (!consumers.isEmpty()) {
|
||||
QVERIFY(consumers.first()->waitForFinished(3000));
|
||||
if (consumers.first()->state() == QProcess::Running ||
|
||||
consumers.first()->exitStatus() != QProcess::NormalExit ||
|
||||
consumers.first()->exitCode() != 0) {
|
||||
consumerFailed = true;
|
||||
}
|
||||
delete consumers.takeFirst();
|
||||
}
|
||||
QCOMPARE(consumerFailed, false);
|
||||
QCOMPARE(failedProcesses, (unsigned int)(0));
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::uniqueKey_data()
|
||||
{
|
||||
QTest::addColumn<QString>("key1");
|
||||
QTest::addColumn<QString>("key2");
|
||||
|
||||
QTest::newRow("null == null") << QString() << QString();
|
||||
QTest::newRow("key == key") << QString("key") << QString("key");
|
||||
QTest::newRow("key1 == key1") << QString("key1") << QString("key1");
|
||||
QTest::newRow("key != key1") << QString("key") << QString("key1");
|
||||
QTest::newRow("ke1y != key1") << QString("ke1y") << QString("key1");
|
||||
QTest::newRow("key1 != key2") << QString("key1") << QString("key2");
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::uniqueKey()
|
||||
{
|
||||
QFETCH(QString, key1);
|
||||
QFETCH(QString, key2);
|
||||
|
||||
QSharedMemory sm1(key1);
|
||||
QSharedMemory sm2(key2);
|
||||
|
||||
bool setEqual = (key1 == key2);
|
||||
bool keyEqual = (sm1.key() == sm2.key());
|
||||
bool nativeEqual = (sm1.nativeKey() == sm2.nativeKey());
|
||||
|
||||
QCOMPARE(keyEqual, setEqual);
|
||||
QCOMPARE(nativeEqual, setEqual);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QSharedMemory)
|
||||
#include "tst_qsharedmemory.moc"
|
||||
|
Loading…
Reference in New Issue
Block a user