Expand SIM_test_varserver (#1459)

* Add tests for alternate ways to open VS port

* Don't connect to varserv when quiet=true in trickops

* Add print to try to help debug hanging trickops test

* Handle multicast connect failures gracefully

* Multicast is disabled by default on mac

* Forgot an important return value

* Take away retries

* Fix issue with restart test

* Revert trickops debugging changes

* Remove debugging accidentally left in [no ci]

* whoops

* Allow retries

* Update trickops.py

* sim test adjustments

* Add docs [no ci]

* wording [no ci]

* Cleanup

* Remove large messages, test that one in unit tests
This commit is contained in:
Jacqueline Deans 2023-02-24 14:44:55 -06:00 committed by GitHub
parent 29fb20c2eb
commit 5f6a5de070
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1041 additions and 240 deletions

View File

@ -63,6 +63,16 @@ trick.var_server_get_hostname()
trick.var_server_get_port()
```
Additional TCP or UDP sockets can be opened as well. Additional TCP sockets operate the same way as the original variable server socket. A UDP socket will only host 1 variable server session, and the responses will be sent to the latest address that sends commands to it.
Note that this is not necessary to allow multiple variable server clients - any number of clients can connect to the original variable server port.
```python
trick.var_server_create_udp_socket( const char * source_address, unsigned short port )
trick.var_server_create_tcp_socket( const char * source_address, unsigned short port )
```
### Commands
The variable server accepts commands in the form of strings. The variable server parses
@ -510,6 +520,8 @@ on your network sends it's information to this address and port so there may be
messages with variable server information available here. Here is some
C code that reads all messages on the variable server channel.
Note that the multicast protocol is disabled by default in MacOS.
```c
#include <stdio.h>
#include <sys/types.h>

View File

@ -1,6 +1,7 @@
#include <unistd.h>
#include <vector>
#include <string>
#include <map>
#include <exception>
#include "trick/parameter_types.h"
@ -54,6 +55,22 @@ typedef union Number {
double double_val;
} Number;
const static std::map<TRICK_TYPE, unsigned int> type_size_map = {{TRICK_CHARACTER, sizeof(char)},
{TRICK_UNSIGNED_CHARACTER, sizeof(unsigned char)},
{TRICK_SHORT, sizeof(short)},
{TRICK_UNSIGNED_SHORT, sizeof(unsigned short)},
{TRICK_INTEGER, sizeof(int)},
{TRICK_UNSIGNED_INTEGER, sizeof(unsigned int)},
{TRICK_LONG, sizeof(long)},
{TRICK_UNSIGNED_LONG, sizeof(unsigned long)},
{TRICK_LONG_LONG, sizeof(long long)},
{TRICK_UNSIGNED_LONG_LONG, sizeof(unsigned long)},
{TRICK_FLOAT, sizeof (float)},
{TRICK_DOUBLE, sizeof (double)},
{TRICK_BOOLEAN, sizeof (bool)},
{TRICK_WCHAR, sizeof (wchar_t)}
};
class Var {
public:
@ -65,10 +82,13 @@ class Var {
// There won't be a general case
template <typename T>
T getValue() const;
std::vector<unsigned char> getRawBytes() const;
int getArraySize() const;
std::string getName() const;
TRICK_TYPE getType() const;
private:
std::vector<unsigned char> value_bytes;
Number getInterpreter () const;
@ -81,16 +101,18 @@ class Var {
TRICK_TYPE _trick_type;
unsigned int _var_size;
unsigned int _arr_length;
};
class ParsedBinaryMessage {
public:
ParsedBinaryMessage() : _byteswap(false), _nonames(false) {}
ParsedBinaryMessage (bool byteswap, bool nonames) : _byteswap(byteswap), _nonames(nonames) {}
ParsedBinaryMessage() : ParsedBinaryMessage(false, false) {}
ParsedBinaryMessage (bool byteswap, bool nonames) : _message_type(0), _message_size(0), _num_vars(0), _byteswap(byteswap), _nonames(nonames) {}
void combine (const ParsedBinaryMessage& message);
int parse (const std::vector<unsigned char>& bytes);
int parse (char * raw_bytes);
int getMessageType() const;
unsigned int getMessageSize() const;

1
test/.gitignore vendored
View File

@ -25,3 +25,4 @@ trick.zip
jitlib
build
S_sie.json
*.ckpnt

View File

@ -0,0 +1,31 @@
import trick
import socket
from trick.unit_test import *
def main():
trick.var_server_set_port(4000)
trick.var_ascii()
trick.real_time_enable()
trick.exec_set_software_frame(0.01)
# trick.set_var_server_info_msg_on()
trick.var_server_create_tcp_socket('localhost', 49000)
trick.var_server_create_udp_socket('', 48000)
# trick.var_server_create_multicast_socket('224.10.10.10','', 47000)
# trick.exec_set_terminate_time(100000.0)
varServerPort = trick.var_server_get_port()
test_output = ( os.getenv("TRICK_HOME") + "/trick_test/SIM_test_varserv.xml" )
# command = 'os.system("./models/test_client/test_client ' + str(varServerPort) + ' --gtest_output=xml:' + test_output + ' &")'
# Start the test client after everything has been initialized (hopefully)
# trick.add_read(1.0, command)
trick.sim_control_panel_set_enabled(True)
if __name__ == "__main__":
main()

View File

@ -11,7 +11,11 @@ def main():
trick.exec_set_software_frame(0.01)
# trick.set_var_server_info_msg_on()
trick.exec_set_terminate_time(100.0)
trick.var_server_create_tcp_socket('localhost', 49000)
trick.var_server_create_udp_socket('', 48000)
# trick.var_server_create_multicast_socket('224.10.10.10','', 47000)
trick.exec_set_terminate_time(1000.0)
varServerPort = trick.var_server_get_port()
test_output = ( os.getenv("TRICK_HOME") + "/trick_test/SIM_test_varserv.xml" )

View File

@ -8,7 +8,8 @@ clean: clean_test_client
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 -o test_client $(TRICK_CXXFLAGS) $(TRICK_SYSTEM_CXXFLAGS) $(TRICK_TEST_FLAGS) -I$(TRICK_HOME)/include $(TEST_CLIENT_LIBS)
cd models/test_client; $(TRICK_CXX) test_client.cpp -o test_client $(TRICK_CXXFLAGS) $(TRICK_SYSTEM_CXXFLAGS) $(TRICK_TEST_FLAGS) -Wno-write-strings -Wno-sign-compare -I$(TRICK_HOME)/include $(TEST_CLIENT_LIBS)
clean_test_client:
rm -f models/test_client/test_client
rm -f models/test_client/test_client

View File

@ -10,14 +10,15 @@
#include <iostream>
#include <functional>
#include <cmath>
#include <ctype.h>
#include <pwd.h>
#include <gtest/gtest.h>
#include "trick/var_binary_parser.hh"
#define SOCKET_BUF_SIZE 20480
#define DOUBLE_TOL 1e-5
#define SOCKET_BUF_SIZE 204800
// Pretend that gtest was kind enough to provide an EXPECT_FEQ operator with a tolerance
#define EXPECT_FEQ(a,b) EXPECT_LE(fabs(a - b), DOUBLE_TOL)
@ -29,12 +30,14 @@ class Socket {
int max_retries = 10;
Socket() : _initialized(false) {}
int init(std::string hostname, int port) {
int init(std::string hostname, int port, int mode = SOCK_STREAM) {
_multicast_socket = false;
_hostname = hostname;
_port = port;
int tries = 0;
while ((_socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 && tries < max_retries) tries++;
while ((_socket_fd = socket(AF_INET, mode, 0)) < 0 && tries < max_retries) tries++;
if (_socket_fd < 0) {
std::cout << "Socket connection failed" << std::endl;
@ -65,12 +68,60 @@ class Socket {
return 0;
}
#ifndef __APPLE__
int init_multicast (std::string hostname, int port) {
_multicast_socket = true;
_hostname = hostname;
_port = port;
int tries = 0;
while ((_socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 && tries < max_retries) tries++;
if (_socket_fd < 0) {
std::cout << "init_multicast: Socket open failed" << std::endl;
return -1;
}
int value = 1;
if (setsockopt(_socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, (socklen_t) sizeof(value)) < 0) {
std::cout << "init_multicast: Socket option failed" << std::endl;
return -1;
}
struct ip_mreq mreq;
// Use setsockopt() to request that the kernel join a multicast group
mreq.imr_multiaddr.s_addr = inet_addr(_hostname.c_str());
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(_socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, (socklen_t) sizeof(mreq)) < 0) {
std::cout << "init_multicast: setsockopt: ip_add_membership failed" << std::endl;
return -1;
}
struct sockaddr_in sockin ;
// Set up destination address
sockin.sin_family = AF_INET;
sockin.sin_addr.s_addr = htonl(INADDR_ANY);
sockin.sin_port = htons(_port);
if ( bind(_socket_fd, (struct sockaddr *) &sockin, (socklen_t) sizeof(sockin)) < 0 ) {
std::cout << "init_multicast: bind failed" << std::endl;
return -1;
}
_initialized = true;
return 0;
}
#endif
int send (std::string message) {
// weird syntax I've never used before - since the send syscall that i'm trying to use is overloaded in this class,
// I have to append :: to the front of it so that the compiler knows to look in the global namespace
int success = ::send(_socket_fd, message.c_str(), message.size(), 0);
if (success < message.size()) {
std::cout << "Failed to send message" << std::endl;
std::cout << "init_multicast: Failed to send message" << std::endl;
}
return success;
}
@ -98,7 +149,7 @@ class Socket {
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::cout << "init_multicast: Failed to read from socket" << std::endl;
}
std::vector<unsigned char> bytes;
@ -139,9 +190,97 @@ class Socket {
std::string _hostname;
int _socket_fd;
bool _initialized;
bool _multicast_socket;
};
class VariableServerTest : public ::testing::Test {
protected:
VariableServerTest() {
socket_status = socket.init("localhost", 40000);
if (socket_status == 0) {
std::stringstream request;
request << "trick.var_set_client_tag(\"VSTest";
request << numSession++;
request << "\") \n";
socket << request.str();
}
}
~VariableServerTest() {
socket << "trick.var_exit()\n";
socket.close();
}
Socket socket;
int socket_status;
static int numSession;
};
class VariableServerUDPTest : public ::testing::Test {
protected:
VariableServerUDPTest() {
socket_status = socket.init("localhost", 48000, SOCK_DGRAM);
if (socket_status == 0) {
std::stringstream request;
request << "trick.var_set_client_tag(\"UDP_VSTest";
request << numSession++;
request << "\") \n";
socket << request.str();
}
}
~VariableServerUDPTest() {
socket.close();
}
Socket socket;
int socket_status;
static int numSession;
};
class VariableServerTestAltListener : public ::testing::Test {
protected:
VariableServerTestAltListener() {
socket_status = socket.init("localhost", 49000);
if (socket_status == 0) {
std::stringstream request;
request << "trick.var_set_client_tag(\"altListener_VSTest";
request << numSession++;
request << "\") \n";
socket << request.str();
}
}
~VariableServerTestAltListener() {
socket << "trick.var_exit()\n";
socket.close();
}
Socket socket;
int socket_status;
static int numSession;
};
int VariableServerTest::numSession = 0;
int VariableServerUDPTest::numSession = 0;
int VariableServerTestAltListener::numSession = 0;
/**********************************************************/
/* Helpful constants and functions */
/**********************************************************/
const int MODE_RUN = 5;
const int MODE_FREEZE = 1;
int strcmp_IgnoringWhiteSpace(std::string s1_str, std::string s2_str) {
const char * s1 = s1_str.c_str();
const char * s2 = s2_str.c_str();
@ -164,32 +303,212 @@ int strcmp_IgnoringWhiteSpace(std::string s1_str, std::string s2_str) {
}
}
class VariableServerTest : public ::testing::Test {
protected:
VariableServerTest() {
socket_status = socket.init("localhost", 40000);
void spin (Socket& socket, int wait_cycles = 5) {
for (int i = 0; i < wait_cycles; i++)
socket.receive();
}
if (socket_status == 0) {
std::stringstream request;
request << "trick.var_set_client_tag(\"VSTest";
request << numSession++;
request << "\") \n";
void wait_for_mode_change (Socket& socket, int expected_mode, int max_wait_iterations = 10) {
int iteration = 0;
std::string mode_reply;
std::string expected_reply = "5\t" + std::to_string(expected_mode) + "\n";
while (iteration++ < max_wait_iterations) {
socket << "trick.var_send_once(\"trick_sys.sched.mode\")\n";
socket >> mode_reply;
if (mode_reply == expected_reply)
break;
}
}
socket << request.str();
}
}
~VariableServerTest() {
socket << "trick.var_exit()\n";
socket.close();
}
void dump_checkpoint (Socket& socket, const std::string& checkpoint_name) {
socket << "trick.var_pause()\ntrick.exec_freeze()\n";
wait_for_mode_change(socket, MODE_FREEZE);
Socket socket;
int socket_status;
static int numSession;
};
std::string checkpoint_cmd = "trick.checkpoint(\"" + checkpoint_name + "\")\n";
socket << checkpoint_cmd;
int VariableServerTest::numSession = 0;
socket << "trick.exec_run()\n";
wait_for_mode_change(socket, MODE_RUN);
socket << "trick.var_unpause()\n";
}
void load_checkpoint (Socket& socket, const std::string& checkpoint_name) {
socket << "trick.var_pause()\ntrick.exec_freeze()\n";
wait_for_mode_change(socket, MODE_FREEZE);
std::string checkpoint_cmd = "trick.load_checkpoint(\"" + checkpoint_name + "\")\n";
socket << checkpoint_cmd;
sleep(5);
socket << "trick.exec_run()\n";
wait_for_mode_change(socket, MODE_RUN);
socket << "trick.var_unpause()\n";
}
/************************************/
/* UDP Tests */
/************************************/
TEST_F (VariableServerUDPTest, Strings) {
if (socket_status != 0) {
FAIL();
}
std::string reply;
socket << "trick.var_send_once(\"vsx.vst.o\")\n";
socket >> reply;
std::string expected("5\tYou 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.");
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
expected = std::string("5\tI am already far north of London, and as I walk in the streets of Petersburgh, I feel a cold northern breeze play upon my cheeks, which braces my nerves and fills me with delight. Do you understand this feeling?");
socket << "trick.var_send_once(\"vsx.vst.p\")\n";
socket >> reply;
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
}
TEST_F (VariableServerUDPTest, AddRemove) {
if (socket_status != 0) {
FAIL();
}
std::string reply;
std::string expected;
socket << "trick.var_add(\"vsx.vst.c\")\n";
socket >> reply;
expected = std::string("0 -1234");
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
socket >> reply;
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
socket << "trick.var_add(\"vsx.vst.m\")\n";
socket >> reply;
expected = std::string("0 -1234 1");
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
socket << "trick.var_remove(\"vsx.vst.m\")\n";
socket >> reply;
expected = std::string("0 -1234");
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
socket << "trick.var_add(\"vsx.vst.n\")\n";
socket >> reply;
expected = std::string("0 -1234 0,1,2,3,4");
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
socket << "trick.var_exit()\n";
}
/*********************************************/
/* Alt Listener Tests */
/*********************************************/
TEST_F (VariableServerTestAltListener, Strings) {
if (socket_status != 0) {
FAIL();
}
std::string reply;
socket << "trick.var_send_once(\"vsx.vst.o\")\n";
socket >> reply;
std::string expected("5\tYou 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.");
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
expected = std::string("5\tI am already far north of London, and as I walk in the streets of Petersburgh, I feel a cold northern breeze play upon my cheeks, which braces my nerves and fills me with delight. Do you understand this feeling?");
socket << "trick.var_send_once(\"vsx.vst.p\")\n";
socket >> reply;
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
}
TEST_F (VariableServerTestAltListener, AddRemove) {
if (socket_status != 0) {
FAIL();
}
std::string reply;
std::string expected;
socket << "trick.var_add(\"vsx.vst.c\")\n";
socket >> reply;
expected = std::string("0 -1234");
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
socket >> reply;
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
socket << "trick.var_add(\"vsx.vst.m\")\n";
socket >> reply;
expected = std::string("0 -1234 1");
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
socket << "trick.var_remove(\"vsx.vst.m\")\n";
socket >> reply;
expected = std::string("0 -1234");
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
socket << "trick.var_add(\"vsx.vst.n\")\n";
socket >> reply;
expected = std::string("0 -1234 0,1,2,3,4");
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
}
TEST_F (VariableServerTestAltListener, RestartAndSet) {
if (socket_status != 0) {
FAIL();
}
std::string reply;
std::string expected;
socket << "trick.var_add(\"vsx.vst.c\")\n";
socket >> reply;
expected = std::string("0\t-1234\n");
EXPECT_EQ(reply, expected);
dump_checkpoint(socket, "reload_file.ckpnt");
socket << "trick.var_add(\"vsx.vst.e\",\"m\")\n";
socket >> reply;
expected = std::string("0\t-1234\t-123456 {m}\n");
socket << "trick.var_set(\"vsx.vst.c\", 5)\n";
socket >> reply;
expected = std::string("0\t5\t-123456 {m}\n");
EXPECT_EQ(reply, expected);
load_checkpoint(socket, "RUN_test/reload_file.ckpnt");
socket >> reply;
expected = std::string("0\t-1234\t-123456\n");
EXPECT_EQ(reply, expected);
}
/*********************************************/
/* Normal case tests */
/*********************************************/
TEST_F (VariableServerTest, Strings) {
if (socket_status != 0) {
@ -222,6 +541,41 @@ TEST_F (VariableServerTest, Strings) {
// EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
}
TEST_F (VariableServerTest, NoExtraTab) {
if (socket_status != 0) {
FAIL();
}
std::string reply;
std::string expected;
socket << "trick.var_add(\"vsx.vst.c\")\n";
socket >> reply;
expected = std::string("0\t-1234\n");
EXPECT_STREQ(reply.c_str(), expected.c_str());
socket >> reply;
EXPECT_STREQ(reply.c_str(), expected.c_str());
socket << "trick.var_add(\"vsx.vst.m\")\n";
socket >> reply;
expected = std::string("0\t-1234\t1\n");
EXPECT_STREQ(reply.c_str(), expected.c_str());
socket << "trick.var_remove(\"vsx.vst.m\")\n";
socket >> reply;
expected = std::string("0\t-1234\n");
socket << "trick.var_add(\"vsx.vst.n\")\n";
socket >> reply;
expected = std::string("0\t-1234\t0,1,2,3,4\n");
EXPECT_STREQ(reply.c_str(), expected.c_str());
}
TEST_F (VariableServerTest, AddRemove) {
if (socket_status != 0) {
FAIL();
@ -256,7 +610,6 @@ TEST_F (VariableServerTest, AddRemove) {
socket >> reply;
expected = std::string("0 -1234 0,1,2,3,4");
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
}
TEST_F (VariableServerTest, BadRefResponse) {
@ -356,8 +709,94 @@ TEST_F (VariableServerTest, Exists) {
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
socket << "trick.var_binary()\n";
std::vector<unsigned char> actual_bytes;
std::vector<unsigned char> expected_bytes;
socket << "trick.var_exists(\"vsx.vst.e\")\n";
actual_bytes = socket.receive_bytes();
expected_bytes = {0x01, 0x00, 0x00, 0x00, 0x01};
EXPECT_EQ(expected_bytes, actual_bytes);
socket << "trick.var_exists(\"vsx.vst.z\")\n";
actual_bytes = socket.receive_bytes();
expected_bytes = {0x01, 0x00, 0x00, 0x00, 0x00};
EXPECT_EQ(expected_bytes, actual_bytes);
}
TEST_F (VariableServerTest, ListSize) {
if (socket_status != 0) {
FAIL();
}
std::string reply;
std::string expected;
socket << "trick.var_add(\"vsx.vst.a\")\ntrick.var_add(\"vsx.vst.b\")\ntrick.var_add(\"vsx.vst.c\")\ntrick.var_add(\"vsx.vst.d\")\ntrick.var_pause()\n";
socket << "trick.var_send_list_size()\n";
socket >> reply;
expected = "3 4";
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0) << "Reply: " << reply << "\tExpected: " << expected;
// Adding a variable to the list more than once is allowed
socket << "trick.var_add(\"vsx.vst.a\")\n";
socket << "trick.var_send_list_size()\n";
socket >> reply;
expected = "3 5";
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0) << "Reply: " << reply << "\tExpected: " << expected;
socket << "trick.var_add(\"vsx.vst.e\")\ntrick.var_binary()\ntrick.var_send_list_size()\n";
std::vector<unsigned char> actual_bytes;
std::vector<unsigned char> expected_bytes;
actual_bytes = socket.receive_bytes();
expected_bytes = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00};
EXPECT_EQ(expected_bytes, actual_bytes);
}
TEST_F (VariableServerTest, RestartAndSet) {
if (socket_status != 0) {
FAIL();
}
std::string reply;
std::string expected;
socket << "trick.var_add(\"vsx.vst.c\")\n";
socket >> reply;
expected = std::string("0\t-1234\n");
EXPECT_EQ(reply, expected);
dump_checkpoint(socket, "reload_file.ckpnt");
socket << "trick.var_add(\"vsx.vst.e\",\"m\")\n";
socket >> reply;
expected = std::string("0\t-1234\t-123456 {m}\n");
socket << "trick.var_set(\"vsx.vst.c\", 5)\n";
socket >> reply;
expected = std::string("0\t5\t-123456 {m}\n");
EXPECT_EQ(reply, expected);
load_checkpoint(socket, "RUN_test/reload_file.ckpnt");
socket >> reply;
expected = std::string("0\t-1234\t-123456\n");
EXPECT_EQ(reply, expected);
}
TEST_F (VariableServerTest, Cycle) {
if (socket_status != 0) {
@ -365,7 +804,6 @@ TEST_F (VariableServerTest, Cycle) {
}
double cycle = 1.0;
double tolerance = 1e-5;
int num_cycles = 5;
// Challenge: no loops allowed
@ -408,7 +846,7 @@ TEST_F (VariableServerTest, Cycle) {
std::string command = "trick.var_set_copy_mode(1)\ntrick.var_set_write_mode(1)\ntrick.var_add(\"time\")\n";
socket << command;
cycle = 0.1;
num_cycles = 5;
run_cycle_test();
@ -451,6 +889,84 @@ TEST_F (VariableServerTest, Pause) {
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
}
#ifndef __APPLE__
TEST_F (VariableServerTest, Multicast) {
if (socket_status != 0) {
FAIL();
}
socket << "trick.var_server_set_user_tag(\"VSTestServer\")\n";
Socket multicast_socket;
if (multicast_socket.init_multicast("224.3.14.15", 9265) != 0) {
FAIL() << "Multicast Socket failed to initialize.";
}
int max_multicast_tries = 100;
int tries = 0;
bool found = false;
char expected_hostname[80];
gethostname(expected_hostname, 80);
int expected_port = 40000;
// get expected username
struct passwd *passp = getpwuid(getuid()) ;
char * expected_username;
if ( passp == NULL ) {
expected_username = strdup("unknown") ;
} else {
expected_username = strdup(passp->pw_name) ;
}
// Don't care about PID, just check that it's > 0
char * expected_sim_dir = "trick/test/SIM_test_varserv"; // Compare against the end of the string for this one
// Don't care about cmdline name
char * expected_input_file = "RUN_test/unit_test.py";
// Don't care about trick_version
char * expected_tag = "VSTestServer";
// Variables to be populated by the multicast message
char actual_hostname[80];
unsigned short actual_port = 0;
char actual_username[80];
int actual_pid = 0;
char actual_sim_dir[80];
char actual_cmdline_name[80];
char actual_input_file[80];
char actual_trick_version[80];
char actual_tag[80];
unsigned short actual_duplicate_port = 0;
while (!found && tries++ < max_multicast_tries) {
std::string broadcast_data = multicast_socket.receive();
sscanf(broadcast_data.c_str(), "%s\t%hu\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%hu\n" , actual_hostname, &actual_port ,
actual_username , &actual_pid , actual_sim_dir , actual_cmdline_name ,
actual_input_file , actual_trick_version , actual_tag, &actual_duplicate_port) ;
if (strcmp(actual_hostname, expected_hostname) == 0 && strcmp(expected_tag, actual_tag) == 0) {
found = true;
EXPECT_STREQ(actual_hostname, expected_hostname);
EXPECT_EQ(actual_port, expected_port);
EXPECT_STREQ(actual_username, expected_username);
EXPECT_GT(actual_pid, 0);
std::string expected_sim_dir_str(expected_sim_dir);
std::string actual_sim_dir_str(actual_sim_dir);
std::string end_of_actual = actual_sim_dir_str.substr(actual_sim_dir_str.length() - expected_sim_dir_str.length(), actual_sim_dir_str.length());
EXPECT_EQ(expected_sim_dir_str, end_of_actual);
EXPECT_STREQ(actual_input_file, expected_input_file);
EXPECT_STREQ(actual_tag, expected_tag);
EXPECT_EQ(actual_duplicate_port, expected_port);
}
}
if (!found)
FAIL() << "Multicast message never received";
}
#endif
TEST_F (VariableServerTest, Freeze) {
if (socket_status != 0) {
FAIL();
@ -461,10 +977,6 @@ TEST_F (VariableServerTest, Freeze) {
int mode;
long long frame_count;
long long freeze_frame_count;
// Constants for clarity
const int MODE_RUN = 5;
const int MODE_FREEZE = 1;
// lambda capture by refence is neat
auto parse_message_for_sim_mode_and_frames = [&](const std::string& message) {
@ -539,7 +1051,6 @@ TEST_F (VariableServerTest, Freeze) {
ASSERT_EQ(mode, MODE_RUN);
}
TEST_F (VariableServerTest, CopyAndWriteModes) {
if (socket_status != 0) {
FAIL();
@ -575,10 +1086,6 @@ TEST_F (VariableServerTest, CopyAndWriteModes) {
vars = token;
};
auto spin = [&](int wait_cycles = 5) {
socket.receive();
};
// Check that every combination of modes is functional
// Check that reasonable times and frames are returned as well
// Default is VS_COPY_ASYNC=0 and VS_WRITE_ASYNC=0
@ -602,7 +1109,7 @@ TEST_F (VariableServerTest, CopyAndWriteModes) {
// With copy mode VS_COPY_SCHEDULED and write mode VS_WRITE_ASYNC, the first reply will be all 0 since the main time to copy has not occurred yet.
// 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();
// spin();
expected = "-1234 1234";
parse_message(socket.receive());
@ -654,11 +1161,7 @@ TEST_F (VariableServerTest, CopyAndWriteModes) {
socket << command;
// Same issue as copy mode 1 write mode 0
spin();
// expected = "0 -1234567 123456789";
// EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
// std::cout << "\tExpected: " << expected << "\n\tActual: " << reply << std::endl;
// spin();
parse_message(socket.receive());
expected = "-1234567 123456789";
EXPECT_EQ(strcmp_IgnoringWhiteSpace(vars, expected), 0) << "Received: " << vars << " Expected: " << expected;
@ -703,7 +1206,7 @@ TEST_F (VariableServerTest, CopyAndWriteModes) {
socket.clear_buffered_data();
}
bool getCompleteBinaryMessage(ParsedBinaryMessage& message, Socket& socket) {
bool getCompleteBinaryMessage(ParsedBinaryMessage& message, Socket& socket, bool print = false) {
static const int max_retries = 5;
int tries = 0;
@ -722,15 +1225,162 @@ bool getCompleteBinaryMessage(ParsedBinaryMessage& message, Socket& socket) {
}
}
if (print) {
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;
}
return parse_success;
}
TEST_F (VariableServerTest, send_stdio) {
if (socket_status != 0) {
FAIL();
}
socket << "trick.var_set_send_stdio(True)\n";
socket << "sys.stdout.write(\"This message should redirect to varserver\")\n";
std::string message;
socket >> message;
std::stringstream message_stream(message);
std::string token;
int message_type;
int stream_num;
int text_size;
std::string text;
message_stream >> message_type;
message_stream >> stream_num;
std::getline(message_stream, token, '\n');
text_size = stoi(token);
std::getline(message_stream, text);
if (text.size() != text_size) {
socket >> text;
}
EXPECT_EQ (message_type, 4);
EXPECT_EQ (stream_num, 1);
EXPECT_EQ (text_size, 41);
EXPECT_EQ(text, std::string("This message should redirect to varserver"));
}
#ifndef __APPLE__
TEST_F (VariableServerTest, MulticastAfterRestart) {
if (socket_status != 0) {
FAIL();
}
socket << "trick.var_server_set_user_tag(\"VSTestServer\")\n";
Socket multicast_socket;
if (multicast_socket.init_multicast("224.3.14.15", 9265) != 0) {
FAIL() << "Multicast Socket failed to initialize.";
}
int max_multicast_tries = 100;
int tries = 0;
bool found = false;
char expected_hostname[80];
gethostname(expected_hostname, 80);
int expected_port = 40000;
// get expected username
struct passwd *passp = getpwuid(getuid()) ;
char * expected_username;
if ( passp == NULL ) {
expected_username = strdup("unknown") ;
} else {
expected_username = strdup(passp->pw_name) ;
}
// Don't care about PID, just check that it's > 0
char * expected_sim_dir = "trick/test/SIM_test_varserv"; // Compare against the end of the string for this one
// Don't care about cmdline name
char * expected_input_file = "RUN_test/unit_test.py";
// Don't care about trick_version
char * expected_tag = "VSTestServer";
// Variables to be populated by the multicast message
char actual_hostname[80];
unsigned short actual_port = 0;
char actual_username[80];
int actual_pid = 0;
char actual_sim_dir[80];
char actual_cmdline_name[80];
char actual_input_file[80];
char actual_trick_version[80];
char actual_tag[80];
unsigned short actual_duplicate_port = 0;
while (!found && tries++ < max_multicast_tries) {
std::string broadcast_data = multicast_socket.receive();
sscanf(broadcast_data.c_str(), "%s\t%hu\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%hu\n" , actual_hostname, &actual_port ,
actual_username , &actual_pid , actual_sim_dir , actual_cmdline_name ,
actual_input_file , actual_trick_version , actual_tag, &actual_duplicate_port) ;
if (strcmp(actual_hostname, expected_hostname) == 0 && strcmp(expected_tag, actual_tag) == 0) {
found = true;
EXPECT_STREQ(actual_hostname, expected_hostname);
EXPECT_EQ(actual_port, expected_port);
EXPECT_STREQ(actual_username, expected_username);
EXPECT_GT(actual_pid, 0);
std::string expected_sim_dir_str(expected_sim_dir);
std::string actual_sim_dir_str(actual_sim_dir);
std::string end_of_actual = actual_sim_dir_str.substr(actual_sim_dir_str.length() - expected_sim_dir_str.length(), actual_sim_dir_str.length());
EXPECT_EQ(expected_sim_dir_str, end_of_actual);
EXPECT_STREQ(actual_input_file, expected_input_file);
EXPECT_STREQ(actual_tag, expected_tag);
EXPECT_EQ(actual_duplicate_port, expected_port);
}
}
if (!found)
FAIL() << "Multicast message never received";
}
#endif
TEST_F (VariableServerTest, Binary) {
if (socket_status != 0) {
FAIL();
}
socket << "trick.var_binary()\ntrick.var_add(\"vsx.vst.n\")\n";
ParsedBinaryMessage message_n;
if (!getCompleteBinaryMessage(message_n, socket)) {
FAIL() << "Parser was not able to interpret the message.";
}
Var arr_var = message_n.getVariable("vsx.vst.n");
EXPECT_EQ(arr_var.getArraySize(), 5);
std::vector<unsigned char> raw_arr = arr_var.getRawBytes();
std::vector<int> values;
for (unsigned int i = 0; i < raw_arr.size(); i += 4) {
int val = 0;
for (unsigned int j = i; j < i+4; j++) {
val |= raw_arr[j] << j*8;
}
values.push_back(val);
}
std::vector<int> expected_arr = {0, 1, 2, 3, 4};
EXPECT_EQ(values, expected_arr);
std::vector<unsigned char> 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";
socket << "trick.var_clear()\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;
@ -792,7 +1442,6 @@ TEST_F (VariableServerTest, Binary) {
}
TEST_F (VariableServerTest, DISABLED_BinaryByteswap) {
// TODO: VAR_BYTESWAP DOES NOT APPEAR TO WORK CORRECTLY
std::vector<unsigned char> reply;
socket << "trick.var_binary()\ntrick.var_byteswap(False)\ntrick.var_add(\"vsx.vst.f\")\ntrick.var_add(\"vsx.vst.j\")\n";
@ -802,11 +1451,11 @@ TEST_F (VariableServerTest, DISABLED_BinaryByteswap) {
// 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;
// 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;
@ -832,11 +1481,11 @@ TEST_F (VariableServerTest, DISABLED_BinaryByteswap) {
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;
// 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);
@ -852,7 +1501,6 @@ TEST_F (VariableServerTest, DISABLED_BinaryByteswap) {
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<unsigned int>(), 123456);
std::cout << "Byteswap value: " << byteswap_message.variables[0].getValue<unsigned int>() << 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);

View File

@ -31,7 +31,9 @@ class VSTest {
int n[5];
std::string o;
char * p;
wchar_t * q;
wchar_t * q; /**< trick_chkpnt_io(**) */
int large_arr[4000];
int status;

View File

@ -4,7 +4,8 @@ REFERENCE: ( None )
ASSUMPTIONS AND LIMITATIONS: ( None )
CLASS: ( scheduled )
LIBRARY DEPENDENCY: ( VS.o )
PROGRAMMERS: ( (Lindsay Landry) (L3) (9-12-2013) (Jackie Deans) (CACI) (11-30-2022) )
PROGRAMMERS: ( (Lindsay Landry) (L3) (9-12-2013)
(Jackie Deans) (CACI) (11-30-2022) )
*******************************************************************************/
#include <iostream>
#include "../include/VS.hh"
@ -35,6 +36,10 @@ int VSTest::default_vars() {
o = std::string("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.");
p = "I am already far north of London, and as I walk in the streets of Petersburgh, I feel a cold northern breeze play upon my cheeks, which braces my nerves and fills me with delight. Do you understand this feeling?";
q = L"This breeze, which has travelled from the regions towards which I am advancing, gives me a foretaste of those icy climes. Inspirited by this wind of promise, my daydreams become more fervent and vivid.";
for (int i = 0; i < 4000; i++) {
large_arr[i] = i;
}
}
int VSTest::init() {
@ -44,10 +49,12 @@ int VSTest::init() {
int VSTest::fail() {
status = 1;
return 0;
}
int VSTest::success() {
status = 0;
status = 0;
return 0;
}
int VSTest::shutdown() {

View File

@ -106,7 +106,10 @@ int Trick::VariableServerListenThread::check_and_move_listen_device() {
}
void Trick::VariableServerListenThread::create_tcp_socket(const char * address, unsigned short in_port ) {
tc_init_with_connection_info(&listen_dev, AF_INET, SOCK_STREAM, address, in_port) ;
int result = tc_init_with_connection_info(&listen_dev, AF_INET, SOCK_STREAM, address, in_port) ;
if (result != 0) {
message_publish(MSG_ERROR, "ERROR: Could not establish additional listen port at address %s and port %d for Variable Server.\n", address, in_port);
}
}
void * Trick::VariableServerListenThread::thread_body() {

View File

@ -5,168 +5,6 @@
#include <sstream>
// Utility method
uint64_t bytesToInt(const std::vector<unsigned char>& 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<unsigned char>& container) : _container(container), _index(0) {}
std::vector<unsigned char> 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<unsigned char>(_container.begin() + _index, _container.begin() + slice_end);
}
void operator+= (int n) {
_index += n;
}
private:
std::vector<unsigned char> _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<unsigned char>& 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<TRICK_TYPE>(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
**************************************************************************/
@ -221,6 +59,24 @@ TRICK_TYPE Var::getType() const {
return _trick_type;
}
std::vector<unsigned char> Var::getRawBytes() const {
return value_bytes;
}
int Var::getArraySize() const {
auto val_size_pair = type_size_map.find(_trick_type);
if (val_size_pair == type_size_map.end()) {
return -1;
}
int val_size = val_size_pair->second;
if (_var_size % val_size != 0) {
return -1;
}
return _var_size / val_size;
}
// Template specialization for each supported type
template<>
@ -362,3 +218,185 @@ wchar_t Var::getValue<wchar_t> () const {
return getInterpreter().wchar_val;
}
// Utility method
uint64_t bytesToInt(const std::vector<unsigned char>& 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<unsigned char>& container) : _container(container), _index(0) {}
std::vector<unsigned char> 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<unsigned char>(_container.begin() + _index, _container.begin() + slice_end);
}
void operator+= (int n) {
_index += n;
}
private:
std::vector<unsigned char> _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;
// Sometimes, we need to reconstruct the size from the message.
// Risk of segfaulting here if the bytes are not correctly formed
int ParsedBinaryMessage::parse (char * raw_bytes) {
std::vector<unsigned char> bytes;
unsigned int i = 0;
for ( ; i < message_indicator_size + message_size_size; i++) {
bytes.push_back(raw_bytes[i]);
}
unsigned int message_size = bytesToInt(std::vector<unsigned char> (bytes.begin() + message_indicator_size, bytes.begin() + message_indicator_size + message_size_size), _byteswap);
for ( ; i < message_size + message_indicator_size; i++) {
bytes.push_back(raw_bytes[i]);
}
return parse(bytes);
}
int ParsedBinaryMessage::parse (const std::vector<unsigned char>& 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<TRICK_TYPE>(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;
}

View File

@ -616,6 +616,35 @@ TEST (BinaryParserTest, GetVarByIndexWrong) {
}
}
TEST (BinaryParserTest, ArrayTest) {
// Message: vst.vsx.n
std::vector<unsigned char> bytes = {0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00};
ParsedBinaryMessage message(false, true);
try {
message.parse(bytes);
} catch (const std::exception& ex) {
FAIL() << "Exception thrown: " << ex.what();
}
ASSERT_EQ(message.getNumVars(), 1);
Var arr_var = message.getVariable(0);
EXPECT_EQ(arr_var.getArraySize(), 5);
std::vector<unsigned char> raw_arr = arr_var.getRawBytes();
std::vector<int> values;
for (unsigned int i = 0; i < raw_arr.size(); i += 4) {
int val = 0;
for (unsigned int j = i; j < i+4; j++) {
val |= raw_arr[j] << j*8;
}
values.push_back(val);
}
std::vector<int> expected = {0, 1, 2, 3, 4};
EXPECT_EQ(values, expected);
}
/**************************************************************************
* Var parsing type tests
**************************************************************************/

View File

@ -28,17 +28,20 @@ class SimTestWorkflow(TrickWorkflow):
# This job in SIM_stls dumps a checkpoint that is then read in and checked by RUN_test/unit_test.py in the same sim
# This is a workaround to ensure that this run goes first.
first_phase_jobs = []
stl_dump_job = self.get_sim('SIM_stls').get_run(input='RUN_test/setup.py').get_run_job()
first_phase_jobs.append(stl_dump_job)
run_jobs.remove(stl_dump_job)
stl_sim = self.get_sim('SIM_stls')
if stl_sim is not None:
stl_dump_job = stl_sim.get_run(input='RUN_test/setup.py').get_run_job()
first_phase_jobs.append(stl_dump_job)
run_jobs.remove(stl_dump_job)
# Same with SIM_checkpoint_data_recording - half the runs dump checkpoints, the others read and verify.
# Make sure that the dump checkpoint runs go first.
num_dump_runs = int(len(self.get_sim('SIM_checkpoint_data_recording').get_runs())/2)
for i in range(num_dump_runs):
job = self.get_sim('SIM_checkpoint_data_recording').get_run(input=('RUN_test' + str(i+1) + '/dump.py')).get_run_job()
first_phase_jobs.append(job)
run_jobs.remove(job)
if self.get_sim('SIM_checkpoint_data_recording') is not None:
num_dump_runs = int(len(self.get_sim('SIM_checkpoint_data_recording').get_runs())/2)
for i in range(num_dump_runs):
job = self.get_sim('SIM_checkpoint_data_recording').get_run(input=('RUN_test' + str(i+1) + '/dump.py')).get_run_job()
first_phase_jobs.append(job)
run_jobs.remove(job)
# Some tests fail intermittently for reasons not related to the tests themselves, mostly network weirdness.
# Allow retries so that we can still cover some network-adjacent code