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