Branch data Line data Source code
1 : : // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #ifndef __ISC_TESTUTILS_SOCKETREQUEST_H
16 : : #define __ISC_TESTUTILS_SOCKETREQUEST_H 1
17 : :
18 : : #include <server_common/socket_request.h>
19 : : #include <server_common/portconfig.h>
20 : :
21 : : #include <asiodns/asiodns.h>
22 : :
23 : : #include <gtest/gtest.h>
24 : : #include <boost/lexical_cast.hpp>
25 : :
26 : : #include <vector>
27 : : #include <string>
28 : :
29 : : namespace isc {
30 : : namespace testutils {
31 : :
32 : : /// \brief A testcase part for faking the SocketRequestor in tests
33 : : ///
34 : : /// It's awkward to request real sockets from the real socket creator
35 : : /// during tests (for one, because it would have to be running, for
36 : : /// another, we need to block real ports). If you instantiate this class in
37 : : /// a test case, the socket requestor will be initialized to a test one which
38 : : /// handles fake socket FDs and stores what was requested, etc.
39 : : ///
40 : : /// Furthermore, you can check if the code requested or released the correct
41 : : /// list of sockets using the checkTokens() method.
42 : : ///
43 : : /// Some member variables are intentionally made public so that test cases
44 : : /// can easily check the value of them. We prefer convenience for tests over
45 : : /// class integrity here.
46 : : class TestSocketRequestor : public isc::server_common::SocketRequestor {
47 : : public:
48 : : /// \brief Constructor
49 : : ///
50 : : /// \param dnss The DNS service. It is expected this gets initialized
51 : : /// after the TestSocketRequestor constructor is called, as the
52 : : /// TestSocketRequestor should be a base class and the service only
53 : : /// a member.
54 : : /// \param store Address store used when cleaning up.
55 : : /// \param expect_port The port which is expected to be requested. If
56 : : /// the application requests a different port, it is considered
57 : : /// a failure.
58 : : /// \param expeted_app The share name for which all the requests should
59 : : /// be made. This is not the usual app_name - the requestSocket does
60 : : /// not fall back to this value if its share_name is left empty, if
61 : : /// you want to check the code relies on the requestor to use the
62 : : /// app name, you set this to empty string.
63 : : TestSocketRequestor(asiodns::DNSServiceBase& dnss,
64 : : server_common::portconfig::AddressList& store,
65 : : uint16_t expect_port,
66 : : const std::string& expected_app) :
67 : : last_token_(0), break_rollback_(false), break_release_(false),
68 : : dnss_(dnss), store_(store), expect_port_(expect_port),
69 [ + - ]: 106 : expected_app_(expected_app)
70 : : {
71 : : // Prepare the requestor (us) for the test
72 [ - + ]: 106 : server_common::initTestSocketRequestor(this);
73 : : }
74 : :
75 : : /// \brief Destructor
76 : : ///
77 : : /// Removes the addresses (if any) installed by installListenAddresses,
78 : : /// resets the socket requestor to uninitialized state and turns off
79 : : /// the portconfig test mode.
80 : 106 : virtual ~TestSocketRequestor() {
81 : : // Make sure no sockets are left inside (if installListenAddresses
82 : : // wasn't used, this is NOP, so it won't hurt).
83 : 106 : server_common::portconfig::AddressList list;
84 [ + - ]: 106 : server_common::portconfig::installListenAddresses(list, store_, dnss_);
85 : : // Don't leave invalid pointers here
86 [ + - ]: 106 : server_common::initTestSocketRequestor(NULL);
87 : 106 : }
88 : :
89 : : /// \brief Tokens released by releaseSocket
90 : : ///
91 : : /// They are stored here by this class and you can examine them.
92 : : std::vector<std::string> released_tokens_;
93 : :
94 : : /// \brief Tokens returned from requestSocket
95 : : ///
96 : : /// They are stored here by this class and you can examine them.
97 : : std::vector<std::string> given_tokens_;
98 : : private:
99 : : // Last token number and fd given out
100 : : size_t last_token_;
101 : : public:
102 : : /// \brief Support a broken rollback case
103 : : ///
104 : : /// If this is set to true, the requestSocket will throw when the
105 : : /// ::1 address is requested.
106 : : bool break_rollback_;
107 : :
108 : : /// \brief Throw on releaseSocket
109 : : ///
110 : : /// If this is set to true, the releaseSocket will throw SocketError.
111 : : /// Defaults to false.
112 : : bool break_release_;
113 : :
114 : : /// \brief Release a socket
115 : : ///
116 : : /// This only stores the token passed.
117 : : /// \param token The socket to release
118 : : ///
119 : : /// \throw SocketError in case the break_release_ is set to true. This is
120 : : /// to test exception handling.
121 : 48 : void releaseSocket(const std::string& token) {
122 [ - + ]: 48 : if (break_release_) {
123 [ # # ]: 0 : isc_throw(SocketError, "Fatal test socket error");
124 : : }
125 : 48 : released_tokens_.push_back(token);
126 : 48 : }
127 : :
128 : : /// \brief Request a socket
129 : : ///
130 : : /// This creates a new token and fakes a new socket and returns it.
131 : : /// The token is stored.
132 : : ///
133 : : /// In case the address is 192.0.2.2, it throws SocketAllocateError
134 : : /// or if the break_rollback_ is true and address is ::1, it throws
135 : : /// ShareError. If the address is 192.0.2.3, it throws SocketError.
136 : : ///
137 : : /// The tokens produced are in form of protocol:address:port:fd. The fds
138 : : /// start at 1 and increase by each successfull call.
139 : : ///
140 : : /// \param protocol The protocol to request
141 : : /// \param address to bind to
142 : : /// \param port to bind to
143 : : /// \param mode checked to be SHARE_SAME for now
144 : : /// \param name checked to be the same as expected_app parameter of the
145 : : /// constructor. Note that this class does not provide the fallback
146 : : /// to an app_name if this is empty string. To check the code relies
147 : : /// on the fallback (wants to use the app_name instead of providing
148 : : /// its own share name), you need to create this class with empty
149 : : /// expected_app.
150 : : /// \return The token and FD
151 : : /// \throw SocketAllocateError as described above, to test error handling
152 : : /// \throw ShareError as described above, to test error handling
153 : : /// \throw SocketError as described above, to test error handling
154 : 56 : SocketID requestSocket(Protocol protocol, const std::string& address,
155 : : uint16_t port, ShareMode mode,
156 : : const std::string& name)
157 : : {
158 [ + + ]: 56 : if (address == "192.0.2.2") {
159 [ + - ]: 14 : isc_throw(SocketAllocateError, "This address is not allowed");
160 : : }
161 [ - + ]: 49 : if (address == "192.0.2.3") {
162 [ # # ]: 0 : isc_throw(SocketError, "Fatal test error");
163 : : }
164 [ + + ][ + + ]: 49 : if (address == "::1" && break_rollback_) {
[ + + ]
165 : : // This is valid address, but in case we need to break the
166 : : // rollback, it needs to be busy or whatever
167 : : //
168 : : // We break the second address to see the first one was
169 : : // allocated and then returned
170 [ + - ]: 2 : isc_throw(ShareError,
171 : : "This address is available, but not for you");
172 : : }
173 [ + + ]: 96 : const std::string proto(protocol == TCP ? "TCP" : "UDP");
174 : 48 : const size_t number = ++ last_token_;
175 [ - + ][ # # ]: 48 : EXPECT_EQ(expect_port_, port);
[ # # ][ # # ]
[ # # ][ + - ]
176 [ - + ][ # # ]: 48 : EXPECT_EQ(SHARE_SAME, mode);
[ # # ][ # # ]
[ # # ][ + - ]
177 [ - + ][ # # ]: 48 : EXPECT_EQ(expected_app_, name);
[ # # ][ # # ]
[ # # ][ + - ]
178 : 48 : const std::string token(proto + ":" + address + ":" +
179 : 96 : boost::lexical_cast<std::string>(port) + ":" +
180 [ + - ][ + - ]: 144 : boost::lexical_cast<std::string>(number));
[ + - ][ + - ]
[ + - ][ + - ]
181 [ + - ]: 48 : given_tokens_.push_back(token);
182 : 48 : return (SocketID(number, token));
183 : : }
184 : :
185 : : /// \brief Check the list of tokens is as expected
186 : : ///
187 : : /// Compares the expected and real tokens.
188 : : ///
189 : : /// \param expected List of the expected tokens, as NULL-terminated array
190 : : /// of C strings (it is more convenient to type as a constant than to
191 : : /// manually push_back all the strings to a vector).
192 : : /// \param real The token list that was produced by this class (usually
193 : : /// either given_tokens_ or released_tokens_).
194 : : /// \param scope Human readable identifier of which checkTokens call it is.
195 : : /// It is printed as a part of failure message.
196 : 16 : void checkTokens(const char** expected,
197 : : const std::vector<std::string>& real,
198 : : const char* scope) const
199 : : {
200 [ + - ][ + - ]: 32 : SCOPED_TRACE(scope);
201 : 16 : size_t position(0);
202 [ + + ]: 76 : while (expected[position] != NULL) {
203 [ + - ][ - + ]: 76 : ASSERT_LT(position, real.size());
[ # # ][ # # ]
[ # # ][ # # ]
[ + - ][ # # ]
204 [ - + ][ # # ]: 60 : EXPECT_EQ(expected[position], real[position]) << position;
[ # # ][ # # ]
[ # # ][ + - ]
205 : 60 : position ++;
206 : : }
207 [ - + ][ # # ]: 16 : EXPECT_EQ(position, real.size());
[ # # ][ # # ]
[ # # ][ + - ]
208 : : }
209 : :
210 : : private:
211 : : asiodns::DNSServiceBase& dnss_;
212 : : server_common::portconfig::AddressList& store_;
213 : : const uint16_t expect_port_;
214 : : const std::string expected_app_;
215 : : };
216 : :
217 : : }
218 : : }
219 : : #endif // __ISC_TESTUTILS_SOCKETREQUEST_H
|