From 9ace85989ebf7ac08fd6bf091d18132180ff8dc3 Mon Sep 17 00:00:00 2001 From: Lukasz Wlazly Date: Tue, 27 Sep 2016 14:08:24 +0200 Subject: [PATCH] Tool for checking AT-SPI tree integrity v0.95 Code has been refactored and some additional features have appeared Change-Id: I4ff2a96f550df3d52116d166e079cc01d4a8b4b1 --- test/Makefile.am | 8 +- test/at_spi2_tool.c | 357 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 252 insertions(+), 113 deletions(-) diff --git a/test/Makefile.am b/test/Makefile.am index f233c36..6725315 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,10 +1,16 @@ LDADD = $(top_builddir)/atspi/libatspi.la + 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_CFLAGS = $(GLIB_CFLAGS) --std=c99 -Wall $(GOBJ_LIBS) $(DBUS_CFLAGS) at_spi2_tool_LDFLAGS = +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 = -include $(top_srcdir)/git.mk diff --git a/test/at_spi2_tool.c b/test/at_spi2_tool.c index f67700d..252f3e5 100644 --- a/test/at_spi2_tool.c +++ b/test/at_spi2_tool.c @@ -7,10 +7,19 @@ #define ERROR_STATE -1 #define SAFE_BUFFER_SIZE 255 +#define CHECK_OUTPUT_WIDTH 15 +#define MINIMAL_MODULE_WIDTH 3 +#define ATTR_WIDTH 50 +#define NUMBER_WIDTH 6 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define COLUMN_NO 3 +#define FLAG_NO 3 +#define DUMP 0 +#define CHECK 1 +#define FIRST_MATCH 2 static unsigned indent_width = 2; +static int module_name_limit = -1; static const char* atspi_state_names[] = { [ATSPI_STATE_INVALID] = "INVALID", @@ -62,17 +71,19 @@ static const char* atspi_state_names[] = { [ATSPI_STATE_LAST_DEFINED] = "LAST_DEFINED" }; -typedef struct _StringAccumulator { - char *string; - int length; -} StringAccumulator; +typedef struct _Box_Size { + int height; + int width; +} Box_Size; -char *strdup(const char *c) { +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) { +static char *_multiply_string(char c, unsigned number) +{ char *result = (char *)calloc(1, number + 1); if (result == NULL) @@ -83,21 +94,39 @@ static char *multiply_string(char c, unsigned number) { return result; } -static void _print_atspi_states_legend() { +static void _print_module_legend() +{ + printf("\n[[node role name],[attributes list],[width,height],[node name],[states list]]\n\n"); +} + +static void _print_atspi_states_legend() +{ + _print_module_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]); + if ((line_idx < line_count - 1) || (array_len % COLUMN_NO == 0)) { + printf("[%d]\t%-24s[%d]\t%-24s[%d]\t%s\n", + line_idx * COLUMN_NO, + atspi_state_names[line_idx * COLUMN_NO], + (line_idx * COLUMN_NO) + 1, + atspi_state_names[(line_idx * COLUMN_NO) + 1], + (line_idx * COLUMN_NO) + 2, + atspi_state_names[(line_idx * COLUMN_NO) + 2]); + } else if (array_len % COLUMN_NO == 2) { + printf("[%d]\t%-24s[%d]\t%s\n", + line_idx * COLUMN_NO, + atspi_state_names[line_idx * COLUMN_NO], + (line_idx * COLUMN_NO) + 1, + atspi_state_names[(line_idx * COLUMN_NO) + 1]); } else { - printf("[%d]\t%s\n", line_idx * 3, atspi_state_names[line_idx * 3]); + printf("[%d]\t%s\n", line_idx * COLUMN_NO, atspi_state_names[line_idx * COLUMN_NO]); } } } -static void set_indent(int indent_size) +static void _set_indent(int indent_size) { if (indent_size < 0) { fprintf(stderr, "WARNING: Invalid indent size. Default value retained.\n"); @@ -107,6 +136,16 @@ static void set_indent(int indent_size) indent_width = indent_size; } +static void _set_module_length_limit(int limit) +{ + if (limit < MINIMAL_MODULE_WIDTH) { + fprintf(stderr, "WARNING: Invalid maximum field size. Default value retained.\n"); + return; + } + + module_name_limit = limit; +} + static int _int_sort_function(gconstpointer a, gconstpointer b) { int int_a = GPOINTER_TO_INT (a); @@ -115,129 +154,187 @@ static int _int_sort_function(gconstpointer a, gconstpointer 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; - } +static void _truncate_string(char *string, int length_limit) +{ + if (length_limit < MINIMAL_MODULE_WIDTH || !string) + return; + + int len = strlen(string); + + if (len > length_limit) + memcpy(string + length_limit - 3, "...", 4); +} + +static size_t _combine_strings(char **destination, const char *source) +{ + if (!source || !*source || !destination) + return 0; + + size_t old_len = *destination ? strlen(*destination) : 0; + size_t source_len = strlen(source); + size_t len = old_len + source_len; + char *buf = realloc(*destination, (len + 1) * sizeof(char)); + + if (!buf) { + fprintf(stderr, "_combine_strings: allocation failed"); + return old_len; } - return TRUE; + + memcpy(buf + old_len, source, source_len + 1); + + *destination = buf; + + return len; } -static char *get_info(AtspiAccessible *node) { - if (!node) +static Box_Size *_get_box_size(AtspiAccessible *node) +{ + Box_Size *box_size = (Box_Size *)malloc(sizeof(Box_Size)); + if (box_size == NULL) 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); + box_size->width = -1; + box_size->height = -1; + 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 (component == NULL) + return box_size; - if (extent != NULL) { - node_width = extent->width; - node_height = extent->height; - } - } + AtspiRect *extent = atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL); + if (extent == NULL) + return box_size; + + box_size->width = extent->width; + box_size->height = extent->height; + + g_object_unref(component); + g_free(extent); + return box_size; +} + +static char *_get_states(AtspiAccessible *node) +{ 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 = {}; + AtspiStateType state_type; + char *state_string = NULL; + for (int i = 0; states && (i < states->len); i++) { - state = g_array_index(states, AtspiStateType, i); + state_type = g_array_index(states, AtspiStateType, i); + char node_state_str[8]; - sprintf(node_state_str, "[%d]", state); - _accumulate_string(node_state_str, &state_accumulator); + sprintf(node_state_str, "(%d)", state_type); + _combine_strings(&state_string, node_state_str); } + g_array_free(states, 0); + g_object_unref(node_state_set); + + return state_string; +} - StringAccumulator attribute_accumulator = {}; +static char *_get_attributes(AtspiAccessible *node) +{ 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); + if (!attributes) + return NULL; - free(node_name); - free(node_role_name); + GHashTableIter attributes_iter; + gpointer attr_key; + gpointer attr_value; + g_hash_table_iter_init (&attributes_iter, attributes); + char *result = NULL; + while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) { + char attributes_string[ATTR_WIDTH]; + int ret = snprintf(attributes_string, ATTR_WIDTH, "(%s=%s)", (char*)attr_key, (char*)attr_value); + if (ret >= ATTR_WIDTH) { + fprintf(stderr, "_get_attributes: generated string is too long. Buffer overflow"); + abort(); + } + _combine_strings(&result, attributes_string); + } + g_hash_table_unref(attributes); - if (state_accumulator.string) - g_free(state_accumulator.string); + return result; +} - if (attribute_accumulator.string) - g_free(attribute_accumulator.string); +static char *_get_info(AtspiAccessible *node, int length_limit) +{ + if (!node) + return NULL; - if (component) - g_object_unref(component); + char *node_name = atspi_accessible_get_name(node, NULL); + char *node_role_name = atspi_accessible_get_role_name(node, NULL); - if (extent) - g_free(extent); + char *attributes = _get_attributes(node); + _truncate_string(attributes, length_limit); + Box_Size *box_size = _get_box_size(node); + char *states = _get_states(node); + _truncate_string(states, length_limit); + + char result[SAFE_BUFFER_SIZE]; + int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%d,%d],[%s],[%s]]", + node_role_name, + attributes, + box_size->width, + box_size->height, + node_name, + states); + + if (ret >= SAFE_BUFFER_SIZE) { + fprintf(stderr, "_get_info: generated string is too long. Buffer overflow"); + abort(); + } - if (node_state_set) - g_object_unref(node_state_set); + free(node_name); + free(node_role_name); + free(attributes); + free(box_size); + free(states); - return strdup(result); + return _strdup(result); } -static void test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessible *parent_candidate, int parent_candidate_index) +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); + + char output[CHECK_OUTPUT_WIDTH]; if (parent_index != parent_candidate_index || parent_candidate != parent) { - printf("[FAIL<%d,", parent_candidate_index); + char parent_status[NUMBER_WIDTH]; if (parent == NULL) - printf("_"); + strcpy(parent_status, "_"); else - printf("%d", parent_index); + snprintf(parent_status, NUMBER_WIDTH, "%d", parent_index); - printf(">]\t"); + snprintf(output, CHECK_OUTPUT_WIDTH, "[FAIL<%d,%s>]", parent_candidate_index, parent_status); } else { - printf("[OK]\t"); + snprintf(output, CHECK_OUTPUT_WIDTH, "[OK]"); } + + printf("%-*s\t", CHECK_OUTPUT_WIDTH, output); } -static int expand_r(int indent_number, AtspiAccessible *object, bool check_integrity) +static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible *object, bool check_integrity, int length_limit) { - char *indent = multiply_string(' ', indent_number*indent_width); + char *indent = _multiply_string(' ', indent_number*indent_width); if (indent != NULL) { printf("%s", indent); free(indent); } - char *node_info = get_info(object); + char *node_info = _get_info(object, length_limit); if (node_info != NULL) { - printf("%s", node_info); + printf("%s\n", node_info); free(node_info); } @@ -246,15 +343,15 @@ static int expand_r(int indent_number, AtspiAccessible *object, bool check_integ AtspiAccessible *child = atspi_accessible_get_child_at_index(object, i, NULL); if (child) { if (check_integrity) - test_atspi_parent_child_relation(child, object, i); + _test_atspi_parent_child_relation(child, object, i); - expand_r(indent_number + 1, child, check_integrity); + _print_atspi_tree_verify_maybe_r(indent_number + 1, child, check_integrity, length_limit); } } return 0; } -void print_help() +void _print_help() { printf("AT-SPI2-CORE-UTIL\n\n"); printf("USAGE: at_spi2_tool [OPTION] [PARAMETER] ...\n\n"); @@ -264,7 +361,9 @@ void print_help() 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("-f, --first-match\tperform dump or check only for first matching application\n"); + printf("-i, --indent\t\tset indentation size, default value stands at 2\n"); + printf("-t, --truncate\t\tset maximum single field size, default value stands at 30\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"); @@ -272,32 +371,44 @@ void print_help() 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) +static void _atspi_tree_traverse(AtspiAccessible *desktop, const char *app_name, bool dump, bool check, bool first_match, int length_limit) { int count = atspi_accessible_get_child_count(desktop, NULL); + bool app_name_matched = false; 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) + 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; + if ((check || dump) && name && !strcmp(name, app_name)) { + app_name_matched = true; + + _print_module_legend(); + + if (check) + _test_atspi_parent_child_relation(child, desktop, i); + + _print_atspi_tree_verify_maybe_r(0, child, check, length_limit); + + if (first_match) { + free(name); + break; + } else + printf("\n"); } + free(name); } } + + if (!app_name_matched && (dump || check)) + fprintf(stderr, "There is no application with name: %s. Try again.\n", app_name); } -static void run_command(int argc, char *argv[], AtspiAccessible *desktop) +static void _run_command(int argc, char *argv[], AtspiAccessible *desktop) { struct option long_options[] = { {"help", no_argument, 0, 'h'}, @@ -305,21 +416,25 @@ static void run_command(int argc, char *argv[], AtspiAccessible *desktop) {"list-apps", no_argument, 0, 'l'}, {"tree-dump", required_argument, 0, 'd'}, {"tree-check", required_argument, 0, 'c'}, + {"first-match", no_argument, 0, 'f'}, {"indent", required_argument, 0, 'i'}, + {"truncate", required_argument, 0, 't'}, }; int command = 0; int option_index = 0; + bool traverse_flags[FLAG_NO] = {false}; + char *app_name = NULL; while (TRUE) { - command = getopt_long(argc, argv, "hgld:c:i:", long_options, &option_index); + command = getopt_long(argc, argv, "hgld:c:ft:i:", long_options, &option_index); if (command == ERROR_STATE) break; switch (command) { case 'h': - print_help(); + _print_help(); break; case 'g': @@ -327,28 +442,42 @@ static void run_command(int argc, char *argv[], AtspiAccessible *desktop) break; case 'l': - atspi_tree_traverse(desktop, NULL, false, false); + _atspi_tree_traverse(desktop, NULL, false, false, false, module_name_limit); break; case 'd': - atspi_tree_traverse(desktop, optarg, true, false); + traverse_flags[DUMP] = true; + app_name = optarg; break; case 'c': - atspi_tree_traverse(desktop, optarg, false, true); + traverse_flags[CHECK] = true; + app_name = optarg; + break; + + case 'f': + traverse_flags[FIRST_MATCH] = true; break; case 'i': - set_indent(atoi(optarg)); + _set_indent(atoi(optarg)); + break; + + case 't': + _set_module_length_limit(atoi(optarg)); break; case '?': + fprintf(stderr, "Invalid parameter. Use: \"--help\"\n"); break; default: abort(); } } + + if (traverse_flags[DUMP] || traverse_flags[CHECK]) + _atspi_tree_traverse(desktop, app_name, traverse_flags[DUMP], traverse_flags[CHECK], traverse_flags[FIRST_MATCH], module_name_limit); } int main(int argc, char *argv[]) @@ -358,16 +487,20 @@ int main(int argc, char *argv[]) return -1; } - AtspiAccessible *desktop = atspi_get_desktop(0); + int result = atspi_init(); + if (result != 0) + { + fprintf(stderr, "Unable to initilize AT-SPI infrastructure, atspi_init failed\n"); + return -1; + } + AtspiAccessible *desktop = atspi_get_desktop(0); if (!desktop) { fprintf(stderr, "atspi_get_desktop failed\n"); - return 1; + return -1; } - atspi_init(); - - run_command(argc, argv, desktop); + _run_command(argc, argv, desktop); return 0; } -- 2.7.4