chore: update class interfaces to support Qt5 and Qt6 while dropping support for Qt4

all tests checked against RabbitMQ 3.11.13 with Erlang 25.3 with following kits:
Qt 5.6.3 32Bit MSVC2017
Qt 5.15.2 32Bit Clang 15.0.1
Qt 5.15.2 32,64Bit MSVC2019
Qt 5.15.2 32Bit MSVC2022
Qt 6.5.0 64Bit MinGW 11.2.0
Qt 6.5.0 64Bit MSVC2019
Qt 6.5.0 64Bit MSVC2022
This commit is contained in:
hensels 2023-05-08 12:07:45 +02:00
parent b5c660a1ac
commit 6cc6bbcdfa
16 changed files with 202 additions and 316 deletions

View File

@ -1,8 +1,8 @@
language: cpp
cache: apt
env:
- QT_SELECT=qt4
- QT_SELECT=qt5
- QT_SELECT=qt6
services:
- rabbitmq
before_install:

View File

@ -3,7 +3,7 @@
QAMQP
=============
A Qt4/Qt5 implementation of AMQP 0.9.1, focusing primarily on RabbitMQ support.
A Qt5/Qt6 implementation of AMQP 0.9.1, focusing primarily on RabbitMQ support.
Usage
------------
@ -16,8 +16,16 @@ Usage
Documentation
------------
Coming soon!
Tests checked and integrated against rabbitmq 3.11 (August 1, 2022)
Qt5.6.3 (MSVC2017) 32Bit
Qt5.15.2 (MSVC2019, MSVC2022, MinGW, Clang) 32 and 64Bit
Qt6.5 (MSVC2019, MSVC2022) 64Bit
A good starting point is:
* running a local RabbitMQ,
* browse to http://localhost:15672/#/queues (guest/guest)
* Start the "receive" sample and see in your browser the "hello" queue appear
* publish a message there
AMQP Support
------------

View File

