Mercurial > irccd
diff tests/src/libcommon/io/main.cpp @ 670:95ac3ace1610
Common: introduce new io code
To avoid code duplication in accept, connect, reading and writing we add a new
set of classes in `io` namespaces located in the following files:
- stream.hpp, acceptor.hpp, connector.hpp
These classes consist of pure abstract interfaces for I/O. Then we reimplement
them in the following files:
- socket_stream.hpp, socket_acceptor.hpp, socket_connector.hpp,
- tls_stream.hpp, tls_acceptor.hpp, tls_conncetor.hpp (for SSL).
This allows future independant connections such as DBus, fifo or any other fancy
optional stuff.
We also no longer need large class hierarchy such as `connection` for irccdctl
controller or transport_server, transport_client classes.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 10 Apr 2018 21:20:30 +0200 |
parents | tests/src/libcommon/network-stream/main.cpp@8a79b5c0ddc7 |
children | 91bc29e87399 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libcommon/io/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,223 @@ +/* + * main.cpp -- test io classes + * + * Copyright (c) 2013-2018 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. + */ + +#define BOOST_TEST_MODULE "io" +#include <boost/test/unit_test.hpp> +#include <boost/mpl/list.hpp> + +#include <irccd/sysconfig.hpp> + +#include <irccd/socket_acceptor.hpp> +#include <irccd/socket_connector.hpp> +#include <irccd/socket_stream.hpp> + +#if defined(HAVE_SSL) +# include <irccd/tls_acceptor.hpp> +# include <irccd/tls_connector.hpp> +# include <irccd/tls_stream.hpp> +#endif // !HAVE_SSL + +using boost::asio::io_service; +using boost::asio::ip::tcp; + +#if defined(HAVE_SSL) +using boost::asio::ssl::context; +#endif + +#if !defined(IRCCD_SYSTEM_WINDOWS) +using boost::asio::local::stream_protocol; +#endif + +namespace irccd { + +class io_test { +public: + io_service service_; + + std::unique_ptr<io::acceptor> acceptor_; + std::unique_ptr<io::connector> connector_; + + std::shared_ptr<io::stream> stream1_; + std::shared_ptr<io::stream> stream2_; + + virtual std::unique_ptr<io::acceptor> create_acceptor() = 0; + + virtual std::unique_ptr<io::connector> create_connector() = 0; + + void init() + { + acceptor_ = create_acceptor(); + connector_ = create_connector(); + + acceptor_->accept([this] (auto code, auto stream) { + if (code) + throw std::system_error(code); + + stream1_ = std::move(stream); + }); + connector_->connect([this] (auto code, auto stream) { + if (code) + throw std::system_error(code); + + stream2_ = std::move(stream); + }); + + service_.run(); + service_.reset(); + } +}; + +class ip_io_test : public io_test { +private: + tcp::endpoint endpoint_; + +protected: + /** + * \copydoc io_test::create_acceptor + */ + std::unique_ptr<io::acceptor> create_acceptor() override + { + tcp::endpoint endpoint(tcp::v4(), 0U); + tcp::acceptor acceptor(service_, std::move(endpoint)); + + endpoint_ = acceptor.local_endpoint(); + + return std::make_unique<io::ip_acceptor>(std::move(acceptor)); + } + + /** + * \copydoc io_test::create_connector + */ + std::unique_ptr<io::connector> create_connector() override + { + return std::make_unique<io::ip_connector>(service_, endpoint_); + } +}; + +#if defined(HAVE_SSL) + +class ssl_io_test : public io_test { +private: + tcp::endpoint endpoint_; + +protected: + /** + * \copydoc io_test::create_acceptor + */ + std::unique_ptr<io::acceptor> create_acceptor() override + { + context context(context::sslv23); + + context.use_certificate_file(TESTS_SOURCE_DIR "/data/test.crt", context::pem); + context.use_private_key_file(TESTS_SOURCE_DIR "/data/test.key", context::pem); + + tcp::endpoint endpoint(tcp::v4(), 0U); + tcp::acceptor acceptor(service_, std::move(endpoint)); + + endpoint_ = acceptor.local_endpoint(); + + return std::make_unique<io::tls_acceptor<>>(std::move(context), std::move(acceptor)); + } + + /** + * \copydoc io_test::create_connector + */ + std::unique_ptr<io::connector> create_connector() override + { + return std::make_unique<io::tls_connector<>>(context(context::sslv23), service_, endpoint_); + } +}; + +#endif // !HAVE_SSL + +#if !defined(IRCCD_SYSTEM_WINDOWS) + +class local_io_test : public io_test { +public: + /** + * \copydoc io_test::create_acceptor + */ + std::unique_ptr<io::acceptor> create_acceptor() override + { + std::remove(CMAKE_BINARY_DIR "/tmp/io-test.sock"); + + stream_protocol::acceptor acceptor(service_, CMAKE_BINARY_DIR "/tmp/io-test.sock"); + + return std::make_unique<io::local_acceptor>(std::move(acceptor)); + } + + /** + * \copydoc io_test::create_connector + */ + std::unique_ptr<io::connector> create_connector() override + { + return std::make_unique<io::local_connector>(service_, CMAKE_BINARY_DIR "/tmp/io-test.sock"); + } +}; + +#endif // !IRCCD_SYSTEM_WINDOWS + +/** + * List of fixtures to tests. + */ +using list = boost::mpl::list< + ip_io_test +#if defined(HAVE_SSL) + , ssl_io_test +#endif +#if !defined(IRCCD_SYSTEM_WINDOWS) + , local_io_test +#endif +>; + +BOOST_AUTO_TEST_CASE_TEMPLATE(invalid_argument, Test, list) +{ + Test fixture; + + const nlohmann::json message{ + { "abc", 123 }, + { "def", 456 } + }; + + fixture.init(); + fixture.stream1_->read([] (auto code, auto message) { + BOOST_TEST(!code); + BOOST_TEST(message.is_object()); + BOOST_TEST(message["abc"].template get<int>() == 123); + BOOST_TEST(message["def"].template get<int>() == 456); + }); + fixture.stream2_->write(message, [] (auto code) { + BOOST_TEST(!code); + }); + fixture.service_.run(); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(network_down, Test, list) +{ + Test fixture; + + fixture.init(); + fixture.stream1_->read([] (auto code, auto message) { + BOOST_TEST(code.value() == static_cast<int>(std::errc::not_connected)); + BOOST_TEST(message.is_null()); + }); + fixture.stream2_ = nullptr; + fixture.service_.run(); +} + +} // !irccd