Tool for checking AT-SPI tree integrity v0.95 40/90140/6 accepted/tizen/3.0/ivi/20161011.053454 accepted/tizen/3.0/mobile/20161015.032325 accepted/tizen/3.0/tv/20161016.003329 accepted/tizen/3.0/wearable/20161015.080352 accepted/tizen/common/20161006.154023 accepted/tizen/ivi/20161006.233148 accepted/tizen/mobile/20161006.233012 accepted/tizen/tv/20161006.233052 accepted/tizen/wearable/20161006.233113 submit/tizen/20161006.113349 submit/tizen_3.0_ivi/20161010.000000 submit/tizen_3.0_ivi/20161010.000010 submit/tizen_3.0_mobile/20161015.000000 submit/tizen_3.0_tv/20161015.000000 submit/tizen_3.0_wearable/20161015.000000
authorLukasz Wlazly <l.wlazly@partner.samsung.com>
Tue, 27 Sep 2016 12:08:24 +0000 (14:08 +0200)
committerLukasz Wlazly <l.wlazly@partner.samsung.com>
Tue, 4 Oct 2016 13:30:41 +0000 (15:30 +0200)
Code has been refactored and some additional features have appeared

Change-Id: I4ff2a96f550df3d52116d166e079cc01d4a8b4b1

test/Makefile.am
test/at_spi2_tool.c

index f233c36..6725315 100644 (file)
@@ -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
index f67700d..252f3e5 100644 (file)
@@ -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;
 }