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() 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 ### Commands
The variable server accepts commands in the form of strings. The variable server parses 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 messages with variable server information available here. Here is some
C code that reads all messages on the variable server channel. C code that reads all messages on the variable server channel.
Note that the multicast protocol is disabled by default in MacOS.
```c ```c
#include <stdio.h> #include <stdio.h>
#include <sys/types.h> #include <sys/types.h>

View File

@ -1,6 +1,7 @@
#include <unistd.h> #include <unistd.h>
#include <vector> #include <vector>
#include <string> #include <string>
#include <map>
#include <exception> #include <exception>
#include "trick/parameter_types.h" #include "trick/parameter_types.h"
@ -54,6 +55,22 @@ typedef union Number {
double double_val; double double_val;
} Number; } 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 { class Var {
public: public:
@ -65,10 +82,13 @@ class Var {
// There won't be a general case // There won't be a general case
template <typename T> template <typename T>
T getValue() const; T getValue() const;
std::vector<unsigned char> getRawBytes() const;
int getArraySize() const;
std::string getName() const; std::string getName() const;
TRICK_TYPE getType() const; TRICK_TYPE getType() const;
private: private:
std::vector<unsigned char> value_bytes; std::vector<unsigned char> value_bytes;
Number getInterpreter () const; Number getInterpreter () const;
@ -81,16 +101,18 @@ class Var {
TRICK_TYPE _trick_type; TRICK_TYPE _trick_type;
unsigned int _var_size; unsigned int _var_size;
unsigned int _arr_length;
}; };
class ParsedBinaryMessage { class ParsedBinaryMessage {
public: public:
ParsedBinaryMessage() : _byteswap(false), _nonames(false) {} ParsedBinaryMessage() : ParsedBinaryMessage(false, false) {}
ParsedBinaryMessage (bool byteswap, bool nonames) : _byteswap(byteswap), _nonames(nonames) {} ParsedBinaryMessage (bool byteswap, bool nonames) : _message_type(0), _message_size(0), _num_vars(0), _byteswap(byteswap), _nonames(nonames) {}
void combine (const ParsedBinaryMessage& message); void combine (const ParsedBinaryMessage& message);
int parse (const std::vector<unsigned char>& bytes); int parse (const std::vector<unsigned char>& bytes);
int parse (char * raw_bytes);
int getMessageType() const; int getMessageType() const;
unsigned int getMessageSize() const; unsigned int getMessageSize() const;

1
test/.gitignore vendored
View File

@ -25,3 +25,4 @@ trick.zip
jitlib jitlib
build build
S_sie.json 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.exec_set_software_frame(0.01)
# trick.set_var_server_info_msg_on() # 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() varServerPort = trick.var_server_get_port()
test_output = ( os.getenv("TRICK_HOME") + "/trick_test/SIM_test_varserv.xml" ) 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_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 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: 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 <iostream>
#include <functional> #include <functional>
#include <cmath> #include <cmath>
#include <ctype.h>
#include <pwd.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "trick/var_binary_parser.hh" #include "trick/var_binary_parser.hh"
#define SOCKET_BUF_SIZE 20480
#define DOUBLE_TOL 1e-5 #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 // 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) #define EXPECT_FEQ(a,b) EXPECT_LE(fabs(a - b), DOUBLE_TOL)
@ -29,12 +30,14 @@ class Socket {
int max_retries = 10; int max_retries = 10;
Socket() : _initialized(false) {} 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; _hostname = hostname;
_port = port; _port = port;
int tries = 0; 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) { if (_socket_fd < 0) {
std::cout << "Socket connection failed" << std::endl; std::cout << "Socket connection failed" << std::endl;
@ -65,12 +68,60 @@ class Socket {
return 0; 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) { 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, // 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 // 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); int success = ::send(_socket_fd, message.c_str(), message.size(), 0);
if (success < message.size()) { if (success < message.size()) {
std::cout << "Failed to send message" << std::endl; std::cout << "init_multicast: Failed to send message" << std::endl;
} }
return success; return success;
} }
@ -98,7 +149,7 @@ class Socket {
unsigned char buffer[SOCKET_BUF_SIZE]; unsigned char buffer[SOCKET_BUF_SIZE];
int numBytes = recv(_socket_fd, buffer, SOCKET_BUF_SIZE, 0); int numBytes = recv(_socket_fd, buffer, SOCKET_BUF_SIZE, 0);
if (numBytes < 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; std::vector<unsigned char> bytes;
@ -139,9 +190,97 @@ class Socket {
std::string _hostname; std::string _hostname;
int _socket_fd; int _socket_fd;
bool _initialized; 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) { int strcmp_IgnoringWhiteSpace(std::string s1_str, std::string s2_str) {
const char * s1 = s1_str.c_str(); const char * s1 = s1_str.c_str();
const char * s2 = s2_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 { void spin (Socket& socket, int wait_cycles = 5) {
protected: for (int i = 0; i < wait_cycles; i++)
VariableServerTest() { socket.receive();
socket_status = socket.init("localhost", 40000); }
if (socket_status == 0) { void wait_for_mode_change (Socket& socket, int expected_mode, int max_wait_iterations = 10) {
std::stringstream request; int iteration = 0;
request << "trick.var_set_client_tag(\"VSTest"; std::string mode_reply;
request << numSession++; std::string expected_reply = "5\t" + std::to_string(expected_mode) + "\n";
request << "\") \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(); 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);
~VariableServerTest() {
socket << "trick.var_exit()\n";
socket.close();
}
Socket socket; std::string checkpoint_cmd = "trick.checkpoint(\"" + checkpoint_name + "\")\n";
int socket_status; socket << checkpoint_cmd;
static int numSession;
};
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) { TEST_F (VariableServerTest, Strings) {
if (socket_status != 0) { if (socket_status != 0) {
@ -222,6 +541,41 @@ TEST_F (VariableServerTest, Strings) {
// EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0); // 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) { TEST_F (VariableServerTest, AddRemove) {
if (socket_status != 0) { if (socket_status != 0) {
FAIL(); FAIL();
@ -256,7 +610,6 @@ TEST_F (VariableServerTest, AddRemove) {
socket >> reply; socket >> reply;
expected = std::string("0 -1234 0,1,2,3,4"); expected = std::string("0 -1234 0,1,2,3,4");
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0); EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
} }
TEST_F (VariableServerTest, BadRefResponse) { TEST_F (VariableServerTest, BadRefResponse) {
@ -356,8 +709,94 @@ TEST_F (VariableServerTest, Exists) {
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0); 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) { TEST_F (VariableServerTest, Cycle) {
if (socket_status != 0) { if (socket_status != 0) {
@ -365,7 +804,6 @@ TEST_F (VariableServerTest, Cycle) {
} }
double cycle = 1.0; double cycle = 1.0;
double tolerance = 1e-5;
int num_cycles = 5; int num_cycles = 5;
// Challenge: no loops allowed // 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"; std::string command = "trick.var_set_copy_mode(1)\ntrick.var_set_write_mode(1)\ntrick.var_add(\"time\")\n";
socket << command; socket << command;
cycle = 0.1; cycle = 0.1;
num_cycles = 5; num_cycles = 5;
run_cycle_test(); run_cycle_test();
@ -451,6 +889,84 @@ TEST_F (VariableServerTest, Pause) {
EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0); 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) { TEST_F (VariableServerTest, Freeze) {
if (socket_status != 0) { if (socket_status != 0) {
FAIL(); FAIL();
@ -461,10 +977,6 @@ TEST_F (VariableServerTest, Freeze) {
int mode; int mode;
long long frame_count; long long frame_count;
long long freeze_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 // lambda capture by refence is neat
auto parse_message_for_sim_mode_and_frames = [&](const std::string& message) { 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); ASSERT_EQ(mode, MODE_RUN);
} }
TEST_F (VariableServerTest, CopyAndWriteModes) { TEST_F (VariableServerTest, CopyAndWriteModes) {
if (socket_status != 0) { if (socket_status != 0) {
FAIL(); FAIL();
@ -575,10 +1086,6 @@ TEST_F (VariableServerTest, CopyAndWriteModes) {
vars = token; vars = token;
}; };
auto spin = [&](int wait_cycles = 5) {
socket.receive();
};
// Check that every combination of modes is functional // Check that every combination of modes is functional
// Check that reasonable times and frames are returned as well // Check that reasonable times and frames are returned as well
// Default is VS_COPY_ASYNC=0 and VS_WRITE_ASYNC=0 // 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. // 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 // 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"; expected = "-1234 1234";
parse_message(socket.receive()); parse_message(socket.receive());
@ -654,11 +1161,7 @@ TEST_F (VariableServerTest, CopyAndWriteModes) {
socket << command; socket << command;
// Same issue as copy mode 1 write mode 0 // Same issue as copy mode 1 write mode 0
spin(); // spin();
// expected = "0 -1234567 123456789";
// EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
// std::cout << "\tExpected: " << expected << "\n\tActual: " << reply << std::endl;
parse_message(socket.receive()); parse_message(socket.receive());
expected = "-1234567 123456789"; expected = "-1234567 123456789";
EXPECT_EQ(strcmp_IgnoringWhiteSpace(vars, expected), 0) << "Received: " << vars << " Expected: " << expected; EXPECT_EQ(strcmp_IgnoringWhiteSpace(vars, expected), 0) << "Received: " << vars << " Expected: " << expected;
@ -703,7 +1206,7 @@ TEST_F (VariableServerTest, CopyAndWriteModes) {
socket.clear_buffered_data(); 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; static const int max_retries = 5;
int tries = 0; 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; 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) { TEST_F (VariableServerTest, Binary) {
if (socket_status != 0) { if (socket_status != 0) {
FAIL(); 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; 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; ParsedBinaryMessage message;
@ -792,7 +1442,6 @@ TEST_F (VariableServerTest, Binary) {
} }
TEST_F (VariableServerTest, DISABLED_BinaryByteswap) { TEST_F (VariableServerTest, DISABLED_BinaryByteswap) {
// TODO: VAR_BYTESWAP DOES NOT APPEAR TO WORK CORRECTLY
std::vector<unsigned char> reply; 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"; 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 // Test byteswap
std::cout << "Message: "; // std::cout << "Message: ";
for (unsigned char byte : reply) { // for (unsigned char byte : reply) {
std::cout << "0x" << std::setw(2) << std::setfill('0') << std::hex << (int)byte << " "; // std::cout << "0x" << std::setw(2) << std::setfill('0') << std::hex << (int)byte << " ";
} // }
std::cout << std::endl; // std::cout << std::endl;
ParsedBinaryMessage message; ParsedBinaryMessage message;
@ -832,11 +1481,11 @@ TEST_F (VariableServerTest, DISABLED_BinaryByteswap) {
socket.receive_bytes(); socket.receive_bytes();
reply = socket.receive_bytes(); reply = socket.receive_bytes();
std::cout << "Message: "; // std::cout << "Message: ";
for (unsigned char byte : reply) { // for (unsigned char byte : reply) {
std::cout << "0x" << std::setw(2) << std::setfill('0') << std::hex << (int)byte << " "; // std::cout << "0x" << std::setw(2) << std::setfill('0') << std::hex << (int)byte << " ";
} // }
std::cout << std::endl; // std::cout << std::endl;
ParsedBinaryMessage byteswap_message(true, false); 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(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].getType(), TRICK_UNSIGNED_INTEGER);
EXPECT_EQ(byteswap_message.variables[0].getValue<unsigned int>(), 123456); 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(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].getType(), TRICK_DOUBLE);

View File

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

View File

@ -4,7 +4,8 @@ REFERENCE: ( None )
ASSUMPTIONS AND LIMITATIONS: ( None ) ASSUMPTIONS AND LIMITATIONS: ( None )
CLASS: ( scheduled ) CLASS: ( scheduled )
LIBRARY DEPENDENCY: ( VS.o ) 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 <iostream>
#include "../include/VS.hh" #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."); 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?"; 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."; 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() { int VSTest::init() {
@ -44,10 +49,12 @@ int VSTest::init() {
int VSTest::fail() { int VSTest::fail() {
status = 1; status = 1;
return 0;
} }
int VSTest::success() { int VSTest::success() {
status = 0; status = 0;
return 0;
} }
int VSTest::shutdown() { 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 ) { 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() { void * Trick::VariableServerListenThread::thread_body() {

View File

@ -5,168 +5,6 @@
#include <sstream> #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 * Var implementation
**************************************************************************/ **************************************************************************/
@ -221,6 +59,24 @@ TRICK_TYPE Var::getType() const {
return _trick_type; 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 specialization for each supported type
template<> template<>
@ -362,3 +218,185 @@ wchar_t Var::getValue<wchar_t> () const {
return getInterpreter().wchar_val; 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 * 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 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. # This is a workaround to ensure that this run goes first.
first_phase_jobs = [] first_phase_jobs = []
stl_dump_job = self.get_sim('SIM_stls').get_run(input='RUN_test/setup.py').get_run_job() stl_sim = self.get_sim('SIM_stls')
first_phase_jobs.append(stl_dump_job) if stl_sim is not None:
run_jobs.remove(stl_dump_job) 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. # 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. # Make sure that the dump checkpoint runs go first.
num_dump_runs = int(len(self.get_sim('SIM_checkpoint_data_recording').get_runs())/2) if self.get_sim('SIM_checkpoint_data_recording') is not None:
for i in range(num_dump_runs): num_dump_runs = int(len(self.get_sim('SIM_checkpoint_data_recording').get_runs())/2)
job = self.get_sim('SIM_checkpoint_data_recording').get_run(input=('RUN_test' + str(i+1) + '/dump.py')).get_run_job() for i in range(num_dump_runs):
first_phase_jobs.append(job) job = self.get_sim('SIM_checkpoint_data_recording').get_run(input=('RUN_test' + str(i+1) + '/dump.py')).get_run_job()
run_jobs.remove(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. # 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 # Allow retries so that we can still cover some network-adjacent code