From 02b8a12c9f72917cf1c203386c80df8cab76e03e Mon Sep 17 00:00:00 2001 From: Abhinav Baid Date: Sun, 9 Jun 2024 01:30:06 +0100 Subject: [PATCH] Add integration test --- tests/create-misc-driver-test.py.in | 99 +++++++++++++++++++++++++++++ tests/crfpmoc/custom.ioctl | 21 ++++++ tests/crfpmoc/custom.py | 86 +++++++++++++++++++++++++ tests/crfpmoc/device | 93 +++++++++++++++++++++++++++ tests/meson.build | 4 ++ 5 files changed, 303 insertions(+) create mode 100755 tests/create-misc-driver-test.py.in create mode 100644 tests/crfpmoc/custom.ioctl create mode 100755 tests/crfpmoc/custom.py create mode 100644 tests/crfpmoc/device diff --git a/tests/create-misc-driver-test.py.in b/tests/create-misc-driver-test.py.in new file mode 100755 index 00000000..88f1d116 --- /dev/null +++ b/tests/create-misc-driver-test.py.in @@ -0,0 +1,99 @@ +#!/usr/bin/python3 + +BUILDDIR='@BUILDDIR@' +SRCDIR='@SRCDIR@' + +import os +import sys +library_path = BUILDDIR + '/libfprint/' + +# Relaunch ourselves with a changed environment so +# that we're loading the development version of libfprint +if 'LD_LIBRARY_PATH' not in os.environ or not library_path in os.environ['LD_LIBRARY_PATH']: + os.environ['LD_LIBRARY_PATH'] = library_path + os.environ['GI_TYPELIB_PATH'] = f'{BUILDDIR}/libfprint/' + os.environ['FP_DEVICE_EMULATION'] = '1' + try: + os.execv(sys.argv[0], sys.argv) + except Exception as e: + print('Could not run script with new library path') + sys.exit(1) + +import gi +gi.require_version('FPrint', '2.0') +from gi.repository import FPrint + +import re +import subprocess + +def print_usage(): + print(f'Usage: {sys.argv[0]} driver [test-variant-name]') + print('A test variant name is optional, and must be all lower case letters, or dashes, with no spaces') + print(f'The captured data will be stored in {SRCDIR}/tests/[driver name]-[test variant name]') + print(f'Create custom.py prior to execution for non image device tests.') + +if len(sys.argv) > 3: + print_usage() + sys.exit(1) + +driver_name = sys.argv[1] +os.environ['FP_DRIVERS_ALLOWLIST'] = driver_name + +test_variant = None +if len(sys.argv) == 3: + valid_re = re.compile('[a-z-]*') + test_variant = sys.argv[2] + if (not valid_re.match(test_variant) or + test_variant.startswith('-') or + test_variant.endswith('-')): + print(f'Invalid variant name {test_variant}\n') + print_usage() + sys.exit(1) + +# Check that running as root + +if os.geteuid() != 0: + print(f'{sys.argv[0]} is expected to be run as root') + sys.exit(1) + +# Find the fingerprint reader +ctx = FPrint.Context() +ctx.enumerate() +devices = ctx.get_devices() +if len(devices) == 0: + print('Could not find a supported fingerprint reader') + sys.exit(1) +elif len(devices) > 1: + print('Capture requires a single supported fingerprint reader to be plugged in') + sys.exit(1) + +test_name = driver_name +if test_variant: + test_name = driver_name + '-' + test_variant +misc_device = devices[0].get_property('fpi-udev-data-misc') + +print(f'### Detected misc device {misc_device}') + +# Make directory + +test_dir = SRCDIR + '/tests/' + test_name +os.makedirs(test_dir, mode=0o775, exist_ok=True) + +# Capture device info + +args = ['umockdev-record', misc_device] +device_out = open(test_dir + '/device', 'w') +process = subprocess.Popen(args, stdout=device_out) +process.wait() + +print('### Capturing fingerprint, please swipe or press your finger on the reader') +capture_file = "custom.ioctl" +cmd = ['umockdev-record', '--ioctl', f'{misc_device}={os.path.join(test_dir, capture_file)}', 'python3', os.path.join(test_dir, "custom.py")] + +with subprocess.Popen(cmd) as capture_process: + capture_process.wait() + if capture_process.returncode != 0: + print('Failed to capture fingerprint') + sys.exit(1) + +print(f"\nDone! Don't forget to add {test_name} to tests/meson.build") diff --git a/tests/crfpmoc/custom.ioctl b/tests/crfpmoc/custom.ioctl new file mode 100644 index 00000000..b844abf0 --- /dev/null +++ b/tests/crfpmoc/custom.ioctl @@ -0,0 +1,21 @@ +@DEV /dev/cros_fp +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000080000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000030000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000010000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000030000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000010000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000030000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000010000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000030000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000010000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000030000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000000000000 +CROS_EC_DEV_IOCXCMD_V2 48 010000000304000000000000300000000000000046504320090000001B020000010000009466000047524559A000A0000800FF0324140000050001000100000004000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000040000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000000000000 +CROS_EC_DEV_IOCXCMD_V2 22 000000000704000000000000160000000000000065630100B44A020007B30300DC33505A070000000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000040000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000000000000 +CROS_EC_DEV_IOCXCMD_V2 22 000000000704000000000000160000000000000060630100874E0200D6B6030027289A5A070000000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000080000000 +CROS_EC_DEV_IOCXCMD_V2 4 000000000204000004000000040000000000000000000000 diff --git a/tests/crfpmoc/custom.py b/tests/crfpmoc/custom.py new file mode 100755 index 00000000..610c76b1 --- /dev/null +++ b/tests/crfpmoc/custom.py @@ -0,0 +1,86 @@ +#!/usr/bin/python3 + +import traceback +import sys +import gi + +gi.require_version('FPrint', '2.0') +from gi.repository import FPrint, GLib + +# Exit with error on any exception, included those happening in async callbacks +sys.excepthook = lambda *args: (traceback.print_exception(*args), sys.exit(1)) + +ctx = GLib.main_context_default() + +c = FPrint.Context() +c.enumerate() +devices = c.get_devices() + +d = devices[0] +del devices + +assert d.get_driver() == "crfpmoc" +assert not d.has_feature(FPrint.DeviceFeature.CAPTURE) +assert d.has_feature(FPrint.DeviceFeature.IDENTIFY) +assert d.has_feature(FPrint.DeviceFeature.VERIFY) +assert not d.has_feature(FPrint.DeviceFeature.DUPLICATES_CHECK) +assert not d.has_feature(FPrint.DeviceFeature.STORAGE) +assert not d.has_feature(FPrint.DeviceFeature.STORAGE_LIST) +assert not d.has_feature(FPrint.DeviceFeature.STORAGE_DELETE) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_CLEAR) + +d.open_sync() + +template = FPrint.Print.new(d) + +def enroll_progress(*args): + print("finger status: ", d.get_finger_status()) + print('enroll progress: ' + str(args)) + +def identify_done(dev, res): + global identified + identified = True + identify_match, identify_print = dev.identify_finish(res) + print('identification_done: ', identify_match, identify_print) + assert identify_match.equal(identify_print) + +# clear, enroll, verify, identify, clear + +print("clear device storage") +d.clear_storage_sync() +print("clear done") + +print("enrolling") +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +p = d.enroll_sync(template, None, enroll_progress, None) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("enroll done") + +print("verifying") +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +verify_res, verify_print = d.verify_sync(p) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("verify done") +assert verify_res == True + +identified = False +deserialized_prints = [] +deserialized_prints.append(FPrint.Print.deserialize(p.serialize())) +assert deserialized_prints[-1].equal(p) +del p + +print('async identifying') +d.identify(deserialized_prints, callback=identify_done) +del deserialized_prints + +while not identified: + ctx.iteration(True) + +print("clear device storage") +d.clear_storage_sync() +print("clear done") + +d.close_sync() + +del d +del c diff --git a/tests/crfpmoc/device b/tests/crfpmoc/device new file mode 100644 index 00000000..719b38e6 --- /dev/null +++ b/tests/crfpmoc/device @@ -0,0 +1,93 @@ +P: /devices/platform/AMDI0020:01/AMDI0020:01:0/AMDI0020:01:0.0/serial0/serial0-0/cros-ec-dev.2.auto/misc/cros_fp +N: cros_fp +E: DEVNAME=/dev/cros_fp +E: MAJOR=10 +E: MINOR=122 +E: SUBSYSTEM=misc +A: dev=10:122\n +L: device=../../../cros-ec-dev.2.auto +A: power/control=auto\n +A: power/runtime_active_time=0\n +A: power/runtime_status=unsupported\n +A: power/runtime_suspended_time=0\n + +P: /devices/platform/AMDI0020:01/AMDI0020:01:0/AMDI0020:01:0.0/serial0/serial0-0/cros-ec-dev.2.auto +E: DRIVER=cros-ec-dev +E: ID_PATH=platform-AMDI0020:01-platform-cros-ec-dev.2.auto +E: ID_PATH_TAG=platform-AMDI0020_01-platform-cros-ec-dev_2_auto +E: ID_VENDOR_FROM_DATABASE=Amdek Corporation +E: MODALIAS=platform:cros-ec-dev +E: SUBSYSTEM=platform +L: driver=../../../../../../../../bus/platform/drivers/cros-ec-dev +A: driver_override=(null)\n +A: modalias=platform:cros-ec-dev\n +A: power/control=auto\n +A: power/runtime_active_time=0\n +A: power/runtime_status=unsupported\n +A: power/runtime_suspended_time=0\n + +P: /devices/platform/AMDI0020:01/AMDI0020:01:0/AMDI0020:01:0.0/serial0/serial0-0 +E: DRIVER=cros-ec-uart +E: MODALIAS=of:NcrfpTCgoogle,cros-ec-uart +E: SUBSYSTEM=serial +L: driver=../../../../../../../bus/serial/drivers/cros-ec-uart +L: firmware_node=../../../../../../LNXSYSTM:00/LNXSYBUS:00/AMDI0020:01/PRP0001:00 +A: modalias=of:NcrfpTCgoogle,cros-ec-uart\n +A: power/control=auto\n +A: power/runtime_active_time=0\n +A: power/runtime_status=unsupported\n +A: power/runtime_suspended_time=0\n + +P: /devices/platform/AMDI0020:01/AMDI0020:01:0/AMDI0020:01:0.0/serial0 +E: SUBSYSTEM=serial +L: firmware_node=../../../../../LNXSYSTM:00/LNXSYBUS:00/AMDI0020:01 +A: power/wakeup=disabled\n +A: power/wakeup_abort_count=\n +A: power/wakeup_active=\n +A: power/wakeup_active_count=\n +A: power/wakeup_count=\n +A: power/wakeup_expire_count=\n +A: power/wakeup_last_time_ms=\n +A: power/wakeup_max_time_ms=\n +A: power/wakeup_total_time_ms=\n +L: software_node=../../../../../../kernel/software_nodes/node1 +A: waiting_for_supplier=0\n + +P: /devices/platform/AMDI0020:01/AMDI0020:01:0/AMDI0020:01:0.0 +E: DEVTYPE=port +E: DRIVER=port +E: SUBSYSTEM=serial-base +L: driver=../../../../../bus/serial-base/drivers/port +A: power/autosuspend_delay_ms=500\n +A: power/control=auto\n +A: power/runtime_active_time=31736669\n +A: power/runtime_status=active\n +A: power/runtime_suspended_time=5190\n + +P: /devices/platform/AMDI0020:01/AMDI0020:01:0 +E: DEVTYPE=ctrl +E: DRIVER=ctrl +E: SUBSYSTEM=serial-base +L: driver=../../../../bus/serial-base/drivers/ctrl +A: power/control=auto\n +A: power/runtime_active_time=31736676\n +A: power/runtime_status=active\n +A: power/runtime_suspended_time=5190\n + +P: /devices/platform/AMDI0020:01 +E: DRIVER=dw-apb-uart +E: ID_PATH=platform-AMDI0020:01 +E: ID_PATH_TAG=platform-AMDI0020_01 +E: ID_VENDOR_FROM_DATABASE=Amdek Corporation +E: MODALIAS=acpi:AMDI0020: +E: SUBSYSTEM=platform +L: driver=../../../bus/platform/drivers/dw-apb-uart +A: driver_override=(null)\n +L: firmware_node=../../LNXSYSTM:00/LNXSYBUS:00/AMDI0020:01 +A: modalias=acpi:AMDI0020:\n +A: power/control=auto\n +A: power/runtime_active_time=31736687\n +A: power/runtime_status=active\n +A: power/runtime_suspended_time=5189\n +L: software_node=../../../kernel/software_nodes/node1 + diff --git a/tests/meson.build b/tests/meson.build index 07c924be..eeb779e1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -59,6 +59,7 @@ drivers_tests = [ 'realtek', 'realtek-5816', 'focaltech_moc', + 'crfpmoc', ] if get_option('introspection') @@ -68,6 +69,9 @@ if get_option('introspection') configure_file(configuration: conf, input: 'create-driver-test.py.in', output: 'create-driver-test.py') + configure_file(configuration: conf, + input: 'create-misc-driver-test.py.in', + output: 'create-misc-driver-test.py') endif env_parser_cmd = '''