summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Hollerbach <marcel-hollerbach@t-online.de>2016-11-13 18:59:33 +0100
committerMarcel Hollerbach <marcel-hollerbach@t-online.de>2016-11-15 09:37:39 +0100
commit91a9f603aef5eeba7cbf83078083da74fd3b8a4a (patch)
tree8c0519784f62546ad8888e59a2e6883854eddcf7
parentbbf9c0bf1638c2b360c1c9e8f2ab9888b88a5379 (diff)
Introduce focus graph checkerdevs/bu5hm4n/focus_checker
This module checks that all managers which are known to the canvas have a probebly setted up graph. Which means that all widgets of a graph are fully connected(no relations to the outside of that graph, and no seperated widgets which are not accessable via relations). It also checks for the case of multiple manager objects, that those manager objects are connected via redirects, and those connections & managers are representing a tree. ERR messages are counted as failures found by the checker, CRIT messages are assertions from the checker which are not met. INF messages are informations (mostly that a manager is checked etc.). How the checks are performed is written as comment above the particular checks.
-rw-r--r--src/modules/Makefile.am8
-rw-r--r--src/modules/client/focus_graph_checker.c384
2 files changed, 391 insertions, 1 deletions
diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am
index ab0ad9a..b8a0a2f 100644
--- a/src/modules/Makefile.am
+++ b/src/modules/Makefile.am
@@ -10,7 +10,7 @@ AM_CPPFLAGS = \
10@EFL_CFLAGS@ 10@EFL_CFLAGS@
11 11
12client_modulesdir = $(libdir)/clouseau/modules/client 12client_modulesdir = $(libdir)/clouseau/modules/client
13client_modules_LTLIBRARIES = canvas_checker.la 13client_modules_LTLIBRARIES = canvas_checker.la focus_graph_checker.la
14 14
15canvas_checker_la_SOURCES = client/canvas_checker.c 15canvas_checker_la_SOURCES = client/canvas_checker.c
16 16
@@ -18,3 +18,9 @@ canvas_checker_la_LDFLAGS = -module -avoid-version -rdynamic
18canvas_checker_la_DEPENDENCIES = $(top_builddir)/config.h 18canvas_checker_la_DEPENDENCIES = $(top_builddir)/config.h
19canvas_checker_la_LIBADD = @EFL_LIBS@ 19canvas_checker_la_LIBADD = @EFL_LIBS@
20 20
21focus_graph_checker_la_SOURCES = client/focus_graph_checker.c
22
23focus_graph_checker_la_LDFLAGS = -module -avoid-version -rdynamic
24focus_graph_checker_la_DEPENDENCIES = $(top_builddir)/config.h
25focus_graph_checker_checker_la_LIBADD = @EFL_LIBS@
26
diff --git a/src/modules/client/focus_graph_checker.c b/src/modules/client/focus_graph_checker.c
new file mode 100644
index 0000000..fb09cc9
--- /dev/null
+++ b/src/modules/client/focus_graph_checker.c
@@ -0,0 +1,384 @@
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#include <Eina.h>
6#include <stdio.h>
7#include <Clouseau.h>
8
9static int _focus_checker_dom = -1;
10
11//critical errors are errors which can make the checks produce wrong results
12#define CRIT(...) EINA_LOG_DOM_CRIT(_focus_checker_dom, __VA_ARGS__)
13//errors are reporting failures of the test
14#define ERR(...) EINA_LOG_DOM_ERR(_focus_checker_dom, __VA_ARGS__)
15#define WRN(...) EINA_LOG_DOM_WARN(_focus_checker_dom, __VA_ARGS__)
16#define INF(...) EINA_LOG_DOM_INFO(_focus_checker_dom, __VA_ARGS__)
17#define DBG(...) EINA_LOG_DOM_DBG(_focus_checker_dom, __VA_ARGS__)
18
19EAPI const char *clouseau_module_name = "Focus Graph Checker";
20
21typedef struct {
22 Eina_List *right;
23 Eina_List *left;
24 Eina_List *top;
25 Eina_List *down;
26 void *next;
27 void *prev;
28 void *manager;
29 void *redirect;
30} Focus_Relations;
31
32typedef struct _Focus_Manager Focus_Manager;
33
34struct _Focus_Manager {
35 void *ptr;
36 void *first_child;
37 Eina_Hash *objects;
38 Eina_List *redirects;
39 struct {
40 Focus_Manager *parent;
41 int depth;
42 } manager_tree_check; //only valid in execution of _managers_link_check();
43};
44
45static Eina_Hash *managers = NULL;
46
47static Focus_Manager*
48_manager_get(void *manager)
49{
50 Focus_Manager *m;
51
52 m = eina_hash_find(managers, &manager);
53 if (!m)
54 {
55 m = calloc(1, sizeof(Focus_Manager));
56
57 m->ptr = manager;
58 m->objects = eina_hash_pointer_new(NULL);
59 eina_hash_add(managers, &manager, m);
60 }
61
62 return m;
63}
64
65static void
66_focus_manager_redirect_add(Focus_Manager *m, void *redirect)
67{
68 m->redirects = eina_list_append(m->redirects, redirect);
69}
70
71static void
72_pointer_free(void *data)
73{
74 free(data);
75}
76
77static Eina_Bool
78_init(void)
79{
80 eina_init();
81 _focus_checker_dom = eina_log_domain_register("Focus Checker", EINA_COLOR_CYAN);
82 eina_log_domain_level_set("Focus Checker", EINA_LOG_LEVEL_INFO);
83 managers = eina_hash_pointer_new(_pointer_free);
84 return EINA_TRUE;
85}
86
87static void
88_shutdown(void)
89{
90 eina_log_domain_unregister(_focus_checker_dom);
91 eina_shutdown();
92}
93
94static Eina_List*
95_list_ptr_get(Efl_Dbg_Info *info)
96{
97 Eina_Value_List list;
98 Efl_Dbg_Info *data;
99 Eina_List *n, *result = NULL;
100
101 if (eina_value_type_get(&info->value) != EINA_VALUE_TYPE_LIST)
102 {
103 CRIT("Expected list type");
104 return NULL;
105 }
106
107 eina_value_pget(&info->value, &list);
108
109 EINA_LIST_FOREACH(list.list, n, data)
110 {
111 unsigned long long ptr;
112
113 eina_value_get(&data->value, &ptr);
114
115 result = eina_list_append(result, (void*)ptr);
116 }
117
118 return result;
119}
120
121static Focus_Relations*
122_tree_it_convert(Clouseau_Tree_Item *it)
123{
124 Focus_Relations *ret;
125 Efl_Dbg_Info *widget, *focus, *next, *prev,
126 *right, *left, *top, *down,
127 *manager, *redirect;
128
129 clouseau_tree_item_from_legacy_convert(it);
130
131 widget = clouseau_eo_info_find(it->new_eo_info , "Elm_Widget");
132 if (!widget)
133 {
134 return NULL;
135 }
136
137 focus = clouseau_eo_info_find(widget, "Focus");
138 if (!focus)
139 {
140 return NULL;
141 }
142
143 next = clouseau_eo_info_find(focus, "next");
144 prev = clouseau_eo_info_find(focus, "prev");
145 right = clouseau_eo_info_find(focus, "right");
146 left = clouseau_eo_info_find(focus, "left");
147 top = clouseau_eo_info_find(focus, "top");
148 down = clouseau_eo_info_find(focus, "down");
149 manager = clouseau_eo_info_find(focus, "manager");
150 redirect = clouseau_eo_info_find(focus, "redirect");
151
152 if (!next || !prev || !right || !left || !top || !down || !manager || !redirect)
153 {
154 CRIT("Widget %p did not present the full set of properties.", (void*)it->ptr);
155
156 return NULL;
157 }
158 ret = calloc(1, sizeof(Focus_Relations));
159
160 eina_value_get(&next->value, &ret->next);
161 eina_value_get(&prev->value, &ret->prev);
162 ret->right = _list_ptr_get(right);
163 ret->left = _list_ptr_get(left);
164 ret->top = _list_ptr_get(top);
165 ret->down = _list_ptr_get(down);
166 eina_value_get(&manager->value, &ret->manager);
167 eina_value_get(&redirect->value, &ret->redirect);
168 return ret;
169}
170
171static void
172_add(Clouseau_Tree_Item *it)
173{
174 Clouseau_Tree_Item *treeit;
175 Focus_Relations *rel;
176 Focus_Manager *m;
177 Eina_List *n;
178
179 rel = _tree_it_convert(it);
180
181 if (rel)
182 {
183 m = _manager_get(rel->manager);
184 if (!m->first_child)
185 m->first_child = (void*)it->ptr;
186 if (rel->redirect)
187 _focus_manager_redirect_add(m, rel->redirect);
188 eina_hash_add(m->objects, &it->ptr, rel);
189 }
190
191 EINA_LIST_FOREACH(it->children , n, treeit)
192 {
193 _add(treeit);
194 }
195}
196
197/*
198 * Check that all widgets are linked to each other
199 *
200 * This is working by duplicating the hash of objects,.
201 * Exploring a item means that all the right left top
202 * down next and prev directions are added into the set of "need explotation".
203 * At first one random element is added to the set of need exploration.
204 * Then remove each time a element from the set, if the element is still
205 * in the duplicated hash, the element is explored, if not the element is ignored.
206 *
207 * If the set is now empty and the hash has still elements there must be a set
208 * of not linked elements to the other elements.
209 */
210Eina_Bool
211_duplicate(const Eina_Hash *hash, const void *key, void *data, void *fdata)
212{
213 eina_hash_add(fdata, key, data);
214
215 return EINA_TRUE;
216}
217
218Eina_Bool
219_count(const Eina_Hash *hash, const void *key, void *data, void *fdata)
220{
221 (*((int*)fdata))++;
222 return EINA_TRUE;
223}
224
225static void
226_manager_check(Focus_Manager *manager)
227{
228 Eina_Hash *dup;
229 Eina_Iterator *itr;
230 void *ptr;
231 Eina_List *runner = NULL, *n, *n_next;
232
233 dup = eina_hash_pointer_new(NULL);
234 eina_hash_foreach(manager->objects, _duplicate, dup);
235
236 runner = eina_list_append(runner, manager->first_child);
237
238 do
239 {
240 void *ptr;
241 Focus_Relations *rel;
242
243 ptr = eina_list_data_get(runner);
244 runner = eina_list_remove(runner, ptr);
245
246 rel = eina_hash_find(manager->objects, &ptr);
247 if (!rel)
248 {
249 ERR("In manager %p %p is used in a reference, but not part of the manager", manager->ptr , ptr);
250 continue;
251 }
252
253 rel = eina_hash_find(dup, &ptr);
254 if (!rel)
255 {
256 continue;
257 }
258
259 eina_hash_del_by_key(dup, &ptr);
260
261
262 runner = eina_list_merge(runner, rel->left);
263 runner = eina_list_merge(runner, rel->right);
264 runner = eina_list_merge(runner, rel->top);
265 runner = eina_list_merge(runner, rel->down);
266 runner = eina_list_append(runner, rel->next);
267 runner = eina_list_append(runner, rel->prev);
268
269 } while(eina_list_count(runner) != 0);
270
271 //if we are done and sonething is left in the hash we have a not connected subset in the manager. BAD
272 int count;
273
274 eina_hash_foreach(dup, _count, &count);
275
276 if (count > 0)
277 {
278 ERR("Manager %p has a disconnected set of widgets", manager->ptr);
279 /* FIXME print all the children */
280 }
281 else
282 INF("Manager %p is checked and fine", manager->ptr);
283}
284
285/**
286 * Functions to check that the with redirect created objects are a tree
287 *
288 * This works by iterating 3 times throuw the elements, at first, we are setting the parent of each child object
289 * In the second stage we are searching for the element with no parent, which is the root.
290 * And we ensure that there is just one root.
291 * In the last stage we are calculating the depth of each manager, if in some element a other depth is laready
292 * calculated we know that there is at least one cycle.
293 */
294
295static Eina_Bool
296_depth_calc(Focus_Manager *m, int depth)
297{
298 Eina_List *n;
299 Focus_Manager *m2;
300 Eina_Bool suc = EINA_TRUE;
301
302 if (m->manager_tree_check.depth != 0)
303 {
304 ERR("Manager %p is part of a cycle in the tree!", m->ptr);
305 return EINA_FALSE;
306 }
307
308 m->manager_tree_check.depth = depth;
309
310 EINA_LIST_FOREACH(m->redirects, n, m2)
311 {
312 if (!_depth_calc(m2, depth + 1))
313 suc = EINA_FALSE;
314 }
315 return suc;
316}
317
318static void
319_managers_link_check(void)
320{
321 Focus_Manager *root = NULL, *m;
322 Eina_Hash *manager_cache;
323 Eina_Iterator *itr;
324
325 //check if this things contains cycles
326 manager_cache = eina_hash_pointer_new(NULL);
327
328 //First stage set parents;
329 itr = eina_hash_iterator_data_new(managers);
330 EINA_ITERATOR_FOREACH(itr, m)
331 {
332 Focus_Manager *m2;
333 Eina_List *n;
334
335 //now we are setting the children
336 EINA_LIST_FOREACH(m->redirects, n, m2)
337 {
338 m2->manager_tree_check.parent = m;
339 }
340 }
341 eina_iterator_free(itr);
342
343 //second stage, search for the root
344 itr = eina_hash_iterator_data_new(managers);
345 EINA_ITERATOR_FOREACH(itr, m)
346 {
347 if (!m->manager_tree_check.parent && !root)
348 root = m;
349 else if (!m->manager_tree_check.parent && root)
350 ERR("Error, there is already a root manager (%p), but %p does not have any parents", root->ptr, m->ptr);
351 }
352 eina_iterator_free(itr);
353
354 //thirds stage, check that this is really a tree
355 if (_depth_calc(root, 1))
356 INF("All managers are linked in the form of a tree");
357}
358
359EAPI void
360clouseau_client_module_run(Eina_List *tree)
361{
362 Clouseau_Tree_Item *treeit;
363 Eina_List *n;
364 Eina_Iterator *managers_itr;
365 Focus_Manager *manager;
366
367 //move everything into a pointer hash
368 EINA_LIST_FOREACH(tree, n, treeit)
369 _add(treeit);
370
371 //check that there are no islands in the graph of a manager
372 managers_itr = eina_hash_iterator_data_new(managers);
373 EINA_ITERATOR_FOREACH(managers_itr, manager)
374 {
375 _manager_check(manager);
376 }
377
378 //check that all managers are linked to at least one other manager
379 _managers_link_check();
380
381}
382
383EINA_MODULE_INIT(_init);
384EINA_MODULE_SHUTDOWN(_shutdown);