• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
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 <stdlib.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <sys/un.h>
32 #include <time.h>
33 #include <unistd.h>
34 
35 #include "wayland-client.h"
36 #include "wayland-server.h"
37 #include "wayland-util.h"
38 #include "test-runner.h"
39 
40 /* Ensure the connection doesn't fail due to lack of XDG_RUNTIME_DIR. */
41 static const char *
require_xdg_runtime_dir(void)42 require_xdg_runtime_dir(void)
43 {
44 	char *val = getenv("XDG_RUNTIME_DIR");
45 	assert(val && "set $XDG_RUNTIME_DIR to run this test");
46 
47 	return val;
48 }
49 
50 struct compositor {
51 	struct wl_display *display;
52 	struct wl_event_loop *loop;
53 	int message;
54 	struct wl_client *client;
55 };
56 
57 struct client {
58 	struct wl_display *display;
59 	struct wl_callback *cb;
60         int message;
61 };
62 
63 struct message {
64 	enum wl_protocol_logger_type type;
65 	const char *class;
66 	int opcode;
67 	const char *message_name;
68 	int args_count;
69 } messages[] = {
70 	{
71 		.type = WL_PROTOCOL_LOGGER_REQUEST,
72 		.class = "wl_display",
73 		.opcode = 0,
74 		.message_name = "sync",
75 		.args_count = 1,
76 	},
77 	{
78 		.type = WL_PROTOCOL_LOGGER_EVENT,
79 		.class = "wl_callback",
80 		.opcode = 0,
81 		.message_name = "done",
82 		.args_count = 1,
83 	},
84 	{
85 		.type = WL_PROTOCOL_LOGGER_EVENT,
86 		.class = "wl_display",
87 		.opcode = 1,
88 		.message_name = "delete_id",
89 		.args_count = 1,
90 	},
91 };
92 
93 struct client_message {
94 	enum wl_protocol_logger_client_type type;
95 	const char *class;
96 	int opcode;
97 	const char *message_name;
98 	int args_count;
99 } client_messages[] = {
100 	{
101 		.type = WL_PROTOCOL_LOGGER_CLIENT_REQUEST,
102 		.class = "wl_display",
103 		.opcode = 0,
104 		.message_name = "sync",
105 		.args_count = 1,
106 	},
107 	{
108 		.type = WL_PROTOCOL_LOGGER_CLIENT_EVENT,
109 		.class = "wl_display",
110 		.opcode = 1,
111 		.message_name = "delete_id",
112 		.args_count = 1,
113 	},
114 	{
115 		.type = WL_PROTOCOL_LOGGER_CLIENT_EVENT,
116 		.class = "wl_callback",
117 		.opcode = 0,
118 		.message_name = "done",
119 		.args_count = 1,
120 	},
121 };
122 
123 static void
logger_func(void * user_data,enum wl_protocol_logger_type type,const struct wl_protocol_logger_message * message)124 logger_func(void *user_data, enum wl_protocol_logger_type type,
125 	    const struct wl_protocol_logger_message *message)
126 {
127 	struct compositor *c = user_data;
128 	struct message *msg = &messages[c->message++];
129 
130 	assert(msg->type == type);
131 	assert(strcmp(msg->class, wl_resource_get_class(message->resource)) == 0);
132 	assert(msg->opcode == message->message_opcode);
133 	assert(strcmp(msg->message_name, message->message->name) == 0);
134 	assert(msg->args_count == message->arguments_count);
135 
136 	c->client = wl_resource_get_client(message->resource);
137 }
138 
139 static void
client_logger_func(void * user_data,enum wl_protocol_logger_client_type type,const struct wl_protocol_logger_client_message * message)140 client_logger_func(void *user_data, enum wl_protocol_logger_client_type type,
141 		const struct wl_protocol_logger_client_message *message)
142 {
143 	struct client *c = user_data;
144 	struct client_message *msg = &client_messages[c->message++];
145 
146 	assert(msg->type == type);
147 	assert(strcmp(msg->class, wl_proxy_get_class(message->proxy)) == 0);
148 	assert(msg->opcode == message->message_opcode);
149 	assert(strcmp(msg->message_name, message->message->name) == 0);
150 	assert(msg->args_count == message->arguments_count);
151 }
152 
153 // A slightly simplified version of  get_next_argument() from src/connection.c
154 static const char*
get_next_argument_type(const char * signature,char * type)155 get_next_argument_type(const char *signature, char* type)
156 {
157 	for (; *signature; ++signature) {
158 		assert(strchr("iufsonah?", *signature) != NULL);
159 		switch (*signature) {
160 		case 'i':
161 		case 'u':
162 		case 'f':
163 		case 's':
164 		case 'o':
165 		case 'n':
166 		case 'a':
167 		case 'h':
168 			*type = *signature;
169 			return signature + 1;
170 		case '?':
171 			break;
172 
173 		}
174 	}
175 	*type = 0;
176 	return signature;
177 }
178 
179 // This duplicates what the internal wl_closure_print function does, and can be
180 // used as a starting point for a client or server that wants to log messages.
181 static void
client_log_to_stderr_demo(void * user_data,enum wl_protocol_logger_client_type type,const struct wl_protocol_logger_client_message * message)182 client_log_to_stderr_demo(void *user_data,
183 			  enum wl_protocol_logger_client_type type,
184 			  const struct wl_protocol_logger_client_message *message) {
185 	int i;
186 	char arg_type;
187 	const char *signature = message->message->signature;
188 	const union wl_argument* args = message->arguments;
189 	struct wl_proxy* arg_proxy;
190 	const char* arg_class;
191 	struct timespec tp;
192 	unsigned int time;
193 
194 	clock_gettime(CLOCK_REALTIME, &tp);
195 	time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
196 
197 	// Note: server logger will be given message->resource, and should
198 	// use wl_resource_get_class and wl_resolurce_get_id.
199 	fprintf(stderr, "[%10.3f] %s%s@%u.%s(",
200 		time / 1000.0,
201 		(type == WL_PROTOCOL_LOGGER_CLIENT_REQUEST) ? " -> " : "",
202 		wl_proxy_get_class(message->proxy), wl_proxy_get_id(message->proxy),
203 		message->message->name);
204 
205 	for (i = 0; i < message->arguments_count; i++) {
206 		signature = get_next_argument_type(signature, &arg_type);
207 		if (i > 0)
208 			fprintf(stderr, ", ");
209 
210 		switch (arg_type) {
211 		case 'u':
212 			fprintf(stderr, "%u", args[i].u);
213 			break;
214 		case 'i':
215 			fprintf(stderr, "%d", args[i].i);
216 			break;
217 		case 'f':
218 			fprintf(stderr, "%f", wl_fixed_to_double(args[i].f));
219 			break;
220 		case 's':
221 			if (args[i].s)
222 				fprintf(stderr, "\"%s\"", args[i].s);
223 			else
224 				fprintf(stderr, "nil");
225 			break;
226 		case 'o':
227 			if (args[i].o) {
228 				// Note: server logger should instead use
229 				// wl_resource_from_object, and then
230 				// wl_resource_get_class and wl_resource_get_id.
231 				arg_proxy = wl_proxy_from_object(args[i].o);
232 				arg_class = wl_proxy_get_class(arg_proxy);
233 
234 				fprintf(stderr, "%s@%u",
235 					arg_class ? arg_class : "[unknown]",
236 					wl_proxy_get_id(arg_proxy));
237 			} else {
238 				fprintf(stderr, "nil");
239 			}
240 			break;
241 		case 'n':
242 			fprintf(stderr, "new id %s@",
243 				  (message->message->types[i]) ?
244 				   message->message->types[i]->name :
245 				    "[unknown]");
246 			if (args[i].n != 0)
247 				fprintf(stderr, "%u", args[i].n);
248 			else
249 				fprintf(stderr, "nil");
250 			break;
251 		case 'a':
252 			fprintf(stderr, "array");
253 			break;
254 		case 'h':
255 			fprintf(stderr, "fd %d", args[i].h);
256 			break;
257 		}
258 	}
259 
260 	fprintf(stderr, ")\n");
261 }
262 
263 static void
callback_done(void * data,struct wl_callback * cb,uint32_t time)264 callback_done(void *data, struct wl_callback *cb, uint32_t time)
265 {
266 	wl_callback_destroy(cb);
267 }
268 
269 static const struct wl_callback_listener callback_listener = {
270 	callback_done,
271 };
272 
TEST(logger)273 TEST(logger)
274 {
275 	test_set_timeout(1);
276 
277 	const char *socket;
278 	struct compositor compositor = { 0 };
279 	struct client client = { 0 };
280 	struct wl_protocol_logger *logger;
281 	struct wl_protocol_logger_client *logger_client;
282 	struct wl_protocol_logger_client *logger_client_demo;
283 
284 	require_xdg_runtime_dir();
285 
286 	compositor.display = wl_display_create();
287 	compositor.loop = wl_display_get_event_loop(compositor.display);
288 	socket = wl_display_add_socket_auto(compositor.display);
289 
290 	logger = wl_display_add_protocol_logger(compositor.display,
291 						logger_func, &compositor);
292 
293 	client.display = wl_display_connect(socket);
294 	logger_client = wl_display_add_protocol_logger_client(
295 		client.display, client_logger_func, &client);
296 	logger_client_demo = wl_display_add_protocol_logger_client(
297 		client.display, client_log_to_stderr_demo, &client);
298 	client.cb = wl_display_sync(client.display);
299 	wl_callback_add_listener(client.cb, &callback_listener, NULL);
300 	wl_display_flush(client.display);
301 
302 	while (compositor.message < 3) {
303 		wl_event_loop_dispatch(compositor.loop, -1);
304 		wl_display_flush_clients(compositor.display);
305 	}
306 
307 	wl_display_dispatch(client.display);
308 	wl_display_disconnect(client.display);
309 
310 	wl_protocol_logger_client_destroy(logger_client);
311 	wl_protocol_logger_client_destroy(logger_client_demo);
312 	wl_client_destroy(compositor.client);
313 	wl_protocol_logger_destroy(logger);
314 	wl_display_destroy(compositor.display);
315 }
316