Skip to content

Commit

Permalink
More tests
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianReimold committed Feb 22, 2024
1 parent 4303076 commit 39ff173
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 348 deletions.
8 changes: 4 additions & 4 deletions samples/asio_sender_multicast/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ int main()
asio::io_service io_service;

const asio::ip::udp::endpoint endpoint(asio::ip::make_address("239.0.0.1"), 14000);
asio::ip::udp::socket upd_socket(io_service, endpoint.protocol());
asio::ip::udp::socket udp_socket(io_service, endpoint.protocol());

// set multicast packet TTL
{
const asio::ip::multicast::hops ttl(2);
asio::error_code ec;
upd_socket.set_option(ttl, ec);
udp_socket.set_option(ttl, ec);
if (ec)
{
std::cerr << "ERROR: Setting TTL failed: " << ec.message() << std::endl;
Expand All @@ -51,7 +51,7 @@ int main()
{
const asio::ip::multicast::enable_loopback loopback(true);
asio::error_code ec;
upd_socket.set_option(loopback, ec);
udp_socket.set_option(loopback, ec);
if (ec)
{
std::cerr << "ERROR: Error setting loopback option: " << ec.message() << std::endl;
Expand All @@ -65,7 +65,7 @@ int main()
std::string buffer_string = "Hello World " + std::to_string(counter);

std::cout << "Sending data \"" << buffer_string << "\"" << std::endl;
upd_socket.send_to(asio::buffer(buffer_string), endpoint);
udp_socket.send_to(asio::buffer(buffer_string), endpoint);
counter++;

std::this_thread::sleep_for(std::chrono::milliseconds(500));
Expand Down
4 changes: 2 additions & 2 deletions samples/asio_sender_unicast/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ int main()
asio::io_service io_service;

const asio::ip::udp::endpoint endpoint(asio::ip::make_address("127.0.0.1"), 14000);
asio::ip::udp::socket upd_socket(io_service, endpoint.protocol());
asio::ip::udp::socket udp_socket(io_service, endpoint.protocol());

int counter = 0;
for(;;)
{
std::string buffer_string = "Hello World " + std::to_string(counter);

std::cout << "Sending data \"" << buffer_string << "\"" << std::endl;
upd_socket.send_to(asio::buffer(buffer_string), endpoint);
udp_socket.send_to(asio::buffer(buffer_string), endpoint);
counter++;

std::this_thread::sleep_for(std::chrono::milliseconds(500));
Expand Down
212 changes: 202 additions & 10 deletions tests/udpcap_test/src/udpcap_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,14 @@ TEST(udpcap, RAIIWithSomebodyWaiting)
// Check that we didn't receive any bytes
ASSERT_EQ(received_bytes, 0);

// TODO: check actual error, which should indicate that the socket is closed
ASSERT_TRUE(bool(error));

// Check the error reason
ASSERT_EQ(error, Udpcap::Error(Udpcap::Error::ErrorCode::SOCKET_CLOSED));

// Check that the socket is closed
ASSERT_TRUE(udpcap_socket.isClosed());

});

// Close the socket
Expand Down Expand Up @@ -187,10 +192,15 @@ TEST(udpcap, MultipleSmallPackages)

if (error)
{
// TODO: Check that actual error reason

// Indicates that somebody closed the socket
ASSERT_EQ(received_messages.get(), num_packages_to_send);

// Check the error reason
ASSERT_EQ(error, Udpcap::Error(Udpcap::Error::ErrorCode::SOCKET_CLOSED));

// Check that the socket is closed
ASSERT_TRUE(udpcap_socket.isClosed());

break;
}

Expand Down Expand Up @@ -332,6 +342,13 @@ TEST(udpcap, DelayedPackageReceiveMultiplePackages)
{
// Indicates that somebody closed the socket
ASSERT_EQ(received_messages.get(), num_packages_to_send);

// Check the error reason
ASSERT_EQ(error, Udpcap::Error(Udpcap::Error::ErrorCode::SOCKET_CLOSED));

// Check that the socket is closed
ASSERT_TRUE(udpcap_socket.isClosed());

break;
}

Expand Down Expand Up @@ -419,7 +436,7 @@ TEST(udpcap, Timeout)

ASSERT_EQ(error, Udpcap::Error::TIMEOUT);
ASSERT_EQ(received_bytes, 0);
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count(), 100);
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count(), 100); // TODO: This sometimes fails. Check why!
}

