Add initial version
[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(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
65 static void test_setup(void)
66 {
67         RESET_FAKE(drmModeGetResources);
68         RESET_FAKE(drmModeFreeResources);
69         RESET_FAKE(drmModeGetPlaneResources);
70         RESET_FAKE(drmModeFreePlaneResources);
71
72         RESET_FAKE(drmModeGetPlane);
73         RESET_FAKE(drmModeFreePlane);
74         RESET_FAKE(drmModeGetConnector);
75         RESET_FAKE(drmModeFreeConnector);
76         RESET_FAKE(drmModeGetEncoder);
77         RESET_FAKE(drmModeFreeEncoder);
78
79         RESET_FAKE(drmModeCreateLease);
80         RESET_FAKE(drmModeRevokeLease);
81
82         drmModeGetResources_fake.return_val = TEST_DEVICE_RESOURCES;
83         drmModeGetPlaneResources_fake.return_val = TEST_DEVICE_PLANE_RESOURCES;
84
85         drmModeGetPlane_fake.custom_fake = get_plane;
86         drmModeGetConnector_fake.custom_fake = get_connector;
87         drmModeGetEncoder_fake.custom_fake = get_encoder;
88         drmModeCreateLease_fake.custom_fake = create_lease;
89 }
90
91 static void test_shutdown(void)
92 {
93         reset_drm_test_device();
94 }
95
96 /************** Resource enumeration tests *************/
97
98 /* These tests verify that the lease manager correctly assigns
99  * DRM resources to thier respective leases. In some cases
100  * the lease manager must choose which resources to include in
101  * each lease, so these tests verify that a valid (but not
102  * necessarily optimal) choice is made.
103  */
104
105 /* all_outputs_connected
106  *
107  * Test details: Create leases when all crtc/encoder/connector paths are
108  *               connected.
109  *
110  * Expected results: Leases are created for the currently connected sets of
111  *                   resources.
112  */
113 START_TEST(all_outputs_connected)
114 {
115         int out_cnt = 2, plane_cnt = 0;
116
117         ck_assert_int_eq(
118             setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true);
119
120         drmModeConnector connectors[] = {
121             CONNECTOR(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1),
122             CONNECTOR(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1),
123         };
124
125         drmModeEncoder encoders[] = {
126             ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x3),
127             ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x2),
128         };
129
130         setup_test_device_layout(connectors, encoders, NULL);
131
132         struct lm *lm = lm_create(TEST_DRM_DEVICE);
133         ck_assert_ptr_ne(lm, NULL);
134
135         struct lease_handle **handles;
136         ck_assert_int_eq(out_cnt, lm_get_lease_handles(lm, &handles));
137         ck_assert_ptr_ne(handles, NULL);
138
139         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0));
140         CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(1), CONNECTOR_ID(1));
141
142         lm_destroy(lm);
143 }
144 END_TEST
145
146 /* no_outputs_connected
147  *
148  * Test details: Create leases when no crtc/encoder/connector paths are
149  *               connected.
150  *
151  * Expected results: Available resources are divided between the leases.
152  *                   The same resource should not appear in multiple leases.
153  */
154 START_TEST(no_outputs_connected)
155 {
156         int out_cnt = 2, plane_cnt = 0;
157
158         ck_assert_int_eq(
159             setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true);
160
161         drmModeConnector connectors[] = {
162             CONNECTOR(CONNECTOR_ID(0), 0, &ENCODER_ID(0), 1),
163             CONNECTOR(CONNECTOR_ID(1), 0, &ENCODER_ID(1), 1),
164         };
165
166         drmModeEncoder encoders[] = {
167             ENCODER(ENCODER_ID(0), 0, 0x2),
168             ENCODER(ENCODER_ID(1), 0, 0x3),
169         };
170
171         setup_test_device_layout(connectors, encoders, NULL);
172
173         struct lm *lm = lm_create(TEST_DRM_DEVICE);
174         ck_assert_ptr_ne(lm, NULL);
175
176         struct lease_handle **handles;
177         ck_assert_int_eq(out_cnt, lm_get_lease_handles(lm, &handles));
178         ck_assert_ptr_ne(handles, NULL);
179
180         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(1), CONNECTOR_ID(0));
181         CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(0), CONNECTOR_ID(1));
182
183         lm_destroy(lm);
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 lm *lm = lm_create(TEST_DRM_DEVICE);
214         ck_assert_ptr_ne(lm, NULL);
215
216         struct lease_handle **handles;
217         ck_assert_int_eq(out_cnt, lm_get_lease_handles(lm, &handles));
218         ck_assert_ptr_ne(handles, NULL);
219
220         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0));
221         CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(1), CONNECTOR_ID(1));
222
223         lm_destroy(lm);
224 }
225 END_TEST
226
227 /* fewer_crtcs_than_connectors  */
228 /* Test details: Create leases on a system with more connectors than CRTCs
229  * Expected results: Number of leases generated should correspond to number of
230  *                   CRTCs.
231  *                   Leases contain one valid connector for each CRTC.
232  */
233 START_TEST(fewer_crtcs_than_connectors)
234 {
235         int out_cnt = 3, plane_cnt = 0, crtc_cnt = 2;
236
237         ck_assert_int_eq(
238             setup_drm_test_device(crtc_cnt, out_cnt, out_cnt, plane_cnt), true);
239
240         drmModeConnector connectors[] = {
241             CONNECTOR(CONNECTOR_ID(0), 0, &ENCODER_ID(0), 1),
242             CONNECTOR(CONNECTOR_ID(1), 0, &ENCODER_ID(1), 1),
243             CONNECTOR(CONNECTOR_ID(2), 0, &ENCODER_ID(2), 1),
244         };
245
246         drmModeEncoder encoders[] = {
247             ENCODER(ENCODER_ID(0), 0, 0x3),
248             ENCODER(ENCODER_ID(1), 0, 0x1),
249             ENCODER(ENCODER_ID(2), 0, 0x3),
250         };
251
252         setup_test_device_layout(connectors, encoders, NULL);
253
254         struct lm *lm = lm_create(TEST_DRM_DEVICE);
255         ck_assert_ptr_ne(lm, NULL);
256
257         struct lease_handle **handles;
258         ck_assert_int_eq(lm_get_lease_handles(lm, &handles), crtc_cnt);
259         ck_assert_ptr_ne(handles, NULL);
260
261         CHECK_LEASE_OBJECTS(handles[0], CRTC_ID(0), CONNECTOR_ID(0));
262         CHECK_LEASE_OBJECTS(handles[1], CRTC_ID(1), CONNECTOR_ID(2));
263         lm_destroy(lm);
264 }
265 END_TEST
266
267 /* separate_overlay_planes_by_crtc  */
268 /* Test details: Add overlay planes to leases. Each plane is tied to a
269  *               specific CRTC.
270  * Expected results: The leases contain all of the planes for connected to
271  *                   each CRTC and no others.
272  */
273 START_TEST(separate_overlay_planes_by_crtc)
274 {
275
276         int out_cnt = 2, plane_cnt = 3;
277
278         ck_assert_int_eq(
279             setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true);
280
281         drmModeConnector connectors[] = {
282             CONNECTOR(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1),
283             CONNECTOR(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1),
284         };
285
286         drmModeEncoder encoders[] = {
287             ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x1),
288             ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x2),
289         };
290
291         drmModePlane planes[] = {
292             PLANE(PLANE_ID(0), 0x2),
293             PLANE(PLANE_ID(1), 0x1),
294             PLANE(PLANE_ID(2), 0x2),
295         };
296
297         setup_test_device_layout(connectors, encoders, planes);
298
299         struct lm *lm = lm_create(TEST_DRM_DEVICE);
300         ck_assert_ptr_ne(lm, NULL);
301
302         struct lease_handle **handles;
303         ck_assert_int_eq(out_cnt, lm_get_lease_handles(lm, &handles));
304         ck_assert_ptr_ne(handles, NULL);
305
306         CHECK_LEASE_OBJECTS(handles[0], PLANE_ID(1), CRTC_ID(0),
307                             CONNECTOR_ID(0));
308         CHECK_LEASE_OBJECTS(handles[1], PLANE_ID(0), PLANE_ID(2), CRTC_ID(1),
309                             CONNECTOR_ID(1));
310         lm_destroy(lm);
311 }
312 END_TEST
313
314 /* reject_planes_shared_between_multiple_crtcs */
315 /* Test details: Add overlay planes to leases. Some planes are shared between
316  *               multiple CRTCs.
317  * Expected results: The leases contain all of the unique planes for each CRTC.
318  *                   Planes that can be used on multiple CRTCs are not included
319  *                   in any lease.
320  */
321 START_TEST(reject_planes_shared_between_multiple_crtcs)
322 {
323
324         int out_cnt = 2, plane_cnt = 3;
325
326         ck_assert_int_eq(
327             setup_drm_test_device(out_cnt, out_cnt, out_cnt, plane_cnt), true);
328
329         drmModeConnector connectors[] = {
330             CONNECTOR(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1),
331             CONNECTOR(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1),
332         };
333
334         drmModeEncoder encoders[] = {
335             ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x1),
336             ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x2),
337         };
338
339         drmModePlane planes[] = {
340             PLANE(PLANE_ID(0), 0x2),
341             PLANE(PLANE_ID(1), 0x1),
342             PLANE(PLANE_ID(2), 0x3),
343         };
344
345         setup_test_device_layout(connectors, encoders, planes);
346
347         struct lm *lm = lm_create(TEST_DRM_DEVICE);
348         ck_assert_ptr_ne(lm, NULL);
349
350         struct lease_handle **handles;
351         ck_assert_int_eq(out_cnt, lm_get_lease_handles(lm, &handles));
352         ck_assert_ptr_ne(handles, NULL);
353
354         CHECK_LEASE_OBJECTS(handles[0], PLANE_ID(1), CRTC_ID(0),
355                             CONNECTOR_ID(0));
356         CHECK_LEASE_OBJECTS(handles[1], PLANE_ID(0), CRTC_ID(1),
357                             CONNECTOR_ID(1));
358         lm_destroy(lm);
359 }
360 END_TEST
361
362 static void add_connector_enum_tests(Suite *s)
363 {
364         TCase *tc = tcase_create("Resource enumeration");
365
366         tcase_add_checked_fixture(tc, test_setup, test_shutdown);
367
368         tcase_add_test(tc, all_outputs_connected);
369         tcase_add_test(tc, no_outputs_connected);
370         tcase_add_test(tc, fewer_crtcs_than_connectors);
371         tcase_add_test(tc, some_outputs_connected);
372         tcase_add_test(tc, separate_overlay_planes_by_crtc);
373         tcase_add_test(tc, reject_planes_shared_between_multiple_crtcs);
374         suite_add_tcase(s, tc);
375 }
376
377 /************** Lease management tests *************/
378
379 /* create_and_revoke_lease */
380 /* Test details: Create leases and revoke them.
381  * Expected results: drmModeRevokeLease() is called with the correct leasee_id.
382  */
383 START_TEST(create_and_revoke_lease)
384 {
385         int lease_cnt = 2;
386         bool res = setup_drm_test_device(lease_cnt, lease_cnt, lease_cnt, 0);
387         ck_assert_int_eq(res, true);
388
389         drmModeConnector connectors[] = {
390             CONNECTOR(CONNECTOR_ID(0), ENCODER_ID(0), &ENCODER_ID(0), 1),
391             CONNECTOR(CONNECTOR_ID(1), ENCODER_ID(1), &ENCODER_ID(1), 1),
392         };
393
394         drmModeEncoder encoders[] = {
395             ENCODER(ENCODER_ID(0), CRTC_ID(0), 0x1),
396             ENCODER(ENCODER_ID(1), CRTC_ID(1), 0x2),
397         };
398
399         setup_test_device_layout(connectors, encoders, NULL);
400
401         struct lm *lm = lm_create(TEST_DRM_DEVICE);
402         ck_assert_ptr_ne(lm, NULL);
403
404         struct lease_handle **handles;
405         ck_assert_int_eq(lease_cnt, lm_get_lease_handles(lm, &handles));
406         ck_assert_ptr_ne(handles, NULL);
407
408         for (int i = 0; i < lease_cnt; i++) {
409                 ck_assert_int_ge(lm_lease_grant(lm, handles[i]), 0);
410                 lm_lease_revoke(lm, handles[i]);
411         }
412
413         ck_assert_int_eq(drmModeRevokeLease_fake.call_count, lease_cnt);
414
415         for (int i = 0; i < lease_cnt; i++) {
416                 ck_assert_int_eq(drmModeRevokeLease_fake.arg1_history[i],
417                                  LESSEE_ID(i));
418         }
419 }
420 END_TEST
421
422 static void add_lease_management_tests(Suite *s)
423 {
424         TCase *tc = tcase_create("Lease management");
425
426         tcase_add_checked_fixture(tc, test_setup, test_shutdown);
427
428         tcase_add_test(tc, create_and_revoke_lease);
429         suite_add_tcase(s, tc);
430 }
431
432 int main(void)
433 {
434         int number_failed;
435         Suite *s;
436         SRunner *sr;
437
438         s = suite_create("DLM lease manager tests");
439
440         add_connector_enum_tests(s);
441         add_lease_management_tests(s);
442
443         sr = srunner_create(s);
444         srunner_run_all(sr, CK_NORMAL);
445         number_failed = srunner_ntests_failed(sr);
446         srunner_free(sr);
447         return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
448 }