lardon3d/tests/test_optical_profiles.c

1294 lines
57 KiB
C

#include <sqlite3.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <lardon3d/acquisition_campaign_task.h>
#include <lardon3d/optical_profiles.h>
#include <lardon3d/project_db.h>
#include <lardon3d/sparse_sfm_model.h>
#define CHECK(condition) \
do { \
if (!(condition)) { \
fprintf(stderr, "optical profiles failure at line %d: %s\n", __LINE__, \
#condition); \
return false; \
} \
} while (0)
static bool raw_sql(const char *path, const char *sql) {
sqlite3 *database = NULL;
if (sqlite3_open(path, &database) != SQLITE_OK)
return false;
int code = sqlite3_exec(database, sql, NULL, NULL, NULL);
return sqlite3_close(database) == SQLITE_OK && code == SQLITE_OK;
}
static bool raw_integer(const char *path, const char *sql,
sqlite3_int64 *value) {
sqlite3 *database = NULL;
sqlite3_stmt *statement = NULL;
bool ok = sqlite3_open(path, &database) == SQLITE_OK &&
sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK &&
sqlite3_step(statement) == SQLITE_ROW &&
sqlite3_column_type(statement, 0) == SQLITE_INTEGER;
if (ok)
*value = sqlite3_column_int64(statement, 0);
(void)sqlite3_finalize(statement);
if (database)
ok = sqlite3_close(database) == SQLITE_OK && ok;
return ok;
}
static bool all_bytes_zero(const void *value, size_t size) {
const unsigned char *bytes = value;
for (size_t index = 0; index < size; ++index)
if (bytes[index] != 0)
return false;
return true;
}
static bool raw_object_sql(const char *path, const char *name, char *output,
size_t capacity) {
sqlite3 *database = NULL;
sqlite3_stmt *statement = NULL;
bool ok = sqlite3_open(path, &database) == SQLITE_OK &&
sqlite3_prepare_v2(
database,
"SELECT sql FROM sqlite_master WHERE name=?1 AND sql IS NOT NULL",
-1, &statement, NULL) == SQLITE_OK;
if (ok)
(void)sqlite3_bind_text(statement, 1, name, -1, SQLITE_TRANSIENT);
if (ok)
ok = sqlite3_step(statement) == SQLITE_ROW &&
sqlite3_column_type(statement, 0) == SQLITE_TEXT;
if (ok) {
int bytes = sqlite3_column_bytes(statement, 0);
const unsigned char *text = sqlite3_column_text(statement, 0);
ok = bytes >= 0 && (size_t)bytes < capacity && text;
if (ok) {
memcpy(output, text, (size_t)bytes);
output[bytes] = '\0';
}
}
(void)sqlite3_finalize(statement);
if (database)
ok = sqlite3_close(database) == SQLITE_OK && ok;
return ok;
}
static bool make_database_path(char directory[64], char path[256]) {
memcpy(directory, "/tmp/lardon3d-optical-XXXXXX", 29);
if (!mkdtemp(directory))
return false;
int bytes = snprintf(path, 256, "%s/project.db", directory);
return bytes > 0 && bytes < 256;
}
static bool create_sparse_calibration(Lardon3DProjectDb *database,
unsigned char fingerprint_byte,
uint32_t width, uint32_t height,
Lardon3DSparseCalibration *output) {
Lardon3DSparseCalibration input = {
.model_kind = LARDON3D_SPARSE_SFM_CALIBRATION_KIND_PINHOLE,
.model_version = LARDON3D_SPARSE_SFM_CALIBRATION_VERSION,
.width = width,
.height = height,
.fx = (double)width * 0.75,
.fy = (double)height,
.cx = (double)width * 0.5,
.cy = (double)height * 0.5,
.provenance_kind = LARDON3D_SPARSE_SFM_PROVENANCE_USER_EXPLICIT,
};
input.provenance_fingerprint[0] = fingerprint_byte;
return lardon3d_sparse_calibration_create(database, &input, output) ==
LARDON3D_PROJECT_DB_OK;
}
static bool seed_campaign(Lardon3DProjectDb *database, uint64_t task_id,
uint64_t scanset_id, uint32_t group_count) {
Lardon3DTaskDurableSnapshot snapshot = {
.id = task_id,
.saved_state = TASK_PENDING,
.recovery_state = TASK_PENDING,
};
memcpy(snapshot.name, "optical campaign", 17);
static const unsigned char request[] = {0x4c, 0x33, 0x44, 0x4f};
Lardon3DProjectDbAcquisitionCampaignTask campaign = {
.task_id = task_id,
.scanset_id = scanset_id,
.next_group_id = 0,
.group_count = group_count,
.request = request,
.request_size = sizeof(request),
};
return lardon3d_project_db_record_acquisition_campaign_task(
database, &snapshot, LARDON3D_ACQUISITION_CAMPAIGN_TASK_KIND,
LARDON3D_ACQUISITION_CAMPAIGN_TASK_KIND_VERSION, NULL, &campaign,
1) == LARDON3D_PROJECT_DB_OK;
}
typedef struct {
uint64_t scanset_id;
uint64_t configuration_id;
uint64_t alternate_configuration_id;
uint64_t capture_id;
} RepairOpticalFixture;
static bool seed_repair_optical_fixture(Lardon3DProjectDb *database,
RepairOpticalFixture *fixture) {
memset(fixture, 0, sizeof(*fixture));
Lardon3DProjectDbScanSet scanset;
if (lardon3d_project_db_create_scanset(database, "repair optical fixture",
&scanset) != LARDON3D_PROJECT_DB_OK)
return false;
Lardon3DOpticalCameraBodyProfile body_input = {0};
memcpy(body_input.manufacturer, "Generic", sizeof("Generic"));
memcpy(body_input.model, "Repair body", sizeof("Repair body"));
memcpy(body_input.name, "Repair body profile",
sizeof("Repair body profile"));
Lardon3DOpticalCameraBodyProfile body;
if (lardon3d_optical_camera_body_create(database, &body_input, &body) !=
LARDON3D_PROJECT_DB_OK)
return false;
Lardon3DOpticalLensProfile lens_input = {
.interface_kind = LARDON3D_OPTICAL_LENS_ELECTRONIC,
.focal_range_kind = LARDON3D_OPTICAL_FOCAL_RANGE_ZOOM,
.minimum_focal_um = 16000,
.maximum_focal_um = 50000,
};
memcpy(lens_input.manufacturer, "Generic", sizeof("Generic"));
memcpy(lens_input.model, "Repair zoom", sizeof("Repair zoom"));
memcpy(lens_input.name, "Repair 16-50 zoom",
sizeof("Repair 16-50 zoom"));
Lardon3DOpticalLensProfile lens;
if (lardon3d_optical_lens_create(database, &lens_input, &lens) !=
LARDON3D_PROJECT_DB_OK)
return false;
Lardon3DOpticalConfiguration configuration_input = {
.camera_body_profile_id = body.camera_body_profile_id,
.lens_profile_id = lens.lens_profile_id,
.has_focal_length = true,
.focal_length_um = 16000,
};
Lardon3DOpticalConfiguration configuration;
if (lardon3d_optical_configuration_create(database, &configuration_input,
&configuration) !=
LARDON3D_PROJECT_DB_OK)
return false;
configuration_input.focal_length_um = 24000;
Lardon3DOpticalConfiguration alternate;
if (lardon3d_optical_configuration_create(database, &configuration_input,
&alternate) !=
LARDON3D_PROJECT_DB_OK)
return false;
Lardon3DProjectDbCapture capture;
if (lardon3d_project_db_create_capture(database, scanset.scanset_id, 1,
&capture) != LARDON3D_PROJECT_DB_OK)
return false;
fixture->scanset_id = scanset.scanset_id;
fixture->configuration_id = configuration.optical_configuration_id;
fixture->alternate_configuration_id = alternate.optical_configuration_id;
fixture->capture_id = capture.capture_id;
return true;
}
static bool expect_group_assign_corrupt_without_row(
const char *path, uint64_t task_id, uint64_t configuration_id) {
Lardon3DProjectDb *database = NULL;
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
if (lardon3d_project_db_open(path, &database, error) !=
LARDON3D_PROJECT_DB_OK)
return false;
bool ok = lardon3d_optical_campaign_group_assign(
database, task_id, 1, configuration_id) ==
LARDON3D_PROJECT_DB_CORRUPT;
lardon3d_project_db_close(database);
sqlite3_int64 count = -1;
return ok &&
raw_integer(path,
"SELECT COUNT(*) FROM acquisition_campaign_group_optics",
&count) &&
count == 0;
}
static bool expect_retain_result_unadvanced(
const char *path, uint64_t task_id, uint64_t capture_id,
Lardon3DProjectDbResult expected) {
Lardon3DProjectDb *database = NULL;
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
if (lardon3d_project_db_open(path, &database, error) !=
LARDON3D_PROJECT_DB_OK)
return false;
bool ok = lardon3d_project_db_retain_acquisition_campaign_capture(
database, task_id, 1, capture_id, 1) == expected;
unsigned char request[16];
Lardon3DProjectDbAcquisitionCampaignTask campaign;
Lardon3DProjectDbAcquisitionCampaignCapture mapping;
ok = ok &&
lardon3d_project_db_load_acquisition_campaign_task(
database, task_id, request, sizeof(request), &campaign) ==
LARDON3D_PROJECT_DB_OK &&
campaign.next_group_id == 0 &&
lardon3d_project_db_load_acquisition_campaign_capture(
database, task_id, 1, &mapping) == LARDON3D_PROJECT_DB_NOT_FOUND;
lardon3d_project_db_close(database);
return ok;
}
static bool test_profiles_assignments_and_calibrations(void) {
char directory[64];
char path[256];
CHECK(make_database_path(directory, path));
Lardon3DProjectDb *database = NULL;
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_schema_version(database) ==
LARDON3D_PROJECT_DB_SCHEMA_VERSION);
Lardon3DProjectDbScanSet scanset;
CHECK(lardon3d_project_db_create_scanset(database, "mixed optical campaign",
&scanset) == LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalCameraBodyProfile sony_input = {0};
memcpy(sony_input.manufacturer, "Sony", 5);
memcpy(sony_input.model, "ILCE-6000", 10);
memcpy(sony_input.name, "A6000 body", 11);
Lardon3DOpticalCameraBodyProfile sony;
CHECK(lardon3d_optical_camera_body_create(database, &sony_input, &sony) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalCameraBodyProfile retry_body;
CHECK(lardon3d_optical_camera_body_create(database, &sony_input, &retry_body) ==
LARDON3D_PROJECT_DB_OK &&
retry_body.camera_body_profile_id == sony.camera_body_profile_id);
Lardon3DOpticalCameraBodyAlias body_alias;
CHECK(lardon3d_optical_camera_body_alias_add(
database, sony.camera_body_profile_id, "SONY", "ILCE-6000",
&body_alias) == LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalCameraBodyProfile alias_body;
CHECK(lardon3d_optical_camera_body_find_exact_alias(
database, "SONY", "ILCE-6000", &alias_body) ==
LARDON3D_PROJECT_DB_OK &&
alias_body.camera_body_profile_id == sony.camera_body_profile_id);
memset(&alias_body, 0x7f, sizeof(alias_body));
CHECK(lardon3d_optical_camera_body_find_exact_alias(
database, "sony", "ILCE-6000", &alias_body) ==
LARDON3D_PROJECT_DB_NOT_FOUND &&
alias_body.camera_body_profile_id == 0 && alias_body.name[0] == '\0');
Lardon3DOpticalCameraBodyProfile second_body_input = {0};
memcpy(second_body_input.manufacturer, "Samsung", 8);
memcpy(second_body_input.model, "SM-G990B", 9);
memcpy(second_body_input.name, "S21 FE body", 12);
Lardon3DOpticalCameraBodyProfile second_body;
CHECK(lardon3d_optical_camera_body_create(database, &second_body_input,
&second_body) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalCameraBodyAlias conflict_alias;
CHECK(lardon3d_optical_camera_body_alias_add(
database, second_body.camera_body_profile_id, "SONY", "ILCE-6000",
&conflict_alias) == LARDON3D_PROJECT_DB_CONSTRAINT);
Lardon3DOpticalLensProfile zoom_input = {
.interface_kind = LARDON3D_OPTICAL_LENS_ELECTRONIC,
.focal_range_kind = LARDON3D_OPTICAL_FOCAL_RANGE_ZOOM,
.minimum_focal_um = 16000,
.maximum_focal_um = 50000,
};
memcpy(zoom_input.manufacturer, "Sony", 5);
memcpy(zoom_input.model, "SELP1650", 9);
memcpy(zoom_input.name, "E PZ 16-50 OSS", 15);
Lardon3DOpticalLensProfile zoom;
CHECK(lardon3d_optical_lens_create(database, &zoom_input, &zoom) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalLensAlias zoom_alias;
CHECK(lardon3d_optical_lens_alias_add(
database, zoom.lens_profile_id, "SONY",
"E PZ 16-50mm F3.5-5.6 OSS", &zoom_alias) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalLensProfile exact_lens;
CHECK(lardon3d_optical_lens_find_exact_alias(
database, "SONY", "E PZ 16-50mm F3.5-5.6 OSS", &exact_lens) ==
LARDON3D_PROJECT_DB_OK &&
exact_lens.lens_profile_id == zoom.lens_profile_id);
/* A manual lens has no electronics and therefore legitimately has an empty
alias page. It remains fully usable by explicit profile/config selection. */
Lardon3DOpticalLensProfile meike_input = {
.interface_kind = LARDON3D_OPTICAL_LENS_MANUAL,
.focal_range_kind = LARDON3D_OPTICAL_FOCAL_RANGE_PRIME,
.minimum_focal_um = 12000,
.maximum_focal_um = 12000,
};
memcpy(meike_input.manufacturer, "Meike", 6);
memcpy(meike_input.model, "12mm F2.8", sizeof("12mm F2.8"));
memcpy(meike_input.name, "Meike manual 12 mm", 19);
Lardon3DOpticalLensProfile meike;
CHECK(lardon3d_optical_lens_create(database, &meike_input, &meike) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalLensAlias no_alias_items[2];
size_t no_alias_count = 99;
uint64_t no_alias_next = 99;
CHECK(lardon3d_optical_lens_alias_list(
database, meike.lens_profile_id, 0, no_alias_items, 2,
&no_alias_count, &no_alias_next) == LARDON3D_PROJECT_DB_OK &&
no_alias_count == 0 && no_alias_next == 0);
Lardon3DOpticalLensProfile prime_input = {
.interface_kind = LARDON3D_OPTICAL_LENS_ELECTRONIC,
.focal_range_kind = LARDON3D_OPTICAL_FOCAL_RANGE_PRIME,
.minimum_focal_um = 50000,
.maximum_focal_um = 50000,
};
memcpy(prime_input.manufacturer, "Generic", 8);
memcpy(prime_input.model, "50 Prime", 9);
memcpy(prime_input.name, "New 50 mm profile", 18);
Lardon3DOpticalLensProfile prime;
CHECK(lardon3d_optical_lens_create(database, &prime_input, &prime) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalLensProfile lens_page[2];
size_t lens_count = 0;
uint64_t lens_next = 0;
CHECK(lardon3d_optical_lens_list(database, 0, lens_page, 2, &lens_count,
&lens_next) == LARDON3D_PROJECT_DB_OK &&
lens_count == 2 && lens_next == meike.lens_profile_id);
CHECK(lardon3d_optical_lens_list(database, lens_next, lens_page, 2,
&lens_count, &lens_next) ==
LARDON3D_PROJECT_DB_OK &&
lens_count == 1 && lens_page[0].lens_profile_id == prime.lens_profile_id);
Lardon3DOpticalLensProfile unknown_manual_input = {
.interface_kind = LARDON3D_OPTICAL_LENS_MANUAL,
.focal_range_kind = LARDON3D_OPTICAL_FOCAL_RANGE_UNKNOWN,
};
memcpy(unknown_manual_input.name, "Unidentified manual lens",
sizeof("Unidentified manual lens"));
Lardon3DOpticalLensProfile unknown_manual;
CHECK(lardon3d_optical_lens_create(database, &unknown_manual_input,
&unknown_manual) ==
LARDON3D_PROJECT_DB_OK &&
unknown_manual.manufacturer[0] == '\0' &&
unknown_manual.model[0] == '\0' &&
unknown_manual.minimum_focal_um == 0 &&
unknown_manual.maximum_focal_um == 0);
Lardon3DOpticalConfiguration configuration_input = {
.camera_body_profile_id = sony.camera_body_profile_id,
.lens_profile_id = zoom.lens_profile_id,
.has_focal_length = true,
.focal_length_um = 16000,
};
Lardon3DOpticalConfiguration config16;
CHECK(lardon3d_optical_configuration_create(database, &configuration_input,
&config16) ==
LARDON3D_PROJECT_DB_OK);
configuration_input.focal_length_um = 24000;
Lardon3DOpticalConfiguration config24;
CHECK(lardon3d_optical_configuration_create(database, &configuration_input,
&config24) ==
LARDON3D_PROJECT_DB_OK);
configuration_input.focal_length_um = 50000;
Lardon3DOpticalConfiguration config50;
CHECK(lardon3d_optical_configuration_create(database, &configuration_input,
&config50) ==
LARDON3D_PROJECT_DB_OK);
CHECK(config16.optical_configuration_id != config24.optical_configuration_id &&
config24.optical_configuration_id != config50.optical_configuration_id);
configuration_input.focal_length_um = 51000;
Lardon3DOpticalConfiguration invalid_configuration;
CHECK(lardon3d_optical_configuration_create(database, &configuration_input,
&invalid_configuration) ==
LARDON3D_PROJECT_DB_CONSTRAINT);
Lardon3DOpticalConfiguration meike_configuration_input = {
.camera_body_profile_id = sony.camera_body_profile_id,
.lens_profile_id = meike.lens_profile_id,
.has_focal_length = true,
.focal_length_um = 12000,
};
Lardon3DOpticalConfiguration meike_configuration;
CHECK(lardon3d_optical_configuration_create(
database, &meike_configuration_input, &meike_configuration) ==
LARDON3D_PROJECT_DB_OK);
meike_configuration_input.focal_length_um = 13000;
CHECK(lardon3d_optical_configuration_create(
database, &meike_configuration_input, &invalid_configuration) ==
LARDON3D_PROJECT_DB_CONSTRAINT);
Lardon3DProjectDbCapture captures[5];
for (size_t index = 0; index < 5; ++index)
CHECK(lardon3d_project_db_create_capture(database, scanset.scanset_id,
(int64_t)index + 1,
&captures[index]) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalCaptureAssignment unresolved;
memset(&unresolved, 0x7f, sizeof(unresolved));
CHECK(lardon3d_optical_capture_assignment_load(
database, captures[4].capture_id, &unresolved) ==
LARDON3D_PROJECT_DB_NOT_FOUND &&
unresolved.capture_id == 0 && unresolved.optical_configuration_id == 0);
CHECK(lardon3d_optical_capture_assign_explicit(
database, captures[3].capture_id,
meike_configuration.optical_configuration_id) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_optical_capture_assign_explicit(
database, captures[3].capture_id,
meike_configuration.optical_configuration_id) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_optical_capture_assign_explicit(
database, captures[3].capture_id,
config16.optical_configuration_id) ==
LARDON3D_PROJECT_DB_CONSTRAINT);
Lardon3DOpticalCaptureAssignment manual_assignment;
CHECK(lardon3d_optical_capture_assignment_load(
database, captures[3].capture_id, &manual_assignment) ==
LARDON3D_PROJECT_DB_OK &&
manual_assignment.provenance ==
LARDON3D_OPTICAL_ASSIGNMENT_CALLER_EXPLICIT &&
!manual_assignment.has_campaign_origin);
CHECK(seed_campaign(database, 101, scanset.scanset_id, 1));
CHECK(lardon3d_optical_campaign_group_assign(
database, 101, 1, config16.optical_configuration_id) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_project_db_retain_acquisition_campaign_capture(
database, 101, 1, captures[3].capture_id, 1) ==
LARDON3D_PROJECT_DB_CONSTRAINT);
Lardon3DProjectDbAcquisitionCampaignCapture conflicted_mapping;
CHECK(lardon3d_project_db_load_acquisition_campaign_capture(
database, 101, 1, &conflicted_mapping) ==
LARDON3D_PROJECT_DB_NOT_FOUND);
unsigned char conflict_request[16];
Lardon3DProjectDbAcquisitionCampaignTask conflict_campaign;
CHECK(lardon3d_project_db_load_acquisition_campaign_task(
database, 101, conflict_request, sizeof(conflict_request),
&conflict_campaign) == LARDON3D_PROJECT_DB_OK &&
conflict_campaign.next_group_id == 0);
CHECK(seed_campaign(database, 100, scanset.scanset_id, 3));
CHECK(lardon3d_optical_campaign_group_assign(
database, 100, 1, config16.optical_configuration_id) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_optical_campaign_group_assign(
database, 100, 3, config50.optical_configuration_id) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_optical_campaign_group_assign(
database, 100, 1, config16.optical_configuration_id) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_optical_campaign_group_assign(
database, 100, 1, config24.optical_configuration_id) ==
LARDON3D_PROJECT_DB_CONSTRAINT);
Lardon3DOpticalCampaignGroupAssignment loaded_group;
CHECK(lardon3d_optical_campaign_group_load(database, 100, 1, &loaded_group) ==
LARDON3D_PROJECT_DB_OK &&
loaded_group.optical_configuration_id ==
config16.optical_configuration_id);
memset(&loaded_group, 0x7f, sizeof(loaded_group));
CHECK(lardon3d_optical_campaign_group_load(database, 100, 2, &loaded_group) ==
LARDON3D_PROJECT_DB_NOT_FOUND &&
loaded_group.campaign_task_id == 0);
CHECK(lardon3d_project_db_retain_acquisition_campaign_capture(
database, 100, 1, captures[0].capture_id, 1) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalCaptureAssignment campaign_assignment;
CHECK(lardon3d_optical_capture_assignment_load(
database, captures[0].capture_id, &campaign_assignment) ==
LARDON3D_PROJECT_DB_OK &&
campaign_assignment.optical_configuration_id ==
config16.optical_configuration_id &&
campaign_assignment.provenance == LARDON3D_OPTICAL_ASSIGNMENT_CAMPAIGN &&
campaign_assignment.has_campaign_origin &&
campaign_assignment.campaign_task_id == 100 &&
campaign_assignment.campaign_group_id == 1);
CHECK(lardon3d_project_db_retain_acquisition_campaign_capture(
database, 100, 1, captures[0].capture_id, 1) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_optical_campaign_group_assign(
database, 100, 2, config24.optical_configuration_id) ==
LARDON3D_PROJECT_DB_CONSTRAINT);
CHECK(lardon3d_project_db_retain_acquisition_campaign_capture(
database, 100, 2, captures[1].capture_id, 2) ==
LARDON3D_PROJECT_DB_OK);
memset(&unresolved, 0x7f, sizeof(unresolved));
CHECK(lardon3d_optical_capture_assignment_load(
database, captures[1].capture_id, &unresolved) ==
LARDON3D_PROJECT_DB_NOT_FOUND &&
unresolved.capture_id == 0);
/* Failure after mapping+optics insertion but before cursor publication must
roll back all three durable facts as one unit. */
CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_CAMPAIGN_OPTICS_COPY", "1", 1) ==
0);
CHECK(lardon3d_project_db_retain_acquisition_campaign_capture(
database, 100, 3, captures[2].capture_id, 3) !=
LARDON3D_PROJECT_DB_OK);
CHECK(unsetenv("LARDON3D_TEST_PROJECT_DB_FAIL_CAMPAIGN_OPTICS_COPY") == 0);
Lardon3DProjectDbAcquisitionCampaignCapture missing_mapping;
CHECK(lardon3d_project_db_load_acquisition_campaign_capture(
database, 100, 3, &missing_mapping) == LARDON3D_PROJECT_DB_NOT_FOUND);
CHECK(lardon3d_optical_capture_assignment_load(
database, captures[2].capture_id, &unresolved) ==
LARDON3D_PROJECT_DB_NOT_FOUND);
unsigned char campaign_request[16];
Lardon3DProjectDbAcquisitionCampaignTask campaign_state;
CHECK(lardon3d_project_db_load_acquisition_campaign_task(
database, 100, campaign_request, sizeof(campaign_request),
&campaign_state) == LARDON3D_PROJECT_DB_OK &&
campaign_state.next_group_id == 2);
CHECK(lardon3d_project_db_retain_acquisition_campaign_capture(
database, 100, 3, captures[2].capture_id, 3) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_optical_capture_assignment_load(
database, captures[2].capture_id, &campaign_assignment) ==
LARDON3D_PROJECT_DB_OK &&
campaign_assignment.optical_configuration_id ==
config50.optical_configuration_id);
Lardon3DSparseCalibration calibration_a;
Lardon3DSparseCalibration calibration_b;
Lardon3DSparseCalibration calibration_other;
CHECK(create_sparse_calibration(database, 1, 6000, 4000, &calibration_a));
CHECK(create_sparse_calibration(database, 2, 6000, 4000, &calibration_b));
CHECK(create_sparse_calibration(database, 3, 4000, 3000,
&calibration_other));
Lardon3DOpticalCalibrationProfile compatible[3];
size_t compatible_count = 77;
uint64_t compatible_next = 77;
CHECK(lardon3d_optical_calibration_profile_list_compatible(
database, config16.optical_configuration_id, 0, compatible, 3,
&compatible_count, &compatible_next) == LARDON3D_PROJECT_DB_OK &&
compatible_count == 0 && compatible_next == 0);
Lardon3DOpticalCalibrationProfile profile_input = {
.optical_configuration_id = config16.optical_configuration_id,
.sparse_calibration_id = calibration_a.calibration_id,
.profile_version = 1,
.applicability = LARDON3D_OPTICAL_CALIBRATION_EXACT_CONFIGURATION,
.created_at = 100,
};
memcpy(profile_input.name, "Lab calibration", 16);
memcpy(profile_input.provenance, "Dedicated checkerboard session", 31);
Lardon3DOpticalCalibrationProfile profile_a;
CHECK(lardon3d_optical_calibration_profile_create(database, &profile_input,
&profile_a) ==
LARDON3D_PROJECT_DB_OK);
compatible_count = 0;
compatible_next = 0;
CHECK(lardon3d_optical_calibration_profile_list_compatible(
database, config16.optical_configuration_id, 0, compatible, 3,
&compatible_count, &compatible_next) == LARDON3D_PROJECT_DB_OK &&
compatible_count == 1 &&
compatible[0].calibration_profile_id == profile_a.calibration_profile_id);
Lardon3DOpticalCalibrationProfile profile_retry;
CHECK(lardon3d_optical_calibration_profile_create(database, &profile_input,
&profile_retry) ==
LARDON3D_PROJECT_DB_OK &&
profile_retry.calibration_profile_id == profile_a.calibration_profile_id);
profile_input.sparse_calibration_id = calibration_b.calibration_id;
memcpy(profile_input.name, "Field calibration", 18);
profile_input.created_at = 101;
Lardon3DOpticalCalibrationProfile profile_b;
CHECK(lardon3d_optical_calibration_profile_create(database, &profile_input,
&profile_b) ==
LARDON3D_PROJECT_DB_OK);
profile_input.optical_configuration_id = config50.optical_configuration_id;
profile_input.sparse_calibration_id = calibration_other.calibration_id;
memcpy(profile_input.name, "Fifty calibration", 18);
profile_input.created_at = 102;
Lardon3DOpticalCalibrationProfile profile_other;
CHECK(lardon3d_optical_calibration_profile_create(database, &profile_input,
&profile_other) ==
LARDON3D_PROJECT_DB_OK);
profile_input.sparse_calibration_id = INT64_MAX;
memcpy(profile_input.name, "Missing calibration", 20);
CHECK(lardon3d_optical_calibration_profile_create(database, &profile_input,
&profile_retry) ==
LARDON3D_PROJECT_DB_NOT_FOUND &&
profile_retry.calibration_profile_id == 0);
compatible_count = 0;
compatible_next = 0;
CHECK(lardon3d_optical_calibration_profile_list_compatible(
database, config16.optical_configuration_id, 0, compatible, 3,
&compatible_count, &compatible_next) == LARDON3D_PROJECT_DB_OK &&
compatible_count == 2 &&
compatible[0].calibration_profile_id == profile_a.calibration_profile_id &&
compatible[1].calibration_profile_id == profile_b.calibration_profile_id);
Lardon3DOpticalCaptureCalibrationSelection selection;
memset(&selection, 0x7f, sizeof(selection));
CHECK(lardon3d_optical_capture_calibration_selection_load(
database, captures[0].capture_id, &selection) ==
LARDON3D_PROJECT_DB_NOT_FOUND &&
selection.capture_id == 0);
CHECK(lardon3d_optical_capture_calibration_select(
database, captures[0].capture_id,
profile_other.calibration_profile_id) ==
LARDON3D_PROJECT_DB_CONSTRAINT);
CHECK(lardon3d_optical_capture_calibration_select(
database, captures[0].capture_id, profile_a.calibration_profile_id) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_optical_capture_calibration_select(
database, captures[0].capture_id, profile_a.calibration_profile_id) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_optical_capture_calibration_select(
database, captures[0].capture_id, profile_b.calibration_profile_id) ==
LARDON3D_PROJECT_DB_CONSTRAINT);
CHECK(lardon3d_optical_capture_calibration_selection_load(
database, captures[0].capture_id, &selection) ==
LARDON3D_PROJECT_DB_OK &&
selection.calibration_profile_id == profile_a.calibration_profile_id &&
selection.optical_configuration_id == config16.optical_configuration_id &&
selection.sparse_calibration_id == calibration_a.calibration_id);
CHECK(lardon3d_optical_capture_calibration_select(
database, captures[1].capture_id, profile_a.calibration_profile_id) ==
LARDON3D_PROJECT_DB_CONSTRAINT);
lardon3d_project_db_close(database);
database = NULL;
/* Dynamic SQLite typing is adversarially corrupted after disabling checks;
public loaders must reject before narrowing and leave outputs zeroed. */
char corruption[1024];
int corruption_bytes = snprintf(
corruption, sizeof(corruption),
"PRAGMA ignore_check_constraints=ON;UPDATE lens_profiles SET "
"focal_range_kind='zoom-ish' WHERE lens_profile_id=%llu;",
(unsigned long long)zoom.lens_profile_id);
CHECK(corruption_bytes > 0 && (size_t)corruption_bytes < sizeof(corruption));
CHECK(raw_sql(path, corruption));
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
memset(&exact_lens, 0x7f, sizeof(exact_lens));
CHECK(lardon3d_optical_lens_load(database, zoom.lens_profile_id, &exact_lens) ==
LARDON3D_PROJECT_DB_CORRUPT &&
exact_lens.lens_profile_id == 0 && exact_lens.name[0] == '\0');
lardon3d_project_db_close(database);
database = NULL;
corruption_bytes = snprintf(
corruption, sizeof(corruption),
"PRAGMA ignore_check_constraints=ON;UPDATE lens_profiles SET "
"focal_range_kind=3 WHERE lens_profile_id=%llu;UPDATE "
"optical_configurations SET focal_length_um='16000x' WHERE "
"optical_configuration_id=%llu;",
(unsigned long long)zoom.lens_profile_id,
(unsigned long long)config16.optical_configuration_id);
CHECK(corruption_bytes > 0 && (size_t)corruption_bytes < sizeof(corruption));
CHECK(raw_sql(path, corruption));
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalConfiguration corrupt_configuration;
memset(&corrupt_configuration, 0x7f, sizeof(corrupt_configuration));
CHECK(lardon3d_optical_configuration_load(
database, config16.optical_configuration_id,
&corrupt_configuration) == LARDON3D_PROJECT_DB_CORRUPT &&
corrupt_configuration.optical_configuration_id == 0);
lardon3d_project_db_close(database);
database = NULL;
corruption_bytes = snprintf(
corruption, sizeof(corruption),
"PRAGMA foreign_keys=OFF;UPDATE optical_configurations SET "
"focal_length_um=16000 WHERE optical_configuration_id=%llu;"
"UPDATE captures SET scanset_id=9223372036854775807 WHERE "
"capture_id=%llu;",
(unsigned long long)config16.optical_configuration_id,
(unsigned long long)captures[3].capture_id);
CHECK(corruption_bytes > 0 && (size_t)corruption_bytes < sizeof(corruption));
CHECK(raw_sql(path, corruption));
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
memset(&manual_assignment, 0x7f, sizeof(manual_assignment));
CHECK(lardon3d_optical_capture_assignment_load(
database, captures[3].capture_id, &manual_assignment) ==
LARDON3D_PROJECT_DB_CORRUPT &&
manual_assignment.capture_id == 0);
lardon3d_project_db_close(database);
database = NULL;
CHECK(raw_sql(path,
"PRAGMA ignore_check_constraints=ON;UPDATE "
"acquisition_campaign_captures SET group_id='1x' WHERE "
"task_id=100 AND group_id=1;"));
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_optical_campaign_group_assign(
database, 100, 1, config16.optical_configuration_id) ==
LARDON3D_PROJECT_DB_CORRUPT);
memset(&loaded_group, 0x7f, sizeof(loaded_group));
CHECK(lardon3d_optical_campaign_group_load(database, 100, 1, &loaded_group) ==
LARDON3D_PROJECT_DB_CORRUPT &&
loaded_group.campaign_task_id == 0);
memset(&campaign_assignment, 0x7f, sizeof(campaign_assignment));
CHECK(lardon3d_optical_capture_assignment_load(
database, captures[0].capture_id, &campaign_assignment) ==
LARDON3D_PROJECT_DB_CORRUPT &&
campaign_assignment.capture_id == 0);
lardon3d_project_db_close(database);
CHECK(unlink(path) == 0);
CHECK(rmdir(directory) == 0);
return true;
}
static bool test_calibration_conflict_result_contract(void) {
char directory[64];
char path[256];
CHECK(make_database_path(directory, path));
Lardon3DProjectDb *database = NULL;
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
RepairOpticalFixture fixture;
CHECK(seed_repair_optical_fixture(database, &fixture));
Lardon3DSparseCalibration calibration;
CHECK(create_sparse_calibration(database, 71, 6000, 4000, &calibration));
Lardon3DOpticalCalibrationProfile input = {
.optical_configuration_id = fixture.configuration_id,
.sparse_calibration_id = calibration.calibration_id,
.profile_version = 1,
.applicability = LARDON3D_OPTICAL_CALIBRATION_EXACT_CONFIGURATION,
.created_at = 10,
};
memcpy(input.name, "Retry identity", sizeof("Retry identity"));
memcpy(input.provenance, "Original provenance",
sizeof("Original provenance"));
Lardon3DOpticalCalibrationProfile created;
CHECK(lardon3d_optical_calibration_profile_create(database, &input, &created) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalCalibrationProfile conflict = input;
memset(conflict.provenance, 0, sizeof(conflict.provenance));
memcpy(conflict.provenance, "Different valid provenance",
sizeof("Different valid provenance"));
Lardon3DOpticalCalibrationProfile conflict_output;
memset(&conflict_output, 0x7f, sizeof(conflict_output));
CHECK(lardon3d_optical_calibration_profile_create(
database, &conflict, &conflict_output) ==
LARDON3D_PROJECT_DB_CONSTRAINT &&
conflict_output.calibration_profile_id == 0 &&
conflict_output.provenance[0] == '\0');
Lardon3DOpticalCalibrationProfile unchanged;
CHECK(lardon3d_optical_calibration_profile_load(
database, created.calibration_profile_id, &unchanged) ==
LARDON3D_PROJECT_DB_OK &&
strcmp(unchanged.provenance, input.provenance) == 0 &&
unchanged.created_at == input.created_at);
lardon3d_project_db_close(database);
database = NULL;
sqlite3_int64 count = 0;
CHECK(raw_integer(path, "SELECT COUNT(*) FROM optical_calibration_profiles",
&count) &&
count == 1);
/* A BLOB in the TEXT property is durable corruption, not a second kind of
caller conflict, even when the natural-key fields still match. */
CHECK(raw_sql(path,
"PRAGMA ignore_check_constraints=ON;UPDATE "
"optical_calibration_profiles SET provenance=X'626164';"));
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
memset(&conflict_output, 0x7f, sizeof(conflict_output));
CHECK(lardon3d_optical_calibration_profile_create(
database, &input, &conflict_output) == LARDON3D_PROJECT_DB_CORRUPT &&
conflict_output.calibration_profile_id == 0 &&
conflict_output.provenance[0] == '\0');
lardon3d_project_db_close(database);
CHECK(raw_integer(path, "SELECT COUNT(*) FROM optical_calibration_profiles",
&count) &&
count == 1);
CHECK(unlink(path) == 0);
CHECK(rmdir(directory) == 0);
return true;
}
static bool test_calibration_dependency_corruption_precedence(void) {
char directory[64];
char path[256];
CHECK(make_database_path(directory, path));
Lardon3DProjectDb *database = NULL;
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
RepairOpticalFixture fixture;
CHECK(seed_repair_optical_fixture(database, &fixture));
Lardon3DSparseCalibration calibration;
Lardon3DSparseCalibration alternate_calibration;
CHECK(create_sparse_calibration(database, 81, 6000, 4000, &calibration));
CHECK(create_sparse_calibration(database, 82, 6000, 4000,
&alternate_calibration));
Lardon3DOpticalCalibrationProfile input = {
.optical_configuration_id = fixture.configuration_id,
.sparse_calibration_id = calibration.calibration_id,
.profile_version = 1,
.applicability = LARDON3D_OPTICAL_CALIBRATION_EXACT_CONFIGURATION,
.created_at = 20,
};
memcpy(input.name, "Dependency identity", sizeof("Dependency identity"));
memcpy(input.provenance, "Dependency provenance",
sizeof("Dependency provenance"));
Lardon3DOpticalCalibrationProfile created;
CHECK(lardon3d_optical_calibration_profile_create(database, &input, &created) ==
LARDON3D_PROJECT_DB_OK);
lardon3d_project_db_close(database);
database = NULL;
char sql[512];
char unchanged_query[512];
int unchanged_bytes = snprintf(
unchanged_query, sizeof(unchanged_query),
"SELECT COUNT(*) FROM optical_calibration_profiles WHERE "
"calibration_profile_id=%llu AND optical_configuration_id=%llu AND "
"sparse_calibration_id=%llu AND name='Dependency identity' AND "
"profile_version=1 AND provenance='Dependency provenance' AND "
"applicability=1 AND created_at=20",
(unsigned long long)created.calibration_profile_id,
(unsigned long long)fixture.configuration_id,
(unsigned long long)calibration.calibration_id);
CHECK(unchanged_bytes > 0 &&
(size_t)unchanged_bytes < sizeof(unchanged_query));
int bytes = snprintf(
sql, sizeof(sql),
"UPDATE sparse_calibrations SET scientific_hash=zeroblob(32) WHERE "
"calibration_id=%llu",
(unsigned long long)calibration.calibration_id);
CHECK(bytes > 0 && (size_t)bytes < sizeof(sql));
CHECK(raw_sql(path, sql));
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
/* The natural-key row is durable, so its malformed scientific dependency
must win over both exact-retry and caller-conflict classification. */
Lardon3DOpticalCalibrationProfile output;
memset(&output, 0x7f, sizeof(output));
CHECK(lardon3d_optical_calibration_profile_create(database, &input, &output) ==
LARDON3D_PROJECT_DB_CORRUPT &&
all_bytes_zero(&output, sizeof(output)));
Lardon3DOpticalCalibrationProfile alternate = input;
alternate.sparse_calibration_id = alternate_calibration.calibration_id;
memset(&output, 0x7f, sizeof(output));
CHECK(lardon3d_optical_calibration_profile_create(
database, &alternate, &output) == LARDON3D_PROJECT_DB_CORRUPT &&
all_bytes_zero(&output, sizeof(output)));
lardon3d_project_db_close(database);
database = NULL;
sqlite3_int64 count = 0;
CHECK(raw_integer(path, unchanged_query, &count) && count == 1);
bytes = snprintf(sql, sizeof(sql),
"PRAGMA foreign_keys=OFF;DELETE FROM sparse_calibrations "
"WHERE calibration_id=%llu",
(unsigned long long)calibration.calibration_id);
CHECK(bytes > 0 && (size_t)bytes < sizeof(sql));
CHECK(raw_sql(path, sql));
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
memset(&output, 0x7f, sizeof(output));
CHECK(lardon3d_optical_calibration_profile_create(database, &input, &output) ==
LARDON3D_PROJECT_DB_CORRUPT &&
all_bytes_zero(&output, sizeof(output)));
memset(&output, 0x7f, sizeof(output));
CHECK(lardon3d_optical_calibration_profile_create(
database, &alternate, &output) == LARDON3D_PROJECT_DB_CORRUPT &&
all_bytes_zero(&output, sizeof(output)));
lardon3d_project_db_close(database);
CHECK(raw_integer(path, "SELECT COUNT(*) FROM optical_calibration_profiles",
&count) &&
count == 1);
CHECK(raw_integer(path, unchanged_query, &count) && count == 1);
CHECK(unlink(path) == 0);
CHECK(rmdir(directory) == 0);
return true;
}
static bool test_campaign_request_corruption_prevents_optics_mutation(void) {
char directory[64];
char path[256];
CHECK(make_database_path(directory, path));
Lardon3DProjectDb *database = NULL;
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
RepairOpticalFixture fixture;
CHECK(seed_repair_optical_fixture(database, &fixture));
CHECK(seed_campaign(database, 200, fixture.scanset_id, 1));
lardon3d_project_db_close(database);
CHECK(raw_sql(path,
"UPDATE acquisition_campaign_tasks SET request='not-a-blob' "
"WHERE task_id=200;"));
CHECK(expect_group_assign_corrupt_without_row(
path, 200, fixture.configuration_id));
CHECK(raw_sql(path,
"PRAGMA ignore_check_constraints=ON;UPDATE "
"acquisition_campaign_tasks SET request=X'' WHERE task_id=200;"));
CHECK(expect_group_assign_corrupt_without_row(
path, 200, fixture.configuration_id));
char oversized_update[256];
int bytes = snprintf(
oversized_update, sizeof(oversized_update),
"UPDATE acquisition_campaign_tasks SET request=zeroblob(%zu) WHERE "
"task_id=200;",
LARDON3D_ACQUISITION_CAMPAIGN_TASK_REQUEST_MAX_BYTES + 1u);
CHECK(bytes > 0 && (size_t)bytes < sizeof(oversized_update));
CHECK(raw_sql(path, oversized_update));
CHECK(expect_group_assign_corrupt_without_row(
path, 200, fixture.configuration_id));
CHECK(unlink(path) == 0);
CHECK(rmdir(directory) == 0);
return true;
}
static bool test_capture_optics_conflict_and_corruption_rollback(void) {
char directory[64];
char path[256];
CHECK(make_database_path(directory, path));
Lardon3DProjectDb *database = NULL;
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
RepairOpticalFixture fixture;
CHECK(seed_repair_optical_fixture(database, &fixture));
CHECK(seed_campaign(database, 300, fixture.scanset_id, 1));
CHECK(lardon3d_optical_campaign_group_assign(
database, 300, 1, fixture.configuration_id) ==
LARDON3D_PROJECT_DB_OK);
CHECK(lardon3d_optical_capture_assign_explicit(
database, fixture.capture_id,
fixture.alternate_configuration_id) == LARDON3D_PROJECT_DB_OK);
lardon3d_project_db_close(database);
CHECK(expect_retain_result_unadvanced(
path, 300, fixture.capture_id, LARDON3D_PROJECT_DB_CONSTRAINT));
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
Lardon3DOpticalCaptureAssignment assignment;
CHECK(lardon3d_optical_capture_assignment_load(
database, fixture.capture_id, &assignment) ==
LARDON3D_PROJECT_DB_OK &&
assignment.provenance ==
LARDON3D_OPTICAL_ASSIGNMENT_CALLER_EXPLICIT &&
assignment.optical_configuration_id ==
fixture.alternate_configuration_id);
lardon3d_project_db_close(database);
char mutation[512];
int bytes = snprintf(
mutation, sizeof(mutation),
"PRAGMA ignore_check_constraints=ON;UPDATE "
"capture_optical_configurations SET assignment_provenance=X'31' WHERE "
"capture_id=%llu;",
(unsigned long long)fixture.capture_id);
CHECK(bytes > 0 && (size_t)bytes < sizeof(mutation));
CHECK(raw_sql(path, mutation));
CHECK(expect_retain_result_unadvanced(
path, 300, fixture.capture_id, LARDON3D_PROJECT_DB_CORRUPT));
bytes = snprintf(
mutation, sizeof(mutation),
"PRAGMA ignore_check_constraints=ON;UPDATE "
"capture_optical_configurations SET assignment_provenance=9,"
"campaign_task_id=NULL,campaign_group_id=NULL WHERE capture_id=%llu;",
(unsigned long long)fixture.capture_id);
CHECK(bytes > 0 && (size_t)bytes < sizeof(mutation));
CHECK(raw_sql(path, mutation));
CHECK(expect_retain_result_unadvanced(
path, 300, fixture.capture_id, LARDON3D_PROJECT_DB_CORRUPT));
bytes = snprintf(
mutation, sizeof(mutation),
"PRAGMA ignore_check_constraints=ON;UPDATE "
"capture_optical_configurations SET assignment_provenance=2,"
"campaign_task_id=300,campaign_group_id=1 WHERE capture_id=%llu;",
(unsigned long long)fixture.capture_id);
CHECK(bytes > 0 && (size_t)bytes < sizeof(mutation));
CHECK(raw_sql(path, mutation));
CHECK(expect_retain_result_unadvanced(
path, 300, fixture.capture_id, LARDON3D_PROJECT_DB_CORRUPT));
bytes = snprintf(
mutation, sizeof(mutation),
"PRAGMA foreign_keys=OFF;PRAGMA ignore_check_constraints=ON;DELETE FROM "
"capture_optical_configurations WHERE capture_id=%llu;INSERT INTO "
"capture_optical_configurations(capture_id,optical_configuration_id,"
"assignment_provenance,campaign_task_id,campaign_group_id) VALUES("
"%llu,%llu,1,300,1);",
(unsigned long long)fixture.capture_id,
(unsigned long long)fixture.capture_id,
(unsigned long long)fixture.configuration_id);
CHECK(bytes > 0 && (size_t)bytes < sizeof(mutation));
CHECK(raw_sql(path, mutation));
/* This exact-looking row is ahead of the cursor and must not be repaired by
the mapping insertion that is later rolled back. */
CHECK(expect_retain_result_unadvanced(
path, 300, fixture.capture_id, LARDON3D_PROJECT_DB_CORRUPT));
sqlite3_int64 count = 0;
CHECK(raw_integer(path,
"SELECT COUNT(*) FROM capture_optical_configurations",
&count) &&
count == 1);
CHECK(raw_integer(path, "SELECT COUNT(*) FROM acquisition_campaign_captures",
&count) &&
count == 0);
CHECK(unlink(path) == 0);
CHECK(rmdir(directory) == 0);
return true;
}
static bool test_malformed_configuration_rolls_back_campaign_retention(void) {
char directory[64];
char path[256];
CHECK(make_database_path(directory, path));
Lardon3DProjectDb *database = NULL;
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
CHECK(lardon3d_project_db_open(path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
RepairOpticalFixture fixture;
CHECK(seed_repair_optical_fixture(database, &fixture));
CHECK(seed_campaign(database, 301, fixture.scanset_id, 1));
CHECK(lardon3d_optical_campaign_group_assign(
database, 301, 1, fixture.configuration_id) ==
LARDON3D_PROJECT_DB_OK);
lardon3d_project_db_close(database);
char mutation[512];
int bytes = snprintf(
mutation, sizeof(mutation),
"PRAGMA ignore_check_constraints=ON;UPDATE optical_configurations SET "
"focal_length_um='16000x' WHERE optical_configuration_id=%llu;",
(unsigned long long)fixture.configuration_id);
CHECK(bytes > 0 && (size_t)bytes < sizeof(mutation));
CHECK(raw_sql(path, mutation));
CHECK(expect_retain_result_unadvanced(
path, 301, fixture.capture_id, LARDON3D_PROJECT_DB_CORRUPT));
sqlite3_int64 count = 0;
CHECK(raw_integer(path, "SELECT COUNT(*) FROM acquisition_campaign_captures",
&count) &&
count == 0);
CHECK(raw_integer(path,
"SELECT COUNT(*) FROM capture_optical_configurations",
&count) &&
count == 0);
bytes = snprintf(
mutation, sizeof(mutation),
"PRAGMA ignore_check_constraints=ON;UPDATE optical_configurations SET "
"focal_length_um=16000 WHERE optical_configuration_id=%llu;UPDATE "
"lens_profiles SET focal_range_kind=2,minimum_focal_um=24000,"
"maximum_focal_um=24000;",
(unsigned long long)fixture.configuration_id);
CHECK(bytes > 0 && (size_t)bytes < sizeof(mutation));
CHECK(raw_sql(path, mutation));
CHECK(expect_retain_result_unadvanced(
path, 301, fixture.capture_id, LARDON3D_PROJECT_DB_CORRUPT));
CHECK(raw_integer(path, "SELECT COUNT(*) FROM acquisition_campaign_captures",
&count) &&
count == 0);
CHECK(raw_integer(path,
"SELECT COUNT(*) FROM capture_optical_configurations",
&count) &&
count == 0);
CHECK(unlink(path) == 0);
CHECK(rmdir(directory) == 0);
return true;
}
static bool downgrade_to_v22_fixture(const char *path) {
Lardon3DProjectDb *database = NULL;
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
if (lardon3d_project_db_open(path, &database, error) != LARDON3D_PROJECT_DB_OK)
return false;
Lardon3DProjectDbScanSet a6000;
Lardon3DProjectDbScanSet s21;
Lardon3DProjectDbCapture capture;
Lardon3DSparseCalibration calibration;
bool ok = lardon3d_project_db_create_scanset(database, "A6000 originals",
&a6000) ==
LARDON3D_PROJECT_DB_OK &&
lardon3d_project_db_create_scanset(database, "S21 originals", &s21) ==
LARDON3D_PROJECT_DB_OK &&
lardon3d_project_db_create_capture(database, a6000.scanset_id, 1,
&capture) ==
LARDON3D_PROJECT_DB_OK &&
create_sparse_calibration(database, 91, 6000, 4000, &calibration) &&
seed_campaign(database, 500, a6000.scanset_id, 1) &&
lardon3d_project_db_retain_acquisition_campaign_capture(
database, 500, 1, capture.capture_id, 1) ==
LARDON3D_PROJECT_DB_OK;
lardon3d_project_db_close(database);
if (!ok)
return false;
/* This removes only objects introduced by the test-created v23 database,
yielding a deterministic v22 fixture without rewriting any real project. */
return raw_sql(
path,
"PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;"
"DROP TABLE IF EXISTS feature_extract_batch_tasks;"
"DROP TABLE raw_development_batch_tasks;"
"DROP TABLE capture_calibration_selections;"
"DROP TABLE optical_calibration_profiles;"
"DROP TABLE capture_optical_configurations;"
"DROP INDEX acquisition_campaign_capture_identity_v23;"
"DROP TABLE acquisition_campaign_group_optics;"
"DROP TABLE optical_configurations;"
"DROP TABLE lens_profile_aliases;DROP TABLE lens_profiles;"
"DROP TABLE camera_body_aliases;DROP TABLE camera_body_profiles;"
"UPDATE metadata SET value=22 WHERE key='schema_version';COMMIT;");
}
static bool test_migration_rollback_retry_and_equivalence(void) {
char migrated_directory[64];
char migrated_path[256];
char fresh_directory[64];
char fresh_path[256];
CHECK(make_database_path(migrated_directory, migrated_path));
CHECK(make_database_path(fresh_directory, fresh_path));
CHECK(downgrade_to_v22_fixture(migrated_path));
Lardon3DProjectDb *database = NULL;
char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY];
CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V23", "1", 1) == 0);
CHECK(lardon3d_project_db_open(migrated_path, &database, error) !=
LARDON3D_PROJECT_DB_OK &&
database == NULL);
CHECK(unsetenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V23") == 0);
sqlite3_int64 value = 0;
CHECK(raw_integer(migrated_path,
"SELECT value FROM metadata WHERE key='schema_version'",
&value) &&
value == 22);
CHECK(raw_integer(migrated_path,
"SELECT COUNT(*) FROM sqlite_master WHERE name="
"'camera_body_profiles'",
&value) &&
value == 0);
CHECK(raw_integer(migrated_path, "SELECT COUNT(*) FROM captures", &value) &&
value == 1);
CHECK(raw_integer(migrated_path, "SELECT COUNT(*) FROM sparse_calibrations",
&value) &&
value == 1);
CHECK(raw_integer(migrated_path,
"SELECT COUNT(*) FROM acquisition_campaign_captures",
&value) &&
value == 1);
CHECK(lardon3d_project_db_open(migrated_path, &database, error) ==
LARDON3D_PROJECT_DB_OK &&
lardon3d_project_db_schema_version(database) ==
LARDON3D_PROJECT_DB_SCHEMA_VERSION);
Lardon3DOpticalCaptureAssignment migrated_unresolved;
CHECK(lardon3d_optical_capture_assignment_load(database, 1,
&migrated_unresolved) ==
LARDON3D_PROJECT_DB_NOT_FOUND &&
migrated_unresolved.capture_id == 0);
unsigned char migrated_request[16];
Lardon3DProjectDbAcquisitionCampaignTask migrated_campaign;
CHECK(lardon3d_project_db_load_acquisition_campaign_task(
database, 500, migrated_request, sizeof(migrated_request),
&migrated_campaign) == LARDON3D_PROJECT_DB_OK &&
migrated_campaign.next_group_id == 1);
Lardon3DProjectDbAcquisitionCampaignCapture migrated_mapping;
CHECK(lardon3d_project_db_load_acquisition_campaign_capture(
database, 500, 1, &migrated_mapping) == LARDON3D_PROJECT_DB_OK &&
migrated_mapping.capture_id == 1);
lardon3d_project_db_close(database);
database = NULL;
CHECK(raw_integer(migrated_path, "SELECT COUNT(*) FROM captures", &value) &&
value == 1);
CHECK(raw_integer(migrated_path, "SELECT COUNT(*) FROM sparse_calibrations",
&value) &&
value == 1);
const char *empty_tables[] = {
"camera_body_profiles",
"camera_body_aliases",
"lens_profiles",
"lens_profile_aliases",
"optical_configurations",
"acquisition_campaign_group_optics",
"capture_optical_configurations",
"optical_calibration_profiles",
"capture_calibration_selections",
};
char query[256];
for (size_t index = 0; index < sizeof(empty_tables) / sizeof(empty_tables[0]);
++index) {
CHECK(snprintf(query, sizeof(query), "SELECT COUNT(*) FROM %s",
empty_tables[index]) > 0);
CHECK(raw_integer(migrated_path, query, &value) && value == 0);
}
CHECK(lardon3d_project_db_open(fresh_path, &database, error) ==
LARDON3D_PROJECT_DB_OK);
lardon3d_project_db_close(database);
database = NULL;
const char *objects[] = {
"camera_body_profiles",
"camera_body_aliases",
"camera_body_aliases_profile_idx",
"lens_profiles",
"lens_profile_aliases",
"lens_profile_aliases_profile_idx",
"optical_configurations",
"optical_configurations_body_idx",
"optical_configurations_lens_idx",
"acquisition_campaign_group_optics",
"acquisition_campaign_capture_identity_v23",
"capture_optical_configurations",
"capture_optical_configurations_config_idx",
"optical_calibration_profiles",
"optical_calibration_profiles_config_idx",
"optical_calibration_profiles_sparse_idx",
"capture_calibration_selections",
};
char migrated_sql[8192];
char fresh_sql[8192];
for (size_t index = 0; index < sizeof(objects) / sizeof(objects[0]); ++index) {
CHECK(raw_object_sql(migrated_path, objects[index], migrated_sql,
sizeof(migrated_sql)));
CHECK(raw_object_sql(fresh_path, objects[index], fresh_sql,
sizeof(fresh_sql)));
CHECK(strcmp(migrated_sql, fresh_sql) == 0);
}
CHECK(unlink(migrated_path) == 0);
CHECK(rmdir(migrated_directory) == 0);
CHECK(unlink(fresh_path) == 0);
CHECK(rmdir(fresh_directory) == 0);
return true;
}
int main(void) {
return test_profiles_assignments_and_calibrations() &&
test_calibration_conflict_result_contract() &&
test_calibration_dependency_corruption_precedence() &&
test_campaign_request_corruption_prevents_optics_mutation() &&
test_capture_optics_conflict_and_corruption_rollback() &&
test_malformed_configuration_rolls_back_campaign_retention() &&
test_migration_rollback_retry_and_equivalence()
? EXIT_SUCCESS
: EXIT_FAILURE;
}