2 * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
23 #include <condition_variable>
25 #include <unordered_map>
30 #include <boost/test/test_tools.hpp>
31 #include <boost_macros_wrapper.h>
33 #include <client-common.h>
34 #include <socket-manager.h>
36 #include <dpl/log/log.h>
42 constexpr char SERVICE_SOCKET_TEST[] = "/tmp/.central-key-manager-test.sock";
43 constexpr CKM::InterfaceID SOCKET_ID_TEST = 42;
44 constexpr std::chrono::seconds CV_TIMEOUT(10);
46 struct TestSocketManager : public SocketManager {
47 size_t TimeoutQueueSize() const { return m_timeoutQueue.size(); }
50 #define THREAD_REQUIRE_MESSAGE(test, message) \
53 std::ostringstream os; \
54 os << __FILE__ << ":" << __LINE__ << " " << #test << " " << message; \
55 throw std::runtime_error(os.str()); \
59 #define THREAD_REQUIRE(test) THREAD_REQUIRE_MESSAGE(test, "")
61 class TestService : public GenericSocketService {
63 ServiceDescriptionVector GetServiceDescription() override {
64 return ServiceDescriptionVector {
65 {SERVICE_SOCKET_TEST, "", SOCKET_ID_TEST}
68 void Event(const AcceptEvent &e) override {
69 std::unique_lock<std::mutex> lock(m_mutex);
71 THREAD_REQUIRE_MESSAGE(m_connections.empty() || m_connections.back().client != -1,
72 "Unexpected server entry waiting for client match " <<
73 m_connections.back().server);
75 m_connections.push_back({-1 , e.connectionID.sock});
77 LogDebug("AcceptEvent. Added: ? <=>" << e.connectionID.sock);
84 void Event(const WriteEvent &) override {}
85 void Event(const ReadEvent &) override {}
86 void Event(const CloseEvent &e) override {
87 std::unique_lock<std::mutex> lock(m_mutex);
88 THREAD_REQUIRE(!m_connections.empty());
90 auto serverMatch = [&](const SocketPair& pair){
91 return pair.server == e.connectionID.sock;
93 auto it = std::find_if(m_connections.begin(), m_connections.end(), serverMatch);
95 THREAD_REQUIRE_MESSAGE(it != m_connections.end(),
96 "Can't find connection for server socket = " << e.connectionID.sock);
98 LogDebug("CloseEvent. Removing: " << it->client << "<=>" << it->server);
99 THREAD_REQUIRE(it->client != -1);
101 m_connections.erase(it);
108 void Event(const SecurityEvent &) override {}
110 void Start() override {}
111 void Stop() override {}
113 void ConnectAndWait(SockRAII& client) {
114 std::unique_lock<std::mutex> lock(m_mutex);
116 THREAD_REQUIRE_MESSAGE(m_connections.empty() || m_connections.back().client != -1,
117 "Unexpected server entry waiting for client match " <<
118 m_connections.back().server);
120 int ret = client.connect(GetServiceDescription()[0].serviceHandlerPath.c_str());
121 BOOST_REQUIRE(ret == CKM_API_SUCCESS);
123 LogDebug("Connected. Waiting for AcceptEvent for: " << client.get() << "<=> ?");
125 BOOST_REQUIRE(m_cv.wait_for(lock, CV_TIMEOUT, [&]{ return AcceptEventArrived(); }));
127 m_connections.back().client = client.get();
129 LogDebug("Accepted. Matched client & server: " << m_connections.back().client << "<=>" <<
130 m_connections.back().server);
133 void DisconnectAndWait(SockRAII& client) {
134 int sock = client.get();
137 LogDebug("Disconnected. Waiting for CloseEvent for: " << sock << "<=> ?");
139 std::unique_lock<std::mutex> lock(m_mutex);
140 BOOST_REQUIRE(m_cv.wait_for(lock, CV_TIMEOUT, [&]{ return ClientAbsent(sock); }));
143 void WaitForRemainingClosures() {
144 std::unique_lock<std::mutex> lock(m_mutex);
145 if (!m_connections.empty())
146 BOOST_TEST_MESSAGE("Waiting for remaining " << m_connections.size() << " to close.");
148 BOOST_REQUIRE(m_cv.wait_for(lock, std::chrono::seconds(OVERRIDE_SOCKET_TIMEOUT + 2), [&]{
149 return m_connections.empty();
156 bool ClientAbsent(int client) const {
157 auto it = std::find_if(m_connections.begin(),
158 m_connections.end(), [&](const SocketPair& pair){
159 return pair.client == client;
161 return it == m_connections.end();
164 bool AcceptEventArrived() const {
165 return !m_connections.empty() && m_connections.back().client == -1;
168 void CompareSizes() const {
169 auto manager = static_cast<TestSocketManager*>(m_serviceManager);
170 THREAD_REQUIRE(m_connections.size() == manager->TimeoutQueueSize());
178 std::vector<SocketPair> m_connections;
179 std::condition_variable m_cv;
182 class SocketManagerLoop {
184 explicit SocketManagerLoop(SocketManager& manager) :
189 } catch (const std::exception& e) {
199 ~SocketManagerLoop() {
200 m_manager.MainLoopStop();
202 BOOST_CHECK_MESSAGE(!m_exception, m_what);
206 bool m_exception = false;
207 std::string m_what = "Unknown exception";
208 SocketManager& m_manager;
209 std::thread m_thread;
214 BOOST_AUTO_TEST_SUITE(SOCKET_MANAGER_TEST)
216 POSITIVE_TEST_CASE(StressTestGrowingTimeoutQueue)
218 constexpr unsigned INITIAL_CONNECTIONS = 20;
219 constexpr unsigned REPEATS = 100000;
220 constexpr auto INTERVAL = REPEATS/10;
222 int ret = unlink(SERVICE_SOCKET_TEST);
224 BOOST_REQUIRE(ret == 0 || (ret == -1 && err == ENOENT));
226 TestSocketManager manager;
227 auto service = new TestService();
228 manager.RegisterSocketService(service);
230 SocketManagerLoop loop(manager);
233 SockRAII socket[INITIAL_CONNECTIONS];
234 for (unsigned i=0;i<INITIAL_CONNECTIONS;i++)
235 service->ConnectAndWait(socket[i]);
237 BOOST_REQUIRE(manager.TimeoutQueueSize() == INITIAL_CONNECTIONS);
240 for(unsigned i=0;i<REPEATS;i++) {
241 service->ConnectAndWait(socket2);
242 service->DisconnectAndWait(socket2);
244 if ((i + 1) % INTERVAL == 0)
245 BOOST_TEST_MESSAGE("Creating connections: " << i + 1 << "/" << REPEATS);
248 // wait for remaining connections to close if any
249 service->WaitForRemainingClosures();
253 BOOST_AUTO_TEST_SUITE_END()