diff --git a/meson.build b/meson.build index c42cf2d..96700f6 100644 --- a/meson.build +++ b/meson.build @@ -82,6 +82,9 @@ gio_dep = dependency('gio-unix-2.0', version: '>=' + glib_min_version) gusb_dep = dependency('gusb', version: '>= 0.3.0') mathlib_dep = cc.find_library('m', required: false) +# The following dependencies are only used for tests +cairo_dep = dependency('cairo', required: false) + # Drivers drivers = get_option('drivers').split(',') virtual_drivers = [ 'virtual_image' ] diff --git a/tests/meson.build b/tests/meson.build index 8ab3791..29ff6af 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -59,6 +59,7 @@ test_utils = static_library('fprint-test-utils', unit_tests = [ 'fpi-device', 'fpi-ssm', + 'fpi-assembling', ] if 'virtual_image' in drivers @@ -68,11 +69,41 @@ if 'virtual_image' in drivers ] endif +unit_tests_deps = { 'fpi-assembling' : [cairo_dep] } + +test_config = configuration_data() +test_config.set_quoted('SOURCE_ROOT', meson.source_root()) +test_config_h = configure_file(output: 'test-config.h', configuration: test_config) + foreach test_name: unit_tests + if unit_tests_deps.has_key(test_name) + missing_deps = false + foreach dep: unit_tests_deps[test_name] + if not dep.found() + missing_deps = true + break + endif + endforeach + + if missing_deps + # Create a dummy test that always skips instead + warning('Test @0@ cannot be compiled due to missing dependencies'.format(test_name)) + test(test_name, + find_program('sh'), + suite: ['unit-tests'], + args: ['-c', 'exit 77'], + ) + continue + endif + extra_deps = unit_tests_deps[test_name] + else + extra_deps = [] + endif + basename = 'test-' + test_name test_exe = executable(basename, - sources: basename + '.c', - dependencies: libfprint_private_dep, + sources: [basename + '.c', test_config_h], + dependencies: [ libfprint_private_dep ] + extra_deps, c_args: common_cflags, link_with: test_utils, ) diff --git a/tests/test-fpi-assembling.c b/tests/test-fpi-assembling.c new file mode 100644 index 0000000..c5b1bca --- /dev/null +++ b/tests/test-fpi-assembling.c @@ -0,0 +1,135 @@ +/* + * Example fingerprint device prints listing and deletion + * Enrolls your right index finger and saves the print to disk + * Copyright (C) 2019 Benjamin Berg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "fpi-assembling.h" +#include "fpi-image.h" +#include "test-config.h" + +typedef struct +{ + struct fpi_frame frame; + cairo_surface_t *surf; + guchar *data; + guint stride; + guint width; + guint height; + guint x; + guint y; +} cairo_frame; + +static unsigned char +cairo_get_pixel (struct fpi_frame_asmbl_ctx *ctx, + struct fpi_frame *frame, + unsigned int x, + unsigned int y) +{ + cairo_frame *c_frame = (void *) frame; /* Indirect cast to avoid alignment warning. */ + + x = x + c_frame->x; + y = y + c_frame->y; + + g_assert (x < c_frame->width); + g_assert (y < c_frame->height); + + return c_frame->data[x * 4 + y * c_frame->stride + 1]; +} + +static void +test_frame_assembling (void) +{ + g_autofree char *path = NULL; + cairo_surface_t *img = NULL; + int width, height, stride, offset; + int test_height; + guchar *data; + struct fpi_frame_asmbl_ctx ctx = { 0, }; + + g_autoptr(FpImage) fp_img = NULL; + GSList *frames = NULL; + + g_assert_false (SOURCE_ROOT == NULL); + path = g_build_path (G_DIR_SEPARATOR_S, SOURCE_ROOT, "tests", "vfs5011", "capture.png", NULL); + + img = cairo_image_surface_create_from_png (path); + data = cairo_image_surface_get_data (img); + width = cairo_image_surface_get_width (img); + height = cairo_image_surface_get_height (img); + stride = cairo_image_surface_get_stride (img); + g_assert_cmpint (cairo_image_surface_get_format (img), ==, CAIRO_FORMAT_RGB24); + + ctx.get_pixel = cairo_get_pixel; + ctx.frame_width = width; + ctx.frame_height = 20; + ctx.image_width = width; + + offset = 10; + test_height = height - (height - ctx.frame_height) % offset; + + /* for now, fixed offset */ + for (int y = 0; y + ctx.frame_height < height; y += offset) + { + cairo_frame *frame = g_new0 (cairo_frame, 1); + + frame->surf = img; + frame->width = width; + frame->height = height; + frame->stride = stride; + frame->data = data; + frame->x = 0; + frame->y = y; + //frame->y = test_height - ctx.frame_height - y; + + frames = g_slist_append (frames, frame); + } + //offset = -offset; + + fpi_do_movement_estimation (&ctx, frames); + for (GSList *l = frames->next; l != NULL; l = l->next) + { + cairo_frame * frame = l->data; + + g_assert_cmpint (frame->frame.delta_x, ==, 0); + g_assert_cmpint (frame->frame.delta_y, ==, offset); + } + + fp_img = fpi_assemble_frames (&ctx, frames); + g_assert_cmpint (fp_img->height, ==, test_height); + + /* The FpImage and cairo surface need to be identical in the test area */ + for (int y = 0; y < test_height; y++) + for (int x = 0; x < width; x++) + g_assert_cmpint (data[x * 4 + y * stride + 1], ==, fp_img->data[x + y * width]); + + g_slist_free_full (frames, g_free); + cairo_surface_destroy (img); + g_assert (1); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/assembling/frames", test_frame_assembling); + + return g_test_run (); +}