test/lease-manager: Add named lease tests
[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 #define INVALID_OBJECT_ID (0)
29
30 /* CHECK_LEASE_OBJECTS
31  *
32  * Checks the list of objects associated with a given lease_index.
33  * Asks the lease manager to create the lease, and checks that
34  * the requested objects are the ones given in the supplied list. */
35
36 #define CHECK_LEASE_OBJECTS(lease, ...)                                     \
37         do {                                                                \
38                 lm_lease_grant(g_lm, lease);                                \
39                 uint32_t objs[] = {__VA_ARGS__};                            \
40                 int nobjs = ARRAY_LEN(objs);                                \
41                 ck_assert_int_eq(drmModeCreateLease_fake.arg2_val, nobjs);  \
42                 check_uint_array_eq(drmModeCreateLease_fake.arg1_val, objs, \
43                                     nobjs);                                 \
44         } while (0)
45
46 /**************  Mock functions  *************/
47 DEFINE_FFF_GLOBALS;
48
49 FAKE_VALUE_FUNC(drmModeResPtr, drmModeGetResources, int);
50 FAKE_VOID_FUNC(drmModeFreeResources, drmModeResPtr);
51 FAKE_VALUE_FUNC(drmModePlaneResPtr, drmModeGetPlaneResources, int);
52 FAKE_VOID_FUNC(drmModeFreePlaneResources, drmModePlaneResPtr);
53
54 FAKE_VALUE_FUNC(drmModePlanePtr, drmModeGetPlane, int, uint32_t);
55 FAKE_VOID_FUNC(drmModeFreePlane, drmModePlanePtr);
56 FAKE_VALUE_FUNC(drmModeConnectorPtr, drmModeGetConnector, int, uint32_t);
57 FAKE_VOID_FUNC(drmModeFreeConnector, drmModeConnectorPtr);
58 FAKE_VALUE_FUNC(drmModeEncoderPtr, drmModeGetEncoder, int, uint32_t);
59 FAKE_VOID_FUNC(drmModeFreeEncoder, drmModeEncoderPtr);
60
61 FAKE_VALUE_FUNC(int, drmModeCreateLease, int, const uint32_t *, int, int,
62                 uint32_t *);
63 FAKE_VALUE_FUNC(int, drmModeRevokeLease, int, uint32_t);
64
65 /************** Test fixutre functions *************************/
66 struct lm *g_lm = NULL;
67
68 static void test_setup(void)
69 {
70         RESET_FAKE(drmModeGetResources);
71         RESET_FAKE(drmModeFreeResources);
72         RESET_FAKE(drmModeGetPlaneResources);
73         RESET_FAKE(drmModeFreePlaneResources);
74
75         RESET_FAKE(drmModeGetPlane);
76         RESET_FAKE(drmModeFreePlane);
77         RESET_FAKE(drmModeGetConnector);
78         RESET_FAKE(drmModeFreeConnector);
79         RESET_FAKE(drmModeGetEncoder);
80         RESET_FAKE(drmModeFreeEncoder);
81
82         RESET_FAKE(drmModeCreateLease);
83         RESET_FAKE(drmModeRevokeLease);
84
85         drmModeGetResources_fake.return_val = TEST_DEVICE_RESOURCES;
86         drmModeGetPlaneResources_fake.return_val = TEST_DEVICE_PLANE_RESOURCES;
87
88         drmModeGetPlane_fake.custom_fake = get_plane;
89         drmModeGetConnector_fake.custom_fake = get_connector;
90         drmModeGetEncoder_fake.custom_fake = get_encoder;
91         drmModeCreateLease_fake.custom_fake = create_lease;
92
93         ck_assert_msg(g_lm == NULL,
94                       "Lease manager context not clear at start of test");
95 }
96
97 static void test_shutdown(void)
98 {
99         reset_drm_test_device();
100         lm_destroy(g_lm);
101         g_lm = NULL;
102 }
103
104 static struct lease_handle **create_leases(int num_leases,
105                                            struct lease_config *configs)
106 {
107         if (configs)
108                 g_lm =
109                     lm_create_with_config(TEST_DRM_DEVICE, num_leases, configs);
110         else
111                 g_lm = lm_create(TEST_DRM_DEVICE);
112
113         ck_assert_ptr_ne(g_lm, NULL);
114
115         struct lease_handle **handles;
116         ck_assert_int_eq(num_leases, lm_get_lease_handles(g_lm, &handles));
117         ck_assert_ptr_ne(handles, NULL);
118
119         return handles;
120 }
121
122 /************** Resource enumeration tests *************/
123
124 /* These tests verify that the lease manager correctly assigns
125  * DRM resources to thier respective leases. In some cases
126  * the lease manager must choose which resources to include in
127  * each lease, so these tests verify that a valid (but not
128  * necessarily optimal) choice is made.
129  */
130
131 /* all_outputs_connected
132  *
133  * Test details: Create leases when all crtc/encoder/connector paths are
134  *               connected.
135  *
136  * Expected results: Leases are created for the currently connected sets of
137  *                   resources.
138  */
139 START_TEST(all_outputs_connected)
140 {
141         int out_cnt = 2, plane_cnt = 0;
142
143         setup_layout_simple_test_device(out_cnt, plane_cnt);
144
145         struct lease_handle **handles = create_leases(out_cnt, NULL);
146
147         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0));
148         CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(1), CONNECTOR_ID(1));
149 }
150 END_TEST
151
152 /* no_outputs_connected
153  *
154  * Test details: Create leases when no crtc/encoder/connector paths are
155  *               connected.
156  *
157  * Expected results: Available resources are divided between the leases.
158  *                   The same resource should not appear in multiple leases.
159  */
160 START_TEST(no_outputs_connected)
161 {
162         int out_cnt = 2, plane_cnt = 0;
163
164         ck_assert_int_eq(
165             setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true);
166
167         drmModeConnector connectors[] = {
168             CONNECTOR(CONNECTOR_ID(0), 0, &ENCODER_ID(0), 1),
169             CONNECTOR(CONNECTOR_ID(1), 0, &ENCODER_ID(1), 1),
170         };
171
172         drmModeEncoder encoders[] = {
173             ENCODER(ENCODER_ID(0), 0, 0x2),
174             ENCODER(ENCODER_ID(1), 0, 0x3),
175         };
176
177         setup_test_device_layout(connectors, encoders, NULL);
178
179         g_lm = lm_create(TEST_DRM_DEVICE);
180         ck_assert_ptr_ne(g_lm, NULL);
181
182         struct lease_handle **handles = create_leases(out_cnt, NULL);
183
184         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(1), CONNECTOR_ID(0));
185         CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(0), CONNECTOR_ID(1));
186 }
187 END_TEST
188
189 /* some_outputs_connected  */
190 /* Test details: Create leases when one output is connected and one is not.
191  * Expected results: Currently connected resources should be added to
192  *                   the same lease.
193  *                   The non-connected resources should be added to a second
194  *                   lease.
195  */
196 START_TEST(some_outputs_connected)
197 {
198         int out_cnt = 2, plane_cnt = 0;
199
200         ck_assert_int_eq(
201             setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true);
202
203         drmModeConnector connectors[] = {
204             CONNECTOR(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1),
205             CONNECTOR(CONNECTOR_ID(1), 0, &ENCODER_ID(1), 1),
206         };
207
208         drmModeEncoder encoders[] = {
209             ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x3),
210             ENCODER(ENCODER_ID(1), 0, 0x3),
211         };
212
213         setup_test_device_layout(connectors, encoders, NULL);
214
215         struct lease_handle **handles = create_leases(out_cnt, NULL);
216
217         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0));
218         CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(1), CONNECTOR_ID(1));
219 }
220 END_TEST
221
222 /* fewer_crtcs_than_connectors  */
223 /* Test details: Create leases on a system with more connectors than CRTCs
224  * Expected results: Number of leases generated should correspond to number of
225  *                   CRTCs.
226  *                   Leases contain one valid connector for each CRTC.
227  */
228 START_TEST(fewer_crtcs_than_connectors)
229 {
230         int out_cnt = 3, plane_cnt = 0, crtc_cnt = 2;
231
232         ck_assert_int_eq(
233             setup_drm_test_device(crtc_cnt, out_cnt, out_cnt, plane_cnt), true);
234
235         drmModeConnector connectors[] = {
236             CONNECTOR(CONNECTOR_ID(0), 0, &ENCODER_ID(0), 1),
237             CONNECTOR(CONNECTOR_ID(1), 0, &ENCODER_ID(1), 1),
238             CONNECTOR(CONNECTOR_ID(2), 0, &ENCODER_ID(2), 1),
239         };
240
241         drmModeEncoder encoders[] = {
242             ENCODER(ENCODER_ID(0), 0, 0x3),
243             ENCODER(ENCODER_ID(1), 0, 0x1),
244             ENCODER(ENCODER_ID(2), 0, 0x3),
245         };
246
247         setup_test_device_layout(connectors, encoders, NULL);
248
249         struct lease_handle **handles = create_leases(crtc_cnt, NULL);
250
251         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0));
252         CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(1), CONNECTOR_ID(2));
253 }
254 END_TEST
255
256 /* separate_overlay_planes_by_crtc  */
257 /* Test details: Add overlay planes to leases. Each plane is tied to a
258  *               specific CRTC.
259  * Expected results: The leases contain all of the planes for connected to
260  *                   each CRTC and no others.
261  */
262 START_TEST(separate_overlay_planes_by_crtc)
263 {
264
265         int out_cnt = 2, plane_cnt = 3;
266
267         setup_layout_simple_test_device(out_cnt, plane_cnt);
268
269         struct lease_handle **handles = create_leases(out_cnt, NULL);
270
271         CHECK_LEASE_OBJECTS(handles[0], PLANE_ID(0), PLANE_ID(2), CRTC_ID(0),
272                             CONNECTOR_ID(0));
273         CHECK_LEASE_OBJECTS(handles[1], PLANE_ID(1), CRTC_ID(1),
274                             CONNECTOR_ID(1));
275 }
276 END_TEST
277
278 /* reject_planes_shared_between_multiple_crtcs */
279 /* Test details: Add overlay planes to leases. Some planes are shared between
280  *               multiple CRTCs.
281  * Expected results: The leases contain all of the unique planes for each CRTC.
282  *                   Planes that can be used on multiple CRTCs are not included
283  *                   in any lease.
284  */
285 START_TEST(reject_planes_shared_between_multiple_crtcs)
286 {
287
288         int out_cnt = 2, plane_cnt = 3;
289
290         ck_assert_int_eq(
291             setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true);
292
293         drmModeConnector connectors[] = {
294             CONNECTOR(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1),
295             CONNECTOR(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1),
296         };
297
298         drmModeEncoder encoders[] = {
299             ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x1),
300             ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x2),
301         };
302
303         drmModePlane planes[] = {
304             PLANE(PLANE_ID(0), 0x2),
305             PLANE(PLANE_ID(1), 0x1),
306             PLANE(PLANE_ID(2), 0x3),
307         };
308
309         setup_test_device_layout(connectors, encoders, planes);
310
311         struct lease_handle **handles = create_leases(out_cnt, NULL);
312
313         CHECK_LEASE_OBJECTS(handles[0], PLANE_ID(1), CRTC_ID(0),
314                             CONNECTOR_ID(0));
315         CHECK_LEASE_OBJECTS(handles[1], PLANE_ID(0), CRTC_ID(1),
316                             CONNECTOR_ID(1));
317 }
318 END_TEST
319
320 static void add_connector_enum_tests(Suite *s)
321 {
322         TCase *tc = tcase_create("Resource enumeration");
323
324         tcase_add_checked_fixture(tc, test_setup, test_shutdown);
325
326         tcase_add_test(tc, all_outputs_connected);
327         tcase_add_test(tc, no_outputs_connected);
328         tcase_add_test(tc, fewer_crtcs_than_connectors);
329         tcase_add_test(tc, some_outputs_connected);
330         tcase_add_test(tc, separate_overlay_planes_by_crtc);
331         tcase_add_test(tc, reject_planes_shared_between_multiple_crtcs);
332         suite_add_tcase(s, tc);
333 }
334
335 /************** Lease management tests *************/
336
337 /* create_and_revoke_lease */
338 /* Test details: Create leases and revoke them.
339  * Expected results: drmModeRevokeLease() is called with the correct leasee_id.
340  */
341 START_TEST(create_and_revoke_lease)
342 {
343         int lease_cnt = 2, plane_cnt = 0;
344
345         setup_layout_simple_test_device(lease_cnt, plane_cnt);
346
347         struct lease_handle **handles = create_leases(lease_cnt, NULL);
348
349         for (int i = 0; i < lease_cnt; i++) {
350                 ck_assert_int_ge(lm_lease_grant(g_lm, handles[i]), 0);
351                 lm_lease_revoke(g_lm, handles[i]);
352         }
353
354         ck_assert_int_eq(drmModeRevokeLease_fake.call_count, lease_cnt);
355
356         for (int i = 0; i < lease_cnt; i++) {
357                 ck_assert_int_eq(drmModeRevokeLease_fake.arg1_history[i],
358                                  LESSEE_ID(i));
359         }
360 }
361 END_TEST
362
363 /* Test lease names */
364 /* Test details: Create some leases and verify that they have the correct names
365  * Expected results: lease names should match the expected values
366  */
367 START_TEST(verify_lease_names)
368 {
369         int lease_cnt = 3;
370         bool res = setup_drm_test_device(lease_cnt, lease_cnt, lease_cnt, 0);
371         ck_assert_int_eq(res, true);
372
373         drmModeConnector connectors[] = {
374             CONNECTOR_FULL(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1,
375                            DRM_MODE_CONNECTOR_HDMIA, 1),
376             CONNECTOR_FULL(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1,
377                            DRM_MODE_CONNECTOR_LVDS, 3),
378             CONNECTOR_FULL(CONNECTOR_ID(2), ENCODER_ID(2), &ENCODER_ID(2), 1,
379                            DRM_MODE_CONNECTOR_eDP, 6),
380         };
381
382         drmModeEncoder encoders[] = {
383             ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x7),
384             ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x7),
385             ENCODER(ENCODER_ID(2), CRTC_ID(2), 0x7),
386         };
387
388         setup_test_device_layout(connectors, encoders, NULL);
389
390         const char *expected_names[] = {
391             "card3-HDMI-A-1",
392             "card3-LVDS-3",
393             "card3-eDP-6",
394         };
395
396         struct lease_handle **handles = create_leases(lease_cnt, NULL);
397
398         for (int i = 0; i < lease_cnt; i++) {
399                 ck_assert_str_eq(handles[i]->name, expected_names[i]);
400         }
401 }
402 END_TEST
403
404 static void add_lease_management_tests(Suite *s)
405 {
406         TCase *tc = tcase_create("Lease management");
407
408         tcase_add_checked_fixture(tc, test_setup, test_shutdown);
409
410         tcase_add_test(tc, create_and_revoke_lease);
411         tcase_add_test(tc, verify_lease_names);
412         suite_add_tcase(s, tc);
413 }
414
415 /***************** Lease Configuration Tests *************/
416
417 /* multiple_connector_lease */
418 /* Test details: Create a lease with multipe connectors
419  * Expected results: a lease is created with the CRTC and connector ID for both
420  *                   connectors.
421  */
422 START_TEST(multiple_connector_lease)
423 {
424         int out_cnt = 2, plane_cnt = 0, lease_cnt = 1;
425
426         setup_layout_simple_test_device(out_cnt, plane_cnt);
427
428         struct lease_config lconfig = {
429             .lease_name = "Lease Config Test 1",
430             .ncids = 2,
431             .connector_ids = (uint32_t[]){CONNECTOR_ID(0), CONNECTOR_ID(1)},
432         };
433
434         struct lease_handle **handles = create_leases(lease_cnt, &lconfig);
435
436         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0), CRTC_ID(1),
437                             CONNECTOR_ID(1));
438 }
439 END_TEST
440
441 /* single_failed_lease */
442 /* Test details: Create 2 lease configs. One with valid data, one without.
443  * Expected results: A handle is created for the single valid lease.
444  */
445 START_TEST(single_failed_lease)
446 {
447         int out_cnt = 3, plane_cnt = 0, success_lease_cnt = 1;
448
449         setup_layout_simple_test_device(out_cnt, plane_cnt);
450
451         struct lease_config lconfigs[2] = {
452             [0] =
453                 {
454                     .lease_name = "Lease Config Test 1",
455                     .ncids = 1,
456                     .connector_ids = (uint32_t[]){INVALID_OBJECT_ID},
457                 },
458             [1] =
459                 {
460                     .lease_name = "Lease Config Test 2",
461                     .ncids = 2,
462                     .connector_ids =
463                         (uint32_t[]){CONNECTOR_ID(0), CONNECTOR_ID(1)},
464                 },
465         };
466
467         /* Expect fewer leases than configurations supplied, so explicitly
468          * create and check leases. */
469         g_lm = lm_create_with_config(TEST_DRM_DEVICE, ARRAY_LEN(lconfigs),
470                                      lconfigs);
471         ck_assert_ptr_ne(g_lm, NULL);
472
473         struct lease_handle **handles;
474         ck_assert_int_eq(success_lease_cnt,
475                          lm_get_lease_handles(g_lm, &handles));
476         ck_assert_ptr_ne(handles, NULL);
477
478         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0), CRTC_ID(1),
479                             CONNECTOR_ID(1));
480 }
481 END_TEST
482
483 /* named_connector_config */
484 /* Test details: Test specifying connectors by name in config
485  * Expected results: A handle is created for each named connector
486  */
487
488 START_TEST(named_connector_config)
489 {
490         int out_cnt = 2, plane_cnt = 0, lease_cnt = 1;
491
492         ck_assert_int_eq(
493             setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true);
494
495         drmModeConnector connectors[] = {
496             CONNECTOR_FULL(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1,
497                            DRM_MODE_CONNECTOR_HDMIA, 1),
498             CONNECTOR_FULL(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1,
499                            DRM_MODE_CONNECTOR_VGA, 3),
500         };
501
502         drmModeEncoder encoders[] = {
503             ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x1),
504             ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x2),
505         };
506
507         setup_test_device_layout(connectors, encoders, NULL);
508
509         struct lease_config lconfig = {
510             .lease_name = "Lease Config Test 1",
511             .cnames = 2,
512             .connector_names = (char *[]){"HDMI-A-1", "VGA-3"},
513         };
514
515         struct lease_handle **handles = create_leases(lease_cnt, &lconfig);
516
517         ck_assert_str_eq(handles[0]->name, lconfig.lease_name);
518         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0), CRTC_ID(1),
519                             CONNECTOR_ID(1));
520 }
521 END_TEST
522
523 static void add_lease_config_tests(Suite *s)
524 {
525         TCase *tc = tcase_create("Lease configuration");
526
527         tcase_add_checked_fixture(tc, test_setup, test_shutdown);
528
529         tcase_add_test(tc, multiple_connector_lease);
530         tcase_add_test(tc, single_failed_lease);
531         tcase_add_test(tc, named_connector_config);
532         suite_add_tcase(s, tc);
533 }
534
535 int main(void)
536 {
537         int number_failed;
538         Suite *s;
539         SRunner *sr;
540
541         s = suite_create("DLM lease manager tests");
542
543         add_connector_enum_tests(s);
544         add_lease_management_tests(s);
545         add_lease_config_tests(s);
546
547         sr = srunner_create(s);
548         srunner_run_all(sr, CK_NORMAL);
549         number_failed = srunner_ntests_failed(sr);
550         srunner_free(sr);
551         return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
552 }