mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-05 08:45:25 +08:00
qt 6.6.0 clean
This commit is contained in:
14
tests/auto/corelib/ipc/CMakeLists.txt
Normal file
14
tests/auto/corelib/ipc/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
# Copyright (C) 2022 Intel Corporation.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if(NOT ANDROID AND NOT UIKIT)
|
||||
if(QT_FEATURE_sharedmemory OR QT_FEATURE_systemsemaphore)
|
||||
add_subdirectory(qnativeipckey)
|
||||
endif()
|
||||
if(QT_FEATURE_sharedmemory)
|
||||
add_subdirectory(qsharedmemory)
|
||||
endif()
|
||||
if(QT_FEATURE_systemsemaphore)
|
||||
add_subdirectory(qsystemsemaphore)
|
||||
endif()
|
||||
endif()
|
83
tests/auto/corelib/ipc/ipctestcommon.h
Normal file
83
tests/auto/corelib/ipc/ipctestcommon.h
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (C) 2022 Intel Corporation.
|
||||
|
||||
#include <QtTest/QTest>
|
||||
#include <QtCore/QNativeIpcKey>
|
||||
|
||||
namespace IpcTestCommon {
|
||||
static QList<QNativeIpcKey::Type> supportedKeyTypes;
|
||||
|
||||
template <typename IpcClass> void addGlobalTestRows()
|
||||
{
|
||||
qDebug() << "Default key type is" << QNativeIpcKey::DefaultTypeForOs
|
||||
<< "and legacy key type is" << QNativeIpcKey::legacyDefaultTypeForOs();
|
||||
|
||||
#if defined(Q_OS_FREEBSD) || defined(Q_OS_DARWIN) || defined(Q_OS_WIN) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
|
||||
// only enforce that IPC works on the platforms above; other platforms may
|
||||
// have no working backends (notably, Android)
|
||||
QVERIFY(IpcClass::isKeyTypeSupported(QNativeIpcKey::DefaultTypeForOs));
|
||||
QVERIFY(IpcClass::isKeyTypeSupported(QNativeIpcKey::legacyDefaultTypeForOs()));
|
||||
#endif
|
||||
|
||||
auto addRowIfSupported = [](const char *name, QNativeIpcKey::Type type) {
|
||||
if (IpcClass::isKeyTypeSupported(type)) {
|
||||
supportedKeyTypes << type;
|
||||
QTest::newRow(name) << type;
|
||||
}
|
||||
};
|
||||
|
||||
QTest::addColumn<QNativeIpcKey::Type>("keyType");
|
||||
|
||||
addRowIfSupported("Windows", QNativeIpcKey::Type::Windows);
|
||||
addRowIfSupported("POSIX", QNativeIpcKey::Type::PosixRealtime);
|
||||
addRowIfSupported("SystemV-Q", QNativeIpcKey::Type::SystemV);
|
||||
addRowIfSupported("SystemV-T", QNativeIpcKey::Type('T'));
|
||||
|
||||
if (supportedKeyTypes.isEmpty())
|
||||
QSKIP("System reports no supported IPC types.");
|
||||
}
|
||||
|
||||
// rotate through the supported types and find another
|
||||
inline QNativeIpcKey::Type nextKeyType(QNativeIpcKey::Type type)
|
||||
{
|
||||
qsizetype idx = supportedKeyTypes.indexOf(type);
|
||||
Q_ASSERT(idx >= 0);
|
||||
|
||||
++idx;
|
||||
if (idx == supportedKeyTypes.size())
|
||||
idx = 0;
|
||||
return supportedKeyTypes.at(idx);
|
||||
}
|
||||
} // namespace IpcTestCommon
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace QTest {
|
||||
template<> inline char *toString(const QNativeIpcKey::Type &type)
|
||||
{
|
||||
switch (type) {
|
||||
case QNativeIpcKey::Type::SystemV: return qstrdup("SystemV");
|
||||
case QNativeIpcKey::Type::PosixRealtime: return qstrdup("PosixRealTime");
|
||||
case QNativeIpcKey::Type::Windows: return qstrdup("Windows");
|
||||
}
|
||||
if (type == QNativeIpcKey::Type{})
|
||||
return qstrdup("Invalid");
|
||||
|
||||
char buf[32];
|
||||
qsnprintf(buf, sizeof(buf), "%u", unsigned(type));
|
||||
return qstrdup(buf);
|
||||
}
|
||||
|
||||
template<> inline char *toString(const QNativeIpcKey &key)
|
||||
{
|
||||
if (!key.isValid())
|
||||
return qstrdup("<invalid>");
|
||||
|
||||
const char *type = toString(key.type());
|
||||
const char *text = toString(key.nativeKey());
|
||||
char buf[256];
|
||||
qsnprintf(buf, sizeof(buf), "QNativeIpcKey(%s, %s)", text, type);
|
||||
delete[] type;
|
||||
delete[] text;
|
||||
return qstrdup(buf);
|
||||
}
|
||||
} // namespace QTest
|
||||
QT_END_NAMESPACE
|
7
tests/auto/corelib/ipc/qnativeipckey/CMakeLists.txt
Normal file
7
tests/auto/corelib/ipc/qnativeipckey/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
# Copyright (C) 2022 Intel Corporation.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
qt_internal_add_test(tst_qnativeipckey
|
||||
SOURCES
|
||||
tst_qnativeipckey.cpp
|
||||
)
|
426
tests/auto/corelib/ipc/qnativeipckey/tst_qnativeipckey.cpp
Normal file
426
tests/auto/corelib/ipc/qnativeipckey/tst_qnativeipckey.cpp
Normal file
@ -0,0 +1,426 @@
|
||||
// Copyright (C) 2022 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QtCore/QNativeIpcKey>
|
||||
#include <QtTest/QTest>
|
||||
|
||||
#include "../ipctestcommon.h"
|
||||
|
||||
#if QT_CONFIG(sharedmemory)
|
||||
# include <qsharedmemory.h>
|
||||
#endif
|
||||
#if QT_CONFIG(systemsemaphore)
|
||||
# include <qsystemsemaphore.h>
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(sharedmemory)
|
||||
static const auto makeLegacyKey = QSharedMemory::legacyNativeKey;
|
||||
#else
|
||||
static const auto makeLegacyKey = QSystemSemaphore::legacyNativeKey;
|
||||
#endif
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
class tst_QNativeIpcKey : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void defaultTypes();
|
||||
void construct();
|
||||
void getSetCheck();
|
||||
void equality();
|
||||
void hash();
|
||||
void swap();
|
||||
void toString_data();
|
||||
void toString();
|
||||
void fromString_data();
|
||||
void fromString();
|
||||
void legacyKeys_data();
|
||||
void legacyKeys();
|
||||
};
|
||||
|
||||
void tst_QNativeIpcKey::defaultTypes()
|
||||
{
|
||||
auto isKnown = [](QNativeIpcKey::Type t) {
|
||||
switch (t) {
|
||||
case QNativeIpcKey::Type::SystemV:
|
||||
case QNativeIpcKey::Type::PosixRealtime:
|
||||
case QNativeIpcKey::Type::Windows:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// because the letter Q looked nice in Håvard's Emacs font back in the 1990s
|
||||
static_assert(qToUnderlying(QNativeIpcKey::Type::SystemV) == 'Q',
|
||||
"QNativeIpcKey::Type::SystemV must be equal to the letter Q");
|
||||
|
||||
auto type = QNativeIpcKey::DefaultTypeForOs;
|
||||
auto legacy = QNativeIpcKey::legacyDefaultTypeForOs();
|
||||
QVERIFY(isKnown(type));
|
||||
QVERIFY(isKnown(legacy));
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QCOMPARE(type, QNativeIpcKey::Type::Windows);
|
||||
#else
|
||||
QCOMPARE(type, QNativeIpcKey::Type::PosixRealtime);
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
QCOMPARE(legacy, QNativeIpcKey::Type::Windows);
|
||||
#elif defined(QT_POSIX_IPC)
|
||||
QCOMPARE(legacy, QNativeIpcKey::Type::PosixRealtime);
|
||||
#elif !defined(Q_OS_DARWIN)
|
||||
QCOMPARE(legacy, QNativeIpcKey::Type::SystemV);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::construct()
|
||||
{
|
||||
{
|
||||
QNativeIpcKey invalid(QNativeIpcKey::Type{});
|
||||
QVERIFY(!invalid.isValid());
|
||||
}
|
||||
|
||||
{
|
||||
QNativeIpcKey key;
|
||||
QVERIFY(key.nativeKey().isEmpty());
|
||||
QVERIFY(key.isEmpty());
|
||||
QVERIFY(key.isValid());
|
||||
QCOMPARE(key.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
|
||||
QNativeIpcKey copy(key);
|
||||
QVERIFY(copy.nativeKey().isEmpty());
|
||||
QVERIFY(copy.isEmpty());
|
||||
QVERIFY(copy.isValid());
|
||||
QCOMPARE(copy.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
|
||||
QNativeIpcKey moved(std::move(copy));
|
||||
QVERIFY(moved.nativeKey().isEmpty());
|
||||
QVERIFY(moved.isEmpty());
|
||||
QVERIFY(moved.isValid());
|
||||
QCOMPARE(moved.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
|
||||
key.setType({});
|
||||
key.setNativeKey("something else");
|
||||
key = std::move(moved);
|
||||
QVERIFY(key.nativeKey().isEmpty());
|
||||
QVERIFY(key.isEmpty());
|
||||
QVERIFY(key.isValid());
|
||||
QCOMPARE(key.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
|
||||
copy.setType({});
|
||||
copy.setNativeKey("something else");
|
||||
copy = key;
|
||||
QVERIFY(copy.nativeKey().isEmpty());
|
||||
QVERIFY(copy.isEmpty());
|
||||
QVERIFY(copy.isValid());
|
||||
QCOMPARE(copy.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
}
|
||||
|
||||
{
|
||||
QNativeIpcKey key("dummy");
|
||||
QCOMPARE(key.nativeKey(), "dummy");
|
||||
QVERIFY(!key.isEmpty());
|
||||
QVERIFY(key.isValid());
|
||||
QCOMPARE(key.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
|
||||
QNativeIpcKey copy(key);
|
||||
QCOMPARE(key.nativeKey(), "dummy");
|
||||
QCOMPARE(copy.nativeKey(), "dummy");
|
||||
QVERIFY(!copy.isEmpty());
|
||||
QVERIFY(copy.isValid());
|
||||
QCOMPARE(copy.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
|
||||
QNativeIpcKey moved(std::move(copy));
|
||||
QCOMPARE(key.nativeKey(), "dummy");
|
||||
QCOMPARE(moved.nativeKey(), "dummy");
|
||||
QVERIFY(!moved.isEmpty());
|
||||
QVERIFY(moved.isValid());
|
||||
QCOMPARE(moved.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
|
||||
key.setType({});
|
||||
key.setNativeKey("something else");
|
||||
key = std::move(moved);
|
||||
QCOMPARE(key.nativeKey(), "dummy");
|
||||
QVERIFY(!key.isEmpty());
|
||||
QVERIFY(key.isValid());
|
||||
QCOMPARE(key.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
|
||||
copy.setType({});
|
||||
copy.setNativeKey("something else");
|
||||
copy = key;
|
||||
QCOMPARE(key.nativeKey(), "dummy");
|
||||
QCOMPARE(copy.nativeKey(), "dummy");
|
||||
QVERIFY(!copy.isEmpty());
|
||||
QVERIFY(copy.isValid());
|
||||
QCOMPARE(copy.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::getSetCheck()
|
||||
{
|
||||
QNativeIpcKey key("key1", QNativeIpcKey::Type::Windows);
|
||||
QVERIFY(key.isValid());
|
||||
QVERIFY(!key.isEmpty());
|
||||
QCOMPARE(key.nativeKey(), "key1");
|
||||
QCOMPARE(key.type(), QNativeIpcKey::Type::Windows);
|
||||
|
||||
key.setType(QNativeIpcKey::Type::SystemV);
|
||||
QVERIFY(key.isValid());
|
||||
QVERIFY(!key.isEmpty());
|
||||
QCOMPARE(key.type(), QNativeIpcKey::Type::SystemV);
|
||||
|
||||
key.setNativeKey("key2");
|
||||
QCOMPARE(key.nativeKey(), "key2");
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::equality()
|
||||
{
|
||||
QNativeIpcKey key1, key2;
|
||||
QCOMPARE(key1, key2);
|
||||
QVERIFY(!(key1 != key2));
|
||||
|
||||
key1.setNativeKey("key1");
|
||||
QCOMPARE_NE(key1, key2);
|
||||
QVERIFY(!(key1 == key2));
|
||||
|
||||
key2.setType({});
|
||||
QCOMPARE_NE(key1, key2);
|
||||
QVERIFY(!(key1 == key2));
|
||||
|
||||
key2.setNativeKey(key1.nativeKey());
|
||||
QCOMPARE_NE(key1, key2);
|
||||
QVERIFY(!(key1 == key2));
|
||||
|
||||
key2.setType(QNativeIpcKey::DefaultTypeForOs);
|
||||
QCOMPARE(key1, key2);
|
||||
QVERIFY(!(key1 != key2));
|
||||
|
||||
key1 = makeLegacyKey("key1", QNativeIpcKey::DefaultTypeForOs);
|
||||
QCOMPARE_NE(key1, key2);
|
||||
QVERIFY(!(key1 == key2));
|
||||
|
||||
key2 = key1;
|
||||
QCOMPARE(key1, key2);
|
||||
QVERIFY(!(key1 != key2));
|
||||
|
||||
// just setting the native key won't make them equal again!
|
||||
key2.setNativeKey(key1.nativeKey());
|
||||
QCOMPARE_NE(key1, key2);
|
||||
QVERIFY(!(key1 == key2));
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::hash()
|
||||
{
|
||||
QNativeIpcKey key1("key1", QNativeIpcKey::DefaultTypeForOs);
|
||||
QNativeIpcKey key2(key1);
|
||||
QCOMPARE_EQ(qHash(key1), qHash(key2));
|
||||
QCOMPARE_EQ(qHash(key1, 123), qHash(key2, 123));
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::swap()
|
||||
{
|
||||
QNativeIpcKey key1("key1", QNativeIpcKey::Type::PosixRealtime);
|
||||
QNativeIpcKey key2("key2", QNativeIpcKey::Type::Windows);
|
||||
|
||||
// self-swaps
|
||||
key1.swap(key1);
|
||||
key2.swap(key2);
|
||||
QCOMPARE(key1.nativeKey(), "key1");
|
||||
QCOMPARE(key1.type(), QNativeIpcKey::Type::PosixRealtime);
|
||||
QCOMPARE(key2.nativeKey(), "key2");
|
||||
QCOMPARE(key2.type(), QNativeIpcKey::Type::Windows);
|
||||
|
||||
key1.swap(key2);
|
||||
QCOMPARE(key2.nativeKey(), "key1");
|
||||
QCOMPARE(key2.type(), QNativeIpcKey::Type::PosixRealtime);
|
||||
QCOMPARE(key1.nativeKey(), "key2");
|
||||
QCOMPARE(key1.type(), QNativeIpcKey::Type::Windows);
|
||||
|
||||
key1.swap(key2);
|
||||
QCOMPARE(key1.nativeKey(), "key1");
|
||||
QCOMPARE(key1.type(), QNativeIpcKey::Type::PosixRealtime);
|
||||
QCOMPARE(key2.nativeKey(), "key2");
|
||||
QCOMPARE(key2.type(), QNativeIpcKey::Type::Windows);
|
||||
|
||||
key1 = makeLegacyKey("key1", QNativeIpcKey::DefaultTypeForOs);
|
||||
QCOMPARE(key1.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
key1.swap(key2);
|
||||
QCOMPARE(key1.type(), QNativeIpcKey::Type::Windows);
|
||||
QCOMPARE(key2.type(), QNativeIpcKey::DefaultTypeForOs);
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::toString_data()
|
||||
{
|
||||
QTest::addColumn<QString>("string");
|
||||
QTest::addColumn<QNativeIpcKey>("key");
|
||||
|
||||
QTest::newRow("invalid") << QString() << QNativeIpcKey(QNativeIpcKey::Type(0));
|
||||
|
||||
auto addRow = [](const char *prefix, QNativeIpcKey::Type type) {
|
||||
auto add = [=](const char *name, QLatin1StringView key, QLatin1StringView encoded = {}) {
|
||||
if (encoded.isNull())
|
||||
encoded = key;
|
||||
QTest::addRow("%s-%s", prefix, name)
|
||||
<< prefix + u":"_s + encoded << QNativeIpcKey(key, type);
|
||||
};
|
||||
add("empty", {});
|
||||
add("text", "foobar"_L1);
|
||||
add("pathlike", "/sometext"_L1);
|
||||
add("objectlike", "Global\\sometext"_L1);
|
||||
add("colon-slash", ":/"_L1);
|
||||
add("slash-colon", "/:"_L1);
|
||||
add("percent", "%"_L1, "%25"_L1);
|
||||
add("question-hash", "?#"_L1, "%3F%23"_L1);
|
||||
add("hash-question", "#?"_L1, "%23%3F"_L1);
|
||||
add("double-slash", "//"_L1, "/%2F"_L1);
|
||||
add("triple-slash", "///"_L1, "/%2F/"_L1);
|
||||
add("non-ascii", "\xe9"_L1);
|
||||
add("non-utf8", "\xa0\xff"_L1);
|
||||
QTest::addRow("%s-%s", prefix, "non-latin1")
|
||||
<< prefix + u":\u0100.\u2000.\U00010000"_s
|
||||
<< QNativeIpcKey(u"\u0100.\u2000.\U00010000"_s, type);
|
||||
};
|
||||
addRow("systemv", QNativeIpcKey::Type::SystemV);
|
||||
addRow("posix", QNativeIpcKey::Type::PosixRealtime);
|
||||
addRow("windows", QNativeIpcKey::Type::Windows);
|
||||
|
||||
addRow("systemv-1", QNativeIpcKey::Type(1));
|
||||
addRow("systemv-84", QNativeIpcKey::Type('T'));
|
||||
addRow("systemv-255", QNativeIpcKey::Type(0xff));
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::toString()
|
||||
{
|
||||
QFETCH(QString, string);
|
||||
QFETCH(QNativeIpcKey, key);
|
||||
|
||||
QCOMPARE(key.toString(), string);
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::fromString_data()
|
||||
{
|
||||
toString_data();
|
||||
QTest::addRow("systemv-alias") << "systemv-81:" << QNativeIpcKey(QNativeIpcKey::Type::SystemV);
|
||||
QTest::addRow("systemv-zeropadded") << "systemv-009:" << QNativeIpcKey(QNativeIpcKey::Type(9));
|
||||
|
||||
QNativeIpcKey valid("/foo", QNativeIpcKey::Type::PosixRealtime);
|
||||
QNativeIpcKey invalid(QNativeIpcKey::Type(0));
|
||||
|
||||
// percent-decoding
|
||||
QTest::addRow("percent-encoded") << "posix:%2f%66o%6f" << valid;
|
||||
QTest::addRow("percent-utf8")
|
||||
<< "posix:%C4%80.%E2%80%80.%F0%90%80%80"
|
||||
<< QNativeIpcKey(u"\u0100.\u2000.\U00010000"_s, QNativeIpcKey::Type::PosixRealtime);
|
||||
|
||||
// fragments are ignored
|
||||
QTest::addRow("with-fragment") << "posix:/foo#bar" << valid;
|
||||
QTest::addRow("with-fragmentquery") << "posix:/foo#bar?baz" << valid;
|
||||
|
||||
// but unknown query items are not
|
||||
QTest::addRow("with-query") << "posix:/foo?bar" << invalid;
|
||||
QTest::addRow("with-queryfragment") << "posix:/foo?bar#baz" << invalid;
|
||||
|
||||
// add some ones that won't parse well
|
||||
QTest::addRow("positive-number") << "81" << invalid;
|
||||
QTest::addRow("negative-number") << "-81" << invalid;
|
||||
QTest::addRow("invalidprefix") << "invalidprefix:" << invalid;
|
||||
QTest::addRow("systemv-nodash") << "systemv255" << invalid;
|
||||
QTest::addRow("systemv-doubledash") << "systemv--255:" << invalid;
|
||||
QTest::addRow("systemv-plus") << "systemv+255" << invalid;
|
||||
QTest::addRow("systemv-hex") << "systemv-0x01:" << invalid;
|
||||
QTest::addRow("systemv-too-low") << "systemv-0:" << invalid;
|
||||
QTest::addRow("systemv-too-high") << "systemv-256" << invalid;
|
||||
QTest::addRow("systemv-overflow-15bit") << "systemv-32769:" << invalid;
|
||||
QTest::addRow("systemv-overflow-16bit") << "systemv-65537:" << invalid;
|
||||
QTest::addRow("systemv-overflow-31bit") << "systemv-2147483649:" << invalid;
|
||||
QTest::addRow("systemv-overflow-32bit") << "systemv-4294967297:" << invalid;
|
||||
|
||||
auto addRows = [=](const char *name) {
|
||||
QTest::addRow("%s-nocolon", name) << name << invalid;
|
||||
QTest::addRow("%s-junk", name) << name + u"junk"_s << invalid;
|
||||
QTest::addRow("junk-%s-colon", name) << u"junk:"_s + name + u':' << invalid;
|
||||
};
|
||||
addRows("systemv");
|
||||
addRows("posix");
|
||||
addRows("windows");
|
||||
addRows("systemv-1");
|
||||
addRows("systemv-255");
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::fromString()
|
||||
{
|
||||
QFETCH(QString, string);
|
||||
QFETCH(QNativeIpcKey, key);
|
||||
|
||||
QCOMPARE(QNativeIpcKey::fromString(string), key);
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::legacyKeys_data()
|
||||
{
|
||||
QTest::addColumn<QNativeIpcKey::Type>("type");
|
||||
QTest::addColumn<QString>("legacyKey");
|
||||
auto addRows = [](QNativeIpcKey::Type type) {
|
||||
const char *label = "<unknown-type>";
|
||||
switch (type) {
|
||||
case QNativeIpcKey::Type::SystemV:
|
||||
label = "systemv";
|
||||
break;
|
||||
case QNativeIpcKey::Type::PosixRealtime:
|
||||
label = "posix";
|
||||
break;
|
||||
case QNativeIpcKey::Type::Windows:
|
||||
label = "windows";
|
||||
break;
|
||||
}
|
||||
auto add = [=](const char *name, const QString &legacyKey) {
|
||||
QTest::addRow("%s-%s", label, name) << type << legacyKey;
|
||||
};
|
||||
add("empty", {});
|
||||
add("text", "foobar"_L1);
|
||||
add("pathlike", "/sometext"_L1);
|
||||
add("objectlike", "Global\\sometext"_L1);
|
||||
add("colon-slash", ":/"_L1);
|
||||
add("slash-colon", "/:"_L1);
|
||||
add("percent", "%"_L1);
|
||||
add("question-hash", "?#"_L1);
|
||||
add("hash-question", "#?"_L1);
|
||||
add("double-slash", "//"_L1);
|
||||
add("triple-slash", "///"_L1);
|
||||
add("non-ascii", "\xe9"_L1);
|
||||
add("non-utf8", "\xa0\xff"_L1);
|
||||
add("non-latin1", u":\u0100.\u2000.\U00010000"_s);
|
||||
};
|
||||
|
||||
addRows(QNativeIpcKey::DefaultTypeForOs);
|
||||
if (auto type = QNativeIpcKey::legacyDefaultTypeForOs();
|
||||
type != QNativeIpcKey::DefaultTypeForOs)
|
||||
addRows(type);
|
||||
}
|
||||
|
||||
void tst_QNativeIpcKey::legacyKeys()
|
||||
{
|
||||
QFETCH(QNativeIpcKey::Type, type);
|
||||
QFETCH(QString, legacyKey);
|
||||
|
||||
QNativeIpcKey key = makeLegacyKey(legacyKey, type);
|
||||
QCOMPARE(key.type(), type);
|
||||
|
||||
QString string = key.toString();
|
||||
QNativeIpcKey key2 = QNativeIpcKey::fromString(string);
|
||||
QCOMPARE(key2, key);
|
||||
|
||||
if (!legacyKey.isEmpty()) {
|
||||
// confirm it shows up in the encoded form
|
||||
Q_ASSERT(!legacyKey.contains(u'&')); // needs extra encoding
|
||||
QUrl u;
|
||||
u.setQuery("legacyKey="_L1 + legacyKey, QUrl::DecodedMode);
|
||||
QString encodedLegacyKey = u.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority
|
||||
| QUrl::DecodeReserved);
|
||||
QVERIFY2(string.contains(encodedLegacyKey), qPrintable(string));
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QNativeIpcKey)
|
||||
#include "tst_qnativeipckey.moc"
|
21
tests/auto/corelib/ipc/qsharedmemory/CMakeLists.txt
Normal file
21
tests/auto/corelib/ipc/qsharedmemory/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
qt_internal_add_test(tst_qsharedmemory
|
||||
SOURCES
|
||||
tst_qsharedmemory.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
)
|
||||
|
||||
## Scopes:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_extend_target(tst_qsharedmemory CONDITION LINUX
|
||||
LIBRARIES
|
||||
rt
|
||||
)
|
||||
add_subdirectory(producerconsumer)
|
||||
if(QT_FEATURE_process)
|
||||
add_dependencies(tst_qsharedmemory producerconsumer_helper)
|
||||
endif()
|
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## producerconsumer_helper Binary:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test_helper(producerconsumer_helper
|
||||
SOURCES
|
||||
main.cpp
|
||||
LIBRARIES
|
||||
Qt::Test
|
||||
)
|
174
tests/auto/corelib/ipc/qsharedmemory/producerconsumer/main.cpp
Normal file
174
tests/auto/corelib/ipc/qsharedmemory/producerconsumer/main.cpp
Normal file
@ -0,0 +1,174 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QSharedMemory>
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
#include <QTest>
|
||||
#include <stdio.h>
|
||||
|
||||
void set(QSharedMemory &sm, int pos, char value)
|
||||
{
|
||||
((char*)sm.data())[pos] = value;
|
||||
}
|
||||
|
||||
QChar get(QSharedMemory &sm, int i)
|
||||
{
|
||||
return QChar::fromLatin1(((char*)sm.data())[i]);
|
||||
}
|
||||
|
||||
int readonly_segfault(const QNativeIpcKey &key)
|
||||
{
|
||||
QSharedMemory sharedMemory(key);
|
||||
sharedMemory.create(1024, QSharedMemory::ReadOnly);
|
||||
sharedMemory.lock();
|
||||
set(sharedMemory, 0, 'a');
|
||||
sharedMemory.unlock();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int producer(const QNativeIpcKey &key)
|
||||
{
|
||||
QSharedMemory producer(key);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
// tell parent we're ready
|
||||
//qDebug("producer created and attached");
|
||||
puts("");
|
||||
fflush(stdout);
|
||||
|
||||
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 exit
|
||||
getchar();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int consumer(const QNativeIpcKey &key)
|
||||
{
|
||||
QSharedMemory consumer(key);
|
||||
|
||||
//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().size() != 3) {
|
||||
fprintf(stderr, "Usage: %s <mode> <key>\n"
|
||||
"<mode> is one of: readonly_segfault, producer, consumer\n",
|
||||
argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
QString function = arguments.at(1);
|
||||
QNativeIpcKey key = QNativeIpcKey::fromString(arguments.at(2));
|
||||
|
||||
if (function == QLatin1String("readonly_segfault"))
|
||||
return readonly_segfault(key);
|
||||
else if (function == QLatin1String("producer"))
|
||||
return producer(key);
|
||||
else if (function == QLatin1String("consumer"))
|
||||
return consumer(key);
|
||||
else
|
||||
qWarning() << "Unknown function" << arguments.at(1);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
904
tests/auto/corelib/ipc/qsharedmemory/tst_qsharedmemory.cpp
Normal file
904
tests/auto/corelib/ipc/qsharedmemory/tst_qsharedmemory.cpp
Normal file
@ -0,0 +1,904 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// Copyright (C) 2022 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#if QT_CONFIG(process)
|
||||
# include <QProcess>
|
||||
#endif
|
||||
#include <QSharedMemory>
|
||||
#include <QTest>
|
||||
#include <QThread>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#ifdef Q_OS_UNIX
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "private/qtcore-config_p.h"
|
||||
#include "../ipctestcommon.h"
|
||||
|
||||
#define EXISTING_SIZE 1024
|
||||
|
||||
Q_DECLARE_METATYPE(QSharedMemory::SharedMemoryError)
|
||||
Q_DECLARE_METATYPE(QSharedMemory::AccessMode)
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
class tst_QSharedMemory : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public Q_SLOTS:
|
||||
void initTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
private slots:
|
||||
// basics
|
||||
void constructor();
|
||||
void nativeKey_data();
|
||||
void nativeKey();
|
||||
void legacyKey_data() { nativeKey_data(); }
|
||||
void legacyKey();
|
||||
void create_data();
|
||||
void create();
|
||||
void attach_data();
|
||||
void attach();
|
||||
void changeKeyType_data() { attach_data(); }
|
||||
void changeKeyType();
|
||||
void lock();
|
||||
|
||||
// custom edge cases
|
||||
void removeWhileAttached();
|
||||
void emptyMemory();
|
||||
void readOnly();
|
||||
|
||||
// basics all together
|
||||
void simpleProducerConsumer_data();
|
||||
void simpleProducerConsumer();
|
||||
void simpleDoubleProducerConsumer();
|
||||
|
||||
// with threads
|
||||
void simpleThreadedProducerConsumer_data();
|
||||
void simpleThreadedProducerConsumer();
|
||||
|
||||
// with processes
|
||||
void simpleProcessProducerConsumer_data();
|
||||
void simpleProcessProducerConsumer();
|
||||
|
||||
// extreme cases
|
||||
void useTooMuchMemory();
|
||||
void attachTooMuch();
|
||||
|
||||
// unique keys
|
||||
void uniqueKey_data();
|
||||
void uniqueKey();
|
||||
|
||||
protected:
|
||||
void remove(const QNativeIpcKey &key);
|
||||
|
||||
QString mangleKey(QStringView key)
|
||||
{
|
||||
if (key.isEmpty())
|
||||
return key.toString();
|
||||
return u"tstshm_%1-%2_%3"_s.arg(QCoreApplication::applicationPid())
|
||||
.arg(seq).arg(key);
|
||||
}
|
||||
|
||||
QNativeIpcKey platformSafeKey(const QString &key)
|
||||
{
|
||||
QFETCH_GLOBAL(QNativeIpcKey::Type, keyType);
|
||||
return QSharedMemory::platformSafeKey(mangleKey(key), keyType);
|
||||
}
|
||||
|
||||
QNativeIpcKey rememberKey(const QString &key)
|
||||
{
|
||||
QNativeIpcKey ipcKey = platformSafeKey(key);
|
||||
if (!keys.contains(ipcKey)) {
|
||||
keys.append(ipcKey);
|
||||
remove(ipcKey);
|
||||
}
|
||||
return ipcKey;
|
||||
}
|
||||
|
||||
QList<QNativeIpcKey> keys;
|
||||
QList<QSharedMemory*> jail;
|
||||
QSharedMemory *existingSharedMemory = nullptr;
|
||||
int seq = 0;
|
||||
|
||||
private:
|
||||
const QString m_helperBinary = "./producerconsumer_helper";
|
||||
};
|
||||
|
||||
void tst_QSharedMemory::initTestCase()
|
||||
{
|
||||
IpcTestCommon::addGlobalTestRows<QSharedMemory>();
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::init()
|
||||
{
|
||||
QNativeIpcKey key = platformSafeKey("existing");
|
||||
existingSharedMemory = new QSharedMemory(key);
|
||||
if (!existingSharedMemory->create(EXISTING_SIZE)) {
|
||||
QCOMPARE(existingSharedMemory->error(), QSharedMemory::AlreadyExists);
|
||||
}
|
||||
keys.append(key);
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::cleanup()
|
||||
{
|
||||
delete existingSharedMemory;
|
||||
qDeleteAll(jail.begin(), jail.end());
|
||||
jail.clear();
|
||||
|
||||
for (int i = 0; i < keys.size(); ++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));
|
||||
}
|
||||
++seq;
|
||||
}
|
||||
|
||||
#if QT_CONFIG(posix_shm)
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#if QT_CONFIG(sysv_shm)
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
|
||||
void tst_QSharedMemory::remove(const QNativeIpcKey &key)
|
||||
{
|
||||
// On Unix, the shared memory might exist from a previously failed test
|
||||
// or segfault, remove it it does
|
||||
if (key.isEmpty())
|
||||
return;
|
||||
|
||||
switch (key.type()) {
|
||||
case QNativeIpcKey::Type::Windows:
|
||||
return;
|
||||
|
||||
case QNativeIpcKey::Type::PosixRealtime:
|
||||
#if QT_CONFIG(posix_shm)
|
||||
if (shm_unlink(QFile::encodeName(key.nativeKey()).constData()) == -1) {
|
||||
if (errno != ENOENT) {
|
||||
perror("shm_unlink");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
|
||||
case QNativeIpcKey::Type::SystemV:
|
||||
break;
|
||||
}
|
||||
|
||||
#if QT_CONFIG(sysv_shm)
|
||||
// ftok requires that an actual file exists somewhere
|
||||
QString fileName = key.nativeKey();
|
||||
if (!QFile::exists(fileName)) {
|
||||
//qDebug() << "exits failed";
|
||||
return;
|
||||
}
|
||||
|
||||
int unix_key = ftok(fileName.toLatin1().constData(), int(key.type()));
|
||||
if (-1 == unix_key) {
|
||||
perror("ftok");
|
||||
return;
|
||||
}
|
||||
|
||||
int id = shmget(unix_key, 0, 0600);
|
||||
if (-1 == id) {
|
||||
if (errno != ENOENT)
|
||||
perror("shmget");
|
||||
return;
|
||||
}
|
||||
|
||||
struct shmid_ds shmid_ds;
|
||||
if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) {
|
||||
perror("shmctl");
|
||||
return;
|
||||
}
|
||||
|
||||
QFile::remove(fileName);
|
||||
#endif // Q_OS_WIN
|
||||
}
|
||||
|
||||
/*!
|
||||
Tests the default values
|
||||
*/
|
||||
void tst_QSharedMemory::constructor()
|
||||
{
|
||||
QSharedMemory sm;
|
||||
QVERIFY(!sm.isAttached());
|
||||
QVERIFY(!sm.data());
|
||||
QCOMPARE(sm.nativeKey(), QString());
|
||||
QCOMPARE(sm.nativeIpcKey(), QNativeIpcKey());
|
||||
QCOMPARE(sm.size(), 0);
|
||||
QCOMPARE(sm.error(), QSharedMemory::NoError);
|
||||
QCOMPARE(sm.errorString(), QString());
|
||||
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
QCOMPARE(sm.key(), QString());
|
||||
QT_WARNING_POP
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::nativeKey_data()
|
||||
{
|
||||
QTest::addColumn<QString>("constructorKey");
|
||||
QTest::addColumn<QString>("setKey");
|
||||
QTest::addColumn<QString>("setNativeKey"); // only used in the legacyKey test
|
||||
|
||||
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::nativeKey()
|
||||
{
|
||||
QFETCH(QString, constructorKey);
|
||||
QFETCH(QString, setKey);
|
||||
QFETCH(QString, setNativeKey);
|
||||
|
||||
QNativeIpcKey constructorIpcKey = platformSafeKey(constructorKey);
|
||||
QNativeIpcKey setIpcKey = platformSafeKey(setKey);
|
||||
|
||||
QSharedMemory sm(constructorIpcKey);
|
||||
QCOMPARE(sm.nativeIpcKey(), constructorIpcKey);
|
||||
QCOMPARE(sm.nativeKey(), constructorIpcKey.nativeKey());
|
||||
sm.setNativeKey(setIpcKey);
|
||||
QCOMPARE(sm.nativeIpcKey(), setIpcKey);
|
||||
QCOMPARE(sm.nativeKey(), setIpcKey.nativeKey());
|
||||
|
||||
QCOMPARE(sm.isAttached(), false);
|
||||
|
||||
QCOMPARE(sm.error(), QSharedMemory::NoError);
|
||||
QCOMPARE(sm.errorString(), QString());
|
||||
QVERIFY(!sm.data());
|
||||
QCOMPARE(sm.size(), 0);
|
||||
|
||||
QCOMPARE(sm.detach(), false);
|
||||
|
||||
// change the key type
|
||||
QNativeIpcKey::Type nextKeyType = IpcTestCommon::nextKeyType(setIpcKey.type());
|
||||
if (nextKeyType != setIpcKey.type()) {
|
||||
QNativeIpcKey setIpcKey2 = QSharedMemory::platformSafeKey(setKey, nextKeyType);
|
||||
sm.setNativeKey(setIpcKey2);
|
||||
|
||||
QCOMPARE(sm.nativeIpcKey(), setIpcKey2);
|
||||
QCOMPARE(sm.nativeKey(), setIpcKey2.nativeKey());
|
||||
|
||||
QCOMPARE(sm.isAttached(), false);
|
||||
|
||||
QCOMPARE(sm.error(), QSharedMemory::NoError);
|
||||
QCOMPARE(sm.errorString(), QString());
|
||||
QVERIFY(!sm.data());
|
||||
QCOMPARE(sm.size(), 0);
|
||||
|
||||
QCOMPARE(sm.detach(), false);
|
||||
}
|
||||
}
|
||||
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
void tst_QSharedMemory::legacyKey()
|
||||
{
|
||||
QFETCH(QString, constructorKey);
|
||||
QFETCH(QString, setKey);
|
||||
QFETCH(QString, setNativeKey);
|
||||
|
||||
#ifdef Q_OS_QNX
|
||||
QSKIP("The legacy native key type is incorrectly set on QNX");
|
||||
#endif
|
||||
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);
|
||||
QCOMPARE(sm.errorString(), QString());
|
||||
QVERIFY(!sm.data());
|
||||
QCOMPARE(sm.size(), 0);
|
||||
|
||||
QCOMPARE(sm.detach(), false);
|
||||
}
|
||||
QT_WARNING_POP
|
||||
|
||||
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("existing") << QString("existing") << 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);
|
||||
|
||||
QNativeIpcKey nativeKey = rememberKey(key);
|
||||
QSharedMemory sm(nativeKey);
|
||||
QCOMPARE(sm.create(size), canCreate);
|
||||
if (sm.error() != error)
|
||||
qDebug() << sm.errorString();
|
||||
QCOMPARE(sm.nativeIpcKey(), nativeKey);
|
||||
if (canCreate) {
|
||||
QCOMPARE(sm.errorString(), QString());
|
||||
QVERIFY(sm.data() != 0);
|
||||
QVERIFY(sm.size() != 0);
|
||||
} else {
|
||||
QVERIFY(!sm.data());
|
||||
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") << QString() << false << QSharedMemory::KeyError;
|
||||
QTest::newRow("doesntexists") << QString("doesntexist") << false << QSharedMemory::NotFound;
|
||||
|
||||
QTest::newRow("existing") << QString("existing") << true << QSharedMemory::NoError;
|
||||
}
|
||||
|
||||
/*!
|
||||
Basic attach/detach testing
|
||||
*/
|
||||
void tst_QSharedMemory::attach()
|
||||
{
|
||||
QFETCH(QString, key);
|
||||
QFETCH(bool, exists);
|
||||
QFETCH(QSharedMemory::SharedMemoryError, error);
|
||||
|
||||
QNativeIpcKey nativeKey = platformSafeKey(key);
|
||||
QSharedMemory sm(nativeKey);
|
||||
QCOMPARE(sm.attach(), exists);
|
||||
QCOMPARE(sm.isAttached(), exists);
|
||||
QCOMPARE(sm.error(), error);
|
||||
QCOMPARE(sm.nativeIpcKey(), nativeKey);
|
||||
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());
|
||||
} else {
|
||||
QVERIFY(!sm.data());
|
||||
QCOMPARE(sm.size(), 0);
|
||||
QVERIFY(sm.errorString() != QString());
|
||||
QVERIFY(!sm.detach());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::changeKeyType()
|
||||
{
|
||||
QFETCH(QString, key);
|
||||
QFETCH(bool, exists);
|
||||
QFETCH(QSharedMemory::SharedMemoryError, error);
|
||||
|
||||
QNativeIpcKey nativeKey = platformSafeKey(key);
|
||||
QNativeIpcKey::Type nextKeyType = IpcTestCommon::nextKeyType(nativeKey.type());
|
||||
if (nextKeyType == nativeKey.type())
|
||||
QSKIP("System only supports one key type");
|
||||
// qDebug() << "Changing from" << nativeKey.type() << "to" << nextKeyType;
|
||||
|
||||
QSharedMemory sm(nativeKey);
|
||||
QCOMPARE(sm.attach(), exists);
|
||||
QCOMPARE(sm.error(), error);
|
||||
|
||||
QNativeIpcKey nextKey =
|
||||
QSharedMemory::platformSafeKey(mangleKey(key), nextKeyType);
|
||||
sm.setNativeKey(nextKey);
|
||||
QCOMPARE(sm.isAttached(), false);
|
||||
QVERIFY(!sm.attach());
|
||||
|
||||
if (exists)
|
||||
QCOMPARE(sm.error(), QSharedMemory::NotFound);
|
||||
else
|
||||
QCOMPARE(sm.error(), error);
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::lock()
|
||||
{
|
||||
QSharedMemory shm;
|
||||
QVERIFY(!shm.lock());
|
||||
QCOMPARE(shm.error(), QSharedMemory::LockError);
|
||||
|
||||
shm.setNativeKey(rememberKey(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.
|
||||
void tst_QSharedMemory::removeWhileAttached()
|
||||
{
|
||||
rememberKey("one");
|
||||
|
||||
// attach 1
|
||||
QNativeIpcKey keyOne = platformSafeKey("one");
|
||||
QSharedMemory *smOne = new QSharedMemory(keyOne);
|
||||
QVERIFY(smOne->create(1024));
|
||||
QVERIFY(smOne->isAttached());
|
||||
|
||||
// attach 2
|
||||
QSharedMemory *smTwo = new QSharedMemory(platformSafeKey("one"));
|
||||
QVERIFY(smTwo->attach());
|
||||
QVERIFY(smTwo->isAttached());
|
||||
|
||||
// detach 1 and remove, remove one first to catch another error.
|
||||
delete smOne;
|
||||
delete smTwo;
|
||||
|
||||
if (keyOne.type() == QNativeIpcKey::Type::PosixRealtime) {
|
||||
// POSIX IPC doesn't guarantee that the shared memory is removed
|
||||
remove(keyOne);
|
||||
}
|
||||
|
||||
// three shouldn't be able to attach
|
||||
QSharedMemory smThree(keyOne);
|
||||
QVERIFY(!smThree.attach());
|
||||
QCOMPARE(smThree.error(), QSharedMemory::NotFound);
|
||||
}
|
||||
|
||||
/*!
|
||||
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.
|
||||
*/
|
||||
void tst_QSharedMemory::readOnly()
|
||||
{
|
||||
#if !QT_CONFIG(process)
|
||||
QSKIP("No qprocess support", SkipAll);
|
||||
#elif defined(Q_OS_MACOS)
|
||||
QSKIP("QTBUG-59936: Times out on macOS", SkipAll);
|
||||
#elif defined(Q_OS_WIN)
|
||||
QSKIP("This test opens a crash dialog on Windows.");
|
||||
#elif defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
|
||||
QSKIP("ASan prevents the crash this test is looking for.", SkipAll);
|
||||
#else
|
||||
QNativeIpcKey key = rememberKey("readonly_segfault");
|
||||
|
||||
// ### on windows disable the popup somehow
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
p.start(m_helperBinary, { "readonly_segfault", key.toString() });
|
||||
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 = QLatin1String("maxmemorytest_") + QString::number(count++);
|
||||
QNativeIpcKey nativeKey = rememberKey(key);
|
||||
QSharedMemory *sm = new QSharedMemory(nativeKey);
|
||||
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->nativeIpcKey(), nativeKey);
|
||||
QCOMPARE(sm->size(), 0);
|
||||
QVERIFY(!sm->data());
|
||||
if (sm->error() != QSharedMemory::OutOfResources)
|
||||
qDebug() << sm->error() << sm->errorString();
|
||||
// ### Linux won't 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.
|
||||
*/
|
||||
void tst_QSharedMemory::attachTooMuch()
|
||||
{
|
||||
QSKIP("disabled");
|
||||
|
||||
QSharedMemory government(rememberKey("government"));
|
||||
QVERIFY(government.create(1024));
|
||||
while (true) {
|
||||
QSharedMemory *war = new QSharedMemory(government.nativeIpcKey());
|
||||
QVERIFY(war);
|
||||
jail.append(war);
|
||||
if (!war->attach()) {
|
||||
QVERIFY(!war->isAttached());
|
||||
QCOMPARE(war->nativeIpcKey(), government.nativeIpcKey());
|
||||
QCOMPARE(war->size(), 0);
|
||||
QVERIFY(!war->data());
|
||||
QCOMPARE(war->error(), QSharedMemory::OutOfResources);
|
||||
QVERIFY(war->errorString() != QString());
|
||||
QVERIFY(!war->detach());
|
||||
break;
|
||||
} else {
|
||||
QVERIFY(war->isAttached());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(platformSafeKey("market"));
|
||||
QSharedMemory consumer(platformSafeKey("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
|
||||
QVERIFY(put != get);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
put[i] = 'Q';
|
||||
QCOMPARE(get[i], 'Q');
|
||||
}
|
||||
QVERIFY(consumer.detach());
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::simpleDoubleProducerConsumer()
|
||||
{
|
||||
QNativeIpcKey nativeKey = rememberKey(QLatin1String("market"));
|
||||
QSharedMemory producer(nativeKey);
|
||||
int size = 512;
|
||||
QVERIFY(producer.create(size));
|
||||
QVERIFY(producer.detach());
|
||||
|
||||
if (nativeKey.type() == QNativeIpcKey::Type::PosixRealtime) {
|
||||
// POSIX IPC doesn't guarantee that the shared memory is removed
|
||||
remove(nativeKey);
|
||||
}
|
||||
|
||||
QVERIFY(producer.create(size));
|
||||
|
||||
{
|
||||
QSharedMemory consumer(nativeKey);
|
||||
QVERIFY(consumer.attach());
|
||||
}
|
||||
}
|
||||
|
||||
class Consumer : public QThread
|
||||
{
|
||||
public:
|
||||
QNativeIpcKey nativeKey;
|
||||
Consumer(const QNativeIpcKey &nativeKey) : nativeKey(nativeKey) {}
|
||||
|
||||
void run() override
|
||||
{
|
||||
QSharedMemory consumer(nativeKey);
|
||||
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:
|
||||
Producer(const QNativeIpcKey &nativeKey) : producer(nativeKey)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
|
||||
char *memory = (char*)producer.data();
|
||||
memory[1] = '0';
|
||||
QElapsedTimer 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());
|
||||
|
||||
}
|
||||
|
||||
QSharedMemory producer;
|
||||
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);
|
||||
QNativeIpcKey nativeKey = rememberKey(QLatin1String("market"));
|
||||
|
||||
Producer p(nativeKey);
|
||||
QVERIFY(p.producer.isAttached());
|
||||
if (producerIsThread)
|
||||
p.start();
|
||||
|
||||
QList<Consumer*> consumers;
|
||||
for (int i = 0; i < threads; ++i) {
|
||||
consumers.append(new Consumer(nativeKey));
|
||||
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()
|
||||
{
|
||||
#if QT_CONFIG(process)
|
||||
QTest::addColumn<int>("processes");
|
||||
int tries = 5;
|
||||
for (int i = 0; i < tries; ++i) {
|
||||
QTest::newRow("1 process") << 1;
|
||||
QTest::newRow("5 processes") << 5;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Create external processes that produce and consume.
|
||||
*/
|
||||
void tst_QSharedMemory::simpleProcessProducerConsumer()
|
||||
{
|
||||
#if !QT_CONFIG(process)
|
||||
QSKIP("No qprocess support", SkipAll);
|
||||
#else
|
||||
QFETCH(int, processes);
|
||||
|
||||
QSKIP("This test is unstable: QTBUG-25655");
|
||||
|
||||
QNativeIpcKey nativeKey = rememberKey("market");
|
||||
|
||||
QProcess producer;
|
||||
producer.start(m_helperBinary, { "producer", nativeKey.toString() });
|
||||
QVERIFY2(producer.waitForStarted(), "Could not start helper binary");
|
||||
QVERIFY2(producer.waitForReadyRead(), "Helper process failed to create shared memory segment: " +
|
||||
producer.readAllStandardError());
|
||||
|
||||
QList<QProcess*> consumers;
|
||||
unsigned int failedProcesses = 0;
|
||||
QStringList consumerArguments = { "consumer", nativeKey.toString() };
|
||||
for (int i = 0; i < processes; ++i) {
|
||||
QProcess *p = new QProcess;
|
||||
p->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
p->start(m_helperBinary, consumerArguments);
|
||||
if (p->waitForStarted(2000))
|
||||
consumers.append(p);
|
||||
else
|
||||
++failedProcesses;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
// tell the producer to exit now
|
||||
producer.write("", 1);
|
||||
producer.waitForBytesWritten();
|
||||
QVERIFY(producer.waitForFinished(5000));
|
||||
#endif
|
||||
}
|
||||
|
||||
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");
|
||||
QTest::newRow("Noël -> Nol") << QString::fromUtf8("N\xc3\xabl") << QString("Nol");
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::uniqueKey()
|
||||
{
|
||||
QFETCH(QString, key1);
|
||||
QFETCH(QString, key2);
|
||||
|
||||
QSharedMemory sm1(platformSafeKey(key1));
|
||||
QSharedMemory sm2(platformSafeKey(key2));
|
||||
|
||||
bool setEqual = (key1 == key2);
|
||||
bool keyEqual = (sm1.nativeIpcKey() == sm2.nativeIpcKey());
|
||||
bool nativeEqual = (sm1.nativeKey() == sm2.nativeKey());
|
||||
|
||||
QCOMPARE(keyEqual, setEqual);
|
||||
QCOMPARE(nativeEqual, setEqual);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QSharedMemory)
|
||||
#include "tst_qsharedmemory.moc"
|
||||
|
16
tests/auto/corelib/ipc/qsystemsemaphore/CMakeLists.txt
Normal file
16
tests/auto/corelib/ipc/qsystemsemaphore/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qsystemsemaphore Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qsystemsemaphore
|
||||
SOURCES
|
||||
tst_qsystemsemaphore.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(acquirerelease)
|
||||
if(QT_FEATURE_process)
|
||||
add_dependencies(tst_qsystemsemaphore acquirerelease_helper)
|
||||
endif()
|
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## acquirerelease_helper Binary:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test_helper(acquirerelease_helper
|
||||
SOURCES
|
||||
main.cpp
|
||||
LIBRARIES
|
||||
Qt::Test
|
||||
)
|
@ -0,0 +1,80 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
#include <QSystemSemaphore>
|
||||
|
||||
int acquire(const QNativeIpcKey &key, int count = 1)
|
||||
{
|
||||
QSystemSemaphore sem(key);
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (!sem.acquire()) {
|
||||
qWarning() << "Could not acquire" << sem.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
qDebug("done aquiring");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int release(const QNativeIpcKey &key)
|
||||
{
|
||||
QSystemSemaphore sem(key);
|
||||
if (!sem.release()) {
|
||||
qWarning() << "Could not release" << sem.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
qDebug("done releasing");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int acquirerelease(const QNativeIpcKey &key)
|
||||
{
|
||||
QSystemSemaphore sem(key);
|
||||
if (!sem.acquire()) {
|
||||
qWarning() << "Could not acquire" << sem.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!sem.release()) {
|
||||
qWarning() << "Could not release" << sem.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
QStringList arguments = app.arguments();
|
||||
// binary name is not used here
|
||||
arguments.takeFirst();
|
||||
if (arguments.size() < 2) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <acquire|release|acquirerelease> <key> [other args...]\n",
|
||||
argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QString function = arguments.takeFirst();
|
||||
QNativeIpcKey key = QNativeIpcKey::fromString(arguments.takeFirst());
|
||||
if (function == QLatin1String("acquire")) {
|
||||
int count = 1;
|
||||
bool ok = true;
|
||||
if (arguments.size())
|
||||
count = arguments.takeFirst().toInt(&ok);
|
||||
if (!ok)
|
||||
count = 1;
|
||||
return acquire(key, count);
|
||||
} else if (function == QLatin1String("release")) {
|
||||
return release(key);
|
||||
} else if (function == QLatin1String("acquirerelease")) {
|
||||
return acquirerelease(key);
|
||||
} else {
|
||||
qWarning() << "Unknown function" << function;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
368
tests/auto/corelib/ipc/qsystemsemaphore/tst_qsystemsemaphore.cpp
Normal file
368
tests/auto/corelib/ipc/qsystemsemaphore/tst_qsystemsemaphore.cpp
Normal file
@ -0,0 +1,368 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// Copyright (C) 2022 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
#if QT_CONFIG(process)
|
||||
#include <QProcess>
|
||||
#endif
|
||||
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QSystemSemaphore>
|
||||
#include <QtCore/QTemporaryDir>
|
||||
|
||||
#include "../ipctestcommon.h"
|
||||
|
||||
#define HELPERWAITTIME 10000
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
class tst_QSystemSemaphore : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QSystemSemaphore();
|
||||
|
||||
QString mangleKey(QStringView key)
|
||||
{
|
||||
if (key.isEmpty())
|
||||
return key.toString();
|
||||
return u"tstsyssem_%1-%2_%3"_s.arg(QCoreApplication::applicationPid())
|
||||
.arg(seq).arg(key);
|
||||
}
|
||||
|
||||
QNativeIpcKey platformSafeKey(const QString &key)
|
||||
{
|
||||
QFETCH_GLOBAL(QNativeIpcKey::Type, keyType);
|
||||
return QSystemSemaphore::platformSafeKey(mangleKey(key), keyType);
|
||||
}
|
||||
|
||||
public Q_SLOTS:
|
||||
void initTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
private slots:
|
||||
void nativeKey_data();
|
||||
void nativeKey();
|
||||
void legacyKey_data() { nativeKey_data(); }
|
||||
void legacyKey();
|
||||
|
||||
void changeKeyType();
|
||||
void basicacquire();
|
||||
void complexacquire();
|
||||
void release();
|
||||
void twoSemaphores();
|
||||
|
||||
void basicProcesses();
|
||||
|
||||
void processes_data();
|
||||
void processes();
|
||||
|
||||
void undo();
|
||||
void initialValue();
|
||||
|
||||
private:
|
||||
int seq = 0;
|
||||
QSystemSemaphore *existingLock;
|
||||
|
||||
const QString m_helperBinary;
|
||||
};
|
||||
|
||||
tst_QSystemSemaphore::tst_QSystemSemaphore()
|
||||
: m_helperBinary("./acquirerelease_helper")
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::initTestCase()
|
||||
{
|
||||
IpcTestCommon::addGlobalTestRows<QSystemSemaphore>();
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::init()
|
||||
{
|
||||
QNativeIpcKey key = platformSafeKey("existing");
|
||||
existingLock = new QSystemSemaphore(key, 1, QSystemSemaphore::Create);
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::cleanup()
|
||||
{
|
||||
delete existingLock;
|
||||
++seq;
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::nativeKey_data()
|
||||
{
|
||||
QTest::addColumn<QString>("constructorKey");
|
||||
QTest::addColumn<QString>("setKey");
|
||||
|
||||
QTest::newRow("null, null") << QString() << QString();
|
||||
QTest::newRow("null, one") << QString() << QString("one");
|
||||
QTest::newRow("one, two") << QString("one") << QString("two");
|
||||
}
|
||||
|
||||
/*!
|
||||
Basic key testing
|
||||
*/
|
||||
void tst_QSystemSemaphore::nativeKey()
|
||||
{
|
||||
QFETCH(QString, constructorKey);
|
||||
QFETCH(QString, setKey);
|
||||
QNativeIpcKey constructorIpcKey = platformSafeKey(constructorKey);
|
||||
QNativeIpcKey setIpcKey = platformSafeKey(setKey);
|
||||
|
||||
QSystemSemaphore sem(constructorIpcKey);
|
||||
QCOMPARE(sem.nativeIpcKey(), constructorIpcKey);
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QCOMPARE(sem.errorString(), QString());
|
||||
|
||||
sem.setNativeKey(setIpcKey);
|
||||
QCOMPARE(sem.nativeIpcKey(), setIpcKey);
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QCOMPARE(sem.errorString(), QString());
|
||||
|
||||
// change the key type
|
||||
QNativeIpcKey::Type nextKeyType = IpcTestCommon::nextKeyType(setIpcKey.type());
|
||||
if (nextKeyType != setIpcKey.type()) {
|
||||
QNativeIpcKey setIpcKey2 = QSystemSemaphore::platformSafeKey(setKey, nextKeyType);
|
||||
sem.setNativeKey(setIpcKey2);
|
||||
QCOMPARE(sem.nativeIpcKey(), setIpcKey2);
|
||||
}
|
||||
}
|
||||
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
void tst_QSystemSemaphore::legacyKey()
|
||||
{
|
||||
QFETCH(QString, constructorKey);
|
||||
QFETCH(QString, setKey);
|
||||
|
||||
QSystemSemaphore sem(constructorKey);
|
||||
QCOMPARE(sem.key(), constructorKey);
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QCOMPARE(sem.errorString(), QString());
|
||||
|
||||
sem.setKey(setKey);
|
||||
QCOMPARE(sem.key(), setKey);
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QCOMPARE(sem.errorString(), QString());
|
||||
}
|
||||
QT_WARNING_POP
|
||||
|
||||
void tst_QSystemSemaphore::changeKeyType()
|
||||
{
|
||||
QString keyName = "changeKeyType";
|
||||
QNativeIpcKey key = platformSafeKey(keyName);
|
||||
QNativeIpcKey otherKey =
|
||||
QSystemSemaphore::platformSafeKey(mangleKey(keyName), IpcTestCommon::nextKeyType(key.type()));
|
||||
if (key == otherKey)
|
||||
QSKIP("System only supports one key type");
|
||||
|
||||
QSystemSemaphore sem1(key, 1, QSystemSemaphore::Create);
|
||||
QSystemSemaphore sem2(otherKey);
|
||||
sem1.setNativeKey(otherKey);
|
||||
sem2.setNativeKey(key);
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::basicacquire()
|
||||
{
|
||||
QNativeIpcKey key = platformSafeKey("basicacquire");
|
||||
QSystemSemaphore sem(key, 1, QSystemSemaphore::Create);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QCOMPARE(sem.errorString(), QString());
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::complexacquire()
|
||||
{
|
||||
QNativeIpcKey key = platformSafeKey("complexacquire");
|
||||
QSystemSemaphore sem(key, 2, QSystemSemaphore::Create);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QCOMPARE(sem.errorString(), QString());
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::release()
|
||||
{
|
||||
QNativeIpcKey key = platformSafeKey("release");
|
||||
QSystemSemaphore sem(key, 0, QSystemSemaphore::Create);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QCOMPARE(sem.errorString(), QString());
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::twoSemaphores()
|
||||
{
|
||||
QNativeIpcKey key = platformSafeKey("twoSemaphores");
|
||||
QSystemSemaphore sem1(key, 1, QSystemSemaphore::Create);
|
||||
QSystemSemaphore sem2(key);
|
||||
QVERIFY(sem1.acquire());
|
||||
QVERIFY(sem2.release());
|
||||
QVERIFY(sem1.acquire());
|
||||
QVERIFY(sem2.release());
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::basicProcesses()
|
||||
{
|
||||
#if !QT_CONFIG(process)
|
||||
QSKIP("No qprocess support", SkipAll);
|
||||
#else
|
||||
QNativeIpcKey key = platformSafeKey("store");
|
||||
QSystemSemaphore sem(key, 0, QSystemSemaphore::Create);
|
||||
|
||||
QProcess acquire;
|
||||
acquire.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
|
||||
QProcess release;
|
||||
release.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
|
||||
acquire.start(m_helperBinary, { "acquire", key.toString() });
|
||||
QVERIFY2(acquire.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
QCOMPARE(acquire.state(), QProcess::Running);
|
||||
acquire.kill();
|
||||
release.start(m_helperBinary, { "release", key.toString() });
|
||||
QVERIFY2(release.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
release.waitForFinished(HELPERWAITTIME);
|
||||
QCOMPARE(acquire.state(), QProcess::NotRunning);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::processes_data()
|
||||
{
|
||||
QTest::addColumn<int>("processes");
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
QTest::addRow("1 process (%d)", i) << 1;
|
||||
QTest::addRow("3 process (%d)", i) << 3;
|
||||
QTest::addRow("10 process (%d)", i) << 10;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::processes()
|
||||
{
|
||||
#if !QT_CONFIG(process)
|
||||
QSKIP("No qprocess support", SkipAll);
|
||||
#else
|
||||
QNativeIpcKey key = platformSafeKey("store");
|
||||
QSystemSemaphore sem(key, 1, QSystemSemaphore::Create);
|
||||
|
||||
QFETCH(int, processes);
|
||||
QList<QString> scripts(processes, "acquirerelease");
|
||||
|
||||
QList<QProcess*> consumers;
|
||||
for (int i = 0; i < scripts.size(); ++i) {
|
||||
QProcess *p = new QProcess;
|
||||
p->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
consumers.append(p);
|
||||
p->start(m_helperBinary, { scripts.at(i), key.toString() });
|
||||
}
|
||||
|
||||
while (!consumers.isEmpty()) {
|
||||
consumers.first()->waitForFinished();
|
||||
QCOMPARE(consumers.first()->exitStatus(), QProcess::NormalExit);
|
||||
QCOMPARE(consumers.first()->exitCode(), 0);
|
||||
delete consumers.takeFirst();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::undo()
|
||||
{
|
||||
#if !QT_CONFIG(process)
|
||||
QSKIP("No qprocess support", SkipAll);
|
||||
#else
|
||||
QNativeIpcKey key = platformSafeKey("store");
|
||||
switch (key.type()) {
|
||||
case QNativeIpcKey::Type::PosixRealtime:
|
||||
case QNativeIpcKey::Type::Windows:
|
||||
QSKIP("This test only checks a System V behavior.");
|
||||
|
||||
case QNativeIpcKey::Type::SystemV:
|
||||
break;
|
||||
}
|
||||
|
||||
QSystemSemaphore sem(key, 1, QSystemSemaphore::Create);
|
||||
|
||||
QStringList acquireArguments = { "acquire", key.toString() };
|
||||
QProcess acquire;
|
||||
acquire.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
acquire.start(m_helperBinary, acquireArguments);
|
||||
QVERIFY2(acquire.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
QVERIFY(acquire.state()== QProcess::NotRunning);
|
||||
|
||||
// At process exit the kernel should auto undo
|
||||
|
||||
acquire.start(m_helperBinary, acquireArguments);
|
||||
QVERIFY2(acquire.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
QVERIFY(acquire.state()== QProcess::NotRunning);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::initialValue()
|
||||
{
|
||||
#if !QT_CONFIG(process)
|
||||
QSKIP("No qprocess support", SkipAll);
|
||||
#else
|
||||
QNativeIpcKey key = platformSafeKey("store");
|
||||
QSystemSemaphore sem(key, 1, QSystemSemaphore::Create);
|
||||
|
||||
QStringList acquireArguments = { "acquire", key.toString() };
|
||||
QStringList releaseArguments = { "release", key.toString() };
|
||||
QProcess acquire;
|
||||
acquire.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
|
||||
QProcess release;
|
||||
release.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
|
||||
acquire.start(m_helperBinary, acquireArguments);
|
||||
QVERIFY2(acquire.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
QVERIFY(acquire.state()== QProcess::NotRunning);
|
||||
|
||||
acquire.start(m_helperBinary, acquireArguments << QLatin1String("2"));
|
||||
QVERIFY2(acquire.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
QVERIFY(acquire.state()== QProcess::Running);
|
||||
acquire.kill();
|
||||
|
||||
release.start(m_helperBinary, releaseArguments);
|
||||
QVERIFY2(release.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
release.waitForFinished(HELPERWAITTIME);
|
||||
QVERIFY(acquire.state()== QProcess::NotRunning);
|
||||
#endif
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QSystemSemaphore)
|
||||
#include "tst_qsystemsemaphore.moc"
|
||||
|
Reference in New Issue
Block a user