mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 17:54:20 +02:00
Working on tests for datagram sequencing, added autogenerated ==/!= operators
to mtc.
This commit is contained in:
parent
d5b8d1c0d6
commit
524ceb4ed2
10 changed files with 414 additions and 3 deletions
|
@ -39,4 +39,5 @@ add_subdirectory(assignment-client)
|
|||
add_subdirectory(domain-server)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(pairing-server)
|
||||
add_subdirectory(voxel-edit)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(voxel-edit)
|
||||
|
|
|
@ -82,6 +82,14 @@ bool AttributeValue::operator==(void* other) const {
|
|||
return _attribute && _attribute->equal(_value, other);
|
||||
}
|
||||
|
||||
bool AttributeValue::operator!=(const AttributeValue& other) const {
|
||||
return _attribute != other._attribute || (_attribute && !_attribute->equal(_value, other._value));
|
||||
}
|
||||
|
||||
bool AttributeValue::operator!=(void* other) const {
|
||||
return !_attribute || !_attribute->equal(_value, other);
|
||||
}
|
||||
|
||||
OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) :
|
||||
AttributeValue(attribute, value) {
|
||||
}
|
||||
|
|
|
@ -105,6 +105,9 @@ public:
|
|||
bool operator==(const AttributeValue& other) const;
|
||||
bool operator==(void* other) const;
|
||||
|
||||
bool operator!=(const AttributeValue& other) const;
|
||||
bool operator!=(void* other) const;
|
||||
|
||||
protected:
|
||||
|
||||
AttributePointer _attribute;
|
||||
|
|
|
@ -415,12 +415,16 @@ public:
|
|||
#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \
|
||||
Bitstream& operator<<(Bitstream& out, const X& obj); \
|
||||
Bitstream& operator>>(Bitstream& in, X& obj); \
|
||||
bool operator==(const X& first, const X& second); \
|
||||
bool operator!=(const X& first, const X& second); \
|
||||
static const int* _TypePtr##X = &X::Type;
|
||||
#else
|
||||
#define STRINGIFY(x) #x
|
||||
#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \
|
||||
Bitstream& operator<<(Bitstream& out, const X& obj); \
|
||||
Bitstream& operator>>(Bitstream& in, X& obj); \
|
||||
bool operator==(const X& first, const X& second); \
|
||||
bool operator!=(const X& first, const X& second); \
|
||||
static const int* _TypePtr##X = &X::Type; \
|
||||
_Pragma(STRINGIFY(unused(_TypePtr##X)))
|
||||
#endif
|
||||
|
|
10
tests/CMakeLists.txt
Normal file
10
tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
# add the test directories
|
||||
file(GLOB TEST_SUBDIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)
|
||||
foreach(DIR ${TEST_SUBDIRS})
|
||||
if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${DIR})
|
||||
add_subdirectory(${DIR})
|
||||
endif()
|
||||
endforeach()
|
||||
|
32
tests/metavoxels/CMakeLists.txt
Normal file
32
tests/metavoxels/CMakeLists.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
set(TARGET_NAME metavoxel-tests)
|
||||
|
||||
set(ROOT_DIR ../..)
|
||||
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
||||
|
||||
# setup for find modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
|
||||
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5Script REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
||||
setup_hifi_project(${TARGET_NAME} TRUE)
|
||||
|
||||
include(${MACRO_DIR}/AutoMTC.cmake)
|
||||
auto_mtc(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Network Script Widgets)
|
||||
|
||||
#include glm
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
# link in the shared libraries
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(metavoxels ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
|
178
tests/metavoxels/src/MetavoxelTests.cpp
Normal file
178
tests/metavoxels/src/MetavoxelTests.cpp
Normal file
|
@ -0,0 +1,178 @@
|
|||
//
|
||||
// MetavoxelTests.cpp
|
||||
// metavoxel-tests
|
||||
//
|
||||
// Created by Andrzej Kapolka on 2/7/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <DatagramSequencer.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "MetavoxelTests.h"
|
||||
|
||||
MetavoxelTests::MetavoxelTests(int& argc, char** argv) :
|
||||
QCoreApplication(argc, argv) {
|
||||
}
|
||||
|
||||
static int highPriorityMessagesSent = 0;
|
||||
static int unreliableMessagesSent = 0;
|
||||
static int unreliableMessagesReceived = 0;
|
||||
|
||||
bool MetavoxelTests::run() {
|
||||
|
||||
qDebug() << "Running metavoxel tests...";
|
||||
|
||||
// seed the random number generator so that our tests are reproducible
|
||||
srand(0xBAAAAABE);
|
||||
|
||||
// create two endpoints with the same header
|
||||
QByteArray datagramHeader("testheader");
|
||||
Endpoint alice(datagramHeader), bob(datagramHeader);
|
||||
|
||||
alice.setOther(&bob);
|
||||
bob.setOther(&alice);
|
||||
|
||||
// perform a large number of simulation iterations
|
||||
const int SIMULATION_ITERATIONS = 100000;
|
||||
for (int i = 0; i < SIMULATION_ITERATIONS; i++) {
|
||||
if (alice.simulate(i) || bob.simulate(i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Sent " << highPriorityMessagesSent << " high priority messages";
|
||||
|
||||
qDebug() << "Sent " << unreliableMessagesSent << " unreliable messages, received " << unreliableMessagesReceived;
|
||||
|
||||
qDebug() << "All tests passed!";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Endpoint::Endpoint(const QByteArray& datagramHeader) :
|
||||
_sequencer(new DatagramSequencer(datagramHeader)),
|
||||
_highPriorityMessagesToSend(0.0f) {
|
||||
|
||||
connect(_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendDatagram(const QByteArray&)));
|
||||
connect(_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readMessage(Bitstream&)));
|
||||
connect(_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)),
|
||||
SLOT(handleHighPriorityMessage(const QVariant&)));
|
||||
}
|
||||
|
||||
static QByteArray createRandomBytes() {
|
||||
const int MIN_BYTES = 4;
|
||||
const int MAX_BYTES = 16;
|
||||
QByteArray bytes(randIntInRange(MIN_BYTES, MAX_BYTES), 0);
|
||||
for (int i = 0; i < bytes.size(); i++) {
|
||||
bytes[i] = rand();
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static QVariant createRandomMessage() {
|
||||
switch (randIntInRange(0, 2)) {
|
||||
case 0: {
|
||||
TestMessageA message = { randomBoolean(), rand(), randFloat() };
|
||||
return QVariant::fromValue(message);
|
||||
}
|
||||
case 1: {
|
||||
TestMessageB message = { createRandomBytes() };
|
||||
return QVariant::fromValue(message);
|
||||
}
|
||||
case 2:
|
||||
default: {
|
||||
TestMessageC message;
|
||||
message.foo = randomBoolean();
|
||||
message.bar = rand();
|
||||
message.baz = randFloat();
|
||||
message.bong.foo = createRandomBytes();
|
||||
return QVariant::fromValue(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMessage) {
|
||||
int type = firstMessage.userType();
|
||||
if (secondMessage.userType() != type) {
|
||||
return false;
|
||||
}
|
||||
if (type == TestMessageA::Type) {
|
||||
return firstMessage.value<TestMessageA>() == secondMessage.value<TestMessageA>();
|
||||
} else if (type == TestMessageB::Type) {
|
||||
return firstMessage.value<TestMessageB>() == secondMessage.value<TestMessageB>();
|
||||
} else if (type == TestMessageC::Type) {
|
||||
return firstMessage.value<TestMessageC>() == secondMessage.value<TestMessageC>();
|
||||
} else {
|
||||
return firstMessage == secondMessage;
|
||||
}
|
||||
}
|
||||
|
||||
bool Endpoint::simulate(int iterationNumber) {
|
||||
// enqueue some number of high priority messages
|
||||
const float MIN_HIGH_PRIORITY_MESSAGES = 0.0f;
|
||||
const float MAX_HIGH_PRIORITY_MESSAGES = 2.0f;
|
||||
_highPriorityMessagesToSend += randFloatInRange(MIN_HIGH_PRIORITY_MESSAGES, MAX_HIGH_PRIORITY_MESSAGES);
|
||||
while (_highPriorityMessagesToSend >= 1.0f) {
|
||||
QVariant message = createRandomMessage();
|
||||
_highPriorityMessagesSent.append(message);
|
||||
_sequencer->sendHighPriorityMessage(message);
|
||||
highPriorityMessagesSent++;
|
||||
_highPriorityMessagesToSend -= 1.0f;
|
||||
}
|
||||
|
||||
// send a packet
|
||||
try {
|
||||
Bitstream& out = _sequencer->startPacket();
|
||||
SequencedTestMessage message = { iterationNumber, createRandomMessage() };
|
||||
_unreliableMessagesSent.append(message);
|
||||
unreliableMessagesSent++;
|
||||
out << message;
|
||||
_sequencer->endPacket();
|
||||
|
||||
} catch (const QString& message) {
|
||||
qDebug() << message;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Endpoint::sendDatagram(const QByteArray& datagram) {
|
||||
// some datagrams are dropped
|
||||
const float DROP_PROBABILITY = 0.1f;
|
||||
if (randFloat() < DROP_PROBABILITY) {
|
||||
return;
|
||||
}
|
||||
_other->_sequencer->receivedDatagram(datagram);
|
||||
}
|
||||
|
||||
void Endpoint::handleHighPriorityMessage(const QVariant& message) {
|
||||
if (_other->_highPriorityMessagesSent.isEmpty()) {
|
||||
throw QString("Received unsent/already sent high priority message.");
|
||||
}
|
||||
QVariant sentMessage = _other->_highPriorityMessagesSent.takeFirst();
|
||||
if (!messagesEqual(message, sentMessage)) {
|
||||
throw QString("Sent/received high priority message mismatch.");
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint::readMessage(Bitstream& in) {
|
||||
SequencedTestMessage message;
|
||||
in >> message;
|
||||
|
||||
for (QList<SequencedTestMessage>::iterator it = _other->_unreliableMessagesSent.begin();
|
||||
it != _other->_unreliableMessagesSent.end(); it++) {
|
||||
if (it->sequenceNumber == message.sequenceNumber) {
|
||||
if (!messagesEqual(it->submessage, message.submessage)) {
|
||||
throw QString("Sent/received unreliable message mismatch.");
|
||||
}
|
||||
_other->_unreliableMessagesSent.erase(_other->_unreliableMessagesSent.begin(), it + 1);
|
||||
unreliableMessagesReceived++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw QString("Received unsent/already sent unreliable message.");
|
||||
}
|
109
tests/metavoxels/src/MetavoxelTests.h
Normal file
109
tests/metavoxels/src/MetavoxelTests.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
//
|
||||
// MetavoxelTests.h
|
||||
// metavoxel-tests
|
||||
//
|
||||
// Created by Andrzej Kapolka on 2/7/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__MetavoxelTests__
|
||||
#define __interface__MetavoxelTests__
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QVariantList>
|
||||
|
||||
#include <Bitstream.h>
|
||||
|
||||
class DatagramSequencer;
|
||||
class SequencedTestMessage;
|
||||
|
||||
/// Tests various aspects of the metavoxel library.
|
||||
class MetavoxelTests : public QCoreApplication {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
MetavoxelTests(int& argc, char** argv);
|
||||
|
||||
/// Performs our various tests.
|
||||
/// \return true if any of the tests failed.
|
||||
bool run();
|
||||
};
|
||||
|
||||
/// Represents a simulated endpoint.
|
||||
class Endpoint : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Endpoint(const QByteArray& datagramHeader);
|
||||
|
||||
void setOther(Endpoint* other) { _other = other; }
|
||||
|
||||
/// Perform a simulation step.
|
||||
/// \return true if failure was detected
|
||||
bool simulate(int iterationNumber);
|
||||
|
||||
private slots:
|
||||
|
||||
void sendDatagram(const QByteArray& datagram);
|
||||
void handleHighPriorityMessage(const QVariant& message);
|
||||
void readMessage(Bitstream& in);
|
||||
|
||||
private:
|
||||
|
||||
DatagramSequencer* _sequencer;
|
||||
Endpoint* _other;
|
||||
float _highPriorityMessagesToSend;
|
||||
QVariantList _highPriorityMessagesSent;
|
||||
QList<SequencedTestMessage> _unreliableMessagesSent;
|
||||
};
|
||||
|
||||
/// A simple test message.
|
||||
class TestMessageA {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM bool foo;
|
||||
STREAM int bar;
|
||||
STREAM float baz;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(TestMessageA)
|
||||
|
||||
// Another simple test message.
|
||||
class TestMessageB {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM QByteArray foo;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(TestMessageB)
|
||||
|
||||
// A test message that demonstrates inheritance and composition.
|
||||
class TestMessageC : public TestMessageA {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM TestMessageB bong;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(TestMessageC)
|
||||
|
||||
/// Combines a sequence number with a submessage; used for testing unreliable transport.
|
||||
class SequencedTestMessage {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM int sequenceNumber;
|
||||
STREAM QVariant submessage;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(SequencedTestMessage)
|
||||
|
||||
#endif /* defined(__interface__MetavoxelTests__) */
|
14
tests/metavoxels/src/main.cpp
Normal file
14
tests/metavoxels/src/main.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// main.cpp
|
||||
// metavoxel-tests
|
||||
//
|
||||
// Created by Andrzej Kapolka on 2/7/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "MetavoxelTests.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return MetavoxelTests(argc, argv).run();
|
||||
}
|
|
@ -90,7 +90,7 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
|
|||
foreach (const Streamable& str, streamables) {
|
||||
const QString& name = str.clazz.name;
|
||||
|
||||
out << "Bitstream& operator<< (Bitstream& out, const " << name << "& obj) {\n";
|
||||
out << "Bitstream& operator<<(Bitstream& out, const " << name << "& obj) {\n";
|
||||
foreach (const QString& base, str.clazz.bases) {
|
||||
out << " out << static_cast<const " << base << "&>(obj);\n";
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
|
|||
out << " return out;\n";
|
||||
out << "}\n";
|
||||
|
||||
out << "Bitstream& operator>> (Bitstream& in, " << name << "& obj) {\n";
|
||||
out << "Bitstream& operator>>(Bitstream& in, " << name << "& obj) {\n";
|
||||
foreach (const QString& base, str.clazz.bases) {
|
||||
out << " in >> static_cast<" << base << "&>(obj);\n";
|
||||
}
|
||||
|
@ -110,6 +110,58 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
|
|||
out << " return in;\n";
|
||||
out << "}\n";
|
||||
|
||||
out << "bool operator==(const " << name << "& first, const " << name << "& second) {\n";
|
||||
if (str.clazz.bases.isEmpty() && str.fields.isEmpty()) {
|
||||
out << " return true";
|
||||
} else {
|
||||
out << " return ";
|
||||
bool first = true;
|
||||
foreach (const QString& base, str.clazz.bases) {
|
||||
if (!first) {
|
||||
out << " &&\n";
|
||||
out << " ";
|
||||
}
|
||||
out << "static_cast<" << base << "&>(first) == static_cast<" << base << "&>(second)";
|
||||
first = false;
|
||||
}
|
||||
foreach (const QString& field, str.fields) {
|
||||
if (!first) {
|
||||
out << " &&\n";
|
||||
out << " ";
|
||||
}
|
||||
out << "first." << field << " == second." << field;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
out << ";\n";
|
||||
out << "}\n";
|
||||
|
||||
out << "bool operator!=(const " << name << "& first, const " << name << "& second) {\n";
|
||||
if (str.clazz.bases.isEmpty() && str.fields.isEmpty()) {
|
||||
out << " return false";
|
||||
} else {
|
||||
out << " return ";
|
||||
bool first = true;
|
||||
foreach (const QString& base, str.clazz.bases) {
|
||||
if (!first) {
|
||||
out << " ||\n";
|
||||
out << " ";
|
||||
}
|
||||
out << "static_cast<" << base << "&>(first) != static_cast<" << base << "&>(second)";
|
||||
first = false;
|
||||
}
|
||||
foreach (const QString& field, str.fields) {
|
||||
if (!first) {
|
||||
out << " ||\n";
|
||||
out << " ";
|
||||
}
|
||||
out << "first." << field << " != second." << field;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
out << ";\n";
|
||||
out << "}\n";
|
||||
|
||||
out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n\n";
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue