5d19992070883ebdedb3ec3c2a753cb01921b326
[src/drm-lease-manager.git] / drm-lease-manager / test / lease-manager-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 #include <fff.h>
18
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <xf86drmMode.h>
22
23 #include "lease-manager.h"
24 #include "log.h"
25 #include "test-drm-device.h"
26 #include "test-helpers.h"
27
28 /* CHECK_LEASE_OBJECTS
29  *
30  * Checks the list of objects associated with a given lease_index.
31  * Asks the lease manager to create the lease, and checks that
32  * the requested objects are the ones given in the supplied list. */
33
34 #define CHECK_LEASE_OBJECTS(lease, ...)                                     \
35         do {                                                                \
36                 lm_lease_grant(g_lm, lease);                                \
37                 uint32_t objs[] = {__VA_ARGS__};                            \
38                 int nobjs = ARRAY_LEN(objs);                                \
39                 ck_assert_int_eq(drmModeCreateLease_fake.arg2_val, nobjs);  \
40                 check_uint_array_eq(drmModeCreateLease_fake.arg1_val, objs, \
41                                     nobjs);                                 \
42         } while (0)
43
44 /**************  Mock functions  *************/
45 DEFINE_FFF_GLOBALS;
46
47 FAKE_VALUE_FUNC(drmModeResPtr, drmModeGetResources, int);
48 FAKE_VOID_FUNC(drmModeFreeResources, drmModeResPtr);
49 FAKE_VALUE_FUNC(drmModePlaneResPtr, drmModeGetPlaneResources, int);
50 FAKE_VOID_FUNC(drmModeFreePlaneResources, drmModePlaneResPtr);
51
52 FAKE_VALUE_FUNC(drmModePlanePtr, drmModeGetPlane, int, uint32_t);
53 FAKE_VOID_FUNC(drmModeFreePlane, drmModePlanePtr);
54 FAKE_VALUE_FUNC(drmModeConnectorPtr, drmModeGetConnector, int, uint32_t);
55 FAKE_VOID_FUNC(drmModeFreeConnector, drmModeConnectorPtr);
56 FAKE_VALUE_FUNC(drmModeEncoderPtr, drmModeGetEncoder, int, uint32_t);
57 FAKE_VOID_FUNC(drmModeFreeEncoder, drmModeEncoderPtr);
58
59 FAKE_VALUE_FUNC(int, drmModeCreateLease, int, const uint32_t *, int, int,
60                 uint32_t *);
61 FAKE_VALUE_FUNC(int, drmModeRevokeLease, int, uint32_t);
62
63 /************** Test fixutre functions *************************/
64 struct lm *g_lm = NULL;
65
66 static void test_setup(void)
67 {
68         RESET_FAKE(drmModeGetResources);
69         RESET_FAKE(drmModeFreeResources);
70         RESET_FAKE(drmModeGetPlaneResources);
71         RESET_FAKE(drmModeFreePlaneResources);
72
73         RESET_FAKE(drmModeGetPlane);
74         RESET_FAKE(drmModeFreePlane);
75         RESET_FAKE(drmModeGetConnector);
76         RESET_FAKE(drmModeFreeConnector);
77         RESET_FAKE(drmModeGetEncoder);
78         RESET_FAKE(drmModeFreeEncoder);
79
80         RESET_FAKE(drmModeCreateLease);
81         RESET_FAKE(drmModeRevokeLease);
82
83         drmModeGetResources_fake.return_val = TEST_DEVICE_RESOURCES;
84         drmModeGetPlaneResources_fake.return_val = TEST_DEVICE_PLANE_RESOURCES;
85
86         drmModeGetPlane_fake.custom_fake = get_plane;
87         drmModeGetConnector_fake.custom_fake = get_connector;
88         drmModeGetEncoder_fake.custom_fake = get_encoder;
89         drmModeCreateLease_fake.custom_fake = create_lease;
90
91         ck_assert_msg(g_lm == NULL,
92                       "Lease manager context not clear at start of test");
93 }
94
95 static void test_shutdown(void)
96 {
97         reset_drm_test_device();
98         lm_destroy(g_lm);
99         g_lm = NULL;
100 }
101
102 static struct lease_handle **create_leases(int num_leases,
103                                            struct lease_config *configs)
104 {
105         if (configs)
106                 g_lm =
107                     lm_create_with_config(TEST_DRM_DEVICE, num_leases, configs);
108         else
109                 g_lm = lm_create(TEST_DRM_DEVICE);
110
111         ck_assert_ptr_ne(g_lm, NULL);
112
113         struct lease_handle **handles;
114         ck_assert_int_eq(num_leases, lm_get_lease_handles(g_lm, &handles));
115         ck_assert_ptr_ne(handles, NULL);
116
117         return handles;
118 }
119
120 /************** Resource enumeration tests *************/
121
122 /* These tests verify that the lease manager correctly assigns
123  * DRM resources to thier respective leases. In some cases
124  * the lease manager must choose which resources to include in
125  * each lease, so these tests verify that a valid (but not
126  * necessarily optimal) choice is made.
127  */
128
129 /* all_outputs_connected
130  *
131  * Test details: Create leases when all crtc/encoder/connector paths are
132  *               connected.
133  *
134  * Expected results: Leases are created for the currently connected sets of
135  *                   resources.
136  */
137 START_TEST(all_outputs_connected)
138 {
139         int out_cnt = 2, plane_cnt = 0;
140
141         setup_layout_simple_test_device(out_cnt, plane_cnt);
142
143         struct lease_handle **handles = create_leases(out_cnt, NULL);
144
145         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0));
146         CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(1), CONNECTOR_ID(1));
147 }
148 END_TEST
149
150 /* no_outputs_connected
151  *
152  * Test details: Create leases when no crtc/encoder/connector paths are
153  *               connected.
154  *
155  * Expected results: Available resources are divided between the leases.
156  *                   The same resource should not appear in multiple leases.
157  */
158 START_TEST(no_outputs_connected)
159 {
160         int out_cnt = 2, plane_cnt = 0;
161
162         ck_assert_int_eq(
163             setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true);
164
165         drmModeConnector connectors[] = {
166             CONNECTOR(CONNECTOR_ID(0), 0, &ENCODER_ID(0), 1),
167             CONNECTOR(CONNECTOR_ID(1), 0, &ENCODER_ID(1), 1),
168         };
169
170         drmModeEncoder encoders[] = {
171             ENCODER(ENCODER_ID(0), 0, 0x2),
172             ENCODER(ENCODER_ID(1), 0, 0x3),
173         };
174
175         setup_test_device_layout(connectors, encoders, NULL);
176
177         g_lm = lm_create(TEST_DRM_DEVICE);
178         ck_assert_ptr_ne(g_lm, NULL);
179
180         struct lease_handle **handles = create_leases(out_cnt, NULL);
181
182         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(1), CONNECTOR_ID(0));
183         CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(0), CONNECTOR_ID(1));
184 }
185 END_TEST
186
187 /* some_outputs_connected  */
188 /* Test details: Create leases when one output is connected and one is not.
189  * Expected results: Currently connected resources should be added to
190  *                   the same lease.
191  *                   The non-connected resources should be added to a second
192  *                   lease.
193  */
194 START_TEST(some_outputs_connected)
195 {
196         int out_cnt = 2, plane_cnt = 0;
197
198         ck_assert_int_eq(
199             setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true);
200
201         drmModeConnector connectors[] = {
202             CONNECTOR(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1),
203             CONNECTOR(CONNECTOR_ID(1), 0, &ENCODER_ID(1), 1),
204         };
205
206         drmModeEncoder encoders[] = {
207             ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x3),
208             ENCODER(ENCODER_ID(1), 0, 0x3),
209         };
210
211         setup_test_device_layout(connectors, encoders, NULL);
212
213         struct lease_handle **handles = create_leases(out_cnt, NULL);
214
215         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0));
216         CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(1), CONNECTOR_ID(1));
217 }
218 END_TEST
219
220 /* fewer_crtcs_than_connectors  */
221 /* Test details: Create leases on a system with more connectors than CRTCs
222  * Expected results: Number of leases generated should correspond to number of
223  *                   CRTCs.
224  *                   Leases contain one valid connector for each CRTC.
225  */
226 START_TEST(fewer_crtcs_than_connectors)
227 {
228         int out_cnt = 3, plane_cnt = 0, crtc_cnt = 2;
229
230         ck_assert_int_eq(
231             setup_drm_test_device(crtc_cnt, out_cnt, out_cnt, plane_cnt), true);
232
233         drmModeConnector connectors[] = {
234             CONNECTOR(CONNECTOR_ID(0), 0, &ENCODER_ID(0), 1),
235             CONNECTOR(CONNECTOR_ID(1), 0, &ENCODER_ID(1), 1),
236             CONNECTOR(CONNECTOR_ID(2), 0, &ENCODER_ID(2), 1),
237         };
238
239         drmModeEncoder encoders[] = {
240             ENCODER(ENCODER_ID(0), 0, 0x3),
241             ENCODER(ENCODER_ID(1), 0, 0x1),
242             ENCODER(ENCODER_ID(2), 0, 0x3),
243         };
244
245         setup_test_device_layout(connectors, encoders, NULL);
246
247         struct lease_handle **handles = create_leases(crtc_cnt, NULL);
248
249         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0));
250         CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(1), CONNECTOR_ID(2));
251 }
252 END_TEST
253
254 /* separate_overlay_planes_by_crtc  */
255 /* Test details: Add overlay planes to leases. Each plane is tied to a
256  *               specific CRTC.
257  * Expected results: The leases contain all of the planes for connected to
258  *                   each CRTC and no others.
259  */
260 START_TEST(separate_overlay_planes_by_crtc)
261 {
262
263         int out_cnt = 2, plane_cnt = 3;
264
265         setup_layout_simple_test_device(out_cnt, plane_cnt);
266
267         struct lease_handle **handles = create_leases(out_cnt, NULL);
268
269         CHECK_LEASE_OBJECTS(handles[0], PLANE_ID(0), PLANE_ID(2), CRTC_ID(0),
270                             CONNECTOR_ID(0));
271         CHECK_LEASE_OBJECTS(handles[1], PLANE_ID(1), CRTC_ID(1),
272                             CONNECTOR_ID(1));
273 }
274 END_TEST
275
276 /* reject_planes_shared_between_multiple_crtcs */
277 /* Test details: Add overlay planes to leases. Some planes are shared between
278  *               multiple CRTCs.
279  * Expected results: The leases contain all of the unique planes for each CRTC.
280  *                   Planes that can be used on multiple CRTCs are not included
281  *                   in any lease.
282  */
283 START_TEST(reject_planes_shared_between_multiple_crtcs)
284 {
285
286         int out_cnt = 2, plane_cnt = 3;
287
288         ck_assert_int_eq(
289             setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true);
290
291         drmModeConnector connectors[] = {
292             CONNECTOR(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1),
293             CONNECTOR(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1),
294         };
295
296         drmModeEncoder encoders[] = {
297             ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x1),
298             ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x2),
299         };
300
301         drmModePlane planes[] = {
302             PLANE(PLANE_ID(0), 0x2),
303             PLANE(PLANE_ID(1), 0x1),
304             PLANE(PLANE_ID(2), 0x3),
305         };
306
307         setup_test_device_layout(connectors, encoders, planes);
308
309         struct lease_handle **handles = create_leases(out_cnt, NULL);
310
311         CHECK_LEASE_OBJECTS(handles[0], PLANE_ID(1), CRTC_ID(0),
312                             CONNECTOR_ID(0));
313         CHECK_LEASE_OBJECTS(handles[1], PLANE_ID(0), CRTC_ID(1),
314                             CONNECTOR_ID(1));
315 }
316 END_TEST
317
318 static void add_connector_enum_tests(Suite *s)
319 {
320         TCase *tc = tcase_create("Resource enumeration");
321
322         tcase_add_checked_fixture(tc, test_setup, test_shutdown);
323
324         tcase_add_test(tc, all_outputs_connected);
325         tcase_add_test(tc, no_outputs_connected);
326         tcase_add_test(tc, fewer_crtcs_than_connectors);
327         tcase_add_test(tc, some_outputs_connected);
328         tcase_add_test(tc, separate_overlay_planes_by_crtc);
329         tcase_add_test(tc, reject_planes_shared_between_multiple_crtcs);
330         suite_add_tcase(s, tc);
331 }
332
333 /************** Lease management tests *************/
334
335 /* create_and_revoke_lease */
336 /* Test details: Create leases and revoke them.
337  * Expected results: drmModeRevokeLease() is called with the correct leasee_id.
338  */
339 START_TEST(create_and_revoke_lease)
340 {
341         int lease_cnt = 2, plane_cnt = 0;
342
343         setup_layout_simple_test_device(lease_cnt, plane_cnt);
344
345         struct lease_handle **handles = create_leases(lease_cnt, NULL);
346
347         for (int i = 0; i < lease_cnt; i++) {
348                 ck_assert_int_ge(lm_lease_grant(g_lm, handles[i]), 0);
349                 lm_lease_revoke(g_lm, handles[i]);
350         }
351
352         ck_assert_int_eq(drmModeRevokeLease_fake.call_count, lease_cnt);
353
354         for (int i = 0; i < lease_cnt; i++) {
355                 ck_assert_int_eq(drmModeRevokeLease_fake.arg1_history[i],
356                                  LESSEE_ID(i));
357         }
358 }
359 END_TEST
360
361 static void add_lease_management_tests(Suite *s)
362 {
363         TCase *tc = tcase_create("Lease management");
364
365         tcase_add_checked_fixture(tc, test_setup, test_shutdown);
366
367         tcase_add_test(tc, create_and_revoke_lease);
368         suite_add_tcase(s, tc);
369 }
370
371 int main(void)
372 {
373         int number_failed;
374         Suite *s;
375         SRunner *sr;
376
377         s = suite_create("DLM lease manager tests");
378
379         add_connector_enum_tests(s);
380         add_lease_management_tests(s);
381
382         sr = srunner_create(s);
383         srunner_run_all(sr, CK_NORMAL);
384         number_failed = srunner_ntests_failed(sr);
385         srunner_free(sr);
386         return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
387 }