Commit b3996920 authored by SIMONIN Matthieu's avatar SIMONIN Matthieu
Browse files

Merge branch 'for-5' into 'master'

Unit Tests -- For #5

See merge request quinson/2018-vsg!3
parents 183790b5 62d38111
Pipeline #182179 passed with stages
in 10 minutes and 13 seconds
......@@ -70,8 +70,18 @@ tansiv:
- mkdir -p build
- cd build
- cmake .. && make
# cpp unittests
- ./tests
# unittests
# NOTE(msimonin): as long as https://gitlab.inria.fr/quinson/2018-vsg/-/issues/5 is around
# we run our tests isolated in different process.
# This is far from ideal since reports will be spread in the output of each process
# But at least catch2 lets use specify this easily...
# But ...
# for some reason this fails: ./tests --list-test-names-only | xargs -d "\n" -n 1 ./tests
# grrr!
- ./tests "VSG receive one message"
- ./tests "VSG deliver one message"
- ./tests "VSG send piggyback port"
- ./tests "piggyback port"
artifacts:
paths:
- build
......
......@@ -15,7 +15,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/tools/cmake/")
# order of linked library is important !
# https://stackoverflow.com/questions/45135/why-does-the-order-in-which-libraries-are-linked-sometimes-cause-errors-in-gcc
set(FAKEVM_LIBS /opt/fake-vm/lib/libfake_vm.a dl rt pthread cppunit)
set(FAKEVM_LIBS /opt/fake-vm/lib/libfake_vm.a dl rt pthread)
set(FAKEVM_INCLUDE_DIR /opt/fake-vm/include)
set(VSG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/vsg/)
......@@ -61,9 +61,9 @@ configure_file(examples/qemus/deployment.xml examples/qemus/deployment.xml COPYO
configure_file(examples/qemus/nova_cluster.xml examples/qemus/nova_cluster.xml COPYONLY)
# Unit tests
add_executable(tests src/tests/tests.cpp)
add_executable(tests src/tests/tests.cpp src/tests/scenario.cpp)
target_include_directories(tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/include)
target_link_libraries(tests PUBLIC ${FAKEVM_LIBS} vsg ${SimGrid_LIBRARY})
target_compile_options(tests PUBLIC -fexceptions)
add_dependencies(tests fake-vm)
......
This diff is collapsed.
#include "scenario.hpp"
#include "catch.hpp"
#include <csignal>
#include <cstdio>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
void sigquit(int signum)
{
// gracefully leave
exit(0);
}
ScenarioRunner::ScenarioRunner(scenario* the_scenario)
{
remove(SOCKET_ACTOR);
printf("\n---\nCreating Simple Actor\n");
int connection_socket = socket(AF_LOCAL, SOCK_STREAM, 0);
struct sockaddr_un address;
address.sun_family = AF_LOCAL;
strcpy(address.sun_path, SOCKET_ACTOR);
if (bind(connection_socket, (sockaddr*)(&address), sizeof(address)) != 0) {
std::perror("unable to bind connection socket");
}
// Start queueing incoming connections otherwise there might be a race
// condition where vsg_init is called before the server side socket is
// listening.
if (listen(connection_socket, 1) != 0) {
std::perror("unable to listen on connection socket");
}
printf("Actor is now ready to listen to connections\n");
pid_t pid = fork();
if (pid == 0) {
// Adding a signal to leave the child gracefully
// when the test ends
signal(SIGQUIT, sigquit);
// child process: we continue the actor execution
struct sockaddr_un vm_address = {0};
unsigned int len = sizeof(vm_address);
printf("\tWaiting connections\n");
int client_socket = accept(connection_socket, (sockaddr*)(&vm_address), &len);
if (client_socket < 0)
std::perror("unable to accept connection on socket");
printf("\tClient connection accepted\n");
// run it
(*the_scenario)(client_socket);
} else if (pid > 0) {
// sets the attributes
printf("I'm your father (my child=%d)\n", pid);
this->child_pid = pid;
this->vsg_fd = connection_socket;
} else {
exit(1);
}
}
ScenarioRunner::~ScenarioRunner()
{
printf("Closing the socket %d\n", this->vsg_fd);
close(this->vsg_fd);
/* Terminate child. */
printf("Terminating %d \n", this->child_pid);
pid_t pid = this->child_pid;
kill(pid, SIGQUIT);
int status;
waitpid(pid, &status, 0);
/* Report an error. */
if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
printf("status=%d\n", WEXITSTATUS(status));
// FIXME, do something
}
}
static void init_sequence(int client_socket)
{
vsg_msg_in_type msg = vsg_msg_in_type::GoToDeadline;
int ret = send(client_socket, &msg, sizeof(uint32_t), 0);
vsg_time t = {0, 200};
send(client_socket, &t, sizeof(vsg_time), 0);
}
static void end_sequence(int client_socket)
{
vsg_msg_in_type msg = vsg_msg_in_type::EndSimulation;
send(client_socket, &msg, sizeof(uint32_t), 0);
}
/*
* Simple scenario
*
* The actor sends the init_sequence:
* - a GoToDeadline message
* - an EndSimulation message
*
*/
void simple(int client_socket)
{
printf("Entering simple scenario\n");
init_sequence(client_socket);
end_sequence(client_socket);
printf("Leaving simple scenario\n");
}
/*
* scenario: recv_one
*
* The actor sends
* - the init sequence
* - wait a message sent by the application
*
*/
void recv_one(int client_socket)
{
printf("Entering recv_one scenario\n");
init_sequence(client_socket);
// first, check the type of message
vsg_msg_out_type msg_type;
recv(client_socket, &msg_type, sizeof(vsg_msg_out_type), MSG_WAITALL);
REQUIRE(vsg_msg_out_type::SendPacket == msg_type);
// second, check the send time and size
vsg_send_packet send_packet = {0};
recv(client_socket, &send_packet, sizeof(vsg_send_packet), MSG_WAITALL);
// test the received addresses
in_addr_t src_expected = inet_addr(SRC);
REQUIRE(src_expected == send_packet.packet.src);
in_addr_t dst_expected = inet_addr(DEST);
REQUIRE(dst_expected == send_packet.packet.dst);
// finally get the payload
uint8_t buf[send_packet.packet.size];
recv(client_socket, buf, send_packet.packet.size, MSG_WAITALL);
std::string expected = MESSAGE;
std::string actual = std::string((char*)buf);
REQUIRE(expected == actual);
end_sequence(client_socket);
printf("Leaving recv_one scenario\n");
};
/*
* scenario: deliver_one
*
* The actor sends
* - the init sequence
* - send a DeliverPacket to the application
*
*/
void deliver_one(int client_socket)
{
printf("Entering deliver_one scenario\n");
init_sequence(client_socket);
uint32_t deliver_flag = vsg_msg_in_type::DeliverPacket;
std::string data = MESSAGE;
vsg_packet packet = {.size = (uint32_t)data.length() + 1, .src = inet_addr(SRC), .dst = inet_addr(DEST)};
struct vsg_deliver_packet deliver_packet = {.packet = packet};
vsg_deliver_send(client_socket, deliver_packet, (uint8_t*)data.c_str());
printf("Leaving deliver_one scenario\n");
end_sequence(client_socket);
};
/*
* scenario: send_deliver_pg_port
*
* The actor sends
* - the init sequence
* - wait a message sent by the application (with a port piggybacked)
*
*/
void send_deliver_pg_port(int client_socket)
{
printf("Entering send_deliver_pg_port scenario\n");
init_sequence(client_socket);
// receive send_packet
vsg_msg_out_type msg_type;
recv(client_socket, &msg_type, sizeof(vsg_msg_out_type), MSG_WAITALL);
vsg_send_packet send_packet = {0};
recv(client_socket, &send_packet, sizeof(vsg_send_packet), MSG_WAITALL);
// here the payload contains the port
// we just pass it back to the app with a deliver message
uint8_t buf[send_packet.packet.size];
recv(client_socket, buf, send_packet.packet.size, MSG_WAITALL);
// deliver sequence
uint32_t deliver_flag = vsg_msg_in_type::DeliverPacket;
vsg_packet packet = {.size = sizeof(buf)};
struct vsg_deliver_packet deliver_packet = {.packet = packet};
vsg_deliver_send(client_socket, deliver_packet, buf);
end_sequence(client_socket);
printf("Leaving send_deliver_pg_port scenario\n");
};
#ifndef __SCENARIO__
#define __SCENARIO__
#include <sys/types.h>
extern "C" {
#include <fake_vm.h>
#include <vsg.h>
}
/* The socket to use for all the tests. */
#define SOCKET_ACTOR "titi"
/* The message to send for send/deliver tests. */
#define MESSAGE "plop"
/* The source to use for send tests. */
#define SRC "127.0.0.1"
/* The destination to use for send tests. */
#define DEST "8.8.8.8"
typedef void scenario(int);
class ScenarioRunner {
public:
ScenarioRunner(scenario s);
~ScenarioRunner();
void finalize();
pid_t child_pid;
int vsg_fd;
};
// the scenarios
void simple(int);
void recv_one(int);
void deliver_one(int);
void send_deliver_pg_port(int);
#endif /* __SCENARIO__ */
#include <cppunit/BriefTestProgressListener.h>
#include <cppunit/CompilerOutputter.h>
#include <cppunit/Exception.h>
#include <cppunit/TestFixture.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TestRunner.h>
#include <cppunit/XmlOutputter.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TextTestRunner.h>
#include <errno.h>
#include <iostream>
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"
#include <atomic>
#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
extern "C" {
#include <fake_vm.h>
#include <vsg.h>
}
/* The socket to use for all the tests. */
#define SOCKET_ACTOR "titi"
/* The message to send for send/deliver tests. */
#define MESSAGE "plop"
/* The source to use for send tests. */
#define SRC "127.0.0.1"
/* The destination to use for send tests. */
#define DEST "8.8.8.8"
using namespace CppUnit;
using namespace std;
typedef void scenario(int);
pid_t simple_actor(scenario f)
{
// I don't care about the status...
scenario* the_scenario = (scenario*)f;
remove(SOCKET_ACTOR);
printf("\n---\nCreating Simple Actor\n");
int connection_socket = socket(AF_LOCAL, SOCK_STREAM, 0);
struct sockaddr_un address;
address.sun_family = AF_LOCAL;
strcpy(address.sun_path, SOCKET_ACTOR);
if (bind(connection_socket, (sockaddr*)(&address), sizeof(address)) != 0) {
std::perror("unable to bind connection socket");
}
if (listen(connection_socket, 1) != 0) {
std::perror("unable to listen on connection socket");
}
printf("Actor is now ready to listen to connections\n");
// we can fork here
// this is the parent thread
// the child process runs the tests ?
pid_t pid = fork();
if (pid == 0) {
// child process: we continue the actor execution
struct sockaddr_un vm_address = {0};
unsigned int len = sizeof(vm_address);
printf("\tWaiting connections\n");
int client_socket = accept(connection_socket, (sockaddr*)(&vm_address), &len);
if (client_socket < 0)
std::perror("unable to accept connection on socket");
printf("\tClient connection accepted\n");
// runit
try {
(*the_scenario)(client_socket);
} catch (CppUnit::Exception e) {
// TODO(msimonin): can we do better than that ?
CppUnit::SourceLine line = e.sourceLine();
printf("Exception in child process:\n line:%d: %s\n", line.lineNumber(), e.what());
exit(142);
} catch (...) {
exit(1);
}
// mimic a server vsg_stop(context);
// our father will kill us anyway when test test is finished
sleep(3600);
exit(0);
} else if (pid > 0) {
// parent: we continue the execution flow with the test
return pid;
} else {
exit(1);
}
}
void finalize(pid_t pid)
{
/* Terminate child. */
kill(pid, SIGTERM);
int status;
waitpid(pid, &status, 0);
/* Report an error. */
if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
printf("status=%d\n", WEXITSTATUS(status));
throw CppUnit::Exception();
}
}
//-----------------------------------------------------------------------------
class TestTansiv : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(TestTansiv);
CPPUNIT_TEST(testVsgStart);
CPPUNIT_TEST(testVsgSend);
// NOTE(msimonin): I'm desactivating this because it has caused some pain lately
// Symptom: Job failed on CI (but ok locally)
// CPPUNIT_TEST(testVsgSendEnsureRaise);
CPPUNIT_TEST(testVsgPiggyBackPort);
CPPUNIT_TEST(testVsgSendPiggyBackPort);
CPPUNIT_TEST(testVsgDeliver);
CPPUNIT_TEST_SUITE_END();
public:
void setUp(void);
void tearDown(void);
protected:
void testVsgStart(void);
void testVsgSend(void);
void testVsgSendEnsureRaise(void);
void testVsgPiggyBackPort(void);
void testVsgSendPiggyBackPort(void);
void testVsgDeliver(void);
private:
/* hold the context created bu vsg_init. */
vsg_context* context;
};
#include "scenario.hpp"
void recv_cb(uintptr_t arg)
{
......@@ -149,234 +12,21 @@ void recv_cb(uintptr_t arg)
write(STDOUT_FILENO, hey, sizeof(hey) - 1);
};
void TestTansiv::setUp(void) {}
void TestTansiv::tearDown(void) {}
void init_sequence(int client_socket)
{
vsg_msg_in_type msg = vsg_msg_in_type::GoToDeadline;
int ret = send(client_socket, &msg, sizeof(uint32_t), 0);
vsg_time t = {0, 200};
send(client_socket, &t, sizeof(vsg_time), 0);
}
void end_sequence(int client_socket)
{
vsg_msg_in_type msg = vsg_msg_in_type::EndSimulation;
send(client_socket, &msg, sizeof(uint32_t), 0);
}
/*
* Simple scenario
*
* The actor sends the init_sequence:
* - a GoToDeadline message
* - an EndSimulation message
*
*/
void simple(int client_socket)
{
printf("Entering simple scenario\n");
init_sequence(client_socket);
end_sequence(client_socket);
printf("Leaving simple scenario\n");
};
/*
* scenario: recv_one
*
* The actor sends
* - the init sequence
* - wait a message sent by the application
*
*/
void recv_one(int client_socket)
{
printf("Entering recv_one scenario\n");
init_sequence(client_socket);
// first, check the type of message
vsg_msg_out_type msg_type;
recv(client_socket, &msg_type, sizeof(vsg_msg_out_type), MSG_WAITALL);
CPPUNIT_ASSERT_EQUAL(vsg_msg_out_type::SendPacket, msg_type);
// second, check the send time and size
vsg_send_packet send_packet = {0};
recv(client_socket, &send_packet, sizeof(vsg_send_packet), MSG_WAITALL);
// test the received addresses
in_addr_t src_expected = inet_addr(SRC);
CPPUNIT_ASSERT_EQUAL(src_expected, send_packet.packet.src);
in_addr_t dst_expected = inet_addr(DEST);
CPPUNIT_ASSERT_EQUAL(dst_expected, send_packet.packet.dst);
// finally get the payload
uint8_t buf[send_packet.packet.size];
recv(client_socket, buf, send_packet.packet.size, MSG_WAITALL);
std::string expected = MESSAGE;
std::string actual = std::string((char*)buf);
CPPUNIT_ASSERT_EQUAL_MESSAGE("payload received by the actor differs from what has been sent by the application",
expected, actual);
end_sequence(client_socket);
printf("Leaving recv_one scenario\n");
};
/*
* scenario: recv_one
*
* The actor sends
* - the init sequence
* - wait a message sent by the application (with a port piggybacked)
*
*/
void send_deliver_pg_port(int client_socket)
{
printf("Entering send_deliver_pg_port scenario\n");
init_sequence(client_socket);
// receive send_packet
vsg_msg_out_type msg_type;
recv(client_socket, &msg_type, sizeof(vsg_msg_out_type), MSG_WAITALL);
vsg_send_packet send_packet = {0};
recv(client_socket, &send_packet, sizeof(vsg_send_packet), MSG_WAITALL);
// here the payload contains the port
// we just pass it back to the app with a deliver message
uint8_t buf[send_packet.packet.size];
recv(client_socket, buf, send_packet.packet.size, MSG_WAITALL);
// deliver sequence
uint32_t deliver_flag = vsg_msg_in_type::DeliverPacket;
vsg_packet packet = {.size = sizeof(buf)};
struct vsg_deliver_packet deliver_packet = {.packet = packet};
vsg_deliver_send(client_socket, deliver_packet, buf);
end_sequence(client_socket);
printf("Leaving send_deliver_pg_port scenario\n");
};
/*
* scenario: deliver_one
*
* The actor sends
* - the init sequence
* - send a DeliverPacket to the application
*
*/
void deliver_one(int client_socket)
void recv_cb_atomic(uintptr_t arg)
{
printf("Entering deliver_one scenario\n");
init_sequence(client_socket);
uint32_t deliver_flag = vsg_msg_in_type::DeliverPacket;
std::string data = MESSAGE;
vsg_packet packet = {.size = data.length() + 1, .src = inet_addr(SRC), .dst = inet_addr(DEST)};
struct vsg_deliver_packet deliver_packet = {.packet = packet};
vsg_deliver_send(client_socket, deliver_packet, (uint8_t*)data.c_str());
printf("Leaving deliver_one scenario\n");
end_sequence(client_socket);
std::atomic<bool>* message_delivered = (std::atomic<bool>*)arg;
*message_delivered = true;
};
void TestTansiv::testVsgStart(void)
{
pid_t pid = simple_actor(simple);
int argc = 6;
const char* const argv[] = {"-a", SOCKET_ACTOR, "-n", SRC, "-t", "1970-01-01T00:00:00"};
vsg_context* context = vsg_init(argc, argv, NULL, recv_cb, 0);
CPPUNIT_ASSERT(context != NULL);
int ret = vsg_start(context);
CPPUNIT_ASSERT_EQUAL(0, ret);
int status;
vsg_stop(context);
vsg_cleanup(context);
finalize(pid);
}
void TestTansiv::testVsgSend(void)
{
pid_t pid = simple_actor(recv_one);
int argc = 6;
const char* const argv[] = {"-a", SOCKET_ACTOR, "-n", SRC, "-t", "1970-01-01T00:00:00"};
vsg_context* context = vsg_init(argc, argv, NULL, recv_cb, 0);
int ret = vsg_start(context);
std::string msg = MESSAGE;
in_addr_t dst = inet_addr(DEST);
vsg_send(context, dst, msg.length() + 1, (uint8_t*)msg.c_str());
vsg_stop(context);
vsg_cleanup(context);
finalize(pid);
}