123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
- //
- // 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 ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC 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 <server_common/socket_request.h>
- #include <server_common/portconfig.h>
- #include <asiodns/asiodns.h>
- #include <gtest/gtest.h>
- #include <boost/lexical_cast.hpp>
- #include <vector>
- #include <string>
- namespace isc {
- namespace server_common {
- namespace portconfig {
- // Access the private hidden flag
- extern bool test_mode;
- }
- }
- namespace testutils {
- /// \brief A testcase part for faking the SocketRequestor in tests
- ///
- /// It's awkward to request real sockets from the real socket creator
- /// during tests (for one, because it would have to be running, for
- /// another, we need to block real ports). If you instantiate this class in
- /// a test case, the socket requestor will be initialized to a test one which
- /// handles fake socket FDs and stores what was requested, etc.
- ///
- /// Furthermore, you can check if the code requested or released the correct
- /// list of sockets using the checkTokens() method.
- ///
- /// Some member variables are intentionally made public so that test cases
- /// can easily check the value of them. We prefer convenience for tests over
- /// class integrity here.
- class TestSocketRequestor : public isc::server_common::SocketRequestor {
- public:
- /// \brief Constructor
- ///
- /// \param dnss The DNS service. It is expected this gets initialized
- /// after the TestSocketRequestor constructor is called, as the
- /// TestSocketRequestor should be a base class and the service only
- /// a member.
- /// \param store Address store used when cleaning up.
- /// \param expect_port The port which is expected to be requested. If
- /// the application requests a different port, it is considered
- /// a failure.
- TestSocketRequestor(asiodns::DNSService& dnss,
- server_common::portconfig::AddressList& store,
- uint16_t expect_port) :
- last_token_(0), break_rollback_(false), dnss_(dnss), store_(store),
- expect_port_(expect_port)
- {
- // Prepare the requestor (us) for the test
- server_common::initTestSocketRequestor(this);
- // Don't manipulate the real sockets
- server_common::portconfig::test_mode = true;
- }
- /// \brief Destructor
- ///
- /// Removes the addresses (if any) installed by installListenAddresses,
- /// resets the socket requestor to uninitialized state and turns off
- /// the portconfig test mode.
- virtual ~TestSocketRequestor() {
- // Make sure no sockets are left inside (if installListenAddresses
- // wasn't used, this is NOP, so it won't hurt).
- server_common::portconfig::AddressList list;
- server_common::portconfig::installListenAddresses(list, store_, dnss_);
- // Don't leave invalid pointers here
- server_common::initTestSocketRequestor(NULL);
- // And return the mode
- server_common::portconfig::test_mode = false;
- }
- /// \brief Tokens released by releaseSocket
- ///
- /// They are stored here by this class and you can examine them.
- std::vector<std::string> released_tokens_;
- /// \brief Tokens returned from requestSocket
- ///
- /// They are stored here by this class and you can examine them.
- std::vector<std::string> given_tokens_;
- private:
- // Last token number and fd given out
- size_t last_token_;
- public:
- /// \brief Support a broken rollback case
- ///
- /// If this is set to true, the requestSocket will throw when the
- /// ::1 address is requested.
- bool break_rollback_;
- /// \brief Release a socket
- ///
- /// This only stores the token passed.
- /// \param token The socket to release
- void releaseSocket(const std::string& token) {
- released_tokens_.push_back(token);
- }
- /// \brief Request a socket
- ///
- /// This creates a new token and fakes a new socket and returns it.
- /// The token is stored.
- ///
- /// In case the address is 192.0.2.2, it throws SocketAllocateError
- /// or if the break_rollback_ is true and address is ::1, it throws
- /// ShareError. If the address is 192.0.2.3, it throws SocketError.
- ///
- /// The tokens produced are in form of protocol:address:port:fd. The fds
- /// start at 1 and increase by each successfull call.
- ///
- /// \param protocol The protocol to request
- /// \param address to bind to
- /// \param port to bind to
- /// \param mode checked to be DONT_SHARE for now
- /// \param name checked to be dummy_app for now
- /// \return The token and FD
- /// \throw SocketAllocateError as described above, to test error handling
- /// \throw ShareError as described above, to test error handling
- /// \throw SocketError as described above, to test error handling
- SocketID requestSocket(Protocol protocol, const std::string& address,
- uint16_t port, ShareMode mode,
- const std::string& name)
- {
- if (address == "192.0.2.2") {
- isc_throw(SocketAllocateError, "This address is not allowed");
- }
- if (address == "192.0.2.3") {
- isc_throw(SocketError, "Fatal test error");
- }
- if (address == "::1" && break_rollback_) {
- // This is valid address, but in case we need to break the
- // rollback, it needs to be busy or whatever
- //
- // We break the second address to see the first one was
- // allocated and then returned
- isc_throw(ShareError,
- "This address is available, but not for you");
- }
- const std::string proto(protocol == TCP ? "TCP" : "UDP");
- const size_t number = ++ last_token_;
- EXPECT_EQ(expect_port_, port);
- EXPECT_EQ(DONT_SHARE, mode);
- EXPECT_EQ("dummy_app", name);
- const std::string token(proto + ":" + address + ":" +
- boost::lexical_cast<std::string>(port) + ":" +
- boost::lexical_cast<std::string>(number));
- given_tokens_.push_back(token);
- return (SocketID(number, token));
- }
- /// \brief Check the list of tokens is as expected
- ///
- /// Compares the expected and real tokens.
- ///
- /// \param expected List of the expected tokens, as NULL-terminated array
- /// of C strings (it is more convenient to type as a constant than to
- /// manually push_back all the strings to a vector).
- /// \param real The token list that was produced by this class (usually
- /// either given_tokens_ or released_tokens_).
- /// \param scope Human readable identifier of which checkTokens call it is.
- /// It is printed as a part of failure message.
- void checkTokens(const char** expected,
- const std::vector<std::string>& real,
- const char* scope) const
- {
- SCOPED_TRACE(scope);
- size_t position(0);
- while (expected[position] != NULL) {
- ASSERT_LT(position, real.size());
- EXPECT_EQ(expected[position], real[position]) << position;
- position ++;
- }
- EXPECT_EQ(position, real.size());
- }
- private:
- asiodns::DNSService& dnss_;
- server_common::portconfig::AddressList& store_;
- const uint16_t expect_port_;
- };
- }
- }
|