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
25 #include <condition_variable>
32 #include <boost/test/test_tools.hpp>
33 #include <boost_macros_wrapper.h>
35 #include <test_common.h>
36 #include <dpl/log/log.h>
38 #include <client-common.h>
39 #include <socket-manager.h>
40 #include <message-buffer.h>
46 size_t Random(size_t max)
48 static unsigned int seed = ::time(nullptr);
49 return ::rand_r(&seed) % max;
52 constexpr char SERVICE_SOCKET_TEST[] = "/tmp/.central-key-manager-test.sock";
53 constexpr CKM::InterfaceID SOCKET_ID_TEST = 42;
54 constexpr std::chrono::seconds CV_TIMEOUT(10);
56 struct TestSocketManager final : public SocketManager {
57 size_t TimeoutQueueSize() const { return m_timeoutQueue.size(); }
60 #define THREAD_REQUIRE_MESSAGE(test, message) \
63 std::ostringstream os; \
64 os << __FILE__ << ":" << __LINE__ << " " << #test << " " << message; \
65 throw std::runtime_error(os.str()); \
69 #define THREAD_REQUIRE(test) THREAD_REQUIRE_MESSAGE(test, "")
71 class NoOpService : public GenericSocketService {
72 void Event(const AcceptEvent &) override {}
73 void Event(const WriteEvent &) override {}
74 void Event(const ReadEvent &) override {}
75 void Event(const CloseEvent &) override {}
76 void Event(const SecurityEvent &) override {}
78 void Start() override {}
79 void Stop() override {}
82 class TestConnection final : public ServiceConnection {
84 explicit TestConnection(const std::string& socketPath) :
85 ServiceConnection(socketPath.c_str()),
87 auto ret = prepareConnection();
88 BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS, "ret = " << ret);
91 int Send(const CKM::RawBuffer &send_buf) {
92 m_sent += send_buf.size();
93 return ServiceConnection::send(SerializeMessage(send_buf));
97 void Receive(const T& logReceived) {
100 auto ret = m_socket.waitForSocket(POLLIN, 100);
101 BOOST_REQUIRE_MESSAGE(ret == 0, "ret = " << ret);
106 int ret = ServiceConnection::receive(m_recv);
107 BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS, "ret = " << ret);
108 BOOST_REQUIRE(m_recv.Ready());
109 while (m_recv.Ready()) {
111 m_recv.Deserialize(tmp);
112 const auto size = tmp.size();
113 BOOST_REQUIRE_MESSAGE(size <= m_sent, size << ">" << m_sent);
119 size_t GetId() const { return m_id; }
124 MessageBuffer m_recv;
125 static inline size_t m_counter = 0;
128 class SocketManagerLoop final {
130 explicit SocketManagerLoop(SocketManager& manager) :
135 } catch (const std::exception& e) {
145 ~SocketManagerLoop() {
146 m_manager.MainLoopStop();
149 BOOST_CHECK_MESSAGE(!m_exception, m_what);
153 bool m_exception = false;
154 std::string m_what = "Unknown exception";
155 SocketManager& m_manager;
156 std::thread m_thread;
159 std::string Id2SockPath(int id) {
160 return std::string(SERVICE_SOCKET_TEST) + std::to_string(id);
163 void unlinkIfExists(const char* path) {
164 int ret = unlink(path);
166 BOOST_REQUIRE(ret == 0 || (ret == -1 && err == ENOENT));
171 BOOST_AUTO_TEST_SUITE(SOCKET_MANAGER_TEST)
173 POSITIVE_TEST_CASE(StressTestGrowingTimeoutQueue)
175 constexpr unsigned INITIAL_CONNECTIONS = 20;
176 constexpr unsigned REPEATS = 100000;
177 constexpr auto INTERVAL = REPEATS/10;
179 class TestService final : public NoOpService {
181 ServiceDescriptionVector GetServiceDescription() override {
182 return ServiceDescriptionVector {
183 {SERVICE_SOCKET_TEST, "", SOCKET_ID_TEST}
186 void Event(const AcceptEvent &e) override {
187 std::unique_lock<std::mutex> lock(m_mutex);
189 THREAD_REQUIRE_MESSAGE(m_connections.empty() || m_connections.back().client != -1,
190 "Unexpected server entry waiting for client match " <<
191 m_connections.back().server);
193 m_connections.push_back({-1 , e.connectionID.sock});
195 LogDebug("AcceptEvent. Added: ? <=>" << e.connectionID.sock);
202 void Event(const CloseEvent &e) override {
203 std::unique_lock<std::mutex> lock(m_mutex);
204 THREAD_REQUIRE(!m_connections.empty());
206 auto serverMatch = [&](const SocketPair& pair){
207 return pair.server == e.connectionID.sock;
209 auto it = std::find_if(m_connections.begin(), m_connections.end(), serverMatch);
211 THREAD_REQUIRE_MESSAGE(it != m_connections.end(),
212 "Can't find connection for server socket = " <<
213 e.connectionID.sock);
215 LogDebug("CloseEvent. Removing: " << it->client << "<=>" << it->server);
216 THREAD_REQUIRE(it->client != -1);
218 m_connections.erase(it);
226 void ConnectAndWait(SockRAII& client) {
227 std::unique_lock<std::mutex> lock(m_mutex);
229 THREAD_REQUIRE_MESSAGE(m_connections.empty() || m_connections.back().client != -1,
230 "Unexpected server entry waiting for client match " <<
231 m_connections.back().server);
233 int ret = client.connect(GetServiceDescription()[0].serviceHandlerPath.c_str());
234 BOOST_REQUIRE(ret == CKM_API_SUCCESS);
236 LogDebug("Connected. Waiting for AcceptEvent for: " << client.get() << "<=> ?");
238 BOOST_REQUIRE(m_cv.wait_for(lock, CV_TIMEOUT, [&]{ return AcceptEventArrived(); }));
240 m_connections.back().client = client.get();
242 LogDebug("Accepted. Matched client & server: " << m_connections.back().client <<
243 "<=>" << m_connections.back().server);
246 void DisconnectAndWait(SockRAII& client) {
247 int sock = client.get();
250 LogDebug("Disconnected. Waiting for CloseEvent for: " << sock << "<=> ?");
252 std::unique_lock<std::mutex> lock(m_mutex);
253 BOOST_REQUIRE(m_cv.wait_for(lock, CV_TIMEOUT, [&]{ return ClientAbsent(sock); }));
256 void WaitForRemainingClosures() {
257 std::unique_lock<std::mutex> lock(m_mutex);
258 if (!m_connections.empty())
259 BOOST_TEST_MESSAGE("Waiting for remaining " << m_connections.size() <<
262 BOOST_REQUIRE(m_cv.wait_for(lock,
263 std::chrono::seconds(OVERRIDE_SOCKET_TIMEOUT + 2),
264 [&]{ return m_connections.empty(); }));
270 bool ClientAbsent(int client) const {
271 auto it = std::find_if(m_connections.begin(),
272 m_connections.end(), [&](const SocketPair& pair){
273 return pair.client == client;
275 return it == m_connections.end();
278 bool AcceptEventArrived() const {
279 return !m_connections.empty() && m_connections.back().client == -1;
282 void CompareSizes() const {
283 auto manager = static_cast<TestSocketManager*>(m_serviceManager);
284 THREAD_REQUIRE(m_connections.size() == manager->TimeoutQueueSize());
292 std::vector<SocketPair> m_connections;
293 std::condition_variable m_cv;
296 unlinkIfExists(SERVICE_SOCKET_TEST);
298 TestSocketManager manager;
299 auto service = new TestService();
300 manager.RegisterSocketService(service);
302 SocketManagerLoop loop(manager);
305 SockRAII socket[INITIAL_CONNECTIONS];
306 for (unsigned i=0;i<INITIAL_CONNECTIONS;i++)
307 service->ConnectAndWait(socket[i]);
309 BOOST_REQUIRE(manager.TimeoutQueueSize() == INITIAL_CONNECTIONS);
312 for(unsigned i=0;i<REPEATS;i++) {
313 service->ConnectAndWait(socket2);
314 service->DisconnectAndWait(socket2);
316 if ((i + 1) % INTERVAL == 0)
317 BOOST_TEST_MESSAGE("Creating connections: " << i + 1 << "/" << REPEATS);
320 // wait for remaining connections to close if any
321 service->WaitForRemainingClosures();
325 POSITIVE_TEST_CASE(StressTestRandomSocketEvents)
327 // Too many services or connections may trigger server side timeouts (OVERRIDE_SOCKET_TIMEOUT)
328 constexpr int SERVICES = 4;
329 constexpr int INTERVAL = 1000;
330 constexpr int REPEATS = 10000;
331 constexpr int MAX_CONNECTIONS = 4;
332 // client and server read 2048B and 4096B at once respectively
333 constexpr size_t MAX_BUF_SIZE = 5000;
344 class TestService final : public NoOpService {
346 explicit TestService(int id) :
347 m_desc({{Id2SockPath(id).c_str(), "", SOCKET_ID_TEST + id}}),
350 unlinkIfExists(GetSocketPath().c_str());
353 ServiceDescriptionVector GetServiceDescription() override { return m_desc; }
355 void Event(const ReadEvent &e) override {
356 LogDebug(e.connectionID.sock << ":" << e.connectionID.counter << " Received " <<
357 e.rawBuffer.size() << "B");
358 m_serviceManager->Write(e.connectionID, e.rawBuffer);
361 size_t GetConnectionCount() const { return m_connections.size(); }
363 void AddConnection() {
364 m_connections.emplace_back(new TestConnection(GetSocketPath()));
365 LogDebug(Prefix(m_connections.back()->GetId()) << "Connected");
368 void Disconnect(size_t idx) {
369 auto it = m_connections.begin() + idx;
370 auto cid = (*it)->GetId();
371 if (idx != m_connections.size() - 1)
372 *it = std::move(m_connections.back());
373 m_connections.pop_back();
374 LogDebug(Prefix(cid) << "Disconnected");
377 void Send(size_t idx) {
378 auto buffer = createRandom(Random(MAX_BUF_SIZE) + 1);
379 auto& conn = m_connections.at(idx);
380 auto ret = conn->Send(buffer);
381 BOOST_REQUIRE_MESSAGE(ret == CKM_API_SUCCESS, "ret = " << ret);
382 LogDebug(Prefix(conn->GetId())<< "Sent " << buffer.size() << "B");
385 void Receive(size_t idx) {
386 auto& conn = m_connections.at(idx);
387 conn->Receive([&](const size_t received) {
388 LogDebug(Prefix(conn->GetId()) << "Received " << received << "B");
393 const std::string& GetSocketPath() const {
394 return m_desc.at(0).serviceHandlerPath;
397 std::string Prefix(size_t idx) const {
398 return std::string(" ") + std::to_string(m_id) + ":" + std::to_string(idx) + " ";
401 ServiceDescriptionVector m_desc;
403 std::vector<std::unique_ptr<TestConnection>> m_connections;
406 SocketManager manager;
407 TestService* services[SERVICES];
409 for (int i = 0;i<SERVICES;i++) {
410 services[i] = new TestService(i);
411 manager.RegisterSocketService(services[i]);
414 SocketManagerLoop loop(manager);
416 for (unsigned i = 0;i < REPEATS; i++) {
418 auto service = services[Random(SERVICES)];
420 // always connect if there are no active connections
423 size_t cCnt = service->GetConnectionCount();
426 eIdx = static_cast<Event>(Random(CNT));
427 if (eIdx == CONNECT && cCnt == MAX_CONNECTIONS)
428 eIdx = SEND; // don't connect if there are too many
433 service->AddConnection();
437 service->Disconnect(cIdx);
445 service->Receive(cIdx);
449 BOOST_FAIL("Unexpected event");
452 if ((i + 1) % INTERVAL == 0)
453 BOOST_TEST_MESSAGE("Executing random socket actions: " << i + 1 << "/" << REPEATS);
457 BOOST_AUTO_TEST_SUITE_END()