Add timeout queue stress test
[platform/core/security/key-manager.git] / unit-tests / test_socket-manager.cpp
1 /*
2  *  Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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
15  */
16
17 #include <cerrno>
18
19 #include <thread>
20 #include <mutex>
21 #include <condition_variable>
22 #include <chrono>
23
24 #include <boost/test/test_tools.hpp>
25 #include <boost_macros_wrapper.h>
26
27 #include <client-common.h>
28 #include <socket-manager.h>
29
30 using namespace CKM;
31 using namespace std::chrono_literals;
32
33 namespace {
34
35 constexpr char SERVICE_SOCKET_TEST[] = "/tmp/.central-key-manager-test.sock";
36 constexpr CKM::InterfaceID SOCKET_ID_TEST = 42;
37
38 class TestService : public GenericSocketService {
39 public:
40         ServiceDescriptionVector GetServiceDescription() override {
41                 return ServiceDescriptionVector {
42                         {SERVICE_SOCKET_TEST, "", SOCKET_ID_TEST}
43                 };
44         }
45         void Event(const AcceptEvent &) override { Connected(true); }
46         void Event(const WriteEvent &) override {}
47         void Event(const ReadEvent &) override {}
48         void Event(const CloseEvent &) override { Connected(false); }
49         void Event(const SecurityEvent &) override {}
50
51         void Start() override {}
52         void Stop() override {}
53
54         void WaitUntilConnected(bool isConnected) {
55                 std::unique_lock<std::mutex> lock(m_mutex);
56                 BOOST_REQUIRE(m_cv.wait_for(lock, 10s, [&]{ return m_connected == isConnected; }));
57         }
58
59         void Connected(bool isConnected) {
60                 std::unique_lock<std::mutex> lock(m_mutex);
61                 m_connected = isConnected;
62                 lock.unlock();
63                 m_cv.notify_one();
64         }
65
66 private:
67         bool m_connected = false;
68         std::mutex m_mutex;
69         std::condition_variable m_cv;
70 };
71
72 struct TestSocketManager : public SocketManager {
73         size_t TimeoutQueueSize() const { return m_timeoutQueue.size(); }
74 };
75
76 } // namespace
77
78 BOOST_AUTO_TEST_SUITE(SOCKET_MANAGER_TEST)
79
80 POSITIVE_TEST_CASE(StressTestGrowingTimeoutQueue)
81 {
82         constexpr unsigned REPEATS = 100000;
83         constexpr auto INTERVAL = REPEATS/10;
84
85         int ret = unlink(SERVICE_SOCKET_TEST);
86         int err = errno;
87         BOOST_REQUIRE(ret == 0 || (ret == -1 && err == ENOENT));
88
89         TestSocketManager manager;
90         auto service = new TestService();
91         manager.RegisterSocketService(service);
92
93         bool exception = false;
94         std::string what("Unknown exception");
95
96         std::thread th([&]{
97                 try {
98                         manager.MainLoop();
99                 } catch (const std::exception& e) {
100                         exception = true;
101                         what = e.what();
102                 } catch (...) {
103                         exception = true;
104                 }
105         });
106
107         {
108                 SockRAII socket;
109                 ret = socket.connect(SERVICE_SOCKET_TEST);
110                 BOOST_REQUIRE(ret == CKM_API_SUCCESS);
111                 service->WaitUntilConnected(true);
112                 BOOST_REQUIRE(manager.TimeoutQueueSize() == 1);
113                 service->Connected(false);
114
115                 // check if the closed socket timeouts are removed from the queue.
116                 SockRAII socket2;
117                 for(unsigned i=0;i<REPEATS;i++) {
118                         ret = socket2.connect(SERVICE_SOCKET_TEST);
119                         BOOST_REQUIRE(ret == CKM_API_SUCCESS);
120                         service->WaitUntilConnected(true);
121
122                         socket2.disconnect();
123                         service->WaitUntilConnected(false);
124
125                         if ((i + 1) % INTERVAL == 0)
126                                 BOOST_TEST_MESSAGE("Creating connections: " << i + 1 << "/" << REPEATS);
127                 }
128                 BOOST_TEST_MESSAGE("Waiting " << OVERRIDE_SOCKET_TIMEOUT + 2 << "s for socket timeouts.");
129
130                 // all sockets should be closed after timeout and the queue should be empty
131                 std::this_thread::sleep_for(std::chrono::seconds(OVERRIDE_SOCKET_TIMEOUT + 2));
132                 BOOST_REQUIRE(manager.TimeoutQueueSize() == 0);
133         }
134
135         manager.MainLoopStop();
136         th.join();
137
138         BOOST_REQUIRE_MESSAGE(!exception, what);
139 }
140
141 BOOST_AUTO_TEST_SUITE_END()