mirror of
https://github.com/overte-org/overte.git
synced 2025-06-08 12:32:03 +02:00
Optional hash/full metadata streaming for QObjects.
This commit is contained in:
parent
35f57cb998
commit
b21247ca67
3 changed files with 243 additions and 40 deletions
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <QCryptographicHash>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QMetaProperty>
|
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
@ -377,13 +377,9 @@ Bitstream& Bitstream::operator<<(const QObject* object) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitstream& Bitstream::operator>>(QObject*& object) {
|
Bitstream& Bitstream::operator>>(QObject*& object) {
|
||||||
const QMetaObject* metaObject;
|
ObjectReader objectReader;
|
||||||
_metaObjectStreamer >> metaObject;
|
_metaObjectStreamer >> objectReader;
|
||||||
if (!metaObject) {
|
object = objectReader.read(*this);
|
||||||
object = NULL;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
readProperties(object = metaObject->newInstance());
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +389,14 @@ Bitstream& Bitstream::operator<<(const QMetaObject* metaObject) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) {
|
Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) {
|
||||||
_metaObjectStreamer >> metaObject;
|
ObjectReader objectReader;
|
||||||
|
_metaObjectStreamer >> objectReader;
|
||||||
|
metaObject = objectReader.getMetaObject();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitstream& Bitstream::operator>>(ObjectReader& objectReader) {
|
||||||
|
_metaObjectStreamer >> objectReader;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,21 +441,111 @@ Bitstream& Bitstream::operator>>(SharedObjectPointer& object) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitstream& Bitstream::operator<(const QMetaObject* metaObject) {
|
Bitstream& Bitstream::operator<(const QMetaObject* metaObject) {
|
||||||
return *this << (metaObject ? QByteArray::fromRawData(metaObject->className(),
|
if (!metaObject) {
|
||||||
strlen(metaObject->className())) : QByteArray());
|
return *this << QByteArray();
|
||||||
|
}
|
||||||
|
*this << QByteArray::fromRawData(metaObject->className(), strlen(metaObject->className()));
|
||||||
|
if (_metadataType == NO_METADATA) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
int storedPropertyCount = 0;
|
||||||
|
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||||
|
QMetaProperty property = metaObject->property(i);
|
||||||
|
if (property.isStored() && getTypeStreamers().contains(property.userType())) {
|
||||||
|
storedPropertyCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*this << storedPropertyCount;
|
||||||
|
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||||
|
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||||
|
QMetaProperty property = metaObject->property(i);
|
||||||
|
if (!property.isStored()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType());
|
||||||
|
if (!typeStreamer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_typeStreamerStreamer << typeStreamer;
|
||||||
|
if (_metadataType == FULL_METADATA) {
|
||||||
|
*this << QByteArray::fromRawData(property.name(), strlen(property.name()));
|
||||||
|
} else {
|
||||||
|
hash.addData(property.name(), strlen(property.name()) + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_metadataType == HASH_METADATA) {
|
||||||
|
QByteArray hashResult = hash.result();
|
||||||
|
write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitstream& Bitstream::operator>(const QMetaObject*& metaObject) {
|
Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
|
||||||
QByteArray className;
|
QByteArray className;
|
||||||
*this >> className;
|
*this >> className;
|
||||||
if (className.isEmpty()) {
|
if (className.isEmpty()) {
|
||||||
metaObject = NULL;
|
objectReader = ObjectReader();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
metaObject = getMetaObjects().value(className);
|
const QMetaObject* metaObject = getMetaObjects().value(className);
|
||||||
if (!metaObject) {
|
if (!metaObject) {
|
||||||
qWarning() << "Unknown class name: " << className << "\n";
|
qWarning() << "Unknown class name: " << className << "\n";
|
||||||
}
|
}
|
||||||
|
if (_metadataType == NO_METADATA) {
|
||||||
|
objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
int storedPropertyCount;
|
||||||
|
*this >> storedPropertyCount;
|
||||||
|
QVector<PropertyReader> properties(storedPropertyCount);
|
||||||
|
for (int i = 0; i < storedPropertyCount; i++) {
|
||||||
|
const TypeStreamer* typeStreamer;
|
||||||
|
*this >> typeStreamer;
|
||||||
|
QMetaProperty property = QMetaProperty();
|
||||||
|
if (_metadataType == FULL_METADATA) {
|
||||||
|
QByteArray propertyName;
|
||||||
|
*this >> propertyName;
|
||||||
|
if (metaObject) {
|
||||||
|
property = metaObject->property(metaObject->indexOfProperty(propertyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
properties[i] = PropertyReader(typeStreamer, property);
|
||||||
|
}
|
||||||
|
// for hash metadata, check the names/types of the properties as well as the name hash against our own class
|
||||||
|
if (_metadataType == HASH_METADATA) {
|
||||||
|
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||||
|
if (metaObject) {
|
||||||
|
int propertyIndex = 0;
|
||||||
|
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||||
|
QMetaProperty property = metaObject->property(i);
|
||||||
|
if (!property.isStored()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType());
|
||||||
|
if (!typeStreamer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (propertyIndex >= properties.size() || properties.at(propertyIndex).getStreamer() != typeStreamer) {
|
||||||
|
objectReader = ObjectReader(className, metaObject, properties);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
hash.addData(property.name(), strlen(property.name()) + 1);
|
||||||
|
propertyIndex++;
|
||||||
|
}
|
||||||
|
if (propertyIndex != properties.size()) {
|
||||||
|
objectReader = ObjectReader(className, metaObject, properties);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QByteArray localHashResult = hash.result();
|
||||||
|
QByteArray remoteHashResult(localHashResult.size(), 0);
|
||||||
|
read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE);
|
||||||
|
if (metaObject && localHashResult == remoteHashResult) {
|
||||||
|
objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
objectReader = ObjectReader(className, metaObject, properties);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,12 +602,9 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) {
|
||||||
}
|
}
|
||||||
QPointer<SharedObject>& pointer = _weakSharedObjectHash[id];
|
QPointer<SharedObject>& pointer = _weakSharedObjectHash[id];
|
||||||
if (pointer) {
|
if (pointer) {
|
||||||
const QMetaObject* metaObject;
|
ObjectReader objectReader;
|
||||||
_metaObjectStreamer >> metaObject;
|
_metaObjectStreamer >> objectReader;
|
||||||
if (metaObject != pointer->metaObject()) {
|
objectReader.read(*this, pointer.data());
|
||||||
qWarning() << "Class mismatch: " << pointer->metaObject()->className() << metaObject->className();
|
|
||||||
}
|
|
||||||
readProperties(pointer.data());
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
QObject* rawObject;
|
QObject* rawObject;
|
||||||
|
@ -533,20 +623,6 @@ void Bitstream::clearSharedObject(QObject* object) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bitstream::readProperties(QObject* object) {
|
|
||||||
const QMetaObject* metaObject = object->metaObject();
|
|
||||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
|
||||||
QMetaProperty property = metaObject->property(i);
|
|
||||||
if (!property.isStored(object)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const TypeStreamer* streamer = getTypeStreamers().value(property.userType());
|
|
||||||
if (streamer) {
|
|
||||||
property.write(object, streamer->read(*this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<QByteArray, const QMetaObject*>& Bitstream::getMetaObjects() {
|
QHash<QByteArray, const QMetaObject*>& Bitstream::getMetaObjects() {
|
||||||
static QHash<QByteArray, const QMetaObject*> metaObjects;
|
static QHash<QByteArray, const QMetaObject*> metaObjects;
|
||||||
return metaObjects;
|
return metaObjects;
|
||||||
|
@ -562,3 +638,53 @@ QHash<int, const TypeStreamer*>& Bitstream::getTypeStreamers() {
|
||||||
return typeStreamers;
|
return typeStreamers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<PropertyReader> Bitstream::getPropertyReaders(const QMetaObject* metaObject) {
|
||||||
|
QVector<PropertyReader> propertyReaders;
|
||||||
|
if (!metaObject) {
|
||||||
|
return propertyReaders;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||||
|
QMetaProperty property = metaObject->property(i);
|
||||||
|
if (!property.isStored()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType());
|
||||||
|
if (typeStreamer) {
|
||||||
|
propertyReaders.append(PropertyReader(typeStreamer, property));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return propertyReaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject,
|
||||||
|
const QVector<PropertyReader>& properties) :
|
||||||
|
_className(className),
|
||||||
|
_metaObject(metaObject),
|
||||||
|
_properties(properties) {
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject* ObjectReader::read(Bitstream& in, QObject* object) const {
|
||||||
|
if (!object && _metaObject) {
|
||||||
|
object = _metaObject->newInstance();
|
||||||
|
}
|
||||||
|
foreach (const PropertyReader& property, _properties) {
|
||||||
|
property.read(in, object);
|
||||||
|
}
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint qHash(const ObjectReader& objectReader, uint seed) {
|
||||||
|
return qHash(objectReader.getClassName(), seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyReader::PropertyReader(const TypeStreamer* streamer, const QMetaProperty& property) :
|
||||||
|
_streamer(streamer),
|
||||||
|
_property(property) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyReader::read(Bitstream& in, QObject* object) const {
|
||||||
|
QVariant value = _streamer->read(in);
|
||||||
|
if (_property.isValid() && object) {
|
||||||
|
_property.write(object, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#define __interface__Bitstream__
|
#define __interface__Bitstream__
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QMetaProperty>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QScriptString>
|
#include <QScriptString>
|
||||||
|
@ -29,7 +30,9 @@ class QUrl;
|
||||||
class Attribute;
|
class Attribute;
|
||||||
class AttributeValue;
|
class AttributeValue;
|
||||||
class Bitstream;
|
class Bitstream;
|
||||||
|
class ObjectReader;
|
||||||
class OwnedAttributeValue;
|
class OwnedAttributeValue;
|
||||||
|
class PropertyReader;
|
||||||
class TypeStreamer;
|
class TypeStreamer;
|
||||||
|
|
||||||
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
|
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
|
||||||
|
@ -187,7 +190,7 @@ public:
|
||||||
|
|
||||||
class ReadMappings {
|
class ReadMappings {
|
||||||
public:
|
public:
|
||||||
QHash<int, const QMetaObject*> metaObjectValues;
|
QHash<int, ObjectReader> metaObjectValues;
|
||||||
QHash<int, const TypeStreamer*> typeStreamerValues;
|
QHash<int, const TypeStreamer*> typeStreamerValues;
|
||||||
QHash<int, AttributePointer> attributeValues;
|
QHash<int, AttributePointer> attributeValues;
|
||||||
QHash<int, QScriptString> scriptStringValues;
|
QHash<int, QScriptString> scriptStringValues;
|
||||||
|
@ -300,6 +303,7 @@ public:
|
||||||
|
|
||||||
Bitstream& operator<<(const QMetaObject* metaObject);
|
Bitstream& operator<<(const QMetaObject* metaObject);
|
||||||
Bitstream& operator>>(const QMetaObject*& metaObject);
|
Bitstream& operator>>(const QMetaObject*& metaObject);
|
||||||
|
Bitstream& operator>>(ObjectReader& objectReader);
|
||||||
|
|
||||||
Bitstream& operator<<(const TypeStreamer* streamer);
|
Bitstream& operator<<(const TypeStreamer* streamer);
|
||||||
Bitstream& operator>>(const TypeStreamer*& streamer);
|
Bitstream& operator>>(const TypeStreamer*& streamer);
|
||||||
|
@ -314,7 +318,7 @@ public:
|
||||||
Bitstream& operator>>(SharedObjectPointer& object);
|
Bitstream& operator>>(SharedObjectPointer& object);
|
||||||
|
|
||||||
Bitstream& operator<(const QMetaObject* metaObject);
|
Bitstream& operator<(const QMetaObject* metaObject);
|
||||||
Bitstream& operator>(const QMetaObject*& metaObject);
|
Bitstream& operator>(ObjectReader& objectReader);
|
||||||
|
|
||||||
Bitstream& operator<(const TypeStreamer* streamer);
|
Bitstream& operator<(const TypeStreamer* streamer);
|
||||||
Bitstream& operator>(const TypeStreamer*& streamer);
|
Bitstream& operator>(const TypeStreamer*& streamer);
|
||||||
|
@ -338,15 +342,13 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void readProperties(QObject* object);
|
|
||||||
|
|
||||||
QDataStream& _underlying;
|
QDataStream& _underlying;
|
||||||
quint8 _byte;
|
quint8 _byte;
|
||||||
int _position;
|
int _position;
|
||||||
|
|
||||||
MetadataType _metadataType;
|
MetadataType _metadataType;
|
||||||
|
|
||||||
RepeatedValueStreamer<const QMetaObject*, const QMetaObject*, const QMetaObject*> _metaObjectStreamer;
|
RepeatedValueStreamer<const QMetaObject*, const QMetaObject*, ObjectReader> _metaObjectStreamer;
|
||||||
RepeatedValueStreamer<const TypeStreamer*> _typeStreamerStreamer;
|
RepeatedValueStreamer<const TypeStreamer*> _typeStreamerStreamer;
|
||||||
RepeatedValueStreamer<AttributePointer> _attributeStreamer;
|
RepeatedValueStreamer<AttributePointer> _attributeStreamer;
|
||||||
RepeatedValueStreamer<QScriptString> _scriptStringStreamer;
|
RepeatedValueStreamer<QScriptString> _scriptStringStreamer;
|
||||||
|
@ -357,6 +359,7 @@ private:
|
||||||
static QHash<QByteArray, const QMetaObject*>& getMetaObjects();
|
static QHash<QByteArray, const QMetaObject*>& getMetaObjects();
|
||||||
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();
|
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();
|
||||||
static QHash<int, const TypeStreamer*>& getTypeStreamers();
|
static QHash<int, const TypeStreamer*>& getTypeStreamers();
|
||||||
|
static QVector<PropertyReader> getPropertyReaders(const QMetaObject* metaObject);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class T> inline Bitstream& Bitstream::operator<<(const QList<T>& list) {
|
template<class T> inline Bitstream& Bitstream::operator<<(const QList<T>& list) {
|
||||||
|
@ -425,6 +428,48 @@ template<class K, class V> inline Bitstream& Bitstream::operator>>(QHash<K, V>&
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contains the information required to read an object from the stream.
|
||||||
|
class ObjectReader {
|
||||||
|
public:
|
||||||
|
|
||||||
|
ObjectReader(const QByteArray& className = QByteArray(), const QMetaObject* metaObject = NULL,
|
||||||
|
const QVector<PropertyReader>& properties = QVector<PropertyReader>());
|
||||||
|
|
||||||
|
const QByteArray& getClassName() const { return _className; }
|
||||||
|
const QMetaObject* getMetaObject() const { return _metaObject; }
|
||||||
|
|
||||||
|
bool isNull() const { return _className.isEmpty(); }
|
||||||
|
|
||||||
|
QObject* read(Bitstream& in, QObject* object = NULL) const;
|
||||||
|
|
||||||
|
bool operator==(const ObjectReader& other) const { return _className == other._className; }
|
||||||
|
bool operator!=(const ObjectReader& other) const { return _className != other._className; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QByteArray _className;
|
||||||
|
const QMetaObject* _metaObject;
|
||||||
|
QVector<PropertyReader> _properties;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint qHash(const ObjectReader& objectReader, uint seed = 0);
|
||||||
|
|
||||||
|
/// Contains the information required to read an object property from the stream and apply it.
|
||||||
|
class PropertyReader {
|
||||||
|
public:
|
||||||
|
|
||||||
|
PropertyReader(const TypeStreamer* streamer = NULL, const QMetaProperty& property = QMetaProperty());
|
||||||
|
|
||||||
|
const TypeStreamer* getStreamer() const { return _streamer; }
|
||||||
|
|
||||||
|
void read(Bitstream& in, QObject* object) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const TypeStreamer* _streamer;
|
||||||
|
QMetaProperty _property;
|
||||||
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(const QMetaObject*)
|
Q_DECLARE_METATYPE(const QMetaObject*)
|
||||||
|
|
||||||
/// Macro for registering streamable meta-objects.
|
/// Macro for registering streamable meta-objects.
|
||||||
|
|
|
@ -34,9 +34,33 @@ static int streamedBytesReceived = 0;
|
||||||
static int sharedObjectsCreated = 0;
|
static int sharedObjectsCreated = 0;
|
||||||
static int sharedObjectsDestroyed = 0;
|
static int sharedObjectsDestroyed = 0;
|
||||||
|
|
||||||
|
static bool testSerialization(Bitstream::MetadataType metadataType) {
|
||||||
|
QByteArray array;
|
||||||
|
QDataStream outStream(&array, QIODevice::WriteOnly);
|
||||||
|
Bitstream out(outStream, metadataType);
|
||||||
|
SharedObjectPointer testObjectWritten = new TestSharedObjectA(randFloat());
|
||||||
|
out << testObjectWritten;
|
||||||
|
out.flush();
|
||||||
|
|
||||||
|
QDataStream inStream(array);
|
||||||
|
Bitstream in(inStream, metadataType);
|
||||||
|
SharedObjectPointer testObjectRead;
|
||||||
|
in >> testObjectRead;
|
||||||
|
|
||||||
|
if (!testObjectWritten->equals(testObjectRead)) {
|
||||||
|
QDebug debug = qDebug() << "Read/write mismatch";
|
||||||
|
testObjectWritten->dump(debug);
|
||||||
|
testObjectRead->dump(debug);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool MetavoxelTests::run() {
|
bool MetavoxelTests::run() {
|
||||||
|
|
||||||
qDebug() << "Running metavoxel tests...";
|
qDebug() << "Running transmission tests...";
|
||||||
|
qDebug();
|
||||||
|
|
||||||
// seed the random number generator so that our tests are reproducible
|
// seed the random number generator so that our tests are reproducible
|
||||||
srand(0xBAAAAABE);
|
srand(0xBAAAAABE);
|
||||||
|
@ -62,6 +86,14 @@ bool MetavoxelTests::run() {
|
||||||
qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived;
|
qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived;
|
||||||
qDebug() << "Sent" << datagramsSent << "datagrams, received" << datagramsReceived;
|
qDebug() << "Sent" << datagramsSent << "datagrams, received" << datagramsReceived;
|
||||||
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
|
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
|
||||||
|
qDebug();
|
||||||
|
|
||||||
|
qDebug() << "Running serialization tests...";
|
||||||
|
qDebug();
|
||||||
|
|
||||||
|
if (testSerialization(Bitstream::HASH_METADATA) || testSerialization(Bitstream::FULL_METADATA)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << "All tests passed!";
|
qDebug() << "All tests passed!";
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue