From cc823d6dacfddf3547e4245ba08346dceacb21c8 Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Wed, 28 Apr 2021 19:52:58 +0900 Subject: [PATCH] e_test_base: Add verifyTC function Check the test result with the reference image by using screenshot. If pixel checking failed, make dump image to /tmp/etTestErr/'testmodule'/ directory. Change-Id: I8aef4425595647b26d4075e72bd5d9c732bd7a50 Signed-off-by: Junkyeong Kim --- configure.ac | 4 + packaging/e-tizen-unittests.spec | 4 + src/e_test_base.cpp | 411 +++++++++++++++++++++++++++++++++++++++ src/e_test_base.h | 5 + src/e_test_util.h | 6 + 5 files changed, 430 insertions(+) diff --git a/configure.ac b/configure.ac index d280684..e9db6ce 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,8 @@ AC_PROG_CXX requirements="\ elementary \ + libtbm \ + libpng \ eldbus \ enlightenment \ capi-ui-efl-util \ @@ -33,6 +35,8 @@ requirements="\ tizen-launch-client \ tzsh-screensaver-manager-service \ tzsh-screensaver-service \ + cairo \ + pixman-1 \ " PKG_CHECK_MODULES(E_TEST_RUNNER, [${requirements}]) diff --git a/packaging/e-tizen-unittests.spec b/packaging/e-tizen-unittests.spec index 8613cde..168a7fe 100644 --- a/packaging/e-tizen-unittests.spec +++ b/packaging/e-tizen-unittests.spec @@ -10,6 +10,8 @@ Source0: %{name}-%{version}.tar.gz Source1001: e-tizen-unittests.manifest License: BSD-2-Clause BuildRequires: pkgconfig(enlightenment) +BuildRequires: pkgconfig(libtbm) +BuildRequires: pkgconfig(libpng) BuildRequires: pkgconfig(eldbus) BuildRequires: pkgconfig(capi-ui-efl-util) BuildRequires: pkgconfig(ecore-wl2) @@ -21,6 +23,8 @@ BuildRequires: pkgconfig(tzsh-screensaver-service) BuildRequires: gtest-devel BuildRequires: pkgconfig(edje) BuildRequires: edje-tools +BuildRequires: pkgconfig(cairo) +BuildRequires: pkgconfig(pixman-1) %description This package is a test case runner for enlightenment. diff --git a/src/e_test_base.cpp b/src/e_test_base.cpp index 3027967..78834d6 100644 --- a/src/e_test_base.cpp +++ b/src/e_test_base.cpp @@ -1,11 +1,47 @@ #include "e_test_base.h" +#include +#include +#include using ::testing::Test; +#ifndef ARRAY_LENGTH +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) +#endif + +#define UT_WAIT(fmt, ...) \ + do { \ + char ch; \ + do { \ + printf(fmt" [n]):next ", ##__VA_ARGS__); \ + ch = tc_getchar(); \ + } while (ch != 'n'); \ + } while (0) + +char +tc_getchar(void) +{ + int c = getchar(); + int ch = c; + + if (ch == '\n' || ch == '\r') + ch = 'y'; + else if (ch < 'a') + ch += ('a' - 'A'); + + while (c != '\n' && c != EOF) + c = getchar(); + + return ch; +} + etTCBase::etTCBase() { etRunner::get().flushEventQueue(); etRunner::get().setTCStart(); + screenshot = efl_util_screenshot_initialize(CAPTURE_WIDTH, CAPTURE_HEIGHT); + if (screenshot == NULL) + ERR("efl_util_screenshot_initialize fail\n"); } etTCBase::~etTCBase() @@ -14,6 +50,12 @@ etTCBase::~etTCBase() numGeomTw = 0; numTw = 0; + if (screenshot) + { + efl_util_screenshot_deinitialize(screenshot); + screenshot = NULL; + } + etRunner::get().setTCEnd(); etRunner::get().flushEventQueue(); @@ -197,3 +239,372 @@ etTCBase::removeTCWin(etWin *tw) return EINA_TRUE; } + +void +_verifyTCGetBufferSize(tbm_surface_h buffer, int *buffer_w, int *buffer_h, int *stride) +{ + tbm_surface_info_s info; + int ret; + + EINA_SAFETY_ON_NULL_RETURN(buffer); + + ret = tbm_surface_get_info(buffer, &info); + if (ret != TBM_SURFACE_ERROR_NONE) + return; + + if (buffer_w) + *buffer_w = info.planes[0].stride >> 2; + + if (buffer_h) + *buffer_h = info.planes[0].size / info.planes[0].stride; + + if (stride) + *stride = info.planes[0].stride; +} + +static void +_verifyTCdumpPng(const char *file, const void *data, int width, int height, int stride) +{ + pixman_image_t *image; + cairo_surface_t *cairo_surface; + cairo_status_t status; + + image = pixman_image_create_bits_no_clear(PIXMAN_x8r8g8b8, width, height, (uint32_t *)data, stride); + if (image == (pixman_image_t *)NULL) + { + ERR("pixman_image_create_bits_no_clear failed"); + return; + } + + cairo_surface = cairo_image_surface_create_for_data((unsigned char *)pixman_image_get_data(image), + CAIRO_FORMAT_RGB24, pixman_image_get_width(image), pixman_image_get_height(image), + pixman_image_get_stride(image)); + if (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS) + { + ERR("cairo_image_surface_create_for_data failed"); + pixman_image_unref(image); + return; + } + + status = cairo_surface_write_to_png(cairo_surface, file); + if (status != CAIRO_STATUS_SUCCESS) + { + ERR("Failed to save image '%s': %s\n", file, cairo_status_to_string(status)); + pixman_image_unref(image); + return; + } + + cairo_surface_destroy(cairo_surface); + pixman_image_unref(image); +} + +std::string +_verifyTCMakeDumpPath(std::string basetype, std::string tcname) +{ + std::string path = "/tmp/etTestErr/"; + std::string format = ".png"; + int ret; + + ret = mkdir(path.c_str(), 0755); + if (ret < 0 && errno != EEXIST) + { + ERR("mkdir failed. (%s)\n", path.c_str()); + return "\0"; + } + + path = path + basetype + '/'; + ret = mkdir(path.c_str(), 0755); + if (ret < 0 && errno != EEXIST) + { + ERR("mkdir failed. (%s)\n", path.c_str()); + return "\0"; + } + + path = path + tcname + format; + ERR("path : %s", path.c_str()); + + return path; +} + +void +_verifyTCDumpBuffer(tbm_surface_h buffer, std::string basetype, std::string tcname) +{ + std::string dumppath; + tbm_surface_info_s info; + int ret; + int bo_cnt; + int bw, bh, stride; + + EINA_SAFETY_ON_NULL_RETURN(buffer); + + dumppath = _verifyTCMakeDumpPath(basetype, tcname); + if (dumppath.length() == 0) + { + ERR("getting dumppath failed"); + return; + } + + ret = tbm_surface_map(buffer, TBM_OPTION_READ, &info); + if (ret != TBM_SURFACE_ERROR_NONE) + { + ERR("tbm_surface_map failed"); + return; + } + + _verifyTCGetBufferSize(buffer, &bw, &bh, &stride); + + bo_cnt = tbm_surface_internal_get_num_bos(buffer); + DBG("buffer: bo_cnt(%d) %dx%d(%dx%d) %c%c%c%c, plane: (%p+%d, %d,%d) (%p+%d, %d,%d) (%p+%d, %d,%d)", + bo_cnt, bw, bh, info.width, info.height, FOURCC_STR(info.format), + info.planes[0].ptr, info.planes[0].offset, info.planes[0].stride, info.planes[0].size, + info.planes[1].ptr, info.planes[1].offset, info.planes[1].stride, info.planes[1].size, + info.planes[2].ptr, info.planes[2].offset, info.planes[2].stride, info.planes[2].size); + + if (info.format == TBM_FORMAT_ARGB8888 || info.format == TBM_FORMAT_XRGB8888) + _verifyTCdumpPng(dumppath.c_str(), info.planes[0].ptr, bw, bh, stride); + else + DBG("not supported dump format"); + + tbm_surface_unmap(buffer); +} + +std::string +_verifyTCMakeRefPath(std::string basetype, std::string tcname) +{ + std::string path = "/usr/share/e_tizen_unittests/data/"; + std::string format = ".png"; + + path = path + basetype + '/' + tcname + format; + + return path; +} + +pixman_format_code_t +_verifyTCCairo2Pixman(cairo_format_t fmt) +{ + if (fmt == CAIRO_FORMAT_ARGB32) + return PIXMAN_a8r8g8b8; + else if (fmt == CAIRO_FORMAT_RGB24) + return PIXMAN_x8r8g8b8; + else + return (pixman_format_code_t)0; +} + +static void +destroy_cairo_surface(pixman_image_t *image, void *data) +{ + cairo_surface_t *surface = (cairo_surface_t *)data; + + cairo_surface_destroy(surface); +} + +pixman_image_t * +_verifyTCConvertx8r8g8b8(pixman_image_t *image) +{ + pixman_image_t *ret; + int width; + int height; + + if (pixman_image_get_format(image) == PIXMAN_x8r8g8b8) + return pixman_image_ref(image); + + width = pixman_image_get_width(image); + height = pixman_image_get_height(image); + + ret = pixman_image_create_bits_no_clear(PIXMAN_x8r8g8b8, width, height, NULL, 0); + if (ret == NULL) + { + ERR("pixman_image_create_bits_no_clear failed"); + return NULL; + } + + pixman_image_composite32(PIXMAN_OP_SRC, image, NULL, ret, 0, 0, 0, 0, 0, 0, width, height); + + return ret; +} + +pixman_image_t * +_verifyTCLoadImage(const char *fname) +{ + pixman_image_t *image; + pixman_image_t *converted; + cairo_format_t cairo_fmt; + pixman_format_code_t pixman_fmt; + cairo_surface_t *reference_cairo_surface; + cairo_status_t status; + int width; + int height; + int stride; + void *data; + + reference_cairo_surface = cairo_image_surface_create_from_png(fname); + cairo_surface_flush(reference_cairo_surface); + status = cairo_surface_status(reference_cairo_surface); + if (status != CAIRO_STATUS_SUCCESS) + { + ERR("Could not open %s: %s\n", fname, cairo_status_to_string(status)); + cairo_surface_destroy(reference_cairo_surface); + return NULL; + } + + cairo_fmt = cairo_image_surface_get_format(reference_cairo_surface); + pixman_fmt = _verifyTCCairo2Pixman(cairo_fmt); + ERR("pixman format(PIXMAN_a8r8g8b8:%d,PIXMAN_x8r8g8b8:%d) : %d", PIXMAN_a8r8g8b8, PIXMAN_x8r8g8b8, pixman_fmt); + if (pixman_fmt == (pixman_format_code_t)0) + { + ERR("unknown format"); + cairo_surface_destroy(reference_cairo_surface); + return NULL; + } + + width = cairo_image_surface_get_width(reference_cairo_surface); + height = cairo_image_surface_get_height(reference_cairo_surface); + stride = cairo_image_surface_get_stride(reference_cairo_surface); + data = cairo_image_surface_get_data(reference_cairo_surface); + + image = pixman_image_create_bits_no_clear(pixman_fmt, width, height, (uint32_t *)data, stride); + if (image == (pixman_image_t *)NULL) + { + ERR("pixman_image_create_bits_no_clear failed"); + cairo_surface_destroy(reference_cairo_surface); + return NULL; + } + + pixman_image_set_destroy_function(image, destroy_cairo_surface, reference_cairo_surface); + + converted = _verifyTCConvertx8r8g8b8(image); + pixman_image_unref(image); + + return converted; +} + +Eina_Bool +_verifyTCCheckPixel(uint32_t pix_a, uint32_t pix_b, int w, int h) +{ + int i, shift; + int diffmin = -3; + int diffmax = 4; + + for (shift = 8, i = 1; i < 4; shift += 8, i++) + { + int val_a = (pix_a >> shift) & 0xFF; + int val_b = (pix_b >> shift) & 0xFF; + int diff = val_b - val_a; + + if (diff < diffmin || diff > diffmax) + return EINA_FALSE; + } + + return EINA_TRUE; +} + +Eina_Bool +_verifyTCCheckImage(pixman_image_t *surface, pixman_image_t *ref) +{ + Eina_Bool ret = EINA_TRUE; + int surface_width, surface_height, surface_stride; + uint32_t *surface_data; + int pixman_width; + uint32_t *pixman_data; + int w, h; + + surface_width = pixman_image_get_width(surface); + surface_height = pixman_image_get_height(surface); + surface_stride = pixman_image_get_stride(surface); + surface_data = pixman_image_get_data(surface); + + pixman_width = pixman_image_get_width(ref); + pixman_data = pixman_image_get_data(ref); + + for (h = 0; h < surface_height; h++ ) + { + uint32_t *current_surface_data = surface_data + h * surface_stride; + uint32_t *current_pixman_data = pixman_data + h * pixman_width; + + for (w = 0; w < surface_width; w++) + { + ret = _verifyTCCheckPixel(*current_surface_data, *current_pixman_data, w, h); + if (ret == EINA_FALSE) + return ret; + + current_surface_data++; + current_pixman_data++; + } + } + + return ret; +} + +Eina_Bool +_verifyTC(tbm_surface_h surface, std::string basetype, std::string tcname) +{ + std::string refpath; + pixman_image_t *shot; + pixman_image_t *ref; + Eina_Bool ret = EINA_FALSE; + tbm_surface_info_s info; + int error; + + refpath = _verifyTCMakeRefPath(basetype, tcname); + if (refpath.length() == 0) + { + ERR("getting dumppath failed"); + return EINA_FALSE; + } + + ref = _verifyTCLoadImage(refpath.c_str()); + if (ref == (pixman_image_t *)NULL) + { + ERR("LoadImage failed"); + return EINA_FALSE; + } + + error = tbm_surface_map(surface, TBM_OPTION_READ, &info); + if (error != TBM_SURFACE_ERROR_NONE) + { + ERR("tbm_surface_map failed"); + pixman_image_unref(ref); + return EINA_FALSE; + } + + shot = pixman_image_create_bits_no_clear(PIXMAN_x8r8g8b8, info.width, info.height, (uint32_t *)info.planes[0].ptr, info.planes[0].stride >> 2); + if (shot == (pixman_image_t *)NULL) + { + ERR("pixman_image_create_bits_no_clear failed"); + tbm_surface_unmap(surface); + pixman_image_unref(ref); + return EINA_FALSE; + } + + ret = _verifyTCCheckImage(shot, ref); + + pixman_image_unref(shot); + pixman_image_unref(ref); + + return ret; +} + +Eina_Bool +etTCBase::verifyTC(std::string basetype, std::string tcname) +{ + tbm_surface_h surface = NULL; + Eina_Bool ret = EINA_FALSE; + + surface = efl_util_screenshot_take_tbm_surface(screenshot); + if (surface == NULL) + { + ERR("screenshot failed"); + return EINA_FALSE; + } + + ret = _verifyTC(surface, basetype, tcname); + if (ret == EINA_FALSE) + { + ERR("verify failed"); + _verifyTCDumpBuffer(surface, basetype, tcname); + } + +// UT_WAIT("test"); + + return ret; +} diff --git a/src/e_test_base.h b/src/e_test_base.h index f3e1c37..f9e85d6 100644 --- a/src/e_test_base.h +++ b/src/e_test_base.h @@ -2,6 +2,9 @@ #define __ET_TESTCASE_BASE__ #include "e_test_event.h" +#include +#include +#include using ::testing::Test; @@ -23,6 +26,7 @@ class etTCBase : public ::testing::Test Eina_Bool showTCWin(etWin *tw); Eina_Bool showActivateTCWin(etWin *tw); Eina_Bool removeTCWin(etWin *tw); + Eina_Bool verifyTC(std::string basetype, std::string tcname); protected: std::shared_ptr ev_result; @@ -36,6 +40,7 @@ class etTCBase : public ::testing::Test int numTw = 0; // number of wins int numGeomTw = 0; // number of wins using usr_geom + efl_util_screenshot_h screenshot; etWin *createWindow(Elm_Win_Type type, const char *name, Eina_Bool usr_geom, int x, int y, int w, int h); }; diff --git a/src/e_test_util.h b/src/e_test_util.h index 15167de..740565d 100644 --- a/src/e_test_util.h +++ b/src/e_test_util.h @@ -264,6 +264,12 @@ typedef struct _Window_Info_List } \ } while (0) +#define CAPTURE_WIDTH 720 +#define CAPTURE_HEIGHT 1280 +#define PNG_DEPTH 8 +#define C(b, m) (((b) >> (m)) & 0xFF) +#define FOURCC_STR(id) C(id, 0), C(id, 8), C(id, 16), C(id, 24) + #ifdef __cplusplus } #endif -- 2.7.4