diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index 3998cc8..46b0993 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -12,7 +12,7 @@ exactness_SOURCES = \ exactness_helper_SOURCES = exactness_helper.c exactness_LDADD = \ - @EFL_LIBS@ + @EFL_LIBS@ ../lib/libexactness_player.la exactness_helper_LDADD = \ @EFL_LIBS@ ../lib/libexactness_player.la diff --git a/src/bin/exactness.c b/src/bin/exactness.c index 2e60eab..5c82a7a 100644 --- a/src/bin/exactness.c +++ b/src/bin/exactness.c @@ -40,6 +40,7 @@ static const Ecore_Getopt optdesc = { ECORE_GETOPT_STORE_TRUE('p', "play", "Run in play mode."), ECORE_GETOPT_STORE_TRUE('i', "init", "Run in init mode."), ECORE_GETOPT_STORE_TRUE('s', "simulation", "Run in simulation mode."), + ECORE_GETOPT_STORE_TRUE('S', "store-objects", "Store information about objects at every screen shot time."), ECORE_GETOPT_STORE_TRUE('v', "verbose", "Turn verbose messages on."), ECORE_GETOPT_LICENSE('L', "license"), @@ -69,6 +70,7 @@ main(int argc, char *argv[]) ECORE_GETOPT_VALUE_BOOL(mode_play), ECORE_GETOPT_VALUE_BOOL(mode_init), ECORE_GETOPT_VALUE_BOOL(mode_simulation), + ECORE_GETOPT_VALUE_BOOL(exactness_config.store_objects), ECORE_GETOPT_VALUE_BOOL(exactness_config.verbose), ECORE_GETOPT_VALUE_BOOL(want_quit), diff --git a/src/bin/exactness_config.h b/src/bin/exactness_config.h index e207c51..60d902e 100644 --- a/src/bin/exactness_config.h +++ b/src/bin/exactness_config.h @@ -12,6 +12,7 @@ struct _Exactness_Config char *dest_dir; char *wrap_command; Eina_Bool verbose; + Eina_Bool store_objects; }; extern Exactness_Config exactness_config; diff --git a/src/bin/exactness_helper.c b/src/bin/exactness_helper.c index 12945b2..0cf4b21 100644 --- a/src/bin/exactness_helper.c +++ b/src/bin/exactness_helper.c @@ -116,7 +116,7 @@ _event_specific_info_get(const Variant_st *v, char output[1024]) } static const Ecore_Getopt optdesc = { "exactness_helper", - "%prog [options] ", + "%prog [options] [ | ]", NULL, "(C) 2016 Enlightenment", "BSD", @@ -125,7 +125,8 @@ static const Ecore_Getopt optdesc = { { ECORE_GETOPT_STORE_USHORT('d', "delay", "Delay the given recording by a given time (in seconds)."), ECORE_GETOPT_STORE_TRUE('c', "clean", "Clean the given recording from wrong events."), - ECORE_GETOPT_STORE_TRUE('l', "list", "List the events of the given recording"), + ECORE_GETOPT_STORE_TRUE('l', "list", "List the events of the given recording."), + ECORE_GETOPT_STORE_TRUE('C', "compare", "Compare two given files (images files or objects eet files)."), ECORE_GETOPT_LICENSE('L', "license"), ECORE_GETOPT_COPYRIGHT('C', "copyright"), @@ -149,14 +150,15 @@ _is_hook_duplicate(const Variant_st *cur_v, const Variant_st *prev_v) int main(int argc, char *argv[]) { - const char *rec_file = NULL; + const char *rec_file = NULL, *comp1 = NULL, *comp2 = NULL; int ret = 0, args = 0; unsigned short delay = 0; - Eina_Bool want_quit, clean = EINA_FALSE, list_get = EINA_FALSE; + Eina_Bool want_quit, clean = EINA_FALSE, list_get = EINA_FALSE, compare_files = EINA_FALSE; Ecore_Getopt_Value values[] = { ECORE_GETOPT_VALUE_USHORT(delay), ECORE_GETOPT_VALUE_BOOL(clean), ECORE_GETOPT_VALUE_BOOL(list_get), + ECORE_GETOPT_VALUE_BOOL(compare_files), ECORE_GETOPT_VALUE_BOOL(want_quit), ECORE_GETOPT_VALUE_BOOL(want_quit), @@ -180,17 +182,33 @@ main(int argc, char *argv[]) ret = 1; goto end; } - else if (args == argc) + else if ((clean || delay || list_get) && args == argc) { fprintf(stderr, "Expected rec file as the last argument..\n"); ecore_getopt_help(stderr, &optdesc); ret = 1; goto end; } + else if (compare_files && argc - args != 2) + { + fprintf(stderr, "Expected two files to compare as last arguments..\n"); + ecore_getopt_help(stderr, &optdesc); + ret = 1; + goto end; + } - rec_file = argv[args]; - Timer_Data td; - Lists_st *list = read_events(rec_file, &td); + Lists_st *list = NULL; + if (clean || delay || list_get) + { + rec_file = argv[args]; + Timer_Data td; + list = read_events(rec_file, &td); + } + if (compare_files) + { + comp1 = argv[args]; + comp2 = argv[args+1]; + } if (clean) { @@ -243,7 +261,37 @@ main(int argc, char *argv[]) } } - write_events(rec_file, list); + if (compare_files) + { + const char *ext = strrchr(comp1, '.'); + if (!ext) + { + fprintf(stderr, "Extension required\n"); + goto end; + } + if (!strcmp(ext, ".eet")) + { + if (!objects_files_compare(comp1, comp2, EINA_TRUE)) + { + fprintf(stderr, "Failed objects comparing\n"); + } + } + else + { + char buf[1024]; + + /* FIXME: Clean up. */ + snprintf(buf, sizeof(buf), + "compare '%s' '%s' 'comp_file%s'", + comp1, comp2, ext); + if (system(buf)) + { + fprintf(stderr, "Failed image comparing '%s' and '%s'\n", comp1, comp2); + } + } + } + + if (rec_file) write_events(rec_file, list); end: ecore_shutdown(); diff --git a/src/bin/run_test.c b/src/bin/run_test.c index 6e4f1d6..27365ef 100644 --- a/src/bin/run_test.c +++ b/src/bin/run_test.c @@ -9,6 +9,8 @@ #include "exactness_config.h" #include "exactness_private.h" +#include "tsuite_file_data.h" + #define CONFIG "ELM_SCALE=1 ELM_FINGER_SIZE=10" typedef enum @@ -38,6 +40,8 @@ _run_command_prepare(const List_Entry *ent, Run_Mode mode, char *buf) eina_strbuf_append(sbuf, "ELM_ENGINE='buffer' "); eina_strbuf_append_printf(sbuf, "TSUITE_DEST_DIR='%s/%s' ", exactness_config.dest_dir, CURRENT_SUBDIR); + if (exactness_config.store_objects) + eina_strbuf_append(sbuf, "TSUITE_STORE_OBJECTS=1 "); break; } case RUN_INIT: @@ -45,6 +49,8 @@ _run_command_prepare(const List_Entry *ent, Run_Mode mode, char *buf) eina_strbuf_append(sbuf, "ELM_ENGINE='buffer' "); eina_strbuf_append_printf(sbuf, "TSUITE_DEST_DIR='%s/%s' ", exactness_config.dest_dir, ORIG_SUBDIR); + if (exactness_config.store_objects) + eina_strbuf_append(sbuf, "TSUITE_STORE_OBJECTS=1 "); break; } case RUN_RECORD: @@ -159,24 +165,35 @@ _compare_list_cb(const char *name, const char *path EINA_UNUSED, void *data) if (_check_prefix(prefix, name)) { char filename1[EXACTNESS_PATH_MAX], filename2[EXACTNESS_PATH_MAX]; - snprintf(filename1, EXACTNESS_PATH_MAX, "%s/%s/%s", exactness_config.dest_dir, CURRENT_SUBDIR, name); - snprintf(filename2, EXACTNESS_PATH_MAX, "%s/%s/%s", exactness_config.dest_dir, ORIG_SUBDIR, name); + snprintf(filename1, EXACTNESS_PATH_MAX, "%s/%s/%s", exactness_config.dest_dir, ORIG_SUBDIR, name); + snprintf(filename2, EXACTNESS_PATH_MAX, "%s/%s/%s", exactness_config.dest_dir, CURRENT_SUBDIR, name); if (!_is_equal(filename1, filename2)) { - char buf[EXACTNESS_PATH_MAX]; - exactness_ctx.compare_errors = - eina_list_append(exactness_ctx.compare_errors, - strdup(name)); - - /* FIXME: Clean up. */ - snprintf(buf, EXACTNESS_PATH_MAX, - "compare '%s' '%s' '%s/%s/comp_%s'", - filename1, filename2, - exactness_config.dest_dir, - CURRENT_SUBDIR, name); - if (system(buf)) + const char *ext = strrchr(name, '.'); + if (!strcmp(ext, ".eet")) { - fprintf(stderr, "Failed image comparing '%s'\n", name); + if (!objects_files_compare(filename1, filename2, EINA_FALSE)) + { + fprintf(stderr, "Failed objects comparing '%s'\n", name); + } + } + else + { + char buf[EXACTNESS_PATH_MAX]; + exactness_ctx.compare_errors = + eina_list_append(exactness_ctx.compare_errors, + strdup(name)); + + /* FIXME: Clean up. */ + snprintf(buf, EXACTNESS_PATH_MAX, + "compare '%s' '%s' '%s/%s/comp_%s'", + filename1, filename2, + exactness_config.dest_dir, + CURRENT_SUBDIR, name); + if (system(buf)) + { + fprintf(stderr, "Failed image comparing '%s'\n", name); + } } } } diff --git a/src/lib/tsuite_file_data.c b/src/lib/tsuite_file_data.c index a0e6104..a71362b 100644 --- a/src/lib/tsuite_file_data.c +++ b/src/lib/tsuite_file_data.c @@ -728,6 +728,114 @@ multi_move_desc_make(void) return _d; } +Eet_Data_Descriptor * +object_info_desc_make(void) +{ + Eet_Data_Descriptor_Class eddc; + static Eet_Data_Descriptor *info_d = NULL; + if (!info_d) + { + EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, Object_Info); + info_d = eet_data_descriptor_stream_new(&eddc); + EET_DATA_DESCRIPTOR_ADD_BASIC(info_d, Object_Info, "kl_name", kl_name, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(info_d, Object_Info, "id", id, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_LIST(info_d, Object_Info, "children", children, info_d); + /* Evas stuff */ + EET_DATA_DESCRIPTOR_ADD_BASIC(info_d, Object_Info, "x", x, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(info_d, Object_Info, "y", y, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(info_d, Object_Info, "w", w, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(info_d, Object_Info, "h", h, EET_T_INT); + } + + return info_d; +} + +#define INFO_CHECK(i1, i2, obj_path, var) \ + ({ \ + Eina_Bool _ret = EINA_TRUE; \ + if (i1->var != i2->var) \ + { \ + if (verbose) fprintf(stderr, "%s value is different for %s: %d-%d\n", #var, obj_path, i1->var, i2->var); \ + _ret = EINA_FALSE; \ + } \ + _ret; \ + }) + + +static Eina_Bool +_object_info_compare(Object_Info *info1, Object_Info *info2, Eina_Bool verbose, const char *path) +{ + /* The caller has to give 2 infos whose kl_name and id are respectively the same */ + Eina_List *itr1, *itr2; + Object_Info *c1, *c2; + int cnt1, cnt2; + Eina_Bool ret = EINA_TRUE; + char fpath[512]; + if (!info1 || !info2) return EINA_FALSE; + if (info1->kl_name) + sprintf(fpath, "%s/%s_%d", path, info1->kl_name, info1->id); + else + *fpath = '\0'; + + ret &= INFO_CHECK(info1, info2, fpath, x); + ret &= INFO_CHECK(info1, info2, fpath, y); + ret &= INFO_CHECK(info1, info2, fpath, w); + ret &= INFO_CHECK(info1, info2, fpath, h); + + cnt1 = eina_list_count(info1->children); + cnt2 = eina_list_count(info2->children); + if (cnt1 != cnt2 && verbose) + fprintf(stderr, "Object %s - number of children differs (%d - %d)\n", fpath, cnt1, cnt2); + EINA_LIST_FOREACH(info1->children, itr1, c1) + { + Eina_Bool found = EINA_FALSE; + if (!verbose && !ret) goto end; + EINA_LIST_FOREACH(info2->children, itr2, c2) + { + if (!found && c1->id == c2->id && c1->kl_name == c2->kl_name) + { + found = EINA_TRUE; + ret &= _object_info_compare(c1, c2, verbose, fpath); + } + } + } +end: + return ret; +} + +EAPI Eina_Bool +objects_files_compare(const char *filename1, const char *filename2, Eina_Bool verbose) +{ + Eina_Bool ret = EINA_FALSE; + Eet_File *f1, *f2; + Eet_Data_Descriptor *desc = NULL; + Object_Info *lst1 = NULL, *lst2 = NULL; + + f1 = eet_open(filename1, EET_FILE_MODE_READ); + f2 = eet_open(filename2, EET_FILE_MODE_READ); + desc = object_info_desc_make(); + if (!f1 || !f2) + { + if (verbose) fprintf(stderr, "Can't open %s\n", !f1?filename1:filename2); + goto end; + } + + lst1 = eet_data_read(f1, desc, "entry"); + lst2 = eet_data_read(f2, desc, "entry"); + if (!lst1 || !lst2) + { + if (verbose) fprintf(stderr, "Can't decode %s data\n", !lst1?filename1:filename2); + goto end; + } + + ret = _object_info_compare(lst1, lst2, verbose, NULL); +end: + if (desc) eet_data_descriptor_free(desc); + if (f1) eet_close(f1); + if (f2) eet_close(f2); + return ret; +} + /* declaring types */ data_desc *_data_descriptors_init(void) { diff --git a/src/lib/tsuite_file_data.h b/src/lib/tsuite_file_data.h index 73c341c..831828d 100644 --- a/src/lib/tsuite_file_data.h +++ b/src/lib/tsuite_file_data.h @@ -245,6 +245,28 @@ data_desc *_data_descriptors_init(void); void _data_descriptors_shutdown(void); /* END Event struct descriptors */ +/* START Objects */ +typedef struct +{ + Eo *object; + Eo *parent; + const char *kl_name; + Eina_List *children; + int id; + + /* Evas stuff */ + int x; + int y; + int w; + int h; +} Object_Info; + +Eet_Data_Descriptor *object_info_desc_make(void); + +EAPI Eina_Bool objects_files_compare(const char *filename1, const char *filename2, Eina_Bool verbose); +/* END Objects */ + + Tsuite_Event_Type tsuite_event_mapping_type_get(const char *name); const char * tsuite_event_mapping_type_str_get(Tsuite_Event_Type t); const char * _variant_type_get(const void *data, Eina_Bool *unknow); diff --git a/src/lib/tsuite_hook_player.c b/src/lib/tsuite_hook_player.c index e5dcc92..9c0230c 100644 --- a/src/lib/tsuite_hook_player.c +++ b/src/lib/tsuite_hook_player.c @@ -18,6 +18,7 @@ #define TSUITE_MAX_PATH 1024 #define IMAGE_FILENAME_EXT ".png" +#define OBJECTS_FILENAME_EXT ".eet" struct _evas_hook_setting { @@ -25,6 +26,7 @@ struct _evas_hook_setting char *test_name; char *file_name; Eina_Bool verbose; + Eina_Bool store_objects; }; typedef struct _evas_hook_setting evas_hook_setting; @@ -34,6 +36,15 @@ static Tsuite_Data ts; static Eina_List *evas_list = NULL; /* List of Evas pointers */ static int ignore_evas_new = 0; /* Counter to know if we should ignore evas new or not. */ +typedef struct +{ + const char *kl_name; + int last; +} Main_Widget_Id; +static Eina_List *_main_widget_ids = NULL; +static Object_Info _widgets_list = {0}; +static void _objects_snapshot_do(); + static void _tsuite_verbosef(const char *fmt, ...) { @@ -85,7 +96,6 @@ _shot_do(char *name, Evas *e) filename = malloc(strlen(_hook_setting->test_name) + strlen(IMAGE_FILENAME_EXT) + dir_name_len + 8); /* also space for serial */ - ts.serial++; if (_hook_setting->dest_dir) sprintf(filename, "%s/", _hook_setting->dest_dir); @@ -142,6 +152,7 @@ ecore_init(void) _hook_setting->dest_dir = getenv("TSUITE_DEST_DIR"); _hook_setting->test_name = getenv("TSUITE_TEST_NAME"); _hook_setting->file_name = getenv("TSUITE_FILE_NAME"); + _hook_setting->store_objects = !!getenv("TSUITE_STORE_OBJECTS"); tmp = getenv("TSUITE_VERBOSE"); if (tmp) _hook_setting->verbose = atoi(tmp); @@ -459,10 +470,12 @@ tsuite_feed_event(void *data) printf("%s take shot timestamp=<%u> t->n_evas=<%d>\n", __func__, t->timestamp, t->n_evas); #endif if (rect) evas_object_color_set(rect, 0, 0, 255, 255); + ts.serial++; if (_hook_setting->dest_dir) { _shot_do(NULL, eina_list_nth(evas_list, t->n_evas)); /* Serial name based on test-name */ + if (_hook_setting->store_objects) _objects_snapshot_do(); } break; } @@ -547,3 +560,149 @@ ecore_main_loop_begin(void) return _ecore_main_loop_begin(); } + +static void +_obj_del(void *data EINA_UNUSED, const Efl_Event *event) +{ + Eo *parent = efl_parent_get(event->object); + Object_Info *info = efl_key_data_get(event->object, "exactness_info"); + if (parent) + { + Object_Info *parent_info = parent ? efl_key_data_get(parent, "exactness_info") : NULL; + if (parent_info) parent_info->children = eina_list_remove(parent_info->children, info); + } + else + { + _widgets_list.children = eina_list_remove(_widgets_list.children, info); + } + efl_key_data_set(event->object, "exactness_info", NULL); + eina_stringshare_del(info->kl_name); + free(info); +} + +EAPI Eo * +_efl_add_internal_start(const char *file, int line, const Efl_Class *klass_id, Eo *parent_id, Eina_Bool ref, Eina_Bool is_fallback) +{ + Eo *(*foo)(const char *, int, const Efl_Class *, Eo *, Eina_Bool, Eina_Bool) = + dlsym(RTLD_NEXT, __FUNCTION__); + Eo *ret = foo(file, line, klass_id, parent_id, ref, is_fallback); + + if (!efl_isa(ret, EFL_CANVAS_INTERFACE) && !efl_isa(ret, EFL_CANVAS_OBJECT_CLASS)) goto end; + + efl_event_callback_add(ret, EFL_EVENT_DEL, _obj_del, NULL); +end: + return ret; +} + +EOAPI void +efl_parent_set(Eo *obj, Efl_Object *new_parent) +{ + void (*foo)(Eo *, Efl_Object *) = dlsym(RTLD_NEXT, __FUNCTION__); + Object_Info *info = NULL; + if (!efl_isa(obj, EFL_CANVAS_INTERFACE) && !efl_isa(obj, EFL_CANVAS_OBJECT_CLASS)) goto end; + + info = efl_key_data_get(obj, "exactness_info"); + if (!info) + { + info = calloc(1, sizeof(*info)); + info->object = obj; + info->kl_name = eina_stringshare_add(efl_class_name_get(obj)); + efl_key_data_set(obj, "exactness_info", info); + } + + Eo *old_parent = efl_parent_get(obj); + if (info->id && old_parent == new_parent) goto end; + if (old_parent) + { + Object_Info *old_parent_info = efl_key_data_get(old_parent, "exactness_info"); + if (old_parent_info) + old_parent_info->children = eina_list_remove(old_parent_info->children, info); + + int last_parent_id = (intptr_t)efl_key_data_get(old_parent, info->kl_name); + if (info->id && last_parent_id == info->id) + efl_key_data_set(old_parent, info->kl_name, (void *)(intptr_t)(last_parent_id - 1)); + } + else + { + Eina_List *itr; + Main_Widget_Id *wid; + EINA_LIST_FOREACH(_main_widget_ids, itr, wid) + { + if (info->kl_name == wid->kl_name) goto found_old_parent; + } + wid = NULL; +found_old_parent: + if (wid && wid->last == info->id) wid->last--; + _widgets_list.children = eina_list_remove(_widgets_list.children, info); + } + info->id = 0; + info->parent = new_parent; + if (new_parent) + { + int last_parent_id = (intptr_t)efl_key_data_get(new_parent, info->kl_name); + info->id = ++last_parent_id; + efl_key_data_set(new_parent, info->kl_name, (void *)(intptr_t)last_parent_id); + + Object_Info *new_parent_info = efl_key_data_get(new_parent, "exactness_info"); + if (new_parent_info) + new_parent_info->children = eina_list_append(new_parent_info->children, info); + } + else + { + Eina_List *itr; + Main_Widget_Id *wid; + EINA_LIST_FOREACH(_main_widget_ids, itr, wid) + { + if (info->kl_name == wid->kl_name) goto found_new_parent; + } + wid = calloc(1, sizeof(*wid)); + wid->kl_name = info->kl_name; + _main_widget_ids = eina_list_append(_main_widget_ids, wid); +found_new_parent: + info->id = ++wid->last; + _widgets_list.children = eina_list_append(_widgets_list.children, info); + } +end: + foo(obj, new_parent); +} + +static void +_info_fill(Object_Info *info) +{ + Eina_List *itr; + if (efl_isa(info->object, EFL_CANVAS_OBJECT_CLASS)) + evas_object_geometry_get(info->object, &info->x, &info->y, &info->w, &info->h); + EINA_LIST_FOREACH(info->children, itr, info) + { + _info_fill(info); + } +} + +static void +_objects_snapshot_do() +{ + Eina_List *itr; + Object_Info *info; + int dir_name_len = _hook_setting->dest_dir ? strlen(_hook_setting->dest_dir) + 1 : 0; /* includes space of a '/' */ + char *filename = malloc(strlen(_hook_setting->test_name) + strlen(OBJECTS_FILENAME_EXT) + + dir_name_len + 8); /* also space for serial */ + + if (_hook_setting->dest_dir) + sprintf(filename, "%s/", _hook_setting->dest_dir); + + sprintf(filename + dir_name_len, "%s%c%03d%s", _hook_setting->test_name, + SHOT_DELIMITER, ts.serial, OBJECTS_FILENAME_EXT); + + printf("%d objects saved into %s\n", eina_list_count(_widgets_list.children), filename); + + EINA_LIST_FOREACH(_widgets_list.children, itr, info) + { + _info_fill(info); + } + + Eet_File *file = eet_open(filename, EET_FILE_MODE_WRITE); + eet_data_write(file, object_info_desc_make(), "entry", &_widgets_list, EINA_TRUE); + eet_close(file); + + free(filename); +}