1 /*
2 * Copyright ?? 2012 Red Hat, Inc.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of Red Hat
9 * not be used in advertising or publicity pertaining to distribution
10 * of the software without specific, written prior permission. Red
11 * Hat makes no representations about the suitability of this software
12 * for any purpose. It is provided "as is" without express or implied
13 * warranty.
14 *
15 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Authors:
24 * Olivier Fourdan <ofourdan@redhat.com>
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35
36 #include <libxml/parser.h>
37 #include <libxml/xmlmemory.h>
38 #include <glib.h>
39 #include "libwacom.h"
40
41 static xmlNodePtr
verify_has_sub(xmlNodePtr cur,char * sub)42 verify_has_sub (xmlNodePtr cur, char *sub)
43 {
44 cur = cur->xmlChildrenNode;
45 while (cur != NULL) {
46 xmlChar *prop;
47
48 /* Descend the tree if dealing with a group */
49 if (xmlStrcmp(cur->name, (const xmlChar *) "g") == 0) {
50 xmlNodePtr sub_node;
51 sub_node = verify_has_sub (cur, sub);
52 if (sub_node != NULL)
53 return sub_node;
54 }
55
56 prop = xmlGetProp(cur, (xmlChar *) "id");
57 if (prop) {
58 int status = xmlStrcmp(prop, (const xmlChar *) sub);
59 xmlFree(prop);
60 if (status == 0)
61 return cur;
62 }
63 cur = cur->next;
64 }
65
66 return NULL;
67 }
68
69 static gboolean
class_found(gchar ** classes,gchar * value)70 class_found (gchar **classes, gchar *value)
71 {
72 gchar **ptr = classes;
73 while (*ptr) {
74 if (strcmp (*ptr++, value) == 0)
75 return TRUE;
76 }
77
78 return FALSE;
79 }
80
81 static void
verify_has_class(xmlNodePtr cur,const gchar * expected)82 verify_has_class (xmlNodePtr cur, const gchar *expected)
83 {
84
85 xmlChar *prop;
86 gchar **classes_present;
87 gchar **classes_expected;
88 gchar **ptr;
89
90 prop = xmlGetProp (cur, (xmlChar *) "class");
91 g_assert (prop != NULL);
92 g_assert (strlen((const char *) prop) > 0);
93
94 classes_present = g_strsplit ((const gchar *) prop, " ", -1);
95 classes_expected = g_strsplit (expected, " ", -1);
96 ptr = classes_expected;
97
98 while (*ptr)
99 g_assert (class_found (classes_present, *ptr++));
100
101 g_strfreev (classes_present);
102 g_strfreev (classes_expected);
103 xmlFree (prop);
104 }
105
106 static void
check_button(xmlNodePtr cur,const WacomDevice * device,char button,gchar * type)107 check_button (xmlNodePtr cur, const WacomDevice *device, char button, gchar *type)
108 {
109 char *sub;
110 char *class;
111 xmlNodePtr node;
112 WacomButtonFlags flags;
113
114 /* Check ID */
115 sub = g_strdup_printf ("%s%c", type, button);
116 node = verify_has_sub (cur, sub);
117 g_assert (node != NULL);
118 g_free (sub);
119
120 /* Check class */
121 flags = libwacom_get_button_flag(device, button);
122 if (flags & WACOM_BUTTON_MODESWITCH)
123 class = g_strdup_printf ("%c ModeSwitch %s", button, type);
124 else
125 class = g_strdup_printf ("%c %s", button, type);
126 verify_has_class (node, class);
127 g_free (class);
128 }
129
130 static void
check_touchstrip(xmlNodePtr cur,gchar * id)131 check_touchstrip (xmlNodePtr cur, gchar *id)
132 {
133 char *sub;
134 char *class;
135 xmlNodePtr node;
136
137 node = verify_has_sub (cur, id);
138 g_assert (node != NULL);
139
140 class = g_strdup_printf ("%s %s", id, "TouchStrip");
141 verify_has_class (node, class);
142 g_free (class);
143
144 sub = g_strdup_printf ("Label%sUp", id);
145 node = verify_has_sub (cur, sub);
146 g_assert (node != NULL);
147 g_free (sub);
148
149 class = g_strdup_printf ("%sUp %s Label", id, id);
150 verify_has_class (node, class);
151 g_free (class);
152
153 sub = g_strdup_printf ("Label%sDown", id);
154 node = verify_has_sub (cur, sub);
155 g_assert (node != NULL);
156 g_free (sub);
157
158 class = g_strdup_printf ("%sDown %s Label", id, id);
159 verify_has_class (node, class);
160 g_free (class);
161
162 sub = g_strdup_printf ("Leader%sUp", id);
163 node = verify_has_sub (cur, sub);
164 g_assert (node != NULL);
165 g_free (sub);
166
167 class = g_strdup_printf ("%sUp %s Leader", id, id);
168 verify_has_class (node, class);
169 g_free (class);
170
171 sub = g_strdup_printf ("Leader%sDown", id);
172 node = verify_has_sub (cur, sub);
173 g_assert (node != NULL);
174 g_free (sub);
175
176 class = g_strdup_printf ("%sDown %s Leader", id, id);
177 verify_has_class (node, class);
178 g_free (class);
179 }
180
181
182 static void
check_touchring(xmlNodePtr cur,gchar * id)183 check_touchring (xmlNodePtr cur, gchar *id)
184 {
185 char *sub;
186 char *class;
187 xmlNodePtr node;
188
189 node = verify_has_sub (cur, id);
190 g_assert (node != NULL);
191
192 class = g_strdup_printf ("%s %s", id, "TouchRing");
193 verify_has_class (node, class);
194 g_free (class);
195
196 sub = g_strdup_printf ("Label%sCCW", id);
197 node = verify_has_sub (cur, sub);
198 g_assert (node != NULL);
199 g_free (sub);
200
201 class = g_strdup_printf ("%sCCW %s Label", id, id);
202 verify_has_class (node, class);
203 g_free (class);
204
205 sub = g_strdup_printf ("Label%sCW", id);
206 node = verify_has_sub (cur, sub);
207 g_assert (node != NULL);
208 g_free (sub);
209
210 class = g_strdup_printf ("%sCW %s Label", id, id);
211 verify_has_class (node, class);
212 g_free (class);
213
214 sub = g_strdup_printf ("Leader%sCCW", id);
215 node = verify_has_sub (cur, sub);
216 g_assert (node != NULL);
217 g_free (sub);
218
219 class = g_strdup_printf ("%sCCW %s Leader", id, id);
220 verify_has_class (node, class);
221 g_free (class);
222
223 sub = g_strdup_printf ("Leader%sCW", id);
224 node = verify_has_sub (cur, sub);
225 g_assert (node != NULL);
226 g_free (sub);
227
228 class = g_strdup_printf ("%sCW %s Leader", id, id);
229 verify_has_class (node, class);
230 g_free (class);
231 }
232
233 struct fixture {
234 xmlDocPtr doc;
235 xmlNodePtr root;
236 };
237
238 static void
test_filename(struct fixture * f,gconstpointer data)239 test_filename(struct fixture *f, gconstpointer data)
240 {
241 const WacomDevice *device = data;
242 const char *filename;
243
244 filename = libwacom_get_layout_filename(device);
245 if (libwacom_get_num_buttons(device) > 0) {
246 g_assert_nonnull(filename);
247 g_assert_cmpstr(filename, !=, "");
248 }
249 }
250
251 static void
test_svg(struct fixture * f,gconstpointer data)252 test_svg(struct fixture *f, gconstpointer data)
253 {
254 g_assert_nonnull(f->doc);
255 g_assert_nonnull(f->root);
256 g_assert_cmpint(xmlStrcmp(f->root->name, (const xmlChar*) "svg"), ==, 0);
257 }
258
259 static void
test_dimensions(struct fixture * f,gconstpointer data)260 test_dimensions(struct fixture *f, gconstpointer data)
261 {
262 xmlChar *prop;
263
264 /* width is provided */
265 prop = xmlGetProp(f->root, (xmlChar *) "width") ;
266 g_assert_nonnull(prop);
267 xmlFree(prop);
268
269 /* height is provided */
270 prop = xmlGetProp(f->root, (xmlChar *) "height") ;
271 g_assert_nonnull(prop);
272 xmlFree(prop);
273 }
274
275 static void
test_rings(struct fixture * f,gconstpointer data)276 test_rings(struct fixture *f, gconstpointer data)
277 {
278 const WacomDevice *device = data;
279
280 if (libwacom_has_ring(device))
281 check_touchring(f->root, "Ring");
282 if (libwacom_has_ring2(device))
283 check_touchring(f->root, "Ring2");
284 }
285
286 static void
test_strips(struct fixture * f,gconstpointer data)287 test_strips(struct fixture *f, gconstpointer data)
288 {
289 const WacomDevice *device = data;
290
291 if (libwacom_get_num_strips(device) > 0)
292 check_touchstrip(f->root, "Strip");
293 if (libwacom_get_num_strips(device) > 1)
294 check_touchstrip(f->root, "Strip2");
295 }
296
297 static void
test_buttons(struct fixture * f,gconstpointer data)298 test_buttons(struct fixture *f, gconstpointer data)
299 {
300 const WacomDevice *device = data;
301 int num_buttons = libwacom_get_num_buttons (device);
302
303 for (char button = 'A'; button < 'A' + num_buttons; button++) {
304 check_button(f->root, device, button, "Button");
305 check_button(f->root, device, button, "Label");
306 check_button(f->root, device, button, "Leader");
307 }
308 }
309
310 static void
setup_svg(struct fixture * f,gconstpointer data)311 setup_svg(struct fixture *f, gconstpointer data)
312 {
313 const WacomDevice *device = data;
314 const char *filename = libwacom_get_layout_filename(device);
315 xmlDocPtr doc;
316
317 if (!filename)
318 return;
319
320 doc = xmlParseFile(filename);
321 f->doc = doc;
322 f->root = doc ? xmlDocGetRootElement(doc) : NULL;
323 }
324
325 static void
teardown_svg(struct fixture * f,gconstpointer data)326 teardown_svg(struct fixture *f, gconstpointer data)
327 {
328 if (f->doc)
329 xmlFreeDoc(f->doc);
330 }
331
332 typedef void (*testfunc)(struct fixture *f, gconstpointer d);
333
334 /* Wrapper function to make adding tests simpler. g_test requires
335 * a unique test case name so we assemble that from the test function and
336 * the tablet data.
337 */
338 static inline void
_add_test(WacomDevice * device,testfunc func,const char * funcname)339 _add_test(WacomDevice *device, testfunc func, const char *funcname)
340 {
341 char buf[128];
342 static int count; /* guarantee unique test case names */
343 const char *prefix;
344
345 /* tests must be test_foobar */
346 g_assert(strncmp(funcname, "test_", 5) == 0);
347 prefix = &funcname[5];
348
349 snprintf(buf, 128, "/svg/%s/%03d/%04x:%04x-%s",
350 prefix,
351 ++count,
352 libwacom_get_vendor_id(device),
353 libwacom_get_product_id(device),
354 libwacom_get_name(device));
355
356 g_test_add(buf, struct fixture, device,
357 setup_svg, func, teardown_svg);
358 }
359 #define add_test(device_, func_) \
360 _add_test(device_, func_, #func_)
361
362 #define add_test(device_, func_) \
363 _add_test(device_, func_, #func_)
364
setup_tests(WacomDevice * device)365 static void setup_tests(WacomDevice *device)
366 {
367 const char *name;
368
369 name = libwacom_get_name(device);
370 if (strcmp(name, "Generic") == 0)
371 return;
372
373 add_test(device, test_filename);
374 if (!libwacom_get_layout_filename(device))
375 return;
376
377 add_test(device, test_svg);
378 add_test(device, test_dimensions);
379 if (libwacom_get_num_buttons(device) > 0)
380 add_test(device, test_buttons);
381 if (libwacom_has_ring(device) || libwacom_has_ring2(device))
382 add_test(device, test_rings);
383 if (libwacom_get_num_strips(device) > 0)
384 add_test(device, test_strips);
385 }
386
387 static WacomDeviceDatabase *
load_database(void)388 load_database(void)
389 {
390 WacomDeviceDatabase *db;
391 const char *datadir;
392
393 datadir = getenv("LIBWACOM_DATA_DIR");
394 if (!datadir)
395 datadir = TOPSRCDIR"/data";
396
397 db = libwacom_database_new_for_path(datadir);
398 if (!db)
399 printf("Failed to load data from %s", datadir);
400
401 g_assert(db);
402 return db;
403 }
404
main(int argc,char ** argv)405 int main(int argc, char **argv)
406 {
407 WacomDeviceDatabase *db;
408 WacomDevice **devices;
409 int rc;
410
411 g_test_init(&argc, &argv, NULL);
412
413 db = load_database();
414
415 devices = libwacom_list_devices_from_database(db, NULL);
416 g_assert(devices);
417 g_assert(*devices);
418
419 for (WacomDevice **device = devices; *device; device++)
420 setup_tests(*device);
421
422 rc = g_test_run();
423
424 free(devices);
425 libwacom_database_destroy (db);
426
427 return rc;
428 }
429 /* vim: set noexpandtab tabstop=8 shiftwidth=8: */
430