// Something already is in the socket, so the call must return earlier
Expand Down Expand Up @@ -489,16 +506,191 @@ TEST(udpcap, Timeout)
udpcap_socket.close();
}

// TODO: Write a test that tests the Source Address and Source Port
// Test receiving without binding the socket (-> error)
TEST(udpcap, ReceiveNotBound)
{
// Create a udpcap socket
Udpcap::UdpcapSocket udpcap_socket;
ASSERT_TRUE(udpcap_socket.isValid());

// Initialize variables for the sender's address and port
Udpcap::HostAddress sender_address;
uint16_t sender_port(0);

// Allocate buffer with max udp datagram size
std::vector<char> received_datagram;
received_datagram.resize(65536);

// Initialize error object
Udpcap::Error error = Udpcap::Error::ErrorCode::GENERIC_ERROR;

// blocking receive
size_t received_bytes = udpcap_socket.receiveDatagram(received_datagram.data(), received_datagram.size(), &sender_address, &sender_port, error);

// Check if the received datagram is valid and contains "Hello World"
ASSERT_EQ(received_bytes, 0);
ASSERT_TRUE(bool(error));
ASSERT_EQ(error, Udpcap::Error(Udpcap::Error::ErrorCode::NOT_BOUND));
}

// TODO: Test the returned errors of the receiveDatagram function
// Test the multicast functionality
TEST(udpcap, MulticastReceive)
{
atomic_signalable<int> received_messages1(0);
atomic_signalable<int> received_messages2(0);

// TODO: test isclosed function
// Create two udpcap sockets
Udpcap::UdpcapSocket udpcap_socket1;
ASSERT_TRUE(udpcap_socket1.isValid());

// TODO: rapidly create and destroy sockets to see if the memory is freed correctly https://stackoverflow.com/questions/29174938/googletest-and-memory-leaks
Udpcap::UdpcapSocket udpcap_socket2;
ASSERT_TRUE(udpcap_socket2.isValid());

// TODO: Test Multicast Receive
udpcap_socket1.setMulticastLoopbackEnabled(true);
udpcap_socket2.setMulticastLoopbackEnabled(true);

// TODO: Test with multiple multicast sockets, that each only receive their own multicast group
// Bind the udpcap sockets to all interfaces
{
bool success = udpcap_socket1.bind(Udpcap::HostAddress::Any(), 14000);
ASSERT_TRUE(success);
}
{
bool success = udpcap_socket2.bind(Udpcap::HostAddress::Any(), 14000);
ASSERT_TRUE(success);
}

// Join the multicast group 224.0.0.1 on both sockets
{
bool success = udpcap_socket1.joinMulticastGroup(Udpcap::HostAddress("224.0.0.1"));
ASSERT_TRUE(success);
}
{
bool success = udpcap_socket2.joinMulticastGroup(Udpcap::HostAddress("224.0.0.1"));
ASSERT_TRUE(success);
}

// Join the multicast group 224.0.0.2 on the second socket
{
bool success = udpcap_socket2.joinMulticastGroup(Udpcap::HostAddress("224.0.0.2"));
ASSERT_TRUE(success);
}

// Create an asio UDP sender socket
asio::io_service io_service;
asio::ip::udp::socket asio_socket(io_service, asio::ip::udp::v4());

// open the socket for multicast sending
asio_socket.set_option(asio::ip::multicast::hops(1));
asio_socket.set_option(asio::ip::multicast::enable_loopback(true));


// Receive datagrams in a separate thread for Socket1 (checks for 224.0.0.1)
std::thread receive_thread1([&udpcap_socket1, &received_messages1]()
{
while (true)
{
// Initialize variables for the sender's address and port
Udpcap::HostAddress sender_address;
uint16_t sender_port(0);

Udpcap::Error error = Udpcap::Error::ErrorCode::GENERIC_ERROR;

std::vector<char> received_datagram;
received_datagram.resize(65536);

size_t bytes_received = udpcap_socket1.receiveDatagram(received_datagram.data(), received_datagram.size(), &sender_address, &sender_port, error);
received_datagram.resize(bytes_received);

if (error)
{
// Indicates that somebody closed the socket
ASSERT_EQ(received_messages1.get(), 1);

// Check the error reason
ASSERT_EQ(error, Udpcap::Error(Udpcap::Error::ErrorCode::SOCKET_CLOSED));

// Check that the socket is closed
ASSERT_TRUE(udpcap_socket1.isClosed());

break;
}

// Check if the received datagram is valid and contains "224.0.0.1"
ASSERT_EQ(std::string(received_datagram.data(), received_datagram.size()), "224.0.0.1");
received_messages1++;
}
});

// Receive datagrams in a separate thread for Socket2 (checks for 224.0.0.1 or 224.0.0.2)
std::thread receive_thread2([&udpcap_socket2, &received_messages2]()
{
while (true)
{
// Initialize variables for the sender's address and port
Udpcap::HostAddress sender_address;
uint16_t sender_port(0);

Udpcap::Error error = Udpcap::Error::ErrorCode::GENERIC_ERROR;

std::vector<char> received_datagram;
received_datagram.resize(65536);

size_t bytes_received = udpcap_socket2.receiveDatagram(received_datagram.data(), received_datagram.size(), &sender_address, &sender_port, error);
received_datagram.resize(bytes_received);

if (error)
{
// Indicates that somebody closed the socket
ASSERT_EQ(received_messages2.get(), 2);

// Check the error reason
ASSERT_EQ(error, Udpcap::Error(Udpcap::Error::ErrorCode::SOCKET_CLOSED));

// Check that the socket is closed
ASSERT_TRUE(udpcap_socket2.isClosed());

break;
}

// Check if the received datagram is valid and contains "224.0.0.1" or "224.0.0.2"
ASSERT_TRUE(std::string(received_datagram.data(), received_datagram.size()) == "224.0.0.1"
|| std::string(received_datagram.data(), received_datagram.size()) == "224.0.0.2");
received_messages2++;
}
});

// Send the multicast message to 224.0.0.1
{
const asio::ip::udp::endpoint endpoint(asio::ip::make_address("224.0.0.1"), 14000);
std::string buffer_string = "224.0.0.1";
asio_socket.send_to(asio::buffer(buffer_string), endpoint);
}

// Send the multicast message to 224.0.0.2
{
const asio::ip::udp::endpoint endpoint(asio::ip::make_address("224.0.0.2"), 14000);
std::string buffer_string = "224.0.0.2";
asio_socket.send_to(asio::buffer(buffer_string), endpoint);
}

// Wait for received_messages1 to be 1 and received_messages2 to be 2
received_messages1.wait_for([](int value) { return value >= 1; }, std::chrono::milliseconds(500));
received_messages2.wait_for([](int value) { return value >= 2; }, std::chrono::milliseconds(500));

// Check if the received message counters
ASSERT_EQ(received_messages1.get(), 1);
ASSERT_EQ(received_messages2.get(), 2);

// Close the sockets
asio_socket.close();
udpcap_socket1.close();
udpcap_socket2.close();

// Join the threads
receive_thread1.join();
receive_thread2.join();
}

