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