e_test_base: Add verifyTC function 28/257628/1
authorJunkyeong Kim <jk0430.kim@samsung.com>
Wed, 28 Apr 2021 10:52:58 +0000 (19:52 +0900)
committerJunkyeong Kim <jk0430.kim@samsung.com>
Wed, 28 Apr 2021 10:53:01 +0000 (19:53 +0900)
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 <jk0430.kim@samsung.com>
configure.ac
packaging/e-tizen-unittests.spec
src/e_test_base.cpp
src/e_test_base.h
src/e_test_util.h

index d280684..e9db6ce 100644 (file)
@@ -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}])
index 8613cde..168a7fe 100644 (file)
@@ -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.
index 3027967..78834d6 100644 (file)
@@ -1,11 +1,47 @@
 #include "e_test_base.h"
+#include <png.h>
+#include <cairo.h>
+#include <pixman.h>
 
 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;
+}
index f3e1c37..f9e85d6 100644 (file)
@@ -2,6 +2,9 @@
 #define __ET_TESTCASE_BASE__
 
 #include "e_test_event.h"
+#include <tbm_surface.h>
+#include <tbm_surface_internal.h>
+#include <string>
 
 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<eventItem> 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);
 };
index 15167de..740565d 100644 (file)
@@ -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