view C++/Tests/Sockets/main.cpp @ 280:91eb0583df52

Socket: * Fix select method that require fdset's to be rebuild at each iteration, * While here always create a m_interface Task: #306
author David Demelier <markand@malikania.fr>
date Fri, 24 Oct 2014 18:28:39 +0200
parents adcae2bde2f0
children ea55a3886da0
line wrap: on
line source

/*
 * main.cpp -- test sockets
 *
 * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>

#include <gtest/gtest.h>

#include <Socket.h>
#include <SocketListener.h>
#include <SocketAddress.h>

using namespace std::literals::chrono_literals;

using namespace error;
using namespace address;

/* --------------------------------------------------------
 * Miscellaneous
 * -------------------------------------------------------- */

TEST(Misc, set)
{
	Socket s;

	try {
		s = { AF_INET6, SOCK_STREAM, 0 };

		s.set(IPPROTO_IPV6, IPV6_V6ONLY, 0);
		ASSERT_EQ(0, s.get<int>(IPPROTO_IPV6, IPV6_V6ONLY));

		s.set(IPPROTO_IPV6, IPV6_V6ONLY, 1);
		ASSERT_EQ(1, s.get<int>(IPPROTO_IPV6, IPV6_V6ONLY));
	} catch (const std::exception &ex) {
		std::cerr << "warning: " << ex.what() << std::endl;
	}

	s.close();
}

TEST(Misc, initializer)
{
	Socket s1, s2;

	try {
		s1 = { AF_INET6, SOCK_STREAM, 0 };
		s2 = { AF_INET6, SOCK_STREAM, 0 };

		SocketListener listener {
			{ s1, Read	},
			{ s2, Read	},
		};

		ASSERT_EQ(2UL, listener.size());

		listener.list([&] (const auto &so, auto direction) {
			ASSERT_TRUE(so == s1 || so == s2);
			ASSERT_EQ(Read, direction);
		});
	} catch (const std::exception &ex) {
		std::cerr << "warning: " << ex.what() << std::endl;
	}

	s1.close();
	s2.close();
}

/* --------------------------------------------------------
 * Select tests
 * -------------------------------------------------------- */