// TODO: Write a test that tests the Source Address and Source Port

// TODO: Create many sockets in threads, wait for them and destroy them to see if there are any race conditions that lead to crashes
11 changes: 6 additions & 5 deletions udpcap/src/npcap_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,30 +207,31 @@ namespace Udpcap

bool TestLoopbackDevice()
{
typedef std::unique_ptr<pcap_if_t*, void(*)(pcap_if_t**)> pcap_if_t_uniqueptr;

std::array<char, PCAP_ERRBUF_SIZE> errbuf{};
pcap_if_t* alldevs_rawptr = nullptr;
const pcap_if_t_uniqueptr alldevs(&alldevs_rawptr, [](pcap_if_t** p) { pcap_freealldevs(*p); });

bool loopback_device_found = false;

if (pcap_findalldevs(alldevs.get(), errbuf.data()) == -1)
if (pcap_findalldevs(&alldevs_rawptr, errbuf.data()) == -1)
{
human_readible_error_ = "Error in pcap_findalldevs: " + std::string(errbuf.data());
fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf.data());
if (alldevs_rawptr != nullptr)
pcap_freealldevs(alldevs_rawptr);
return false;
}

// Check if the loopback device is accessible
for (pcap_if_t* pcap_dev = *alldevs.get(); pcap_dev != nullptr; pcap_dev = pcap_dev->next)
for (pcap_if_t* pcap_dev = alldevs_rawptr; pcap_dev != nullptr; pcap_dev = pcap_dev->next)
{
if (IsLoopbackDevice_NoLock(pcap_dev->name))
{
loopback_device_found = true;
}
}

pcap_freealldevs(alldevs_rawptr);

// if we didn't find the loopback device, the test has failed
if (!loopback_device_found)
{
Expand Down
Loading

0 comments on commit 39ff173

Please sign in to comment.