feat: add evidence metadata during import

This commit is contained in:
grayTerminal-sh 2026-07-20 09:35:36 +02:00
parent 6c34287feb
commit 18566280a7
12 changed files with 4469 additions and 18 deletions

8
.gitignore vendored
View file

@ -1,7 +1,13 @@
# Build # Build
enquete-gui labfy-investigation
*.o *.o
# Executables de tests
/tests/test_*
# Conserver les sources des tests
!/tests/test_*.c
# SQLite temporaires # SQLite temporaires
*.sqlite-shm *.sqlite-shm
*.sqlite-wal *.sqlite-wal

View file

@ -24,6 +24,18 @@ TEST_CFLAGS = -std=c17 \
TEST_LDFLAGS = $(shell $(PKG_CONFIG) --libs glib-2.0 gio-2.0) TEST_LDFLAGS = $(shell $(PKG_CONFIG) --libs glib-2.0 gio-2.0)
EVIDENCE_RECORD_TEST_CFLAGS := $(TEST_CFLAGS) -Wpedantic EVIDENCE_RECORD_TEST_CFLAGS := $(TEST_CFLAGS) -Wpedantic
EVIDENCE_TYPE_TEST_CFLAGS := $(TEST_CFLAGS) -Wpedantic
EVIDENCE_TYPE_DAO_TEST_CFLAGS := $(TEST_CFLAGS) -Wpedantic
EVIDENCE_IMPORT_DIALOG_TEST_CFLAGS := \
-std=c17 \
-Wall \
-Wextra \
-Werror \
-Iinclude \
$(shell $(PKG_CONFIG) --cflags gtk4)
EVIDENCE_IMPORT_DIALOG_TEST_LDFLAGS := \
$(shell $(PKG_CONFIG) --libs gtk4)
SRC := $(shell find src -name "*.c") SRC := $(shell find src -name "*.c")
@ -55,6 +67,9 @@ TEST_FILE_HASH := tests/test_file_hash
TEST_EVIDENCE_COPY := tests/test_evidence_copy TEST_EVIDENCE_COPY := tests/test_evidence_copy
TEST_EVIDENCE_IMPORTER := tests/test_evidence_importer TEST_EVIDENCE_IMPORTER := tests/test_evidence_importer
TEST_EVIDENCE_IMPORT_TASK := tests/test_evidence_import_task TEST_EVIDENCE_IMPORT_TASK := tests/test_evidence_import_task
TEST_EVIDENCE_TYPE := tests/test_evidence_type
TEST_EVIDENCE_TYPE_DAO := tests/test_evidence_type_dao
TEST_EVIDENCE_IMPORT_DIALOG := tests/test_evidence_import_dialog
all: $(TARGET) all: $(TARGET)
@ -265,6 +280,30 @@ $(TEST_EVIDENCE_IMPORT_TASK): \
-DEVIDENCE_IMPORTER_ENABLE_TEST_HOOKS \ -DEVIDENCE_IMPORTER_ENABLE_TEST_HOOKS \
$^ -o $@ $(TEST_LDFLAGS) -lsqlite3 $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
$(TEST_EVIDENCE_TYPE): \
tests/test_evidence_type.c \
src/models/evidence_type.c
$(CC) $(EVIDENCE_TYPE_TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_EVIDENCE_TYPE_DAO): \
tests/test_evidence_type_dao.c \
src/dao/evidence_type_dao.c \
src/models/evidence_type.c \
src/database/database.c \
src/database/schema.c \
src/database/statement.c \
src/database/transaction.c \
src/database/error.c
$(CC) $(EVIDENCE_TYPE_DAO_TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
$(TEST_EVIDENCE_IMPORT_DIALOG): \
tests/test_evidence_import_dialog.c \
src/views/evidence_import_dialog.c \
src/models/evidence_type.c
$(CC) $(EVIDENCE_IMPORT_DIALOG_TEST_CFLAGS) $^ \
-o $@ \
$(EVIDENCE_IMPORT_DIALOG_TEST_LDFLAGS)
test: \ test: \
$(TEST_NODE) \ $(TEST_NODE) \
$(TEST_TREE_MODEL) \ $(TEST_TREE_MODEL) \
@ -276,8 +315,10 @@ test: \
$(TEST_ERROR) \ $(TEST_ERROR) \
$(TEST_INVESTIGATION_RECORD) \ $(TEST_INVESTIGATION_RECORD) \
$(TEST_EVIDENCE_RECORD) \ $(TEST_EVIDENCE_RECORD) \
$(TEST_EVIDENCE_TYPE) \
$(TEST_INVESTIGATION_DAO) \ $(TEST_INVESTIGATION_DAO) \
$(TEST_EVIDENCE_DAO) \ $(TEST_EVIDENCE_DAO) \
$(TEST_EVIDENCE_TYPE_DAO) \
$(TEST_INVESTIGATION_SESSION) \ $(TEST_INVESTIGATION_SESSION) \
$(TEST_BACKGROUND_TASK) \ $(TEST_BACKGROUND_TASK) \
$(TEST_TASK_MANAGER) \ $(TEST_TASK_MANAGER) \
@ -289,7 +330,8 @@ test: \
$(TEST_FILE_HASH) \ $(TEST_FILE_HASH) \
$(TEST_EVIDENCE_COPY) \ $(TEST_EVIDENCE_COPY) \
$(TEST_EVIDENCE_IMPORTER) \ $(TEST_EVIDENCE_IMPORTER) \
$(TEST_EVIDENCE_IMPORT_TASK) $(TEST_EVIDENCE_IMPORT_TASK) \
$(TEST_EVIDENCE_IMPORT_DIALOG)
@echo "Exécution des tests..." @echo "Exécution des tests..."
@./$(TEST_NODE) @./$(TEST_NODE)
@./$(TEST_TREE_MODEL) @./$(TEST_TREE_MODEL)
@ -301,8 +343,10 @@ test: \
@$(TEST_ERROR) @$(TEST_ERROR)
@$(TEST_INVESTIGATION_RECORD) @$(TEST_INVESTIGATION_RECORD)
@$(TEST_EVIDENCE_RECORD) @$(TEST_EVIDENCE_RECORD)
@$(TEST_EVIDENCE_TYPE)
@$(TEST_INVESTIGATION_DAO) @$(TEST_INVESTIGATION_DAO)
@$(TEST_EVIDENCE_DAO) @$(TEST_EVIDENCE_DAO)
@$(TEST_EVIDENCE_TYPE_DAO)
@$(TEST_INVESTIGATION_SESSION) @$(TEST_INVESTIGATION_SESSION)
@$(TEST_BACKGROUND_TASK) @$(TEST_BACKGROUND_TASK)
@$(TEST_TASK_MANAGER) @$(TEST_TASK_MANAGER)
@ -315,6 +359,7 @@ test: \
@$(TEST_EVIDENCE_COPY) @$(TEST_EVIDENCE_COPY)
@$(TEST_EVIDENCE_IMPORTER) @$(TEST_EVIDENCE_IMPORTER)
@$(TEST_EVIDENCE_IMPORT_TASK) @$(TEST_EVIDENCE_IMPORT_TASK)
@$(TEST_EVIDENCE_IMPORT_DIALOG)
@echo "Tous les tests sont valides." @echo "Tous les tests sont valides."
%.o: %.c %.o: %.c
@ -335,6 +380,8 @@ clean:
$(TEST_ERROR) \ $(TEST_ERROR) \
$(TEST_INVESTIGATION_RECORD) \ $(TEST_INVESTIGATION_RECORD) \
$(TEST_EVIDENCE_RECORD) \ $(TEST_EVIDENCE_RECORD) \
$(TEST_EVIDENCE_TYPE) \
$(TEST_EVIDENCE_TYPE_DAO) \
$(TEST_INVESTIGATION_DAO) \ $(TEST_INVESTIGATION_DAO) \
$(TEST_EVIDENCE_DAO) \ $(TEST_EVIDENCE_DAO) \
$(TEST_INVESTIGATION_SESSION) \ $(TEST_INVESTIGATION_SESSION) \
@ -348,7 +395,8 @@ clean:
$(TEST_FILE_HASH) \ $(TEST_FILE_HASH) \
$(TEST_EVIDENCE_COPY) \ $(TEST_EVIDENCE_COPY) \
$(TEST_EVIDENCE_IMPORTER) \ $(TEST_EVIDENCE_IMPORTER) \
$(TEST_EVIDENCE_IMPORT_TASK) $(TEST_EVIDENCE_IMPORT_TASK) \
$(TEST_EVIDENCE_IMPORT_DIALOG)
.PHONY: clean run test .PHONY: clean run test

View file

@ -0,0 +1,121 @@
/******************************************************************************
* @file evidence_type_dao.h
* @brief Lecture des types de preuve depuis SQLite.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_EVIDENCE_TYPE_DAO_H
#define LABFY_INVESTIGATION_EVIDENCE_TYPE_DAO_H
#include "database/database.h"
#include <glib.h>
G_BEGIN_DECLS
/**
* @brief DAO opaque donnant accès aux types de preuve.
*
* Le DAO emprunte la connexion Database reçue lors de sa création.
*/
typedef struct EvidenceTypeDao EvidenceTypeDao;
/**
* @brief Catégories derreurs produites par EvidenceTypeDao.
*/
typedef enum
{
/**
* Un argument public est invalide.
*/
EVIDENCE_TYPE_DAO_ERROR_INVALID_ARGUMENT,
/**
* Lallocation dune ressource a échoué.
*/
EVIDENCE_TYPE_DAO_ERROR_MEMORY,
/**
* La requête SQLite na pas pu être préparée.
*/
EVIDENCE_TYPE_DAO_ERROR_PREPARE,
/**
* Une ligne SQLite na pas pu être lue.
*/
EVIDENCE_TYPE_DAO_ERROR_READ,
/**
* Une ligne SQLite ne peut pas produire un EvidenceType valide.
*/
EVIDENCE_TYPE_DAO_ERROR_MODEL,
/**
* La table ou le schéma attendu est absent ou invalide.
*/
EVIDENCE_TYPE_DAO_ERROR_SCHEMA
} EvidenceTypeDaoError;
/**
* @brief Domaine derreur du DAO des types de preuve.
*/
#define EVIDENCE_TYPE_DAO_ERROR \
evidence_type_dao_error_quark()
/**
* @brief Retourne le domaine derreur du DAO.
*
* @return Quark GLib associé à EvidenceTypeDao.
*/
GQuark evidence_type_dao_error_quark(void);
/**
* @brief Crée un DAO utilisant une connexion Database existante.
*
* La connexion est empruntée. Elle doit rester valide pendant toute
* la durée de vie du DAO.
*
* @param database Connexion SQLite ouverte.
* @param error Emplacement facultatif recevant une erreur.
*
* @return Nouveau DAO, ou NULL en cas déchec.
*/
EvidenceTypeDao *evidence_type_dao_new(
Database *database,
GError **error
);
/**
* @brief Libère le DAO.
*
* La connexion Database empruntée nest pas fermée.
* Cette fonction accepte NULL.
*
* @param evidence_type_dao DAO à libérer.
*/
void evidence_type_dao_free(
EvidenceTypeDao *evidence_type_dao
);
/**
* @brief Charge tous les types de preuve.
*
* Les types sont triés par identifiant numérique croissant.
*
* Le tableau retourné appartient à lappelant et utilise
* evidence_type_free() comme fonction de destruction.
*
* Une table vide produit un tableau vide, sans erreur.
*
* @param evidence_type_dao DAO valide.
* @param error Emplacement facultatif recevant une erreur.
*
* @return Nouveau GPtrArray, ou NULL en cas déchec.
*/
GPtrArray *evidence_type_dao_list_all(
EvidenceTypeDao *evidence_type_dao,
GError **error
);
G_END_DECLS
#endif

View file

@ -0,0 +1,132 @@
/******************************************************************************
* @file evidence_type.h
* @brief Modèle représentant un type de preuve.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_EVIDENCE_TYPE_H
#define LABFY_INVESTIGATION_EVIDENCE_TYPE_H
#include <glib.h>
G_BEGIN_DECLS
/**
* @brief Modèle opaque représentant un type de preuve.
*/
typedef struct EvidenceType EvidenceType;
/**
* @brief Codes derreur produits par EvidenceType.
*/
typedef enum
{
/**
* Un argument général est invalide.
*/
EVIDENCE_TYPE_ERROR_INVALID_ARGUMENT,
/**
* Lidentifiant numérique est invalide.
*/
EVIDENCE_TYPE_ERROR_INVALID_IDENTIFIER,
/**
* Le code métier est invalide.
*/
EVIDENCE_TYPE_ERROR_INVALID_CODE,
/**
* Le libellé utilisateur est invalide.
*/
EVIDENCE_TYPE_ERROR_INVALID_LABEL
} EvidenceTypeError;
/**
* @brief Domaine derreur du modèle EvidenceType.
*/
#define EVIDENCE_TYPE_ERROR \
evidence_type_error_quark()
/**
* @brief Retourne le domaine derreur du modèle.
*
* @return Quark GLib associé à EvidenceType.
*/
GQuark evidence_type_error_quark(void);
/**
* @brief Crée un type de preuve.
*
* Les chaînes sont copiées et appartiennent au nouvel objet.
*
* Lidentifiant doit être strictement positif.
*
* Le code et le libellé sont obligatoires.
* La description peut être NULL ou vide.
*
* @param identifier Identifiant numérique SQLite.
* @param code Code métier du type.
* @param label Libellé destiné à lutilisateur.
* @param description Description facultative.
* @param error Emplacement facultatif recevant une erreur.
*
* @return Nouveau modèle, ou NULL lorsque les données sont invalides.
*/
EvidenceType *evidence_type_new(
gint64 identifier,
const char *code,
const char *label,
const char *description,
GError **error
);
/**
* @brief Libère un modèle EvidenceType.
*
* Cette fonction accepte evidence_type == NULL.
*
* @param evidence_type Modèle à libérer.
*/
void evidence_type_free(
EvidenceType *evidence_type
);
/**
* @brief Retourne lidentifiant numérique du type.
*
* @return Identifiant, ou 0 lorsque evidence_type est NULL.
*/
gint64 evidence_type_get_identifier(
const EvidenceType *evidence_type
);
/**
* @brief Retourne le code métier du type.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *evidence_type_get_code(
const EvidenceType *evidence_type
);
/**
* @brief Retourne le libellé utilisateur du type.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *evidence_type_get_label(
const EvidenceType *evidence_type
);
/**
* @brief Retourne la description du type.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *evidence_type_get_description(
const EvidenceType *evidence_type
);
G_END_DECLS
#endif

View file

@ -0,0 +1,176 @@
/******************************************************************************
* @file evidence_import_dialog.h
* @brief Dialogue GTK de saisie des métadonnées d'une preuve.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_EVIDENCE_IMPORT_DIALOG_H
#define LABFY_INVESTIGATION_EVIDENCE_IMPORT_DIALOG_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
/**
* @brief Résultat opaque produit par le dialogue d'import.
*/
typedef struct EvidenceImportDialogResult EvidenceImportDialogResult;
/**
* @brief Codes d'erreur produits par EvidenceImportDialog.
*/
typedef enum
{
/**
* Un argument public est invalide.
*/
EVIDENCE_IMPORT_DIALOG_ERROR_INVALID_ARGUMENT,
/**
* Aucun type de preuve n'est disponible.
*/
EVIDENCE_IMPORT_DIALOG_ERROR_EMPTY_TYPES,
/**
* Une allocation ou la création du dialogue a échoué.
*/
EVIDENCE_IMPORT_DIALOG_ERROR_MEMORY,
/**
* Les métadonnées saisies sont invalides.
*/
EVIDENCE_IMPORT_DIALOG_ERROR_INVALID_DATA
} EvidenceImportDialogError;
/**
* @brief Domaine d'erreur du dialogue d'import.
*/
#define EVIDENCE_IMPORT_DIALOG_ERROR \
evidence_import_dialog_error_quark()
/**
* @brief Callback appelé après la fermeture du dialogue.
*
* En cas de validation, result est non NULL et appartient au callback.
* Il doit être libéré avec evidence_import_dialog_result_free().
*
* En cas d'annulation, result vaut NULL.
*
* @param result Résultat possédé par le callback, ou NULL.
* @param user_data Données privées fournies lors de l'ouverture.
*/
typedef void (*EvidenceImportDialogCallback)(
EvidenceImportDialogResult *result,
gpointer user_data
);
/**
* @brief Retourne le domaine d'erreur du dialogue.
*
* @return Quark GLib associé à EvidenceImportDialog.
*/
GQuark evidence_import_dialog_error_quark(void);
/**
* @brief Présente le dialogue de saisie des métadonnées.
*
* Le chemin du fichier est affiché en lecture seule.
*
* evidence_types doit contenir des pointeurs EvidenceType valides.
* Le dialogue copie les codes et les libellés dont il a besoin :
* le tableau peut donc être libéré après le retour de cette fonction.
*
* Le type dont le code vaut "document" est sélectionné par défaut
* lorsqu'il est disponible. Sinon, le premier type est sélectionné.
*
* @param parent Fenêtre GTK parente.
* @param file_path Chemin du fichier sélectionné.
* @param evidence_types Tableau de EvidenceType.
* @param callback Fonction appelée après validation ou annulation.
* @param user_data Données privées transmises au callback.
* @param error Emplacement facultatif recevant une erreur immédiate.
*
* @return TRUE si le dialogue a é créé et présenté.
*/
gboolean evidence_import_dialog_present(
GtkWindow *parent,
const char *file_path,
const GPtrArray *evidence_types,
EvidenceImportDialogCallback callback,
gpointer user_data,
GError **error
);
/**
* @brief Valide les valeurs obligatoires d'un import.
*
* Cette fonction ne dépend pas de l'affichage du dialogue et peut être
* testée sans ouvrir de fenêtre GTK.
*
* Le chemin doit désigner un fichier existant.
* Le code du type doit être non vide.
* La date doit être une date ISO 8601 valide comprenant un fuseau horaire.
*
* @param file_path Chemin du fichier à importer.
* @param type_identifier Code métier du type de preuve.
* @param collected_at Date de collecte au format ISO 8601.
* @param error Emplacement facultatif recevant une erreur.
*
* @return TRUE lorsque les valeurs sont valides.
*/
gboolean evidence_import_dialog_validate_values(
const char *file_path,
const char *type_identifier,
const char *collected_at,
GError **error
);
/**
* @brief Libère le résultat produit par le dialogue.
*
* Cette fonction accepte NULL.
*
* @param result Résultat à libérer.
*/
void evidence_import_dialog_result_free(
EvidenceImportDialogResult *result
);
/**
* @brief Retourne le code du type sélectionné.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *evidence_import_dialog_result_get_type_identifier(
const EvidenceImportDialogResult *result
);
/**
* @brief Retourne la date ISO 8601 de collecte.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *evidence_import_dialog_result_get_collected_at(
const EvidenceImportDialogResult *result
);
/**
* @brief Retourne la source renseignée.
*
* @return Chaîne empruntée, ou NULL lorsque la source est vide.
*/
const char *evidence_import_dialog_result_get_source(
const EvidenceImportDialogResult *result
);
/**
* @brief Retourne la description renseignée.
*
* @return Chaîne empruntée, ou NULL lorsque la description est vide.
*/
const char *evidence_import_dialog_result_get_description(
const EvidenceImportDialogResult *result
);
G_END_DECLS
#endif

View file

@ -21,6 +21,8 @@
#include "views/file_dialog.h" #include "views/file_dialog.h"
#include "core/evidence_import_task.h" #include "core/evidence_import_task.h"
#include "models/evidence_record.h" #include "models/evidence_record.h"
#include "dao/evidence_type_dao.h"
#include "views/evidence_import_dialog.h"
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <errno.h> #include <errno.h>
@ -36,12 +38,6 @@
#define APPLICATION_EVIDENCE_DIRECTORY \ #define APPLICATION_EVIDENCE_DIRECTORY \
"01_Preuves_Originales/Documents" "01_Preuves_Originales/Documents"
/**
* @brief Type utilisé avant lajout du formulaire de métadonnées.
*/
#define APPLICATION_DEFAULT_EVIDENCE_TYPE \
"document"
/** /**
* @brief Erreurs produites lors du chargement d'une enquête. * @brief Erreurs produites lors du chargement d'une enquête.
*/ */
@ -96,6 +92,43 @@ typedef struct
char *investigation_root_path; char *investigation_root_path;
} ApplicationEvidenceImportContext; } ApplicationEvidenceImportContext;
/**
* @brief Contexte conservé pendant le dialogue de métadonnées.
*
* Le chemin fourni par FileDialog n'est valide que pendant son callback.
* Il doit donc être copié avant l'ouverture du second dialogue.
*/
typedef struct
{
Application *application;
char *file_path;
} ApplicationEvidenceDialogContext;
/**
* @brief Libère le contexte du dialogue de métadonnées.
*/
static void application_evidence_dialog_context_free(
gpointer user_data
)
{
ApplicationEvidenceDialogContext *context =
user_data;
if (context == NULL)
{
return;
}
g_free(
context->file_path
);
g_free(
context
);
}
/** /**
* @brief Libère le contexte dun import asynchrone. * @brief Libère le contexte dun import asynchrone.
*/ */
@ -1115,7 +1148,11 @@ static void application_on_evidence_import_completed(
*/ */
static void application_start_evidence_import( static void application_start_evidence_import(
Application *application, Application *application,
const char *file_path const char *file_path,
const char *type_identifier,
const char *collected_at,
const char *source,
const char *description
) )
{ {
const InvestigationProject *project = NULL; const InvestigationProject *project = NULL;
@ -1142,7 +1179,11 @@ static void application_start_evidence_import(
application->session == NULL || application->session == NULL ||
application->task_manager == NULL || application->task_manager == NULL ||
file_path == NULL || file_path == NULL ||
file_path[0] == '\0') file_path[0] == '\0' ||
type_identifier == NULL ||
type_identifier[0] == '\0' ||
collected_at == NULL ||
collected_at[0] == '\0')
{ {
return; return;
} }
@ -1301,11 +1342,16 @@ static void application_start_evidence_import(
APPLICATION_EVIDENCE_DIRECTORY; APPLICATION_EVIDENCE_DIRECTORY;
request.type_identifier = request.type_identifier =
APPLICATION_DEFAULT_EVIDENCE_TYPE; type_identifier;
request.collected_at = NULL; request.collected_at =
request.source = NULL; collected_at;
request.description = NULL;
request.source =
source;
request.description =
description;
task = task =
evidence_import_task_start( evidence_import_task_start(
@ -1388,6 +1434,91 @@ static void application_start_evidence_import(
); );
} }
/**
* @brief Traite la validation ou l'annulation des métadonnées.
*
* @param result Résultat possédé par ce callback, ou NULL.
* @param user_data Contexte ApplicationEvidenceDialogContext.
*/
static void application_on_evidence_metadata_completed(
EvidenceImportDialogResult *result,
gpointer user_data
)
{
ApplicationEvidenceDialogContext *context =
user_data;
Application *application = NULL;
if (context == NULL)
{
evidence_import_dialog_result_free(
result
);
return;
}
application =
context->application;
if (application == NULL ||
application->main_window == NULL)
{
evidence_import_dialog_result_free(
result
);
application_evidence_dialog_context_free(
context
);
return;
}
/*
* Une annulation du dialogue ne doit lancer aucune tâche.
*/
if (result == NULL)
{
main_window_set_status(
application->main_window,
"Import de preuve annulé."
);
application_evidence_dialog_context_free(
context
);
return;
}
application_start_evidence_import(
application,
context->file_path,
evidence_import_dialog_result_get_type_identifier(
result
),
evidence_import_dialog_result_get_collected_at(
result
),
evidence_import_dialog_result_get_source(
result
),
evidence_import_dialog_result_get_description(
result
)
);
evidence_import_dialog_result_free(
result
);
application_evidence_dialog_context_free(
context
);
}
/** /**
* @brief Traite le fichier de preuve sélectionné. * @brief Traite le fichier de preuve sélectionné.
* *
@ -1452,9 +1583,174 @@ static void application_on_evidence_file_selected(
return; return;
} }
application_start_evidence_import( Database *database = NULL;
application, EvidenceTypeDao *evidence_type_dao = NULL;
file_path GPtrArray *evidence_types = NULL;
ApplicationEvidenceDialogContext *dialog_context =
NULL;
GError *dialog_error = NULL;
database =
investigation_session_get_database(
application->session
);
if (database == NULL)
{
application_present_error(
application,
"Import impossible",
"La session ne fournit aucune connexion SQLite."
);
return;
}
evidence_type_dao =
evidence_type_dao_new(
database,
&dialog_error
);
if (evidence_type_dao == NULL)
{
application_present_error(
application,
"Types de preuve indisponibles",
dialog_error != NULL
? dialog_error->message
: "Impossible de créer le DAO des types de preuve."
);
g_clear_error(
&dialog_error
);
return;
}
evidence_types =
evidence_type_dao_list_all(
evidence_type_dao,
&dialog_error
);
evidence_type_dao_free(
evidence_type_dao
);
if (evidence_types == NULL)
{
application_present_error(
application,
"Types de preuve indisponibles",
dialog_error != NULL
? dialog_error->message
: "Impossible de lire les types de preuve."
);
g_clear_error(
&dialog_error
);
return;
}
if (evidence_types->len == 0)
{
application_present_error(
application,
"Types de preuve indisponibles",
"Aucun type de preuve n'est enregistré dans la base."
);
g_ptr_array_unref(
evidence_types
);
return;
}
dialog_context =
g_try_new0(
ApplicationEvidenceDialogContext,
1
);
if (dialog_context == NULL)
{
application_present_error(
application,
"Import impossible",
"Impossible d'allouer le contexte du dialogue."
);
g_ptr_array_unref(
evidence_types
);
return;
}
dialog_context->application =
application;
dialog_context->file_path =
g_strdup(
file_path
);
if (dialog_context->file_path == NULL)
{
application_present_error(
application,
"Import impossible",
"Impossible de conserver le chemin du fichier."
);
application_evidence_dialog_context_free(
dialog_context
);
g_ptr_array_unref(
evidence_types
);
return;
}
if (!evidence_import_dialog_present(
main_window_get_window(
application->main_window
),
dialog_context->file_path,
evidence_types,
application_on_evidence_metadata_completed,
dialog_context,
&dialog_error
))
{
application_present_error(
application,
"Dialogue d'import impossible",
dialog_error != NULL
? dialog_error->message
: "Le dialogue de métadonnées n'a pas pu être ouvert."
);
g_clear_error(
&dialog_error
);
application_evidence_dialog_context_free(
dialog_context
);
}
g_ptr_array_unref(
evidence_types
); );
} }

498
src/dao/evidence_type_dao.c Normal file
View file

@ -0,0 +1,498 @@
/******************************************************************************
* @file evidence_type_dao.c
* @brief Lecture des types de preuve depuis SQLite.
******************************************************************************/
#include "dao/evidence_type_dao.h"
#include "database/error.h"
#include "database/statement.h"
#include "models/evidence_type.h"
#include <stdint.h>
#include <glib.h>
/**
* @brief Requête listant les types de preuve.
*/
static const char *const evidence_type_dao_list_all_sql =
"SELECT "
" id, "
" code, "
" label, "
" description "
"FROM types_preuve "
"ORDER BY id ASC;";
/**
* @struct EvidenceTypeDao
* @brief Représentation interne du DAO.
*/
struct EvidenceTypeDao
{
Database *database;
};
/**
* @brief Libère un EvidenceType contenu dans un GPtrArray.
*/
static void evidence_type_dao_destroy_type(
gpointer user_data
)
{
EvidenceType *evidence_type = user_data;
evidence_type_free(
evidence_type
);
}
/**
* @brief Détermine si un message SQLite indique un schéma incompatible.
*/
static gboolean evidence_type_dao_is_schema_error(
const char *database_message
)
{
if (database_message == NULL)
{
return FALSE;
}
return g_strstr_len(
database_message,
-1,
"no such table"
) != NULL ||
g_strstr_len(
database_message,
-1,
"no such column"
) != NULL;
}
/**
* @brief Enregistre une erreur de préparation SQL.
*/
static void evidence_type_dao_set_prepare_error(
EvidenceTypeDao *evidence_type_dao,
GError **error
)
{
const char *database_message = NULL;
EvidenceTypeDaoError error_code =
EVIDENCE_TYPE_DAO_ERROR_PREPARE;
if (error == NULL ||
*error != NULL)
{
return;
}
if (evidence_type_dao != NULL)
{
database_message =
database_error_get_message(
evidence_type_dao->database
);
}
if (evidence_type_dao_is_schema_error(
database_message
))
{
error_code =
EVIDENCE_TYPE_DAO_ERROR_SCHEMA;
}
g_set_error(
error,
EVIDENCE_TYPE_DAO_ERROR,
error_code,
"Impossible de préparer la lecture des types de preuve : %s",
database_message != NULL
? database_message
: "erreur Database inconnue"
);
}
/**
* @brief Enregistre une erreur de lecture SQLite.
*/
static void evidence_type_dao_set_read_error(
EvidenceTypeDao *evidence_type_dao,
GError **error,
const char *context
)
{
const char *database_message = NULL;
if (error == NULL ||
*error != NULL)
{
return;
}
if (evidence_type_dao != NULL)
{
database_message =
database_error_get_message(
evidence_type_dao->database
);
}
g_set_error(
error,
EVIDENCE_TYPE_DAO_ERROR,
EVIDENCE_TYPE_DAO_ERROR_READ,
"%s : %s",
context != NULL
? context
: "Impossible de lire un type de preuve",
database_message != NULL
? database_message
: "donnée SQLite invalide"
);
}
/**
* @brief Construit un EvidenceType depuis la ligne SQLite courante.
*/
static EvidenceType *evidence_type_dao_read_current_row(
EvidenceTypeDao *evidence_type_dao,
DatabaseStatement *statement,
GError **error
)
{
EvidenceType *evidence_type = NULL;
int64_t identifier = 0;
char *code = NULL;
char *label = NULL;
char *description = NULL;
GError *model_error = NULL;
if (evidence_type_dao == NULL ||
statement == NULL)
{
g_set_error_literal(
error,
EVIDENCE_TYPE_DAO_ERROR,
EVIDENCE_TYPE_DAO_ERROR_INVALID_ARGUMENT,
"La ligne du type de preuve ne peut pas être lue."
);
return NULL;
}
if (!database_statement_column_int64(
statement,
0,
&identifier
))
{
evidence_type_dao_set_read_error(
evidence_type_dao,
error,
"Impossible de lire lidentifiant du type de preuve"
);
goto cleanup;
}
if (!database_statement_column_text(
statement,
1,
&code
))
{
evidence_type_dao_set_read_error(
evidence_type_dao,
error,
"Impossible de lire le code du type de preuve"
);
goto cleanup;
}
if (!database_statement_column_text(
statement,
2,
&label
))
{
evidence_type_dao_set_read_error(
evidence_type_dao,
error,
"Impossible de lire le libellé du type de preuve"
);
goto cleanup;
}
if (!database_statement_column_text(
statement,
3,
&description
))
{
evidence_type_dao_set_read_error(
evidence_type_dao,
error,
"Impossible de lire la description du type de preuve"
);
goto cleanup;
}
evidence_type =
evidence_type_new(
(gint64) identifier,
code,
label,
description,
&model_error
);
if (evidence_type == NULL)
{
g_set_error(
error,
EVIDENCE_TYPE_DAO_ERROR,
EVIDENCE_TYPE_DAO_ERROR_MODEL,
"Le type de preuve lu depuis SQLite est invalide : %s",
model_error != NULL
? model_error->message
: "erreur de modèle inconnue"
);
}
cleanup:
g_clear_error(
&model_error
);
g_free(
description
);
g_free(
label
);
g_free(
code
);
return evidence_type;
}
GQuark evidence_type_dao_error_quark(void)
{
return g_quark_from_static_string(
"evidence-type-dao-error-quark"
);
}
EvidenceTypeDao *evidence_type_dao_new(
Database *database,
GError **error
)
{
EvidenceTypeDao *evidence_type_dao = NULL;
g_return_val_if_fail(
error == NULL || *error == NULL,
NULL
);
if (database == NULL)
{
g_set_error_literal(
error,
EVIDENCE_TYPE_DAO_ERROR,
EVIDENCE_TYPE_DAO_ERROR_INVALID_ARGUMENT,
"La connexion Database est obligatoire."
);
return NULL;
}
evidence_type_dao =
g_try_new0(
EvidenceTypeDao,
1
);
if (evidence_type_dao == NULL)
{
g_set_error_literal(
error,
EVIDENCE_TYPE_DAO_ERROR,
EVIDENCE_TYPE_DAO_ERROR_MEMORY,
"Impossible dallouer le DAO des types de preuve."
);
return NULL;
}
/*
* La connexion reste possédée par la session ou le code appelant.
*/
evidence_type_dao->database =
database;
return evidence_type_dao;
}
void evidence_type_dao_free(
EvidenceTypeDao *evidence_type_dao
)
{
if (evidence_type_dao == NULL)
{
return;
}
/*
* Database est empruntée et ne doit pas être fermée ici.
*/
evidence_type_dao->database = NULL;
g_free(
evidence_type_dao
);
}
GPtrArray *evidence_type_dao_list_all(
EvidenceTypeDao *evidence_type_dao,
GError **error
)
{
DatabaseStatement *statement = NULL;
GPtrArray *evidence_types = NULL;
DatabaseStatementStepResult step_result =
DATABASE_STATEMENT_STEP_ERROR;
gboolean success = FALSE;
g_return_val_if_fail(
error == NULL || *error == NULL,
NULL
);
if (evidence_type_dao == NULL ||
evidence_type_dao->database == NULL)
{
g_set_error_literal(
error,
EVIDENCE_TYPE_DAO_ERROR,
EVIDENCE_TYPE_DAO_ERROR_INVALID_ARGUMENT,
"Le DAO des types de preuve est invalide."
);
return NULL;
}
evidence_types =
g_ptr_array_new_with_free_func(
evidence_type_dao_destroy_type
);
if (evidence_types == NULL)
{
g_set_error_literal(
error,
EVIDENCE_TYPE_DAO_ERROR,
EVIDENCE_TYPE_DAO_ERROR_MEMORY,
"Impossible dallouer la liste des types de preuve."
);
return NULL;
}
statement =
database_statement_prepare(
evidence_type_dao->database,
evidence_type_dao_list_all_sql
);
if (statement == NULL)
{
evidence_type_dao_set_prepare_error(
evidence_type_dao,
error
);
goto cleanup;
}
while (TRUE)
{
EvidenceType *evidence_type = NULL;
step_result =
database_statement_step(
statement
);
if (step_result ==
DATABASE_STATEMENT_STEP_DONE)
{
success = TRUE;
break;
}
if (step_result ==
DATABASE_STATEMENT_STEP_ERROR)
{
evidence_type_dao_set_read_error(
evidence_type_dao,
error,
"Impossible de parcourir les types de preuve"
);
break;
}
evidence_type =
evidence_type_dao_read_current_row(
evidence_type_dao,
statement,
error
);
if (evidence_type == NULL)
{
break;
}
g_ptr_array_add(
evidence_types,
evidence_type
);
}
cleanup:
database_statement_finalize(
statement
);
if (!success)
{
g_ptr_array_unref(
evidence_types
);
return NULL;
}
return evidence_types;
}

266
src/models/evidence_type.c Normal file
View file

@ -0,0 +1,266 @@
/******************************************************************************
* @file evidence_type.c
* @brief Implémentation du modèle représentant un type de preuve.
******************************************************************************/
#include "models/evidence_type.h"
#include <glib.h>
/**
* @struct EvidenceType
* @brief Représentation interne dun type de preuve.
*/
struct EvidenceType
{
gint64 identifier;
char *code;
char *label;
char *description;
};
/**
* @brief Copie une chaîne après suppression des espaces périphériques.
*
* @param value Chaîne à copier.
*
* @return Nouvelle chaîne normalisée, ou NULL lorsque value vaut NULL.
*/
static char *evidence_type_duplicate_trimmed(
const char *value
)
{
char *copy = NULL;
if (value == NULL)
{
return NULL;
}
copy = g_strdup(
value
);
g_strstrip(
copy
);
return copy;
}
/**
* @brief Vérifie quune chaîne obligatoire contient du texte.
*
* @param value Chaîne déjà normalisée.
*
* @return TRUE lorsque la chaîne contient au moins un caractère.
*/
static gboolean evidence_type_required_value_is_valid(
const char *value
)
{
return value != NULL &&
value[0] != '\0';
}
GQuark evidence_type_error_quark(void)
{
return g_quark_from_static_string(
"evidence-type-error-quark"
);
}
EvidenceType *evidence_type_new(
gint64 identifier,
const char *code,
const char *label,
const char *description,
GError **error
)
{
EvidenceType *evidence_type = NULL;
char *normalized_code = NULL;
char *normalized_label = NULL;
char *normalized_description = NULL;
g_return_val_if_fail(
error == NULL || *error == NULL,
NULL
);
if (identifier <= 0)
{
g_set_error_literal(
error,
EVIDENCE_TYPE_ERROR,
EVIDENCE_TYPE_ERROR_INVALID_IDENTIFIER,
"Lidentifiant du type de preuve doit être strictement positif."
);
return NULL;
}
normalized_code =
evidence_type_duplicate_trimmed(
code
);
if (!evidence_type_required_value_is_valid(
normalized_code
))
{
g_set_error_literal(
error,
EVIDENCE_TYPE_ERROR,
EVIDENCE_TYPE_ERROR_INVALID_CODE,
"Le code du type de preuve est obligatoire."
);
g_free(
normalized_code
);
return NULL;
}
normalized_label =
evidence_type_duplicate_trimmed(
label
);
if (!evidence_type_required_value_is_valid(
normalized_label
))
{
g_set_error_literal(
error,
EVIDENCE_TYPE_ERROR,
EVIDENCE_TYPE_ERROR_INVALID_LABEL,
"Le libellé du type de preuve est obligatoire."
);
g_free(
normalized_code
);
g_free(
normalized_label
);
return NULL;
}
normalized_description =
evidence_type_duplicate_trimmed(
description
);
/*
* Une description vide ou composée uniquement despaces
* est représentée par NULL.
*/
if (normalized_description != NULL &&
normalized_description[0] == '\0')
{
g_free(
normalized_description
);
normalized_description = NULL;
}
evidence_type =
g_new0(
EvidenceType,
1
);
evidence_type->identifier =
identifier;
evidence_type->code =
normalized_code;
evidence_type->label =
normalized_label;
evidence_type->description =
normalized_description;
return evidence_type;
}
void evidence_type_free(
EvidenceType *evidence_type
)
{
if (evidence_type == NULL)
{
return;
}
g_free(
evidence_type->code
);
g_free(
evidence_type->label
);
g_free(
evidence_type->description
);
g_free(
evidence_type
);
}
gint64 evidence_type_get_identifier(
const EvidenceType *evidence_type
)
{
if (evidence_type == NULL)
{
return 0;
}
return evidence_type->identifier;
}
const char *evidence_type_get_code(
const EvidenceType *evidence_type
)
{
if (evidence_type == NULL)
{
return NULL;
}
return evidence_type->code;
}
const char *evidence_type_get_label(
const EvidenceType *evidence_type
)
{
if (evidence_type == NULL)
{
return NULL;
}
return evidence_type->label;
}
const char *evidence_type_get_description(
const EvidenceType *evidence_type
)
{
if (evidence_type == NULL)
{
return NULL;
}
return evidence_type->description;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,369 @@
/******************************************************************************
* @file test_evidence_import_dialog.c
* @brief Tests de validation du dialogue d'import d'une preuve.
******************************************************************************/
#include "views/evidence_import_dialog.h"
#include <glib.h>
#include <glib/gstdio.h>
/**
* @brief Fichier temporaire utilisé par les tests.
*/
typedef struct
{
char *directory_path;
char *file_path;
} TestEvidenceImportDialogFixture;
/**
* @brief Crée un fichier ordinaire temporaire.
*/
static TestEvidenceImportDialogFixture
test_evidence_import_dialog_fixture_create(void)
{
TestEvidenceImportDialogFixture fixture = {0};
GError *error = NULL;
fixture.directory_path =
g_dir_make_tmp(
"labfy-evidence-import-dialog-test-XXXXXX",
&error
);
g_assert_no_error(error);
g_assert_nonnull(fixture.directory_path);
fixture.file_path =
g_build_filename(
fixture.directory_path,
"preuve.txt",
NULL
);
g_assert_nonnull(fixture.file_path);
g_file_set_contents(
fixture.file_path,
"preuve de test\n",
-1,
&error
);
g_assert_no_error(error);
return fixture;
}
/**
* @brief Supprime les ressources temporaires.
*/
static void test_evidence_import_dialog_fixture_clear(
TestEvidenceImportDialogFixture *fixture
)
{
g_assert_nonnull(fixture);
g_assert_cmpint(
g_remove(fixture->file_path),
==,
0
);
g_assert_cmpint(
g_rmdir(fixture->directory_path),
==,
0
);
g_free(fixture->file_path);
g_free(fixture->directory_path);
fixture->file_path = NULL;
fixture->directory_path = NULL;
}
/**
* @brief Vérifie une date UTC valide.
*/
static void test_evidence_import_dialog_validate_utc(void)
{
TestEvidenceImportDialogFixture fixture =
test_evidence_import_dialog_fixture_create();
GError *error = NULL;
g_assert_true(
evidence_import_dialog_validate_values(
fixture.file_path,
"document",
"2026-07-20T08:30:00Z",
&error
)
);
g_assert_no_error(error);
test_evidence_import_dialog_fixture_clear(
&fixture
);
}
/**
* @brief Vérifie une date avec décalage horaire valide.
*/
static void test_evidence_import_dialog_validate_offset(void)
{
TestEvidenceImportDialogFixture fixture =
test_evidence_import_dialog_fixture_create();
GError *error = NULL;
g_assert_true(
evidence_import_dialog_validate_values(
fixture.file_path,
"photo",
"2026-07-20T10:30:00+02:00",
&error
)
);
g_assert_no_error(error);
test_evidence_import_dialog_fixture_clear(
&fixture
);
}
/**
* @brief Vérifie le refus d'un chemin absent.
*/
static void test_evidence_import_dialog_missing_path(void)
{
GError *error = NULL;
g_assert_false(
evidence_import_dialog_validate_values(
NULL,
"document",
"2026-07-20T08:30:00Z",
&error
)
);
g_assert_error(
error,
EVIDENCE_IMPORT_DIALOG_ERROR,
EVIDENCE_IMPORT_DIALOG_ERROR_INVALID_DATA
);
g_clear_error(&error);
}
/**
* @brief Vérifie le refus d'un fichier inexistant.
*/
static void test_evidence_import_dialog_missing_file(void)
{
GError *error = NULL;
g_assert_false(
evidence_import_dialog_validate_values(
"/tmp/labfy-file-that-does-not-exist",
"document",
"2026-07-20T08:30:00Z",
&error
)
);
g_assert_error(
error,
EVIDENCE_IMPORT_DIALOG_ERROR,
EVIDENCE_IMPORT_DIALOG_ERROR_INVALID_DATA
);
g_clear_error(&error);
}
/**
* @brief Vérifie le refus d'un type vide.
*/
static void test_evidence_import_dialog_missing_type(void)
{
TestEvidenceImportDialogFixture fixture =
test_evidence_import_dialog_fixture_create();
GError *error = NULL;
g_assert_false(
evidence_import_dialog_validate_values(
fixture.file_path,
" ",
"2026-07-20T08:30:00Z",
&error
)
);
g_assert_error(
error,
EVIDENCE_IMPORT_DIALOG_ERROR,
EVIDENCE_IMPORT_DIALOG_ERROR_INVALID_DATA
);
g_clear_error(&error);
test_evidence_import_dialog_fixture_clear(
&fixture
);
}
/**
* @brief Vérifie le refus d'une date absente.
*/
static void test_evidence_import_dialog_missing_date(void)
{
TestEvidenceImportDialogFixture fixture =
test_evidence_import_dialog_fixture_create();
GError *error = NULL;
g_assert_false(
evidence_import_dialog_validate_values(
fixture.file_path,
"document",
NULL,
&error
)
);
g_assert_error(
error,
EVIDENCE_IMPORT_DIALOG_ERROR,
EVIDENCE_IMPORT_DIALOG_ERROR_INVALID_DATA
);
g_clear_error(&error);
test_evidence_import_dialog_fixture_clear(
&fixture
);
}
/**
* @brief Vérifie le refus d'une date sans fuseau horaire.
*/
static void test_evidence_import_dialog_missing_timezone(void)
{
TestEvidenceImportDialogFixture fixture =
test_evidence_import_dialog_fixture_create();
GError *error = NULL;
g_assert_false(
evidence_import_dialog_validate_values(
fixture.file_path,
"document",
"2026-07-20T08:30:00",
&error
)
);
g_assert_error(
error,
EVIDENCE_IMPORT_DIALOG_ERROR,
EVIDENCE_IMPORT_DIALOG_ERROR_INVALID_DATA
);
g_clear_error(&error);
test_evidence_import_dialog_fixture_clear(
&fixture
);
}
/**
* @brief Vérifie le refus d'une date civile impossible.
*/
static void test_evidence_import_dialog_invalid_date(void)
{
TestEvidenceImportDialogFixture fixture =
test_evidence_import_dialog_fixture_create();
GError *error = NULL;
g_assert_false(
evidence_import_dialog_validate_values(
fixture.file_path,
"document",
"2026-02-30T08:30:00+01:00",
&error
)
);
g_assert_error(
error,
EVIDENCE_IMPORT_DIALOG_ERROR,
EVIDENCE_IMPORT_DIALOG_ERROR_INVALID_DATA
);
g_clear_error(&error);
test_evidence_import_dialog_fixture_clear(
&fixture
);
}
int main(
int argc,
char **argv
)
{
g_test_init(
&argc,
&argv,
NULL
);
g_test_add_func(
"/evidence-import-dialog/validate-utc",
test_evidence_import_dialog_validate_utc
);
g_test_add_func(
"/evidence-import-dialog/validate-offset",
test_evidence_import_dialog_validate_offset
);
g_test_add_func(
"/evidence-import-dialog/missing-path",
test_evidence_import_dialog_missing_path
);
g_test_add_func(
"/evidence-import-dialog/missing-file",
test_evidence_import_dialog_missing_file
);
g_test_add_func(
"/evidence-import-dialog/missing-type",
test_evidence_import_dialog_missing_type
);
g_test_add_func(
"/evidence-import-dialog/missing-date",
test_evidence_import_dialog_missing_date
);
g_test_add_func(
"/evidence-import-dialog/missing-timezone",
test_evidence_import_dialog_missing_timezone
);
g_test_add_func(
"/evidence-import-dialog/invalid-date",
test_evidence_import_dialog_invalid_date
);
return g_test_run();
}

339
tests/test_evidence_type.c Normal file
View file

@ -0,0 +1,339 @@
/******************************************************************************
* @file test_evidence_type.c
* @brief Tests unitaires du modèle EvidenceType.
******************************************************************************/
#include "models/evidence_type.h"
#include <glib.h>
/**
* @brief Vérifie la création dun type valide.
*/
static void test_evidence_type_new_valid(void)
{
EvidenceType *evidence_type = NULL;
GError *error = NULL;
evidence_type = evidence_type_new(
4,
"document",
"Document",
"Document numérique ou numérisé.",
&error
);
g_assert_no_error(error);
g_assert_nonnull(evidence_type);
g_assert_cmpint(
evidence_type_get_identifier(evidence_type),
==,
4
);
g_assert_cmpstr(
evidence_type_get_code(evidence_type),
==,
"document"
);
g_assert_cmpstr(
evidence_type_get_label(evidence_type),
==,
"Document"
);
g_assert_cmpstr(
evidence_type_get_description(evidence_type),
==,
"Document numérique ou numérisé."
);
evidence_type_free(evidence_type);
}
/**
* @brief Vérifie la suppression des espaces périphériques.
*/
static void test_evidence_type_values_are_trimmed(void)
{
EvidenceType *evidence_type = NULL;
GError *error = NULL;
evidence_type = evidence_type_new(
1,
" screenshot ",
" Capture décran ",
" Image capturée depuis un écran. ",
&error
);
g_assert_no_error(error);
g_assert_nonnull(evidence_type);
g_assert_cmpstr(
evidence_type_get_code(evidence_type),
==,
"screenshot"
);
g_assert_cmpstr(
evidence_type_get_label(evidence_type),
==,
"Capture décran"
);
g_assert_cmpstr(
evidence_type_get_description(evidence_type),
==,
"Image capturée depuis un écran."
);
evidence_type_free(evidence_type);
}
/**
* @brief Vérifie quune description absente reste facultative.
*/
static void test_evidence_type_description_is_optional(void)
{
EvidenceType *evidence_type = NULL;
GError *error = NULL;
evidence_type = evidence_type_new(
9,
"other",
"Autre",
NULL,
&error
);
g_assert_no_error(error);
g_assert_nonnull(evidence_type);
g_assert_null(
evidence_type_get_description(evidence_type)
);
evidence_type_free(evidence_type);
}
/**
* @brief Vérifie quune description vide est normalisée vers NULL.
*/
static void test_evidence_type_empty_description_becomes_null(void)
{
EvidenceType *evidence_type = NULL;
GError *error = NULL;
evidence_type = evidence_type_new(
8,
"text",
"Texte",
" ",
&error
);
g_assert_no_error(error);
g_assert_nonnull(evidence_type);
g_assert_null(
evidence_type_get_description(evidence_type)
);
evidence_type_free(evidence_type);
}
/**
* @brief Vérifie le refus dun identifiant invalide.
*/
static void test_evidence_type_invalid_identifier(void)
{
EvidenceType *evidence_type = NULL;
GError *error = NULL;
evidence_type = evidence_type_new(
0,
"document",
"Document",
NULL,
&error
);
g_assert_null(evidence_type);
g_assert_error(
error,
EVIDENCE_TYPE_ERROR,
EVIDENCE_TYPE_ERROR_INVALID_IDENTIFIER
);
g_clear_error(&error);
}
/**
* @brief Vérifie le refus dun code absent.
*/
static void test_evidence_type_missing_code(void)
{
EvidenceType *evidence_type = NULL;
GError *error = NULL;
evidence_type = evidence_type_new(
4,
NULL,
"Document",
NULL,
&error
);
g_assert_null(evidence_type);
g_assert_error(
error,
EVIDENCE_TYPE_ERROR,
EVIDENCE_TYPE_ERROR_INVALID_CODE
);
g_clear_error(&error);
}
/**
* @brief Vérifie le refus dun code vide.
*/
static void test_evidence_type_empty_code(void)
{
EvidenceType *evidence_type = NULL;
GError *error = NULL;
evidence_type = evidence_type_new(
4,
" ",
"Document",
NULL,
&error
);
g_assert_null(evidence_type);
g_assert_error(
error,
EVIDENCE_TYPE_ERROR,
EVIDENCE_TYPE_ERROR_INVALID_CODE
);
g_clear_error(&error);
}
/**
* @brief Vérifie le refus dun libellé absent.
*/
static void test_evidence_type_missing_label(void)
{
EvidenceType *evidence_type = NULL;
GError *error = NULL;
evidence_type = evidence_type_new(
4,
"document",
NULL,
NULL,
&error
);
g_assert_null(evidence_type);
g_assert_error(
error,
EVIDENCE_TYPE_ERROR,
EVIDENCE_TYPE_ERROR_INVALID_LABEL
);
g_clear_error(&error);
}
/**
* @brief Vérifie le comportement des getters avec NULL.
*/
static void test_evidence_type_null_getters(void)
{
g_assert_cmpint(
evidence_type_get_identifier(NULL),
==,
0
);
g_assert_null(
evidence_type_get_code(NULL)
);
g_assert_null(
evidence_type_get_label(NULL)
);
g_assert_null(
evidence_type_get_description(NULL)
);
evidence_type_free(NULL);
}
int main(
int argc,
char **argv
)
{
g_test_init(
&argc,
&argv,
NULL
);
g_test_add_func(
"/evidence-type/new-valid",
test_evidence_type_new_valid
);
g_test_add_func(
"/evidence-type/values-are-trimmed",
test_evidence_type_values_are_trimmed
);
g_test_add_func(
"/evidence-type/description-is-optional",
test_evidence_type_description_is_optional
);
g_test_add_func(
"/evidence-type/empty-description-becomes-null",
test_evidence_type_empty_description_becomes_null
);
g_test_add_func(
"/evidence-type/invalid-identifier",
test_evidence_type_invalid_identifier
);
g_test_add_func(
"/evidence-type/missing-code",
test_evidence_type_missing_code
);
g_test_add_func(
"/evidence-type/empty-code",
test_evidence_type_empty_code
);
g_test_add_func(
"/evidence-type/missing-label",
test_evidence_type_missing_label
);
g_test_add_func(
"/evidence-type/null-getters",
test_evidence_type_null_getters
);
return g_test_run();
}

View file

@ -0,0 +1,495 @@
/******************************************************************************
* @file test_evidence_type_dao.c
* @brief Tests du DAO des types de preuve.
******************************************************************************/
#include "dao/evidence_type_dao.h"
#include "database/database.h"
#include "database/statement.h"
#include "models/evidence_type.h"
#include <glib.h>
#include <glib/gstdio.h>
/**
* @brief Ressources utilisées par un test EvidenceTypeDao.
*/
typedef struct
{
char *temporary_directory;
char *database_path;
Database *database;
EvidenceTypeDao *evidence_type_dao;
} TestEvidenceTypeDaoFixture;
/**
* @brief Exécute une requête SQL ne retournant aucune ligne.
*/
static void test_evidence_type_dao_execute_sql(
Database *database,
const char *sql
)
{
DatabaseStatement *statement = NULL;
g_assert_nonnull(database);
g_assert_nonnull(sql);
statement =
database_statement_prepare(
database,
sql
);
g_assert_nonnull(statement);
g_assert_cmpint(
database_statement_step(statement),
==,
DATABASE_STATEMENT_STEP_DONE
);
database_statement_finalize(
statement
);
}
/**
* @brief Crée une base temporaire contenant le schéma de lapplication.
*/
static TestEvidenceTypeDaoFixture
test_evidence_type_dao_fixture_create(void)
{
TestEvidenceTypeDaoFixture fixture = {0};
GError *error = NULL;
fixture.temporary_directory =
g_dir_make_tmp(
"labfy-evidence-type-dao-test-XXXXXX",
&error
);
g_assert_no_error(error);
g_assert_nonnull(
fixture.temporary_directory
);
fixture.database_path =
g_build_filename(
fixture.temporary_directory,
"Enquete.sqlite",
NULL
);
g_assert_nonnull(
fixture.database_path
);
g_assert_true(
database_initialize(
fixture.database_path,
"Enquete_EvidenceTypeDao",
fixture.temporary_directory
)
);
fixture.database =
database_open(
fixture.database_path
);
g_assert_nonnull(
fixture.database
);
fixture.evidence_type_dao =
evidence_type_dao_new(
fixture.database,
&error
);
g_assert_no_error(error);
g_assert_nonnull(
fixture.evidence_type_dao
);
return fixture;
}
/**
* @brief Libère la fixture et supprime la base temporaire.
*/
static void test_evidence_type_dao_fixture_clear(
TestEvidenceTypeDaoFixture *fixture
)
{
g_assert_nonnull(fixture);
evidence_type_dao_free(
fixture->evidence_type_dao
);
database_close(
fixture->database
);
g_assert_cmpint(
g_remove(fixture->database_path),
==,
0
);
g_assert_cmpint(
g_rmdir(fixture->temporary_directory),
==,
0
);
g_free(
fixture->database_path
);
g_free(
fixture->temporary_directory
);
fixture->evidence_type_dao = NULL;
fixture->database = NULL;
fixture->database_path = NULL;
fixture->temporary_directory = NULL;
}
/**
* @brief Vérifie le refus dune connexion absente.
*/
static void test_evidence_type_dao_new_invalid_database(void)
{
EvidenceTypeDao *evidence_type_dao = NULL;
GError *error = NULL;
evidence_type_dao =
evidence_type_dao_new(
NULL,
&error
);
g_assert_null(
evidence_type_dao
);
g_assert_error(
error,
EVIDENCE_TYPE_DAO_ERROR,
EVIDENCE_TYPE_DAO_ERROR_INVALID_ARGUMENT
);
g_clear_error(
&error
);
}
/**
* @brief Vérifie le chargement du catalogue initial.
*/
static void test_evidence_type_dao_list_all(void)
{
static const char *const expected_codes[] =
{
"screenshot",
"photo",
"video",
"document",
"email",
"archive",
"audio",
"text",
"other"
};
static const char *const expected_labels[] =
{
"Capture d'écran",
"Photographie",
"Vidéo",
"Document",
"Courrier électronique",
"Archive",
"Audio",
"Texte",
"Autre"
};
TestEvidenceTypeDaoFixture fixture =
test_evidence_type_dao_fixture_create();
GPtrArray *evidence_types = NULL;
GError *error = NULL;
guint index = 0;
evidence_types =
evidence_type_dao_list_all(
fixture.evidence_type_dao,
&error
);
g_assert_no_error(error);
g_assert_nonnull(evidence_types);
g_assert_cmpuint(
evidence_types->len,
==,
G_N_ELEMENTS(expected_codes)
);
for (index = 0;
index < evidence_types->len;
index++)
{
const EvidenceType *evidence_type =
g_ptr_array_index(
evidence_types,
index
);
g_assert_nonnull(
evidence_type
);
/*
* La requête utilise ORDER BY id ASC.
*/
g_assert_cmpint(
evidence_type_get_identifier(
evidence_type
),
==,
(gint64) index + 1
);
g_assert_cmpstr(
evidence_type_get_code(
evidence_type
),
==,
expected_codes[index]
);
g_assert_cmpstr(
evidence_type_get_label(
evidence_type
),
==,
expected_labels[index]
);
}
g_ptr_array_unref(
evidence_types
);
test_evidence_type_dao_fixture_clear(
&fixture
);
}
/**
* @brief Vérifie quune table vide retourne un tableau vide.
*/
static void test_evidence_type_dao_list_empty(void)
{
TestEvidenceTypeDaoFixture fixture =
test_evidence_type_dao_fixture_create();
GPtrArray *evidence_types = NULL;
GError *error = NULL;
test_evidence_type_dao_execute_sql(
fixture.database,
"DELETE FROM types_preuve;"
);
evidence_types =
evidence_type_dao_list_all(
fixture.evidence_type_dao,
&error
);
g_assert_no_error(error);
g_assert_nonnull(evidence_types);
g_assert_cmpuint(
evidence_types->len,
==,
0
);
g_ptr_array_unref(
evidence_types
);
test_evidence_type_dao_fixture_clear(
&fixture
);
}
/**
* @brief Vérifie le signalement dune table absente.
*/
static void test_evidence_type_dao_missing_table(void)
{
Database *database = NULL;
EvidenceTypeDao *evidence_type_dao = NULL;
GPtrArray *evidence_types = NULL;
GError *error = NULL;
database =
database_open(
":memory:"
);
g_assert_nonnull(database);
evidence_type_dao =
evidence_type_dao_new(
database,
&error
);
g_assert_no_error(error);
g_assert_nonnull(evidence_type_dao);
/*
* DatabaseStatement journalise léchec de préparation SQLite.
*/
g_test_expect_message(
NULL,
G_LOG_LEVEL_WARNING,
"*Impossible de préparer la requête SQL*"
);
evidence_types =
evidence_type_dao_list_all(
evidence_type_dao,
&error
);
g_test_assert_expected_messages();
g_assert_null(evidence_types);
g_assert_error(
error,
EVIDENCE_TYPE_DAO_ERROR,
EVIDENCE_TYPE_DAO_ERROR_SCHEMA
);
g_clear_error(
&error
);
evidence_type_dao_free(
evidence_type_dao
);
database_close(
database
);
}
/**
* @brief Vérifie le refus dune ligne produisant un modèle invalide.
*/
static void test_evidence_type_dao_invalid_model(void)
{
TestEvidenceTypeDaoFixture fixture =
test_evidence_type_dao_fixture_create();
GPtrArray *evidence_types = NULL;
GError *error = NULL;
/*
* La contrainte SQLite interdit NULL mais pas une chaîne
* uniquement composée despaces.
*
* EvidenceType doit normaliser puis refuser cette valeur.
*/
test_evidence_type_dao_execute_sql(
fixture.database,
"UPDATE types_preuve "
"SET code = ' ' "
"WHERE id = 1;"
);
evidence_types =
evidence_type_dao_list_all(
fixture.evidence_type_dao,
&error
);
g_assert_null(evidence_types);
g_assert_error(
error,
EVIDENCE_TYPE_DAO_ERROR,
EVIDENCE_TYPE_DAO_ERROR_MODEL
);
g_clear_error(
&error
);
test_evidence_type_dao_fixture_clear(
&fixture
);
}
int main(
int argc,
char **argv
)
{
g_test_init(
&argc,
&argv,
NULL
);
g_test_add_func(
"/evidence-type-dao/new-invalid-database",
test_evidence_type_dao_new_invalid_database
);
g_test_add_func(
"/evidence-type-dao/list-all",
test_evidence_type_dao_list_all
);
g_test_add_func(
"/evidence-type-dao/list-empty",
test_evidence_type_dao_list_empty
);
g_test_add_func(
"/evidence-type-dao/missing-table",
test_evidence_type_dao_missing_table
);
g_test_add_func(
"/evidence-type-dao/invalid-model",
test_evidence_type_dao_invalid_model
);
return g_test_run();
}