X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=test%2Fat_spi2_tool.c;h=c39100345e81aafbc7d69827ecad0c3152e37948;hb=819f38462c452c3cd7d7fb08adae1f4e018a9c01;hp=badaaa3d03600578d24b0bbebf70da6e96c36d7d;hpb=2a9f101b5bdcd8f382b6a730750e0352f711fd72;p=platform%2Fupstream%2Fat-spi2-core.git diff --git a/test/at_spi2_tool.c b/test/at_spi2_tool.c index badaaa3..c391003 100644 --- a/test/at_spi2_tool.c +++ b/test/at_spi2_tool.c @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #define ERROR_STATE -1 #define SAFE_BUFFER_SIZE 2048 @@ -17,6 +20,10 @@ #define DUMP 0 #define CHECK 1 #define FIRST_MATCH 2 +#define RELATION_TABLE_COLUMN_COUNT 7 +#define ATTRIBUTE_TABLE_COLUMN_COUNT 3 +#define RELATION_TABLE_COLUMN_WIDTH 20 +#define ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH 20 #define VERSION "1.1" static unsigned indent_width = 2; @@ -79,12 +86,6 @@ typedef struct _Box_Size { char *width; } Box_Size; -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); @@ -99,7 +100,7 @@ static char *_multiply_string(char c, unsigned number) static void _print_module_legend() { - printf("\n[[node],[node role name],[attributes list],[x,y,width,height],[node name],[states list][flow to node, flow from node]\n\n"); + printf("\n[[node],[node role name],[attributes list],[x,y,width,height],[node name],[states list][relations]\n\n"); } static void _print_atspi_states_legend() @@ -240,16 +241,17 @@ static char *_get_states(AtspiAccessible *node, int length_limit) { AtspiStateSet *node_state_set = atspi_accessible_get_state_set(node); GArray *states = atspi_state_set_get_states(node_state_set); + if (!states) return NULL; g_array_sort(states, _int_sort_function); AtspiStateType state_type; char *state_string = NULL; - for (int i = 0; states && (i < states->len); i++) { + for (int i = 0; i < states->len; i++) { state_type = g_array_index(states, AtspiStateType, i); char node_state_str[8]; - sprintf(node_state_str, "(%d)", state_type); + snprintf(node_state_str, 8, "(%d)", state_type); _combine_strings(&state_string, node_state_str); } @@ -261,7 +263,7 @@ static char *_get_states(AtspiAccessible *node, int length_limit) return state_string; } -static char *_get_attributes(AtspiAccessible *node, int length_limit) +static char *_get_attributes(AtspiAccessible *node, int length_limit, bool *attributes_are_too_long) { GHashTable *attributes = atspi_accessible_get_attributes(node, NULL); @@ -274,61 +276,45 @@ static char *_get_attributes(AtspiAccessible *node, int length_limit) 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, "\n_get_attributes: generated string is too long. Buffer overflow\n"); + char attributes_string[SAFE_BUFFER_SIZE]; + int ret = snprintf(attributes_string, SAFE_BUFFER_SIZE, "(%s=%s)", (char *)attr_key, (char *)attr_value); + if (ret >= SAFE_BUFFER_SIZE) + fprintf(stderr, "\n%s, %s %d: generated string is too long. Buffer overflow\n", __FILE__, __FUNCTION__, __LINE__); _combine_strings(&result, attributes_string); } g_hash_table_unref(attributes); - _truncate_string(result, length_limit); - return result; -} - -static char *_get_object_in_relation(AtspiAccessible *source, AtspiRelationType search_type) -{ - if (source == NULL) - return ""; - - GArray *relations = atspi_accessible_get_relation_set(source, NULL); - - if (relations == NULL) - return ""; - - AtspiAccessible *ret = NULL; - for (int i = 0; i < relations->len; ++i) { - AtspiRelation *relation = g_array_index(relations, AtspiRelation *, i); - AtspiRelationType type = atspi_relation_get_relation_type(relation); - - if (type == search_type) { - ret = atspi_relation_get_target(relation, 0); - break; - } - } + int real_truncate_size = (length_limit < ATTR_WIDTH && length_limit > MINIMAL_MODULE_WIDTH) ? length_limit : ATTR_WIDTH; + if (result && attributes_are_too_long && strlen(result) > real_truncate_size) + *attributes_are_too_long = true; - g_array_free(relations, TRUE); - - return ret ? atspi_accessible_get_path(ret, NULL) : g_strdup(""); + _truncate_string(result, real_truncate_size); + return result; } -static char *_get_info(AtspiAccessible *node, int length_limit) +static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes_are_too_long, bool *app_has_relations) { if (!node) return NULL; char *node_name = atspi_accessible_get_name(node, NULL); char *node_role_name = atspi_accessible_get_role_name(node, NULL); + char *unique_id = atspi_accessible_get_unique_id(node, NULL); char *path = atspi_accessible_get_path(node, NULL); + unsigned long long eo_ptr = 0; + sscanf(path, "%llu", &eo_ptr); - char *attributes = _get_attributes(node, length_limit); + char *attributes = _get_attributes(node, length_limit, attributes_are_too_long); Box_Size *box_size = _get_box_size(node); char *states = _get_states(node, length_limit); + GArray *relations = atspi_accessible_get_relation_set(node, NULL); + bool current_node_has_relations = (relations && relations->len); + char result[SAFE_BUFFER_SIZE]; - int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s,%s]]", - path, + int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s(%p)],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s]]", + unique_id, (uintptr_t)eo_ptr, node_role_name, attributes, box_size->x, @@ -337,14 +323,18 @@ static char *_get_info(AtspiAccessible *node, int length_limit) box_size->height, node_name, states, - _get_object_in_relation(node, ATSPI_RELATION_FLOWS_TO), - _get_object_in_relation(node, ATSPI_RELATION_FLOWS_FROM)); + current_node_has_relations ? "*" : ""); if (ret >= SAFE_BUFFER_SIZE) - fprintf(stderr, "\n%s, %s %s: generated string is too long. Buffer overflow\n", __FILE__, __FUNCTION__, __LINE__); + fprintf(stderr, "\n%s, %s %d: generated string is too long. Buffer overflow\n", __FILE__, __FUNCTION__, __LINE__); + + if (current_node_has_relations) + *app_has_relations = true; free(node_name); free(node_role_name); + free(unique_id); + free(path); free(attributes); if (box_size) { free(box_size->width); @@ -352,8 +342,9 @@ static char *_get_info(AtspiAccessible *node, int length_limit) free(box_size); } free(states); + g_array_free(relations, TRUE); - return _strdup(result); + return g_strdup(result); } static void _test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessible *parent_candidate, int parent_candidate_index) @@ -366,13 +357,16 @@ static void _test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessi char parent_status[NUMBER_WIDTH]; if (parent == NULL) - strcpy(parent_status, "_"); + strncpy(parent_status, "_", NUMBER_WIDTH); else snprintf(parent_status, NUMBER_WIDTH, "%d", parent_index); + char *parent_unique_id = atspi_accessible_get_unique_id(parent, NULL); + char *parent_candidate_unique_id = atspi_accessible_get_unique_id(parent_candidate, NULL); snprintf(output, CHECK_OUTPUT_WIDTH, "[FAIL<%d,%s><%s,%s>]", parent_candidate_index, parent_status, - atspi_accessible_get_path(parent_candidate, NULL), - atspi_accessible_get_path(parent, NULL)); + parent_candidate_unique_id, parent_unique_id); + free(parent_unique_id); + free(parent_candidate_unique_id); } else { snprintf(output, CHECK_OUTPUT_WIDTH, "[OK]"); @@ -381,7 +375,8 @@ static void _test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessi printf("%-*s\t", CHECK_OUTPUT_WIDTH, output); } -static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible *object, bool check_integrity, int length_limit) +static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible *object, bool check_integrity, int length_limit, + bool *attributes_are_too_long, bool *app_has_relations) { char *indent = _multiply_string(' ', indent_number*indent_width); if (indent != NULL) { @@ -389,7 +384,7 @@ static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible * free(indent); } - char *node_info = _get_info(object, length_limit); + char *node_info = _get_info(object, length_limit, attributes_are_too_long, app_has_relations); if (node_info != NULL) { printf("%s\n", node_info); free(node_info); @@ -402,7 +397,7 @@ static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible * if (check_integrity) _test_atspi_parent_child_relation(child, object, i); - _print_atspi_tree_verify_maybe_r(indent_number + 1, child, check_integrity, length_limit); + _print_atspi_tree_verify_maybe_r(indent_number + 1, child, check_integrity, length_limit, attributes_are_too_long, app_has_relations); } } return 0; @@ -417,6 +412,8 @@ void _print_help() printf("-v, --version\t\tshow actual version of tool\n"); printf("-g, --show-legend\tprint AT-SPI state legend\n"); printf("-l, --list-apps\t\tlist all applications of desktop\n"); + printf("-a, --at-spi-client \tenable/disable org.a11y.Status.IsEnabled property\n"); + printf("-s, --sleep \tsleep N seconds\n"); printf("-d, --tree-dump\t\tdump tree for selected application\n"); printf("-c, --tree-check\tcheck tree for selected application\n"); printf("-f, --first-match\tperform dump or check only for first matching application\n"); @@ -428,23 +425,202 @@ void _print_help() 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"); } + void _print_version() { printf("AT-SPI2-CORE-UTIL v%s\n", VERSION); } -static void _atspi_tree_traverse(AtspiAccessible *desktop, const char *app_name, bool dump, bool check, bool first_match, int length_limit) +static void _print_horizontal_line_in_relations_table() { + for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) { + for (int j = 0; j < RELATION_TABLE_COLUMN_WIDTH; j++) + printf("-"); + printf("+"); + } + printf("\n"); +} + +static char *_get_unique_id_of_object_in_relation(AtspiRelation *relation) { + if (!relation) + return NULL; + + gint last_index = atspi_relation_get_n_targets(relation) - 1; + AtspiAccessible *target = atspi_relation_get_target(relation, last_index); + return atspi_accessible_get_unique_id(target, NULL); +} + +static void _print_relations_for_object(AtspiAccessible *node) { + GArray *relations = atspi_accessible_get_relation_set(node, NULL); + if (!relations) + return; + + if (!relations->len) { + g_array_free(relations, FALSE); + return; + } + + char **table = calloc(RELATION_TABLE_COLUMN_COUNT, sizeof(char *)); + if (!table) { + fprintf(stderr, "Calloc failed. Can't alloc memory for object relations string\n"); + return; + } + + table[0] = atspi_accessible_get_unique_id(node, NULL); + for (int i = 0; i < relations->len; i++) { + AtspiRelation *relation = g_array_index(relations, AtspiRelation *, i); + AtspiRelationType type = atspi_relation_get_relation_type(relation); + int idx; + switch (type) { + case ATSPI_RELATION_CONTROLLER_FOR: idx = 1; break; + case ATSPI_RELATION_CONTROLLED_BY: idx = 2; break; + case ATSPI_RELATION_FLOWS_TO: idx = 3; break; + case ATSPI_RELATION_FLOWS_FROM: idx = 4; break; + case ATSPI_RELATION_DESCRIBED_BY: idx = 5; break; + default: idx = 0; + } + + if (idx > 0) + table[idx] = _get_unique_id_of_object_in_relation(relation); + else { + char buf[16]; + snprintf(buf, sizeof(buf), " [%d]", type); + _combine_strings(&table[RELATION_TABLE_COLUMN_COUNT - 1], buf); + } + } + + for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) { + printf("%*s|", RELATION_TABLE_COLUMN_WIDTH, table[i] ? table[i] : ""); + free(table[i]); + } + free(table); + + printf("\n"); + _print_horizontal_line_in_relations_table(); + g_array_free(relations, TRUE); +} + +typedef void (*print_information_about_object_function)(AtspiAccessible *); + +static void _iterate_over_tree(print_information_about_object_function func, AtspiAccessible *node) { + func(node); + + int count = atspi_accessible_get_child_count(node, NULL); + for (int i = 0; i < count; i++) { + AtspiAccessible *child = atspi_accessible_get_child_at_index(node, i, NULL); + if (child) + _iterate_over_tree(func, child); + } +} + +static void _print_relations_for_objects_in_tree(AtspiAccessible *node) { + _iterate_over_tree(_print_relations_for_object, node); +} + +static void _print_header_for_relation_table() { + char *table[] = {"OBJECT", "CONTROLLER_FOR", "CONTROLLED_BY", "FLOWS_TO", "FLOWS_FROM", "DESCRIBED_BY", "OTHER RELATION [ID]"}; + assert(ARRAY_SIZE(table) == RELATION_TABLE_COLUMN_COUNT); + + _print_horizontal_line_in_relations_table(); + + for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) + printf("%*s|", RELATION_TABLE_COLUMN_WIDTH , table[i]); + + printf("\n"); + _print_horizontal_line_in_relations_table(); +} + +static void _print_relations_table(AtspiAccessible *node) { + printf("\nRELATIONS TABLE\n"); + _print_header_for_relation_table(); + _print_relations_for_objects_in_tree(node); +} + +static void _print_horizontal_line_in_attributes_table() { + int size_factor = 1; + for (int i = 0; i < ATTRIBUTE_TABLE_COLUMN_COUNT; i++) { + if (i == ATTRIBUTE_TABLE_COLUMN_COUNT - 1) + size_factor = 4; + for (int j = 0; j < ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * size_factor; j++) + printf("-"); + printf("+"); + } + printf("\n"); +} + +static void _print_attributes_for_object(AtspiAccessible *node) { + GHashTable *attributes = atspi_accessible_get_attributes(node, NULL); + + if (!attributes) + return; + + char *unique_id = atspi_accessible_get_unique_id(node, NULL); + GHashTableIter attributes_iter; + gpointer attr_key; + gpointer attr_value; + + g_hash_table_iter_init (&attributes_iter, attributes); + while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) { + printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH, unique_id ? unique_id : ""); + printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH, attr_key ? (char *) attr_key : ""); + printf("%*s|\n", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * 4, attr_value ? (char *) attr_value : ""); + } + + if (g_hash_table_size (attributes)) + _print_horizontal_line_in_attributes_table(); + + g_hash_table_unref(attributes); + free(unique_id); +} + +static void _print_attributes_for_objects_in_tree(AtspiAccessible *node) { + _iterate_over_tree(_print_attributes_for_object, node); +} + +static void _print_header_for_attributes_table() { + char *table[] = {"OBJECT", "ATTRIBUTE KEY", "ATTRIBUTE VALUE"}; + assert(ARRAY_SIZE(table) == ATTRIBUTE_TABLE_COLUMN_COUNT); + + _print_horizontal_line_in_attributes_table(); + + int size_factor = 1; + for (int i = 0; i < ATTRIBUTE_TABLE_COLUMN_COUNT; i++) { + if (i == ATTRIBUTE_TABLE_COLUMN_COUNT - 1) + size_factor = 4; + printf("%*s|", ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH * size_factor , table[i]); + } + + printf("\n"); + _print_horizontal_line_in_attributes_table(); +} + +static void _print_attributes_table(AtspiAccessible *node) { + printf("\nATTRIBUTES TABLE\n"); + _print_header_for_attributes_table(); + _print_attributes_for_objects_in_tree(node); +} + +static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bool first_match, int length_limit) { + + AtspiAccessible *desktop = atspi_get_desktop(0); + if (!desktop) { + fprintf(stderr, "atspi_get_desktop failed\n"); + return; + } + 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 == NULL) { - fprintf(stderr, "\n%s, %s %s: Null child occured. Results may be misleading.\n", __FILE__, __FUNCTION__, __LINE__); + fprintf(stderr, "\n%s, %s %d: Null application occured. Results may be misleading.\n", __FILE__, __FUNCTION__, __LINE__); continue; } char *name = atspi_accessible_get_name(child, NULL); + bool attributes_are_too_long = false; + bool app_has_relations = false; if (!dump && !check) printf("%s\n", name); @@ -457,13 +633,20 @@ static void _atspi_tree_traverse(AtspiAccessible *desktop, const char *app_name, if (check) _test_atspi_parent_child_relation(child, desktop, i); - _print_atspi_tree_verify_maybe_r(0, child, check, length_limit); + _print_atspi_tree_verify_maybe_r(0, child, check, length_limit, &attributes_are_too_long, &app_has_relations); + + if (app_has_relations) + _print_relations_table(child); + + if (attributes_are_too_long) + _print_attributes_table(child); if (first_match) { free(name); break; - } else + } else { printf("\n"); + } free(name); } @@ -471,15 +654,59 @@ static void _atspi_tree_traverse(AtspiAccessible *desktop, const char *app_name, if (!app_name_matched && (dump || check)) fprintf(stderr, "There is no application with name: %s. Try again.\n", app_name); + + g_object_unref(desktop); } -static void _run_command(int argc, char *argv[], AtspiAccessible *desktop) +static void _at_spi_client_enable(gboolean enabled) +{ + static GDBusProxy *proxy = NULL; //we keep proxy (dbus connection) until program exits + GVariant *result; + GError *error = NULL; + GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_NONE; + + + if (!proxy) { + proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + flags, + NULL, /* GDBusInterfaceInfo */ + "org.a11y.Bus", + "/org/a11y/bus", + "org.freedesktop.DBus.Properties", + NULL, /* GCancellable */ + &error); + if (error) { + fprintf(stderr, "Failed to create proxy object for '/org/a11y/bus': %s\n", error->message); + g_error_free(error); + return; + } + } + + result = g_dbus_proxy_call_sync(proxy, + "Set", + g_variant_new ("(ssv)", "org.a11y.Status", "IsEnabled", g_variant_new_boolean(enabled)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (result) + g_variant_unref(result); + + if (error) { + fprintf(stderr, "Fail to call org.freedesktop.DBus.Properties.Set: %s\n", error->message); + g_error_free(error); + } +} + +static void _run_command(int argc, char *argv[]) { struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'v'}, {"show-legend", no_argument, 0, 'g'}, {"list-apps", no_argument, 0, 'l'}, + {"at-spi-client", no_argument, 0, 'a'}, + {"sleep", required_argument, 0, 's'}, {"tree-dump", required_argument, 0, 'd'}, {"tree-check", required_argument, 0, 'c'}, {"first-match", no_argument, 0, 'f'}, @@ -491,9 +718,10 @@ static void _run_command(int argc, char *argv[], AtspiAccessible *desktop) int option_index = 0; bool traverse_flags[FLAG_NO] = {false}; char *app_name = NULL; + gboolean enable_at_spi_client; while (TRUE) { - command = getopt_long(argc, argv, "hvgld:c:ft:i:", long_options, &option_index); + command = getopt_long(argc, argv, "hvgla:s:d:c:ft:i:", long_options, &option_index); if (command == ERROR_STATE) break; @@ -512,7 +740,19 @@ static void _run_command(int argc, char *argv[], AtspiAccessible *desktop) break; case 'l': - _atspi_tree_traverse(desktop, NULL, false, false, false, module_name_limit); + _atspi_tree_traverse(NULL, false, false, false, module_name_limit); + break; + + case 'a': + enable_at_spi_client = TRUE; + if (optarg[0] == 'f' || optarg[0] == '0') + enable_at_spi_client = FALSE; + + _at_spi_client_enable(enable_at_spi_client); + break; + + case 's': + sleep(atoi(optarg)); break; case 'd': @@ -547,7 +787,7 @@ static void _run_command(int argc, char *argv[], AtspiAccessible *desktop) } 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); + _atspi_tree_traverse(app_name, traverse_flags[DUMP], traverse_flags[CHECK], traverse_flags[FIRST_MATCH], module_name_limit); } int main(int argc, char *argv[]) @@ -564,13 +804,7 @@ int main(int argc, char *argv[]) return -1; } - AtspiAccessible *desktop = atspi_get_desktop(0); - if (!desktop) { - fprintf(stderr, "atspi_get_desktop failed\n"); - return -1; - } - - _run_command(argc, argv, desktop); + _run_command(argc, argv); return 0; }