summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrett Stottlemyer <bstottle@ford.com>2019-04-27 09:34:44 -0400
committerMichael Brasser <michael.brasser@live.com>2019-05-01 17:11:10 +0000
commit539a087eb099181630b5f5bd20d41a7b4bd45b66 (patch)
tree876590b25647c8a2b5004daaaf9077d858fb96cc
parentc4e5f629c09d197c49fd1be59a8fdfeaba064c15 (diff)
Fix registration for dynamic Replicas
This adds new tests for checking that the proxy (which uses dynamic replicas to avoid needing to include headers for all proxied types) works correctly over multiple processes. This found several edge cases not seen in the earlier (single process) proxy auto tests. 1) When sending a new sub class to a dynamic replica, the properties need to be stored in a separate QByteArray and extracted after the metatype info (for both classes and gadgets) is processed. 2) When the templated acquire is used, the metatype information needs to be added to the dynamicTypeManager. 3) There is an edge case where the DynamicApiMap for a null object could not be built correctly. In this case, it needed to be regenerated once a valid object was available for the Replica sub-class. 4) There were several edge cases where the typeName needed to be updated. [ChangeLog][Important Behavior Changes] Qt Remote Objects uses an internal protocol to pass data between processes and/or devices. The same protocol version needs to be used on all sides. The version was bumped from 1.2 to 1.3 in this release, fixing potential crashes (see QTBUG-75017). If there is a mismatch, the connecting node will output a warning and the host node will not send any data. Change-Id: I7af50f16ed370351eb0692d5f4b983848846b504 Fixes: QTBUG-75017 Reviewed-by: Michael Brasser <michael.brasser@live.com>
-rw-r--r--src/remoteobjects/qconnectionfactories_p.h2
-rw-r--r--src/remoteobjects/qremoteobjectnode.cpp49
-rw-r--r--src/remoteobjects/qremoteobjectnode_p.h2
-rw-r--r--src/remoteobjects/qremoteobjectpacket.cpp183
-rw-r--r--src/remoteobjects/qremoteobjectpacket_p.h4
-rw-r--r--src/remoteobjects/qremoteobjectsource.cpp20
-rw-r--r--src/remoteobjects/qremoteobjectsource_p.h4
-rw-r--r--tests/auto/auto.pro2
-rw-r--r--tests/auto/proxy_multiprocess/client/client.pro16
-rw-r--r--tests/auto/proxy_multiprocess/client/main.cpp93
-rw-r--r--tests/auto/proxy_multiprocess/proxy/main.cpp53
-rw-r--r--tests/auto/proxy_multiprocess/proxy/proxy.pro14
-rw-r--r--tests/auto/proxy_multiprocess/proxy_multiprocess.pro2
-rw-r--r--tests/auto/proxy_multiprocess/server/main.cpp99
-rw-r--r--tests/auto/proxy_multiprocess/server/mytestserver.cpp55
-rw-r--r--tests/auto/proxy_multiprocess/server/mytestserver.h55
-rw-r--r--tests/auto/proxy_multiprocess/server/server.pro19
-rw-r--r--tests/auto/proxy_multiprocess/shared.h5
-rw-r--r--tests/auto/proxy_multiprocess/subclass.rep20
-rw-r--r--tests/auto/proxy_multiprocess/tst/tst.pro8
-rw-r--r--tests/auto/proxy_multiprocess/tst/tst_proxy_multiprocess.cpp138
21 files changed, 747 insertions, 96 deletions
diff --git a/src/remoteobjects/qconnectionfactories_p.h b/src/remoteobjects/qconnectionfactories_p.h
index f0384ec..7bd38a1 100644
--- a/src/remoteobjects/qconnectionfactories_p.h
+++ b/src/remoteobjects/qconnectionfactories_p.h
@@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE
namespace QtRemoteObjects {
static const int dataStreamVersion = QDataStream::Qt_5_12;
-static const QLatin1String protocolVersion("QtRO 1.2");
+static const QLatin1String protocolVersion("QtRO 1.3");
}
diff --git a/src/remoteobjects/qremoteobjectnode.cpp b/src/remoteobjects/qremoteobjectnode.cpp
index 4704577..0e9de09 100644
--- a/src/remoteobjects/qremoteobjectnode.cpp
+++ b/src/remoteobjects/qremoteobjectnode.cpp
@@ -470,6 +470,11 @@ void QRemoteObjectNode::initializeReplica(QRemoteObjectReplica *instance, const
d->setReplicaImplementation(nullptr, instance, name);
} else {
const QMetaObject *meta = instance->metaObject();
+ // This is a templated acquire, so we tell the Source we don't need
+ // them to send the class definition. Thus we need to store the
+ // metaObject for this class - if this is a nested class, the QObject
+ // could be a nullptr or updated from the source,
+ d->dynamicTypeManager.addFromMetaObject(meta);
d->setReplicaImplementation(meta, instance, name.isEmpty() ? ::name(meta) : name);
}
}
@@ -622,6 +627,7 @@ QRemoteObjectMetaObjectManager::~QRemoteObjectMetaObjectManager()
const QMetaObject *QRemoteObjectMetaObjectManager::metaObjectForType(const QString &type)
{
+ qCDebug(QT_REMOTEOBJECT) << "metaObjectForType: looking for" << type << "static keys:" << staticTypes.keys() << "dynamic keys:" << dynamicTypes.keys();
Q_ASSERT(staticTypes.contains(type) || dynamicTypes.contains(type));
auto it = staticTypes.constFind(type);
if (it != staticTypes.constEnd())
@@ -700,7 +706,6 @@ static void registerAllGadgets(IoDeviceBase *connection, QRemoteObjectPackets::G
QMetaObject *QRemoteObjectMetaObjectManager::addDynamicType(IoDeviceBase *connection, QDataStream &in)
{
QMetaObjectBuilder builder;
- builder.setClassName("QRemoteObjectDynamicReplica");
builder.setSuperClass(&QRemoteObjectReplica::staticMetaObject);
builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
@@ -712,6 +717,8 @@ QMetaObject *QRemoteObjectMetaObjectManager::addDynamicType(IoDeviceBase *connec
quint32 numProperties = 0;
in >> type;
+ builder.addClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE, type.toLatin1());
+ builder.setClassName(type.toLatin1());
in >> numEnums;
for (quint32 i = 0; i < numEnums; ++i) {
@@ -804,13 +811,15 @@ QMetaObject *QRemoteObjectMetaObjectManager::addDynamicType(IoDeviceBase *connec
return meta;
}
-void QRemoteObjectMetaObjectManager::addFromReplica(QConnectedReplicaImplementation *rep)
+void QRemoteObjectMetaObjectManager::addFromMetaObject(const QMetaObject *metaObject)
{
- QString className = QString::fromLatin1(rep->m_metaObject->className());
+ QString className = QLatin1String(metaObject->className());
+ if (!className.endsWith(QLatin1String("Replica")))
+ return;
if (className == QStringLiteral("QRemoteObjectDynamicReplica") || staticTypes.contains(className))
return;
className.chop(7); //Remove 'Replica' from name
- staticTypes.insert(className, rep->m_metaObject);
+ staticTypes.insert(className, metaObject);
}
void QRemoteObjectNodePrivate::connectReplica(QObject *object, QRemoteObjectReplica *instance)
@@ -1082,7 +1091,7 @@ void QRemoteObjectNodePrivate::onClientRead(QObject *obj)
}
case Handshake:
if (rxName != QtRemoteObjects::protocolVersion) {
- qROPrivWarning() << "Protocol Mismatch, closing connection. Got" << rxObjects << "expected" << QtRemoteObjects::protocolVersion;
+ qWarning() << "*** Protocol Mismatch, closing connection ***. Got" << rxObjects << "expected" << QtRemoteObjects::protocolVersion;
setLastError(QRemoteObjectNode::ProtocolMismatch);
connection->close();
} else {
@@ -1779,7 +1788,7 @@ QVariant QRemoteObjectNodePrivate::handlePointerToQObjectProperty(QConnectedRepl
if (rep->isInitialized()) {
auto childRep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.take(childInfo.name));
if (childRep && !childRep->isShortCircuit())
- dynamicTypeManager.addFromReplica(static_cast<QConnectedReplicaImplementation *>(childRep.data()));
+ dynamicTypeManager.addFromMetaObject(childRep->metaObject());
}
if (childInfo.type == ObjectType::CLASS)
retval = QVariant::fromValue(q->acquireDynamic(childInfo.name));
@@ -1791,18 +1800,32 @@ QVariant QRemoteObjectNodePrivate::handlePointerToQObjectProperty(QConnectedRepl
QSharedPointer<QConnectedReplicaImplementation> childRep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(childInfo.name).toStrongRef());
if (childRep->connectionToSource.isNull())
childRep->connectionToSource = rep->connectionToSource;
+ QVariantList parameters;
+ QDataStream ds(childInfo.parameters);
if (childRep->needsDynamicInitialization()) {
- if (childInfo.classDefinition.isEmpty())
- childRep->setDynamicMetaObject(dynamicTypeManager.metaObjectForType(childInfo.typeName));
- else {
+ if (childInfo.classDefinition.isEmpty()) {
+ auto typeName = childInfo.typeName;
+ if (typeName == QLatin1String("QObject")) {
+ // The sender would have included the class name if needed
+ // So the acquire must have been templated, and we have the typeName
+ typeName = QString::fromLatin1(rep->getProperty(index).typeName());
+ if (typeName.endsWith(QLatin1String("Replica*")))
+ typeName.chop(8);
+ }
+ childRep->setDynamicMetaObject(dynamicTypeManager.metaObjectForType(typeName));
+ } else {
QDataStream in(childInfo.classDefinition);
childRep->setDynamicMetaObject(dynamicTypeManager.addDynamicType(rep->connectionToSource, in));
}
- handlePointerToQObjectProperties(childRep.data(), childInfo.parameters);
- childRep->setDynamicProperties(childInfo.parameters);
+ if (!childInfo.parameters.isEmpty())
+ ds >> parameters;
+ handlePointerToQObjectProperties(childRep.data(), parameters);
+ childRep->setDynamicProperties(parameters);
} else {
- handlePointerToQObjectProperties(childRep.data(), childInfo.parameters);
- childRep->initialize(childInfo.parameters);
+ if (!childInfo.parameters.isEmpty())
+ ds >> parameters;
+ handlePointerToQObjectProperties(childRep.data(), parameters);
+ childRep->initialize(parameters);
}
return retval;
diff --git a/src/remoteobjects/qremoteobjectnode_p.h b/src/remoteobjects/qremoteobjectnode_p.h
index 17642b6..bf6a0bd 100644
--- a/src/remoteobjects/qremoteobjectnode_p.h
+++ b/src/remoteobjects/qremoteobjectnode_p.h
@@ -91,7 +91,7 @@ public:
const QMetaObject *metaObjectForType(const QString &type);
QMetaObject *addDynamicType(IoDeviceBase* connection, QDataStream &in);
- void addFromReplica(QConnectedReplicaImplementation *rep);
+ void addFromMetaObject(const QMetaObject *);
private:
QHash<QString, QMetaObject*> dynamicTypes;
diff --git a/src/remoteobjects/qremoteobjectpacket.cpp b/src/remoteobjects/qremoteobjectpacket.cpp
index f160df4..58c8a2d 100644
--- a/src/remoteobjects/qremoteobjectpacket.cpp
+++ b/src/remoteobjects/qremoteobjectpacket.cpp
@@ -51,7 +51,7 @@ using namespace QtRemoteObjects;
namespace QRemoteObjectPackets {
-void serializeProperty(DataStreamPacket &ds, const QRemoteObjectSourceBase *source, int internalIndex)
+void serializeProperty(QDataStream &ds, const QRemoteObjectSourceBase *source, int internalIndex)
{
const int propertyIndex = source->m_api->sourcePropertyIndex(internalIndex);
Q_ASSERT (propertyIndex >= 0);
@@ -66,7 +66,7 @@ void serializeProperty(DataStreamPacket &ds, const QRemoteObjectSourceBase *sour
if (childSource->m_object != valueAsPointerToQObject)
childSource->resetObject(valueAsPointerToQObject);
QRO_ qro(childSource);
- if (source->d->isDynamic && qro.type == ObjectType::CLASS && !source->d->sentTypes.contains(qro.typeName)) {
+ if (source->d->isDynamic && qro.type == ObjectType::CLASS && childSource->m_object && !source->d->sentTypes.contains(qro.typeName)) {
QDataStream classDef(&qro.classDefinition, QIODevice::WriteOnly);
serializeDefinition(classDef, childSource);
source->d->sentTypes.insert(qro.typeName);
@@ -75,9 +75,13 @@ void serializeProperty(DataStreamPacket &ds, const QRemoteObjectSourceBase *sour
if (qro.isNull)
return;
const int propertyCount = childSource->m_api->propertyCount();
- ds << propertyCount;
+ // Put the properties in a buffer, the receiver may not know how to
+ // interpret the types until it registers new ones.
+ QDataStream params(&qro.parameters, QIODevice::WriteOnly);
+ params << propertyCount;
for (int internalIndex = 0; internalIndex < propertyCount; ++internalIndex)
- serializeProperty(ds, childSource, internalIndex);
+ serializeProperty(params, childSource, internalIndex);
+ ds << qro.parameters;
} else {
ds << value; // return original
}
@@ -192,44 +196,39 @@ static GadgetsData gadgetData(const QMetaObject *mo)
return res;
}
-void serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source)
+static ObjectType objectType(const QString &typeName)
{
- const SourceApiMap *api = source->m_api;
-
- ds << source->m_api->typeName();
+ if (typeName == QLatin1String("QAbstractItemModelAdapter"))
+ return ObjectType::MODEL;
+ auto tid = QMetaType::type(typeName.toUtf8());
+ if (tid == QMetaType::UnknownType)
+ return ObjectType::CLASS;
+ QMetaType type(tid);
+ auto mo = type.metaObject();
+ if (mo && mo->inherits(&QAbstractItemModel::staticMetaObject))
+ return ObjectType::MODEL;
+ return ObjectType::CLASS;
+}
- //Now copy the property data
- const int numEnums = api->enumCount();
- const auto metaObject = source->m_object->metaObject();
- ds << quint32(numEnums); //Number of Enums
- for (int i = 0; i < numEnums; ++i) {
- auto enumerator = metaObject->enumerator(api->sourceEnumIndex(i));
- Q_ASSERT(enumerator.isValid());
- ds << enumerator.name();
- ds << enumerator.isFlag();
- ds << enumerator.scope();
- const int keyCount = enumerator.keyCount();
- ds << keyCount;
- for (int k = 0; k < keyCount; ++k) {
- ds << enumerator.key(k);
- ds << enumerator.value(k);
- }
- }
+void recurseForGadgets(GadgetsData &gadgets, const QRemoteObjectSourceBase *source)
+{
+ const SourceApiMap *api = source->m_api;
const int numSignals = api->signalCount();
const int numMethods = api->methodCount();
const int numProperties = api->propertyCount();
- GadgetsData gadgets;
- QSet<int> processedTypes;
for (int si = 0; si < numSignals; ++si) {
const int params = api->signalParameterCount(si);
for (int pi = 0; pi < params; ++pi) {
const int type = api->signalParameterType(si, pi);
- if (processedTypes.contains(type) || !QMetaType::typeFlags(type).testFlag(QMetaType::IsGadget))
- continue;
- mergeData(gadgets, gadgetData(QMetaType::metaObjectForType(type)));
- processedTypes.insert(type);
+ if (!QMetaType::typeFlags(type).testFlag(QMetaType::IsGadget))
+ continue;
+ const auto mo = QMetaType::metaObjectForType(type);
+ if (source->d->sentTypes.contains(QLatin1String(mo->className())))
+ continue;
+ mergeData(gadgets, gadgetData(mo));
+ source->d->sentTypes.insert(QLatin1String(mo->className()));
}
}
@@ -237,10 +236,13 @@ void serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source)
const int params = api->methodParameterCount(mi);
for (int pi = 0; pi < params; ++pi) {
const int type = api->methodParameterType(mi, pi);
- if (processedTypes.contains(type) || !QMetaType::typeFlags(type).testFlag(QMetaType::IsGadget))
- continue;
- mergeData(gadgets, gadgetData(QMetaType::metaObjectForType(type)));
- processedTypes.insert(type);
+ if (!QMetaType::typeFlags(type).testFlag(QMetaType::IsGadget))
+ continue;
+ const auto mo = QMetaType::metaObjectForType(type);
+ if (source->d->sentTypes.contains(QLatin1String(mo->className())))
+ continue;
+ mergeData(gadgets, gadgetData(mo));
+ source->d->sentTypes.insert(QLatin1String(mo->className()));
}
}
for (int pi = 0; pi < numProperties; ++pi) {
@@ -248,30 +250,77 @@ void serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source)
Q_ASSERT(index >= 0);
const auto target = api->isAdapterProperty(pi) ? source->m_adapter : source->m_object;
const auto metaProperty = target->metaObject()->property(index);
+ if (QMetaType::typeFlags(metaProperty.userType()).testFlag(QMetaType::PointerToQObject)) {
+ auto const type = objectType(QString::fromLatin1(metaProperty.typeName()));
+ if (type == ObjectType::CLASS) {
+ auto const childSource = source->m_children.value(pi);
+ if (childSource->m_object)
+ recurseForGadgets(gadgets, childSource);
+ }
+ }
const int type = metaProperty.userType();
- if (processedTypes.contains(type) || !QMetaType::typeFlags(type).testFlag(QMetaType::IsGadget))
- continue;
- mergeData(gadgets, gadgetData(QMetaType::metaObjectForType(type)));
- processedTypes.insert(type);
+ if (!QMetaType::typeFlags(type).testFlag(QMetaType::IsGadget))
+ continue;
+ const auto mo = QMetaType::metaObjectForType(type);
+ if (source->d->sentTypes.contains(QLatin1String(mo->className())))
+ continue;
+ mergeData(gadgets, gadgetData(mo));
+ source->d->sentTypes.insert(QLatin1String(mo->className()));
}
- ds << quint32(gadgets.size());
- for (auto it = gadgets.constBegin(); it != gadgets.constEnd(); ++it) {
- ds << it.key();
- ds << quint32(it.value().size());
- for (const auto &prop : qAsConst(it.value())) {
- ds << prop.name;
- ds << prop.type;
+}
+
+void serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source)
+{
+ const SourceApiMap *api = source->m_api;
+
+ ds << source->m_api->typeName();
+
+ //Now copy the property data
+ const int numEnums = api->enumCount();
+ const auto metaObject = source->m_object->metaObject();
+ ds << quint32(numEnums); //Number of Enums
+ for (int i = 0; i < numEnums; ++i) {
+ auto enumerator = metaObject->enumerator(api->sourceEnumIndex(i));
+ Q_ASSERT(enumerator.isValid());
+ ds << enumerator.name();
+ ds << enumerator.isFlag();
+ ds << enumerator.scope();
+ const int keyCount = enumerator.keyCount();
+ ds << keyCount;
+ for (int k = 0; k < keyCount; ++k) {
+ ds << enumerator.key(k);
+ ds << enumerator.value(k);
}
}
+ if (source->d->isDynamic) {
+ GadgetsData gadgets;
+ recurseForGadgets(gadgets, source);
+ ds << quint32(gadgets.size());
+ for (auto it = gadgets.constBegin(); it != gadgets.constEnd(); ++it) {
+ ds << it.key();
+ ds << quint32(it.value().size());
+ for (const auto &prop : qAsConst(it.value())) {
+ ds << prop.name;
+ ds << prop.type;
+ }
+ }
+ } else
+ ds << quint32(0);
+
+ const int numSignals = api->signalCount();
ds << quint32(numSignals); //Number of signals
for (int i = 0; i < numSignals; ++i) {
const int index = api->sourceSignalIndex(i);
Q_ASSERT(index >= 0);
- ds << api->signalSignature(i);
+ auto signature = api->signalSignature(i);
+ signature.replace("SimpleSource*)", "Replica*)");
+ signature.replace("Source*)", "Replica*)");
+ ds << signature;
ds << api->signalParameterNames(i);
}
+ const int numMethods = api->methodCount();
ds << quint32(numMethods); //Number of methods
for (int i = 0; i < numMethods; ++i) {
const int index = api->sourceMethodIndex(i);
@@ -281,6 +330,7 @@ void serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source)
ds << api->methodParameterNames(i);
}
+ const int numProperties = api->propertyCount();
ds << quint32(numProperties); //Number of properties
for (int i = 0; i < numProperties; ++i) {
const int index = api->sourcePropertyIndex(i);
@@ -289,11 +339,19 @@ void serializeDefinition(QDataStream &ds, const QRemoteObjectSourceBase *source)
const auto target = api->isAdapterProperty(i) ? source->m_adapter : source->m_object;
const auto metaProperty = target->metaObject()->property(index);
ds << metaProperty.name();
- ds << metaProperty.typeName();
+ if (QMetaType::typeFlags(metaProperty.userType()).testFlag(QMetaType::PointerToQObject)) {
+ auto type = objectType(QLatin1String(metaProperty.typeName()));
+ ds << (type == ObjectType::CLASS ? "QObject*" : "QAbstractItemModel*");
+ } else
+ ds << metaProperty.typeName();
if (metaProperty.notifySignalIndex() == -1)
ds << QByteArray();
- else
- ds << metaProperty.notifySignal().methodSignature();
+ else {
+ auto signature = metaProperty.notifySignal().methodSignature();
+ signature.replace("SimpleSource*)", "Replica*)");
+ signature.replace("Source*)", "Replica*)");
+ ds << signature;
+ }
}
}
@@ -406,37 +464,21 @@ void serializePongPacket(DataStreamPacket &ds, const QString &name)
ds.finishPacket();
}
-static ObjectType objectType(const QString &typeName)
-{
- if (typeName == QLatin1String("QAbstractItemModelAdapter"))
- return ObjectType::MODEL;
- auto tid = QMetaType::type(typeName.toUtf8());
- if (tid == QMetaType::UnknownType)
- return ObjectType::CLASS;
- QMetaType type(tid);
- auto mo = type.metaObject();
- if (mo && mo->inherits(&QAbstractItemModel::staticMetaObject))
- return ObjectType::MODEL;
- return ObjectType::CLASS;
-}
-
QRO_::QRO_(QRemoteObjectSourceBase *source)
: name(source->name())
, typeName(source->m_api->typeName())
, type(source->m_adapter ? ObjectType::MODEL : objectType(typeName))
, isNull(source->m_object == nullptr)
, classDefinition()
+ , parameters()
{}
QDataStream &operator<<(QDataStream &stream, const QRO_ &info)
{
stream << info.name << info.typeName << (quint8)(info.type) << info.classDefinition << info.isNull;
qCDebug(QT_REMOTEOBJECT) << "Serializing QRO_" << info.name << info.typeName << (info.type == ObjectType::CLASS ? "Class" : "Model")
- << (info.isNull ? "nullptr" : "valid pointer");
- //We expect info.parameters to be empty (parameters are pushed to stream individually in serializeProperty)
- if (!info.isNull && !info.parameters.isEmpty())
- stream << info.parameters;
-
+ << (info.isNull ? "nullptr" : "valid pointer") << (info.classDefinition.isEmpty() ? "no definitions" : "with definitions");
+ // info.parameters will be filled in by serializeProperty
return stream;
}
@@ -445,7 +487,8 @@ QDataStream &operator>>(QDataStream &stream, QRO_ &info)
quint8 tmpType;
stream >> info.name >> info.typeName >> tmpType >> info.classDefinition >> info.isNull;
info.type = static_cast<ObjectType>(tmpType);
- qCDebug(QT_REMOTEOBJECT) << "Deserializing QRO_" << info.name << info.typeName << (info.isNull ? "nullptr" : "valid pointer");
+ qCDebug(QT_REMOTEOBJECT) << "Deserializing QRO_" << info.name << info.typeName << (info.isNull ? "nullptr" : "valid pointer")
+ << (info.classDefinition.isEmpty() ? "no definitions" : "with definitions");
if (!info.isNull)
stream >> info.parameters;
return stream;
diff --git a/src/remoteobjects/qremoteobjectpacket_p.h b/src/remoteobjects/qremoteobjectpacket_p.h
index 549cb68..8082e2b 100644
--- a/src/remoteobjects/qremoteobjectpacket_p.h
+++ b/src/remoteobjects/qremoteobjectpacket_p.h
@@ -112,7 +112,7 @@ public:
ObjectType type;
bool isNull;
QByteArray classDefinition;
- QVariantList parameters;
+ QByteArray parameters;
};
inline QDebug operator<<(QDebug dbg, const QRO_ &info)
@@ -171,7 +171,7 @@ private:
Q_DISABLE_COPY(DataStreamPacket)
};
-void serializeProperty(DataStreamPacket &, const QRemoteObjectSourceBase *source, int internalIndex);
+void serializeProperty(QDataStream &, const QRemoteObjectSourceBase *source, int internalIndex);
QVariant deserializedProperty(const QVariant &in, const QMetaProperty &property);
void serializeHandshakePacket(DataStreamPacket &);
diff --git a/src/remoteobjects/qremoteobjectsource.cpp b/src/remoteobjects/qremoteobjectsource.cpp
index d855220..015cda7 100644
--- a/src/remoteobjects/qremoteobjectsource.cpp
+++ b/src/remoteobjects/qremoteobjectsource.cpp
@@ -40,6 +40,7 @@
#include "qremoteobjectsource.h"
#include "qremoteobjectsource_p.h"
#include "qremoteobjectnode.h"
+#include "qremoteobjectdynamicreplica.h"
#include "qconnectionfactories_p.h"
#include "qremoteobjectsourceio_p.h"
@@ -190,6 +191,18 @@ void QRemoteObjectSourceBase::resetObject(QObject *newObject)
delete m_adapter;
m_adapter = nullptr;
}
+ // We need some dynamic replica specific code here, in case an object had null sub-classes that
+ // have been replaced with real objects. In this case, the ApiMap could be wrong and need updating.
+ if (newObject && qobject_cast<QRemoteObjectDynamicReplica *>(newObject) && m_api->isDynamic()) {
+ auto api = static_cast<const DynamicApiMap*>(m_api);
+ if (api->m_properties[0] == 0) { // 0 is an index into QObject itself, so this isn't a valid QtRO index
+ const auto rep = qobject_cast<QRemoteObjectDynamicReplica *>(newObject);
+ auto tmp = m_api;
+ m_api = new DynamicApiMap(newObject, rep->metaObject(), api->m_name, QLatin1String(rep->metaObject()->className()));
+ qCDebug(QT_REMOTEOBJECT) << " Reset m_api for" << api->m_name << "using new metaObject:" << rep->metaObject()->className();
+ delete tmp;
+ }
+ }
m_object = newObject;
auto model = qobject_cast<QAbstractItemModel *>(newObject);
@@ -364,7 +377,7 @@ void QRemoteObjectSourceBase::handleMetaCall(int index, QMetaObject::Call call,
void QRemoteObjectRootSource::addListener(IoDeviceBase *io, bool dynamic)
{
d->m_listeners.append(io);
- d->isDynamic = dynamic;
+ d->isDynamic = d->isDynamic || dynamic;
if (dynamic) {
d->sentTypes.clear();
@@ -374,11 +387,6 @@ void QRemoteObjectRootSource::addListener(IoDeviceBase *io, bool dynamic)
serializeInitPacket(d->m_packet, this);
io->write(d->m_packet.array, d->m_packet.size);
}
-
- //Setting isDynamic == false will prevent class definitions from being sent if the
- //QObject pointer is changed (setting a new subclass pointer). I.e., class definitions
- //are only sent by serializeInitDynamicPacket, not propertyChangePackets.
- d->isDynamic = false;
}
int QRemoteObjectRootSource::removeListener(IoDeviceBase *io, bool shouldSendRemove)
diff --git a/src/remoteobjects/qremoteobjectsource_p.h b/src/remoteobjects/qremoteobjectsource_p.h
index a94885d..0fdd3c8 100644
--- a/src/remoteobjects/qremoteobjectsource_p.h
+++ b/src/remoteobjects/qremoteobjectsource_p.h
@@ -72,7 +72,7 @@ public:
void resetObject(QObject *newObject);
int qt_metacall(QMetaObject::Call call, int methodId, void **a) final;
QObject *m_object, *m_adapter;
- const SourceApiMap * const m_api;
+ const SourceApiMap *m_api;
QVariantList m_marshalledArgs;
bool hasAdapter() const { return m_adapter; }
virtual QString name() const = 0;
@@ -189,7 +189,7 @@ public:
QByteArray objectSignature() const override { return m_objectSignature; }
bool isDynamic() const override { return true; }
-private:
+
int parameterCount(int objectIndex) const;
int parameterType(int objectIndex, int paramIndex) const;
const QByteArray signature(int objectIndex) const;
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 28ee54f..69ca9f1 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -24,4 +24,4 @@ SUBDIRS += \
contains(QT_CONFIG, ssl): SUBDIRS += external_IODevice
qtHaveModule(qml): SUBDIRS += qml
-qtConfig(process): SUBDIRS += integration_multiprocess integration_external restart
+qtConfig(process): SUBDIRS += integration_multiprocess proxy_multiprocess integration_external restart
diff --git a/tests/auto/proxy_multiprocess/client/client.pro b/tests/auto/proxy_multiprocess/client/client.pro
new file mode 100644
index 0000000..ce435c2
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/client/client.pro
@@ -0,0 +1,16 @@
+TEMPLATE = app
+QT += remoteobjects core testlib
+QT -= gui
+
+TARGET = client
+DESTDIR = ./
+CONFIG += c++11
+CONFIG -= app_bundle
+
+REPC_REPLICA = ../subclass.rep
+
+SOURCES += main.cpp \
+
+HEADERS += \
+
+INCLUDEPATH += $$PWD
diff --git a/tests/auto/proxy_multiprocess/client/main.cpp b/tests/auto/proxy_multiprocess/client/main.cpp
new file mode 100644
index 0000000..05f31ea
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/client/main.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Ford Motor Company
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtRemoteObjects module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "rep_subclass_replica.h"
+#include "../shared.h"
+
+#include <QCoreApplication>
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtTest/QtTest>
+
+class tst_Client_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase()
+ {
+ m_repNode.reset(new QRemoteObjectNode);
+ m_repNode->connectToNode(QUrl(QStringLiteral("tcp://127.0.0.1:65213")));
+ m_rep.reset(m_repNode->acquire<ParentClassReplica>());
+ const auto objectMode = qEnvironmentVariable("ObjectMode");
+ qDebug() << "Waiting to connect, mode =" << objectMode;
+ QVERIFY(m_rep->waitForSource());
+ }
+
+ void testSubClass()
+ {
+ const auto objectMode = qEnvironmentVariable("ObjectMode");
+
+ qDebug() << "Starting test" << objectMode;
+ if (objectMode == QLatin1Literal("ObjectPointer")) {
+ QSignalSpy tracksSpy(m_rep->tracks(), &QAbstractItemModelReplica::initialized);
+ QVERIFY(m_rep->subClass() != nullptr);
+ QCOMPARE(m_rep->subClass()->myPOD(), initialValue);
+ QCOMPARE(m_rep->subClass()->i(), initialI);
+ QVERIFY(m_rep->tracks() != nullptr);
+ QVERIFY(tracksSpy.count() || tracksSpy.wait());
+ } else {
+ QVERIFY(m_rep->subClass() == nullptr);
+ QVERIFY(m_rep->tracks() == nullptr);
+ }
+ qDebug() << "Verified expected initial states, sending start.";
+ auto reply = m_rep->start();
+ QVERIFY(reply.waitForFinished());
+
+ QSignalSpy advanceSpy(m_rep.data(), SIGNAL(advance()));
+ QVERIFY(advanceSpy.wait());
+ QVERIFY(m_rep->subClass() != nullptr);
+ QCOMPARE(m_rep->subClass()->myPOD(), updatedValue);
+ QCOMPARE(m_rep->subClass()->i(), updatedI);
+ QVERIFY(m_rep->tracks() != nullptr);
+ qDebug() << "Verified expected final states, cleaning up.";
+ }
+
+ void cleanupTestCase()
+ {
+ auto reply = m_rep->quit();
+ QVERIFY(reply.waitForFinished());
+ }
+
+private:
+ QScopedPointer<QRemoteObjectNode> m_repNode;
+ QScopedPointer<ParentClassReplica> m_rep;
+};
+
+QTEST_MAIN(tst_Client_Process)
+
+#include "main.moc"
diff --git a/tests/auto/proxy_multiprocess/proxy/main.cpp b/tests/auto/proxy_multiprocess/proxy/main.cpp
new file mode 100644
index 0000000..46a73a8
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/proxy/main.cpp
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Ford Motor Company
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtRemoteObjects module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtTest/QtTest>
+
+class tst_Proxy_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testRun()
+ {
+ m_hostNode.reset(new QRemoteObjectHost);
+ m_hostNode->setHostUrl(QUrl(QStringLiteral("tcp://127.0.0.1:65213")));
+ m_hostNode->proxy(QUrl("local:testRegistry"));
+
+ QTest::qWait(500);
+ }
+
+private:
+ QScopedPointer<QRemoteObjectHost> m_hostNode;
+};
+
+QTEST_MAIN(tst_Proxy_Process)
+
+#include "main.moc"
diff --git a/tests/auto/proxy_multiprocess/proxy/proxy.pro b/tests/auto/proxy_multiprocess/proxy/proxy.pro
new file mode 100644
index 0000000..7065ecf
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/proxy/proxy.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+QT += remoteobjects core testlib
+QT -= gui
+
+TARGET = proxy
+DESTDIR = ./
+CONFIG += c++11
+CONFIG -= app_bundle
+
+SOURCES += main.cpp \
+
+HEADERS += \
+
+INCLUDEPATH += $$PWD
diff --git a/tests/auto/proxy_multiprocess/proxy_multiprocess.pro b/tests/auto/proxy_multiprocess/proxy_multiprocess.pro
new file mode 100644
index 0000000..075bc00
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/proxy_multiprocess.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS = client server proxy tst
diff --git a/tests/auto/proxy_multiprocess/server/main.cpp b/tests/auto/proxy_multiprocess/server/main.cpp
new file mode 100644
index 0000000..af94879
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/server/main.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Ford Motor Company
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtRemoteObjects module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mytestserver.h"
+#include "rep_subclass_source.h"
+#include "../shared.h"
+
+#include <QCoreApplication>
+#include <QtTest/QtTest>
+
+class tst_Server_Process : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testRun()
+ {
+ const auto objectMode = qEnvironmentVariable("ObjectMode");
+ bool templated = qEnvironmentVariableIsSet("TEMPLATED_REMOTING");
+
+ qDebug() << "Starting tests:" << objectMode << "templated =" << templated;
+ QRemoteObjectRegistryHost srcNode(QUrl(QStringLiteral("local:testRegistry")));
+
+ MyTestServer parent;
+ SubClassSimpleSource subclass;
+ subclass.setMyPOD(initialValue);
+ subclass.setI(initialI);
+ QStringListModel model;
+ model.setStringList(QStringList() << "Track1" << "Track2" << "Track3");
+ if (objectMode == QLatin1Literal("ObjectPointer")) {
+ parent.setSubClass(&subclass);
+ parent.setTracks(&model);
+ }
+
+ if (templated)
+ srcNode.enableRemoting<ParentClassSourceAPI>(&parent);
+ else
+ srcNode.enableRemoting(&parent);
+
+ qDebug() << "Waiting for incoming connections";
+
+ QSignalSpy waitForStartedSpy(&parent, SIGNAL(startedChanged(bool)));
+ QVERIFY(waitForStartedSpy.isValid());
+ QVERIFY(waitForStartedSpy.wait());
+ QCOMPARE(waitForStartedSpy.value(0).value(0).toBool(), true);
+
+ // wait for delivery of events
+ QTest::qWait(200);
+
+ //Change SubClass and make sure change propagates
+ SubClassSimpleSource updatedSubclass;
+ updatedSubclass.setMyPOD(updatedValue);
+ updatedSubclass.setI(updatedI);
+ parent.setSubClass(&updatedSubclass);
+ if (objectMode == QLatin1Literal("NullPointer"))
+ parent.setTracks(&model);
+
+ emit parent.advance();
+
+ // wait for quit
+ bool quit = false;
+ connect(&parent, &MyTestServer::quitApp, [&quit]{quit = true;});
+ QTRY_VERIFY_WITH_TIMEOUT(quit, 5000);
+
+ // wait for delivery of events
+ QTest::qWait(200);
+
+ qDebug() << "Done. Shutting down.";
+ }
+};
+
+QTEST_MAIN(tst_Server_Process)
+
+#include "main.moc"
diff --git a/tests/auto/proxy_multiprocess/server/mytestserver.cpp b/tests/auto/proxy_multiprocess/server/mytestserver.cpp
new file mode 100644
index 0000000..b0c75ab
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/server/mytestserver.cpp
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Ford Motor Company
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtRemoteObjects module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+
+#include "mytestserver.h"
+#include "rep_subclass_source.h"
+
+MyTestServer::MyTestServer(QObject *parent)
+ : ParentClassSimpleSource(parent)
+{
+ qDebug() << "Server started";
+}
+
+MyTestServer::~MyTestServer()
+{
+ qDebug() << "Server stopped";
+}
+
+bool MyTestServer::start()
+{
+ setStarted(true);
+ return true;
+}
+
+bool MyTestServer::quit()
+{
+ emit quitApp();
+ return true;
+}
diff --git a/tests/auto/proxy_multiprocess/server/mytestserver.h b/tests/auto/proxy_multiprocess/server/mytestserver.h
new file mode 100644
index 0000000..df044c1
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/server/mytestserver.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Ford Motor Company
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtRemoteObjects module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MYTESTSERVER_H
+#define MYTESTSERVER_H
+
+#include <QTimer>
+
+#include <QtRemoteObjects/qremoteobjectnode.h>
+#include <QtRemoteObjects/qremoteobjectsource.h>
+
+#include "rep_subclass_source.h"
+
+class MyTestServer : public ParentClassSimpleSource
+{
+ Q_OBJECT
+
+public:
+ MyTestServer(QObject *parent = nullptr);
+ ~MyTestServer() override;
+
+public Q_SLOTS:
+ bool start() override;
+ bool quit() override;
+
+Q_SIGNALS:
+ void quitApp();
+};
+
+#endif // MYTESTSERVER_H
diff --git a/tests/auto/proxy_multiprocess/server/server.pro b/tests/auto/proxy_multiprocess/server/server.pro
new file mode 100644
index 0000000..7167fda
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/server/server.pro
@@ -0,0 +1,19 @@
+TEMPLATE = app
+QT += remoteobjects core testlib
+QT -= gui
+
+TARGET = server
+DESTDIR = ./
+CONFIG += c++11
+CONFIG -= app_bundle
+
+REPC_SOURCE = $$PWD/../subclass.rep
+
+SOURCES += main.cpp \
+ mytestserver.cpp
+
+HEADERS += \
+ mytestserver.h \
+ $$OUT_PWD/rep_subclass_source.h
+
+INCLUDEPATH += $$PWD
diff --git a/tests/auto/proxy_multiprocess/shared.h b/tests/auto/proxy_multiprocess/shared.h
new file mode 100644
index 0000000..8aa2dfe
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/shared.h
@@ -0,0 +1,5 @@
+const MyPOD initialValue(42, 3.14, QStringLiteral("SubClass"));
+const MyPOD updatedValue(-1, 123.456, QStringLiteral("Updated"));
+const int initialI = 100;
+const int updatedI = 200;
+
diff --git a/tests/auto/proxy_multiprocess/subclass.rep b/tests/auto/proxy_multiprocess/subclass.rep
new file mode 100644
index 0000000..5d3f9e9
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/subclass.rep
@@ -0,0 +1,20 @@
+POD MyPOD(int i, float f, QString s)
+
+class SubClass
+{
+ PROP(MyPOD myPOD)
+ PROP(int i)
+}
+
+class ParentClass
+{
+ PROP(bool started = false)
+
+ SLOT(bool start())
+ SLOT(bool quit())
+ SIGNAL(advance())
+
+ CLASS subClass(SubClass)
+ MODEL tracks(display)
+}
+
diff --git a/tests/auto/proxy_multiprocess/tst/tst.pro b/tests/auto/proxy_multiprocess/tst/tst.pro
new file mode 100644
index 0000000..954ebdb
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/tst/tst.pro
@@ -0,0 +1,8 @@
+CONFIG += testcase c++11
+CONFIG -= app_bundle
+TARGET = tst_proxy_multiprocess
+DESTDIR = ./
+QT += testlib remoteobjects
+QT -= gui
+
+SOURCES += tst_proxy_multiprocess.cpp
diff --git a/tests/auto/proxy_multiprocess/tst/tst_proxy_multiprocess.cpp b/tests/auto/proxy_multiprocess/tst/tst_proxy_multiprocess.cpp
new file mode 100644
index 0000000..e7f382b
--- /dev/null
+++ b/tests/auto/proxy_multiprocess/tst/tst_proxy_multiprocess.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Ford Motor Company
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtRemoteObjects module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QMetaType>
+#include <QProcess>
+#include <QStandardPaths>
+
+namespace {
+
+QString findExecutable(const QString &executableName, const QStringList &paths)
+{
+ const auto path = QStandardPaths::findExecutable(executableName, paths);
+ if (!path.isEmpty()) {
+ return path;
+ }
+
+ qWarning() << "Could not find executable:" << executableName << "in any of" << paths;
+ return QString();
+}
+
+}
+
+class tst_Proxy_MultiProcess: public QObject
+{
+ Q_OBJECT
+
+public:
+ enum ObjectMode { NullPointer, ObjectPointer };
+ Q_ENUM(ObjectMode)
+
+private slots:
+ void initTestCase()
+ {
+ }
+
+ void cleanup()
+ {
+ // wait for delivery of RemoveObject events to the source
+ QTest::qWait(200);
+ }
+
+ void testRun_data()
+ {
+ QTest::addColumn<bool>("templated");
+ QTest::addColumn<ObjectMode>("objectMode");
+ QTest::newRow("non-templated, subobject") << false << ObjectPointer;
+ QTest::newRow("templated, subobject") << true << ObjectPointer;
+ QTest::newRow("non-templated, nullptr") << false << NullPointer;
+ QTest::newRow("templated, nullptr") << true << NullPointer;
+ }
+
+ void testRun()
+ {
+ QFETCH(bool, templated);
+ QFETCH(ObjectMode, objectMode);
+
+ qDebug() << "Starting server process";
+ QProcess serverProc;
+ serverProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ env.insert("ObjectMode", QVariant::fromValue(objectMode).toString());
+ if (templated) {
+ env.insert("TEMPLATED_REMOTING", "true");
+ }
+ serverProc.setProcessEnvironment(env);
+ serverProc.start(findExecutable("server", {
+ QCoreApplication::applicationDirPath() + "/../server/"
+ }));
+ QVERIFY(serverProc.waitForStarted());
+
+ // wait for server start
+ QTest::qWait(200);
+
+
+ qDebug() << "Starting client process";
+ QProcess clientProc;
+ clientProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ clientProc.setProcessEnvironment(env);
+ clientProc.start(findExecutable("client", {
+ QCoreApplication::applicationDirPath() + "/../client/"
+ }));
+ QVERIFY(clientProc.waitForStarted());
+
+ // wait for client start
+ QTest::qWait(200);
+
+
+ qDebug() << "Starting proxy process";
+ QProcess proxyProc;
+ proxyProc.setProcessChannelMode(QProcess::ForwardedChannels);
+ proxyProc.start(findExecutable("proxy", {
+ QCoreApplication::applicationDirPath() + "/../proxy/"
+ }));
+ QVERIFY(proxyProc.waitForStarted());
+
+ // wait for proxy start
+ QTest::qWait(200);
+
+
+ QVERIFY(clientProc.waitForFinished());
+ QVERIFY(proxyProc.waitForFinished());
+ QVERIFY(serverProc.waitForFinished());
+
+ QCOMPARE(serverProc.exitCode(), 0);
+ QCOMPARE(proxyProc.exitCode(), 0);
+ QCOMPARE(clientProc.exitCode(), 0);
+ }
+};
+
+QTEST_MAIN(tst_Proxy_MultiProcess)
+
+#include "tst_proxy_multiprocess.moc"