1 /*
2  * Copyright © 2020 Manuel Stoeckl
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the
13  * next paragraph) shall be included in all copies or substantial
14  * portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25 
26 #include "common.h"
27 #include "main.h"
28 #include "parsing.h"
29 #include "util.h"
30 
31 #include "protocol_functions.h"
32 
33 #include <errno.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/mman.h>
38 #include <unistd.h>
39 
40 struct msgtransfer {
41 	struct test_state *src;
42 	struct test_state *dst;
43 };
44 
45 /* Override the libc clock_gettime, so we can test presentation-time
46  * protocol. Note: the video drivers sometimes call this function. */
clock_gettime(clockid_t clock_id,struct timespec * tp)47 int clock_gettime(clockid_t clock_id, struct timespec *tp)
48 {
49 	/* Assume every call costs 1ns */
50 	time_value += 1;
51 
52 	if (clock_id == CLOCK_REALTIME) {
53 		tp->tv_sec = (int64_t)(time_value / 1000000000uLL);
54 		tp->tv_nsec = (int64_t)(time_value % 1000000000uLL);
55 	} else {
56 		tp->tv_sec = (int64_t)((time_value + local_time_offset) /
57 				       1000000000uLL);
58 		tp->tv_nsec = (int64_t)((time_value + local_time_offset) %
59 					1000000000uLL);
60 	}
61 	return 0;
62 }
63 
print_pass(bool pass)64 static void print_pass(bool pass)
65 {
66 	fprintf(stdout, "%s\n", pass ? "PASS" : "FAIL");
67 }
68 
make_filled_pattern(size_t size,uint32_t contents)69 static char *make_filled_pattern(size_t size, uint32_t contents)
70 {
71 	uint32_t *mem = calloc(size, 1);
72 	for (size_t i = 0; i < size / 4; i++) {
73 		mem[i] = contents;
74 	}
75 	return (char *)mem;
76 }
77 
make_filled_file(size_t size,const char * contents)78 static int make_filled_file(size_t size, const char *contents)
79 {
80 	int fd = create_anon_file();
81 	ftruncate(fd, (off_t)size);
82 
83 	uint32_t *mem = (uint32_t *)mmap(
84 			NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
85 	memcpy(mem, contents, size);
86 	munmap(mem, size);
87 	return fd;
88 }
89 
check_file_contents(int fd,size_t size,const char * contents)90 static bool check_file_contents(int fd, size_t size, const char *contents)
91 {
92 	if (fd == -1) {
93 		return false;
94 	}
95 
96 	off_t fsize = lseek(fd, 0, SEEK_END);
97 	if (fsize != (off_t)size) {
98 		wp_error("fd size mismatch: %zu %zu\n", fsize, size);
99 		return -1;
100 	}
101 
102 	uint32_t *mem = (uint32_t *)mmap(
103 			NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
104 	if (mem == MAP_FAILED) {
105 		wp_error("Failed to map file");
106 		return -1;
107 	}
108 	bool match = memcmp(mem, contents, size) == 0;
109 	munmap(mem, size);
110 	return match;
111 }
get_only_fd_from_msg(const struct test_state * s)112 static int get_only_fd_from_msg(const struct test_state *s)
113 {
114 
115 	if (s->rcvd && s->rcvd[s->nrcvd - 1].nfds == 1) {
116 		return s->rcvd[s->nrcvd - 1].fds[0];
117 	} else {
118 		return -1;
119 	}
120 }
get_fd_from_nth_to_last_msg(const struct test_state * s,int nth)121 static int get_fd_from_nth_to_last_msg(const struct test_state *s, int nth)
122 {
123 	if (!s->rcvd || s->nrcvd < nth) {
124 		return -1;
125 	}
126 	const struct msg *m = &s->rcvd[s->nrcvd - nth];
127 	if (m->nfds != 1) {
128 		return -1;
129 	}
130 	return m->fds[0];
131 }
132 
msg_send_handler(struct transfer_states * ts,struct test_state * src,struct test_state * dst)133 static void msg_send_handler(struct transfer_states *ts, struct test_state *src,
134 		struct test_state *dst)
135 {
136 	struct msg m;
137 	m.data = ts->msg_space;
138 	m.fds = ts->fd_space;
139 	m.len = (int)ts->msg_size;
140 	m.nfds = (int)ts->fd_size;
141 	for (int i = 0; i < m.nfds; i++) {
142 		m.fds[i] = dup(m.fds[i]);
143 		if (m.fds[i] == -1) {
144 			wp_error("Invalid fd provided");
145 		}
146 	}
147 	send_protocol_msg(src, dst, m);
148 	memset(ts->msg_space, 0, sizeof(ts->msg_space));
149 	memset(ts->fd_space, 0, sizeof(ts->fd_space));
150 }
setup_tstate(struct transfer_states * ts)151 static int setup_tstate(struct transfer_states *ts)
152 {
153 	memset(ts, 0, sizeof(*ts));
154 	ts->send = msg_send_handler;
155 	ts->comp = calloc(1, sizeof(struct test_state));
156 	ts->app = calloc(1, sizeof(struct test_state));
157 	if (!ts->comp || !ts->app) {
158 		goto fail_alloc;
159 	}
160 	if (setup_state(ts->comp, true, true) == -1) {
161 		goto fail_comp_setup;
162 	}
163 	if (setup_state(ts->app, false, true) == -1) {
164 		goto fail_app_setup;
165 	}
166 	return 0;
167 
168 fail_app_setup:
169 	cleanup_state(ts->app);
170 fail_comp_setup:
171 	cleanup_state(ts->comp);
172 fail_alloc:
173 	free(ts->comp);
174 	free(ts->app);
175 	return -1;
176 }
cleanup_tstate(struct transfer_states * ts)177 static void cleanup_tstate(struct transfer_states *ts)
178 {
179 	cleanup_state(ts->comp);
180 	cleanup_state(ts->app);
181 	free(ts->comp);
182 	free(ts->app);
183 }
184 
test_fixed_shm_buffer_copy()185 static bool test_fixed_shm_buffer_copy()
186 {
187 	fprintf(stdout, "\n  shm_pool+buffer test\n");
188 
189 	struct transfer_states T;
190 	if (setup_tstate(&T) == -1) {
191 		wp_error("Test setup failed");
192 		return true;
193 	}
194 	bool pass = true;
195 
196 	char *testpat = make_filled_pattern(16384, 0xFEDCBA98);
197 	int fd = make_filled_file(16384, testpat);
198 	int ret_fd = -1;
199 
200 	struct wp_objid display = {0x1}, registry = {0x2}, shm = {0x3},
201 			compositor = {0x4}, pool = {0x5}, buffer = {0x6},
202 			surface = {0x7};
203 
204 	send_wl_display_req_get_registry(&T, display, registry);
205 	send_wl_registry_evt_global(&T, registry, 1, "wl_shm", 1);
206 	send_wl_registry_evt_global(&T, registry, 2, "wl_compositor", 1);
207 	send_wl_registry_req_bind(&T, registry, 1, "wl_shm", 1, shm);
208 	send_wl_registry_req_bind(
209 			&T, registry, 2, "wl_compositor", 1, compositor);
210 	send_wl_shm_req_create_pool(&T, shm, pool, fd, 16384);
211 	ret_fd = get_only_fd_from_msg(T.comp);
212 	send_wl_shm_pool_req_create_buffer(
213 			&T, pool, buffer, 0, 64, 64, 256, 0x30334258);
214 	send_wl_compositor_req_create_surface(&T, compositor, surface);
215 	send_wl_surface_req_attach(&T, surface, buffer, 0, 0);
216 	send_wl_surface_req_damage(&T, surface, 0, 0, 64, 64);
217 	send_wl_surface_req_commit(&T, surface);
218 
219 	/* confirm receipt of fd with the correct contents; if not,
220 	 * reject */
221 	if (ret_fd == -1) {
222 		wp_error("Fd not passed through");
223 		pass = false;
224 		goto end;
225 	}
226 	pass = check_file_contents(ret_fd, 16384, testpat);
227 	if (!pass) {
228 		wp_error("Failed to transfer file");
229 	}
230 end:
231 	free(testpat);
232 	checked_close(fd);
233 	cleanup_tstate(&T);
234 
235 	print_pass(pass);
236 	return pass;
237 }
238 
test_fixed_shm_screencopy_copy()239 static bool test_fixed_shm_screencopy_copy()
240 {
241 	fprintf(stdout, "\n screencopy test\n");
242 
243 	struct transfer_states T;
244 	if (setup_tstate(&T) == -1) {
245 		wp_error("Test setup failed");
246 		return true;
247 	}
248 	bool pass = true;
249 
250 	char *testpat_orig = make_filled_pattern(16384, 0xFEDCBA98);
251 	char *testpat_screen = make_filled_pattern(16384, 0x77557755);
252 	int fd = make_filled_file(16384, testpat_orig);
253 	int ret_fd = -1;
254 
255 	struct wp_objid display = {0x1}, registry = {0x2}, shm = {0x3},
256 			output = {0x4}, pool = {0x5}, buffer = {0x6},
257 			frame = {0x7}, screencopy = {0x8};
258 
259 	send_wl_display_req_get_registry(&T, display, registry);
260 	send_wl_registry_evt_global(&T, registry, 1, "wl_shm", 1);
261 	send_wl_registry_evt_global(&T, registry, 2, "wl_output", 1);
262 	send_wl_registry_evt_global(
263 			&T, registry, 3, "zwlr_screencopy_manager_v1", 1);
264 	send_wl_registry_req_bind(&T, registry, 1, "wl_shm", 1, shm);
265 	send_wl_registry_req_bind(&T, registry, 2, "wl_output", 1, output);
266 	send_wl_registry_req_bind(&T, registry, 3, "zwlr_screencopy_manager_v1",
267 			1, screencopy);
268 	send_wl_shm_req_create_pool(&T, shm, pool, fd, 16384);
269 	ret_fd = get_only_fd_from_msg(T.comp);
270 	if (ret_fd == -1) {
271 		wp_error("Fd not passed through");
272 		pass = false;
273 		goto end;
274 	}
275 	send_zwlr_screencopy_manager_v1_req_capture_output(
276 			&T, screencopy, frame, 0, output);
277 	send_zwlr_screencopy_frame_v1_evt_buffer(&T, frame, 0, 64, 64, 16384);
278 	send_wl_shm_pool_req_create_buffer(
279 			&T, pool, buffer, 0, 64, 64, 256, 0x30334258);
280 	send_zwlr_screencopy_frame_v1_req_copy(&T, frame, buffer);
281 
282 	uint32_t *mem = (uint32_t *)mmap(NULL, 16384, PROT_READ | PROT_WRITE,
283 			MAP_SHARED, ret_fd, 0);
284 	memcpy(mem, testpat_screen, 16384);
285 	munmap(mem, 16384);
286 
287 	send_zwlr_screencopy_frame_v1_evt_flags(&T, frame, 0);
288 	send_zwlr_screencopy_frame_v1_evt_ready(&T, frame, 0, 12345, 600000000);
289 
290 	/* confirm receipt of fd with the correct contents; if not,
291 	 * reject */
292 	if (ret_fd == -1) {
293 		wp_error("Fd not passed through");
294 		pass = false;
295 		goto end;
296 	}
297 	pass = check_file_contents(fd, 16384, testpat_screen);
298 	if (!pass) {
299 		wp_error("Failed to transfer file");
300 	}
301 end:
302 	free(testpat_screen);
303 	free(testpat_orig);
304 	checked_close(fd);
305 	cleanup_tstate(&T);
306 
307 	print_pass(pass);
308 	return pass;
309 }
310 
test_fixed_keymap_copy()311 static bool test_fixed_keymap_copy()
312 {
313 	fprintf(stdout, "\n  Keymap test\n");
314 	struct transfer_states T;
315 	if (setup_tstate(&T) == -1) {
316 		wp_error("Test setup failed");
317 		return true;
318 	}
319 	bool pass = true;
320 
321 	char *testpat = make_filled_pattern(16384, 0xFEDCBA98);
322 	int fd = make_filled_file(16384, testpat);
323 	int ret_fd = -1;
324 
325 	struct wp_objid display = {0x1}, registry = {0x2}, seat = {0x3},
326 			keyboard = {0x4};
327 
328 	send_wl_display_req_get_registry(&T, display, registry);
329 	send_wl_registry_evt_global(&T, registry, 1, "wl_seat", 7);
330 	send_wl_registry_req_bind(&T, registry, 1, "wl_seat", 7, seat);
331 	send_wl_seat_evt_capabilities(&T, seat, 3);
332 	send_wl_seat_req_get_keyboard(&T, seat, keyboard);
333 	send_wl_keyboard_evt_keymap(&T, keyboard, 1, fd, 16384);
334 	ret_fd = get_only_fd_from_msg(T.app);
335 
336 	/* confirm receipt of fd with the correct contents; if not,
337 	 * reject */
338 	if (ret_fd == -1) {
339 		wp_error("Fd not passed through");
340 		pass = false;
341 		goto end;
342 	}
343 	pass = check_file_contents(ret_fd, 16384, testpat);
344 	if (!pass) {
345 		wp_error("Failed to transfer file");
346 	}
347 
348 end:
349 	free(testpat);
350 	checked_close(fd);
351 	cleanup_tstate(&T);
352 
353 	print_pass(pass);
354 	return pass;
355 }
356 
357 #define DMABUF_FORMAT 875713112
358 
create_dmabuf(void)359 static int create_dmabuf(void)
360 {
361 	struct render_data rd;
362 	memset(&rd, 0, sizeof(rd));
363 	rd.drm_fd = -1;
364 	rd.av_disabled = true;
365 
366 	const size_t test_width = 256;
367 	const size_t test_height = 384;
368 	const size_t test_cpp = 4;
369 	const size_t test_size = test_width * test_height * test_cpp;
370 	const struct dmabuf_slice_data slice_data = {
371 			.width = (uint32_t)test_width,
372 			.height = (uint32_t)test_height,
373 			.format = DMABUF_FORMAT,
374 			.num_planes = 1,
375 			.modifier = 0,
376 			.offsets = {0, 0, 0, 0},
377 			.strides = {(uint32_t)(test_width * test_cpp), 0, 0, 0},
378 			.using_planes = {true, false, false, false},
379 	};
380 
381 	int dmafd = -1;
382 	if (init_render_data(&rd) == -1) {
383 		return -1;
384 	}
385 	struct gbm_bo *bo = make_dmabuf(&rd, test_size, &slice_data);
386 	if (!bo) {
387 		goto end;
388 	}
389 
390 	void *map_handle = NULL;
391 	void *data = map_dmabuf(bo, true, &map_handle, NULL, NULL);
392 	if (!data) {
393 		destroy_dmabuf(bo);
394 		goto end;
395 	}
396 	/* TODO: the best test pattern is a colored gradient, so we can
397 	 * check whether the copy flips things or not */
398 	memset(data, 0x80, test_size);
399 	unmap_dmabuf(bo, map_handle);
400 
401 	dmafd = export_dmabuf(bo);
402 	if (dmafd == -1) {
403 		goto end;
404 	}
405 
406 end:
407 	destroy_dmabuf(bo);
408 	cleanup_render_data(&rd);
409 
410 	return dmafd;
411 }
412 
413 enum dmabuf_copy_type {
414 	COPY_LINUX_DMABUF,
415 	COPY_LINUX_DMABUF_INDIR,
416 	COPY_DRM_PRIME,
417 	COPY_WLR_EXPORT,
418 };
419 
test_fixed_dmabuf_copy(enum dmabuf_copy_type type)420 static bool test_fixed_dmabuf_copy(enum dmabuf_copy_type type)
421 {
422 	const char *const types[] = {"linux-dmabuf", "linux-dmabuf-indir",
423 			"drm-prime", "wlr-export"};
424 	fprintf(stdout, "\n  DMABUF test, %s\n", types[(int)type]);
425 
426 	int dmabufd = create_dmabuf();
427 	const int width = 256, height = 384;
428 	if (dmabufd == -1) {
429 		return true;
430 	}
431 	struct transfer_states T;
432 	if (setup_tstate(&T) == -1) {
433 		wp_error("Test setup failed");
434 		return true;
435 	}
436 	bool pass = true;
437 	int ret_fd = -1;
438 
439 	switch (type) {
440 	case COPY_LINUX_DMABUF: {
441 		struct wp_objid display = {0x1}, registry = {0x2},
442 				linux_dmabuf = {0x3}, compositor = {0x4},
443 				params = {0x5}, buffer = {0x6}, surface = {0x7};
444 
445 		send_wl_display_req_get_registry(&T, display, registry);
446 		send_wl_registry_evt_global(
447 				&T, registry, 1, "zwp_linux_dmabuf_v1", 1);
448 		send_wl_registry_evt_global(
449 				&T, registry, 2, "wl_compositor", 1);
450 		send_wl_registry_req_bind(&T, registry, 1,
451 				"zwp_linux_dmabuf_v1", 1, linux_dmabuf);
452 		send_wl_registry_req_bind(&T, registry, 12, "wl_compositor", 1,
453 				compositor);
454 		send_zwp_linux_dmabuf_v1_evt_modifier(
455 				&T, linux_dmabuf, DMABUF_FORMAT, 0, 0);
456 		send_zwp_linux_dmabuf_v1_req_create_params(
457 				&T, linux_dmabuf, params);
458 		send_zwp_linux_buffer_params_v1_req_add(
459 				&T, params, dmabufd, 0, 0, 256 * 4, 0, 0);
460 		send_zwp_linux_buffer_params_v1_req_create_immed(
461 				&T, params, buffer, 256, 384, DMABUF_FORMAT, 0);
462 		/* this message + previous, after reordering, are treated as one
463 		 * bundle; if that is fixed, this will break, and 1 should
464 		 * become 2 */
465 		ret_fd = get_fd_from_nth_to_last_msg(T.comp, 1);
466 		send_zwp_linux_buffer_params_v1_req_destroy(&T, params);
467 		send_wl_compositor_req_create_surface(&T, compositor, surface);
468 		send_wl_surface_req_attach(&T, surface, buffer, 0, 0);
469 		send_wl_surface_req_damage(&T, surface, 0, 0, 64, 64);
470 		send_wl_surface_req_commit(&T, surface);
471 	} break;
472 	case COPY_LINUX_DMABUF_INDIR: {
473 		struct wp_objid display = {0x1}, registry = {0x2},
474 				linux_dmabuf = {0x3}, compositor = {0x4},
475 				params = {0x5}, buffer = {0x6}, surface = {0x7};
476 
477 		send_wl_display_req_get_registry(&T, display, registry);
478 		send_wl_registry_evt_global(
479 				&T, registry, 1, "zwp_linux_dmabuf_v1", 1);
480 		send_wl_registry_evt_global(
481 				&T, registry, 2, "wl_compositor", 1);
482 		send_wl_registry_req_bind(&T, registry, 1,
483 				"zwp_linux_dmabuf_v1", 1, linux_dmabuf);
484 		send_wl_registry_req_bind(&T, registry, 12, "wl_compositor", 1,
485 				compositor);
486 		send_zwp_linux_dmabuf_v1_evt_modifier(
487 				&T, linux_dmabuf, DMABUF_FORMAT, 0, 0);
488 		send_zwp_linux_dmabuf_v1_req_create_params(
489 				&T, linux_dmabuf, params);
490 		send_zwp_linux_buffer_params_v1_req_add(
491 				&T, params, dmabufd, 0, 0, 256 * 4, 0, 0);
492 		send_zwp_linux_buffer_params_v1_req_create(
493 				&T, params, 256, 384, DMABUF_FORMAT, 0);
494 		/* this message + previous, after reordering, are treated as one
495 		 * bundle; if that is fixed, this will break, and 1 should
496 		 * become 2 */
497 		ret_fd = get_fd_from_nth_to_last_msg(T.comp, 1);
498 		send_zwp_linux_buffer_params_v1_evt_created(&T, params, buffer);
499 		send_zwp_linux_buffer_params_v1_req_destroy(&T, params);
500 		send_wl_compositor_req_create_surface(&T, compositor, surface);
501 		send_wl_surface_req_attach(&T, surface, buffer, 0, 0);
502 		send_wl_surface_req_damage(&T, surface, 0, 0, 64, 64);
503 		send_wl_surface_req_commit(&T, surface);
504 	} break;
505 	case COPY_DRM_PRIME: {
506 		struct wp_objid display = {0x1}, registry = {0x2},
507 				wl_drm = {0x3}, compositor = {0x4},
508 				buffer = {0x5}, surface = {0x6};
509 
510 		send_wl_display_req_get_registry(&T, display, registry);
511 		send_wl_registry_evt_global(&T, registry, 1, "wl_drm", 1);
512 		send_wl_registry_evt_global(
513 				&T, registry, 2, "wl_compositor", 1);
514 		send_wl_registry_req_bind(&T, registry, 1, "wl_drm", 1, wl_drm);
515 		send_wl_registry_req_bind(&T, registry, 12, "wl_compositor", 1,
516 				compositor);
517 
518 		send_wl_drm_evt_device(&T, wl_drm, "/dev/dri/renderD128");
519 		send_wl_drm_evt_format(&T, wl_drm, DMABUF_FORMAT);
520 		send_wl_drm_evt_capabilities(&T, wl_drm, 1);
521 		send_wl_drm_req_create_prime_buffer(&T, wl_drm, buffer, dmabufd,
522 				width, height, DMABUF_FORMAT, 0, width * 4, 0,
523 				0, 0, 0);
524 
525 		ret_fd = get_fd_from_nth_to_last_msg(T.comp, 1);
526 		send_wl_compositor_req_create_surface(&T, compositor, surface);
527 		send_wl_surface_req_attach(&T, surface, buffer, 0, 0);
528 		send_wl_surface_req_damage(&T, surface, 0, 0, 64, 64);
529 		send_wl_surface_req_commit(&T, surface);
530 	} break;
531 
532 	case COPY_WLR_EXPORT: {
533 		/* note: here the compositor creates and sends fd to client */
534 
535 		struct wp_objid display = {0x1}, registry = {0x2},
536 				export_manager = {0x3}, output = {0x4},
537 				dmabuf_frame = {0x5};
538 
539 		send_wl_display_req_get_registry(&T, display, registry);
540 		send_wl_registry_evt_global(&T, registry, 1,
541 				"zwlr_export_dmabuf_manager_v1", 1);
542 		send_wl_registry_evt_global(&T, registry, 2, "wl_output", 1);
543 		send_wl_registry_req_bind(&T, registry, 1,
544 				"zwlr_export_dmabuf_manager_v1", 1,
545 				export_manager);
546 		send_wl_registry_req_bind(
547 				&T, registry, 12, "wl_output", 1, output);
548 
549 		send_zwlr_export_dmabuf_manager_v1_req_capture_output(
550 				&T, export_manager, dmabuf_frame, 1, output);
551 		send_zwlr_export_dmabuf_frame_v1_evt_frame(&T, dmabuf_frame,
552 				width, height, 0, 0, 0, 1, DMABUF_FORMAT, 0, 0,
553 				1);
554 		send_zwlr_export_dmabuf_frame_v1_evt_object(&T, dmabuf_frame, 0,
555 				dmabufd, width * height * 4, 0, width * 4, 0);
556 		ret_fd = get_only_fd_from_msg(T.app);
557 		send_zwlr_export_dmabuf_frame_v1_evt_ready(
558 				&T, dmabuf_frame, 555555, 555555555, 333333333);
559 	} break;
560 	}
561 
562 	if (ret_fd == -1) {
563 		wp_error("Fd not passed through");
564 		pass = false;
565 		goto end;
566 	}
567 	// TODO: verify that the FD contents are correct
568 
569 end:
570 	checked_close(dmabufd);
571 	/* todo: the drm_fd may be dup'd by libgbm but not freed */
572 	cleanup_tstate(&T);
573 
574 	print_pass(pass);
575 	return pass;
576 }
577 
578 enum data_device_type {
579 	DDT_WAYLAND,
580 	DDT_GTK_PRIMARY,
581 	DDT_PRIMARY,
582 	DDT_WLR,
583 };
584 static const char *const data_device_type_strs[] = {"wayland main",
585 		"gtk primary selection", "primary selection",
586 		"wlroots data control"};
587 
588 /* Confirm that wl_data_offer.receive creates a pipe matching the input */
test_data_offer(enum data_device_type type)589 static bool test_data_offer(enum data_device_type type)
590 {
591 	fprintf(stdout, "\n  Data offer test: %s\n",
592 			data_device_type_strs[type]);
593 	struct transfer_states T;
594 	if (setup_tstate(&T) == -1) {
595 		wp_error("Test setup failed");
596 		return true;
597 	}
598 	bool pass = true;
599 
600 	int src_pipe[2];
601 	pipe(src_pipe);
602 	int ret_fd = -1;
603 
604 	struct wp_objid display = {0x1}, registry = {0x2}, ddman = {0x3},
605 			seat = {0x4}, ddev = {0x5}, offer = {0xff000001};
606 
607 	send_wl_display_req_get_registry(&T, display, registry);
608 	send_wl_registry_evt_global(&T, registry, 1, "wl_seat", 7);
609 	send_wl_registry_req_bind(&T, registry, 1, "wl_seat", 7, seat);
610 	switch (type) {
611 	case DDT_WAYLAND:
612 		send_wl_registry_evt_global(
613 				&T, registry, 2, "wl_data_device_manager", 3);
614 		send_wl_registry_req_bind(&T, registry, 2,
615 				"wl_data_device_manager", 3, ddman);
616 		send_wl_data_device_manager_req_get_data_device(
617 				&T, ddman, ddev, seat);
618 		send_wl_data_device_evt_data_offer(&T, ddev, offer);
619 		send_wl_data_offer_evt_offer(
620 				&T, offer, "text/plain;charset=utf-8");
621 		send_wl_data_device_evt_selection(&T, ddev, offer);
622 		send_wl_data_offer_req_receive(&T, offer,
623 				"text/plain;charset=utf-8", src_pipe[1]);
624 		break;
625 	case DDT_GTK_PRIMARY:
626 		send_wl_registry_evt_global(&T, registry, 2,
627 				"gtk_primary_selection_device_manager", 1);
628 		send_wl_registry_req_bind(&T, registry, 2,
629 				"gtk_primary_selection_device_manager", 1,
630 				ddman);
631 		send_gtk_primary_selection_device_manager_req_get_device(
632 				&T, ddman, ddev, seat);
633 		send_gtk_primary_selection_device_evt_data_offer(
634 				&T, ddev, offer);
635 		send_gtk_primary_selection_offer_evt_offer(
636 				&T, offer, "text/plain;charset=utf-8");
637 		send_gtk_primary_selection_device_evt_selection(
638 				&T, ddev, offer);
639 		send_gtk_primary_selection_offer_req_receive(&T, offer,
640 				"text/plain;charset=utf-8", src_pipe[1]);
641 		break;
642 	case DDT_PRIMARY:
643 		send_wl_registry_evt_global(&T, registry, 2,
644 				"zwp_primary_selection_device_manager_v1", 1);
645 		send_wl_registry_req_bind(&T, registry, 2,
646 				"zwp_primary_selection_device_manager_v1", 1,
647 				ddman);
648 		send_zwp_primary_selection_device_manager_v1_req_get_device(
649 				&T, ddman, ddev, seat);
650 		send_zwp_primary_selection_device_v1_evt_data_offer(
651 				&T, ddev, offer);
652 		send_zwp_primary_selection_offer_v1_evt_offer(
653 				&T, offer, "text/plain;charset=utf-8");
654 		send_zwp_primary_selection_device_v1_evt_selection(
655 				&T, ddev, offer);
656 		send_zwp_primary_selection_offer_v1_req_receive(&T, offer,
657 				"text/plain;charset=utf-8", src_pipe[1]);
658 		break;
659 	case DDT_WLR:
660 		send_wl_registry_evt_global(&T, registry, 2,
661 				"zwlr_data_control_manager_v1", 1);
662 		send_wl_registry_req_bind(&T, registry, 2,
663 				"zwlr_data_control_manager_v1", 1, ddman);
664 		send_zwlr_data_control_manager_v1_req_get_data_device(
665 				&T, ddman, ddev, seat);
666 		send_zwlr_data_control_device_v1_evt_data_offer(
667 				&T, ddev, offer);
668 		send_zwlr_data_control_offer_v1_evt_offer(
669 				&T, offer, "text/plain;charset=utf-8");
670 		send_zwlr_data_control_device_v1_evt_selection(&T, ddev, offer);
671 		send_zwlr_data_control_offer_v1_req_receive(&T, offer,
672 				"text/plain;charset=utf-8", src_pipe[1]);
673 		break;
674 	}
675 	ret_fd = get_only_fd_from_msg(T.comp);
676 
677 	/* confirm receipt of fd with the correct contents; if not,
678 	 * reject */
679 	if (ret_fd == -1) {
680 		wp_error("Fd not passed through");
681 		pass = false;
682 		goto end;
683 	}
684 	uint8_t tmp = 0xab;
685 	if (write(ret_fd, &tmp, 1) != 1) {
686 		wp_error("Fd not writable");
687 		pass = false;
688 		goto end;
689 	}
690 end:
691 
692 	checked_close(src_pipe[0]);
693 	checked_close(src_pipe[1]);
694 	cleanup_tstate(&T);
695 
696 	print_pass(pass);
697 	return pass;
698 }
699 
700 /* Confirm that wl_data_source.data_offer creates a pipe matching the input */
test_data_source(enum data_device_type type)701 static bool test_data_source(enum data_device_type type)
702 {
703 	fprintf(stdout, "\n  Data source test: %s\n",
704 			data_device_type_strs[type]);
705 	struct transfer_states T;
706 	if (setup_tstate(&T) == -1) {
707 		wp_error("Test setup failed");
708 		return true;
709 	}
710 	bool pass = true;
711 
712 	int dst_pipe[2];
713 	pipe(dst_pipe);
714 	int ret_fd = -1;
715 
716 	struct wp_objid display = {0x1}, registry = {0x2}, ddman = {0x3},
717 			seat = {0x4}, ddev = {0x5}, dsource = {0x6};
718 
719 	send_wl_display_req_get_registry(&T, display, registry);
720 	send_wl_registry_evt_global(&T, registry, 1, "wl_seat", 7);
721 	send_wl_registry_req_bind(&T, registry, 1, "wl_seat", 7, seat);
722 	switch (type) {
723 	case DDT_WAYLAND:
724 		send_wl_registry_evt_global(
725 				&T, registry, 2, "wl_data_device_manager", 1);
726 		send_wl_registry_req_bind(&T, registry, 2,
727 				"wl_data_device_manager", 1, ddman);
728 		send_wl_data_device_manager_req_get_data_device(
729 				&T, ddman, ddev, seat);
730 		send_wl_data_device_manager_req_create_data_source(
731 				&T, ddman, dsource);
732 		send_wl_data_source_req_offer(
733 				&T, dsource, "text/plain;charset=utf-8");
734 		send_wl_data_device_req_set_selection(&T, ddev, dsource, 9999);
735 		send_wl_data_source_evt_send(&T, dsource,
736 				"text/plain;charset=utf-8", dst_pipe[0]);
737 		break;
738 	case DDT_GTK_PRIMARY:
739 		send_wl_registry_evt_global(&T, registry, 2,
740 				"gtk_primary_selection_device_manager", 1);
741 		send_wl_registry_req_bind(&T, registry, 2,
742 				"gtk_primary_selection_device_manager", 1,
743 				ddman);
744 		send_gtk_primary_selection_device_manager_req_get_device(
745 				&T, ddman, ddev, seat);
746 		send_gtk_primary_selection_device_manager_req_create_source(
747 				&T, ddman, dsource);
748 		send_gtk_primary_selection_source_req_offer(
749 				&T, dsource, "text/plain;charset=utf-8");
750 		send_gtk_primary_selection_device_req_set_selection(
751 				&T, ddev, dsource, 9999);
752 		send_gtk_primary_selection_source_evt_send(&T, dsource,
753 				"text/plain;charset=utf-8", dst_pipe[0]);
754 		break;
755 	case DDT_PRIMARY:
756 		send_wl_registry_evt_global(&T, registry, 2,
757 				"zwp_primary_selection_device_manager_v1", 1);
758 		send_wl_registry_req_bind(&T, registry, 2,
759 				"zwp_primary_selection_device_manager_v1", 1,
760 				ddman);
761 		send_zwp_primary_selection_device_manager_v1_req_get_device(
762 				&T, ddman, ddev, seat);
763 		send_zwp_primary_selection_device_manager_v1_req_create_source(
764 				&T, ddman, dsource);
765 		send_zwp_primary_selection_source_v1_req_offer(
766 				&T, dsource, "text/plain;charset=utf-8");
767 		send_zwp_primary_selection_device_v1_req_set_selection(
768 				&T, ddev, dsource, 9999);
769 		send_zwp_primary_selection_source_v1_evt_send(&T, dsource,
770 				"text/plain;charset=utf-8", dst_pipe[0]);
771 		break;
772 	case DDT_WLR:
773 		send_wl_registry_evt_global(&T, registry, 2,
774 				"zwlr_data_control_manager_v1", 1);
775 		send_wl_registry_req_bind(&T, registry, 2,
776 				"zwlr_data_control_manager_v1", 1, ddman);
777 		send_zwlr_data_control_manager_v1_req_get_data_device(
778 				&T, ddman, ddev, seat);
779 		send_zwlr_data_control_manager_v1_req_create_data_source(
780 				&T, ddman, dsource);
781 		send_zwlr_data_control_source_v1_req_offer(
782 				&T, dsource, "text/plain;charset=utf-8");
783 		send_zwlr_data_control_device_v1_req_set_selection(
784 				&T, ddev, dsource);
785 		send_zwlr_data_control_source_v1_evt_send(&T, dsource,
786 				"text/plain;charset=utf-8", dst_pipe[0]);
787 		break;
788 	}
789 	ret_fd = get_only_fd_from_msg(T.app);
790 
791 	/* confirm receipt of fd with the correct contents; if not,
792 	 * reject */
793 	if (ret_fd == -1) {
794 		wp_error("Fd not passed through");
795 		pass = false;
796 		goto end;
797 	}
798 	/* todo: check readable */
799 end:
800 
801 	checked_close(dst_pipe[0]);
802 	checked_close(dst_pipe[1]);
803 	cleanup_tstate(&T);
804 
805 	print_pass(pass);
806 	return pass;
807 }
808 
809 /* Check that gamma_control copies the input file */
test_gamma_control()810 static bool test_gamma_control()
811 {
812 	fprintf(stdout, "\n  Gamma control test\n");
813 	struct transfer_states T;
814 	if (setup_tstate(&T) == -1) {
815 		wp_error("Test setup failed");
816 		return true;
817 	}
818 	bool pass = true;
819 
820 	int ret_fd = -1;
821 
822 	char *testpat = make_filled_pattern(1024, 0x12345678);
823 	int fd = make_filled_file(1024, testpat);
824 
825 	struct wp_objid display = {0x1}, registry = {0x2},
826 			gamma_manager = {0x3}, output = {0x4},
827 			gamma_control = {0x5};
828 
829 	send_wl_display_req_get_registry(&T, display, registry);
830 	send_wl_registry_evt_global(
831 			&T, registry, 1, "zwlr_gamma_control_manager_v1", 1);
832 	send_wl_registry_req_bind(&T, registry, 1,
833 			"zwlr_gamma_control_manager_v1", 1, gamma_manager);
834 	send_wl_registry_evt_global(&T, registry, 1, "wl_output", 3);
835 	send_wl_registry_req_bind(&T, registry, 1, "wl_output", 3, output);
836 	send_zwlr_gamma_control_manager_v1_req_get_gamma_control(
837 			&T, gamma_manager, gamma_control, output);
838 	send_zwlr_gamma_control_v1_evt_gamma_size(&T, gamma_control, 1024);
839 	send_zwlr_gamma_control_v1_req_set_gamma(&T, gamma_control, fd);
840 
841 	ret_fd = get_only_fd_from_msg(T.comp);
842 
843 	/* confirm receipt of fd with the correct contents; if not,
844 	 * reject */
845 	if (ret_fd == -1) {
846 		wp_error("Fd not passed through");
847 		pass = false;
848 		goto end;
849 	}
850 	pass = check_file_contents(ret_fd, 1024, testpat);
851 	if (!pass) {
852 		wp_error("Failed to transfer file");
853 	}
854 end:
855 
856 	free(testpat);
857 	checked_close(fd);
858 	cleanup_tstate(&T);
859 
860 	print_pass(pass);
861 	return pass;
862 }
863 
864 /* Check that gamma_control copies the input file */
test_presentation_time()865 static bool test_presentation_time()
866 {
867 	fprintf(stdout, "\n  Presentation time test\n");
868 	struct transfer_states T;
869 	if (setup_tstate(&T) == -1) {
870 		wp_error("Test setup failed");
871 		return true;
872 	}
873 	bool pass = true;
874 
875 	struct wp_objid display = {0x1}, registry = {0x2}, presentation = {0x3},
876 			compositor = {0x4}, surface = {0x5}, feedback = {0x6};
877 	T.app->local_time_offset = 500;
878 	T.comp->local_time_offset = 600;
879 
880 	send_wl_display_req_get_registry(&T, display, registry);
881 	send_wl_registry_evt_global(&T, registry, 1, "wp_presentation", 1);
882 	send_wl_registry_evt_global(&T, registry, 2, "wl_compositor", 1);
883 	send_wl_registry_req_bind(
884 			&T, registry, 1, "wp_presentation", 1, presentation);
885 	/* todo: run another branch with CLOCK_REALTIME */
886 	send_wp_presentation_evt_clock_id(&T, presentation, CLOCK_MONOTONIC);
887 	send_wl_registry_req_bind(
888 			&T, registry, 12, "wl_compositor", 1, compositor);
889 	send_wl_compositor_req_create_surface(&T, compositor, surface);
890 
891 	send_wl_surface_req_damage(&T, surface, 0, 0, 64, 64);
892 
893 	send_wp_presentation_req_feedback(&T, presentation, surface, feedback);
894 	send_wl_surface_req_commit(&T, surface);
895 	send_wp_presentation_feedback_evt_presented(
896 			&T, feedback, 0, 30, 120000, 16666666, 0, 0, 7);
897 	const struct msg *const last_msg = &T.app->rcvd[T.app->nrcvd - 1];
898 	uint32_t tv_sec_hi = last_msg->data[2], tv_sec_lo = last_msg->data[3],
899 		 tv_nsec = last_msg->data[4];
900 	if (tv_nsec != 120000 + T.app->local_time_offset -
901 					T.comp->local_time_offset) {
902 		wp_error("Time translation failed %d %d %d", tv_sec_hi,
903 				tv_sec_lo, tv_nsec);
904 		pass = false;
905 		goto end;
906 	}
907 
908 	/* look at timestamp */
909 	if (!pass) {
910 		goto end;
911 	}
912 end:
913 	cleanup_tstate(&T);
914 
915 	print_pass(pass);
916 	return pass;
917 }
918 
919 /* Check whether the video encoding feature can replicate a uniform
920  * color image */
test_fixed_video_color_copy(enum video_coding_fmt fmt,bool hw)921 static bool test_fixed_video_color_copy(enum video_coding_fmt fmt, bool hw)
922 {
923 	(void)fmt;
924 	(void)hw;
925 	/* todo: back out if no dmabuf support or no video support */
926 	return true;
927 }
928 
929 log_handler_func_t log_funcs[2] = {test_log_handler, test_log_handler};
main(int argc,char ** argv)930 int main(int argc, char **argv)
931 {
932 	(void)argc;
933 	(void)argv;
934 
935 	set_initial_fds();
936 
937 	int ntest = 20;
938 	int nsuccess = 0;
939 	nsuccess += test_fixed_shm_buffer_copy();
940 	nsuccess += test_fixed_shm_screencopy_copy();
941 	nsuccess += test_fixed_keymap_copy();
942 	nsuccess += test_fixed_dmabuf_copy(COPY_LINUX_DMABUF);
943 	nsuccess += test_fixed_dmabuf_copy(COPY_LINUX_DMABUF_INDIR);
944 	nsuccess += test_fixed_dmabuf_copy(COPY_DRM_PRIME);
945 	nsuccess += test_fixed_dmabuf_copy(COPY_WLR_EXPORT);
946 	nsuccess += test_data_offer(DDT_WAYLAND);
947 	nsuccess += test_data_offer(DDT_PRIMARY);
948 	nsuccess += test_data_offer(DDT_GTK_PRIMARY);
949 	nsuccess += test_data_offer(DDT_WLR);
950 	nsuccess += test_data_source(DDT_WAYLAND);
951 	nsuccess += test_data_source(DDT_PRIMARY);
952 	nsuccess += test_data_source(DDT_GTK_PRIMARY);
953 	nsuccess += test_data_source(DDT_WLR);
954 	nsuccess += test_gamma_control();
955 	nsuccess += test_presentation_time();
956 	nsuccess += test_fixed_video_color_copy(VIDEO_H264, false);
957 	nsuccess += test_fixed_video_color_copy(VIDEO_H264, true);
958 	nsuccess += test_fixed_video_color_copy(VIDEO_VP9, false);
959 	// TODO: add tests for handling of common errors, e.g. invalid fd,
960 	// or type confusion
961 
962 	fprintf(stdout, "\n%d of %d cases passed\n", nsuccess, ntest);
963 
964 	check_unclosed_fds();
965 
966 	return (nsuccess == ntest) ? EXIT_SUCCESS : EXIT_FAILURE;
967 }
968