@ -1,4 +1,4 @@
QAMQP_VERSION = 0.5.0
QAMQP_VERSION = 0.6.0
isEmpty(QAMQP_LIBRARY_TYPE) {
QAMQP_LIBRARY_TYPE = shared
@ -7,6 +7,7 @@ isEmpty(QAMQP_LIBRARY_TYPE) {
QT += network
QAMQP_INCLUDEPATH = $${PWD}/src
QAMQP_LIBS = -lqamqp
CONFIG(debug, debug|release){
QAMQP_LIBS = -lqamqpd
}
@ -14,7 +15,6 @@ contains(QAMQP_LIBRARY_TYPE, staticlib) {
DEFINES += QAMQP_STATIC
} else {
DEFINES += QAMQP_SHARED
win32:QAMQP_LIBS = -lqamqp0
}
isEmpty(PREFIX) {

View File

@ -62,10 +62,25 @@ void QAmqpClientPrivate::initSocket()
QObject::connect(socket, SIGNAL(connected()), q, SLOT(_q_socketConnected()));
QObject::connect(socket, SIGNAL(disconnected()), q, SLOT(_q_socketDisconnected()));
QObject::connect(socket, SIGNAL(readyRead()), q, SLOT(_q_readyRead()));
QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
q, SLOT(_q_socketError(QAbstractSocket::SocketError)));
QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
q, SIGNAL(socketError(QAbstractSocket::SocketError)));
#if QT_VERSION >= 0x060000
QObject::connect(socket,
SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
q,
SLOT(_q_socketError(QAbstractSocket::SocketError)));
QObject::connect(socket,
SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
q,
SIGNAL(socketErrorOccurred(QAbstractSocket::SocketError)));
#else
QObject::connect(socket,
SIGNAL(error(QAbstractSocket::SocketError)),
q,
SLOT(_q_socketError(QAbstractSocket::SocketError)));
QObject::connect(socket,
SIGNAL(error(QAbstractSocket::SocketError)),
q,
SIGNAL(socketErrorOccurred(QAbstractSocket::SocketError)));
#endif
QObject::connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
q, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)));
QObject::connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
@ -107,11 +122,7 @@ void QAmqpClientPrivate::setPassword(const QString &password)
void QAmqpClientPrivate::parseConnectionString(const QString &uri)
{
#if QT_VERSION > 0x040801
QUrl connectionString = QUrl::fromUserInput(uri);
#else
QUrl connectionString(uri, QUrl::TolerantMode);
#endif
if (connectionString.scheme() != AMQP_SCHEME &&
connectionString.scheme() != AMQP_SSL_SCHEME) {
@ -126,15 +137,9 @@ void QAmqpClientPrivate::parseConnectionString(const QString &uri)
QString vhost = connectionString.path();
if (vhost.startsWith("/") && vhost.size() > 1)
vhost = vhost.mid(1);
#if QT_VERSION <= 0x050200
virtualHost = QUrl::fromPercentEncoding(vhost.toUtf8());
setPassword(QUrl::fromPercentEncoding(connectionString.password().toUtf8()));
setUsername(QUrl::fromPercentEncoding(connectionString.userName().toUtf8()));
#else
virtualHost = vhost;
setPassword(connectionString.password());
setUsername(connectionString.userName());
#endif
}
void QAmqpClientPrivate::_q_connect()
@ -531,7 +536,11 @@ void QAmqpClientPrivate::startOk()
clientProperties["version"] = QString(QAMQP_VERSION);
clientProperties["platform"] = QString("Qt %1").arg(qVersion());
clientProperties["product"] = QString("QAMQP");
#if QT_VERSION >= 0x060000
clientProperties.insert(customProperties);
#else
clientProperties.unite(customProperties);
#endif
stream << clientProperties;
authenticator->write(stream);

View File

@ -115,7 +115,7 @@ Q_SIGNALS:
void disconnected();
void heartbeat();
void error(QAMQP::Error error);
void socketError(QAbstractSocket::SocketError error);
void socketErrorOccurred(QAbstractSocket::SocketError error);
void socketStateChanged(QAbstractSocket::SocketState state);
void sslErrors(const QList<QSslError> &errors);

View File

@ -31,7 +31,6 @@ class QAMQP_EXPORT QAmqpExchange : public QAmqpChannel
Q_OBJECT
Q_PROPERTY(QString type READ type CONSTANT)
Q_PROPERTY(ExchangeOptions options READ options CONSTANT)
Q_ENUMS(ExchangeOptions)
public:
virtual ~QAmqpExchange();
@ -68,6 +67,7 @@ public:
};
Q_DECLARE_FLAGS(ExchangeOptions, ExchangeOption)
ExchangeOptions options() const;
Q_ENUM(ExchangeOptions)
bool isDeclared() const;
@ -83,11 +83,11 @@ Q_SIGNALS:
public Q_SLOTS:
// AMQP Exchange
void declare(ExchangeType type = Direct,
ExchangeOptions options = NoOptions,
void declare(QAmqpExchange::ExchangeType type = Direct,
QAmqpExchange::ExchangeOptions options = NoOptions,
const QAmqpTable &args = QAmqpTable());
void declare(const QString &type,
ExchangeOptions options = NoOptions,
QAmqpExchange::ExchangeOptions options = NoOptions,
const QAmqpTable &args = QAmqpTable());
void remove(int options = roIfUnused|roNoWait);

View File

@ -1,6 +1,7 @@
#include <QDateTime>
#include <QList>
#include <QDebug>
#include <QIODevice>
#include <QList>
#include "qamqptable.h"
#include "qamqpglobal.h"
@ -208,7 +209,11 @@ QVariant QAmqpFrame::readAmqpField(QDataStream &s, QAmqpMetaType::ValueType type
{
qulonglong tmp_value;
s >> tmp_value;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
return QDateTime::fromSecsSinceEpoch(tmp_value);
#else
return QDateTime::fromTime_t(tmp_value);
#endif
}
case QAmqpMetaType::Hash:
{
@ -262,7 +267,11 @@ void QAmqpFrame::writeAmqpField(QDataStream &s, QAmqpMetaType::ValueType type, c
}
break;
case QAmqpMetaType::Timestamp:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
s << qulonglong(value.toDateTime().toSecsSinceEpoch());
#else
s << qulonglong(value.toDateTime().toTime_t());
#endif
break;
case QAmqpMetaType::Hash:
{

View File

@ -47,7 +47,7 @@
#define AMQP_BASIC_APP_ID_FLAG (1 << 3)
#define AMQP_BASIC_CLUSTER_ID_FLAG (1 << 2)
#define QAMQP_VERSION "0.5.0"
#define QAMQP_VERSION "0.6.0"
#define AMQP_CONNECTION_FORCED 320
@ -61,7 +61,7 @@
# define QAMQP_EXPORT
#endif
#define qAmqpDebug if (qgetenv("QAMQP_DEBUG").isEmpty()); else qDebug
#define qAmqpDebug if (qEnvironmentVariableIsEmpty("QAMQP_DEBUG")); else qDebug
namespace QAmqpMetaType {

View File

@ -113,13 +113,6 @@ QHash<QString, QVariant> QAmqpMessage::headers() const
return d->headers;
}
#if QT_VERSION < 0x050000
bool QAmqpMessage::isDetached() const
{
return d && d->ref == 1;
}
#endif
uint qHash(const QAmqpMessage &message, uint seed)
{
Q_UNUSED(seed);

View File

@ -34,9 +34,7 @@ public:
QAmqpMessage &operator=(const QAmqpMessage &other);
~QAmqpMessage();
#if QT_VERSION >= 0x050000
inline void swap(QAmqpMessage &other) { qSwap(d, other.d); }
#endif
bool operator==(const QAmqpMessage &message) const;
inline bool operator!=(const QAmqpMessage &message) const { return !(operator==(message)); }
@ -80,22 +78,9 @@ private:
QSharedDataPointer<QAmqpMessagePrivate> d;
friend class QAmqpQueuePrivate;
friend class QAmqpQueue;
#if QT_VERSION < 0x050000
public:
typedef QSharedDataPointer<QAmqpMessagePrivate> DataPtr;
inline DataPtr &data_ptr() { return d; }
// internal
bool isDetached() const;
#endif
};
Q_DECLARE_METATYPE(QAmqpMessage::PropertyHash)
#if QT_VERSION < 0x050000
Q_DECLARE_TYPEINFO(QAmqpMessage, Q_MOVABLE_TYPE);
#endif
Q_DECLARE_SHARED(QAmqpMessage)
// NOTE: needed only for MSVC support, don't depend on this hash

View File

@ -32,12 +32,8 @@ class QAmqpQueuePrivate;
class QAMQP_EXPORT QAmqpQueue : public QAmqpChannel, public QQueue<QAmqpMessage>
{
Q_OBJECT
Q_ENUMS(QueueOptions)
Q_PROPERTY(int options READ options CONSTANT)
Q_PROPERTY(QString consumerTag READ consumerTag WRITE setConsumerTag)
Q_ENUMS(QueueOption)
Q_ENUMS(ConsumeOption)
Q_ENUMS(RemoveOption)
public:
enum QueueOption {
@ -49,7 +45,8 @@ public:
NoWait = 0x10
};
Q_DECLARE_FLAGS(QueueOptions, QueueOption)
int options() const;
Q_ENUM(QueueOption)
Q_ENUM(QueueOptions) int options() const;
enum ConsumeOption {
coNoLocal = 0x01,
@ -58,6 +55,7 @@ public:
coNoWait = 0x08
};
Q_DECLARE_FLAGS(ConsumeOptions, ConsumeOption)
Q_ENUM(ConsumeOption)
enum RemoveOption {
roForce = 0x0,
@ -66,6 +64,7 @@ public:
roNoWait = 0x04
};
Q_DECLARE_FLAGS(RemoveOptions, RemoveOption)
Q_ENUM(RemoveOption)
~QAmqpQueue();

View File

@ -2,6 +2,7 @@
#include <QDateTime>
#include <QDebug>
#include <QIODevice>
#include "qamqpframe_p.h"
#include "qamqptable.h"

View File

@ -64,16 +64,12 @@ HEADERS += \
$${INSTALL_HEADERS} \
$${PRIVATE_HEADERS}
SOURCES += \
qamqpauthenticator.cpp \
qamqpchannel.cpp \
qamqpchannelhash.cpp \
qamqpclient.cpp \
qamqpexchange.cpp \
qamqpframe.cpp \
qamqpmessage.cpp \
qamqpqueue.cpp \
qamqptable.cpp
SOURCES += $$files($$PWD/*.cpp)
DISTFILES += \
../.travis.yml \
../LICENSE \
../README.md
# install
headers.files = $${INSTALL_HEADERS}

View File

@ -179,7 +179,7 @@ void tst_QAMQPClient::tune()
client.connectToHost();
QVERIFY(waitForSignal(&client, SIGNAL(connected())));
QCOMPARE((int)client.channelMax(), 15);
QCOMPARE((int) client.channelMax(), 2047);
QCOMPARE((int)client.heartbeatDelay(), 600);
QCOMPARE((int)client.frameMax(), 5000);
@ -191,7 +191,7 @@ void tst_QAMQPClient::socketError()
{
QAmqpClient client;
client.connectToHost("amqp://127.0.0.1:56725/");
QVERIFY(waitForSignal(&client, SIGNAL(socketError(QAbstractSocket::SocketError))));
QVERIFY(waitForSignal(&client, SIGNAL(socketErrorOccurred(QAbstractSocket::SocketError))));
QCOMPARE(client.socketError(), QAbstractSocket::ConnectionRefusedError);
}
@ -208,10 +208,8 @@ void tst_QAMQPClient::validateUri_data()
<< "guest" << "guest" << "192.168.1.10" << quint16(5672) << "/";
QTest::newRow("standard") << "amqp://user:pass@host:10000/vhost"
<< "user" << "pass" << "host" << quint16(10000) << "vhost";
#if QT_VERSION >= 0x040806
QTest::newRow("urlencoded") << "amqp://user%61:%61pass@ho%61st:10000/v%2fhost"
<< "usera" << "apass" << "hoast" << quint16(10000) << "v/host";
#endif
QTest::newRow("empty") << "amqp://" << "" << "" << "" << quint16(AMQP_PORT) << "";
QTest::newRow("empty2") << "amqp://:@/" << "" << "" << "" << quint16(AMQP_PORT) << "/";
QTest::newRow("onlyuser") << "amqp://user@" << "user" << "" << "" << quint16(AMQP_PORT) << "";

View File

@ -356,7 +356,7 @@ void tst_QAMQPQueue::canOnlyStartConsumingOnce()
void tst_QAMQPQueue::ensureConsumeOnlySentOnce()
{
QAmqpQueue *queue = client->createQueue("test-single-consumer");
SignalSpy spy(queue, SIGNAL(consuming(QString)));
QSignalSpy spy(queue, SIGNAL(consuming(QString)));
queue->declare();
QVERIFY(waitForSignal(queue, SIGNAL(declared())));
@ -601,8 +601,13 @@ void tst_QAMQPQueue::tableFieldDataTypes()
QCOMPARE(message.header("double").toDouble(), double(FLT_MAX));
QCOMPARE(message.header("short-string").toString(), QLatin1String("test"));
QCOMPARE(message.header("long-string").toString(), QLatin1String("test"));
QCOMPARE(message.header("timestamp").toDateTime().toTime_t(), timestamp.toTime_t());
QCOMPARE(message.header("bytes").toByteArray(), QByteArray("abcdefg1234567"));
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
QCOMPARE(message.header("timestamp").toDateTime().toSecsSinceEpoch(),
timestamp.toSecsSinceEpoch());
#else
QCOMPARE(message.header("timestamp").toDateTime().toTime_t(), timestamp.toTime_t());
#endif
QVERIFY(message.hasHeader("nested-table"));
QAmqpTable compareTable(message.header("nested-table").toHash());
@ -654,11 +659,17 @@ void tst_QAMQPQueue::messageProperties()
QCOMPARE(message.property(QAmqpMessage::ReplyTo).toString(), QLatin1String("another-queue"));
QCOMPARE(message.property(QAmqpMessage::MessageId).toString(), QLatin1String("some-message-id"));
QCOMPARE(message.property(QAmqpMessage::Expiration).toString(), QLatin1String("60000"));
QCOMPARE(message.property(QAmqpMessage::Timestamp).toDateTime().toTime_t(), timestamp.toTime_t());
QCOMPARE(message.property(QAmqpMessage::Type).toString(), QLatin1String("some-message-type"));
QCOMPARE(message.property(QAmqpMessage::UserId).toString(), QLatin1String("guest"));
QCOMPARE(message.property(QAmqpMessage::AppId).toString(), QLatin1String("some-app-id"));
QCOMPARE(message.property(QAmqpMessage::ClusterID).toString(), QLatin1String("some-cluster-id"));
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
QCOMPARE(message.property(QAmqpMessage::Timestamp).toDateTime().toSecsSinceEpoch(),
timestamp.toSecsSinceEpoch());
#else
QCOMPARE(message.property(QAmqpMessage::Timestamp).toDateTime().toTime_t(),
timestamp.toTime_t());
#endif
}
void tst_QAMQPQueue::emptyMessage()

View File

@ -17,127 +17,4 @@ bool waitForSignal(QObject *obj, const char *signal, int delay)
} // namespace Test
} // namespace QAMQP
#if QT_VERSION >= 0x050000
typedef QSignalSpy SignalSpy;
#else
#include <QtCore/qbytearray.h>
#include <QtCore/qlist.h>
#include <QtCore/qobject.h>
#include <QtCore/qmetaobject.h>
#include <QtCore/qvariant.h>
#include <QtCore/qvector.h>
#include <QtTest/qtesteventloop.h>
class QVariant;
class SignalSpy: public QObject, public QList<QList<QVariant> >
{
public:
explicit SignalSpy(const QObject *obj, const char *aSignal)
: m_waiting(false)
{
static const int memberOffset = QObject::staticMetaObject.methodCount();
if (!obj) {
qWarning("SignalSpy: Cannot spy on a null object");
return;
}
if (!aSignal) {
qWarning("SignalSpy: Null signal name is not valid");
return;
}
if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) {
qWarning("SignalSpy: Not a valid signal, use the SIGNAL macro");
return;
}
const QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1);
const QMetaObject * const mo = obj->metaObject();
const int sigIndex = mo->indexOfMethod(ba.constData());
if (sigIndex < 0) {
qWarning("SignalSpy: No such signal: '%s'", ba.constData());
return;
}
if (!QMetaObject::connect(obj, sigIndex, this, memberOffset,
Qt::DirectConnection, 0)) {
qWarning("SignalSpy: QMetaObject::connect returned false. Unable to connect.");
return;
}
sig = ba;
initArgs(mo->method(sigIndex), obj);
}
inline bool isValid() const { return !sig.isEmpty(); }
inline QByteArray signal() const { return sig; }
bool wait(int timeout = 5)
{
Q_ASSERT(!m_waiting);
const int origCount = count();
m_waiting = true;
m_loop.enterLoop(timeout);
m_waiting = false;
return count() > origCount;
}
int qt_metacall(QMetaObject::Call call, int methodId, void **a)
{
methodId = QObject::qt_metacall(call, methodId, a);
if (methodId < 0)
return methodId;
if (call == QMetaObject::InvokeMetaMethod) {
if (methodId == 0) {
appendArgs(a);
}
--methodId;
}
return methodId;
}
private:
void initArgs(const QMetaMethod &member)
{
initArgs(member, 0);
}
void initArgs(const QMetaMethod &member, const QObject *obj)
{
Q_UNUSED(obj)
QList<QByteArray> params = member.parameterTypes();
for (int i = 0; i < params.count(); ++i) {
int tp = QMetaType::type(params.at(i).constData());
if (tp == QMetaType::Void)
qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
params.at(i).constData());
args << tp;
}
}
void appendArgs(void **a)
{
QList<QVariant> list;
for (int i = 0; i < args.count(); ++i) {
QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
list << QVariant(type, a[i + 1]);
}
append(list);
if (m_waiting)
m_loop.exitLoop();
}
// the full, normalized signal name
QByteArray sig;
// holds the QMetaType types for the argument list of the signal
QVector<int> args;
QTestEventLoop m_loop;
bool m_waiting;
};
#endif
#endif