mirror of
https://gitlab.freedesktop.org/libfprint/libfprint.git
synced 2026-05-14 09:58:07 +02:00
Adds test-validity-verify.c with 16 unit tests that prevent regression
of all 8 issues found during the Iteration 6 code audit (b05657f):
R1: parse_match_result TLV parsing (5 tests)
- valid payload with all fields extracted correctly
- multi-tag iteration (tag ordering independence)
- empty dict returns no-match
- truncated/malformed data handled gracefully
- unknown tags skipped without error
R1f: match_result_clear frees hash and zeros struct
R2: identity builder NULL rejection (2 tests)
- NULL uuid returns NULL (prevented g_variant_new_string crash)
- valid UUID produces correct identity bytes
R3: gallery matching by subtype (3 tests)
- matches correct print by finger subtype
- falls back to first entry when subtype not found
- returns NULL for empty/NULL gallery
R4: struct field separation — enroll_user_dbid != delete_storage_dbid
R5: del_record command format — cmd 0x48 with dbid(2LE)
R6: match_finger single allocation — exactly 13 bytes
R7: SSM state enums exist (2 tests)
- CLEAR_* states 0-5
- DELETE_* states 0-7
To make the tests possible, extracted previously-static functions:
- parse_match_result → validity_parse_match_result (public)
- ValidityMatchResult struct moved to validity_db.h
- validity_match_result_clear added to validity_db.c
- validity_find_gallery_match helper extracted from verify SSM
551 lines
20 KiB
C
551 lines
20 KiB
C
/*
|
||
* Regression tests for validity verify/identify/delete/clear operations.
|
||
*
|
||
* These tests cover issues found during the Iteration 6 code audit:
|
||
* 1. parse_match_result dead while loop — TLV dict not iterated
|
||
* 2. ENROLL_CREATE_USER NULL user_id — validity_db_build_identity(NULL) crash
|
||
* 3. Identify always returns first gallery print — subtype not matched
|
||
* 4. delete_storage_dbid field reused for enrollment — struct field abuse
|
||
* 5. Delete SSM non-functional — del_record never called
|
||
* 6. match_finger double allocation — 12 bytes freed then 13 allocated
|
||
* 7. clear_storage returned NOT_SUPPORTED
|
||
* 8. Stale TODOs
|
||
*
|
||
* Copyright (C) 2024 libfprint contributors
|
||
*
|
||
* 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.
|
||
*/
|
||
|
||
#include <glib.h>
|
||
#include <string.h>
|
||
|
||
#include "fp-enums.h"
|
||
#include "fpi-device.h"
|
||
#include "fpi-byte-utils.h"
|
||
#include "fp-print.h"
|
||
#include "test-device-fake.h"
|
||
|
||
#include "drivers/validity/validity.h"
|
||
#include "drivers/validity/validity_db.h"
|
||
#include "drivers/validity/validity_capture.h"
|
||
#include "drivers/validity/vcsfw_protocol.h"
|
||
|
||
/* ================================================================
|
||
* Helper: build a TLV dict entry tag(2LE) | len(2LE) | data[len]
|
||
* Returns bytes written.
|
||
* ================================================================ */
|
||
static gsize
|
||
build_tlv_entry (guint8 *buf, guint16 tag, const guint8 *data, guint16 len)
|
||
{
|
||
FP_WRITE_UINT16_LE (&buf[0], tag);
|
||
FP_WRITE_UINT16_LE (&buf[2], len);
|
||
if (len > 0)
|
||
memcpy (&buf[4], data, len);
|
||
return 4 + len;
|
||
}
|
||
|
||
/* ================================================================
|
||
* Helper: Build a complete match result payload:
|
||
* total_len(2LE) | TLV entries...
|
||
* ================================================================ */
|
||
static guint8 *
|
||
build_match_payload (guint32 user_dbid,
|
||
guint16 subtype,
|
||
const guint8 *hash,
|
||
gsize hash_len,
|
||
gsize *out_len)
|
||
{
|
||
/* Max size: 2 (total_len) + 3 entries × (4 header + max data) */
|
||
guint8 *buf = g_new0 (guint8, 256);
|
||
gsize pos = 2; /* skip total_len placeholder */
|
||
|
||
/* Tag 1: user_dbid (4 bytes LE) */
|
||
guint8 dbid_data[4];
|
||
FP_WRITE_UINT32_LE (dbid_data, user_dbid);
|
||
pos += build_tlv_entry (&buf[pos], 1, dbid_data, 4);
|
||
|
||
/* Tag 3: subtype (2 bytes LE) */
|
||
guint8 sub_data[2];
|
||
FP_WRITE_UINT16_LE (sub_data, subtype);
|
||
pos += build_tlv_entry (&buf[pos], 3, sub_data, 2);
|
||
|
||
/* Tag 4: hash */
|
||
if (hash && hash_len > 0)
|
||
pos += build_tlv_entry (&buf[pos], 4, hash, hash_len);
|
||
|
||
/* Write total_len at offset 0 */
|
||
FP_WRITE_UINT16_LE (&buf[0], (guint16) (pos - 2));
|
||
|
||
*out_len = pos;
|
||
return buf;
|
||
}
|
||
|
||
/* ================================================================
|
||
* R1: parse_match_result with valid TLV data
|
||
*
|
||
* Regression: Issue #1 — dead while loop would never extract fields.
|
||
* Verifies that user_dbid, subtype, and hash are correctly parsed
|
||
* from a TLV dictionary matching python-validity's parse_dict() format.
|
||
* ================================================================ */
|
||
static void
|
||
test_parse_match_result_valid (void)
|
||
{
|
||
guint8 hash[] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE };
|
||
gsize payload_len;
|
||
g_autofree guint8 *payload = build_match_payload (
|
||
0x00001234, 3, hash, sizeof (hash), &payload_len);
|
||
|
||
ValidityMatchResult result = { 0 };
|
||
gboolean ok = validity_parse_match_result (payload, payload_len, &result);
|
||
|
||
g_assert_true (ok);
|
||
g_assert_true (result.matched);
|
||
g_assert_cmpuint (result.user_dbid, ==, 0x00001234);
|
||
g_assert_cmpuint (result.subtype, ==, 3);
|
||
g_assert_nonnull (result.hash);
|
||
g_assert_cmpuint (result.hash_len, ==, sizeof (hash));
|
||
g_assert_cmpmem (result.hash, result.hash_len, hash, sizeof (hash));
|
||
|
||
validity_match_result_clear (&result);
|
||
}
|
||
|
||
/* ================================================================
|
||
* R1b: parse_match_result iterates ALL TLV entries
|
||
*
|
||
* Regression: The dead while loop would break after first entry.
|
||
* Build a dict with tag 3 (subtype) BEFORE tag 1 (user_dbid) to
|
||
* ensure the parser doesn't stop after the first entry.
|
||
* ================================================================ */
|
||
static void
|
||
test_parse_match_result_multi_tags (void)
|
||
{
|
||
/* Manually build: total_len(2) | tag3(2+2+2) | tag1(2+2+4) | tag4(2+2+3) */
|
||
guint8 buf[256];
|
||
gsize pos = 2;
|
||
|
||
/* Tag 3 first: subtype = 7 */
|
||
guint8 sub[2];
|
||
FP_WRITE_UINT16_LE (sub, 7);
|
||
pos += build_tlv_entry (&buf[pos], 3, sub, 2);
|
||
|
||
/* Tag 1 second: user_dbid = 0xDEADBEEF */
|
||
guint8 dbid[4];
|
||
FP_WRITE_UINT32_LE (dbid, 0xDEADBEEF);
|
||
pos += build_tlv_entry (&buf[pos], 1, dbid, 4);
|
||
|
||
/* Tag 4 third: hash = {0x11, 0x22, 0x33} */
|
||
guint8 hash[] = { 0x11, 0x22, 0x33 };
|
||
pos += build_tlv_entry (&buf[pos], 4, hash, 3);
|
||
|
||
FP_WRITE_UINT16_LE (&buf[0], (guint16) (pos - 2));
|
||
|
||
ValidityMatchResult result = { 0 };
|
||
gboolean ok = validity_parse_match_result (buf, pos, &result);
|
||
|
||
g_assert_true (ok);
|
||
g_assert_true (result.matched);
|
||
g_assert_cmpuint (result.user_dbid, ==, 0xDEADBEEF);
|
||
g_assert_cmpuint (result.subtype, ==, 7);
|
||
g_assert_nonnull (result.hash);
|
||
g_assert_cmpuint (result.hash_len, ==, 3);
|
||
g_assert_cmpmem (result.hash, result.hash_len, hash, 3);
|
||
|
||
validity_match_result_clear (&result);
|
||
}
|
||
|
||
/* ================================================================
|
||
* R1c: parse_match_result with empty dict (no match)
|
||
*
|
||
* Regression: A no-match scenario should return ok=TRUE but matched=FALSE.
|
||
* ================================================================ */
|
||
static void
|
||
test_parse_match_result_empty (void)
|
||
{
|
||
/* total_len = 0, no TLV entries */
|
||
guint8 buf[2] = { 0x00, 0x00 };
|
||
|
||
ValidityMatchResult result = { 0 };
|
||
gboolean ok = validity_parse_match_result (buf, sizeof (buf), &result);
|
||
|
||
g_assert_true (ok);
|
||
g_assert_false (result.matched);
|
||
g_assert_cmpuint (result.user_dbid, ==, 0);
|
||
g_assert_cmpuint (result.subtype, ==, 0);
|
||
g_assert_null (result.hash);
|
||
}
|
||
|
||
/* ================================================================
|
||
* R1d: parse_match_result with truncated data
|
||
*
|
||
* Ensure graceful handling of malformed/truncated payloads.
|
||
* ================================================================ */
|
||
static void
|
||
test_parse_match_result_truncated (void)
|
||
{
|
||
/* Only 1 byte — too short for total_len */
|
||
guint8 buf1[1] = { 0x05 };
|
||
ValidityMatchResult result = { 0 };
|
||
g_assert_false (validity_parse_match_result (buf1, 1, &result));
|
||
|
||
/* total_len says 20 but only 6 bytes follow (partial TLV entry) */
|
||
guint8 buf2[8];
|
||
FP_WRITE_UINT16_LE (&buf2[0], 20);
|
||
FP_WRITE_UINT16_LE (&buf2[2], 1); /* tag = 1 */
|
||
FP_WRITE_UINT16_LE (&buf2[4], 10); /* len = 10, but only 2 bytes remain */
|
||
buf2[6] = 0xFF;
|
||
buf2[7] = 0xFF;
|
||
|
||
memset (&result, 0, sizeof (result));
|
||
gboolean ok = validity_parse_match_result (buf2, sizeof (buf2), &result);
|
||
/* Should return TRUE (parsing succeeded) but matched=FALSE (incomplete entry) */
|
||
g_assert_true (ok);
|
||
g_assert_false (result.matched);
|
||
}
|
||
|
||
/* ================================================================
|
||
* R1e: parse_match_result ignores unknown tags
|
||
*
|
||
* Unknown tags should be skipped without error.
|
||
* ================================================================ */
|
||
static void
|
||
test_parse_match_result_unknown_tags (void)
|
||
{
|
||
guint8 buf[256];
|
||
gsize pos = 2;
|
||
|
||
/* Unknown tag 99 with 2 bytes of data */
|
||
guint8 unk[] = { 0x42, 0x43 };
|
||
pos += build_tlv_entry (&buf[pos], 99, unk, 2);
|
||
|
||
/* Tag 1: user_dbid = 0x0042 */
|
||
guint8 dbid[4];
|
||
FP_WRITE_UINT32_LE (dbid, 0x0042);
|
||
pos += build_tlv_entry (&buf[pos], 1, dbid, 4);
|
||
|
||
FP_WRITE_UINT16_LE (&buf[0], (guint16) (pos - 2));
|
||
|
||
ValidityMatchResult result = { 0 };
|
||
gboolean ok = validity_parse_match_result (buf, pos, &result);
|
||
|
||
g_assert_true (ok);
|
||
g_assert_true (result.matched);
|
||
g_assert_cmpuint (result.user_dbid, ==, 0x0042);
|
||
|
||
validity_match_result_clear (&result);
|
||
}
|
||
|
||
/* ================================================================
|
||
* R2: validity_db_build_identity rejects NULL
|
||
*
|
||
* Regression: Issue #2 — NULL user_id was passed to build_identity
|
||
* which would then be passed to g_variant_new_string(NULL) → crash.
|
||
* The guard should return NULL.
|
||
* ================================================================ */
|
||
static void
|
||
test_build_identity_null (void)
|
||
{
|
||
gsize len = 999;
|
||
|
||
/* The function uses g_return_val_if_fail which emits g_critical.
|
||
* With G_DEBUG=fatal-warnings the critical would be fatal,
|
||
* so we must expect the message. */
|
||
g_test_expect_message ("libfprint-validity",
|
||
G_LOG_LEVEL_CRITICAL,
|
||
"*assertion*uuid_str*failed*");
|
||
|
||
guint8 *id = validity_db_build_identity (NULL, &len);
|
||
g_test_assert_expected_messages ();
|
||
|
||
g_assert_null (id);
|
||
}
|
||
|
||
/* ================================================================
|
||
* R2b: validity_db_build_identity with valid UUID
|
||
*
|
||
* Regression: Ensures UUID → identity bytes works correctly
|
||
* (complementary to the NULL test above).
|
||
* ================================================================ */
|
||
static void
|
||
test_build_identity_valid_uuid (void)
|
||
{
|
||
const gchar *uuid = "12345678-1234-5678-1234-567812345678";
|
||
gsize len;
|
||
g_autofree guint8 *id = validity_db_build_identity (uuid, &len);
|
||
|
||
g_assert_nonnull (id);
|
||
g_assert_cmpuint (len, >=, VALIDITY_IDENTITY_MIN_SIZE);
|
||
|
||
/* Type should be SID (3) */
|
||
g_assert_cmpuint (FP_READ_UINT32_LE (&id[0]), ==, VALIDITY_IDENTITY_TYPE_SID);
|
||
|
||
/* Length field should be UUID string length */
|
||
g_assert_cmpuint (FP_READ_UINT32_LE (&id[4]), ==, strlen (uuid));
|
||
|
||
/* UUID payload should be present */
|
||
g_assert_cmpmem (&id[8], strlen (uuid), uuid, strlen (uuid));
|
||
}
|
||
|
||
/* ================================================================
|
||
* R3: Gallery matching by subtype
|
||
*
|
||
* Regression: Issue #3 — identify always returned first gallery print
|
||
* regardless of actual subtype. Now it should match by finger subtype.
|
||
* ================================================================ */
|
||
static void
|
||
test_gallery_match_by_subtype (void)
|
||
{
|
||
g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL);
|
||
g_autoptr(GPtrArray) gallery = g_ptr_array_new_with_free_func (g_object_unref);
|
||
|
||
/* Create 3 prints with fingers: LEFT_THUMB(1), LEFT_INDEX(2), RIGHT_MIDDLE(8) */
|
||
FpPrint *p1 = fp_print_new (device);
|
||
fp_print_set_finger (p1, FP_FINGER_LEFT_THUMB);
|
||
g_ptr_array_add (gallery, g_object_ref_sink (p1));
|
||
|
||
FpPrint *p2 = fp_print_new (device);
|
||
fp_print_set_finger (p2, FP_FINGER_LEFT_INDEX);
|
||
g_ptr_array_add (gallery, g_object_ref_sink (p2));
|
||
|
||
FpPrint *p3 = fp_print_new (device);
|
||
fp_print_set_finger (p3, FP_FINGER_RIGHT_MIDDLE);
|
||
g_ptr_array_add (gallery, g_object_ref_sink (p3));
|
||
|
||
/* Subtype 2 = LEFT_INDEX → should match p2, not p1 */
|
||
guint16 subtype_left_index = validity_finger_to_subtype (FP_FINGER_LEFT_INDEX);
|
||
FpPrint *match = validity_find_gallery_match (gallery, subtype_left_index);
|
||
g_assert_true (match == p2);
|
||
|
||
/* Subtype 8 = RIGHT_MIDDLE → should match p3 */
|
||
guint16 subtype_right_middle = validity_finger_to_subtype (FP_FINGER_RIGHT_MIDDLE);
|
||
match = validity_find_gallery_match (gallery, subtype_right_middle);
|
||
g_assert_true (match == p3);
|
||
|
||
/* Subtype 1 = LEFT_THUMB → should match p1 */
|
||
guint16 subtype_left_thumb = validity_finger_to_subtype (FP_FINGER_LEFT_THUMB);
|
||
match = validity_find_gallery_match (gallery, subtype_left_thumb);
|
||
g_assert_true (match == p1);
|
||
}
|
||
|
||
/* ================================================================
|
||
* R3b: Gallery match falls back to first when subtype doesn't match
|
||
*
|
||
* The sensor confirmed a match but the subtype can't be correlated
|
||
* to any gallery entry — should fall back to first.
|
||
* ================================================================ */
|
||
static void
|
||
test_gallery_match_fallback (void)
|
||
{
|
||
g_autoptr(FpDevice) device = g_object_new (FPI_TYPE_DEVICE_FAKE, NULL);
|
||
g_autoptr(GPtrArray) gallery = g_ptr_array_new_with_free_func (g_object_unref);
|
||
|
||
FpPrint *p1 = fp_print_new (device);
|
||
fp_print_set_finger (p1, FP_FINGER_LEFT_THUMB);
|
||
g_ptr_array_add (gallery, g_object_ref_sink (p1));
|
||
|
||
/* Subtype 9 = RIGHT_RING, not in gallery → should fall back to p1 */
|
||
guint16 subtype_right_ring = validity_finger_to_subtype (FP_FINGER_RIGHT_RING);
|
||
FpPrint *match = validity_find_gallery_match (gallery, subtype_right_ring);
|
||
g_assert_true (match == p1);
|
||
}
|
||
|
||
/* ================================================================
|
||
* R3c: Gallery match with NULL/empty gallery
|
||
*
|
||
* Should return NULL when gallery is empty or NULL.
|
||
* ================================================================ */
|
||
static void
|
||
test_gallery_match_empty (void)
|
||
{
|
||
g_autoptr(GPtrArray) empty = g_ptr_array_new_with_free_func (g_object_unref);
|
||
|
||
g_assert_null (validity_find_gallery_match (NULL, 1));
|
||
g_assert_null (validity_find_gallery_match (empty, 1));
|
||
}
|
||
|
||
/* ================================================================
|
||
* R4: enroll_user_dbid field exists separately from delete_storage_dbid
|
||
*
|
||
* Regression: Issue #4 — delete_storage_dbid was abused for enrollment.
|
||
* This compile-time test verifies both fields exist independently.
|
||
* ================================================================ */
|
||
static void
|
||
test_struct_separate_fields (void)
|
||
{
|
||
/* Verify both fields exist and are at different offsets */
|
||
g_assert_cmpuint (
|
||
G_STRUCT_OFFSET (FpiDeviceValidity, enroll_user_dbid), !=,
|
||
G_STRUCT_OFFSET (FpiDeviceValidity, delete_storage_dbid));
|
||
|
||
/* Also verify delete_finger_subtype and delete_finger_dbid exist
|
||
* (needed for the functional delete SSM) */
|
||
g_assert_cmpuint (
|
||
G_STRUCT_OFFSET (FpiDeviceValidity, delete_finger_subtype), !=,
|
||
G_STRUCT_OFFSET (FpiDeviceValidity, delete_storage_dbid));
|
||
g_assert_cmpuint (
|
||
G_STRUCT_OFFSET (FpiDeviceValidity, delete_finger_dbid), !=,
|
||
G_STRUCT_OFFSET (FpiDeviceValidity, delete_storage_dbid));
|
||
}
|
||
|
||
/* ================================================================
|
||
* R5: del_record command format
|
||
*
|
||
* Regression: Issue #5 — delete SSM never sent del_record cmd.
|
||
* Verify cmd 0x48 produces correct format: 0x48 | dbid(2LE).
|
||
* (This already exists in test-validity-db.c but we double-check
|
||
* the format critical for delete functionality.)
|
||
* ================================================================ */
|
||
static void
|
||
test_del_record_format (void)
|
||
{
|
||
gsize len;
|
||
g_autofree guint8 *cmd = validity_db_build_cmd_del_record (0x4321, &len);
|
||
|
||
g_assert_nonnull (cmd);
|
||
g_assert_cmpuint (len, ==, 3);
|
||
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_DEL_RECORD);
|
||
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[1]), ==, 0x4321);
|
||
}
|
||
|
||
/* ================================================================
|
||
* R6: match_finger command is exactly 13 bytes (single allocation)
|
||
*
|
||
* Regression: Issue #6 — build_cmd_match_finger allocated 12 bytes,
|
||
* freed, then re-allocated 13 bytes. Now single allocation.
|
||
* ================================================================ */
|
||
static void
|
||
test_match_finger_size (void)
|
||
{
|
||
gsize len;
|
||
g_autofree guint8 *cmd = validity_db_build_cmd_match_finger (&len);
|
||
|
||
g_assert_nonnull (cmd);
|
||
g_assert_cmpuint (len, ==, 13);
|
||
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_MATCH_FINGER);
|
||
g_assert_cmpuint (cmd[1], ==, 0x02);
|
||
g_assert_cmpuint (cmd[2], ==, 0xFF);
|
||
|
||
/* Verify all 5 uint16_le fields */
|
||
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[3]), ==, 0); /* stg_id */
|
||
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[5]), ==, 0); /* usr_id */
|
||
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[7]), ==, 1);
|
||
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[9]), ==, 0);
|
||
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[11]), ==, 0);
|
||
}
|
||
|
||
/* ================================================================
|
||
* R7: Clear storage SSM states exist
|
||
*
|
||
* Regression: Issue #7 — clear_storage was a stub returning NOT_SUPPORTED.
|
||
* Verify the CLEAR_* enum states exist (compile-time regression test).
|
||
* ================================================================ */
|
||
static void
|
||
test_clear_storage_states_exist (void)
|
||
{
|
||
/* Verify clear SSM states exist and are ordered correctly */
|
||
g_assert_cmpint (CLEAR_GET_STORAGE, ==, 0);
|
||
g_assert_cmpint (CLEAR_GET_STORAGE_RECV, ==, 1);
|
||
g_assert_cmpint (CLEAR_DEL_USER, ==, 2);
|
||
g_assert_cmpint (CLEAR_DEL_USER_RECV, ==, 3);
|
||
g_assert_cmpint (CLEAR_DONE, ==, 4);
|
||
g_assert_cmpint (CLEAR_NUM_STATES, ==, 5);
|
||
}
|
||
|
||
/* ================================================================
|
||
* R7b: Delete SSM states are complete
|
||
*
|
||
* Verify the delete SSM has all required states including
|
||
* DEL_RECORD and DEL_RECORD_RECV (which were previously dead code).
|
||
* ================================================================ */
|
||
static void
|
||
test_delete_states_exist (void)
|
||
{
|
||
g_assert_cmpint (DELETE_GET_STORAGE, ==, 0);
|
||
g_assert_cmpint (DELETE_GET_STORAGE_RECV, ==, 1);
|
||
g_assert_cmpint (DELETE_LOOKUP_USER, ==, 2);
|
||
g_assert_cmpint (DELETE_LOOKUP_USER_RECV, ==, 3);
|
||
g_assert_cmpint (DELETE_DEL_RECORD, ==, 4);
|
||
g_assert_cmpint (DELETE_DEL_RECORD_RECV, ==, 5);
|
||
g_assert_cmpint (DELETE_DONE, ==, 6);
|
||
g_assert_cmpint (DELETE_NUM_STATES, ==, 7);
|
||
}
|
||
|
||
/* ================================================================
|
||
* R1f: match_result_clear frees hash
|
||
*
|
||
* Ensure the clear function properly frees the hash allocation.
|
||
* ================================================================ */
|
||
static void
|
||
test_match_result_clear (void)
|
||
{
|
||
ValidityMatchResult result = { 0 };
|
||
result.matched = TRUE;
|
||
result.user_dbid = 42;
|
||
result.subtype = 5;
|
||
result.hash = g_memdup2 ((guint8[]){0x01, 0x02}, 2);
|
||
result.hash_len = 2;
|
||
|
||
validity_match_result_clear (&result);
|
||
|
||
g_assert_false (result.matched);
|
||
g_assert_cmpuint (result.user_dbid, ==, 0);
|
||
g_assert_cmpuint (result.subtype, ==, 0);
|
||
g_assert_null (result.hash);
|
||
g_assert_cmpuint (result.hash_len, ==, 0);
|
||
}
|
||
|
||
int
|
||
main (int argc, char *argv[])
|
||
{
|
||
g_test_init (&argc, &argv, NULL);
|
||
|
||
/* R1: parse_match_result regression tests (Issue #1: dead while loop) */
|
||
g_test_add_func ("/validity/verify/parse_match_result_valid",
|
||
test_parse_match_result_valid);
|
||
g_test_add_func ("/validity/verify/parse_match_result_multi_tags",
|
||
test_parse_match_result_multi_tags);
|
||
g_test_add_func ("/validity/verify/parse_match_result_empty",
|
||
test_parse_match_result_empty);
|
||
g_test_add_func ("/validity/verify/parse_match_result_truncated",
|
||
test_parse_match_result_truncated);
|
||
g_test_add_func ("/validity/verify/parse_match_result_unknown_tags",
|
||
test_parse_match_result_unknown_tags);
|
||
g_test_add_func ("/validity/verify/match_result_clear",
|
||
test_match_result_clear);
|
||
|
||
/* R2: identity builder NULL regression (Issue #2: NULL crash) */
|
||
g_test_add_func ("/validity/verify/build_identity_null",
|
||
test_build_identity_null);
|
||
g_test_add_func ("/validity/verify/build_identity_valid_uuid",
|
||
test_build_identity_valid_uuid);
|
||
|
||
/* R3: gallery matching by subtype (Issue #3: always returned first) */
|
||
g_test_add_func ("/validity/verify/gallery_match_by_subtype",
|
||
test_gallery_match_by_subtype);
|
||
g_test_add_func ("/validity/verify/gallery_match_fallback",
|
||
test_gallery_match_fallback);
|
||
g_test_add_func ("/validity/verify/gallery_match_empty",
|
||
test_gallery_match_empty);
|
||
|
||
/* R4: struct field separation (Issue #4: field abuse) */
|
||
g_test_add_func ("/validity/verify/struct_separate_fields",
|
||
test_struct_separate_fields);
|
||
|
||
/* R5: del_record command format (Issue #5: delete SSM non-functional) */
|
||
g_test_add_func ("/validity/verify/del_record_format",
|
||
test_del_record_format);
|
||
|
||
/* R6: match_finger single allocation (Issue #6: double alloc) */
|
||
g_test_add_func ("/validity/verify/match_finger_size",
|
||
test_match_finger_size);
|
||
|
||
/* R7: clear/delete storage SSM states (Issue #7: stub) */
|
||
g_test_add_func ("/validity/verify/clear_storage_states",
|
||
test_clear_storage_states_exist);
|
||
g_test_add_func ("/validity/verify/delete_states",
|
||
test_delete_states_exist);
|
||
|
||
return g_test_run ();
|
||
}
|