TEST(ListenerMethodSelect, timeout)
{
	std::thread server([] () {
		Socket s, client;
		SocketListener listener(Select);
		bool running = true;
		int tries = 0;

		try {
			s = { AF_INET, SOCK_STREAM, 0 };
			s.set(SOL_SOCKET, SO_REUSEADDR, 1);
			s.bind(Internet{"*", 10000, AF_INET});
			s.listen(10);

			listener.add(s, Read);

			while (running) {
				try {
					listener.select(500ms);
					client = s.accept();
					running = false;

					// Abort if no client connected
					if (tries >= 10)
						running = false;
				} catch (const Timeout &) {
					puts("DEBUG: TIMEOUT");
				}
			}
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s.close();
		client.close();
	});

	std::thread client([] () {
		std::this_thread::sleep_for(2s);

		Socket s;

		try {
			s = { AF_INET, SOCK_STREAM, 0 };
			s.connect(Internet{"localhost", 10000, AF_INET});
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s.close();
	});

	server.join();
	client.join();
}

TEST(ListenerMethodSelect, add)
{
	Socket s, s2;

	try {
		s = { AF_INET, SOCK_STREAM, 0 };
		s2 = { AF_INET, SOCK_STREAM, 0 };
		SocketListener listener(Select);

		listener.add(s, Read);
		listener.add(s2, Read);

		ASSERT_EQ(2UL, listener.size());
	} catch (const std::exception &ex) {
		std::cerr << "warning: " << ex.what() << std::endl;
	}

	s.close();
	s2.close();
}

TEST(ListenerMethodSelect, remove)
{
	Socket s, s2;

	try {
		s = { AF_INET, SOCK_STREAM, 0 };
		s2 = { AF_INET, SOCK_STREAM, 0 };
		SocketListener listener(Select);

		listener.add(s, Read);
		listener.add(s2, Read);
		listener.remove(s, Read);
		listener.remove(s2, Read);

		ASSERT_EQ(0UL, listener.size());
	} catch (const std::exception &ex) {
		std::cerr << "warning: " << ex.what() << std::endl;
	}

	s.close();
	s2.close();
}

/*
 * Add two sockets for both reading and writing, them remove only reading and then
 * move only writing.
 */
TEST(ListenerMethodSelect, inOut)
{
	Socket s, s2;

	try {
		s = { AF_INET, SOCK_STREAM, 0 };
		s2 = { AF_INET, SOCK_STREAM, 0 };
		SocketListener listener(Select);

		listener.add(s, Read | Write);
		listener.add(s2, Read | Write);

		listener.list([&] (Socket &si, int dir) {
			ASSERT_TRUE(si == s || si == s2);
			ASSERT_EQ(0x03, static_cast<int>(dir));
		});

		listener.remove(s, Write);
		listener.remove(s2, Write);

		ASSERT_EQ(2UL, listener.size());

		listener.list([&] (Socket &si, int dir) {
			ASSERT_TRUE(si == s || si == s2);
			ASSERT_EQ(Read, dir);
		});

		listener.remove(s, Read);
		listener.remove(s2, Read);

		ASSERT_EQ(0UL, listener.size());
	} catch (const std::exception &ex) {
		std::cerr << "warning: " << ex.what() << std::endl;
	}

	s.close();
	s2.close();
}

TEST(ListenerMethodSelect, addSame)
{
	Socket s;

	try {
		s = { AF_INET, SOCK_STREAM, 0 };
		SocketListener listener(Select);

		listener.add(s, Read);
		ASSERT_EQ(1UL, listener.size());
		listener.list([&] (const Socket &si, int dir) {
			ASSERT_TRUE(si == s);
			ASSERT_EQ(Read, dir);
		});

		listener.add(s, Write);
		ASSERT_EQ(1UL, listener.size());
		listener.list([&] (const Socket &si, int dir) {
			ASSERT_TRUE(si == s);
			ASSERT_EQ(0x03, static_cast<int>(dir));
		});

		// Oops, added the same
		listener.add(s, Read);
		listener.add(s, Write);
		listener.add(s, Read | Write);

		ASSERT_EQ(1UL, listener.size());
		listener.list([&] (const Socket &si, int dir) {
			ASSERT_TRUE(si == s);
			ASSERT_EQ(0x03, static_cast<int>(dir));
		});
	} catch (const std::exception &ex) {
		std::cerr << "warning: " << ex.what() << std::endl;
	}

	s.close();
}

/* --------------------------------------------------------
 * Poll tests
 * -------------------------------------------------------- */

TEST(ListenerMethodPoll, timeout)
{
	std::thread server([] () {
		Socket s, client;
		SocketListener listener(Poll);
		bool running = true;
		int tries = 0;

		try {
			s = { AF_INET, SOCK_STREAM, 0 };
			s.set(SOL_SOCKET, SO_REUSEADDR, 1);
			s.bind(Internet{"*", 10000, AF_INET});
			s.listen(10);

			listener.add(s, Read);

			while (running) {
				try {
					listener.select(500ms);
					client = s.accept();
					running = false;

					// Abort if no client connected
					if (tries >= 10)
						running = false;
				} catch (const Timeout &) {
					puts("DEBUG: TIMEOUT");
				}
			}
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s.close();
		client.close();
	});

	std::thread client([] () {
		std::this_thread::sleep_for(2s);

		Socket s;

		try {
			s = { AF_INET, SOCK_STREAM, 0 };
			s.connect(Internet{"localhost", 10000, AF_INET});
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s.close();
	});

	server.join();
	client.join();
}

TEST(ListenerMethodPoll, add)
{
	Socket s, s2;

	try {
		s = { AF_INET, SOCK_STREAM, 0 };
		s2 = { AF_INET, SOCK_STREAM, 0 };
		SocketListener listener(Poll);

		listener.add(s, Read);
		listener.add(s2, Read);

		ASSERT_EQ(2UL, listener.size());
	} catch (const std::exception &ex) {
		std::cerr << "warning: " << ex.what() << std::endl;
	}

	s.close();
	s2.close();
}

TEST(ListenerMethodPoll, remove)
{
	Socket s, s2;

	try {
		s = { AF_INET, SOCK_STREAM, 0 };
		s2 = { AF_INET, SOCK_STREAM, 0 };
		SocketListener listener(Poll);

		listener.add(s, Read);
		listener.add(s2, Read);
		listener.remove(s, Read);
		listener.remove(s2, Read);

		ASSERT_EQ(0UL, listener.size());
	} catch (const std::exception &ex) {
		std::cerr << "warning: " << ex.what() << std::endl;
	}

	s.close();
	s2.close();
}

TEST(ListenerMethodPoll, inOut)
{
	Socket s, s2;

	try {
		s = { AF_INET, SOCK_STREAM, 0 };
		s2 = { AF_INET, SOCK_STREAM, 0 };
		SocketListener listener(Poll);

		listener.add(s, Read | Write);
		listener.add(s2, Read | Write);

		listener.list([&] (Socket &si, int dir) {
			ASSERT_TRUE(si == s || si == s2);
			ASSERT_EQ(0x03, static_cast<int>(dir));
		});

		listener.remove(s, Write);
		listener.remove(s2, Write);

		ASSERT_EQ(2UL, listener.size());

		listener.list([&] (Socket &si, int dir) {
			ASSERT_TRUE(si == s || si == s2);
			ASSERT_EQ(Read, dir);
		});

		listener.remove(s, Read);
		listener.remove(s2, Read);

		ASSERT_EQ(0UL, listener.size());
	} catch (const std::exception &ex) {
		std::cerr << "warning: " << ex.what() << std::endl;
	}

	s.close();
	s2.close();
}

TEST(ListenerMethodPoll, addSame)
{
	Socket s;

	try {
		s = { AF_INET, SOCK_STREAM, 0 };
		SocketListener listener(Poll);

		listener.add(s, Read);
		ASSERT_EQ(1UL, listener.size());
		listener.list([&] (const Socket &si, int dir) {
			ASSERT_TRUE(si == s);
			ASSERT_EQ(Read, dir);
		});

		listener.add(s, Write);
		ASSERT_EQ(1UL, listener.size());
		listener.list([&] (const Socket &si, int dir) {
			ASSERT_TRUE(si == s);
			ASSERT_EQ(0x03, static_cast<int>(dir));
		});

		// Oops, added the same
		listener.add(s, Read);
		listener.add(s, Write);
		listener.add(s, Read | Write);

		ASSERT_EQ(1UL, listener.size());
		listener.list([&] (const Socket &si, int dir) {
			ASSERT_TRUE(si == s);
			ASSERT_EQ(0x03, static_cast<int>(dir));
		});
	} catch (const std::exception &ex) {
		std::cerr << "warning: " << ex.what() << std::endl;
	}

	s.close();
}

/* --------------------------------------------------------
 * Socket listener class
 * -------------------------------------------------------- */

TEST(Listener, connection)
{
	std::thread server([] () {
		Socket s;
		SocketListener listener;

		try {
			s = { AF_INET, SOCK_STREAM, 0 };

			s.set(SOL_SOCKET, SO_REUSEADDR, 1);
			s.bind(Internet{"*", 10000, AF_INET});
			s.listen(8);

			listener.add(s, Read);

			auto client = listener.select(10s);

			ASSERT_TRUE(client.direction == Read);
			ASSERT_TRUE(client.socket == s);
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s.close();
	});

	std::thread client([] () {
		Socket client;

		std::this_thread::sleep_for(500ms);

		try {
			client = { AF_INET, SOCK_STREAM, 0 };
			client.connect(Internet{"localhost", 10000, AF_INET});
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		client.close();
	});

	server.join();
	client.join();
}

TEST(Listener, connectionAndRead)
{
	std::thread server([] () {
		Socket s;
		SocketListener listener;

		try {
			s = { AF_INET, SOCK_STREAM, 0 };

			s.set(SOL_SOCKET, SO_REUSEADDR, 1);
			s.bind(Internet("*", 10000, AF_INET));
			s.listen(8);

			// Read for master
			listener.add(s, Read);

			auto result = listener.select(10s);

			ASSERT_TRUE(result.direction == Read);
			ASSERT_TRUE(result.socket == s);

			// Wait for client
			auto client = s.accept();
			listener.add(client, Read);

			result = listener.select(10s);

			ASSERT_TRUE(result.direction == Read);
			ASSERT_TRUE(result.socket == client);

			char data[512];
			auto nb = client.recv(data, sizeof (data) - 1);

			data[nb] = '\0';

			client.close();
			ASSERT_STREQ("hello world", data);
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s.close();
	});

	std::thread client([] () {
		Socket client;

		std::this_thread::sleep_for(500ms);

		try {
			client = Socket(AF_INET, SOCK_STREAM, 0);
			client.connect(Internet("localhost", 10000, AF_INET));
			client.send("hello world");
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		client.close();
	});

	server.join();
	client.join();
}

TEST(Listener, bigData)
{
	std::thread server([] () {
		std::ostringstream out;

		Socket server;
		SocketListener listener;
		bool finished(false);

		try {
			server = { AF_INET, SOCK_STREAM, 0 };

			server.set(SOL_SOCKET, SO_REUSEADDR, 1);
			server.bind(Internet{"*", 10000, AF_INET});
			server.listen(10);
			listener.add(server, Read);

			while (!finished) {
				auto s = listener.select(60s).socket;

				if (s == server) {
					listener.add(s.accept(), Read);
				} else {
					char data[512];
					auto nb = s.recv(data, sizeof (data) - 1);

					if (nb == 0)
						finished = true;
					else {
						data[nb] = '\0';
						out << data;
					}
				}
			}

			ASSERT_EQ(9000002UL, out.str().size());
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		server.close();
	});

	std::thread client([] () {
		std::string data;

		data.reserve(9000000);
		for (int i = 0; i < 9000000; ++i)
			data.push_back('a');

		data.push_back('\r');
		data.push_back('\n');

		std::this_thread::sleep_for(500ms);

		Socket client;
		SocketListener listener;

		try {
			client = { AF_INET, SOCK_STREAM, 0 };

			client.connect(Internet{"localhost", 10000, AF_INET});
			client.blockMode(false);
			listener.add(client, Write);

			while (data.size() > 0) {
				auto s = listener.select(30s).socket;
				auto nb = s.send(data.data(), data.size());
				data.erase(0, nb);
			}
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		client.close();
	});

	server.join();
	client.join();
}

/* --------------------------------------------------------
 * Multiple selection tests
 * -------------------------------------------------------- */

TEST(MultipleSelection, select)
{
	/*
	 * Normally, 3 sockets added for writing should be marked ready immediately
	 * as there are no data being currently queued to be sent.
	 */
	std::thread server([] () {
		Socket master;

		try {
			SocketListener masterListener, clientListener;
			master = { AF_INET, SOCK_STREAM, 0 };

			master.set(SOL_SOCKET, SO_REUSEADDR, 1);
			master.bind(Internet{"*", 10000, AF_INET});
			master.listen(8);

			masterListener.add(master, Read);

			while (clientListener.size() != 3) {
				masterListener.select(3s);
				clientListener.add(master.accept(), Write);
			}

			// Now do the test of writing
			auto result = clientListener.selectMultiple(3s);
			ASSERT_EQ(3UL, result.size());

			clientListener.list([] (auto s, auto) {
				s.close();
			});
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		master.close();
	});

	std::thread client([] () {
		Socket s1, s2, s3;

		try {
			s1 = { AF_INET, SOCK_STREAM, 0 };
			s2 = { AF_INET, SOCK_STREAM, 0 };
			s3 = { AF_INET, SOCK_STREAM, 0 };

			std::this_thread::sleep_for(1s);

			s1.connect(Internet{"localhost", 10000, AF_INET});
			s2.connect(Internet{"localhost", 10000, AF_INET});
			s3.connect(Internet{"localhost", 10000, AF_INET});
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s1.close();
		s2.close();
		s3.close();
	});

	server.join();
	client.join();
}

#if defined(SOCKET_LISTENER_HAVE_POLL)

TEST(MultipleSelection, poll)
{
	/*
	 * Normally, 3 sockets added for writing should be marked ready immediately
	 * as there are no data being currently queued to be sent.
	 */
	std::thread server([] () {
		Socket master;

		try {
			SocketListener masterListener(Poll), clientListener(Poll);
			master = { AF_INET, SOCK_STREAM, 0 };

			master.set(SOL_SOCKET, SO_REUSEADDR, 1);
			master.bind(Internet{"*", 10000, AF_INET});
			master.listen(8);

			masterListener.add(master, Read);

			while (clientListener.size() != 3) {
				masterListener.select(3s);
				clientListener.add(master.accept(), Write);
			}

			// Now do the test of writing
			auto result = clientListener.selectMultiple(3s);
			ASSERT_EQ(3UL, result.size());

			clientListener.list([] (auto s, auto) {
				s.close();
			});
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		master.close();
	});

	std::thread client([] () {
		Socket s1, s2, s3;

		try {
			s1 = { AF_INET, SOCK_STREAM, 0 };
			s2 = { AF_INET, SOCK_STREAM, 0 };
			s3 = { AF_INET, SOCK_STREAM, 0 };

			std::this_thread::sleep_for(1s);

			s1.connect(Internet("localhost", 10000, AF_INET));
			s2.connect(Internet("localhost", 10000, AF_INET));
			s3.connect(Internet("localhost", 10000, AF_INET));
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s1.close();
		s2.close();
		s3.close();
	});

	server.join();
	client.join();
}

#endif

/* --------------------------------------------------------
 * Basic TCP tests
 * -------------------------------------------------------- */

TEST(BasicTcp, sendipv4) {
	std::thread server([] () {
		Socket server, client;

		try {
			server = { AF_INET, SOCK_STREAM, 0 };

			server.set(SOL_SOCKET, SO_REUSEADDR, 1);
			server.bind(Internet{"*", 10000, AF_INET});
			server.listen(8);

			client = server.accept();

			char data[512];
			auto nb = client.recv(data, sizeof (data) - 1);

			data[nb] = '\0';

			ASSERT_STREQ("hello", data);
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		server.close();
		client.close();
	});

	std::thread client([] () {
		Socket s;

		std::this_thread::sleep_for(500ms);
		try {
			s = { AF_INET, SOCK_STREAM, 0 };

			s.connect(Internet{"localhost", 10000, AF_INET});
			s.send("hello");
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s.close();
	});

	server.join();
	client.join();
}

TEST(BasicTcp, sendipv6) {
	std::thread server([] () {
		Socket server, client;

		try {
			server = { AF_INET6, SOCK_STREAM, 0 };

			server.set(SOL_SOCKET, SO_REUSEADDR, 1);
			server.bind(Internet("*", 10000, AF_INET6));
			server.listen(8);

			client = server.accept();

			char data[512];
			auto nb = client.recv(data, sizeof (data) - 1);

			data[nb] = '\0';

			ASSERT_STREQ("hello", data);
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		server.close();
		client.close();
	});

	std::thread client([] () {
		Socket s;

		std::this_thread::sleep_for(500ms);
		try {
			s = { AF_INET6, SOCK_STREAM, 0 };

			s.connect(Internet{"ip6-localhost", 10000, AF_INET6});
			s.send("hello");
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s.close();
	});

	server.join();
	client.join();
}

#if !defined(_WIN32)

TEST(BasicTcp, sendunix) {
	std::thread server([] () {
		Socket server, client;

		try {
			server = { AF_UNIX, SOCK_STREAM, 0 };

			server.set(SOL_SOCKET, SO_REUSEADDR, 1);
			server.bind(Unix("/tmp/gtest-send-tcp-unix.sock", true));
			server.listen(8);

			client = server.accept();

			char data[512];
			auto nb = client.recv(data, sizeof (data) - 1);

			data[nb] = '\0';

			ASSERT_STREQ("hello", data);
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		server.close();
		client.close();
	});

	std::thread client([] () {
		Socket s;

		std::this_thread::sleep_for(500ms);
		try {
			s = { AF_UNIX, SOCK_STREAM, 0 };

			s.connect(Unix{"/tmp/gtest-send-tcp-unix.sock"});
			s.send("hello");
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s.close();
	});

	server.join();
	client.join();
}

#endif

/* --------------------------------------------------------
 * Basic UDP tests
 * -------------------------------------------------------- */

TEST(BasicUdp, sendipv4) {
	std::thread server([] () {
		Socket server;

		try {
			server = { AF_INET, SOCK_DGRAM, 0 };

			server.set(SOL_SOCKET, SO_REUSEADDR, 1);
			server.bind(Internet("*", 10000, AF_INET));

			char data[512];
			auto nb = server.recvfrom(data, sizeof (data) - 1);

			data[nb] = '\0';

			ASSERT_STREQ("hello", data);
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		server.close();
	});

	std::thread client([] () {
		Socket s;

		std::this_thread::sleep_for(500ms);
		try {
			s = { AF_INET, SOCK_DGRAM, 0 };

			s.sendto("hello", Internet{"localhost", 10000, AF_INET});
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s.close();
	});

	server.join();
	client.join();
}

TEST(BasicUdp, sendipv6) {
	std::thread server([] () {
		Socket server;

		try {
			server = { AF_INET6, SOCK_DGRAM, 0 };

			server.set(SOL_SOCKET, SO_REUSEADDR, 1);
			server.set(IPPROTO_IPV6, IPV6_V6ONLY, 1);
			server.bind(Internet{"*", 10000, AF_INET6});

			char data[512];
			auto nb = server.recvfrom(data, sizeof (data) - 1);

			data[nb] = '\0';

			ASSERT_STREQ("hello", data);
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		server.close();
	});

	std::thread client([] () {
		Socket s;

		std::this_thread::sleep_for(500ms);
		try {
			s = { AF_INET6, SOCK_DGRAM, 0 };

			s.sendto("hello", Internet{"ip6-localhost", 10000, AF_INET6});
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s.close();
	});

	server.join();
	client.join();
}

#if !defined(_WIN32)

TEST(BasicUdp, sendunix) {
	std::thread server([] () {
		Socket server;

		try {
			server = { AF_UNIX, SOCK_DGRAM, 0 };

			server.set(SOL_SOCKET, SO_REUSEADDR, 1);
			server.bind(Unix{"/tmp/gtest-send-udp-unix.sock", true});

			char data[512];
			auto nb = server.recvfrom(data, sizeof (data) - 1);

			data[nb] = '\0';

			ASSERT_STREQ("hello", data);
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		server.close();
	});

	std::thread client([] () {
		Socket s;

		std::this_thread::sleep_for(500ms);
		try {
			s = { AF_UNIX, SOCK_DGRAM, 0 };

			s.sendto("hello", Unix{"/tmp/gtest-send-udp-unix.sock"});
		} catch (const std::exception &ex) {
			std::cerr << "warning: " << ex.what() << std::endl;
		}

		s.close();
	});

	server.join();
	client.join();
}

#endif

int main(int argc, char **argv)
{
	Socket::init();

	testing::InitGoogleTest(&argc, argv);

	auto ret = RUN_ALL_TESTS();

	Socket::finish();

	return ret;
}