diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..07d73995 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.swp +_build diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..d7a4a246 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,145 @@ +include: + - local: '.gitlab-ci/libfprint-templates.yaml' + - project: 'wayland/ci-templates' + ref: master + file: '/templates/fedora.yml' + +variables: + extends: .libfprint_common_variables + FEDORA_TAG: rawhide + FEDORA_VERSION: rawhide + FEDORA_IMAGE: "$CI_REGISTRY/libfprint/$CI_PROJECT_NAME/fedora/$FEDORA_VERSION:$FEDORA_TAG" + BUNDLE: "org.freedesktop.libfprint.Demo.flatpak" + LAST_ABI_BREAK: "056ea541ddc97f5806cffbd99a12dc87e4da3546" + +stages: + - check-source + - build + - test + - flatpack + +image: "$FEDORA_IMAGE" + +.build_one_driver_template: &build_one_driver + script: + # Build with a driver that doesn't need imaging, or nss + - meson --werror -Ddrivers=$driver . _build + - ninja -C _build + - rm -rf _build/ + +.build_template: &build + script: + # And build with everything + - meson --werror -Ddrivers=all . _build + - ninja -C _build + - ninja -C _build install + +.build_template: &check_abi + script: + - ./.ci/check-abi ${LAST_ABI_BREAK} $(git rev-parse HEAD) + +build: + stage: build + except: + variables: + - $CI_PIPELINE_SOURCE == "schedule" + variables: + driver: virtual_image + <<: *build_one_driver + <<: *build +# <<: *check_abi + +test: + stage: test + except: + variables: + - $CI_PIPELINE_SOURCE == "schedule" + script: + - meson --werror -Ddrivers=all -Db_coverage=true . _build + - ninja -C _build + - meson test -C _build --verbose --no-stdsplit --timeout-multiplier 3 + - ninja -C _build coverage + artifacts: + paths: + - _build/meson-logs + expire_in: 1 week + +test_valgrind: + stage: test + except: + variables: + - $CI_PIPELINE_SOURCE == "schedule" + script: + - meson -Ddrivers=all . _build + - ninja -C _build + - meson test -C _build --verbose --no-stdsplit --setup=valgrind + +test_indent: + stage: check-source + except: + variables: + - $CI_PIPELINE_SOURCE == "schedule" + script: + - scripts/uncrustify.sh --check + +.flatpak_script_template: &flatpak_script + script: + - flatpak-builder --stop-at=${FLATPAK_MODULE} app ${MANIFEST_PATH} + # Make sure to keep this in sync with the Flatpak manifest, all arguments + # are passed except the config-args because we build it ourselves + - flatpak build app meson --prefix=/app --libdir=lib ${MESON_ARGS} _build + - flatpak build app ninja -C _build install + - flatpak build app rm -rf /app/include/ /app/lib/pkgconfig/ + - flatpak-builder --finish-only --repo=repo app ${MANIFEST_PATH} + # Generate a Flatpak bundle + - flatpak build-bundle repo ${BUNDLE} --runtime-repo=${RUNTIME_REPO} ${DBUS_ID} + +.flatpak_artifacts_template: &flatpak_artifacts + artifacts: + paths: + - ${BUNDLE} + when: always + expire_in: 30 days + +.flatpak_template: &flatpak + <<: *flatpak_script + <<: *flatpak_artifacts + +.flatpak_master_template: &flatpak_master + image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:3.32 + stage: flatpack + variables: + MANIFEST_PATH: "demo/org.freedesktop.libfprint.Demo.json" + # From demo/org.freedesktop.libfprint.Demo.json + MESON_ARGS: "-Dudev_rules=false -Dx11-examples=false -Dgtk-examples=true" + FLATPAK_MODULE: "libfprint" + DBUS_ID: "org.freedesktop.libfprint.Demo" + <<: *flatpak + +flatpak-auto master: + <<: *flatpak_master + when: always + only: + - tags + - master + +flatpak-manual master: + <<: *flatpak_master + when: manual + except: + refs: + - tags + - master + variables: + - $CI_PIPELINE_SOURCE == "schedule" + +# CONTAINERS creation stage +container_fedora_build: + extends: .fedora@container-build + only: + variables: + - $CI_PIPELINE_SOURCE == "schedule" && $CRON_TASK == "BUILD_CI_IMAGES" + variables: + GIT_STRATEGY: none # no need to pull the whole tree for rebuilding the image + # a list of packages to install + FEDORA_RPMS: $LIBFPRINT_DEPENDENCIES diff --git a/.gitlab-ci/libfprint-templates.yaml b/.gitlab-ci/libfprint-templates.yaml new file mode 100644 index 00000000..e8a510f8 --- /dev/null +++ b/.gitlab-ci/libfprint-templates.yaml @@ -0,0 +1,26 @@ +.libfprint_common_variables: + LIBFPRINT_DEPENDENCIES: + doxygen + flatpak-builder + gcc + gcc-c++ + gcovr + git + glib2-devel + glibc-devel + gobject-introspection-devel + gtk-doc + gtk3-devel + libabigail + libgusb-devel + libX11-devel + libXv-devel + meson + nss-devel + pixman-devel + python3-cairo + python3-gobject + systemd + umockdev + uncrustify + valgrind diff --git a/NEWS.tod.md b/NEWS.tod.md new file mode 100644 index 00000000..66e764ff --- /dev/null +++ b/NEWS.tod.md @@ -0,0 +1,25 @@ +### libfprint-TOD v1 + +- First public release +- Based on [libfprint 1.90.1](https://gitlab.freedesktop.org/libfprint/libfprint/-/releases#v1.90.1) +- Bumped TOD version to 1 + +### Highlights of the Drivers API changes + +Both the driver and external APIs have changed, as both the verify and the identify functions now have early reporting mechanisms. + +- Added API for early report of matching results or retry errors +- Verify and identification completion functions have been simplified +- Support variadic arguments in error functions +- Various re-definitions of ownership handling +- Add convenience API to change state after a timeout +- Add unit tests for all the drivers API + +### Drivers required changes +As per the early report mechanism, drivers need to adapt, in particular: + - New pkg-config dependency name is `libfprint-2-tod-1` + - Verification and Identification API for non-image drivers has changed and drivers need to both `report` the result of the action and complete it: + - `fpi_device_{verify,identify}_report` must inform whether a match/no-match or identification happened or report a *retry error*. + - `fpi_device_{verify,identify}_complete` must be called once the device has completed the verification / identification process, in case reporting device errors (not retry ones!) + +You can see examples of changes needed in the [reference example driver](https://gitlab.freedesktop.org/3v1n0/libfprint-tod-example-driver/-/commit/8308f84f7d1cfd1b9ed0936c13c73b43a4a46772) or the [upstream synaptics driver](https://gitlab.freedesktop.org/libfprint/libfprint/-/merge_requests/112/diffs) diff --git a/README.tod.md b/README.tod.md new file mode 100644 index 00000000..34d71a8b --- /dev/null +++ b/README.tod.md @@ -0,0 +1,9 @@ +### libfprint-TOD - libfprint for Touch OEM Drivers + +This is a light fork of libfprint to expose internal Drivers API in order to +create drivers as shared libraries. + +Fork is hosted at: https://gitlab.freedesktop.org/3v1n0/libfprint/tree/tod + +An example driver implementation is available: + - https://gitlab.freedesktop.org/3v1n0/libfprint-tod-example-driver diff --git a/libfprint/fp-context.c b/libfprint/fp-context.c index ffd38a4a..f0dd6858 100644 --- a/libfprint/fp-context.c +++ b/libfprint/fp-context.c @@ -24,6 +24,11 @@ #include "fpi-device.h" #include +#include +#ifdef HAVE_LIBFPRINT_TOD +#include "tod/tod-shared-loader.h" +#endif + /** * SECTION: fp-context * @title: FpContext @@ -214,6 +219,8 @@ fp_context_finalize (GObject *object) g_object_run_dispose (G_OBJECT (priv->usb_ctx)); g_clear_object (&priv->usb_ctx); + fpi_tod_shared_drivers_unregister (); + G_OBJECT_CLASS (fp_context_parent_class)->finalize (object); } @@ -265,11 +272,16 @@ static void fp_context_init (FpContext *self) { g_autoptr(GError) error = NULL; + g_autoptr(GArray) shared_drivers = NULL; FpContextPrivate *priv = fp_context_get_instance_private (self); guint i; priv->drivers = fpi_get_driver_types (); + fpi_tod_shared_drivers_register (); + shared_drivers = fpi_tod_shared_drivers_get (); + g_array_prepend_vals (priv->drivers, shared_drivers->data, shared_drivers->len); + if (get_drivers_whitelist_env ()) { for (i = 0; i < priv->drivers->len;) diff --git a/libfprint/fp-image.c b/libfprint/fp-image.c index ac70d68d..6b63a438 100644 --- a/libfprint/fp-image.c +++ b/libfprint/fp-image.c @@ -23,6 +23,7 @@ #include "fpi-image.h" #include "fpi-log.h" +#include #include /** diff --git a/libfprint/fpi-assembling.h b/libfprint/fpi-assembling.h index 295e3159..6e1b7071 100644 --- a/libfprint/fpi-assembling.h +++ b/libfprint/fpi-assembling.h @@ -110,6 +110,7 @@ struct fpi_line_asmbl_ctx unsigned char (*get_pixel)(struct fpi_line_asmbl_ctx *ctx, GSList *line, unsigned int x); + gpointer _padding_dummy[32]; }; FpImage *fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx, diff --git a/libfprint/fpi-device.h b/libfprint/fpi-device.h index 1f53eaf9..23656ee1 100644 --- a/libfprint/fpi-device.h +++ b/libfprint/fpi-device.h @@ -45,6 +45,10 @@ struct _FpIdEntry const gchar *virtual_envvar; }; guint64 driver_data; + + /*< private >*/ + /* padding for future expansion */ + gpointer _padding_dummy[16]; }; /** @@ -129,6 +133,10 @@ struct _FpDeviceClass void (*delete) (FpDevice * device); void (*cancel) (FpDevice *device); + + /*< private >*/ + /* padding for future expansion */ + gpointer _padding_dummy[32]; }; /** diff --git a/libfprint/fpi-image-device.h b/libfprint/fpi-image-device.h index 155390de..864cb905 100644 --- a/libfprint/fpi-image-device.h +++ b/libfprint/fpi-image-device.h @@ -93,6 +93,10 @@ struct _FpImageDeviceClass void (*change_state) (FpImageDevice *dev, FpiImageDeviceState state); void (*deactivate) (FpImageDevice *dev); + + /*< private >*/ + /* padding for future expansion */ + gpointer _padding_dummy[32]; }; void fpi_image_device_set_bz3_threshold (FpImageDevice *self, diff --git a/libfprint/fpi-image.c b/libfprint/fpi-image.c index 83440376..1fb88656 100644 --- a/libfprint/fpi-image.c +++ b/libfprint/fpi-image.c @@ -24,6 +24,7 @@ #include "fpi-log.h" #include +#include #if HAVE_PIXMAN #include @@ -108,12 +109,12 @@ fpi_mean_sq_diff_norm (const guint8 *buf1, return res / size; } -#if HAVE_PIXMAN FpImage * fpi_image_resize (FpImage *orig_img, guint w_factor, guint h_factor) { +#if HAVE_PIXMAN int new_width = orig_img->width * w_factor; int new_height = orig_img->height * h_factor; pixman_image_t *orig, *resized; @@ -146,5 +147,9 @@ fpi_image_resize (FpImage *orig_img, pixman_image_unref (resized); return newimg; -} +#else + fp_err ("Libfprint compiled without pixman support, impossible to resize"); + + return NULL; #endif +} diff --git a/libfprint/fpi-image.h b/libfprint/fpi-image.h index dd6dbf88..2855c78f 100644 --- a/libfprint/fpi-image.h +++ b/libfprint/fpi-image.h @@ -20,7 +20,6 @@ #pragma once -#include #include "fp-image.h" /** @@ -76,8 +75,6 @@ gint fpi_mean_sq_diff_norm (const guint8 *buf1, const guint8 *buf2, gint size); -#if HAVE_PIXMAN FpImage *fpi_image_resize (FpImage *orig, guint w_factor, guint h_factor); -#endif diff --git a/libfprint/fpi-usb-transfer.h b/libfprint/fpi-usb-transfer.h index 09d22e85..c69acfcb 100644 --- a/libfprint/fpi-usb-transfer.h +++ b/libfprint/fpi-usb-transfer.h @@ -101,6 +101,10 @@ struct _FpiUsbTransfer /* Data free function */ GDestroyNotify free_buffer; + + /*< private >*/ + /* padding for future expansion */ + gpointer _padding_dummy[32]; }; GType fpi_usb_transfer_get_type (void) G_GNUC_CONST; diff --git a/libfprint/meson.build b/libfprint/meson.build index acb97c4d..322a2fcc 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -31,6 +31,7 @@ libfprint_private_headers = [ 'fpi-byte-reader.h', 'fpi-byte-utils.h', 'fpi-byte-writer.h', + 'fpi-compat.h', 'fpi-context.h', 'fpi-device.h', 'fpi-image-device.h', @@ -238,6 +239,10 @@ libfprint_private = static_library('fprint-private', link_with: libnbis, install: false) +if get_option('tod') + subdir('tod') +endif + libfprint_drivers = static_library('fprint-drivers', sources: drivers_sources, c_args: drivers_cflags, diff --git a/libfprint/tod/libfprint-tod.ver b/libfprint/tod/libfprint-tod.ver new file mode 100644 index 00000000..d18569f1 --- /dev/null +++ b/libfprint/tod/libfprint-tod.ver @@ -0,0 +1,6 @@ +LIBFPRINT_TOD_1.0.0 { +global: + fpi_*; +local: + *; +}; diff --git a/libfprint/tod/meson.build b/libfprint/tod/meson.build new file mode 100644 index 00000000..58c48680 --- /dev/null +++ b/libfprint/tod/meson.build @@ -0,0 +1,86 @@ +tod_soversion = 1 +tod_subpath = versioned_libname / 'tod-@0@'.format(tod_soversion) +tod_modules_prefix = get_option('libdir') / tod_subpath + +tod_conf = configuration_data() +tod_conf.set_quoted('TOD_DRIVERS_DIR', + get_option('prefix') / get_option('libdir') / tod_subpath) +configure_file(output: 'tod-config.h', configuration: tod_conf) + +gmodule_dep = dependency('gmodule-2.0', version: '>=' + glib_min_version) +deps += gmodule_dep + +mapfile = files('libfprint-tod.ver') + +libfprint_tod_private = static_library('fprint-tod-private', + sources: [ + 'tod-shared-loader.c', + ], + include_directories: include_directories('..'), + link_with: libfprint_private, + dependencies: deps, + install: false, +) + +tod_sources = [] +foreach source: libfprint_private_sources + tod_sources += '..' / source +endforeach + +libfprint_tod = library(versioned_libname.split('lib')[1] + '-tod', + sources: [ + tod_sources, + ], + soversion: tod_soversion, + include_directories: include_directories('..'), + link_args: [ + '-Wl,--version-script,@0@/@1@'.format(meson.source_root(), mapfile[0]), + '-Wl,--unresolved-symbols=ignore-in-object-files' + ], + link_depends: mapfile, + link_with: [libfprint_private], + dependencies: deps, + install: true) + +deps += declare_dependency( + link_with: [ + libfprint_tod, + libfprint_tod_private, + ] +) + +pkgconfig = import('pkgconfig') +pkgconfig.generate(libfprint_tod, + name: versioned_libname + '-tod', + filebase: '@0@-tod-@1@'.format(versioned_libname, tod_soversion), + description: 'Private Libfprint Touch Drivers API', + version: meson.project_version() + '+tod@0@'.format(tod_soversion), + subdirs: tod_subpath, + requires_private: [ + versioned_libname, + ], + variables: [ + 'tod_driversdir=${libdir}/@0@'.format(tod_subpath) + ] +) + +tod_headers = [] +extra_libfprint_headers = [ + 'drivers_api.h', +] + +foreach header: libfprint_private_headers + extra_libfprint_headers + tod_headers += '..' / header +endforeach + +custom_target('tod_fpi_enums_headers', + depends: fpi_enums, + input: fpi_enums_h, + output: 'fpi-enums.h', + command: ['cp', '@INPUT@', '-v', '@OUTPUT@'], + install: true, + install_dir: get_option('includedir') / tod_subpath) + +install_headers(tod_headers, + subdir: tod_subpath +) diff --git a/libfprint/tod/tod-shared-loader.c b/libfprint/tod/tod-shared-loader.c new file mode 100644 index 00000000..3fb04209 --- /dev/null +++ b/libfprint/tod/tod-shared-loader.c @@ -0,0 +1,151 @@ +/* + * Shared library loader for libfprint + * Copyright (C) 2019 Marco Trevisan + * + * 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 + */ + +#define FP_COMPONENT "tod" + +#include + +#include "tod-shared-loader.h" +#include "fpi-device.h" +#include "fpi-log.h" +#include "tod-config.h" + +#define FPI_TOD_ENTRY_GTYPE_GETTER "fpi_tod_shared_driver_get_type" + +static GArray *shared_drivers = NULL; +static GList *shared_modules = NULL; + +typedef GModule FpiTodModule; +typedef GType (*FpiTodShardDriverTypeGetter) (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpiTodModule, g_module_close); + +static const char * +get_tod_drivers_dir (void) +{ + const char *tod_env_path = g_getenv ("FP_TOD_DRIVERS_DIR"); + + if (!tod_env_path || *tod_env_path == '\0') + return TOD_DRIVERS_DIR; + + return tod_env_path; +} + +void +fpi_tod_shared_drivers_register (void) +{ + const char *dirname; + const char *basename; + + g_autoptr(GError) error = NULL; + g_autoptr(GDir) dir = NULL; + gpointer symbol; + + g_assert_null (shared_drivers); + + dirname = get_tod_drivers_dir (); + dir = g_dir_open (dirname, 0, &error); + + shared_drivers = g_array_new (TRUE, FALSE, sizeof (GType)); + + if (error) + { + fp_dbg ("Impossible to load the shared drivers dir %s", error->message); + return; + } + + while ((basename = g_dir_read_name (dir)) != NULL) + { + g_autoptr(FpiTodModule) module = NULL; + g_autoptr(GTypeClass) type_class = NULL; + g_autofree char *module_path = NULL; + FpiTodShardDriverTypeGetter type_getter; + FpDeviceClass *cls; + GType driver; + + if (!g_str_has_prefix (basename, "lib")) + continue; + + if (!g_str_has_suffix (basename, ".so")) + continue; + + module_path = g_build_filename (dirname, basename, NULL); + + if (!g_file_test (module_path, G_FILE_TEST_IS_REGULAR)) + continue; + + fp_dbg ("Opening driver %s", module_path); + + module = g_module_open (module_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + + if (!module) + { + fp_err ("Impossible to load module %s: %s", module_path, + g_module_error ()); + continue; + } + + if (!g_module_symbol (module, FPI_TOD_ENTRY_GTYPE_GETTER, &symbol)) + { + fp_err ("Library %s doesn't expose the required entry point symbol", + module_path); + continue; + } + + type_getter = symbol; + driver = type_getter (); + fp_dbg ("Found TOD entry point symbol %p, GType is %lu", symbol, driver); + + if (!G_TYPE_IS_OBJECT (driver) || !g_type_is_a (driver, FP_TYPE_DEVICE)) + { + fp_err ("Library %s returned GType (%lu) doesn't represent a device", + module_path, driver); + continue; + } + + type_class = g_type_class_ref (driver); + g_assert_true (g_type_check_class_is_a (type_class, FP_TYPE_DEVICE)); + + cls = FP_DEVICE_CLASS (type_class); + + fp_dbg ("Loading driver %s (%s)", cls->id, cls->full_name); + g_array_append_val (shared_drivers, driver); + + shared_modules = g_list_prepend (shared_modules, + g_steal_pointer (&module)); + } +} + +void +fpi_tod_shared_drivers_unregister (void) +{ + g_clear_pointer (&shared_drivers, g_array_unref); + + if (g_strcmp0 (g_getenv ("FP_TOD_KEEP_MODULES_OPEN"), "TRUE") != 0) + { + g_list_free_full (shared_modules, (GDestroyNotify) g_module_close); + shared_modules = NULL; + } +} + +GArray * +fpi_tod_shared_drivers_get (void) +{ + return g_array_ref (shared_drivers); +} diff --git a/libfprint/tod/tod-shared-loader.h b/libfprint/tod/tod-shared-loader.h new file mode 100644 index 00000000..8ed2e811 --- /dev/null +++ b/libfprint/tod/tod-shared-loader.h @@ -0,0 +1,28 @@ +/* + * Shared library loader for libfprint + * Copyright (C) 2019 Marco Trevisan + * + * 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 + */ + +#pragma once + +#include + + +void fpi_tod_shared_drivers_register (void); +void fpi_tod_shared_drivers_unregister (void); + +GArray *fpi_tod_shared_drivers_get (void); diff --git a/meson.build b/meson.build index d4248d80..ccc68fec 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('libfprint', [ 'c', 'cpp' ], - version: '1.90.1', + version: '1.90.1+tod1', license: 'LGPLv2.1+', default_options: [ 'buildtype=debugoptimized', @@ -190,6 +190,8 @@ if get_option('gtk-examples') endif endif +libfprint_conf.set10('HAVE_LIBFPRINT_TOD', get_option('tod')) + configure_file(output: 'config.h', configuration: libfprint_conf) subdir('libfprint') diff --git a/meson_options.txt b/meson_options.txt index 746efdc7..d059ae59 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -22,3 +22,8 @@ option('doc', description: 'Whether to build the API documentation', type: 'boolean', value: true) + +option('tod', + description: 'Whether to build the TOD library', + type: 'boolean', + value: true) diff --git a/tests/meson.build b/tests/meson.build index cce5c041..0a556a99 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -189,3 +189,45 @@ if valgrind.found() valgrind.path(), glib_suppressions, python_suppressions) ]) endif + +if get_option('tod') + tod_envs = envs + tod_envs.set('FP_TOD_DRIVERS_DIR', meson.current_build_dir()) + tod_envs.set('FP_TOD_KEEP_MODULES_OPEN', 'TRUE') + tod_envs.set('FP_VIRTUAL_FAKE_DEVICE', 'yes') + tod_envs.append('FP_DRIVERS_WHITELIST', 'fake_test_dev') + + fake_driver = shared_module('device-fake-tod-driver', + sources: [ + 'test-device-fake-tod.c', + ], + link_with: [ + test_utils, + libfprint_tod, + ], + include_directories: include_directories('../libfprint'), + dependencies: deps, + install: false + ) + + tod_unit_tests = [ + 'fp-context-tod', + 'fp-device-tod', + ] + + foreach test_name: tod_unit_tests + basename = 'test-' + test_name + test_exe = executable(basename, + sources: basename + '.c', + dependencies: libfprint_private_dep, + c_args: common_cflags, + link_with: test_utils, + ) + test(test_name, + find_program('test-runner.sh'), + suite: ['unit-tests', 'tod'], + args: [test_exe], + env: tod_envs, + ) + endforeach +endif diff --git a/tests/test-device-fake-tod.c b/tests/test-device-fake-tod.c new file mode 100644 index 00000000..aea73870 --- /dev/null +++ b/tests/test-device-fake-tod.c @@ -0,0 +1,28 @@ +/* + * Virtual driver for device debugging + * + * Copyright (C) 2019 Marco Trevisan + * + * 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 "test-device-fake-tod.h" +#include "test-device-fake.h" + +GType +fpi_tod_shared_driver_get_type (void) +{ + return fpi_device_fake_get_type (); +} diff --git a/tests/test-device-fake-tod.h b/tests/test-device-fake-tod.h new file mode 100644 index 00000000..36602125 --- /dev/null +++ b/tests/test-device-fake-tod.h @@ -0,0 +1,25 @@ +/* + * Virtual driver for device debugging + * + * Copyright (C) 2019 Marco Trevisan + * + * 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 + */ + +#pragma once + +#include + +GType fpi_tod_shared_driver_get_type (void); diff --git a/tests/test-fp-context-tod.c b/tests/test-fp-context-tod.c new file mode 100644 index 00000000..abafc62d --- /dev/null +++ b/tests/test-fp-context-tod.c @@ -0,0 +1,101 @@ +/* + * FpContext Unit tests + * Copyright (C) 2019 Marco Trevisan + * + * 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 + +static void +test_context_new (void) +{ + g_autoptr(FpContext) context = fp_context_new (); + g_assert_true (FP_CONTEXT (context)); +} + +static void +test_context_has_no_devices (void) +{ + g_autoptr(FpContext) context = NULL; + GPtrArray *devices; + const char *old_drivers_dir = g_getenv ("FP_TOD_DRIVERS_DIR"); + + g_setenv ("FP_TOD_DRIVERS_DIR", "__HOPEFULLY_AN_INVALID_PATH", TRUE); + context = fp_context_new (); + devices = fp_context_get_devices (context); + g_setenv ("FP_TOD_DRIVERS_DIR", old_drivers_dir, TRUE); + + g_assert_nonnull (devices); + g_assert_cmpuint (devices->len, ==, 0); +} + +static void +test_context_has_fake_device (void) +{ + g_autoptr(FpContext) context = NULL; + FpDevice *fake_device = NULL; + GPtrArray *devices; + unsigned int i; + + context = fp_context_new (); + devices = fp_context_get_devices (context); + + g_assert_nonnull (devices); + g_assert_cmpuint (devices->len, ==, 1); + + for (i = 0; i < devices->len; ++i) + { + FpDevice *device = devices->pdata[i]; + + if (g_strcmp0 (fp_device_get_driver (device), "fake_test_dev") == 0) + { + fake_device = device; + break; + } + } + + g_assert_true (FP_IS_DEVICE (fake_device)); +} + +static void +test_context_enumerates_new_devices (void) +{ + g_autoptr(FpContext) context = NULL; + GPtrArray *devices; + + context = fp_context_new (); + + fp_context_enumerate (context); + devices = fp_context_get_devices (context); + + g_assert_nonnull (devices); + g_assert_cmpuint (devices->len, ==, 1); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_assert_nonnull (g_getenv ("FP_TOD_DRIVERS_DIR")); + + g_test_add_func ("/context/new", test_context_new); + g_test_add_func ("/context/no-devices", test_context_has_no_devices); + g_test_add_func ("/context/has-virtual-device", test_context_has_fake_device); + g_test_add_func ("/context/enumerates-new-devices", test_context_enumerates_new_devices); + + return g_test_run (); +} diff --git a/tests/test-fp-device-tod.c b/tests/test-fp-device-tod.c new file mode 100644 index 00000000..d1a132b7 --- /dev/null +++ b/tests/test-fp-device-tod.c @@ -0,0 +1,270 @@ +/* + * FpDevice Unit tests + * Copyright (C) 2019 Marco Trevisan + * + * 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 "test-utils.h" + +static FptContext * +fpt_context_new_with_fake_dev (void) +{ + FptContext *tctx; + GPtrArray *devices; + unsigned int i; + + tctx = fpt_context_new (); + devices = fp_context_get_devices (tctx->fp_context); + + g_assert_nonnull (devices); + g_assert_cmpuint (devices->len, ==, 1); + + for (i = 0; i < devices->len; ++i) + { + FpDevice *device = devices->pdata[i]; + + if (g_strcmp0 (fp_device_get_driver (device), "fake_test_dev") == 0) + { + tctx->device = device; + break; + } + } + + g_assert_true (FP_IS_DEVICE (tctx->device)); + g_object_add_weak_pointer (G_OBJECT (tctx->device), (gpointer) & tctx->device); + + return tctx; +} + +static void +on_device_opened (FpDevice *dev, GAsyncResult *res, FptContext *tctx) +{ + g_autoptr(GError) error = NULL; + + g_assert_true (fp_device_open_finish (dev, res, &error)); + g_assert_no_error (error); + g_assert_true (fp_device_is_open (tctx->device)); + + tctx->user_data = GUINT_TO_POINTER (TRUE); +} + +static void +test_device_open_async (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open (tctx->device, NULL, (GAsyncReadyCallback) on_device_opened, tctx); + + while (!GPOINTER_TO_UINT (tctx->user_data)) + g_main_context_iteration (NULL, TRUE); +} + +static void +on_device_closed (FpDevice *dev, GAsyncResult *res, FptContext *tctx) +{ + g_autoptr(GError) error = NULL; + + g_assert_true (fp_device_close_finish (dev, res, &error)); + g_assert_no_error (error); + g_assert_false (fp_device_is_open (tctx->device)); + + tctx->user_data = GUINT_TO_POINTER (TRUE); +} + +static void +test_device_close_async (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open (tctx->device, NULL, (GAsyncReadyCallback) on_device_opened, tctx); + while (!tctx->user_data) + g_main_context_iteration (NULL, TRUE); + + tctx->user_data = GUINT_TO_POINTER (FALSE); + fp_device_close (tctx->device, NULL, (GAsyncReadyCallback) on_device_closed, tctx); + + while (!GPOINTER_TO_UINT (tctx->user_data)) + g_main_context_iteration (NULL, TRUE); +} + +static void +test_device_open_sync (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open_sync (tctx->device, NULL, &error); + g_assert_no_error (error); + g_assert_true (fp_device_is_open (tctx->device)); + + fp_device_open_sync (tctx->device, NULL, &error); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_ALREADY_OPEN); +} + +static void +on_open_notify (FpDevice *rdev, GParamSpec *spec, FptContext *tctx) +{ + g_assert_cmpstr (spec->name, ==, "open"); + tctx->user_data = GUINT_TO_POINTER (TRUE); +} + +static void +test_device_open_sync_notify (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + g_signal_connect (tctx->device, "notify::open", G_CALLBACK (on_open_notify), tctx); + fp_device_open_sync (tctx->device, NULL, &error); + g_assert_no_error (error); + g_assert_true (GPOINTER_TO_INT (tctx->user_data)); +} + +static void +test_device_close_sync (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + fp_device_close_sync (tctx->device, NULL, &error); + g_assert_no_error (error); + g_assert_false (fp_device_is_open (tctx->device)); + + fp_device_close_sync (tctx->device, NULL, &error); + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_NOT_OPEN); +} + +static void +on_close_notify (FpDevice *rdev, GParamSpec *spec, FptContext *tctx) +{ + g_assert_cmpstr (spec->name, ==, "open"); + tctx->user_data = GUINT_TO_POINTER (TRUE); +} + +static void +test_device_close_sync_notify (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + + g_signal_connect (tctx->device, "notify::open", G_CALLBACK (on_close_notify), tctx); + fp_device_close_sync (tctx->device, NULL, &error); + g_assert_no_error (error); + g_assert_true (GPOINTER_TO_INT (tctx->user_data)); +} + +static void +test_device_get_driver (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_cmpstr (fp_device_get_driver (tctx->device), ==, "fake_test_dev"); +} + +static void +test_device_get_device_id (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_cmpstr (fp_device_get_device_id (tctx->device), ==, "fake_test_dev"); +} + +static void +test_device_get_name (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_cmpstr (fp_device_get_name (tctx->device), ==, + "Virtual device for debugging"); +} + +static void +test_device_get_scan_type (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_cmpint (fp_device_get_scan_type (tctx->device), ==, FP_SCAN_TYPE_PRESS); +} + +static void +test_device_get_nr_enroll_stages (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_cmpuint (fp_device_get_nr_enroll_stages (tctx->device), ==, 5); +} + +static void +test_device_supports_identify (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_true (fp_device_supports_identify (tctx->device)); +} + +static void +test_device_supports_capture (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_true (fp_device_supports_capture (tctx->device)); +} + +static void +test_device_has_storage (void) +{ + g_autoptr(FptContext) tctx = fpt_context_new_with_fake_dev (); + + fp_device_open_sync (tctx->device, NULL, NULL); + g_assert_true (fp_device_has_storage (tctx->device)); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_assert_nonnull (g_getenv ("FP_TOD_DRIVERS_DIR")); + + g_test_add_func ("/device/async/open", test_device_open_async); + g_test_add_func ("/device/async/close", test_device_close_async); + g_test_add_func ("/device/sync/open", test_device_open_sync); + g_test_add_func ("/device/sync/open/notify", test_device_open_sync_notify); + g_test_add_func ("/device/sync/close", test_device_close_sync); + g_test_add_func ("/device/sync/close/notify", test_device_close_sync_notify); + g_test_add_func ("/device/sync/get_driver", test_device_get_driver); + g_test_add_func ("/device/sync/get_device_id", test_device_get_device_id); + g_test_add_func ("/device/sync/get_name", test_device_get_name); + g_test_add_func ("/device/sync/get_scan_type", test_device_get_scan_type); + g_test_add_func ("/device/sync/get_nr_enroll_stages", test_device_get_nr_enroll_stages); + g_test_add_func ("/device/sync/supports_identify", test_device_supports_identify); + g_test_add_func ("/device/sync/supports_capture", test_device_supports_capture); + g_test_add_func ("/device/sync/has_storage", test_device_has_storage); + + return g_test_run (); +}