diff --git a/.gitignore b/.gitignore index 2f71ded6..2d3bd41f 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,7 @@ civet_server_error.log server.pem archive/ .venv -trickops_logs/ \ No newline at end of file +trickops_logs/ +*.gcda +*.gcno +coverage.info \ No newline at end of file diff --git a/Makefile b/Makefile index c3548abf..6a5f5c57 100644 --- a/Makefile +++ b/Makefile @@ -96,13 +96,16 @@ UTILS_DIRS := \ ${TRICK_HOME}/trick_source/trick_utils/shm \ ${TRICK_HOME}/trick_source/trick_utils/math \ ${TRICK_HOME}/trick_source/trick_utils/units \ - ${TRICK_HOME}/trick_source/trick_utils/unicode + ${TRICK_HOME}/trick_source/trick_utils/unicode \ + ${TRICK_HOME}/trick_source/trick_utils/var_binary_parser + UTILS_OBJS := $(addsuffix /object_$(TRICK_HOST_CPU)/*.o ,$(UTILS_DIRS)) # filter out the directories that make their own libraries UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/comm/%, $(UTILS_OBJS)) UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/math/%, $(UTILS_OBJS)) UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/units/%, $(UTILS_OBJS)) +UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/var_binary_parser/%, $(UTILS_OBJS)) #------------------------------------------------------------------------------- # Specify the contents of: libtrick_pyip.a @@ -306,9 +309,20 @@ sim_test: pytest: make -C share/trick/pymods/trick -code-coverage: test - lcov --capture --directory trick_source/sim_services --output-file coverage_large.info - lcov --remove coverage_large.info '/Library/*' '/usr/*' '*/io_src/*' '*/test/*' -o coverage.info +COVERAGE_DIRS = trick_source/sim_services \ + trick_source/trick_utils/var_binary_parser \ + trick_source/trick_utils/unicode \ + trick_source/trick_utils/units \ + trick_source/trick_utils/interpolator \ + trick_source/trick_utils/comm \ + trick_source/trick_utils/SAIntegrator + +extra-coverage-builds: + @ $(MAKE) test -C trick_source/trick_utils/SAIntegrator + +code-coverage: test extra-coverage-builds + lcov --capture $(addprefix --directory , $(COVERAGE_DIRS)) --output-file coverage_large.info + lcov --remove coverage_large.info '/Library/*' '/usr/*' '*/io_src/*' '*/test/*' '*/unittest/*' -o coverage.info rm coverage_large.info lcov --list coverage.info diff --git a/include/trick/var_binary_parser.hh b/include/trick/var_binary_parser.hh new file mode 100644 index 00000000..eb96f9b0 --- /dev/null +++ b/include/trick/var_binary_parser.hh @@ -0,0 +1,120 @@ +#include +#include +#include +#include + +#include "trick/parameter_types.h" + +class IncorrectUsageException : public std::exception +{ + private: + std::string _message; + public: + IncorrectUsageException(std::string msg) : _message(msg) {} + const char * what() const noexcept override { return _message.c_str(); } +}; + +class ParseTypeException : public IncorrectUsageException +{ + public: + ParseTypeException() : IncorrectUsageException("Mismatched trick type and template call") {} +}; + +class MalformedMessageException : public std::exception +{ + private: + std::string _message; + public: + MalformedMessageException() : _message("Attempted to parse an incorrectly formed message") {} + MalformedMessageException(std::string msg) : _message(msg) {} + const char * what() const noexcept override { return _message.c_str(); } +}; + +typedef union Number { + unsigned char raw_bytes[sizeof(long long)]; // Raw bytes + + uint8_t unsigned_char_val; + int8_t char_val; + bool bool_val; + + wchar_t wchar_val; + uint16_t unsigned_short_val; + int16_t short_val; + + uint32_t unsigned_int_val; + int32_t int_val; + + uint64_t unsigned_long_val; + int64_t long_val; + + unsigned long long unsigned_long_long_val; + long long long_long_val; + + float float_val; + double double_val; +} Number; + + +class Var { + public: + Var () : _has_name(false) {}; + void setValue(const std::vector& bytes, size_t size, TRICK_TYPE type, bool byteswap = false); + void setName(size_t name_size, const std::vector& name_data); + + // The closest to runtime return type polymorphism that I can think of + // There won't be a general case + template + T getValue() const; + + std::string getName() const; + TRICK_TYPE getType() const; + + private: + std::vector value_bytes; + Number getInterpreter () const; + + bool _has_name; + unsigned int _name_length; + std::string _name; + + bool _byteswap; + + TRICK_TYPE _trick_type; + unsigned int _var_size; +}; + +class ParsedBinaryMessage { + public: + ParsedBinaryMessage() : _byteswap(false), _nonames(false) {} + ParsedBinaryMessage (bool byteswap, bool nonames) : _byteswap(byteswap), _nonames(nonames) {} + + void combine (const ParsedBinaryMessage& message); + + int parse (const std::vector& bytes); + + int getMessageType() const; + unsigned int getMessageSize() const; + unsigned int getNumVars() const; + Var getVariable(const std::string& name); + Var getVariable(unsigned int index); + + std::vector variables; + + private: + static bool validateMessageType(int message_type); + + int _message_type; + unsigned int _message_size; + unsigned int _num_vars; + + bool _byteswap; + bool _nonames; + + const static size_t header_size; + const static size_t message_indicator_size; + const static size_t variable_num_size; + const static size_t message_size_size; + const static size_t variable_name_length_size; + const static size_t variable_type_size; + const static size_t variable_size_size; +}; \ No newline at end of file diff --git a/include/trick/variable_server_message_types.h b/include/trick/variable_server_message_types.h index b2e317c3..51793ba4 100644 --- a/include/trick/variable_server_message_types.h +++ b/include/trick/variable_server_message_types.h @@ -13,7 +13,9 @@ typedef enum { VS_SIE_RESOURCE = 2, VS_LIST_SIZE = 3 , VS_STDIO = 4, - VS_SEND_ONCE = 5 + VS_SEND_ONCE = 5, + VS_MIN_CODE = VS_IP_ERROR, + VS_MAX_CODE = VS_SEND_ONCE } VS_MESSAGE_TYPE ; #endif diff --git a/test/SIM_test_varserv/S_overrides.mk b/test/SIM_test_varserv/S_overrides.mk index 45d59b63..05507252 100644 --- a/test/SIM_test_varserv/S_overrides.mk +++ b/test/SIM_test_varserv/S_overrides.mk @@ -1,13 +1,14 @@ + TRICK_CFLAGS += -I./models -TRICK_CXXFLAGS += -I./models -I$(GTEST_HOME)/include -std=c++11 +TRICK_CXXFLAGS += -I./models all: test_client clean: clean_test_client -TEST_CLIENT_LIBS += -L${GTEST_HOME}lib64 -L${GTEST_HOME}lib -lgtest -lgtest_main -lpthread +TEST_CLIENT_LIBS += -L${GTEST_HOME}/lib64 -L${GTEST_HOME}/lib -lgtest -lgtest_main -lpthread -L${TRICK_LIB_DIR} -ltrick_var_binary_parser -test_client: models/test_client/test_client.cpp - cd models/test_client; $(TRICK_CXX) test_client.cpp $(TRICK_SYSTEM_LDFLAGS) $(TRICK_CXXFLAGS) -o test_client $(TEST_CLIENT_LIBS) -std=c++11 +test_client: models/test_client/test_client.cpp + cd models/test_client; $(TRICK_CXX) test_client.cpp -o test_client $(TRICK_CXXFLAGS) -I$(TRICK_HOME)/include $(TEST_CLIENT_LIBS) -std=c++11 clean_test_client: - rm -f models/test_client/test_client + rm -f models/test_client/test_client \ No newline at end of file diff --git a/test/SIM_test_varserv/models/test_client/test_client.cpp b/test/SIM_test_varserv/models/test_client/test_client.cpp index 982806bf..712e096c 100644 --- a/test/SIM_test_varserv/models/test_client/test_client.cpp +++ b/test/SIM_test_varserv/models/test_client/test_client.cpp @@ -13,6 +13,8 @@ #include +#include "trick/var_binary_parser.hh" + #define SOCKET_BUF_SIZE 20480 #define DOUBLE_TOL 1e-5 @@ -92,6 +94,21 @@ class Socket { ret = receive(); } + std::vector receive_bytes() { + unsigned char buffer[SOCKET_BUF_SIZE]; + int numBytes = recv(_socket_fd, buffer, SOCKET_BUF_SIZE, 0); + if (numBytes < 0) { + std::cout << "Failed to read from socket" << std::endl; + } + + std::vector bytes; + for (int i = 0; i < numBytes; i++) { + bytes.push_back(buffer[i]); + } + + return bytes; + } + bool check_for_message_availible(long timeout_sec = 2) { fd_set read_fd_set; struct timeval timeout = { .tv_sec = timeout_sec, .tv_usec = 0 }; @@ -581,9 +598,6 @@ TEST_F (VariableServerTest, CopyAndWriteModes) { // Is this what we want? Maybe we should have more strict communication on whether the data has been staged so the first message isn't incorrect spin(); - // expected = "0 -1234 1234"; - // EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0); - // std::cout << "\tExpected: " << expected << "\n\tActual: " << reply << std::endl; expected = "-1234 1234"; parse_message(socket.receive()); EXPECT_EQ(strcmp_IgnoringWhiteSpace(vars, expected), 0) << "Received: " << vars << " Expected: " << expected; @@ -683,6 +697,163 @@ TEST_F (VariableServerTest, CopyAndWriteModes) { socket.clear_buffered_data(); } +bool getCompleteBinaryMessage(ParsedBinaryMessage& message, Socket& socket) { + static const int max_retries = 5; + + int tries = 0; + bool parse_success = false; + std::vector reply; + + // Try parsing until message received is correct + // May take a cycle or 2 to update properly + while (!parse_success && tries++ < max_retries) { + try { + reply = socket.receive_bytes(); + message.parse(reply); + parse_success = true; + } catch (const MalformedMessageException& ex) { + std::cout << "Attempt " << tries << " failed with exception " << ex.what() << std::endl;; + } + } + + return parse_success; +} + +TEST_F (VariableServerTest, Binary) { + if (socket_status != 0) { + FAIL(); + } + std::vector reply; + socket << "trick.var_binary()\ntrick.var_add(\"vsx.vst.c\")\ntrick.var_add(\"vsx.vst.k\")\ntrick.var_add(\"vsx.vst.o\")\ntrick.var_add(\"vsx.vst.m\")\n"; + + ParsedBinaryMessage message; + + if (!getCompleteBinaryMessage(message, socket)) { + FAIL() << "Parser was not able to interpret the message."; + } + + EXPECT_EQ(message.getMessageType(), 0); + ASSERT_EQ(message.getNumVars(), 4); + + EXPECT_EQ(strcmp(message.variables[0].getName().c_str(), "vsx.vst.c"), 0); + EXPECT_EQ(message.variables[0].getType(), TRICK_SHORT); + EXPECT_EQ(message.variables[0].getValue(), -1234); + + + EXPECT_EQ(strcmp(message.variables[1].getName().c_str(), "vsx.vst.k"), 0); + EXPECT_EQ(message.variables[1].getType(), TRICK_LONG_LONG); + EXPECT_EQ(message.variables[1].getValue(), -12345678912345); + + EXPECT_EQ(strcmp(message.variables[2].getName().c_str(), "vsx.vst.o"), 0); + EXPECT_EQ(message.variables[2].getType(), TRICK_STRING); + std::string actual = message.variables[2].getValue(); + std::string expected = "You will rejoice to hear that no disaster has accompanied the commencement of an enterprise which you have regarded with such evil forebodings. I arrived here yesterday, and my first task is to assure my dear sister of my welfare and increasing confidence in the success of my undertaking.\0"; + EXPECT_EQ(actual.substr(0, actual.size()-1), expected); + + Var variable = message.getVariable("vsx.vst.m"); + EXPECT_EQ(strcmp(variable.getName().c_str(), "vsx.vst.m"), 0); + EXPECT_EQ(variable.getType(), TRICK_BOOLEAN); + EXPECT_EQ(variable.getValue(), true); + + // Switch to nonames + socket << "trick.var_binary_nonames()\n"; + + ParsedBinaryMessage noname_message(false, true); + if (!getCompleteBinaryMessage(noname_message, socket)) { + FAIL() << "Parser was not able to interpret the message."; + } + + EXPECT_EQ(noname_message.getMessageType(), 0); + ASSERT_EQ(noname_message.getNumVars(), 4); + + EXPECT_EQ(strcmp(noname_message.variables[0].getName().c_str(), ""), 0); + EXPECT_EQ(noname_message.variables[0].getType(), TRICK_SHORT); + EXPECT_EQ(noname_message.variables[0].getValue(), -1234); + + EXPECT_EQ(strcmp(noname_message.variables[1].getName().c_str(), ""), 0); + EXPECT_EQ(noname_message.variables[1].getType(), TRICK_LONG_LONG); + EXPECT_EQ(noname_message.variables[1].getValue(), -12345678912345); + + EXPECT_EQ(strcmp(noname_message.variables[2].getName().c_str(), ""), 0); + EXPECT_EQ(noname_message.variables[2].getType(), TRICK_STRING); + actual = noname_message.variables[2].getValue(); + EXPECT_EQ(actual.substr(0, actual.size()-1), expected); + + Var variable_noname = noname_message.getVariable(3); + EXPECT_EQ(strcmp(variable_noname.getName().c_str(), ""), 0); + EXPECT_EQ(variable_noname.getType(), TRICK_BOOLEAN); + EXPECT_EQ(variable_noname.getValue(), true); +} + +TEST_F (VariableServerTest, DISABLED_BinaryByteswap) { + // TODO: VAR_BYTESWAP DOES NOT APPEAR TO WORK CORRECTLY + + std::vector reply; + socket << "trick.var_binary()\ntrick.var_byteswap(False)\ntrick.var_add(\"vsx.vst.f\")\ntrick.var_add(\"vsx.vst.j\")\n"; + + socket.receive_bytes(); + reply = socket.receive_bytes(); + + // Test byteswap + + std::cout << "Message: "; + for (unsigned char byte : reply) { + std::cout << "0x" << std::setw(2) << std::setfill('0') << std::hex << (int)byte << " "; + } + std::cout << std::endl; + + ParsedBinaryMessage message; + + try { + message.parse(reply); + } catch (const MalformedMessageException& ex) { + FAIL() << "Parser threw an exception: " << ex.what(); + } + + EXPECT_EQ(message.getMessageType(), 0); + ASSERT_EQ(message.getNumVars(), 2); + + EXPECT_EQ(strcmp(message.variables[0].getName().c_str(), "vsx.vst.f"), 0); + EXPECT_EQ(message.variables[0].getType(), TRICK_UNSIGNED_INTEGER); + EXPECT_EQ(message.variables[0].getValue(), 123456); + + EXPECT_EQ(strcmp(message.variables[1].getName().c_str(), "vsx.vst.j"), 0); + EXPECT_EQ(message.variables[1].getType(), TRICK_DOUBLE); + EXPECT_EQ(message.variables[1].getValue(), -1234.567890); + + socket << "trick.var_byteswap(True)\n"; + + socket.receive_bytes(); + reply = socket.receive_bytes(); + + std::cout << "Message: "; + for (unsigned char byte : reply) { + std::cout << "0x" << std::setw(2) << std::setfill('0') << std::hex << (int)byte << " "; + } + std::cout << std::endl; + + ParsedBinaryMessage byteswap_message(true, false); + + try { + byteswap_message.parse(reply); + } catch (const MalformedMessageException& ex) { + FAIL() << "Parser threw an exception: " << ex.what(); + } + + EXPECT_EQ(byteswap_message.getMessageType(), 0); + ASSERT_EQ(byteswap_message.getNumVars(), 2); + + EXPECT_EQ(strcmp(byteswap_message.variables[0].getName().c_str(), "vsx.vst.f"), 0); + EXPECT_EQ(byteswap_message.variables[0].getType(), TRICK_UNSIGNED_INTEGER); + EXPECT_EQ(byteswap_message.variables[0].getValue(), 123456); + std::cout << "Byteswap value: " << byteswap_message.variables[0].getValue() << std::endl; + + EXPECT_EQ(strcmp(byteswap_message.variables[1].getName().c_str(), "vsx.vst.j"), 0); + EXPECT_EQ(byteswap_message.variables[1].getType(), TRICK_DOUBLE); + EXPECT_EQ(byteswap_message.variables[1].getValue(), -1234.567890); +} + + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); int result = RUN_ALL_TESTS(); diff --git a/trick_source/trick_utils/SAIntegrator/makefile b/trick_source/trick_utils/SAIntegrator/makefile index 6f647760..cdd6eaeb 100644 --- a/trick_source/trick_utils/SAIntegrator/makefile +++ b/trick_source/trick_utils/SAIntegrator/makefile @@ -3,7 +3,7 @@ RM = rm -rf CC = cc CPP = c++ -CFLAGS = -g -Wall -std=c++11 +CFLAGS = -g -Wall -std=c++11 ${TRICK_CXXFLAGS} INCLUDE_DIRS = -Iinclude OBJDIR = obj LIBDIR = lib diff --git a/trick_source/trick_utils/SAIntegrator/unittest/Makefile b/trick_source/trick_utils/SAIntegrator/unittest/Makefile index d7f90f24..56f8d811 100644 --- a/trick_source/trick_utils/SAIntegrator/unittest/Makefile +++ b/trick_source/trick_utils/SAIntegrator/unittest/Makefile @@ -3,7 +3,7 @@ include ${TRICK_HOME}/share/trick/makefiles/Makefile.common RM = rm -rf -TRICK_CPPFLAGS += -I$(GTEST_HOME)/include -I$(TRICK_HOME)/include -g -Wall -Wextra -std=c++11 ${TRICK_SYSTEM_CXXFLAGS} +TRICK_CPPFLAGS += -I$(GTEST_HOME)/include -I$(TRICK_HOME)/include -g -Wall -Wextra -std=c++11 ${TRICK_CXXFLAGS} INCLUDE_DIRS = -I../include -I$(GTEST_HOME)/include SAI_OBJDIR = obj diff --git a/trick_source/trick_utils/var_binary_parser/.gitignore b/trick_source/trick_utils/var_binary_parser/.gitignore new file mode 100644 index 00000000..2b9516df --- /dev/null +++ b/trick_source/trick_utils/var_binary_parser/.gitignore @@ -0,0 +1,2 @@ +*.o +TEST_var_binary_parser \ No newline at end of file diff --git a/trick_source/trick_utils/var_binary_parser/Makefile b/trick_source/trick_utils/var_binary_parser/Makefile new file mode 100644 index 00000000..906a0930 --- /dev/null +++ b/trick_source/trick_utils/var_binary_parser/Makefile @@ -0,0 +1,52 @@ +RM = rm -rf +CC = cc +CPP = c++ +include ${TRICK_HOME}/share/trick/makefiles/Makefile.common + + +#include ${TRICK_HOME}/share/trick/makefiles/Makefile.tricklib + +CFLAGS = -g -Wall -Wextra -std=c++11 $(TRICK_CXXFLAGS) +INCLUDE_DIRS = -I${TRICK_HOME}/include +OBJDIR = obj +LIBDIR = lib +LIBNAME = libtrick_var_binary_parser.a +LIBOBJS = ${OBJDIR}/var_binary_parser.o + +TRICK_LIB := $(TRICK_LIB_DIR)/$(LIBNAME) +# TRICK_LIB := $(TRICK_HOME)/lib/$(LIBNAME) + + +trick: ${TRICK_LIB} + +install: ${TRICK_LIB} + +all: test + +test: ${LIBDIR}/${LIBNAME} + ${MAKE} -C test + +$(LIBOBJS): $(OBJDIR)/%.o : src/%.cc | $(OBJDIR) + $(CPP) $(CFLAGS) ${INCLUDE_DIRS} -c $< -o $@ + +${LIBDIR}/${LIBNAME}: ${LIBOBJS} | ${LIBDIR} + ar crs $@ ${LIBOBJS} + +${OBJDIR}: + mkdir -p ${OBJDIR} + +${LIBDIR}: + mkdir -p ${LIBDIR} + +${TRICK_LIB}: ${LIBDIR}/${LIBNAME} + cp ${LIBDIR}/${LIBNAME} $(TRICK_LIB) + +clean: + ${RM} -r ${OBJDIR} + ${MAKE} -C test clean + +spotless: + ${RM} ${OBJDIR} + ${RM} ${LIBDIR} + ${MAKE} -C unittest spotless + ${MAKE} -C examples spotless diff --git a/trick_source/trick_utils/var_binary_parser/src/var_binary_parser.cc b/trick_source/trick_utils/var_binary_parser/src/var_binary_parser.cc new file mode 100644 index 00000000..30d3ac4a --- /dev/null +++ b/trick_source/trick_utils/var_binary_parser/src/var_binary_parser.cc @@ -0,0 +1,364 @@ +#include "trick/var_binary_parser.hh" +#include "trick/variable_server_message_types.h" +#include +#include +#include + + +// Utility method +uint64_t bytesToInt(const std::vector& bytes, bool byteswap) { + uint64_t result = 0; + + if (byteswap) { + for (unsigned int i = 0; i < bytes.size(); i++) { + result |= bytes[bytes.size() - 1 - i] << i*8; + } + } else { + for (unsigned int i = 0; i < bytes.size(); i++) { + result |= bytes[i] << i*8; + } + } + + return result; +} + +class MessageIterator { + public: + MessageIterator(const std::vector& container) : _container(container), _index(0) {} + + std::vector slice(unsigned int length) { + unsigned int slice_end = _index + length; + if (_index > _container.size() || slice_end > _container.size()) { + throw MalformedMessageException("Message ends unexpectedly"); + } + + return std::vector(_container.begin() + _index, _container.begin() + slice_end); + } + + void operator+= (int n) { + _index += n; + } + + private: + std::vector _container; + unsigned int _index; +}; + +/************************************************************************** + * Var implementation +**************************************************************************/ + +const size_t ParsedBinaryMessage::header_size = 12; +const size_t ParsedBinaryMessage::message_indicator_size = 4; +const size_t ParsedBinaryMessage::variable_num_size = 4; +const size_t ParsedBinaryMessage::message_size_size = 4; +const size_t ParsedBinaryMessage::variable_name_length_size = 4; +const size_t ParsedBinaryMessage::variable_type_size = 4; +const size_t ParsedBinaryMessage::variable_size_size = 4; + +int ParsedBinaryMessage::parse (const std::vector& bytes){ + if (bytes.size() < header_size) { + throw MalformedMessageException(std::string("Not enough bytes in message to contain header: expected at least 12, got " + std::to_string(bytes.size()))); + } + + MessageIterator messageIterator (bytes); + + // First 4 bytes is message type + _message_type = bytesToInt(messageIterator.slice(message_indicator_size), _byteswap); + if (!validateMessageType(_message_type)) { + throw MalformedMessageException(std::string("Received unknown message type: " + std::to_string(_message_type))); + } + + messageIterator += message_indicator_size; + + // Next 4 bytes is message size + _message_size = bytesToInt(messageIterator.slice(message_size_size), _byteswap); + if (bytes.size() - message_indicator_size != _message_size) { + std::string error_message = "Message size in header (" + std::to_string(_message_size) + ") does not match size of message received (" + std::to_string(bytes.size() - message_indicator_size) + ")"; + throw MalformedMessageException(error_message); + } + + messageIterator += message_size_size; + + // Next 4 bytes is number of variables + _num_vars = bytesToInt(messageIterator.slice(variable_num_size), _byteswap); + messageIterator += variable_num_size; + + // Pull out all of the variables + for (unsigned int i = 0; i < _num_vars; i++) { + Var variable; + + if (!_nonames) { + // Get the name + size_t name_length = bytesToInt(messageIterator.slice(variable_name_length_size), _byteswap); + messageIterator += variable_name_length_size; + + variable.setName(name_length, messageIterator.slice(name_length)); + messageIterator += name_length; + } + + // Parse the type first + int var_type = bytesToInt(messageIterator.slice(variable_type_size), _byteswap); + messageIterator += variable_type_size; + + size_t var_size = bytesToInt(messageIterator.slice(variable_size_size), _byteswap); + messageIterator += variable_size_size; + + variable.setValue(messageIterator.slice(var_size), var_size, static_cast(var_type), _byteswap); + messageIterator += var_size; + + variables.emplace_back(variable); + } + + return 0; +} + +void ParsedBinaryMessage::combine (const ParsedBinaryMessage& other) { + if (_message_type != other._message_type) { + std::string error_message = "Trying to combine two messages with different message indicators (" + std::to_string(_message_type) + " and " + std::to_string(other._message_type) + ")"; + throw MalformedMessageException(error_message); + } + + // Combined size - subtract the header size from other message size + _message_size += other._message_size - message_size_size - variable_num_size; + + // Combine variables + _num_vars += other._num_vars; + variables.insert(variables.end(), other.variables.begin(), other.variables.end()); + + // Other error checking - duplicate variables? +} + +Var ParsedBinaryMessage::getVariable(const std::string& name) { + if (_nonames) + throw IncorrectUsageException("Cannot fetch variables by name in noname message"); + + for (auto variable : variables) { + if (variable.getName() == name) + return variable; + } + + throw IncorrectUsageException("Variable " + name + " does not exist in this message."); +} + +Var ParsedBinaryMessage::getVariable(unsigned int index) { + if (index >= variables.size()) { + throw IncorrectUsageException("Variable index " + std::to_string(index) + " does not exist in this message."); + } + + return variables[index]; +} + +int ParsedBinaryMessage::getMessageType() const { + return _message_type; +} + +unsigned int ParsedBinaryMessage::getMessageSize() const { + return _message_size; +} + +unsigned int ParsedBinaryMessage::getNumVars() const { + return _num_vars; +} + +// Static methods + +bool ParsedBinaryMessage::validateMessageType(int message_type) { + return message_type >= VS_MIN_CODE && message_type <= VS_MAX_CODE; +} + +/************************************************************************** + * Var implementation +**************************************************************************/ + + +void Var::setName(size_t name_size, const std::vector& name_data) { + _has_name = true; + _name_length = name_size; + std::stringstream ss; + + for (unsigned char character : name_data) { + ss << static_cast(character); + } + + _name = ss.str(); +} + + +void Var::setValue(const std::vector& bytes, size_t size, TRICK_TYPE type, bool byteswap) { + _trick_type = type; + _var_size = size; + _byteswap = byteswap; + + value_bytes = std::vector (bytes); +} + +Number Var::getInterpreter() const { + Number interpreted_val; + + if (_byteswap) { + for (unsigned int i = 0; i < _var_size; i++) { + interpreted_val.raw_bytes[i] = value_bytes[_var_size - 1 - i]; + } + } else { + for (unsigned int i = 0; i < _var_size; i++) { + interpreted_val.raw_bytes[i] = value_bytes[i]; + } + } + + return interpreted_val; +} + +std::string Var::getName() const { + if (_has_name) { + return _name; + } else { + return std::string (""); + } +} + +TRICK_TYPE Var::getType() const { + return _trick_type; +} + + +// Template specialization for each supported type +template<> +char Var::getValue () const { + if (_trick_type != TRICK_CHARACTER) { + throw ParseTypeException(); + } + + return getInterpreter().char_val; +} + +template<> +unsigned char Var::getValue () const { + if (_trick_type != TRICK_UNSIGNED_CHARACTER) { + throw ParseTypeException(); + } + + return getInterpreter().unsigned_char_val; +} + +template<> +bool Var::getValue () const { + if (_trick_type != TRICK_BOOLEAN) { + throw ParseTypeException(); + } + + return getInterpreter().bool_val; +} + +template<> +short Var::getValue () const { + if (_trick_type != TRICK_SHORT) { + throw ParseTypeException(); + } + + return getInterpreter().short_val; +} + +template<> +unsigned short Var::getValue () const { + if (_trick_type != TRICK_UNSIGNED_SHORT) { + throw ParseTypeException(); + } + + return getInterpreter().unsigned_short_val; +} + +template<> +unsigned int Var::getValue () const { + if (_trick_type != TRICK_UNSIGNED_INTEGER) { + throw ParseTypeException(); + } + + return getInterpreter().unsigned_int_val; +} + +template<> +int Var::getValue () const { + if (_trick_type != TRICK_INTEGER) { + throw ParseTypeException(); + } + + return getInterpreter().int_val; +} + +template<> +long Var::getValue () const { + if (_trick_type != TRICK_LONG) { + throw ParseTypeException(); + } + + return getInterpreter().long_val; +} + +template<> +unsigned long Var::getValue () const { + if (_trick_type != TRICK_UNSIGNED_LONG) { + throw ParseTypeException(); + } + + return getInterpreter().unsigned_long_val; +} + +template<> +long long Var::getValue () const { + if (_trick_type != TRICK_LONG_LONG) { + throw ParseTypeException(); + } + + return getInterpreter().long_long_val; +} + +template<> +unsigned long long Var::getValue () const { + if (_trick_type != TRICK_UNSIGNED_LONG_LONG) { + throw ParseTypeException(); + } + + return getInterpreter().unsigned_long_long_val; +} + +template<> +float Var::getValue () const { + if (_trick_type != TRICK_FLOAT) { + throw ParseTypeException(); + } + + return getInterpreter().float_val; +} + +template<> +double Var::getValue () const { + if (_trick_type != TRICK_DOUBLE) { + throw ParseTypeException(); + } + + return getInterpreter().double_val; +} + +template <> +std::string Var::getValue () const { + if (_trick_type != TRICK_STRING) { + throw ParseTypeException(); + } + + std::stringstream stream; + for (unsigned char character : value_bytes) { + stream << static_cast(character); + } + + return stream.str(); +} + +template <> +wchar_t Var::getValue () const { + if (_trick_type != TRICK_WCHAR) { + throw ParseTypeException(); + } + + return getInterpreter().wchar_val; +} diff --git a/trick_source/trick_utils/var_binary_parser/test/Makefile b/trick_source/trick_utils/var_binary_parser/test/Makefile new file mode 100644 index 00000000..27d68d8c --- /dev/null +++ b/trick_source/trick_utils/var_binary_parser/test/Makefile @@ -0,0 +1,37 @@ +#SYNOPSIS: +# +## make [all] - makes everything. +# make TARGET - makes the given target. +# # make clean - removes all files generated by make. + +# Flags passed to the preprocessor. +CPPFLAGS += -g -Wall -Wextra -std=c++11 ${TRICK_SYSTEM_CXXFLAGS} -I../include -I${TRICK_HOME}/include + +# TRICK_LIBS = ${TRICK_LIB_DIR}/libtrick_units.a +EXEC_LINK_LIBS += -L${GTEST_HOME}/lib64 -L${GTEST_HOME}/lib -lgtest -lgtest_main + +#Added pthreads for Ubuntu +EXEC_LINK_LIBS += -lpthread + +OTHER_OBJECTS = ../obj/*.o + +TESTS = TEST_var_binary_parser + +GTEST_HEADERS = $(GTEST_DIR)/include/gtest/*.h \ + $(GTEST_DIR)/include/gtest/internal/*.h + +all : $(TESTS) test + +test: $(TESTS) + ./TEST_var_binary_parser --gtest_output=xml:${TRICK_HOME}/trick_test/TEST_var_binary_parser.xml + +clean : + rm -f $(TESTS) + rm -f *.o + +TEST_var_binary_parser.o : TEST_var_binary_parser.cc $(OTHER_OBJECTS) + $(CXX) $(CPPFLAGS) -c $< + +TEST_var_binary_parser : TEST_var_binary_parser.o $(OTHER_OBJECTS) + @echo 'Building TEST_var_binary_parser' + $(CXX) $(TRICK_SYSTEM_LDFLAGS) -o $@ $^ -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(LIBS) $(EXEC_LINK_LIBS) \ No newline at end of file diff --git a/trick_source/trick_utils/var_binary_parser/test/TEST_var_binary_parser.cc b/trick_source/trick_utils/var_binary_parser/test/TEST_var_binary_parser.cc new file mode 100644 index 00000000..f81021e9 --- /dev/null +++ b/trick_source/trick_utils/var_binary_parser/test/TEST_var_binary_parser.cc @@ -0,0 +1,1030 @@ +#include +#include +#include +#include +#include +// #include +#include "trick/var_binary_parser.hh" + +// int hi = 161 +std::vector test_var_1 = {0x02, 0x00, 0x00, 0x00, 0x68, 0x69, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00}; + +// string my_string = "99 red balloons" +std::vector test_var_2 = {0x09, 0x00, 0x00, 0x00, 0x6D, 0x79, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x39, 0x39, 0x20, 0x72, 0x65, 0x64, 0x20, 0x62, 0x61, 0x6C, 0x6C, 0x6F, 0x6F, 0x6E, 0x73}; + +// Byteswapped +std::vector test_var_1_byteswap = {0x00, 0x00, 0x00, 0x02, 0x68, 0x69, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa1}; +std::vector test_var_2_byteswap = {0x00, 0x00, 0x00, 0x09, 0x6D, 0x79, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x39, 0x39, 0x20, 0x72, 0x65, 0x64, 0x20, 0x62, 0x61, 0x6C, 0x6C, 0x6F, 0x6F, 0x6E, 0x73}; + +// noname variables +std::vector test_var_1_noname = {0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00}; +std::vector test_var_2_noname = {0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x39, 0x39, 0x20, 0x72, 0x65, 0x64, 0x20, 0x62, 0x61, 0x6C, 0x6C, 0x6F, 0x6F, 0x6E, 0x73}; + + +TEST (BinaryParserTest, ParseMessageType) { + ParsedBinaryMessage message; + std::vector bytes = {0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + EXPECT_EQ(message.getMessageType(), 5); +} + +TEST (BinaryParserTest, ParseMessageSize) { + ParsedBinaryMessage message; + std::vector bytes = {0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + EXPECT_EQ(message.getMessageSize(), 8); +} + +TEST (BinaryParserTest, ParseNumVars) { + ParsedBinaryMessage message; + std::vector bytes = {0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}; + + try { + message.parse(bytes); + FAIL() << "Expected a thrown exception"; + } catch(const MalformedMessageException& ex) { + EXPECT_STREQ(ex.what(), "Message ends unexpectedly"); + } catch(...) { + FAIL() << "Incorrect exception thrown"; + } + + EXPECT_EQ(message.getNumVars(), 1); +} + +TEST (BinaryParserTest, ValidateHeaderSize) { + ParsedBinaryMessage message; + std::vector bytes = {0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00}; + + try { + message.parse(bytes); + FAIL() << "Expected excpetion to be thrown"; + } catch(const MalformedMessageException& ex) { + EXPECT_STREQ(ex.what(), "Not enough bytes in message to contain header: expected at least 12, got 11"); + } catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ValidateMessageType) { + ParsedBinaryMessage message; + std::vector bytes = {0xFF, 0xFF, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00}; + + try { + message.parse(bytes); + FAIL() << "Expected excpetion to be thrown"; + } catch(const MalformedMessageException& ex) { + EXPECT_STREQ(ex.what(), "Received unknown message type: 65535"); + } catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ValidateMessageSize) { + ParsedBinaryMessage message; + std::vector bytes = {0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}; + + try { + message.parse(bytes); + FAIL() << "Expected excpetion to be thrown"; + } catch(const MalformedMessageException& ex) { + EXPECT_STREQ(ex.what(), "Message size in header (8) does not match size of message received (10)"); + } catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseFirstVariableName) { + ParsedBinaryMessage message; + std::vector bytes = {0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x69, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00}; + + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + ASSERT_EQ(message.variables.size(), 1); + EXPECT_EQ(message.variables[0].getName(), "hi"); +} + +TEST (BinaryParserTest, ParseFirstVariableType) { + ParsedBinaryMessage message; + std::vector bytes = {0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x69, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00}; + + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + ASSERT_EQ(message.variables.size(), 1); + EXPECT_EQ(message.variables[0].getType(), TRICK_INTEGER); +} + +TEST (BinaryParserTest, ParseFirstVariableValue) { + ParsedBinaryMessage message; + std::vector bytes = {0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x69, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00}; + + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + ASSERT_EQ(message.variables.size(), 1); + EXPECT_EQ(message.variables[0].getValue(), 161); +} + +TEST (BinaryParserTest, HandleUnexpectedEnd) { + ParsedBinaryMessage message; + std::vector bytes = {0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x69, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}; + + try { + message.parse(bytes); + FAIL() << "Expected a thrown exception"; + } catch (const MalformedMessageException& ex) { + EXPECT_STREQ(ex.what(), "Message ends unexpectedly"); + } catch (...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseTwoVariables) { + ParsedBinaryMessage message; + + std::vector bytes = {0x01, 0x00, 0x00, 0x00}; + char message_size = 8; + + // Messages are getting more complicated, build them programmatically + + // Calculate and push message size + message_size += test_var_1.size(); + message_size += test_var_2.size(); + bytes.push_back(message_size); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push number of variables + bytes.push_back(2); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push variables + bytes.insert(bytes.end(), test_var_1.begin(), test_var_1.end()); + bytes.insert(bytes.end(), test_var_2.begin(), test_var_2.end()); + + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + ASSERT_EQ(message.variables.size(), 2); + EXPECT_EQ(message.variables[0].getName(), "hi"); + EXPECT_EQ(message.variables[0].getType(), TRICK_INTEGER); + EXPECT_EQ(message.variables[0].getValue(), 161); + EXPECT_EQ(message.variables[1].getName(), "my_string"); + EXPECT_EQ(message.variables[1].getType(), TRICK_STRING); + EXPECT_EQ(message.variables[1].getValue(), "99 red balloons"); +} + +TEST (BinaryParserTest, ParseVariableNoname) { + std::vector bytes = {0x01, 0x00, 0x00, 0x00}; + char message_size = 8; + + // Messages are getting more complicated, build them programmatically + // Calculate and push message size + message_size += test_var_1_noname.size(); + message_size += test_var_2_noname.size(); + bytes.push_back(message_size); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push number of variables + bytes.push_back(2); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push variables + bytes.insert(bytes.end(), test_var_1_noname.begin(), test_var_1_noname.end()); + bytes.insert(bytes.end(), test_var_2_noname.begin(), test_var_2_noname.end()); + + ParsedBinaryMessage message(false, true); + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + ASSERT_EQ(message.variables.size(), 2); + EXPECT_EQ(message.variables[0].getName(), ""); + EXPECT_EQ(message.variables[0].getType(), TRICK_INTEGER); + EXPECT_EQ(message.variables[0].getValue(), 161); + EXPECT_EQ(message.variables[1].getName(), ""); + EXPECT_EQ(message.variables[1].getType(), TRICK_STRING); + EXPECT_EQ(message.variables[1].getValue(), "99 red balloons"); +} + +TEST (BinaryParserTest, GetByNameNoname) { + std::vector bytes = {0x01, 0x00, 0x00, 0x00}; + char message_size = 8; + + // Messages are getting more complicated, build them programmatically + // Calculate and push message size + message_size += test_var_1_noname.size(); + message_size += test_var_2_noname.size(); + bytes.push_back(message_size); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push number of variables + bytes.push_back(2); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push variables + bytes.insert(bytes.end(), test_var_1_noname.begin(), test_var_1_noname.end()); + bytes.insert(bytes.end(), test_var_2_noname.begin(), test_var_2_noname.end()); + + ParsedBinaryMessage message(false, true); + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + try { + message.getVariable("hi"); + FAIL() << "Expected exception thrown"; + } catch (const IncorrectUsageException& ex) { + EXPECT_STREQ(ex.what(), "Cannot fetch variables by name in noname message"); + } catch (...) { + FAIL() << "Incorrect exception thrown."; + } +} + +TEST (BinaryParserTest, ParseVariableByteswap) { + std::vector bytes = {0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x68, 0x69, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa1}; + ParsedBinaryMessage message(true, false); + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + ASSERT_EQ(message.variables.size(), 1); + + EXPECT_EQ(message.variables[0].getName(), "hi"); + EXPECT_EQ(message.variables[0].getType(), TRICK_INTEGER); + EXPECT_EQ(message.variables[0].getValue(), 161); +} + +TEST (BinaryParserTest, ParseVariableByteswap2) { + std::vector bytes = {0x00, 0x00, 0x00, 0x01}; + char message_size = 8; + + // Messages are getting more complicated, build them programmatically + + // Calculate and push message size + message_size += test_var_1_byteswap.size(); + message_size += test_var_2_byteswap.size(); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(message_size); + + // Push number of variables + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(2); + + // Push variables + bytes.insert(bytes.end(), test_var_1_byteswap.begin(), test_var_1_byteswap.end()); + bytes.insert(bytes.end(), test_var_2_byteswap.begin(), test_var_2_byteswap.end()); + + ParsedBinaryMessage message(true, false); + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + ASSERT_EQ(message.variables.size(), 2); + EXPECT_EQ(message.variables[0].getName(), "hi"); + EXPECT_EQ(message.variables[0].getType(), TRICK_INTEGER); + EXPECT_EQ(message.variables[0].getValue(), 161); + EXPECT_EQ(message.variables[1].getName(), "my_string"); + EXPECT_EQ(message.variables[1].getType(), TRICK_STRING); + EXPECT_EQ(message.variables[1].getValue(), "99 red balloons"); +} + +TEST (BinaryParserTest, CombineMessages) { + ParsedBinaryMessage message1; + ParsedBinaryMessage message2; + std::vector bytes1 = {0x01, 0x00, 0x00, 0x00}; + std::vector bytes2 = {0x01, 0x00, 0x00, 0x00}; + + char message_size1 = 8; + char message_size2 = 8; + + // Messages are getting more complicated, build them programmatically + + // Calculate and push message size + message_size1 += test_var_1.size(); + message_size2 += test_var_2.size(); + bytes1.push_back(message_size1); + bytes1.push_back(0); + bytes1.push_back(0); + bytes1.push_back(0); + + bytes2.push_back(message_size2); + bytes2.push_back(0); + bytes2.push_back(0); + bytes2.push_back(0); + + // Push number of variables + bytes1.push_back(1); + bytes1.push_back(0); + bytes1.push_back(0); + bytes1.push_back(0); + + bytes2.push_back(1); + bytes2.push_back(0); + bytes2.push_back(0); + bytes2.push_back(0); + + // Push variables + bytes1.insert(bytes1.end(), test_var_1.begin(), test_var_1.end()); + bytes2.insert(bytes2.end(), test_var_2.begin(), test_var_2.end()); + + try { + message1.parse(bytes1); + message2.parse(bytes2); + message1.combine(message2); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + ASSERT_EQ(message1.variables.size(), 2); + EXPECT_EQ(message1.variables[0].getName(), "hi"); + EXPECT_EQ(message1.variables[0].getType(), TRICK_INTEGER); + EXPECT_EQ(message1.variables[0].getValue(), 161); + EXPECT_EQ(message1.variables[1].getName(), "my_string"); + EXPECT_EQ(message1.variables[1].getType(), TRICK_STRING); + EXPECT_EQ(message1.variables[1].getValue(), "99 red balloons"); +} + + +TEST (BinaryParserTest, CombineMessagesIncorrectType) { + ParsedBinaryMessage message1; + ParsedBinaryMessage message2; + std::vector bytes1 = {0x01, 0x00, 0x00, 0x00}; + std::vector bytes2 = {0x05, 0x00, 0x00, 0x00}; + + char message_size1 = 8; + char message_size2 = 8; + + // Messages are getting more complicated, build them programmatically + + // Calculate and push message size + message_size1 += test_var_1.size(); + message_size2 += test_var_2.size(); + bytes1.push_back(message_size1); + bytes1.push_back(0); + bytes1.push_back(0); + bytes1.push_back(0); + + bytes2.push_back(message_size2); + bytes2.push_back(0); + bytes2.push_back(0); + bytes2.push_back(0); + + // Push number of variables + bytes1.push_back(1); + bytes1.push_back(0); + bytes1.push_back(0); + bytes1.push_back(0); + + bytes2.push_back(1); + bytes2.push_back(0); + bytes2.push_back(0); + bytes2.push_back(0); + + // Push variables + bytes1.insert(bytes1.end(), test_var_1.begin(), test_var_1.end()); + bytes2.insert(bytes2.end(), test_var_2.begin(), test_var_2.end()); + + try { + message1.parse(bytes1); + message2.parse(bytes2); + message1.combine(message2); + FAIL() << "Expected a thrown exception"; + } catch (const MalformedMessageException& ex) { + EXPECT_STREQ(ex.what(), "Trying to combine two messages with different message indicators (1 and 5)"); + } catch (...) { + FAIL() << "Incorrect Exception thrown"; + } +} + +TEST (BinaryParserTest, GetVarByName) { + ParsedBinaryMessage message; + + std::vector bytes = {0x01, 0x00, 0x00, 0x00}; + char message_size = 8; + + // Messages are getting more complicated, build them programmatically + + // Calculate and push message size + message_size += test_var_1.size(); + message_size += test_var_2.size(); + bytes.push_back(message_size); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push number of variables + bytes.push_back(2); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push variables + bytes.insert(bytes.end(), test_var_1.begin(), test_var_1.end()); + bytes.insert(bytes.end(), test_var_2.begin(), test_var_2.end()); + + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + Var var1 = message.getVariable("hi"); + EXPECT_EQ(var1.getName(), "hi"); + EXPECT_EQ(var1.getType(), TRICK_INTEGER); + EXPECT_EQ(var1.getValue(), 161); + + Var var2 = message.getVariable("my_string"); + EXPECT_EQ(var2.getName(), "my_string"); + EXPECT_EQ(var2.getType(), TRICK_STRING); + EXPECT_EQ(var2.getValue(), "99 red balloons"); +} + +TEST (BinaryParserTest, GetVarByIndex) { + ParsedBinaryMessage message; + + std::vector bytes = {0x01, 0x00, 0x00, 0x00}; + char message_size = 8; + + // Messages are getting more complicated, build them programmatically + + // Calculate and push message size + message_size += test_var_1.size(); + message_size += test_var_2.size(); + bytes.push_back(message_size); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push number of variables + bytes.push_back(2); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push variables + bytes.insert(bytes.end(), test_var_1.begin(), test_var_1.end()); + bytes.insert(bytes.end(), test_var_2.begin(), test_var_2.end()); + + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + Var var1 = message.getVariable(0); + EXPECT_EQ(var1.getName(), "hi"); + EXPECT_EQ(var1.getType(), TRICK_INTEGER); + EXPECT_EQ(var1.getValue(), 161); + + Var var2 = message.getVariable(1); + EXPECT_EQ(var2.getName(), "my_string"); + EXPECT_EQ(var2.getType(), TRICK_STRING); + EXPECT_EQ(var2.getValue(), "99 red balloons"); +} + +TEST (BinaryParserTest, GetVarByNameIncorrect) { + ParsedBinaryMessage message; + + std::vector bytes = {0x01, 0x00, 0x00, 0x00}; + char message_size = 8; + + // Messages are getting more complicated, build them programmatically + + // Calculate and push message size + message_size += test_var_1.size(); + message_size += test_var_2.size(); + bytes.push_back(message_size); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push number of variables + bytes.push_back(2); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push variables + bytes.insert(bytes.end(), test_var_1.begin(), test_var_1.end()); + bytes.insert(bytes.end(), test_var_2.begin(), test_var_2.end()); + + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + try { + Var var1 = message.getVariable("no_such_var"); + FAIL() << "Expected exception thrown"; + } catch (const IncorrectUsageException& ex) { + EXPECT_STREQ(ex.what(), "Variable no_such_var does not exist in this message."); + } catch (...) { + FAIL() << "Incorrect exception thrown."; + } + +} + +TEST (BinaryParserTest, GetVarByIndexWrong) { + ParsedBinaryMessage message; + + std::vector bytes = {0x01, 0x00, 0x00, 0x00}; + char message_size = 8; + + // Messages are getting more complicated, build them programmatically + + // Calculate and push message size + message_size += test_var_1.size(); + message_size += test_var_2.size(); + bytes.push_back(message_size); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push number of variables + bytes.push_back(2); + bytes.push_back(0); + bytes.push_back(0); + bytes.push_back(0); + + // Push variables + bytes.insert(bytes.end(), test_var_1.begin(), test_var_1.end()); + bytes.insert(bytes.end(), test_var_2.begin(), test_var_2.end()); + + try { + message.parse(bytes); + } catch (const std::exception& ex) { + FAIL() << "Exception thrown: " << ex.what(); + } + + try { + Var variable = message.getVariable(100); + FAIL() << "Expected exception thrown"; + } catch (const IncorrectUsageException& ex) { + EXPECT_STREQ(ex.what(), "Variable index 100 does not exist in this message."); + } catch (...) { + FAIL() << "Incorrect exception thrown."; + } +} + +/************************************************************************** + * Var parsing type tests +**************************************************************************/ + +TEST (BinaryParserTest, ParseChar) { + Var variable; + std::vector bytes = {0x41}; + variable.setValue(bytes, 1, TRICK_CHARACTER, false); + EXPECT_EQ(variable.getValue(), 'A'); +} + +TEST (BinaryParserTest, ParseCharWrongType) { + Var variable; + std::vector bytes = {0x0a, 0x00, 0x00, 0x00}; + variable.setValue(bytes, 4, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseUnsignedChar) { + Var variable; + std::vector bytes = {0x41}; + variable.setValue(bytes, 1, TRICK_UNSIGNED_CHARACTER, false); + EXPECT_EQ(variable.getValue(), 'A'); +} + +TEST (BinaryParserTest, ParseUnsignedCharWrongType) { + Var variable; + std::vector bytes = {0x0a, 0x00, 0x00, 0x00}; + variable.setValue(bytes, 4, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseBool) { + Var variable; + std::vector bytes = {0x01}; + variable.setValue(bytes, 1, TRICK_BOOLEAN, false); + EXPECT_EQ(variable.getValue(), true); +} + +TEST (BinaryParserTest, ParseBoolWrongType) { + Var variable; + std::vector bytes = {0x0a}; + variable.setValue(bytes, 1, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseShort) { + Var variable; + std::vector bytes = {0x00, 0x80}; + variable.setValue(bytes, 2, TRICK_SHORT, false); + + EXPECT_EQ(variable.getValue(), INT16_MIN); +} + +TEST (BinaryParserTest, ParseShortWrongType) { + Var variable; + std::vector bytes = {0x0a, 0x00, 0x00, 0x00}; + variable.setValue(bytes, 4, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseUnsignedShort) { + Var variable; + + std::vector bytes = {0xFF, 0xFF}; + variable.setValue(bytes, 2, TRICK_UNSIGNED_SHORT, false); + + EXPECT_EQ(variable.getValue(), UINT16_MAX); +} + +TEST (BinaryParserTest, ParseUnsignedShortWrongType) { + Var variable; + std::vector bytes = {0x0a, 0x00, 0x00, 0x00}; + variable.setValue(bytes, 4, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseInt) { + Var variable; + std::vector bytes = {0x01, 0xFF, 0xFF, 0xFF}; + variable.setValue(bytes, 4, TRICK_INTEGER, false); + + EXPECT_EQ(variable.getValue(), -255); +} + +TEST (BinaryParserTest, ParseIntWrongType) { + Var variable; + std::vector bytes = {0x0a, 0x00, 0x00, 0x00}; + variable.setValue(bytes, 4, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseUnsignedInt) { + Var variable; + + std::vector bytes = {0x01, 0xFF, 0xFF, 0xFF}; + variable.setValue(bytes, 4, TRICK_UNSIGNED_INTEGER, false); + + EXPECT_EQ(variable.getValue(), 4294967041); +} + +TEST (BinaryParserTest, ParseUnsignedIntWrongType) { + Var variable; + std::vector bytes = {0x0a, 0x00, 0x00, 0x00}; + variable.setValue(bytes, 4, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseLong) { + Var variable; + + std::vector bytes = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80}; + variable.setValue(bytes, 8, TRICK_LONG, false); + + EXPECT_EQ(variable.getValue(), -9151314442816847873); +} + +TEST (BinaryParserTest, ParseLongWrongType) { + Var variable; + std::vector bytes = {0x0a, 0x00, 0x00, 0x00}; + variable.setValue(bytes, 4, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseUnsignedLong) { + Var variable; + + std::vector bytes = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80}; + variable.setValue(bytes, 8, TRICK_UNSIGNED_LONG, false); + + EXPECT_EQ(variable.getValue(), 9295429630892703743); +} + +TEST (BinaryParserTest, ParseUnsignedLongWrongType) { + Var variable; + std::vector bytes = {0x0a, 0x00, 0x00, 0x00}; + variable.setValue(bytes, 4, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseLongLong) { + Var variable; + + // apparently this can be different by platform so we need to be careful here + size_t long_long_size = sizeof(long long); + std::vector bytes; + for (int i = 0; i < long_long_size-1; i++) { + bytes.push_back(0x00); + } + bytes.push_back(0x80); + + variable.setValue(bytes, long_long_size, TRICK_LONG_LONG, false); + + EXPECT_EQ(variable.getValue(), LLONG_MIN); +} + +TEST (BinaryParserTest, ParseLongLongWrongType) { + Var variable; + std::vector bytes = {0x0a, 0x00, 0x00, 0x00}; + variable.setValue(bytes, 4, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseUnsignedLongLong) { + Var variable; + + // apparently this can be different by platform so we need to be careful here + size_t long_long_size = sizeof(unsigned long long); + std::vector bytes; + for (int i = 0; i < long_long_size-1; i++) { + bytes.push_back(0xFF); + } + bytes.push_back(0x7F); + + variable.setValue(bytes, long_long_size, TRICK_UNSIGNED_LONG_LONG, false); + + EXPECT_EQ(variable.getValue(), LLONG_MAX); +} + +TEST (BinaryParserTest, ParseUnsignedLongLongWrongType) { + Var variable; + std::vector bytes = {0x0a, 0x00, 0x00, 0x00}; + variable.setValue(bytes, 4, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseFloat) { + Var variable; + + //40 86 66 66 + std::vector bytes = {0x66, 0x66, 0x86, 0x40}; + variable.setValue(bytes, 4, TRICK_FLOAT, false); + + // This is supposed to be 4.2, but floating point is gonna be the way it is + float expected = 4.19999980926513671875; + EXPECT_EQ(variable.getValue(), expected); +} + +TEST (BinaryParserTest, ParseFloatWrongType) { + Var variable; + std::vector bytes = {0x0a, 0x00, 0x00, 0x00}; + variable.setValue(bytes, 4, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseDouble) { + Var variable; + + std::vector bytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xc0}; + variable.setValue(bytes, 8, TRICK_DOUBLE, false); + + // This is supposed to be 4.2, but floating point is gonna be the way it is + double expected = -5.0; + EXPECT_EQ(variable.getValue(), expected); +} + +TEST (BinaryParserTest, ParseDoubleWrongType) { + Var variable; + std::vector bytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xc0}; + variable.setValue(bytes, 8, TRICK_STRING, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseString) { + Var variable; + + std::vector bytes = {0x48, 0x69, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67}; + variable.setValue(bytes, 19, TRICK_STRING, false); + + // This is supposed to be 4.2, but floating point is gonna be the way it is + std::string expected = "Hi this is a string"; + EXPECT_EQ(variable.getValue(), expected); +} + +TEST (BinaryParserTest, ParseStringWrongType) { + Var variable; + std::vector bytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xc0}; + variable.setValue(bytes, 8, TRICK_INTEGER, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} + +TEST (BinaryParserTest, ParseWChar) { + Var variable; + + wchar_t test_wchar = L'J'; + std::vector bytes; + + for (int i = 0; i < sizeof(wchar_t); i++) { + bytes.push_back((unsigned char)((test_wchar >> (i*8)) & 0xFF)); + } + + variable.setValue(bytes, sizeof(wchar_t), TRICK_WCHAR, false); + + EXPECT_EQ(variable.getValue(), test_wchar); +} + +TEST (BinaryParserTest, ParseWCharWrongType) { + Var variable; + std::vector bytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xc0}; + variable.setValue(bytes, 8, TRICK_INTEGER, false); + + try { + variable.getValue(); + FAIL() << "Expected exception thrown"; + } + catch(ParseTypeException& ex) { + EXPECT_STREQ(ex.what(), "Mismatched trick type and template call"); + } + catch(...) { + FAIL() << "Incorrect exception thrown"; + } +} \ No newline at end of file