eb4f1b31fca40c9913f31e3b2d2a77ff02472e74
[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 static void add_lease_management_tests(Suite *s)
364 {
365         TCase *tc = tcase_create("Lease management");
366
367         tcase_add_checked_fixture(tc, test_setup, test_shutdown);
368
369         tcase_add_test(tc, create_and_revoke_lease);
370         suite_add_tcase(s, tc);
371 }
372
373 /***************** Lease Configuration Tests *************/
374
375 /* multiple_connector_lease */
376 /* Test details: Create a lease with multipe connectors
377  * Expected results: a lease is created with the CRTC and connector ID for both
378  *                   connectors.
379  */
380 START_TEST(multiple_connector_lease)
381 {
382         int out_cnt = 2, plane_cnt = 0, lease_cnt = 1;
383
384         setup_layout_simple_test_device(out_cnt, plane_cnt);
385
386         struct lease_config lconfig = {
387             .lease_name = "Lease Config Test 1",
388             .ncids = 2,
389             .connector_ids = (uint32_t[]){CONNECTOR_ID(0), CONNECTOR_ID(1)},
390         };
391
392         struct lease_handle **handles = create_leases(lease_cnt, &lconfig);
393
394         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0), CRTC_ID(1),
395                             CONNECTOR_ID(1));
396 }
397 END_TEST
398
399 /* single_failed_lease */
400 /* Test details: Create 2 lease configs. One with valid data, one without.
401  * Expected results: A handle is created for the single valid lease.
402  */
403 START_TEST(single_failed_lease)
404 {
405         int out_cnt = 3, plane_cnt = 0, success_lease_cnt = 1;
406
407         setup_layout_simple_test_device(out_cnt, plane_cnt);
408
409         struct lease_config lconfigs[2] = {
410             [0] =
411                 {
412                     .lease_name = "Lease Config Test 1",
413                     .ncids = 1,
414                     .connector_ids = (uint32_t[]){INVALID_OBJECT_ID},
415                 },
416             [1] =
417                 {
418                     .lease_name = "Lease Config Test 2",
419                     .ncids = 2,
420                     .connector_ids =
421                         (uint32_t[]){CONNECTOR_ID(0), CONNECTOR_ID(1)},
422                 },
423         };
424
425         /* Expect fewer leases than configurations supplied, so explicitly
426          * create and check leases. */
427         g_lm = lm_create_with_config(TEST_DRM_DEVICE, ARRAY_LEN(lconfigs),
428                                      lconfigs);
429         ck_assert_ptr_ne(g_lm, NULL);
430
431         struct lease_handle **handles;
432         ck_assert_int_eq(success_lease_cnt,
433                          lm_get_lease_handles(g_lm, &handles));
434         ck_assert_ptr_ne(handles, NULL);
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 static void add_lease_config_tests(Suite *s)
442 {
443         TCase *tc = tcase_create("Lease configuration");
444
445         tcase_add_checked_fixture(tc, test_setup, test_shutdown);
446
447         tcase_add_test(tc, multiple_connector_lease);
448         tcase_add_test(tc, single_failed_lease);
449         suite_add_tcase(s, tc);
450 }
451
452 int main(void)
453 {
454         int number_failed;
455         Suite *s;
456         SRunner *sr;
457
458         s = suite_create("DLM lease manager tests");
459
460         add_connector_enum_tests(s);
461         add_lease_management_tests(s);
462         add_lease_config_tests(s);
463
464         sr = srunner_create(s);
465         srunner_run_all(sr, CK_NORMAL);
466         number_failed = srunner_ntests_failed(sr);
467         srunner_free(sr);
468         return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
469 }