1 /* Copyright © 2015 Uli Schlachter
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a
4  * copy of this software and associated documentation files (the "Software"),
5  * to deal in the Software without restriction, including without limitation
6  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7  * and/or sell copies of the Software, and to permit persons to whom the
8  * Software is furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
17  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  *
20  * Except as contained in this notice, the names of the authors or their
21  * institutions shall not be used in advertising or otherwise to promote the
22  * sale, use or other dealings in this Software without prior written
23  * authorization from the authors.
24  */
25 
26 #include "xcb_errors.h"
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #if defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 203)
33 #define ATTRIBUTE_PRINTF(x,y) __attribute__((__format__(__printf__,x,y)))
34 #else
35 #define ATTRIBUTE_PRINTF(x,y)
36 #endif
37 
38 #define SKIP 77
39 
40 static int check_strings(const char *expected, const char *actual,
41 		const char *format, ...) ATTRIBUTE_PRINTF(3, 4);
42 
check_strings(const char * expected,const char * actual,const char * format,...)43 static int check_strings(const char *expected, const char *actual,
44 		const char *format, ...)
45 {
46 	va_list ap;
47 
48 	if (expected == NULL && actual == NULL)
49 		return 0;
50 	if (expected != NULL && actual != NULL && strcmp(expected, actual) == 0)
51 		return 0;
52 
53 	va_start(ap, format);
54 	vfprintf(stderr, format, ap);
55 	va_end(ap);
56 	return 1;
57 }
58 
check_request(xcb_errors_context_t * ctx,uint8_t opcode,const char * expected)59 static int check_request(xcb_errors_context_t *ctx, uint8_t opcode, const char *expected)
60 {
61 	const char *actual = xcb_errors_get_name_for_major_code(ctx, opcode);
62 	return check_strings(expected, actual, "For opcode %d: Expected %s, got %s\n",
63 			opcode, expected, actual);
64 }
65 
check_error(xcb_errors_context_t * ctx,uint8_t error,const char * expected,const char * expected_extension)66 static int check_error(xcb_errors_context_t *ctx, uint8_t error,
67 		const char *expected, const char *expected_extension)
68 {
69 	const char *actual, *actual_extension, *tmp;
70 	int ret = 0;
71 
72 	actual = xcb_errors_get_name_for_error(ctx, error, &actual_extension);
73 	ret |= check_strings(expected_extension, actual_extension,
74 			"For error %d: Expected ext %s, got %s\n",
75 			error, expected_extension, actual_extension);
76 	ret |= check_strings(expected, actual,
77 			"For error %d: Expected %s, got %s\n",
78 			error, expected, actual);
79 
80 	tmp = xcb_errors_get_name_for_error(ctx, error, NULL);
81 	ret |= check_strings(actual, tmp,
82 			"For error %d: Passing NULL made a difference: %s vs %s\n",
83 			error, actual, tmp);
84 	return ret;
85 }
86 
check_event(xcb_errors_context_t * ctx,uint8_t event,const char * expected,const char * expected_extension)87 static int check_event(xcb_errors_context_t *ctx, uint8_t event,
88 		const char *expected, const char *expected_extension)
89 {
90 	const char *actual, *actual_extension, *tmp;
91 	int ret = 0;
92 
93 	actual = xcb_errors_get_name_for_core_event(ctx, event, &actual_extension);
94 	ret |= check_strings(expected_extension, actual_extension,
95 			"For event %d: Expected ext %s, got %s\n",
96 			event, expected_extension, actual_extension);
97 	ret |= check_strings(expected, actual,
98 			"For event %d: Expected %s, got %s\n",
99 			event, expected, actual);
100 
101 	tmp = xcb_errors_get_name_for_core_event(ctx, event, NULL);
102 	ret |= check_strings(actual, tmp,
103 			"For event %d: Passing NULL made a difference: %s vs %s\n",
104 			event, actual, tmp);
105 
106 	tmp = xcb_errors_get_name_for_core_event(ctx, event | 0x80, NULL);
107 	ret |= check_strings(expected, tmp,
108 			"For event %d|0x80: Expected %s, got %s\n",
109 			event, expected, tmp);
110 
111 	/* The wire_event we construct isn't a proper GE event */
112 	if (event != XCB_GE_GENERIC) {
113 		xcb_generic_event_t wire_event = {
114 			.response_type = event
115 		};
116 
117 		actual = xcb_errors_get_name_for_xcb_event(ctx, &wire_event, &actual_extension);
118 		ret |= check_strings(expected_extension, actual_extension,
119 				"For xcb wire event %d: Expected ext %s, got %s\n",
120 				event, expected_extension, actual_extension);
121 		ret |= check_strings(expected, actual,
122 				"For xcb wire event %d: Expected %s, got %s\n",
123 				event, expected, actual);
124 
125 		tmp = xcb_errors_get_name_for_xcb_event(ctx, &wire_event, NULL);
126 		ret |= check_strings(actual, tmp,
127 				"For xcb wire event %d: Passing NULL made a difference: %s vs %s\n",
128 				event, actual, tmp);
129 
130 		wire_event.response_type |= 0x80;
131 		tmp = xcb_errors_get_name_for_xcb_event(ctx, &wire_event, NULL);
132 		ret |= check_strings(expected, tmp,
133 				"For xcb wire event %d|0x80: Expected %s, got %s\n",
134 				event, expected, tmp);
135 	}
136 	return ret;
137 }
138 
check_xge_event(xcb_errors_context_t * ctx,uint8_t major_code,uint16_t event_type,const char * expected,const char * expected_extension)139 static int check_xge_event(xcb_errors_context_t *ctx, uint8_t major_code,
140 		uint16_t event_type, const char *expected, const char *expected_extension)
141 {
142 	xcb_ge_generic_event_t wire_event = {
143 		.response_type = XCB_GE_GENERIC,
144 		.extension = major_code,
145 		.event_type = event_type
146 	};
147 	const char *actual, *actual_extension, *tmp;
148 	int ret = 0;
149 
150 	actual = xcb_errors_get_name_for_xge_event(ctx, major_code, event_type);
151 	ret |= check_strings(expected, actual,
152 			"For xge event (%d, %d): Expected %s, got %s\n",
153 			major_code, event_type, expected, actual);
154 
155 	actual = xcb_errors_get_name_for_xcb_event(ctx, (void *) &wire_event, &actual_extension);
156 	ret |= check_strings(expected_extension, actual_extension,
157 			"For xcb xge wire event %d: Expected ext %s, got %s\n",
158 			event_type, expected_extension, actual_extension);
159 	ret |= check_strings(expected, actual,
160 			"For xcb xge wire event %d: Expected %s, got %s\n",
161 			event_type, expected, actual);
162 
163 	tmp = xcb_errors_get_name_for_xcb_event(ctx, (void *) &wire_event, NULL);
164 	ret |= check_strings(actual, tmp,
165 			"For xcb xge wire event %d: Passing NULL made a difference: %s vs %s\n",
166 			event_type, actual, tmp);
167 	return ret;
168 }
169 
check_xkb_event(xcb_errors_context_t * ctx,uint8_t major_code,uint8_t first_event,uint16_t event_type,const char * expected)170 static int check_xkb_event(xcb_errors_context_t *ctx, uint8_t major_code, uint8_t first_event,
171 		uint16_t event_type, const char *expected)
172 {
173 	xcb_generic_event_t wire_event = {
174 		.response_type = first_event,
175 		.pad0 = event_type
176 	};
177 	const char *actual, *actual_extension, *tmp;
178 	int ret = 0;
179 
180 	actual = xcb_errors_get_name_for_xge_event(ctx, major_code, event_type);
181 	ret |= check_strings(expected, actual,
182 			"For xkb event (%d, %d): Expected %s, got %s\n",
183 			major_code, event_type, expected, actual);
184 
185 	actual = xcb_errors_get_name_for_xcb_event(ctx, &wire_event, &actual_extension);
186 	ret |= check_strings("xkb", actual_extension,
187 			"For xcb xkb wire event %d: Expected ext xkb, got %s\n",
188 			event_type, actual_extension);
189 	ret |= check_strings(expected, actual,
190 			"For xcb xkb wire event %d: Expected %s, got %s\n",
191 			event_type, expected, actual);
192 
193 	tmp = xcb_errors_get_name_for_xcb_event(ctx, &wire_event, NULL);
194 	ret |= check_strings(actual, tmp,
195 			"For xcb xkb wire event %d: Passing NULL made a difference: %s vs %s\n",
196 			event_type, actual, tmp);
197 	return ret;
198 }
199 
check_minor(xcb_errors_context_t * ctx,uint8_t major,uint16_t minor,const char * expected)200 static int check_minor(xcb_errors_context_t *ctx, uint8_t major, uint16_t minor, const char *expected)
201 {
202 	const char *actual = xcb_errors_get_name_for_minor_code(ctx, major, minor);
203 	return check_strings(expected, actual, "For minor (%d, %d): Expected %s, got %s\n",
204 			major, minor, expected, actual);
205 }
206 
test_error_connection(void)207 static int test_error_connection(void)
208 {
209 	xcb_errors_context_t *ctx;
210 	xcb_connection_t *c;
211 	int err = 0;
212 
213 	c = xcb_connect("does-not-exist", NULL);
214 	if (!xcb_connection_has_error(c)) {
215 		fprintf(stderr, "Failed to create an error connection\n");
216 		err = 1;
217 	}
218 
219 	if (xcb_errors_context_new(c, &ctx) == 0) {
220 		fprintf(stderr, "Successfully created context for error connection\n");
221 		err = 1;
222 	}
223 
224 	xcb_errors_context_free(ctx);
225 	xcb_disconnect(c);
226 
227 	return err;
228 }
229 
test_randr(xcb_connection_t * c,xcb_errors_context_t * ctx)230 static int test_randr(xcb_connection_t *c, xcb_errors_context_t *ctx)
231 {
232 	struct xcb_query_extension_reply_t *reply;
233 	int err = 0;
234 
235 	reply = xcb_query_extension_reply(c,
236 			xcb_query_extension(c, strlen("RANDR"), "RANDR"), NULL);
237 	if (!reply || !reply->present) {
238 		fprintf(stderr, "RANDR not supported by display\n");
239 		free(reply);
240 		return SKIP;
241 	}
242 
243 	err |= check_request(ctx, reply->major_opcode, "RandR");
244 	err |= check_error(ctx, reply->first_error + 0, "BadOutput", "RandR");
245 	err |= check_error(ctx, reply->first_error + 3, "BadProvider", "RandR");
246 	err |= check_event(ctx, reply->first_event + 0, "ScreenChangeNotify", "RandR");
247 	err |= check_event(ctx, reply->first_event + 1, "Notify", "RandR");
248 	err |= check_minor(ctx, reply->major_opcode, 0, "QueryVersion");
249 	err |= check_minor(ctx, reply->major_opcode, 1, "Unknown (1)");
250 	err |= check_minor(ctx, reply->major_opcode, 33, "GetProviderInfo");
251 	err |= check_minor(ctx, reply->major_opcode, 41, "GetProviderProperty");
252 	err |= check_minor(ctx, reply->major_opcode, 1337, NULL);
253 	err |= check_minor(ctx, reply->major_opcode, 0xffff, NULL);
254 
255 	free(reply);
256 	return err;
257 }
258 
test_xfixes(xcb_connection_t * c,xcb_errors_context_t * ctx)259 static int test_xfixes(xcb_connection_t *c, xcb_errors_context_t *ctx)
260 {
261 	struct xcb_query_extension_reply_t *reply;
262 	int err = 0;
263 
264 	reply = xcb_query_extension_reply(c,
265 			xcb_query_extension(c, strlen("XFIXES"), "XFIXES"), NULL);
266 	if (!reply || !reply->present) {
267 		fprintf(stderr, "XFIXES not supported by display\n");
268 		free(reply);
269 		return SKIP;
270 	}
271 
272 	err |= check_request(ctx, reply->major_opcode, "XFixes");
273 	err |= check_error(ctx, reply->first_error + 0, "BadRegion", "XFixes");
274 	err |= check_event(ctx, reply->first_event + 0, "SelectionNotify", "XFixes");
275 	err |= check_event(ctx, reply->first_event + 1, "CursorNotify", "XFixes");
276 	err |= check_minor(ctx, reply->major_opcode, 0, "QueryVersion");
277 	err |= check_minor(ctx, reply->major_opcode, 32, "DeletePointerBarrier");
278 	err |= check_minor(ctx, reply->major_opcode, 1337, NULL);
279 	err |= check_minor(ctx, reply->major_opcode, 0xffff, NULL);
280 
281 	free(reply);
282 	return err;
283 }
284 
test_xinput(xcb_connection_t * c,xcb_errors_context_t * ctx)285 static int test_xinput(xcb_connection_t *c, xcb_errors_context_t *ctx)
286 {
287 	struct xcb_query_extension_reply_t *reply;
288 	int err = 0;
289 
290 	reply = xcb_query_extension_reply(c,
291 			xcb_query_extension(c, strlen("XInputExtension"), "XInputExtension"), NULL);
292 	if (!reply || !reply->present) {
293 		fprintf(stderr, "XInputExtension not supported by display\n");
294 		free(reply);
295 		return SKIP;
296 	}
297 
298 	err |= check_request(ctx, reply->major_opcode, "Input");
299 	err |= check_error(ctx, reply->first_error + 0, "Device", "Input");
300 	err |= check_error(ctx, reply->first_error + 4, "Class", "Input");
301 	err |= check_event(ctx, reply->first_event + 0, "DeviceValuator", "Input");
302 	err |= check_event(ctx, reply->first_event + 16, "DevicePropertyNotify", "Input");
303 	err |= check_xge_event(ctx, reply->major_opcode, 0, "Unknown (0)", "Input");
304 	err |= check_xge_event(ctx, reply->major_opcode, 1, "DeviceChanged", "Input");
305 	err |= check_xge_event(ctx, reply->major_opcode, 26, "BarrierLeave", "Input");
306 	err |= check_xge_event(ctx, reply->major_opcode, 27, NULL, "Input");
307 	err |= check_xge_event(ctx, reply->major_opcode, 1337, NULL, "Input");
308 	err |= check_xge_event(ctx, reply->major_opcode, 0xffff, NULL, "Input");
309 	err |= check_minor(ctx, reply->major_opcode, 0, "Unknown (0)");
310 	err |= check_minor(ctx, reply->major_opcode, 1, "GetExtensionVersion");
311 	err |= check_minor(ctx, reply->major_opcode, 47, "XIQueryVersion");
312 	err |= check_minor(ctx, reply->major_opcode, 61, "XIBarrierReleasePointer");
313 	err |= check_minor(ctx, reply->major_opcode, 62, NULL);
314 	err |= check_minor(ctx, reply->major_opcode, 1337, NULL);
315 	err |= check_minor(ctx, reply->major_opcode, 0xffff, NULL);
316 
317 	free(reply);
318 	return err;
319 }
320 
test_xkb(xcb_connection_t * c,xcb_errors_context_t * ctx)321 static int test_xkb(xcb_connection_t *c, xcb_errors_context_t *ctx)
322 {
323 	struct xcb_query_extension_reply_t *reply;
324 	int err = 0;
325 
326 	reply = xcb_query_extension_reply(c,
327 			xcb_query_extension(c, strlen("XKEYBOARD"), "XKEYBOARD"), NULL);
328 	if (!reply || !reply->present) {
329 		fprintf(stderr, "XKB not supported by display\n");
330 		free(reply);
331 		return SKIP;
332 	}
333 
334 	err |= check_request(ctx, reply->major_opcode, "xkb");
335 	err |= check_error(ctx, reply->first_error + 0, "Keyboard", "xkb");
336 	err |= check_xkb_event(ctx, reply->major_opcode, reply->first_event, 0, "NewKeyboardNotify");
337 	err |= check_xkb_event(ctx, reply->major_opcode, reply->first_event, 1, "MapNotify");
338 	err |= check_xkb_event(ctx, reply->major_opcode, reply->first_event, 11, "ExtensionDeviceNotify");
339 	err |= check_xkb_event(ctx, reply->major_opcode, reply->first_event, 12, NULL);
340 	err |= check_xkb_event(ctx, reply->major_opcode, reply->first_event, 1337, NULL);
341 	err |= check_xkb_event(ctx, reply->major_opcode, reply->first_event, 0xffff, NULL);
342 
343 	free(reply);
344 	return err;
345 }
346 
test_valid_connection(void)347 static int test_valid_connection(void)
348 {
349 	xcb_errors_context_t *ctx;
350 	xcb_connection_t *c;
351 	int err = 0;
352 
353 	c = xcb_connect(NULL, NULL);
354 	if (xcb_connection_has_error(c)) {
355 		fprintf(stderr, "Failed to connect to X11 server (%d)\n",
356 				xcb_connection_has_error(c));
357 		return SKIP;
358 	}
359 	if (xcb_errors_context_new(c, &ctx) < 0) {
360 		fprintf(stderr, "Failed to initialize util-errors\n");
361 		xcb_disconnect(c);
362 		return 1;
363 	}
364 
365 	err |= check_request(ctx, XCB_CREATE_WINDOW, "CreateWindow");
366 	err |= check_request(ctx, XCB_NO_OPERATION, "NoOperation");
367 	err |= check_request(ctx, 126, "Unknown (126)");
368 	err |= check_request(ctx, 0xff, "Unknown (255)");
369 	err |= check_minor(ctx, XCB_CREATE_WINDOW, 0, NULL);
370 	err |= check_minor(ctx, XCB_CREATE_WINDOW, 42, NULL);
371 	err |= check_minor(ctx, XCB_CREATE_WINDOW, 0xffff, NULL);
372 
373 	err |= check_error(ctx, XCB_REQUEST, "Request", NULL);
374 	err |= check_error(ctx, XCB_IMPLEMENTATION, "Implementation", NULL);
375 	err |= check_error(ctx, 18, "Unknown (18)", NULL);
376 	err |= check_error(ctx, 127, "Unknown (127)", NULL);
377 	err |= check_error(ctx, 0xff, "Unknown (255)", NULL);
378 
379 	err |= check_event(ctx, XCB_KEY_PRESS, "KeyPress", NULL);
380 	err |= check_event(ctx, XCB_KEY_RELEASE, "KeyRelease", NULL);
381 	err |= check_event(ctx, XCB_GE_GENERIC, "GeGeneric", NULL);
382 	err |= check_event(ctx, 36, "Unknown (36)", NULL);
383 	err |= check_event(ctx, 127, "Unknown (127)", NULL);
384 
385 	err |= test_randr(c, ctx);
386 	err |= test_xinput(c, ctx);
387 	err |= test_xkb(c, ctx);
388 	err |= test_xfixes(c, ctx);
389 
390 	xcb_errors_context_free(ctx);
391 	xcb_disconnect(c);
392 	return err;
393 }
394 
test_NULL_context(void)395 static int test_NULL_context(void)
396 {
397 	int err = 0;
398 	const char *msg = "xcb-errors API misuse: context argument is NULL";
399 
400 	xcb_errors_context_free(NULL);
401 	err |= check_strings(msg, xcb_errors_get_name_for_major_code(NULL, 0),
402 			"xcb_errors_get_name_for_major_code(NULL, 0) does not behave correctly");
403 	err |= check_strings(msg, xcb_errors_get_name_for_minor_code(NULL, 0, 0),
404 			"xcb_errors_get_name_for_minor_code(NULL, 0, 0) does not behave correctly");
405 	err |= check_strings(msg, xcb_errors_get_name_for_core_event(NULL, 0, NULL),
406 			"xcb_errors_get_name_for_core_event(NULL, 0, NULL) does not behave correctly");
407 	err |= check_strings(msg, xcb_errors_get_name_for_xge_event(NULL, 0, 0),
408 			"xcb_errors_get_name_for_xge_event(NULL, 0, 0) does not behave correctly");
409 	err |= check_strings(msg, xcb_errors_get_name_for_xcb_event(NULL, NULL, NULL),
410 			"xcb_errors_get_name_for_xcb_event(NULL, NULL, NULL) does not behave correctly");
411 	err |= check_strings(msg, xcb_errors_get_name_for_error(NULL, 0, NULL),
412 			"xcb_errors_get_name_for_xcb_error(NULL, 0, NULL) does not behave correctly");
413 
414 	return err;
415 }
416 
main(void)417 int main(void)
418 {
419 	int err = 0;
420 	err |= test_error_connection();
421 	err |= test_valid_connection();
422 	err |= test_NULL_context();
423 	return err;
424 }
425