Add initial version
[src/drm-lease-manager.git] / libdlmclient / test / libdlmclient-test.c
1 /* Copyright 2020-2021 IGEL Co., Ltd.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <check.h>
17
18 #include <dirent.h>
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23
24 #include "config.h"
25 #include "dlmclient.h"
26 #include "test-helpers.h"
27 #include "test-socket-server.h"
28
29 #define SOCKETDIR "/tmp"
30
31 #define TEST_LEASE_NAME "test-lease"
32
33 /**************  Test fixutre functions *************/
34 struct test_config default_test_config;
35
36 static void test_setup(void)
37 {
38         dlm_enable_debug_log(true);
39         setenv("DLM_RUNTIME_PATH", SOCKETDIR, 1);
40
41         default_test_config = (struct test_config){
42             .lease_name = TEST_LEASE_NAME,
43             .nfds = 1,
44         };
45 }
46
47 static void test_shutdown(void)
48 {
49         test_config_cleanup(&default_test_config);
50 }
51
52 /**************  Lease manager error tests *************/
53
54 /* These tests verify that the client library gracefully handles
55  * failures when trying to receive data from the lease manager.
56  * Failures or errors in the lease manager should cause meaningful
57  * errors to be reported by the client.  Lease manager errors should
58  * not cause crashes or invalid state in the client */
59
60 /* manager_connection_err
61  *
62  * Test details: Simulate socket connection failure.
63  * Expected results: dlm_get_lease() fails.
64  */
65 START_TEST(manager_connection_err)
66 {
67         struct server_state *sstate = test_server_start(&default_test_config);
68
69         struct dlm_lease *lease = dlm_get_lease(TEST_LEASE_NAME "-bad");
70
71         ck_assert_ptr_eq(lease, NULL);
72
73         test_server_stop(sstate);
74 }
75 END_TEST
76
77 /* no_data_from_manager
78  *
79  * Test details: Close the remote (lease manager) without sending any data.
80  *               Currently this means that the lease request has been rejected
81  *               for some reason.
82  *
83  * TODO: Update this when the client-server protocol is updated to
84  *       include the reason for the lease rejection.
85  *
86  * Expected results: dlm_get_lease() fails, errno set to EACCESS.
87  */
88 START_TEST(no_data_from_manager)
89 {
90
91         struct test_config config = {
92             .lease_name = TEST_LEASE_NAME,
93             .send_no_data = true,
94         };
95
96         struct server_state *sstate = test_server_start(&config);
97
98         struct dlm_lease *lease = dlm_get_lease(TEST_LEASE_NAME);
99
100         ck_assert_ptr_eq(lease, NULL);
101         ck_assert_int_eq(errno, EACCES);
102
103         test_server_stop(sstate);
104 }
105 END_TEST
106
107 /* no_lease_fd_from_manager
108  *
109  * Test details: Simulate receiving response from lease manager with
110  *               no fd attached.  (i.e. a protocol error)
111  *
112  * Expected results: dlm_get_lease() fails, errno set to EPROTO.
113  */
114 START_TEST(no_lease_fd_from_manager)
115 {
116         /* Receive message from the lease manager with missing lease fd */
117         struct test_config config = {
118             .lease_name = TEST_LEASE_NAME,
119             .send_data_without_fd = true,
120         };
121
122         struct server_state *sstate = test_server_start(&config);
123
124         struct dlm_lease *lease = dlm_get_lease(TEST_LEASE_NAME);
125
126         ck_assert_ptr_eq(lease, NULL);
127         ck_assert_int_eq(errno, EPROTO);
128
129         test_server_stop(sstate);
130 }
131 END_TEST
132
133 static void add_lease_manager_error_tests(Suite *s)
134 {
135         TCase *tc = tcase_create("Lease manager error handling");
136
137         tcase_add_checked_fixture(tc, test_setup, test_shutdown);
138
139         tcase_add_test(tc, manager_connection_err);
140         tcase_add_test(tc, no_data_from_manager);
141         tcase_add_test(tc, no_lease_fd_from_manager);
142
143         suite_add_tcase(s, tc);
144 }
145
146 /**************  Lease handling tests  *****************/
147
148 /* These tests verify that the client library handles the received
149  * lease data properly. Receiving the lease fds without leaks,
150  * properly passing the fds to the client application and cleaning
151  * them up on release.
152  */
153
154 static int count_open_fds(void)
155 {
156         int fds = 0;
157         DIR *dirp = opendir("/proc/self/fd");
158         while ((readdir(dirp) != NULL))
159                 fds++;
160         closedir(dirp);
161         return fds;
162 }
163
164 /* receive_fd_from_manager
165  *
166  * Test details: Successfully receive a file descriptor.
167  * Expected results: dlm_get_lease() succeeds.
168  *                   dlm_lease_fd() returns the correct fd value.
169  */
170 START_TEST(receive_fd_from_manager)
171 {
172         struct server_state *sstate = test_server_start(&default_test_config);
173
174         struct dlm_lease *lease = dlm_get_lease(TEST_LEASE_NAME);
175         ck_assert_ptr_ne(lease, NULL);
176
177         int received_fd = dlm_lease_fd(lease);
178
179         int sent_fd = default_test_config.fds[0];
180
181         check_fd_equality(received_fd, sent_fd);
182
183         dlm_release_lease(lease);
184
185         test_server_stop(sstate);
186         close(sent_fd);
187 }
188 END_TEST
189
190 /* lease_fd_is_closed_on_release
191  *
192  * Test details: Verify that dlm_release_lease() closes the lease fd.
193  * Expected results: lease fd is closed.
194  */
195 START_TEST(lease_fd_is_closed_on_release)
196 {
197         struct server_state *sstate = test_server_start(&default_test_config);
198
199         struct dlm_lease *lease = dlm_get_lease(TEST_LEASE_NAME);
200         ck_assert_ptr_ne(lease, NULL);
201
202         int received_fd = dlm_lease_fd(lease);
203
204         check_fd_is_open(received_fd);
205         dlm_release_lease(lease);
206         check_fd_is_closed(received_fd);
207
208         test_server_stop(sstate);
209 }
210 END_TEST
211
212 /* dlm_lease_fd_always_returns_same_lease
213  *
214  * Test details: Verify that dlm_lease_fd() always returns the same value
215  *               for a given lease.
216  * Expected results: same value is returned when called multiple times.
217  */
218 START_TEST(dlm_lease_fd_always_returns_same_lease)
219 {
220         struct server_state *sstate = test_server_start(&default_test_config);
221
222         struct dlm_lease *lease = dlm_get_lease(TEST_LEASE_NAME);
223         ck_assert_ptr_ne(lease, NULL);
224
225         int received_fd = dlm_lease_fd(lease);
226
227         ck_assert_int_eq(received_fd, dlm_lease_fd(lease));
228         ck_assert_int_eq(received_fd, dlm_lease_fd(lease));
229
230         dlm_release_lease(lease);
231
232         test_server_stop(sstate);
233 }
234 END_TEST
235
236 START_TEST(verify_that_unused_fds_are_not_leaked)
237 {
238         int nopen_fds = count_open_fds();
239
240         struct test_config config = {
241             .lease_name = TEST_LEASE_NAME,
242             .nfds = 2,
243         };
244
245         struct server_state *sstate = test_server_start(&config);
246
247         struct dlm_lease *lease = dlm_get_lease(TEST_LEASE_NAME);
248
249         ck_assert_ptr_eq(lease, NULL);
250         ck_assert_int_eq(errno, EPROTO);
251
252         dlm_release_lease(lease);
253
254         test_server_stop(sstate);
255
256         test_config_cleanup(&config);
257         ck_assert_int_eq(nopen_fds, count_open_fds());
258 }
259 END_TEST
260
261 static void add_lease_handling_tests(Suite *s)
262 {
263         TCase *tc = tcase_create("Lease processing tests");
264
265         tcase_add_checked_fixture(tc, test_setup, test_shutdown);
266
267         tcase_add_test(tc, receive_fd_from_manager);
268         tcase_add_test(tc, lease_fd_is_closed_on_release);
269         tcase_add_test(tc, dlm_lease_fd_always_returns_same_lease);
270         tcase_add_test(tc, verify_that_unused_fds_are_not_leaked);
271         suite_add_tcase(s, tc);
272 }
273
274 int main(void)
275 {
276         int number_failed;
277         Suite *s;
278         SRunner *sr;
279
280         s = suite_create("DLM client library tests");
281
282         add_lease_manager_error_tests(s);
283         add_lease_handling_tests(s);
284
285         sr = srunner_create(s);
286
287         srunner_run_all(sr, CK_NORMAL);
288         number_failed = srunner_ntests_failed(sr);
289         srunner_free(sr);
290         return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
291 }