#include #include #include #include #include #include #include #include namespace { extern "C" void lardon3d_acquisition_test_extract_raw_image_unique_id( const libraw_data_t *raw, Lardon3DAcquisitionMetadata *metadata); extern "C" void lardon3d_acquisition_test_extract_raw_fixed_text( const libraw_data_t *raw, Lardon3DAcquisitionMetadata *metadata); Lardon3DAcquisitionMetadata metadata() { Lardon3DAcquisitionMetadata value = {}; value.policy_version = LARDON3D_ACQUISITION_PAIRING_POLICY_VERSION; value.source_kind = LARDON3D_ACQUISITION_SOURCE_RAW; return value; } void set_text(Lardon3DAcquisitionMetadata &value, uint32_t field, char *destination, size_t capacity, const char *text) { assert(std::strlen(text) < capacity); std::strcpy(destination, text); value.present_fields |= field; } Lardon3DAcquisitionPairResult compare(const Lardon3DAcquisitionMetadata &left, const Lardon3DAcquisitionMetadata &right, bool same_asset = false, bool basename_hint = false) { Lardon3DAcquisitionPairResult result = {}; assert(lardon3d_acquisition_compare(&left, &right, same_asset, basename_hint, &result) == LARDON3D_ACQUISITION_OK); return result; } void test_pairing_policy() { auto left = metadata(); auto right = metadata(); set_text(left, LARDON3D_ACQUISITION_FIELD_IMAGE_UNIQUE_ID, left.image_unique_id, sizeof(left.image_unique_id), "UID-1"); set_text(right, LARDON3D_ACQUISITION_FIELD_IMAGE_UNIQUE_ID, right.image_unique_id, sizeof(right.image_unique_id), "UID-1"); assert(compare(left, right).decision == LARDON3D_ACQUISITION_SAME_ACQUISITION_STRONG); auto result = compare(left, right, false, true); assert(result.decision == LARDON3D_ACQUISITION_SAME_ACQUISITION_STRONG); assert((result.evidence & LARDON3D_ACQUISITION_EVIDENCE_BASENAME_HINT) != 0u); auto empty_left = metadata(); auto empty_right = metadata(); result = compare(empty_left, empty_right, false, true); /* DSC03350.ARW / DSC03350.JPG */ assert(result.decision == LARDON3D_ACQUISITION_INSUFFICIENT); assert(result.evidence == LARDON3D_ACQUISITION_EVIDENCE_BASENAME_HINT); set_text(empty_left, LARDON3D_ACQUISITION_FIELD_DATETIME_ORIGINAL, empty_left.datetime_original, sizeof(empty_left.datetime_original), "2026:08:27 12:34:56"); set_text(empty_right, LARDON3D_ACQUISITION_FIELD_DATETIME_ORIGINAL, empty_right.datetime_original, sizeof(empty_right.datetime_original), "2026:08:27 12:34:56"); assert(compare(empty_left, empty_right).decision == LARDON3D_ACQUISITION_SAME_ACQUISITION_CANDIDATE); set_text(empty_left, LARDON3D_ACQUISITION_FIELD_MODEL, empty_left.model, sizeof(empty_left.model), "ILCE-7M4"); set_text(empty_right, LARDON3D_ACQUISITION_FIELD_MODEL, empty_right.model, sizeof(empty_right.model), "ILCE-7M4"); assert(compare(empty_left, empty_right).decision == LARDON3D_ACQUISITION_SAME_ACQUISITION_CANDIDATE); std::strcpy(right.image_unique_id, "UID-2"); result = compare(left, right); assert(result.decision == LARDON3D_ACQUISITION_DIFFERENT); assert(result.contradictions == LARDON3D_ACQUISITION_CONTRADICTION_IMAGE_UNIQUE_ID); auto serial_left = metadata(); auto serial_right = metadata(); set_text(serial_left, LARDON3D_ACQUISITION_FIELD_BODY_SERIAL, serial_left.body_serial, sizeof(serial_left.body_serial), "BODY-A"); set_text(serial_right, LARDON3D_ACQUISITION_FIELD_BODY_SERIAL, serial_right.body_serial, sizeof(serial_right.body_serial), "BODY-B"); assert(compare(serial_left, serial_right).decision == LARDON3D_ACQUISITION_DIFFERENT); serial_right = metadata(); assert(compare(serial_left, serial_right).decision == LARDON3D_ACQUISITION_INSUFFICIENT); auto other_device = metadata(); set_text(other_device, LARDON3D_ACQUISITION_FIELD_BODY_SERIAL, other_device.body_serial, sizeof(other_device.body_serial), "BODY-B"); set_text(other_device, LARDON3D_ACQUISITION_FIELD_DATETIME_ORIGINAL, other_device.datetime_original, sizeof(other_device.datetime_original), "2026:08:27 12:34:56"); set_text(serial_left, LARDON3D_ACQUISITION_FIELD_DATETIME_ORIGINAL, serial_left.datetime_original, sizeof(serial_left.datetime_original), "2026:08:27 12:34:56"); assert(compare(serial_left, other_device, false, true).decision == LARDON3D_ACQUISITION_DIFFERENT); assert(compare(left, left, true, true).decision == LARDON3D_ACQUISITION_INSUFFICIENT); } void test_selector() { auto source = metadata(); auto candidate_b = metadata(); auto candidate_c = metadata(); set_text(source, LARDON3D_ACQUISITION_FIELD_IMAGE_UNIQUE_ID, source.image_unique_id, sizeof(source.image_unique_id), "SHARED"); set_text(candidate_b, LARDON3D_ACQUISITION_FIELD_IMAGE_UNIQUE_ID, candidate_b.image_unique_id, sizeof(candidate_b.image_unique_id), "SHARED"); set_text(candidate_c, LARDON3D_ACQUISITION_FIELD_IMAGE_UNIQUE_ID, candidate_c.image_unique_id, sizeof(candidate_c.image_unique_id), "SHARED"); Lardon3DAcquisitionCandidate candidates[2] = { {20u, &candidate_b, 0u, 0u, {}}, {30u, &candidate_c, 0u, 0u, {}}, }; Lardon3DAcquisitionSelection selection = {}; assert(lardon3d_acquisition_select_candidate(&source, candidates, 2u, &selection) == LARDON3D_ACQUISITION_OK); assert(selection.decision == LARDON3D_ACQUISITION_AMBIGUOUS); assert(selection.top_candidate_count == 2u); assert(selection.selected_candidate_id == 0u); const auto temporary = candidates[0]; candidates[0] = candidates[1]; candidates[1] = temporary; assert(lardon3d_acquisition_select_candidate(&source, candidates, 2u, &selection) == LARDON3D_ACQUISITION_OK); assert(selection.decision == LARDON3D_ACQUISITION_AMBIGUOUS); assert(selection.top_candidate_count == 2u); assert(selection.selected_candidate_id == 0u); } void test_non_terminated_public_text_is_unavailable() { auto left = metadata(); auto right = metadata(); std::memset(left.image_unique_id, 'X', sizeof(left.image_unique_id)); std::memset(right.image_unique_id, 'X', sizeof(right.image_unique_id)); left.present_fields |= LARDON3D_ACQUISITION_FIELD_IMAGE_UNIQUE_ID; right.present_fields |= LARDON3D_ACQUISITION_FIELD_IMAGE_UNIQUE_ID; auto result = compare(left, right); assert(result.decision == LARDON3D_ACQUISITION_INSUFFICIENT); assert(result.evidence == 0u); assert(result.contradictions == 0u); std::memcpy(right.image_unique_id, "VALID", 6u); result = compare(left, right); assert(result.decision == LARDON3D_ACQUISITION_INSUFFICIENT); assert(result.evidence == 0u); assert(result.contradictions == 0u); } void add_ascii(ExifData *data, ExifIfd ifd, ExifTag tag, const char *value) { ExifEntry *entry = exif_entry_new(); assert(entry != nullptr); entry->tag = tag; entry->format = EXIF_FORMAT_ASCII; const size_t value_size = std::strlen(value) + 2u; assert(value_size <= UINT32_MAX); entry->components = static_cast(value_size); entry->size = static_cast(value_size); entry->data = static_cast(std::malloc(entry->size)); assert(entry->data != nullptr); std::memcpy(entry->data, value, std::strlen(value)); entry->data[entry->size - 2u] = ' '; entry->data[entry->size - 1u] = '\0'; exif_content_add_entry(data->ifd[ifd], entry); exif_entry_unref(entry); } std::string temporary_path(const char *suffix) { char path[] = "/tmp/lardon3d-acquisition-XXXXXX"; const int descriptor = mkstemp(path); assert(descriptor >= 0); close(descriptor); std::string result(path); result += suffix; assert(std::rename(path, result.c_str()) == 0); return result; } void write_exif_jpeg(const std::string &path) { ExifData *data = exif_data_new(); assert(data != nullptr); exif_data_set_byte_order(data, EXIF_BYTE_ORDER_INTEL); add_ascii(data, EXIF_IFD_0, EXIF_TAG_MAKE, "Sony"); add_ascii(data, EXIF_IFD_0, EXIF_TAG_MODEL, "ILCE-7M4"); add_ascii(data, EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, "2026:08:27 12:34:56"); add_ascii(data, EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_ORIGINAL, "123"); add_ascii(data, EXIF_IFD_EXIF, EXIF_TAG_OFFSET_TIME_ORIGINAL, "+02:00"); add_ascii(data, EXIF_IFD_EXIF, EXIF_TAG_BODY_SERIAL_NUMBER, "BODY-42"); add_ascii(data, EXIF_IFD_EXIF, EXIF_TAG_IMAGE_UNIQUE_ID, "IMAGE-99"); unsigned char *encoded = nullptr; unsigned int encoded_size = 0u; exif_data_save_data(data, &encoded, &encoded_size); assert(encoded != nullptr && encoded_size > 0u && encoded_size <= UINT16_MAX - 2u); FILE *file = std::fopen(path.c_str(), "wb"); assert(file != nullptr); const unsigned char soi[] = {0xffu, 0xd8u, 0xffu, 0xe1u}; const unsigned int segment_size = encoded_size + 2u; const unsigned char length[] = {static_cast(segment_size >> 8u), static_cast(segment_size & 0xffu)}; const unsigned char eoi[] = {0xffu, 0xd9u}; assert(std::fwrite(soi, 1u, sizeof(soi), file) == sizeof(soi)); assert(std::fwrite(length, 1u, sizeof(length), file) == sizeof(length)); assert(std::fwrite(encoded, 1u, encoded_size, file) == encoded_size); assert(std::fwrite(eoi, 1u, sizeof(eoi), file) == sizeof(eoi)); assert(std::fclose(file) == 0); std::free(encoded); exif_data_unref(data); } void test_extraction() { const std::string jpeg = temporary_path(".jpg"); write_exif_jpeg(jpeg); Lardon3DAcquisitionMetadata value = {}; assert(lardon3d_acquisition_extract_metadata(jpeg.c_str(), &value) == LARDON3D_ACQUISITION_OK); assert(value.source_kind == LARDON3D_ACQUISITION_SOURCE_JPEG); assert(std::strcmp(value.make, "Sony") == 0); assert(std::strcmp(value.model, "ILCE-7M4") == 0); assert(std::strcmp(value.datetime_original, "2026:08:27 12:34:56") == 0); assert(std::strcmp(value.subsec_original, "123") == 0); assert(std::strcmp(value.offset_original, "+02:00") == 0); assert(std::strcmp(value.body_serial, "BODY-42") == 0); assert(std::strcmp(value.image_unique_id, "IMAGE-99") == 0); assert(unlink(jpeg.c_str()) == 0); const std::string malformed = temporary_path(".jpg"); FILE *file = std::fopen(malformed.c_str(), "wb"); assert(file != nullptr); const unsigned char bad[] = {0xffu, 0xd8u, 0x00u}; assert(std::fwrite(bad, 1u, sizeof(bad), file) == sizeof(bad)); assert(std::fclose(file) == 0); assert(lardon3d_acquisition_extract_metadata(malformed.c_str(), &value) == LARDON3D_ACQUISITION_CORRUPT_SOURCE); assert(unlink(malformed.c_str()) == 0); const std::string unsupported = temporary_path(".bin"); file = std::fopen(unsupported.c_str(), "wb"); assert(file != nullptr); assert(std::fwrite("not an image", 1u, 12u, file) == 12u); assert(std::fclose(file) == 0); assert(lardon3d_acquisition_extract_metadata(unsupported.c_str(), &value) == LARDON3D_ACQUISITION_UNSUPPORTED_FORMAT); assert(unlink(unsupported.c_str()) == 0); } void test_raw_image_unique_id_mapping() { libraw_data_t raw = {}; Lardon3DAcquisitionMetadata value = {}; std::memcpy(raw.color.ImageUniqueID, "RAW-UID-42 ", 11u); lardon3d_acquisition_test_extract_raw_image_unique_id(&raw, &value); assert(std::strcmp(value.image_unique_id, "RAW-UID-42") == 0); assert((value.present_fields & LARDON3D_ACQUISITION_FIELD_IMAGE_UNIQUE_ID) != 0u); } void test_non_terminated_raw_fixed_text_mapping() { libraw_data_t raw = {}; Lardon3DAcquisitionMetadata value = {}; std::memset(raw.idata.make, 'M', sizeof(raw.idata.make)); std::memset(raw.idata.model, 'D', sizeof(raw.idata.model)); std::memset(raw.shootinginfo.BodySerial, 'S', sizeof(raw.shootinginfo.BodySerial)); lardon3d_acquisition_test_extract_raw_fixed_text(&raw, &value); assert(std::strlen(value.make) == sizeof(raw.idata.make)); assert(std::strlen(value.model) == sizeof(raw.idata.model)); assert(std::strlen(value.body_serial) == sizeof(raw.shootinginfo.BodySerial)); assert((value.present_fields & LARDON3D_ACQUISITION_FIELD_MAKE) != 0u); assert((value.present_fields & LARDON3D_ACQUISITION_FIELD_MODEL) != 0u); assert((value.present_fields & LARDON3D_ACQUISITION_FIELD_BODY_SERIAL) != 0u); } } // namespace int main() { test_pairing_policy(); test_selector(); test_non_terminated_public_text_is_unavailable(); test_extraction(); test_raw_image_unique_id_mapping(); test_non_terminated_raw_fixed_text_mapping(); return 0; }