From: Lukasz Wlazly Date: Mon, 19 Sep 2016 14:01:38 +0000 (+0200) Subject: Tool for checking AT-SPI tree integrity v0.5 X-Git-Tag: accepted/tizen/common/20160927.152731^0 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fupstream%2Fat-spi2-core.git;a=commitdiff_plain;h=aa29c756e51d95c2d1611035690cb0c764193c73 Tool for checking AT-SPI tree integrity v0.5 Tool is placed in /bin/ To check AT-SPI tree user "owner" must be set: su - owner Change-Id: I5da4bcaba19547695ae46242f5bbf2dbce7c411c --- diff --git a/packaging/at-spi2-core.spec b/packaging/at-spi2-core.spec index ad24f8d..472f089 100644 --- a/packaging/at-spi2-core.spec +++ b/packaging/at-spi2-core.spec @@ -82,6 +82,7 @@ cp %{SOURCE1001} . %install rm -rf %{buildroot} find %{buildroot} -name '*.la' -or -name '*.a' | xargs rm -f + %make_install %find_lang %{name} @@ -97,6 +98,8 @@ rm -fr %{buildroot} %files -f %{name}.lang %manifest %{name}.manifest %defattr(-,root,root) +%{_bindir}/at_spi2_tool + %doc AUTHORS README %license COPYING %{_libexecdir}/at-spi2/at-spi-bus-launcher diff --git a/test/Makefile.am b/test/Makefile.am index 7af2ee0..f233c36 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,8 +1,10 @@ LDADD = $(top_builddir)/atspi/libatspi.la -noinst_PROGRAMS = memory -memory_SOURCES = memory.c -memory_CPPFLAGS = -I$(top_srcdir) -I$(top_builddir) -I$(top_builddir)/atspi -memory_CFLAGS = $(GLIB_CFLAGS) $(GOBJ_LIBS) $(DBUS_CFLAGS) -memory_LDFLAGS = +bin_PROGRAMS = at_spi2_tool +at_spi2_tool_SOURCES = at_spi2_tool.c +at_spi2_tool_CPPFLAGS = -I$(top_srcdir) -I$(top_builddir) -I$(top_builddir)/atspi +at_spi2_tool_CFLAGS = $(GLIB_CFLAGS) --std=c99 $(GOBJ_LIBS) $(DBUS_CFLAGS) +at_spi2_tool_LDFLAGS = + + -include $(top_srcdir)/git.mk diff --git a/test/at_spi2_tool.c b/test/at_spi2_tool.c new file mode 100644 index 0000000..f67700d --- /dev/null +++ b/test/at_spi2_tool.c @@ -0,0 +1,373 @@ +#include "atspi/atspi.h" +#include +#include +#include +#include +#include + +#define ERROR_STATE -1 +#define SAFE_BUFFER_SIZE 255 +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#define COLUMN_NO 3 + +static unsigned indent_width = 2; + +static const char* atspi_state_names[] = { + [ATSPI_STATE_INVALID] = "INVALID", + [ATSPI_STATE_ACTIVE] = "ACTIVE", + [ATSPI_STATE_ARMED] = "ARMED", + [ATSPI_STATE_BUSY] = "BUSY", + [ATSPI_STATE_CHECKED] = "CHECKED", + [ATSPI_STATE_COLLAPSED] = "COLLAPSED", + [ATSPI_STATE_DEFUNCT] = "DEFUNCT", + [ATSPI_STATE_EDITABLE] = "EDITABLE", + [ATSPI_STATE_ENABLED] = "ENABLED", + [ATSPI_STATE_EXPANDABLE] = "EXPANDABLE", + [ATSPI_STATE_EXPANDED] = "EXPANDED", + [ATSPI_STATE_FOCUSABLE] = "FOCUSABLE", + [ATSPI_STATE_FOCUSED] = "FOCUSED", + [ATSPI_STATE_HAS_TOOLTIP] = "HAS_TOOLTIP", + [ATSPI_STATE_HORIZONTAL] = "HORIZONTAL", + [ATSPI_STATE_ICONIFIED] = "ICONIFIED", + [ATSPI_STATE_MULTI_LINE] = "MULTI_LINE", + [ATSPI_STATE_MULTISELECTABLE] = "MULTISELECTABLE", + [ATSPI_STATE_OPAQUE] = "OPAQUE", + [ATSPI_STATE_PRESSED] = "PRESSED", + [ATSPI_STATE_RESIZABLE] = "RESIZABLE", + [ATSPI_STATE_SELECTABLE] = "SELECTABLE", + [ATSPI_STATE_SELECTED] = "SELECTED", + [ATSPI_STATE_SENSITIVE] = "SENSITIVE", + [ATSPI_STATE_SHOWING] = "SHOWING", + [ATSPI_STATE_SINGLE_LINE] = "SINGLE_LINE", + [ATSPI_STATE_STALE] = "STALE", + [ATSPI_STATE_TRANSIENT] = "TRANSIENT", + [ATSPI_STATE_VERTICAL] = "VERTICAL", + [ATSPI_STATE_VISIBLE] = "VISIBLE", + [ATSPI_STATE_MANAGES_DESCENDANTS] = "MANAGES_DESCENDANTS", + [ATSPI_STATE_INDETERMINATE] = "INDETERMINATE", + [ATSPI_STATE_REQUIRED] = "REQUIRED", + [ATSPI_STATE_TRUNCATED] = "TRUNCATED", + [ATSPI_STATE_ANIMATED] = "ANIMATED", + [ATSPI_STATE_INVALID_ENTRY] = "INVALID_ENTRY", + [ATSPI_STATE_SUPPORTS_AUTOCOMPLETION] = "SUPPORTS_AUTOCOMPLETION", + [ATSPI_STATE_SELECTABLE_TEXT] = "SELECTABLE_TEXT", + [ATSPI_STATE_IS_DEFAULT] = "IS_DEFAULT", + [ATSPI_STATE_VISITED] = "VISITED", + [ATSPI_STATE_CHECKABLE] = "CHECKABLE", + [ATSPI_STATE_MODAL] = "MODAL", + [ATSPI_STATE_HIGHLIGHTED] = "HIGHLIGHTED", + [ATSPI_STATE_HIGHLIGHTABLE] = "HIGHLIGHTABLE", + [ATSPI_STATE_HAS_POPUP] = "HAS_POPUP", + [ATSPI_STATE_READ_ONLY] = "READ_ONLY", + [ATSPI_STATE_LAST_DEFINED] = "LAST_DEFINED" +}; + +typedef struct _StringAccumulator { + char *string; + int length; +} StringAccumulator; + +char *strdup(const char *c) { + char *result = (char *)calloc(1, strlen(c) + 1); + return result ? strcpy(result, c) : result; +} + +static char *multiply_string(char c, unsigned number) { + char *result = (char *)calloc(1, number + 1); + + if (result == NULL) + return result; + + memset(result, c, number); + + return result; +} + +static void _print_atspi_states_legend() { + int array_len = ARRAY_SIZE(atspi_state_names); + int line_count = (array_len + COLUMN_NO - 1)/COLUMN_NO; + for (int line_idx = 0; line_idx < line_count; line_idx++) { + if ((line_idx < line_count - 1) || (array_len % 3 == 0)) { + printf("[%d]\t%-24s[%d]\t%-24s[%d]\t%s\n", line_idx * 3, atspi_state_names[line_idx * 3], (line_idx * 3) + 1, atspi_state_names[(line_idx * 3) + 1], (line_idx * 3) + 2, atspi_state_names[(line_idx * 3) + 2]); + } else if (array_len % 3 == 2) { + printf("[%d]\t%-24s[%d]\t%s\n", line_idx * 3, atspi_state_names[line_idx * 3], (line_idx * 3) + 1, atspi_state_names[(line_idx * 3) + 1]); + } else { + printf("[%d]\t%s\n", line_idx * 3, atspi_state_names[line_idx * 3]); + } + } +} + +static void set_indent(int indent_size) +{ + if (indent_size < 0) { + fprintf(stderr, "WARNING: Invalid indent size. Default value retained.\n"); + return; + } + + indent_width = indent_size; +} + +static int _int_sort_function(gconstpointer a, gconstpointer b) +{ + int int_a = GPOINTER_TO_INT (a); + int int_b = GPOINTER_TO_INT (b); + + return int_a - int_b; +} + +static int _accumulate_string(const char *string, StringAccumulator *accumulator) { + if (!accumulator) + return FALSE; + + if (string) { + int length = accumulator->length + strlen(string); + char *rebuffer = realloc(accumulator->string, length + 1); + if (rebuffer) { + strcpy(rebuffer + accumulator->length, string); + accumulator->string = rebuffer; + accumulator->length = length; + } else { + return FALSE; + } + } + return TRUE; +} + +static char *get_info(AtspiAccessible *node) { + if (!node) + return NULL; + + int node_width = -1; + int node_height = -1; + char *node_name = atspi_accessible_get_name(node, NULL); + char *node_role_name = atspi_accessible_get_role_name(node, NULL); + AtspiComponent *component = atspi_accessible_get_component_iface(node); + + AtspiRect *extent = NULL; + if (component != NULL){ + extent = atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL); + + if (extent != NULL) { + node_width = extent->width; + node_height = extent->height; + } + } + + AtspiStateSet *node_state_set = atspi_accessible_get_state_set(node); + GArray *states = atspi_state_set_get_states(node_state_set); + g_array_sort(states, _int_sort_function); + + AtspiStateType state; + StringAccumulator state_accumulator = {}; + for (int i = 0; states && (i < states->len); i++) { + state = g_array_index(states, AtspiStateType, i); + char node_state_str[8]; + sprintf(node_state_str, "[%d]", state); + _accumulate_string(node_state_str, &state_accumulator); + } + g_array_free(states, 0); + + StringAccumulator attribute_accumulator = {}; + GHashTable *attributes = NULL; + attributes = atspi_accessible_get_attributes(node, NULL); + if (attributes) { + GHashTableIter attributes_iter; + gpointer attr_key, attr_value; + g_hash_table_iter_init (&attributes_iter, attributes); + while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) { + _accumulate_string("[", &attribute_accumulator); + _accumulate_string((char*)attr_key, &attribute_accumulator); + _accumulate_string("=", &attribute_accumulator); + _accumulate_string((char*)attr_value, &attribute_accumulator); + _accumulate_string("]", &attribute_accumulator); + } + g_hash_table_unref(attributes); + } + + char result[SAFE_BUFFER_SIZE + 1]; + snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%d,%d],[%s],[%s]]\n", node_role_name, attribute_accumulator.string, node_width, node_height, node_name, state_accumulator.string); + + free(node_name); + free(node_role_name); + + if (state_accumulator.string) + g_free(state_accumulator.string); + + if (attribute_accumulator.string) + g_free(attribute_accumulator.string); + + if (component) + g_object_unref(component); + + if (extent) + g_free(extent); + + if (node_state_set) + g_object_unref(node_state_set); + + return strdup(result); +} + +static void test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessible *parent_candidate, int parent_candidate_index) +{ + int parent_index = atspi_accessible_get_index_in_parent(obj, NULL); + AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL); + if (parent_index != parent_candidate_index || parent_candidate != parent) { + printf("[FAIL<%d,", parent_candidate_index); + + if (parent == NULL) + printf("_"); + else + printf("%d", parent_index); + + printf(">]\t"); + + } else { + printf("[OK]\t"); + } +} + +static int expand_r(int indent_number, AtspiAccessible *object, bool check_integrity) +{ + char *indent = multiply_string(' ', indent_number*indent_width); + if (indent != NULL) { + printf("%s", indent); + free(indent); + } + + char *node_info = get_info(object); + if (node_info != NULL) { + printf("%s", node_info); + free(node_info); + } + + int count = atspi_accessible_get_child_count(object, NULL); + for (int i = 0; i < count; i++) { + AtspiAccessible *child = atspi_accessible_get_child_at_index(object, i, NULL); + if (child) { + if (check_integrity) + test_atspi_parent_child_relation(child, object, i); + + expand_r(indent_number + 1, child, check_integrity); + } + } + return 0; +} + +void print_help() +{ + printf("AT-SPI2-CORE-UTIL\n\n"); + printf("USAGE: at_spi2_tool [OPTION] [PARAMETER] ...\n\n"); + printf("OPTION LIST:\n"); + printf("-h, --help\t\tshow this message\n"); + printf("-g, --show-legend\tprint AT-SPI state legend\n"); + printf("-l, --list-apps\t\tlist all applications of desktop\n"); + printf("-d, --tree-dump\t\tdump tree for selected application\n"); + printf("-c, --tree-check\tcheck tree for selected application\n"); + printf("-i, --indent\t\tset indentation size\n"); + printf("\nEXAMPLE:\n"); + printf("\tat_spi2_tool -i3 -d quickpanel\n"); + printf("\t show AT-SPI tree for node \"quickpanel\" using three-space indentation\n"); + printf("\tat_spi2_tool -i1 -c starter\n"); + printf("\t show AT-SPI tree with integrity test for node \"starter\" using one-space indentation\n"); +} + +static void atspi_tree_traverse(AtspiAccessible *desktop, const char *app_name, bool dump, bool check) +{ + int count = atspi_accessible_get_child_count(desktop, NULL); + for (int i = 0; i < count; i++) { + AtspiAccessible *child = atspi_accessible_get_child_at_index(desktop, i, NULL); + if (child) { + char *name = atspi_accessible_get_name(child, NULL); + + if (!dump && !check) + printf("%s\n", name); + + if (dump && name && !strcmp(name, app_name)) { + expand_r(0, child, false); + break; + } + + if (check && name && !strcmp(name, app_name)) { + test_atspi_parent_child_relation(child, desktop, i); + expand_r(0, child, true); + break; + } + } + } +} + +static void run_command(int argc, char *argv[], AtspiAccessible *desktop) +{ + struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"show-legend", no_argument, 0, 'g'}, + {"list-apps", no_argument, 0, 'l'}, + {"tree-dump", required_argument, 0, 'd'}, + {"tree-check", required_argument, 0, 'c'}, + {"indent", required_argument, 0, 'i'}, + }; + + int command = 0; + int option_index = 0; + + while (TRUE) { + command = getopt_long(argc, argv, "hgld:c:i:", long_options, &option_index); + + if (command == ERROR_STATE) + break; + + switch (command) { + case 'h': + print_help(); + break; + + case 'g': + _print_atspi_states_legend(); + break; + + case 'l': + atspi_tree_traverse(desktop, NULL, false, false); + break; + + case 'd': + atspi_tree_traverse(desktop, optarg, true, false); + break; + + case 'c': + atspi_tree_traverse(desktop, optarg, false, true); + break; + + case 'i': + set_indent(atoi(optarg)); + break; + + case '?': + break; + + default: + abort(); + } + } +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + printf("Arguments required. Type %s --help.\n", argv[0]); + return -1; + } + + AtspiAccessible *desktop = atspi_get_desktop(0); + + if (!desktop) { + fprintf(stderr, "atspi_get_desktop failed\n"); + return 1; + } + + atspi_init(); + + run_command(argc, argv, desktop); + + return 0; +}