feat: display categorized evidence in sidebar

This commit is contained in:
grayTerminal-sh 2026-07-20 11:57:56 +02:00
parent 18566280a7
commit 5d87b35242
20 changed files with 6208 additions and 24 deletions

View file

@ -70,6 +70,10 @@ 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
TEST_EVIDENCE_LIST_ITEM := tests/test_evidence_list_item
TEST_EVIDENCE_LIST_MODEL := tests/test_evidence_list_model
TEST_EVIDENCE_CATEGORY_ITEM := tests/test_evidence_category_item
TEST_EVIDENCE_CATEGORY_MODEL := tests/test_evidence_category_model
all: $(TARGET)
@ -149,6 +153,19 @@ $(TEST_EVIDENCE_RECORD): \
src/models/evidence_record.c
$(CC) $(EVIDENCE_RECORD_TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_EVIDENCE_LIST_ITEM): \
tests/test_evidence_list_item.c \
src/widgets/evidence_list_item.c \
src/models/evidence_record.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_EVIDENCE_LIST_MODEL): \
tests/test_evidence_list_model.c \
src/widgets/evidence_list_model.c \
src/widgets/evidence_list_item.c \
src/models/evidence_record.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_INVESTIGATION_DAO): \
tests/test_investigation_dao.c \
src/dao/investigation_dao.c \
@ -304,6 +321,21 @@ $(TEST_EVIDENCE_IMPORT_DIALOG): \
-o $@ \
$(EVIDENCE_IMPORT_DIALOG_TEST_LDFLAGS)
$(TEST_EVIDENCE_CATEGORY_ITEM): \
tests/test_evidence_category_item.c \
src/widgets/evidence_category_item.c \
src/widgets/evidence_list_item.c \
src/models/evidence_record.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_EVIDENCE_CATEGORY_MODEL): \
tests/test_evidence_category_model.c \
src/widgets/evidence_category_model.c \
src/widgets/evidence_category_item.c \
src/widgets/evidence_list_item.c \
src/models/evidence_record.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
test: \
$(TEST_NODE) \
$(TEST_TREE_MODEL) \
@ -315,6 +347,10 @@ test: \
$(TEST_ERROR) \
$(TEST_INVESTIGATION_RECORD) \
$(TEST_EVIDENCE_RECORD) \
$(TEST_EVIDENCE_LIST_ITEM) \
$(TEST_EVIDENCE_LIST_MODEL) \
$(TEST_EVIDENCE_CATEGORY_ITEM) \
$(TEST_EVIDENCE_CATEGORY_MODEL) \
$(TEST_EVIDENCE_TYPE) \
$(TEST_INVESTIGATION_DAO) \
$(TEST_EVIDENCE_DAO) \
@ -342,6 +378,10 @@ test: \
@$(TEST_TRANSACTION)
@$(TEST_ERROR)
@$(TEST_INVESTIGATION_RECORD)
@$(TEST_EVIDENCE_LIST_ITEM)
@$(TEST_EVIDENCE_LIST_MODEL)
@$(TEST_EVIDENCE_CATEGORY_ITEM)
@$(TEST_EVIDENCE_CATEGORY_MODEL)
@$(TEST_EVIDENCE_RECORD)
@$(TEST_EVIDENCE_TYPE)
@$(TEST_INVESTIGATION_DAO)
@ -380,6 +420,10 @@ clean:
$(TEST_ERROR) \
$(TEST_INVESTIGATION_RECORD) \
$(TEST_EVIDENCE_RECORD) \
$(TEST_EVIDENCE_LIST_ITEM) \
$(TEST_EVIDENCE_LIST_MODEL) \
$(TEST_EVIDENCE_CATEGORY_ITEM) \
$(TEST_EVIDENCE_CATEGORY_MODEL) \
$(TEST_EVIDENCE_TYPE) \
$(TEST_EVIDENCE_TYPE_DAO) \
$(TEST_INVESTIGATION_DAO) \

View file

@ -10,6 +10,7 @@
#include "widgets/investigation_tree_view.h"
#include "core/investigation_node.h"
#include "core/task_manager.h"
#include "widgets/evidence_category_model.h"
#include <gtk/gtk.h>
@ -37,6 +38,20 @@ typedef void (*MainWindowImportEvidenceCallback)(
gpointer user_data
);
/**
* @brief Callback appelé lorsqu'une preuve est sélectionnée.
*
* L'identifiant est emprunté et uniquement valide pendant l'appel.
* Une sélection vide ou une catégorie transmet NULL.
*
* @param evidence_identifier UUID de la preuve, ou NULL.
* @param user_data Données privées fournies par l'appelant.
*/
typedef void (*MainWindowEvidenceSelectionCallback)(
const char *evidence_identifier,
gpointer user_data
);
/**
* @brief Définit le callback du bouton « Nouvelle enquête ».
*
@ -100,6 +115,22 @@ void main_window_set_tree_model(
const InvestigationTreeModel *tree_model
);
/**
* @brief Transmet le modèle des catégories de preuves à la Sidebar.
*
* MainWindow ne devient pas propriétaire du modèle.
* Le modèle doit rester valide pendant son affichage.
*
* Passer NULL vide l'onglet Preuves.
*
* @param main_window Fenêtre principale.
* @param evidence_category_model Modèle de catégories, ou NULL.
*/
void main_window_set_evidence_model(
MainWindow *main_window,
EvidenceCategoryModel *evidence_category_model
);
/**
* @brief Met à jour l'identité de l'enquête affichée.
*
@ -128,6 +159,21 @@ void main_window_set_tree_selection_callback(
gpointer user_data
);
/**
* @brief Définit le callback de sélection d'une preuve.
*
* MainWindow transmet uniquement le callback à la Sidebar.
*
* @param main_window Fenêtre principale.
* @param callback Callback facultatif.
* @param user_data Données privées transmises au callback.
*/
void main_window_set_evidence_selection_callback(
MainWindow *main_window,
MainWindowEvidenceSelectionCallback callback,
gpointer user_data
);
/**
* @brief Affiche dans le Workspace les informations du nœud sélectionné.
*

View file

@ -0,0 +1,100 @@
/******************************************************************************
* @file evidence_category_item.h
* @brief Catégorie GTK regroupant plusieurs preuves affichées.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_EVIDENCE_CATEGORY_ITEM_H
#define LABFY_INVESTIGATION_EVIDENCE_CATEGORY_ITEM_H
#include "widgets/evidence_list_item.h"
#include <gio/gio.h>
#include <glib-object.h>
G_BEGIN_DECLS
/**
* @brief Type GObject de EvidenceCategoryItem.
*/
#define EVIDENCE_TYPE_CATEGORY_ITEM \
(evidence_category_item_get_type())
G_DECLARE_FINAL_TYPE(
EvidenceCategoryItem,
evidence_category_item,
EVIDENCE,
CATEGORY_ITEM,
GObject
)
/**
* @brief Crée une catégorie de preuves.
*
* Le code et le libellé sont copiés et nettoyés.
*
* @param type_identifier Code métier stable de la catégorie.
* @param label Libellé destiné à l'utilisateur.
*
* @return Nouvelle catégorie, ou NULL.
*/
EvidenceCategoryItem *evidence_category_item_new(
const char *type_identifier,
const char *label
);
/**
* @brief Ajoute une preuve à la catégorie.
*
* La catégorie conserve sa propre référence sur evidence_list_item.
*
* @param evidence_category_item Catégorie cible.
* @param evidence_list_item Preuve d'affichage à ajouter.
*
* @return TRUE en cas de succès.
*/
gboolean evidence_category_item_append(
EvidenceCategoryItem *evidence_category_item,
EvidenceListItem *evidence_list_item
);
/**
* @brief Retourne le code métier de la catégorie.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *evidence_category_item_get_type_identifier(
const EvidenceCategoryItem *evidence_category_item
);
/**
* @brief Retourne le libellé utilisateur.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *evidence_category_item_get_label(
const EvidenceCategoryItem *evidence_category_item
);
/**
* @brief Retourne le nombre de preuves de la catégorie.
*
* @return Nombre de preuves, ou 0.
*/
guint evidence_category_item_get_count(
const EvidenceCategoryItem *evidence_category_item
);
/**
* @brief Retourne le modèle contenant les preuves.
*
* La référence retournée est empruntée.
*
* @return GListModel emprunté, ou NULL.
*/
GListModel *evidence_category_item_get_children(
EvidenceCategoryItem *evidence_category_item
);
G_END_DECLS
#endif

View file

@ -0,0 +1,128 @@
/******************************************************************************
* @file evidence_category_model.h
* @brief Modèle GTK regroupant les preuves par catégorie.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_EVIDENCE_CATEGORY_MODEL_H
#define LABFY_INVESTIGATION_EVIDENCE_CATEGORY_MODEL_H
#include <gio/gio.h>
#include <glib.h>
G_BEGIN_DECLS
/**
* @brief Modèle opaque contenant des EvidenceCategoryItem.
*/
typedef struct EvidenceCategoryModel EvidenceCategoryModel;
/**
* @brief Codes d'erreur produits par EvidenceCategoryModel.
*/
typedef enum
{
/**
* Un argument public est invalide.
*/
EVIDENCE_CATEGORY_MODEL_ERROR_INVALID_ARGUMENT,
/**
* Le modèle contient un élément invalide.
*/
EVIDENCE_CATEGORY_MODEL_ERROR_INVALID_ITEM,
/**
* Une catégorie n'a pas pu être créée.
*/
EVIDENCE_CATEGORY_MODEL_ERROR_CATEGORY,
/**
* Une allocation a échoué.
*/
EVIDENCE_CATEGORY_MODEL_ERROR_MEMORY
} EvidenceCategoryModelError;
/**
* @brief Domaine d'erreur du modèle de catégories.
*/
#define EVIDENCE_CATEGORY_MODEL_ERROR \
evidence_category_model_error_quark()
/**
* @brief Retourne le domaine d'erreur du module.
*
* @return Quark associé à EvidenceCategoryModel.
*/
GQuark evidence_category_model_error_quark(void);
/**
* @brief Crée un modèle de catégories vide.
*
* @return Nouveau modèle, ou NULL.
*/
EvidenceCategoryModel *evidence_category_model_new(void);
/**
* @brief Libère le modèle.
*
* Cette fonction accepte NULL.
*
* @param evidence_category_model Modèle à libérer.
*/
void evidence_category_model_free(
EvidenceCategoryModel *evidence_category_model
);
/**
* @brief Retourne le modèle GTK contenant les catégories.
*
* La référence retournée est empruntée.
*
* @return GListModel emprunté, ou NULL.
*/
GListModel *evidence_category_model_get_list_model(
EvidenceCategoryModel *evidence_category_model
);
/**
* @brief Retourne le nombre de catégories.
*
* @return Nombre de catégories, ou 0.
*/
guint evidence_category_model_get_count(
const EvidenceCategoryModel *evidence_category_model
);
/**
* @brief Regroupe les preuves par type métier.
*
* evidence_items doit contenir des EvidenceListItem.
*
* type_labels est une table facultative associant :
*
* - clé : code métier du type ;
* - valeur : libellé utilisateur.
*
* La table est uniquement empruntée pendant l'appel.
* Lorsqu'un libellé est absent, le code métier est utilisé.
*
* Le nouveau contenu est entièrement préparé avant que l'ancien modèle
* soit remplacé.
*
* @param evidence_category_model Modèle à actualiser.
* @param evidence_items Modèle contenant des EvidenceListItem.
* @param type_labels Correspondance code vers libellé, ou NULL.
* @param error Emplacement facultatif recevant une erreur.
*
* @return TRUE en cas de succès.
*/
gboolean evidence_category_model_replace(
EvidenceCategoryModel *evidence_category_model,
GListModel *evidence_items,
GHashTable *type_labels,
GError **error
);
G_END_DECLS
#endif

View file

@ -0,0 +1,124 @@
/******************************************************************************
* @file evidence_list_item.h
* @brief Objet d'affichage GTK représentant une preuve importée.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_EVIDENCE_LIST_ITEM_H
#define LABFY_INVESTIGATION_EVIDENCE_LIST_ITEM_H
#include "models/evidence_record.h"
#include <glib-object.h>
G_BEGIN_DECLS
/**
* @brief Type GObject de EvidenceListItem.
*/
#define EVIDENCE_TYPE_LIST_ITEM \
(evidence_list_item_get_type())
G_DECLARE_FINAL_TYPE(
EvidenceListItem,
evidence_list_item,
EVIDENCE,
LIST_ITEM,
GObject
)
/**
* @brief Crée un élément d'affichage depuis une preuve métier.
*
* Les valeurs nécessaires à l'interface sont copiées.
* L'objet ne conserve aucun pointeur emprunté vers EvidenceRecord.
*
* L'appelant possède la référence retournée et doit la libérer
* avec g_object_unref() ou g_clear_object().
*
* @param evidence_record Preuve métier valide.
*
* @return Nouvel élément d'affichage, ou NULL.
*/
EvidenceListItem *evidence_list_item_new(
const EvidenceRecord *evidence_record
);
/**
* @brief Retourne l'identifiant UUID de la preuve.
*
* Cet identifiant est utilisé pour signaler une sélection à Application.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *evidence_list_item_get_identifier(
const EvidenceListItem *evidence_list_item
);
/**
* @brief Retourne le nom original de la preuve.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *evidence_list_item_get_original_name(
const EvidenceListItem *evidence_list_item
);
/**
* @brief Retourne l'identifiant métier du type de preuve.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *evidence_list_item_get_type_identifier(
const EvidenceListItem *evidence_list_item
);
/**
* @brief Retourne la taille de la preuve.
*
* @return Taille en octets, ou 0.
*/
guint64 evidence_list_item_get_size_bytes(
const EvidenceListItem *evidence_list_item
);
/**
* @brief Retourne la date UTC de collecte.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *evidence_list_item_get_collected_at(
const EvidenceListItem *evidence_list_item
);
/**
* @brief Retourne la source déclarée.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *evidence_list_item_get_source(
const EvidenceListItem *evidence_list_item
);
/**
* @brief Retourne l'état d'intégrité.
*
* @return État d'intégrité enregistré.
*/
EvidenceIntegrityStatus evidence_list_item_get_integrity_status(
const EvidenceListItem *evidence_list_item
);
/**
* @brief Retourne un libellé utilisateur pour l'état d'intégrité.
*
* La chaîne retournée est statique et ne doit pas être libérée.
*
* @return Libellé d'état.
*/
const char *evidence_list_item_get_integrity_status_label(
const EvidenceListItem *evidence_list_item
);
G_END_DECLS
#endif

View file

@ -0,0 +1,119 @@
/******************************************************************************
* @file evidence_list_model.h
* @brief Modèle GTK en mémoire contenant la liste des preuves affichées.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_EVIDENCE_LIST_MODEL_H
#define LABFY_INVESTIGATION_EVIDENCE_LIST_MODEL_H
#include <gio/gio.h>
#include <glib.h>
G_BEGIN_DECLS
/**
* @brief Modèle opaque contenant des EvidenceListItem.
*/
typedef struct EvidenceListModel EvidenceListModel;
/**
* @brief Codes d'erreur produits par EvidenceListModel.
*/
typedef enum
{
/**
* Un argument public est invalide.
*/
EVIDENCE_LIST_MODEL_ERROR_INVALID_ARGUMENT,
/**
* Une allocation a échoué.
*/
EVIDENCE_LIST_MODEL_ERROR_MEMORY,
/**
* Un EvidenceRecord ne peut pas produire un élément d'affichage.
*/
EVIDENCE_LIST_MODEL_ERROR_INVALID_RECORD
} EvidenceListModelError;
/**
* @brief Domaine d'erreur du modèle de liste.
*/
#define EVIDENCE_LIST_MODEL_ERROR \
evidence_list_model_error_quark()
/**
* @brief Retourne le domaine d'erreur du modèle.
*
* @return Quark GLib associé à EvidenceListModel.
*/
GQuark evidence_list_model_error_quark(void);
/**
* @brief Crée un modèle de liste vide.
*
* @return Nouveau modèle, ou NULL.
*/
EvidenceListModel *evidence_list_model_new(void);
/**
* @brief Libère le modèle et tous ses éléments.
*
* Cette fonction accepte NULL.
*
* @param evidence_list_model Modèle à libérer.
*/
void evidence_list_model_free(
EvidenceListModel *evidence_list_model
);
/**
* @brief Retourne le GListModel utilisé par les widgets GTK.
*
* La référence retournée est empruntée.
*
* @param evidence_list_model Modèle valide.
*
* @return GListModel emprunté, ou NULL.
*/
GListModel *evidence_list_model_get_list_model(
EvidenceListModel *evidence_list_model
);
/**
* @brief Retourne le nombre d'éléments affichés.
*
* @param evidence_list_model Modèle de liste.
*
* @return Nombre d'éléments, ou 0.
*/
guint evidence_list_model_get_count(
const EvidenceListModel *evidence_list_model
);
/**
* @brief Remplace complètement le contenu du modèle.
*
* evidence_records doit contenir des pointeurs EvidenceRecord valides.
*
* Tous les nouveaux EvidenceListItem sont construits avant la modification
* du modèle existant. En cas d'erreur, l'ancien contenu reste inchangé.
*
* Le tableau et les EvidenceRecord restent la propriété de l'appelant.
*
* @param evidence_list_model Modèle à mettre à jour.
* @param evidence_records Tableau de EvidenceRecord.
* @param error Emplacement facultatif recevant une erreur.
*
* @return TRUE en cas de succès.
*/
gboolean evidence_list_model_replace(
EvidenceListModel *evidence_list_model,
const GPtrArray *evidence_records,
GError **error
);
G_END_DECLS
#endif

View file

@ -0,0 +1,99 @@
/******************************************************************************
* @file evidence_tree_view.h
* @brief Affichage GTK hiérarchique des catégories et preuves importées.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_EVIDENCE_TREE_VIEW_H
#define LABFY_INVESTIGATION_EVIDENCE_TREE_VIEW_H
#include "widgets/evidence_category_model.h"
#include <gtk/gtk.h>
G_BEGIN_DECLS
/**
* @brief Vue opaque affichant les preuves regroupées par catégorie.
*/
typedef struct EvidenceTreeView EvidenceTreeView;
/**
* @brief Callback appelé lorsqu'une preuve est sélectionnée.
*
* L'identifiant est emprunté et uniquement valide pendant l'appel.
*
* Lorsque la sélection est vide ou correspond à une catégorie,
* evidence_identifier vaut NULL.
*
* @param evidence_identifier UUID de la preuve sélectionnée, ou NULL.
* @param user_data Données privées fournies par l'appelant.
*/
typedef void (*EvidenceTreeViewSelectionCallback)(
const char *evidence_identifier,
gpointer user_data
);
/**
* @brief Crée une vue hiérarchique vide.
*
* @return Nouvelle vue, ou NULL.
*/
EvidenceTreeView *evidence_tree_view_new(void);
/**
* @brief Retourne le widget GTK racine.
*
* Le widget retourné est emprunté.
*
* @param evidence_tree_view Vue à consulter.
*
* @return GtkScrolledWindow racine, ou NULL.
*/
GtkWidget *evidence_tree_view_get_widget(
const EvidenceTreeView *evidence_tree_view
);
/**
* @brief Installe un modèle de catégories.
*
* La vue ne devient pas propriétaire de EvidenceCategoryModel.
* Le modèle doit rester valide pendant son utilisation.
*
* Passer NULL vide la vue.
*
* @param evidence_tree_view Vue à actualiser.
* @param evidence_category_model Modèle de catégories, ou NULL.
*/
void evidence_tree_view_set_model(
EvidenceTreeView *evidence_tree_view,
EvidenceCategoryModel *evidence_category_model
);
/**
* @brief Définit le callback de sélection.
*
* @param evidence_tree_view Vue à configurer.
* @param callback Callback facultatif.
* @param user_data Données privées transmises au callback.
*/
void evidence_tree_view_set_selection_callback(
EvidenceTreeView *evidence_tree_view,
EvidenceTreeViewSelectionCallback callback,
gpointer user_data
);
/**
* @brief Libère la structure d'encapsulation.
*
* Les widgets GTK intégrés dans la fenêtre restent gérés par l'arbre GTK.
* Cette fonction accepte NULL.
*
* @param evidence_tree_view Vue à libérer.
*/
void evidence_tree_view_free(
EvidenceTreeView *evidence_tree_view
);
G_END_DECLS
#endif

View file

@ -8,6 +8,7 @@
#include "core/investigation_tree_model.h"
#include "widgets/investigation_tree_view.h"
#include "widgets/evidence_category_model.h"
#include <gtk/gtk.h>
@ -19,6 +20,20 @@
*/
typedef struct Sidebar Sidebar;
/**
* @brief Callback appelé lorsqu'une preuve est sélectionnée.
*
* L'identifiant est emprunté et uniquement valide pendant l'appel.
* Une sélection vide ou une catégorie transmet NULL.
*
* @param evidence_identifier UUID de la preuve, ou NULL.
* @param user_data Données privées fournies par l'appelant.
*/
typedef void (*SidebarEvidenceSelectionCallback)(
const char *evidence_identifier,
gpointer user_data
);
/**
* @brief Crée un nouveau panneau de navigation latéral.
*
@ -71,6 +86,33 @@ void sidebar_set_selection_callback(
gpointer user_data
);
/**
* @brief Installe le modèle des catégories de preuves.
*
* La Sidebar ne devient pas propriétaire du modèle.
* Passer NULL rétablit l'état vide.
*
* @param sidebar Barre latérale à actualiser.
* @param evidence_category_model Modèle de catégories, ou NULL.
*/
void sidebar_set_evidence_model(
Sidebar *sidebar,
EvidenceCategoryModel *evidence_category_model
);
/**
* @brief Définit le callback de sélection d'une preuve.
*
* @param sidebar Barre latérale à configurer.
* @param callback Callback facultatif.
* @param user_data Données privées transmises au callback.
*/
void sidebar_set_evidence_selection_callback(
Sidebar *sidebar,
SidebarEvidenceSelectionCallback callback,
gpointer user_data
);
/**
* @brief Libère la structure d'encapsulation du panneau latéral.
*

View file

@ -23,6 +23,10 @@
#include "models/evidence_record.h"
#include "dao/evidence_type_dao.h"
#include "views/evidence_import_dialog.h"
#include "dao/evidence_dao.h"
#include "models/evidence_type.h"
#include "widgets/evidence_list_model.h"
#include "widgets/evidence_category_model.h"
#include <gtk/gtk.h>
#include <errno.h>
@ -46,7 +50,8 @@ typedef enum
APPLICATION_OPEN_ERROR_INVALID_ARGUMENT,
APPLICATION_OPEN_ERROR_INVALID_PROJECT,
APPLICATION_OPEN_ERROR_TREE_BUILD,
APPLICATION_OPEN_ERROR_INSTALL
APPLICATION_OPEN_ERROR_INSTALL,
APPLICATION_OPEN_ERROR_EVIDENCE
} ApplicationOpenError;
/**
@ -81,10 +86,12 @@ struct Application
InvestigationTreeModel *tree_model;
TaskManager *task_manager;
ToolInitializer *tool_initializer;
EvidenceListModel *evidence_list_model;
EvidenceCategoryModel *evidence_category_model;
};
/**
* @brief Contexte conservé jusquà la fin dun import.
* @brief Contexte ApplicationOpenErrorconservé jusquà la fin dun import.
*/
typedef struct
{
@ -390,6 +397,469 @@ static void application_present_error(
const char *message
);
/**
* @brief Charge les preuves de la session et construit leurs modèles GTK.
*
* Les nouveaux modèles sont entièrement préparés avant de remplacer
* ceux actuellement affichés.
*
* En cas d'échec, les anciens modèles restent la propriété de
* l'application et ne sont pas modifiés.
*/
static gboolean application_refresh_evidence_models(
Application *application,
GError **error
)
{
Database *database = NULL;
EvidenceDao *evidence_dao = NULL;
EvidenceTypeDao *evidence_type_dao = NULL;
GPtrArray *evidence_records = NULL;
GPtrArray *evidence_types = NULL;
GHashTable *type_labels = NULL;
EvidenceListModel *new_evidence_list_model =
NULL;
EvidenceCategoryModel *new_evidence_category_model =
NULL;
GError *local_error = NULL;
guint type_index = 0;
gboolean refreshed = FALSE;
g_return_val_if_fail(
error == NULL || *error == NULL,
FALSE
);
if (application == NULL ||
application->main_window == NULL ||
application->session == NULL)
{
g_set_error_literal(
error,
APPLICATION_OPEN_ERROR,
APPLICATION_OPEN_ERROR_EVIDENCE,
"L'application ou la session d'enquête est invalide."
);
return FALSE;
}
database =
investigation_session_get_database(
application->session
);
if (database == NULL)
{
g_set_error_literal(
error,
APPLICATION_OPEN_ERROR,
APPLICATION_OPEN_ERROR_EVIDENCE,
"La session ne fournit aucune connexion SQLite."
);
return FALSE;
}
evidence_dao =
evidence_dao_new(
database,
&local_error
);
if (evidence_dao == NULL)
{
if (local_error != NULL)
{
if (error != NULL)
{
g_propagate_prefixed_error(
error,
local_error,
"Impossible de créer le DAO des preuves : "
);
local_error = NULL;
}
else
{
g_clear_error(
&local_error
);
}
}
else
{
g_set_error_literal(
error,
APPLICATION_OPEN_ERROR,
APPLICATION_OPEN_ERROR_EVIDENCE,
"Impossible de créer le DAO des preuves."
);
}
goto cleanup;
}
evidence_records =
evidence_dao_list_all(
evidence_dao,
&local_error
);
if (evidence_records == NULL)
{
if (local_error != NULL)
{
if (error != NULL)
{
g_propagate_prefixed_error(
error,
local_error,
"Impossible de charger les preuves : "
);
local_error = NULL;
}
else
{
g_clear_error(
&local_error
);
}
}
else
{
g_set_error_literal(
error,
APPLICATION_OPEN_ERROR,
APPLICATION_OPEN_ERROR_EVIDENCE,
"Impossible de charger les preuves."
);
}
goto cleanup;
}
evidence_type_dao =
evidence_type_dao_new(
database,
&local_error
);
if (evidence_type_dao == NULL)
{
if (local_error != NULL)
{
if (error != NULL)
{
g_propagate_prefixed_error(
error,
local_error,
"Impossible de créer le DAO des types de preuve : "
);
local_error = NULL;
}
else
{
g_clear_error(
&local_error
);
}
}
else
{
g_set_error_literal(
error,
APPLICATION_OPEN_ERROR,
APPLICATION_OPEN_ERROR_EVIDENCE,
"Impossible de créer le DAO des types de preuve."
);
}
goto cleanup;
}
evidence_types =
evidence_type_dao_list_all(
evidence_type_dao,
&local_error
);
if (evidence_types == NULL)
{
if (local_error != NULL)
{
if (error != NULL)
{
g_propagate_prefixed_error(
error,
local_error,
"Impossible de charger les types de preuve : "
);
local_error = NULL;
}
else
{
g_clear_error(
&local_error
);
}
}
else
{
g_set_error_literal(
error,
APPLICATION_OPEN_ERROR,
APPLICATION_OPEN_ERROR_EVIDENCE,
"Impossible de charger les types de preuve."
);
}
goto cleanup;
}
type_labels =
g_hash_table_new_full(
g_str_hash,
g_str_equal,
g_free,
g_free
);
if (type_labels == NULL)
{
g_set_error_literal(
error,
APPLICATION_OPEN_ERROR,
APPLICATION_OPEN_ERROR_EVIDENCE,
"Impossible d'allouer la table des libellés."
);
goto cleanup;
}
for (type_index = 0;
type_index < evidence_types->len;
type_index++)
{
const EvidenceType *evidence_type =
NULL;
const char *type_code = NULL;
const char *type_label = NULL;
evidence_type =
g_ptr_array_index(
evidence_types,
type_index
);
if (evidence_type == NULL)
{
continue;
}
type_code =
evidence_type_get_code(
evidence_type
);
type_label =
evidence_type_get_label(
evidence_type
);
if (type_code == NULL ||
type_code[0] == '\0' ||
type_label == NULL ||
type_label[0] == '\0')
{
continue;
}
g_hash_table_replace(
type_labels,
g_strdup(
type_code
),
g_strdup(
type_label
)
);
}
new_evidence_list_model =
evidence_list_model_new();
new_evidence_category_model =
evidence_category_model_new();
if (new_evidence_list_model == NULL ||
new_evidence_category_model == NULL)
{
g_set_error_literal(
error,
APPLICATION_OPEN_ERROR,
APPLICATION_OPEN_ERROR_EVIDENCE,
"Impossible d'allouer les modèles d'affichage des preuves."
);
goto cleanup;
}
if (!evidence_list_model_replace(
new_evidence_list_model,
evidence_records,
&local_error
))
{
if (local_error != NULL)
{
if (error != NULL)
{
g_propagate_prefixed_error(
error,
local_error,
"Impossible de préparer la liste des preuves : "
);
local_error = NULL;
}
else
{
g_clear_error(
&local_error
);
}
}
goto cleanup;
}
if (!evidence_category_model_replace(
new_evidence_category_model,
evidence_list_model_get_list_model(
new_evidence_list_model
),
type_labels,
&local_error
))
{
if (local_error != NULL)
{
if (error != NULL)
{
g_propagate_prefixed_error(
error,
local_error,
"Impossible de regrouper les preuves : "
);
local_error = NULL;
}
else
{
g_clear_error(
&local_error
);
}
}
goto cleanup;
}
/*
* La vue doit être détachée avant la libération de son ancien modèle.
*/
main_window_set_evidence_model(
application->main_window,
NULL
);
evidence_category_model_free(
application->evidence_category_model
);
evidence_list_model_free(
application->evidence_list_model
);
application->evidence_list_model =
new_evidence_list_model;
application->evidence_category_model =
new_evidence_category_model;
new_evidence_list_model =
NULL;
new_evidence_category_model =
NULL;
main_window_set_evidence_model(
application->main_window,
application->evidence_category_model
);
refreshed = TRUE;
cleanup:
g_clear_error(
&local_error
);
evidence_category_model_free(
new_evidence_category_model
);
evidence_list_model_free(
new_evidence_list_model
);
if (type_labels != NULL)
{
g_hash_table_unref(
type_labels
);
}
if (evidence_types != NULL)
{
g_ptr_array_unref(
evidence_types
);
}
if (evidence_records != NULL)
{
g_ptr_array_unref(
evidence_records
);
}
evidence_type_dao_free(
evidence_type_dao
);
evidence_dao_free(
evidence_dao
);
return refreshed;
}
/**
* @brief Installe une nouvelle session et son arbre dans l'application.
*
@ -416,6 +886,8 @@ static gboolean application_install_session(
const char *root_path = NULL;
const char *investigation_name = NULL;
GError *evidence_error = NULL;
if (application == NULL ||
application->main_window == NULL ||
new_session == NULL ||
@ -485,6 +957,54 @@ static gboolean application_install_session(
TRUE
);
/*
* L'ancienne liste ne doit jamais rester affichée après
* un changement d'enquête.
*/
main_window_set_evidence_model(
application->main_window,
NULL
);
evidence_category_model_free(
application->evidence_category_model
);
evidence_list_model_free(
application->evidence_list_model
);
application->evidence_category_model =
NULL;
application->evidence_list_model =
NULL;
if (!application_refresh_evidence_models(
application,
&evidence_error
))
{
g_warning(
"Impossible de charger les preuves de l'enquête : %s",
evidence_error != NULL
? evidence_error->message
: "erreur inconnue"
);
application_present_error(
application,
"Preuves indisponibles",
evidence_error != NULL
? evidence_error->message
: "Les preuves enregistrées n'ont pas pu être chargées."
);
g_clear_error(
&evidence_error
);
}
return TRUE;
}
@ -2123,6 +2643,22 @@ void application_free(
application->session
);
if (application->main_window != NULL)
{
main_window_set_evidence_model(
application->main_window,
NULL
);
}
evidence_category_model_free(
application->evidence_category_model
);
evidence_list_model_free(
application->evidence_list_model
);
/*
* MainWindow contient TaskPanel.
* Il doit être détruit avant TaskManager.

View file

@ -14,7 +14,7 @@
/**
* @brief Largeur initiale de la fenêtre principale.
*/
#define MAIN_WINDOW_DEFAULT_WIDTH 1000
#define MAIN_WINDOW_DEFAULT_WIDTH 1100
/**
* @brief Hauteur initiale de la fenêtre principale.
@ -24,7 +24,7 @@
/**
* @brief Position initiale de la séparation horizontale.
*/
#define MAIN_WINDOW_SIDEBAR_POSITION 250
#define MAIN_WINDOW_SIDEBAR_POSITION 320
/**
* @brief Position initiale de la séparation verticale.
@ -716,6 +716,22 @@ void main_window_set_tree_model(
);
}
void main_window_set_evidence_model(
MainWindow *main_window,
EvidenceCategoryModel *evidence_category_model
)
{
if (main_window == NULL)
{
return;
}
sidebar_set_evidence_model(
main_window->sidebar,
evidence_category_model
);
}
void main_window_set_investigation(
MainWindow *main_window,
const char *investigation_name,
@ -848,6 +864,24 @@ void main_window_set_tree_selection_callback(
);
}
void main_window_set_evidence_selection_callback(
MainWindow *main_window,
MainWindowEvidenceSelectionCallback callback,
gpointer user_data
)
{
if (main_window == NULL)
{
return;
}
sidebar_set_evidence_selection_callback(
main_window->sidebar,
callback,
user_data
);
}
void main_window_set_new_investigation_callback(
MainWindow *main_window,
MainWindowNewInvestigationCallback callback,

View file

@ -0,0 +1,331 @@
/******************************************************************************
* @file evidence_category_item.c
* @brief Implémentation d'une catégorie de preuves affichées.
******************************************************************************/
#include "widgets/evidence_category_item.h"
/**
* @brief État interne de la catégorie.
*/
struct _EvidenceCategoryItem
{
GObject parent_instance;
char *type_identifier;
char *label;
GListStore *children;
};
G_DEFINE_TYPE(
EvidenceCategoryItem,
evidence_category_item,
G_TYPE_OBJECT
)
/**
* @brief Copie et nettoie une chaîne obligatoire.
*/
static char *evidence_category_item_duplicate_required_text(
const char *value
)
{
char *normalized_value = NULL;
if (value == NULL)
{
return NULL;
}
normalized_value =
g_strdup(
value
);
if (normalized_value == NULL)
{
return NULL;
}
g_strstrip(
normalized_value
);
if (normalized_value[0] == '\0')
{
g_free(
normalized_value
);
return NULL;
}
return normalized_value;
}
/**
* @brief Libère les ressources possédées par la catégorie.
*/
static void evidence_category_item_finalize(
GObject *object
)
{
EvidenceCategoryItem *evidence_category_item =
EVIDENCE_CATEGORY_ITEM(
object
);
g_clear_pointer(
&evidence_category_item->type_identifier,
g_free
);
g_clear_pointer(
&evidence_category_item->label,
g_free
);
g_clear_object(
&evidence_category_item->children
);
G_OBJECT_CLASS(
evidence_category_item_parent_class
)->finalize(
object
);
}
/**
* @brief Initialise la classe GObject.
*/
static void evidence_category_item_class_init(
EvidenceCategoryItemClass *evidence_category_item_class
)
{
GObjectClass *object_class =
G_OBJECT_CLASS(
evidence_category_item_class
);
object_class->finalize =
evidence_category_item_finalize;
}
/**
* @brief Initialise une catégorie vide.
*/
static void evidence_category_item_init(
EvidenceCategoryItem *evidence_category_item
)
{
evidence_category_item->children =
g_list_store_new(
EVIDENCE_TYPE_LIST_ITEM
);
}
EvidenceCategoryItem *evidence_category_item_new(
const char *type_identifier,
const char *label
)
{
EvidenceCategoryItem *evidence_category_item =
NULL;
char *normalized_type_identifier =
NULL;
char *normalized_label =
NULL;
normalized_type_identifier =
evidence_category_item_duplicate_required_text(
type_identifier
);
normalized_label =
evidence_category_item_duplicate_required_text(
label
);
if (normalized_type_identifier == NULL ||
normalized_label == NULL)
{
g_free(
normalized_label
);
g_free(
normalized_type_identifier
);
return NULL;
}
evidence_category_item =
g_object_new(
EVIDENCE_TYPE_CATEGORY_ITEM,
NULL
);
if (evidence_category_item == NULL ||
evidence_category_item->children == NULL)
{
g_clear_object(
&evidence_category_item
);
g_free(
normalized_label
);
g_free(
normalized_type_identifier
);
return NULL;
}
evidence_category_item->type_identifier =
normalized_type_identifier;
evidence_category_item->label =
normalized_label;
return evidence_category_item;
}
/**
* @brief Compare deux preuves par nom original puis par identifiant.
*/
static gint evidence_category_item_compare_list_items(
gconstpointer first_pointer,
gconstpointer second_pointer,
gpointer user_data
)
{
const EvidenceListItem *first_item =
first_pointer;
const EvidenceListItem *second_item =
second_pointer;
const char *first_name = NULL;
const char *second_name = NULL;
gint comparison = 0;
(void) user_data;
first_name =
evidence_list_item_get_original_name(
first_item
);
second_name =
evidence_list_item_get_original_name(
second_item
);
comparison =
g_utf8_collate(
first_name,
second_name
);
if (comparison != 0)
{
return comparison;
}
return g_strcmp0(
evidence_list_item_get_identifier(
first_item
),
evidence_list_item_get_identifier(
second_item
)
);
}
gboolean evidence_category_item_append(
EvidenceCategoryItem *evidence_category_item,
EvidenceListItem *evidence_list_item
)
{
if (evidence_category_item == NULL ||
evidence_category_item->children == NULL ||
evidence_list_item == NULL)
{
return FALSE;
}
g_list_store_insert_sorted(
evidence_category_item->children,
G_OBJECT(
evidence_list_item
),
evidence_category_item_compare_list_items,
NULL
);
return TRUE;
}
const char *evidence_category_item_get_type_identifier(
const EvidenceCategoryItem *evidence_category_item
)
{
if (evidence_category_item == NULL)
{
return NULL;
}
return evidence_category_item->type_identifier;
}
const char *evidence_category_item_get_label(
const EvidenceCategoryItem *evidence_category_item
)
{
if (evidence_category_item == NULL)
{
return NULL;
}
return evidence_category_item->label;
}
guint evidence_category_item_get_count(
const EvidenceCategoryItem *evidence_category_item
)
{
if (evidence_category_item == NULL ||
evidence_category_item->children == NULL)
{
return 0;
}
return g_list_model_get_n_items(
G_LIST_MODEL(
evidence_category_item->children
)
);
}
GListModel *evidence_category_item_get_children(
EvidenceCategoryItem *evidence_category_item
)
{
if (evidence_category_item == NULL ||
evidence_category_item->children == NULL)
{
return NULL;
}
return G_LIST_MODEL(
evidence_category_item->children
);
}

View file

@ -0,0 +1,548 @@
/******************************************************************************
* @file evidence_category_model.c
* @brief Regroupement des preuves par catégorie pour l'affichage GTK.
******************************************************************************/
#include "widgets/evidence_category_model.h"
#include "widgets/evidence_category_item.h"
#include "widgets/evidence_list_item.h"
/**
* @brief État interne du modèle.
*/
struct EvidenceCategoryModel
{
GListStore *categories;
};
GQuark evidence_category_model_error_quark(void)
{
return g_quark_from_static_string(
"labfy-investigation-evidence-category-model-error"
);
}
/**
* @brief Compare deux catégories par libellé puis par code métier.
*/
static gint evidence_category_model_compare_categories(
gconstpointer first_pointer,
gconstpointer second_pointer,
gpointer user_data
)
{
const EvidenceCategoryItem *first_category =
first_pointer;
const EvidenceCategoryItem *second_category =
second_pointer;
const char *first_label = NULL;
const char *second_label = NULL;
gint comparison = 0;
(void) user_data;
first_label =
evidence_category_item_get_label(
first_category
);
second_label =
evidence_category_item_get_label(
second_category
);
comparison =
g_utf8_collate(
first_label,
second_label
);
if (comparison != 0)
{
return comparison;
}
return g_strcmp0(
evidence_category_item_get_type_identifier(
first_category
),
evidence_category_item_get_type_identifier(
second_category
)
);
}
/**
* @brief Vérifie qu'une chaîne obligatoire contient du texte.
*/
static gboolean evidence_category_model_value_has_text(
const char *value
)
{
return value != NULL &&
value[0] != '\0';
}
/**
* @brief Libère les références temporaires d'un tableau d'ajouts.
*/
static void evidence_category_model_release_additions(
gpointer *additions,
guint addition_count
)
{
guint addition_index = 0;
if (additions == NULL)
{
return;
}
for (addition_index = 0;
addition_index < addition_count;
addition_index++)
{
g_clear_object(
&additions[addition_index]
);
}
g_free(
additions
);
}
EvidenceCategoryModel *evidence_category_model_new(void)
{
EvidenceCategoryModel *evidence_category_model =
NULL;
evidence_category_model =
g_try_new0(
EvidenceCategoryModel,
1
);
if (evidence_category_model == NULL)
{
return NULL;
}
evidence_category_model->categories =
g_list_store_new(
EVIDENCE_TYPE_CATEGORY_ITEM
);
if (evidence_category_model->categories == NULL)
{
g_free(
evidence_category_model
);
return NULL;
}
return evidence_category_model;
}
void evidence_category_model_free(
EvidenceCategoryModel *evidence_category_model
)
{
if (evidence_category_model == NULL)
{
return;
}
g_clear_object(
&evidence_category_model->categories
);
g_free(
evidence_category_model
);
}
GListModel *evidence_category_model_get_list_model(
EvidenceCategoryModel *evidence_category_model
)
{
if (evidence_category_model == NULL ||
evidence_category_model->categories == NULL)
{
return NULL;
}
return G_LIST_MODEL(
evidence_category_model->categories
);
}
guint evidence_category_model_get_count(
const EvidenceCategoryModel *evidence_category_model
)
{
if (evidence_category_model == NULL ||
evidence_category_model->categories == NULL)
{
return 0;
}
return g_list_model_get_n_items(
G_LIST_MODEL(
evidence_category_model->categories
)
);
}
gboolean evidence_category_model_replace(
EvidenceCategoryModel *evidence_category_model,
GListModel *evidence_items,
GHashTable *type_labels,
GError **error
)
{
GListStore *new_categories = NULL;
GHashTable *categories_by_type =
NULL;
EvidenceListItem *evidence_list_item =
NULL;
EvidenceCategoryItem *evidence_category_item =
NULL;
gpointer *additions = NULL;
const char *type_identifier = NULL;
const char *category_label = NULL;
guint evidence_count = 0;
guint evidence_index = 0;
guint new_category_count = 0;
guint category_index = 0;
guint previous_category_count = 0;
gboolean replaced = FALSE;
g_return_val_if_fail(
error == NULL || *error == NULL,
FALSE
);
if (evidence_category_model == NULL ||
evidence_category_model->categories == NULL ||
evidence_items == NULL)
{
g_set_error_literal(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_INVALID_ARGUMENT,
"Le modèle de catégories ou la liste de preuves est invalide."
);
return FALSE;
}
if (!g_type_is_a(
g_list_model_get_item_type(
evidence_items
),
EVIDENCE_TYPE_LIST_ITEM
))
{
g_set_error_literal(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_INVALID_ARGUMENT,
"Le modèle fourni ne contient pas des EvidenceListItem."
);
return FALSE;
}
new_categories =
g_list_store_new(
EVIDENCE_TYPE_CATEGORY_ITEM
);
categories_by_type =
g_hash_table_new_full(
g_str_hash,
g_str_equal,
g_free,
NULL
);
if (new_categories == NULL ||
categories_by_type == NULL)
{
g_set_error_literal(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_MEMORY,
"Impossible de préparer le regroupement des preuves."
);
goto cleanup;
}
evidence_count =
g_list_model_get_n_items(
evidence_items
);
for (evidence_index = 0;
evidence_index < evidence_count;
evidence_index++)
{
evidence_list_item =
g_list_model_get_item(
evidence_items,
evidence_index
);
if (evidence_list_item == NULL ||
!EVIDENCE_IS_LIST_ITEM(
evidence_list_item
))
{
g_set_error(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_INVALID_ITEM,
"La preuve située à l'index %u est invalide.",
evidence_index
);
goto cleanup;
}
type_identifier =
evidence_list_item_get_type_identifier(
evidence_list_item
);
if (!evidence_category_model_value_has_text(
type_identifier
))
{
g_set_error(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_INVALID_ITEM,
"La preuve située à l'index %u ne possède aucun type.",
evidence_index
);
goto cleanup;
}
evidence_category_item =
g_hash_table_lookup(
categories_by_type,
type_identifier
);
if (evidence_category_item == NULL)
{
category_label = NULL;
if (type_labels != NULL)
{
category_label =
g_hash_table_lookup(
type_labels,
type_identifier
);
}
/*
* Un type inconnu reste affichable.
* Son code métier devient son libellé de secours.
*/
if (!evidence_category_model_value_has_text(
category_label
))
{
category_label =
type_identifier;
}
evidence_category_item =
evidence_category_item_new(
type_identifier,
category_label
);
if (evidence_category_item == NULL)
{
g_set_error(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_CATEGORY,
"Impossible de créer la catégorie '%s'.",
type_identifier
);
goto cleanup;
}
g_list_store_insert_sorted(
new_categories,
G_OBJECT(
evidence_category_item
),
evidence_category_model_compare_categories,
NULL
);
/*
* La table emprunte le pointeur.
* Le GListStore possède la référence.
*/
g_hash_table_insert(
categories_by_type,
g_strdup(
type_identifier
),
evidence_category_item
);
g_object_unref(
evidence_category_item
);
}
if (!evidence_category_item_append(
evidence_category_item,
evidence_list_item
))
{
g_set_error(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_CATEGORY,
"Impossible d'ajouter la preuve %u à sa catégorie.",
evidence_index
);
goto cleanup;
}
g_clear_object(
&evidence_list_item
);
evidence_category_item =
NULL;
}
new_category_count =
g_list_model_get_n_items(
G_LIST_MODEL(
new_categories
)
);
previous_category_count =
g_list_model_get_n_items(
G_LIST_MODEL(
evidence_category_model->categories
)
);
if (new_category_count == 0)
{
g_list_store_remove_all(
evidence_category_model->categories
);
replaced = TRUE;
goto cleanup;
}
additions =
g_try_new0(
gpointer,
new_category_count
);
if (additions == NULL)
{
g_set_error_literal(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_MEMORY,
"Impossible de préparer le remplacement des catégories."
);
goto cleanup;
}
for (category_index = 0;
category_index < new_category_count;
category_index++)
{
additions[category_index] =
g_list_model_get_item(
G_LIST_MODEL(
new_categories
),
category_index
);
if (additions[category_index] == NULL)
{
g_set_error_literal(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_CATEGORY,
"Impossible de lire une catégorie préparée."
);
goto cleanup;
}
}
g_list_store_splice(
evidence_category_model->categories,
0,
previous_category_count,
additions,
new_category_count
);
replaced = TRUE;
cleanup:
g_clear_object(
&evidence_list_item
);
evidence_category_model_release_additions(
additions,
new_category_count
);
if (categories_by_type != NULL)
{
g_hash_table_unref(
categories_by_type
);
}
g_clear_object(
&new_categories
);
return replaced;
}

View file

@ -0,0 +1,315 @@
/******************************************************************************
* @file evidence_list_item.c
* @brief Implémentation de EvidenceListItem.
******************************************************************************/
#include "widgets/evidence_list_item.h"
/**
* @brief Données privées copiées pour l'affichage GTK.
*/
struct _EvidenceListItem
{
GObject parent_instance;
char *identifier;
char *original_name;
char *type_identifier;
guint64 size_bytes;
char *collected_at;
char *source;
EvidenceIntegrityStatus integrity_status;
};
G_DEFINE_TYPE(
EvidenceListItem,
evidence_list_item,
G_TYPE_OBJECT
)
/**
* @brief Libère les données possédées par l'objet.
*/
static void evidence_list_item_finalize(
GObject *object
)
{
EvidenceListItem *evidence_list_item =
EVIDENCE_LIST_ITEM(
object
);
g_clear_pointer(
&evidence_list_item->original_name,
g_free
);
g_clear_pointer(
&evidence_list_item->type_identifier,
g_free
);
g_clear_pointer(
&evidence_list_item->collected_at,
g_free
);
g_clear_pointer(
&evidence_list_item->source,
g_free
);
G_OBJECT_CLASS(
evidence_list_item_parent_class
)->finalize(
object
);
}
/**
* @brief Initialise la classe GObject.
*/
static void evidence_list_item_class_init(
EvidenceListItemClass *evidence_list_item_class
)
{
GObjectClass *object_class =
G_OBJECT_CLASS(
evidence_list_item_class
);
object_class->finalize =
evidence_list_item_finalize;
}
/**
* @brief Initialise une nouvelle instance vide.
*/
static void evidence_list_item_init(
EvidenceListItem *evidence_list_item
)
{
evidence_list_item->integrity_status =
EVIDENCE_INTEGRITY_STATUS_UNKNOWN;
}
EvidenceListItem *evidence_list_item_new(
const EvidenceRecord *evidence_record
)
{
EvidenceListItem *evidence_list_item = NULL;
const char *identifier = NULL;
const char *original_name = NULL;
const char *type_identifier = NULL;
if (evidence_record == NULL)
{
return NULL;
}
identifier =
evidence_record_get_identifier(
evidence_record
);
original_name =
evidence_record_get_original_name(
evidence_record
);
type_identifier =
evidence_record_get_type_identifier(
evidence_record
);
/*
* Ces deux valeurs sont obligatoires dans EvidenceRecord.
* Cette vérification protège néanmoins l'adaptateur d'affichage.
*/
if (identifier == NULL ||
original_name == NULL ||
original_name[0] == '\0' ||
type_identifier == NULL ||
type_identifier[0] == '\0')
{
return NULL;
}
evidence_list_item =
g_object_new(
EVIDENCE_TYPE_LIST_ITEM,
NULL
);
evidence_list_item->identifier =
g_strdup(
identifier
);
if (evidence_list_item == NULL)
{
return NULL;
}
evidence_list_item->original_name =
g_strdup(
original_name
);
evidence_list_item->type_identifier =
g_strdup(
type_identifier
);
evidence_list_item->size_bytes =
evidence_record_get_size_bytes(
evidence_record
);
evidence_list_item->collected_at =
g_strdup(
evidence_record_get_collected_at(
evidence_record
)
);
evidence_list_item->source =
g_strdup(
evidence_record_get_source(
evidence_record
)
);
evidence_list_item->integrity_status =
evidence_record_get_integrity_status(
evidence_record
);
if (evidence_list_item->identifier == NULL ||
evidence_list_item->original_name == NULL ||
evidence_list_item->type_identifier == NULL)
{
g_object_unref(
evidence_list_item
);
return NULL;
}
return evidence_list_item;
}
const char *evidence_list_item_get_identifier(
const EvidenceListItem *evidence_list_item
)
{
if (evidence_list_item == NULL)
{
return NULL;
}
return evidence_list_item->identifier;
}
const char *evidence_list_item_get_original_name(
const EvidenceListItem *evidence_list_item
)
{
if (evidence_list_item == NULL)
{
return NULL;
}
return evidence_list_item->original_name;
}
const char *evidence_list_item_get_type_identifier(
const EvidenceListItem *evidence_list_item
)
{
if (evidence_list_item == NULL)
{
return NULL;
}
return evidence_list_item->type_identifier;
}
guint64 evidence_list_item_get_size_bytes(
const EvidenceListItem *evidence_list_item
)
{
if (evidence_list_item == NULL)
{
return 0;
}
return evidence_list_item->size_bytes;
}
const char *evidence_list_item_get_collected_at(
const EvidenceListItem *evidence_list_item
)
{
if (evidence_list_item == NULL)
{
return NULL;
}
return evidence_list_item->collected_at;
}
const char *evidence_list_item_get_source(
const EvidenceListItem *evidence_list_item
)
{
if (evidence_list_item == NULL)
{
return NULL;
}
return evidence_list_item->source;
}
EvidenceIntegrityStatus evidence_list_item_get_integrity_status(
const EvidenceListItem *evidence_list_item
)
{
if (evidence_list_item == NULL)
{
return EVIDENCE_INTEGRITY_STATUS_UNKNOWN;
}
return evidence_list_item->integrity_status;
}
const char *evidence_list_item_get_integrity_status_label(
const EvidenceListItem *evidence_list_item
)
{
EvidenceIntegrityStatus integrity_status =
evidence_list_item_get_integrity_status(
evidence_list_item
);
switch (integrity_status)
{
case EVIDENCE_INTEGRITY_STATUS_VALID:
return "Valide";
case EVIDENCE_INTEGRITY_STATUS_MISSING:
return "Fichier absent";
case EVIDENCE_INTEGRITY_STATUS_MODIFIED:
return "Modifiée";
case EVIDENCE_INTEGRITY_STATUS_ERROR:
return "Erreur";
case EVIDENCE_INTEGRITY_STATUS_UNKNOWN:
default:
return "Non vérifiée";
}
}

View file

@ -0,0 +1,239 @@
/******************************************************************************
* @file evidence_list_model.c
* @brief Implémentation du modèle de liste des preuves.
******************************************************************************/
#include "widgets/evidence_list_model.h"
#include "widgets/evidence_list_item.h"
/**
* @brief État interne du modèle de liste.
*/
struct EvidenceListModel
{
GListStore *items;
};
GQuark evidence_list_model_error_quark(void)
{
return g_quark_from_static_string(
"labfy-investigation-evidence-list-model-error"
);
}
EvidenceListModel *evidence_list_model_new(void)
{
EvidenceListModel *evidence_list_model =
NULL;
evidence_list_model =
g_try_new0(
EvidenceListModel,
1
);
if (evidence_list_model == NULL)
{
return NULL;
}
evidence_list_model->items =
g_list_store_new(
EVIDENCE_TYPE_LIST_ITEM
);
if (evidence_list_model->items == NULL)
{
g_free(
evidence_list_model
);
return NULL;
}
return evidence_list_model;
}
void evidence_list_model_free(
EvidenceListModel *evidence_list_model
)
{
if (evidence_list_model == NULL)
{
return;
}
g_clear_object(
&evidence_list_model->items
);
g_free(
evidence_list_model
);
}
GListModel *evidence_list_model_get_list_model(
EvidenceListModel *evidence_list_model
)
{
if (evidence_list_model == NULL ||
evidence_list_model->items == NULL)
{
return NULL;
}
return G_LIST_MODEL(
evidence_list_model->items
);
}
guint evidence_list_model_get_count(
const EvidenceListModel *evidence_list_model
)
{
if (evidence_list_model == NULL ||
evidence_list_model->items == NULL)
{
return 0;
}
return g_list_model_get_n_items(
G_LIST_MODEL(
evidence_list_model->items
)
);
}
gboolean evidence_list_model_replace(
EvidenceListModel *evidence_list_model,
const GPtrArray *evidence_records,
GError **error
)
{
GPtrArray *new_items = NULL;
guint record_index = 0;
guint previous_count = 0;
g_return_val_if_fail(
error == NULL || *error == NULL,
FALSE
);
if (evidence_list_model == NULL ||
evidence_list_model->items == NULL ||
evidence_records == NULL)
{
g_set_error_literal(
error,
EVIDENCE_LIST_MODEL_ERROR,
EVIDENCE_LIST_MODEL_ERROR_INVALID_ARGUMENT,
"Le modèle ou le tableau de preuves est invalide."
);
return FALSE;
}
/*
* Le tableau libère notre référence locale sur chaque objet.
* GListStore prendra ensuite sa propre référence lors du splice.
*/
new_items =
g_ptr_array_new_with_free_func(
g_object_unref
);
if (new_items == NULL)
{
g_set_error_literal(
error,
EVIDENCE_LIST_MODEL_ERROR,
EVIDENCE_LIST_MODEL_ERROR_MEMORY,
"Impossible d'allouer la nouvelle liste de preuves."
);
return FALSE;
}
for (record_index = 0;
record_index < evidence_records->len;
record_index++)
{
const EvidenceRecord *evidence_record =
NULL;
EvidenceListItem *evidence_list_item =
NULL;
evidence_record =
g_ptr_array_index(
evidence_records,
record_index
);
evidence_list_item =
evidence_list_item_new(
evidence_record
);
if (evidence_list_item == NULL)
{
g_set_error(
error,
EVIDENCE_LIST_MODEL_ERROR,
EVIDENCE_LIST_MODEL_ERROR_INVALID_RECORD,
"La preuve située à l'index %u est invalide.",
record_index
);
g_ptr_array_unref(
new_items
);
return FALSE;
}
g_ptr_array_add(
new_items,
evidence_list_item
);
}
previous_count =
g_list_model_get_n_items(
G_LIST_MODEL(
evidence_list_model->items
)
);
if (new_items->len == 0)
{
g_list_store_remove_all(
evidence_list_model->items
);
}
else
{
/*
* Tous les éléments sont prêts.
* Le remplacement peut maintenant être effectué en une opération.
*/
g_list_store_splice(
evidence_list_model->items,
0,
previous_count,
new_items->pdata,
new_items->len
);
}
/*
* GListStore possède désormais une référence sur chaque élément.
*/
g_ptr_array_unref(
new_items
);
return TRUE;
}

File diff suppressed because it is too large Load diff

View file

@ -6,13 +6,38 @@
#include "widgets/sidebar.h"
#include "core/investigation_node.h"
#include "widgets/investigation_tree_view.h"
#include "widgets/evidence_tree_view.h"
#include <glib.h>
/**
* @brief Largeur initiale du panneau latéral.
*/
#define SIDEBAR_DEFAULT_WIDTH 250
#define SIDEBAR_DEFAULT_WIDTH 320
/**
* @brief Identifiant de la page contenant l'arborescence.
*/
#define SIDEBAR_PAGE_FOLDER \
"folder"
/**
* @brief Identifiant de la page contenant les preuves.
*/
#define SIDEBAR_PAGE_EVIDENCE \
"evidence"
/**
* @brief État vide de l'onglet Preuves.
*/
#define SIDEBAR_EVIDENCE_STATE_EMPTY \
"empty"
/**
* @brief État contenant l'arbre des preuves.
*/
#define SIDEBAR_EVIDENCE_STATE_TREE \
"tree"
/**
* @struct Sidebar
@ -25,23 +50,74 @@ struct Sidebar
{
GtkWidget *root_widget;
GtkWidget *title_label;
GtkWidget *page_switcher;
GtkWidget *stack;
GtkWidget *folder_page;
GtkWidget *evidence_page;
GtkWidget *evidence_content_stack;
GtkWidget *evidence_empty_page;
GtkWidget *evidence_empty_label;
InvestigationTreeView *tree_view;
EvidenceTreeView *evidence_tree_view;
SidebarEvidenceSelectionCallback
evidence_selection_callback;
gpointer evidence_selection_user_data;
};
/**
* @brief Relaie la sélection provenant de EvidenceTreeView.
*/
static void sidebar_on_evidence_selected(
const char *evidence_identifier,
gpointer user_data
)
{
Sidebar *sidebar =
user_data;
if (sidebar == NULL ||
sidebar->evidence_selection_callback == NULL)
{
return;
}
sidebar->evidence_selection_callback(
evidence_identifier,
sidebar->evidence_selection_user_data
);
}
Sidebar *sidebar_new(void)
{
Sidebar *sidebar = NULL;
sidebar = g_new0(Sidebar, 1);
GtkWidget *tree_view_widget = NULL;
sidebar->root_widget = gtk_box_new(
GTK_ORIENTATION_VERTICAL,
0
);
GtkWidget *tree_view_widget = NULL;
GtkWidget *evidence_tree_widget = NULL;
sidebar =
g_new0(
Sidebar,
1
);
sidebar->root_widget =
gtk_box_new(
GTK_ORIENTATION_VERTICAL,
0
);
if (sidebar->root_widget == NULL)
{
sidebar_free(sidebar);
sidebar_free(
sidebar
);
return NULL;
}
@ -51,9 +127,22 @@ Sidebar *sidebar_new(void)
-1
);
sidebar->title_label = gtk_label_new(
"Dossier d'enquête"
);
/*
* Titre de l'enquête active.
*/
sidebar->title_label =
gtk_label_new(
"Dossier d'enquête"
);
if (sidebar->title_label == NULL)
{
sidebar_free(
sidebar
);
return NULL;
}
gtk_widget_set_halign(
sidebar->title_label,
@ -77,29 +166,155 @@ Sidebar *sidebar_new(void)
gtk_widget_set_margin_bottom(
sidebar->title_label,
12
8
);
gtk_box_append(
GTK_BOX(sidebar->root_widget),
GTK_BOX(
sidebar->root_widget
),
sidebar->title_label
);
sidebar->tree_view = investigation_tree_view_new();
/*
* GtkStack contenant les pages Dossier et Preuves.
*/
sidebar->stack =
gtk_stack_new();
if (sidebar->tree_view == NULL)
if (sidebar->stack == NULL)
{
sidebar_free(sidebar);
sidebar_free(
sidebar
);
return NULL;
}
tree_view_widget = investigation_tree_view_get_widget(
sidebar->tree_view
gtk_widget_set_hexpand(
sidebar->stack,
TRUE
);
gtk_widget_set_vexpand(
sidebar->stack,
TRUE
);
gtk_stack_set_transition_type(
GTK_STACK(
sidebar->stack
),
GTK_STACK_TRANSITION_TYPE_CROSSFADE
);
/*
* Boutons permettant de sélectionner une page du GtkStack.
*/
sidebar->page_switcher =
gtk_stack_switcher_new();
if (sidebar->page_switcher == NULL)
{
sidebar_free(
sidebar
);
return NULL;
}
gtk_stack_switcher_set_stack(
GTK_STACK_SWITCHER(
sidebar->page_switcher
),
GTK_STACK(
sidebar->stack
)
);
gtk_widget_set_hexpand(
sidebar->page_switcher,
TRUE
);
gtk_widget_set_halign(
sidebar->page_switcher,
GTK_ALIGN_CENTER
);
gtk_widget_set_margin_start(
sidebar->page_switcher,
8
);
gtk_widget_set_margin_end(
sidebar->page_switcher,
8
);
gtk_widget_set_margin_bottom(
sidebar->page_switcher,
8
);
gtk_box_append(
GTK_BOX(
sidebar->root_widget
),
sidebar->page_switcher
);
/*
* Page Dossier.
*/
sidebar->folder_page =
gtk_box_new(
GTK_ORIENTATION_VERTICAL,
0
);
if (sidebar->folder_page == NULL)
{
sidebar_free(
sidebar
);
return NULL;
}
gtk_widget_set_hexpand(
sidebar->folder_page,
TRUE
);
gtk_widget_set_vexpand(
sidebar->folder_page,
TRUE
);
sidebar->tree_view =
investigation_tree_view_new();
if (sidebar->tree_view == NULL)
{
sidebar_free(
sidebar
);
return NULL;
}
tree_view_widget =
investigation_tree_view_get_widget(
sidebar->tree_view
);
if (tree_view_widget == NULL)
{
sidebar_free(sidebar);
sidebar_free(
sidebar
);
return NULL;
}
@ -114,10 +329,279 @@ Sidebar *sidebar_new(void)
);
gtk_box_append(
GTK_BOX(sidebar->root_widget),
GTK_BOX(
sidebar->folder_page
),
tree_view_widget
);
gtk_stack_add_titled(
GTK_STACK(
sidebar->stack
),
sidebar->folder_page,
SIDEBAR_PAGE_FOLDER,
"Dossier"
);
/*
* Page Preuves.
*/
sidebar->evidence_page =
gtk_box_new(
GTK_ORIENTATION_VERTICAL,
0
);
if (sidebar->evidence_page == NULL)
{
sidebar_free(
sidebar
);
return NULL;
}
gtk_widget_set_hexpand(
sidebar->evidence_page,
TRUE
);
gtk_widget_set_vexpand(
sidebar->evidence_page,
TRUE
);
/*
* Stack interne alternant entre l'état vide
* et l'arborescence des preuves.
*/
sidebar->evidence_content_stack =
gtk_stack_new();
if (sidebar->evidence_content_stack == NULL)
{
sidebar_free(
sidebar
);
return NULL;
}
gtk_widget_set_hexpand(
sidebar->evidence_content_stack,
TRUE
);
gtk_widget_set_vexpand(
sidebar->evidence_content_stack,
TRUE
);
gtk_stack_set_transition_type(
GTK_STACK(
sidebar->evidence_content_stack
),
GTK_STACK_TRANSITION_TYPE_CROSSFADE
);
/*
* État vide.
*/
sidebar->evidence_empty_page =
gtk_box_new(
GTK_ORIENTATION_VERTICAL,
0
);
if (sidebar->evidence_empty_page == NULL)
{
sidebar_free(
sidebar
);
return NULL;
}
gtk_widget_set_hexpand(
sidebar->evidence_empty_page,
TRUE
);
gtk_widget_set_vexpand(
sidebar->evidence_empty_page,
TRUE
);
sidebar->evidence_empty_label =
gtk_label_new(
"Aucune preuve importée dans cette enquête."
);
if (sidebar->evidence_empty_label == NULL)
{
sidebar_free(
sidebar
);
return NULL;
}
gtk_label_set_wrap(
GTK_LABEL(
sidebar->evidence_empty_label
),
TRUE
);
gtk_label_set_justify(
GTK_LABEL(
sidebar->evidence_empty_label
),
GTK_JUSTIFY_CENTER
);
gtk_widget_set_halign(
sidebar->evidence_empty_label,
GTK_ALIGN_CENTER
);
gtk_widget_set_valign(
sidebar->evidence_empty_label,
GTK_ALIGN_CENTER
);
gtk_widget_set_hexpand(
sidebar->evidence_empty_label,
TRUE
);
gtk_widget_set_vexpand(
sidebar->evidence_empty_label,
TRUE
);
gtk_widget_set_margin_start(
sidebar->evidence_empty_label,
20
);
gtk_widget_set_margin_end(
sidebar->evidence_empty_label,
20
);
gtk_box_append(
GTK_BOX(
sidebar->evidence_empty_page
),
sidebar->evidence_empty_label
);
gtk_stack_add_named(
GTK_STACK(
sidebar->evidence_content_stack
),
sidebar->evidence_empty_page,
SIDEBAR_EVIDENCE_STATE_EMPTY
);
/*
* Arborescence des catégories et preuves.
*/
sidebar->evidence_tree_view =
evidence_tree_view_new();
if (sidebar->evidence_tree_view == NULL)
{
sidebar_free(
sidebar
);
return NULL;
}
evidence_tree_widget =
evidence_tree_view_get_widget(
sidebar->evidence_tree_view
);
if (evidence_tree_widget == NULL)
{
sidebar_free(
sidebar
);
return NULL;
}
gtk_widget_set_hexpand(
evidence_tree_widget,
TRUE
);
gtk_widget_set_vexpand(
evidence_tree_widget,
TRUE
);
evidence_tree_view_set_selection_callback(
sidebar->evidence_tree_view,
sidebar_on_evidence_selected,
sidebar
);
gtk_stack_add_named(
GTK_STACK(
sidebar->evidence_content_stack
),
evidence_tree_widget,
SIDEBAR_EVIDENCE_STATE_TREE
);
/*
* Aucun modèle n'est encore installé.
*/
gtk_stack_set_visible_child_name(
GTK_STACK(
sidebar->evidence_content_stack
),
SIDEBAR_EVIDENCE_STATE_EMPTY
);
gtk_box_append(
GTK_BOX(
sidebar->evidence_page
),
sidebar->evidence_content_stack
);
gtk_stack_add_titled(
GTK_STACK(
sidebar->stack
),
sidebar->evidence_page,
SIDEBAR_PAGE_EVIDENCE,
"Preuves"
);
/*
* L'onglet Dossier reste sélectionné au démarrage.
*/
gtk_stack_set_visible_child_name(
GTK_STACK(
sidebar->stack
),
SIDEBAR_PAGE_FOLDER
);
gtk_box_append(
GTK_BOX(
sidebar->root_widget
),
sidebar->stack
);
return sidebar;
}
@ -209,6 +693,64 @@ void sidebar_set_selection_callback(
);
}
void sidebar_set_evidence_model(
Sidebar *sidebar,
EvidenceCategoryModel *evidence_category_model
)
{
if (sidebar == NULL ||
sidebar->evidence_tree_view == NULL ||
sidebar->evidence_content_stack == NULL)
{
return;
}
evidence_tree_view_set_model(
sidebar->evidence_tree_view,
evidence_category_model
);
if (evidence_category_model == NULL ||
evidence_category_model_get_count(
evidence_category_model
) == 0)
{
gtk_stack_set_visible_child_name(
GTK_STACK(
sidebar->evidence_content_stack
),
SIDEBAR_EVIDENCE_STATE_EMPTY
);
return;
}
gtk_stack_set_visible_child_name(
GTK_STACK(
sidebar->evidence_content_stack
),
SIDEBAR_EVIDENCE_STATE_TREE
);
}
void sidebar_set_evidence_selection_callback(
Sidebar *sidebar,
SidebarEvidenceSelectionCallback callback,
gpointer user_data
)
{
if (sidebar == NULL)
{
return;
}
sidebar->evidence_selection_callback =
callback;
sidebar->evidence_selection_user_data =
user_data;
}
void sidebar_free(Sidebar *sidebar)
{
if (sidebar == NULL)
@ -218,6 +760,19 @@ void sidebar_free(Sidebar *sidebar)
investigation_tree_view_free(sidebar->tree_view);
if (sidebar->evidence_tree_view != NULL)
{
evidence_tree_view_set_selection_callback(
sidebar->evidence_tree_view,
NULL,
NULL
);
evidence_tree_view_free(
sidebar->evidence_tree_view
);
}
/*
* Les widgets GTK sont intégrés dans l'arbre de widgets de la fenêtre.
* GTK gère leur destruction lorsque la fenêtre est détruite.

View file

@ -0,0 +1,332 @@
/******************************************************************************
* @file test_evidence_category_item.c
* @brief Tests unitaires de EvidenceCategoryItem.
******************************************************************************/
#include "widgets/evidence_category_item.h"
#include <glib.h>
/**
* @brief Crée une preuve métier valide.
*/
static EvidenceRecord *test_evidence_category_item_create_record(void)
{
EvidenceRecord *evidence_record = NULL;
GError *error = NULL;
evidence_record =
evidence_record_new(
"550e8400-e29b-41d4-a716-446655440000",
"capture-instagram.png",
"550e8400-e29b-41d4-a716-446655440000.png",
"01_Preuves_Originales/Documents/"
"550e8400-e29b-41d4-a716-446655440000.png",
"image",
4096,
"00000000000000000000000000000000"
"00000000000000000000000000000000",
"2026-07-20T07:16:01Z",
"2026-07-19T18:30:00Z",
"Conversation Instagram",
"Capture d'écran reçue.",
EVIDENCE_INTEGRITY_STATUS_UNKNOWN,
&error
);
g_assert_no_error(
error
);
g_assert_nonnull(
evidence_record
);
return evidence_record;
}
/**
* @brief Vérifie la création d'une catégorie vide.
*/
static void test_evidence_category_item_new_valid(void)
{
EvidenceCategoryItem *evidence_category_item =
NULL;
evidence_category_item =
evidence_category_item_new(
" image ",
" Images "
);
g_assert_nonnull(
evidence_category_item
);
g_assert_cmpstr(
evidence_category_item_get_type_identifier(
evidence_category_item
),
==,
"image"
);
g_assert_cmpstr(
evidence_category_item_get_label(
evidence_category_item
),
==,
"Images"
);
g_assert_cmpuint(
evidence_category_item_get_count(
evidence_category_item
),
==,
0
);
g_assert_nonnull(
evidence_category_item_get_children(
evidence_category_item
)
);
g_assert_cmpuint(
g_list_model_get_item_type(
evidence_category_item_get_children(
evidence_category_item
)
),
==,
EVIDENCE_TYPE_LIST_ITEM
);
g_object_unref(
evidence_category_item
);
}
/**
* @brief Vérifie l'ajout et la propriété des preuves.
*/
static void test_evidence_category_item_append(void)
{
EvidenceCategoryItem *evidence_category_item =
NULL;
EvidenceRecord *evidence_record =
NULL;
EvidenceListItem *evidence_list_item =
NULL;
EvidenceListItem *stored_item =
NULL;
evidence_category_item =
evidence_category_item_new(
"image",
"Images"
);
evidence_record =
test_evidence_category_item_create_record();
evidence_list_item =
evidence_list_item_new(
evidence_record
);
g_assert_nonnull(
evidence_category_item
);
g_assert_nonnull(
evidence_list_item
);
g_assert_true(
evidence_category_item_append(
evidence_category_item,
evidence_list_item
)
);
/*
* La catégorie possède maintenant sa propre référence.
*/
g_object_unref(
evidence_list_item
);
evidence_record_free(
evidence_record
);
g_assert_cmpuint(
evidence_category_item_get_count(
evidence_category_item
),
==,
1
);
stored_item =
g_list_model_get_item(
evidence_category_item_get_children(
evidence_category_item
),
0
);
g_assert_nonnull(
stored_item
);
g_assert_cmpstr(
evidence_list_item_get_identifier(
stored_item
),
==,
"550e8400-e29b-41d4-a716-446655440000"
);
g_assert_cmpstr(
evidence_list_item_get_original_name(
stored_item
),
==,
"capture-instagram.png"
);
g_object_unref(
stored_item
);
g_object_unref(
evidence_category_item
);
}
/**
* @brief Vérifie les arguments invalides.
*/
static void test_evidence_category_item_invalid_arguments(void)
{
EvidenceCategoryItem *evidence_category_item =
NULL;
g_assert_null(
evidence_category_item_new(
NULL,
"Images"
)
);
g_assert_null(
evidence_category_item_new(
"image",
NULL
)
);
g_assert_null(
evidence_category_item_new(
" ",
"Images"
)
);
g_assert_null(
evidence_category_item_new(
"image",
" "
)
);
evidence_category_item =
evidence_category_item_new(
"image",
"Images"
);
g_assert_nonnull(
evidence_category_item
);
g_assert_false(
evidence_category_item_append(
evidence_category_item,
NULL
)
);
g_assert_false(
evidence_category_item_append(
NULL,
NULL
)
);
g_assert_null(
evidence_category_item_get_type_identifier(
NULL
)
);
g_assert_null(
evidence_category_item_get_label(
NULL
)
);
g_assert_cmpuint(
evidence_category_item_get_count(
NULL
),
==,
0
);
g_assert_null(
evidence_category_item_get_children(
NULL
)
);
g_clear_object(
&evidence_category_item
);
}
int main(
int argc,
char **argv
)
{
g_test_init(
&argc,
&argv,
NULL
);
g_test_add_func(
"/evidence-category-item/new-valid",
test_evidence_category_item_new_valid
);
g_test_add_func(
"/evidence-category-item/append",
test_evidence_category_item_append
);
g_test_add_func(
"/evidence-category-item/invalid-arguments",
test_evidence_category_item_invalid_arguments
);
return g_test_run();
}

View file

@ -0,0 +1,637 @@
/******************************************************************************
* @file test_evidence_category_model.c
* @brief Tests unitaires de EvidenceCategoryModel.
******************************************************************************/
#include "widgets/evidence_category_model.h"
#include "widgets/evidence_category_item.h"
#include "widgets/evidence_list_item.h"
#include "models/evidence_record.h"
#include <glib.h>
/**
* @brief Crée un élément d'affichage valide.
*/
static EvidenceListItem *test_evidence_category_model_create_item(
const char *identifier,
const char *original_name,
const char *type_identifier
)
{
EvidenceRecord *evidence_record = NULL;
EvidenceListItem *evidence_list_item = NULL;
char *internal_name = NULL;
char *relative_path = NULL;
GError *error = NULL;
internal_name =
g_strdup_printf(
"%s.bin",
identifier
);
relative_path =
g_strdup_printf(
"01_Preuves_Originales/Documents/%s.bin",
identifier
);
g_assert_nonnull(
internal_name
);
g_assert_nonnull(
relative_path
);
evidence_record =
evidence_record_new(
identifier,
original_name,
internal_name,
relative_path,
type_identifier,
4096,
"00000000000000000000000000000000"
"00000000000000000000000000000000",
"2026-07-20T07:16:01Z",
"2026-07-19T18:30:00Z",
NULL,
NULL,
EVIDENCE_INTEGRITY_STATUS_UNKNOWN,
&error
);
g_assert_no_error(
error
);
g_assert_nonnull(
evidence_record
);
evidence_list_item =
evidence_list_item_new(
evidence_record
);
g_assert_nonnull(
evidence_list_item
);
evidence_record_free(
evidence_record
);
g_free(
relative_path
);
g_free(
internal_name
);
return evidence_list_item;
}
/**
* @brief Ajoute une preuve à un GListStore.
*/
static void test_evidence_category_model_append_item(
GListStore *evidence_items,
EvidenceListItem *evidence_list_item
)
{
g_list_store_append(
evidence_items,
G_OBJECT(
evidence_list_item
)
);
g_object_unref(
evidence_list_item
);
}
/**
* @brief Crée la table des libellés de types.
*/
static GHashTable *test_evidence_category_model_create_labels(void)
{
GHashTable *type_labels =
NULL;
type_labels =
g_hash_table_new_full(
g_str_hash,
g_str_equal,
g_free,
g_free
);
g_hash_table_insert(
type_labels,
g_strdup("document"),
g_strdup("Documents")
);
g_hash_table_insert(
type_labels,
g_strdup("image"),
g_strdup("Images")
);
return type_labels;
}
/**
* @brief Vérifie la création d'un modèle vide.
*/
static void test_evidence_category_model_new_empty(void)
{
EvidenceCategoryModel *evidence_category_model =
NULL;
evidence_category_model =
evidence_category_model_new();
g_assert_nonnull(
evidence_category_model
);
g_assert_nonnull(
evidence_category_model_get_list_model(
evidence_category_model
)
);
g_assert_cmpuint(
evidence_category_model_get_count(
evidence_category_model
),
==,
0
);
g_assert_cmpuint(
g_list_model_get_item_type(
evidence_category_model_get_list_model(
evidence_category_model
)
),
==,
EVIDENCE_TYPE_CATEGORY_ITEM
);
evidence_category_model_free(
evidence_category_model
);
evidence_category_model_free(
NULL
);
}
/**
* @brief Vérifie le regroupement et le tri.
*/
static void test_evidence_category_model_group_and_sort(void)
{
EvidenceCategoryModel *evidence_category_model =
NULL;
GListStore *evidence_items =
NULL;
GHashTable *type_labels =
NULL;
EvidenceCategoryItem *documents_category =
NULL;
EvidenceCategoryItem *images_category =
NULL;
EvidenceListItem *first_image =
NULL;
evidence_category_model =
evidence_category_model_new();
evidence_items =
g_list_store_new(
EVIDENCE_TYPE_LIST_ITEM
);
type_labels =
test_evidence_category_model_create_labels();
test_evidence_category_model_append_item(
evidence_items,
test_evidence_category_model_create_item(
"550e8400-e29b-41d4-a716-446655440000",
"zeta.png",
"image"
)
);
test_evidence_category_model_append_item(
evidence_items,
test_evidence_category_model_create_item(
"550e8400-e29b-41d4-a716-446655440001",
"rapport.pdf",
"document"
)
);
test_evidence_category_model_append_item(
evidence_items,
test_evidence_category_model_create_item(
"550e8400-e29b-41d4-a716-446655440002",
"alpha.png",
"image"
)
);
g_assert_true(
evidence_category_model_replace(
evidence_category_model,
G_LIST_MODEL(
evidence_items
),
type_labels,
NULL
)
);
g_assert_cmpuint(
evidence_category_model_get_count(
evidence_category_model
),
==,
2
);
documents_category =
g_list_model_get_item(
evidence_category_model_get_list_model(
evidence_category_model
),
0
);
images_category =
g_list_model_get_item(
evidence_category_model_get_list_model(
evidence_category_model
),
1
);
g_assert_cmpstr(
evidence_category_item_get_label(
documents_category
),
==,
"Documents"
);
g_assert_cmpuint(
evidence_category_item_get_count(
documents_category
),
==,
1
);
g_assert_cmpstr(
evidence_category_item_get_label(
images_category
),
==,
"Images"
);
g_assert_cmpuint(
evidence_category_item_get_count(
images_category
),
==,
2
);
first_image =
g_list_model_get_item(
evidence_category_item_get_children(
images_category
),
0
);
g_assert_cmpstr(
evidence_list_item_get_original_name(
first_image
),
==,
"alpha.png"
);
g_object_unref(
first_image
);
g_object_unref(
images_category
);
g_object_unref(
documents_category
);
g_hash_table_unref(
type_labels
);
g_object_unref(
evidence_items
);
evidence_category_model_free(
evidence_category_model
);
}
/**
* @brief Vérifie le libellé de secours pour un type inconnu.
*/
static void test_evidence_category_model_unknown_type(void)
{
EvidenceCategoryModel *evidence_category_model =
NULL;
GListStore *evidence_items =
NULL;
EvidenceCategoryItem *category =
NULL;
evidence_category_model =
evidence_category_model_new();
evidence_items =
g_list_store_new(
EVIDENCE_TYPE_LIST_ITEM
);
test_evidence_category_model_append_item(
evidence_items,
test_evidence_category_model_create_item(
"550e8400-e29b-41d4-a716-446655440003",
"archive.bin",
"unknown"
)
);
g_assert_true(
evidence_category_model_replace(
evidence_category_model,
G_LIST_MODEL(
evidence_items
),
NULL,
NULL
)
);
category =
g_list_model_get_item(
evidence_category_model_get_list_model(
evidence_category_model
),
0
);
g_assert_cmpstr(
evidence_category_item_get_label(
category
),
==,
"unknown"
);
g_object_unref(
category
);
g_object_unref(
evidence_items
);
evidence_category_model_free(
evidence_category_model
);
}
/**
* @brief Vérifie les remplacements successifs et les erreurs.
*/
static void test_evidence_category_model_replace_and_clear(void)
{
EvidenceCategoryModel *evidence_category_model =
NULL;
GListStore *valid_items =
NULL;
GListStore *empty_items =
NULL;
GListStore *invalid_items =
NULL;
GObject *invalid_object =
NULL;
GError *error = NULL;
evidence_category_model =
evidence_category_model_new();
valid_items =
g_list_store_new(
EVIDENCE_TYPE_LIST_ITEM
);
empty_items =
g_list_store_new(
EVIDENCE_TYPE_LIST_ITEM
);
invalid_items =
g_list_store_new(
G_TYPE_OBJECT
);
test_evidence_category_model_append_item(
valid_items,
test_evidence_category_model_create_item(
"550e8400-e29b-41d4-a716-446655440004",
"preuve.txt",
"document"
)
);
g_assert_true(
evidence_category_model_replace(
evidence_category_model,
G_LIST_MODEL(
valid_items
),
NULL,
NULL
)
);
invalid_object =
g_object_new(
G_TYPE_OBJECT,
NULL
);
g_list_store_append(
invalid_items,
invalid_object
);
g_object_unref(
invalid_object
);
g_assert_false(
evidence_category_model_replace(
evidence_category_model,
G_LIST_MODEL(
invalid_items
),
NULL,
&error
)
);
g_assert_error(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_INVALID_ARGUMENT
);
g_clear_error(
&error
);
/*
* L'ancien contenu est conservé après l'échec.
*/
g_assert_cmpuint(
evidence_category_model_get_count(
evidence_category_model
),
==,
1
);
g_assert_true(
evidence_category_model_replace(
evidence_category_model,
G_LIST_MODEL(
empty_items
),
NULL,
NULL
)
);
g_assert_cmpuint(
evidence_category_model_get_count(
evidence_category_model
),
==,
0
);
g_assert_false(
evidence_category_model_replace(
NULL,
G_LIST_MODEL(
valid_items
),
NULL,
&error
)
);
g_assert_error(
error,
EVIDENCE_CATEGORY_MODEL_ERROR,
EVIDENCE_CATEGORY_MODEL_ERROR_INVALID_ARGUMENT
);
g_clear_error(
&error
);
g_object_unref(
invalid_items
);
g_object_unref(
empty_items
);
g_object_unref(
valid_items
);
evidence_category_model_free(
evidence_category_model
);
}
int main(
int argc,
char **argv
)
{
g_test_init(
&argc,
&argv,
NULL
);
g_test_add_func(
"/evidence-category-model/new-empty",
test_evidence_category_model_new_empty
);
g_test_add_func(
"/evidence-category-model/group-and-sort",
test_evidence_category_model_group_and_sort
);
g_test_add_func(
"/evidence-category-model/unknown-type",
test_evidence_category_model_unknown_type
);
g_test_add_func(
"/evidence-category-model/replace-and-clear",
test_evidence_category_model_replace_and_clear
);
return g_test_run();
}

View file

@ -0,0 +1,336 @@
/******************************************************************************
* @file test_evidence_list_item.c
* @brief Tests unitaires de EvidenceListItem.
******************************************************************************/
#include "widgets/evidence_list_item.h"
#include <glib.h>
/**
* @brief Crée une preuve métier valide pour les tests.
*/
static EvidenceRecord *test_evidence_list_item_create_record(
const char *collected_at,
const char *source,
EvidenceIntegrityStatus integrity_status
)
{
EvidenceRecord *evidence_record = NULL;
GError *error = NULL;
evidence_record =
evidence_record_new(
"550e8400-e29b-41d4-a716-446655440000",
"capture_originale.png",
"550e8400-e29b-41d4-a716-446655440000.png",
"01_Preuves_Originales/Documents/"
"550e8400-e29b-41d4-a716-446655440000.png",
"image",
4096,
"00000000000000000000000000000000"
"00000000000000000000000000000000",
"2026-07-20T07:16:01Z",
collected_at,
source,
"Capture reçue pendant la conversation.",
integrity_status,
&error
);
g_assert_no_error(
error
);
g_assert_nonnull(
evidence_record
);
return evidence_record;
}
/**
* @brief Vérifie la copie des valeurs nominales.
*/
static void test_evidence_list_item_new_valid(void)
{
EvidenceRecord *evidence_record = NULL;
EvidenceListItem *evidence_list_item = NULL;
evidence_record =
test_evidence_list_item_create_record(
"2026-07-19T18:30:00Z",
"Conversation Instagram",
EVIDENCE_INTEGRITY_STATUS_VALID
);
evidence_list_item =
evidence_list_item_new(
evidence_record
);
g_assert_nonnull(
evidence_list_item
);
g_assert_cmpstr(
evidence_list_item_get_original_name(
evidence_list_item
),
==,
"capture_originale.png"
);
g_assert_cmpstr(
evidence_list_item_get_type_identifier(
evidence_list_item
),
==,
"image"
);
g_assert_cmpuint(
evidence_list_item_get_size_bytes(
evidence_list_item
),
==,
4096
);
g_assert_cmpstr(
evidence_list_item_get_collected_at(
evidence_list_item
),
==,
"2026-07-19T18:30:00Z"
);
g_assert_cmpstr(
evidence_list_item_get_source(
evidence_list_item
),
==,
"Conversation Instagram"
);
g_assert_cmpint(
evidence_list_item_get_integrity_status(
evidence_list_item
),
==,
EVIDENCE_INTEGRITY_STATUS_VALID
);
g_assert_cmpstr(
evidence_list_item_get_integrity_status_label(
evidence_list_item
),
==,
"Valide"
);
g_object_unref(
evidence_list_item
);
evidence_record_free(
evidence_record
);
}
/**
* @brief Vérifie que les chaînes ne dépendent plus du modèle métier.
*/
static void test_evidence_list_item_copies_strings(void)
{
EvidenceRecord *evidence_record = NULL;
EvidenceListItem *evidence_list_item = NULL;
evidence_record =
test_evidence_list_item_create_record(
"2026-07-19T18:30:00Z",
"Email reçu",
EVIDENCE_INTEGRITY_STATUS_UNKNOWN
);
evidence_list_item =
evidence_list_item_new(
evidence_record
);
g_assert_nonnull(
evidence_list_item
);
evidence_record_free(
evidence_record
);
g_assert_cmpstr(
evidence_list_item_get_original_name(
evidence_list_item
),
==,
"capture_originale.png"
);
g_assert_cmpstr(
evidence_list_item_get_source(
evidence_list_item
),
==,
"Email reçu"
);
g_object_unref(
evidence_list_item
);
}
/**
* @brief Vérifie les valeurs facultatives absentes.
*/
static void test_evidence_list_item_optional_values(void)
{
EvidenceRecord *evidence_record = NULL;
EvidenceListItem *evidence_list_item = NULL;
evidence_record =
test_evidence_list_item_create_record(
NULL,
NULL,
EVIDENCE_INTEGRITY_STATUS_UNKNOWN
);
evidence_list_item =
evidence_list_item_new(
evidence_record
);
g_assert_nonnull(
evidence_list_item
);
g_assert_null(
evidence_list_item_get_collected_at(
evidence_list_item
)
);
g_assert_null(
evidence_list_item_get_source(
evidence_list_item
)
);
g_assert_cmpstr(
evidence_list_item_get_integrity_status_label(
evidence_list_item
),
==,
"Non vérifiée"
);
g_object_unref(
evidence_list_item
);
evidence_record_free(
evidence_record
);
}
/**
* @brief Vérifie les arguments NULL.
*/
static void test_evidence_list_item_null_values(void)
{
EvidenceListItem *evidence_list_item = NULL;
g_assert_null(
evidence_list_item_new(
NULL
)
);
g_assert_null(
evidence_list_item_get_original_name(
NULL
)
);
g_assert_null(
evidence_list_item_get_type_identifier(
NULL
)
);
g_assert_cmpuint(
evidence_list_item_get_size_bytes(
NULL
),
==,
0
);
g_assert_null(
evidence_list_item_get_collected_at(
NULL
)
);
g_assert_null(
evidence_list_item_get_source(
NULL
)
);
g_assert_cmpint(
evidence_list_item_get_integrity_status(
NULL
),
==,
EVIDENCE_INTEGRITY_STATUS_UNKNOWN
);
/*
* g_clear_object() accepte un pointeur contenant NULL.
*/
g_clear_object(
&evidence_list_item
);
}
int main(
int argc,
char **argv
)
{
g_test_init(
&argc,
&argv,
NULL
);
g_test_add_func(
"/evidence-list-item/new-valid",
test_evidence_list_item_new_valid
);
g_test_add_func(
"/evidence-list-item/copies-strings",
test_evidence_list_item_copies_strings
);
g_test_add_func(
"/evidence-list-item/optional-values",
test_evidence_list_item_optional_values
);
g_test_add_func(
"/evidence-list-item/null-values",
test_evidence_list_item_null_values
);
return g_test_run();
}

View file

@ -0,0 +1,592 @@
/******************************************************************************
* @file test_evidence_list_model.c
* @brief Tests unitaires de EvidenceListModel.
******************************************************************************/
#include "widgets/evidence_list_model.h"
#include "widgets/evidence_list_item.h"
#include <glib.h>
/**
* @brief Crée une preuve valide pour les tests.
*/
static EvidenceRecord *test_evidence_list_model_create_record(
const char *uuid,
const char *original_name,
const char *source
)
{
EvidenceRecord *evidence_record = NULL;
char *internal_name = NULL;
char *relative_path = NULL;
GError *error = NULL;
internal_name =
g_strdup_printf(
"%s.bin",
uuid
);
relative_path =
g_strdup_printf(
"01_Preuves_Originales/Documents/%s.bin",
uuid
);
g_assert_nonnull(
internal_name
);
g_assert_nonnull(
relative_path
);
evidence_record =
evidence_record_new(
uuid,
original_name,
internal_name,
relative_path,
"document",
4096,
"00000000000000000000000000000000"
"00000000000000000000000000000000",
"2026-07-20T07:16:01Z",
"2026-07-19T18:30:00Z",
source,
"Description de test.",
EVIDENCE_INTEGRITY_STATUS_UNKNOWN,
&error
);
g_assert_no_error(
error
);
g_assert_nonnull(
evidence_record
);
g_free(
relative_path
);
g_free(
internal_name
);
return evidence_record;
}
/**
* @brief Crée un tableau qui libère automatiquement ses preuves.
*/
static GPtrArray *test_evidence_list_model_create_records(void)
{
return g_ptr_array_new_with_free_func(
(GDestroyNotify) evidence_record_free
);
}
/**
* @brief Vérifie la création d'un modèle vide.
*/
static void test_evidence_list_model_new_empty(void)
{
EvidenceListModel *evidence_list_model =
NULL;
GListModel *list_model =
NULL;
evidence_list_model =
evidence_list_model_new();
g_assert_nonnull(
evidence_list_model
);
list_model =
evidence_list_model_get_list_model(
evidence_list_model
);
g_assert_nonnull(
list_model
);
g_assert_cmpuint(
evidence_list_model_get_count(
evidence_list_model
),
==,
0
);
g_assert_cmpuint(
g_list_model_get_item_type(
list_model
),
==,
EVIDENCE_TYPE_LIST_ITEM
);
evidence_list_model_free(
evidence_list_model
);
evidence_list_model_free(
NULL
);
}
/**
* @brief Vérifie l'alimentation nominale du modèle.
*/
static void test_evidence_list_model_replace_records(void)
{
EvidenceListModel *evidence_list_model =
NULL;
GPtrArray *evidence_records =
NULL;
EvidenceListItem *first_item =
NULL;
evidence_list_model =
evidence_list_model_new();
evidence_records =
test_evidence_list_model_create_records();
g_ptr_array_add(
evidence_records,
test_evidence_list_model_create_record(
"550e8400-e29b-41d4-a716-446655440000",
"premiere-preuve.pdf",
"Email reçu"
)
);
g_ptr_array_add(
evidence_records,
test_evidence_list_model_create_record(
"550e8400-e29b-41d4-a716-446655440001",
"deuxieme-preuve.png",
"Conversation Instagram"
)
);
g_assert_true(
evidence_list_model_replace(
evidence_list_model,
evidence_records,
NULL
)
);
g_assert_cmpuint(
evidence_list_model_get_count(
evidence_list_model
),
==,
2
);
first_item =
g_list_model_get_item(
evidence_list_model_get_list_model(
evidence_list_model
),
0
);
g_assert_nonnull(
first_item
);
g_assert_cmpstr(
evidence_list_item_get_original_name(
first_item
),
==,
"premiere-preuve.pdf"
);
g_object_unref(
first_item
);
/*
* La liste doit rester valide après libération des EvidenceRecord.
*/
g_ptr_array_unref(
evidence_records
);
first_item =
g_list_model_get_item(
evidence_list_model_get_list_model(
evidence_list_model
),
0
);
g_assert_cmpstr(
evidence_list_item_get_source(
first_item
),
==,
"Email reçu"
);
g_object_unref(
first_item
);
evidence_list_model_free(
evidence_list_model
);
}
/**
* @brief Vérifie qu'un rafraîchissement remplace l'ancien contenu.
*/
static void test_evidence_list_model_successive_replacements(void)
{
EvidenceListModel *evidence_list_model =
NULL;
GPtrArray *first_records =
NULL;
GPtrArray *second_records =
NULL;
EvidenceListItem *remaining_item =
NULL;
evidence_list_model =
evidence_list_model_new();
first_records =
test_evidence_list_model_create_records();
second_records =
test_evidence_list_model_create_records();
g_ptr_array_add(
first_records,
test_evidence_list_model_create_record(
"550e8400-e29b-41d4-a716-446655440002",
"ancienne-preuve.txt",
NULL
)
);
g_ptr_array_add(
second_records,
test_evidence_list_model_create_record(
"550e8400-e29b-41d4-a716-446655440003",
"nouvelle-preuve.txt",
NULL
)
);
g_assert_true(
evidence_list_model_replace(
evidence_list_model,
first_records,
NULL
)
);
g_assert_true(
evidence_list_model_replace(
evidence_list_model,
second_records,
NULL
)
);
g_assert_cmpuint(
evidence_list_model_get_count(
evidence_list_model
),
==,
1
);
remaining_item =
g_list_model_get_item(
evidence_list_model_get_list_model(
evidence_list_model
),
0
);
g_assert_cmpstr(
evidence_list_item_get_original_name(
remaining_item
),
==,
"nouvelle-preuve.txt"
);
g_object_unref(
remaining_item
);
g_ptr_array_unref(
second_records
);
g_ptr_array_unref(
first_records
);
evidence_list_model_free(
evidence_list_model
);
}
/**
* @brief Vérifie qu'un tableau vide efface le modèle.
*/
static void test_evidence_list_model_replace_empty(void)
{
EvidenceListModel *evidence_list_model =
NULL;
GPtrArray *evidence_records =
NULL;
GPtrArray *empty_records =
NULL;
evidence_list_model =
evidence_list_model_new();
evidence_records =
test_evidence_list_model_create_records();
empty_records =
test_evidence_list_model_create_records();
g_ptr_array_add(
evidence_records,
test_evidence_list_model_create_record(
"550e8400-e29b-41d4-a716-446655440004",
"preuve-temporaire.txt",
NULL
)
);
g_assert_true(
evidence_list_model_replace(
evidence_list_model,
evidence_records,
NULL
)
);
g_assert_true(
evidence_list_model_replace(
evidence_list_model,
empty_records,
NULL
)
);
g_assert_cmpuint(
evidence_list_model_get_count(
evidence_list_model
),
==,
0
);
g_ptr_array_unref(
empty_records
);
g_ptr_array_unref(
evidence_records
);
evidence_list_model_free(
evidence_list_model
);
}
/**
* @brief Vérifie les arguments invalides et le remplacement atomique.
*/
static void test_evidence_list_model_invalid_records(void)
{
EvidenceListModel *evidence_list_model =
NULL;
GPtrArray *valid_records =
NULL;
GPtrArray *invalid_records =
NULL;
GError *error = NULL;
evidence_list_model =
evidence_list_model_new();
valid_records =
test_evidence_list_model_create_records();
invalid_records =
test_evidence_list_model_create_records();
g_ptr_array_add(
valid_records,
test_evidence_list_model_create_record(
"550e8400-e29b-41d4-a716-446655440005",
"preuve-conservee.txt",
NULL
)
);
g_assert_true(
evidence_list_model_replace(
evidence_list_model,
valid_records,
NULL
)
);
/*
* NULL représente ici une ligne métier invalide.
*/
g_ptr_array_add(
invalid_records,
NULL
);
g_assert_false(
evidence_list_model_replace(
evidence_list_model,
invalid_records,
&error
)
);
g_assert_error(
error,
EVIDENCE_LIST_MODEL_ERROR,
EVIDENCE_LIST_MODEL_ERROR_INVALID_RECORD
);
g_clear_error(
&error
);
/*
* L'ancien élément doit toujours être présent.
*/
g_assert_cmpuint(
evidence_list_model_get_count(
evidence_list_model
),
==,
1
);
g_assert_false(
evidence_list_model_replace(
NULL,
valid_records,
&error
)
);
g_assert_error(
error,
EVIDENCE_LIST_MODEL_ERROR,
EVIDENCE_LIST_MODEL_ERROR_INVALID_ARGUMENT
);
g_clear_error(
&error
);
g_assert_false(
evidence_list_model_replace(
evidence_list_model,
NULL,
&error
)
);
g_assert_error(
error,
EVIDENCE_LIST_MODEL_ERROR,
EVIDENCE_LIST_MODEL_ERROR_INVALID_ARGUMENT
);
g_clear_error(
&error
);
g_ptr_array_unref(
invalid_records
);
g_ptr_array_unref(
valid_records
);
evidence_list_model_free(
evidence_list_model
);
}
int main(
int argc,
char **argv
)
{
g_test_init(
&argc,
&argv,
NULL
);
g_test_add_func(
"/evidence-list-model/new-empty",
test_evidence_list_model_new_empty
);
g_test_add_func(
"/evidence-list-model/replace-records",
test_evidence_list_model_replace_records
);
g_test_add_func(
"/evidence-list-model/successive-replacements",
test_evidence_list_model_successive_replacements
);
g_test_add_func(
"/evidence-list-model/replace-empty",
test_evidence_list_model_replace_empty
);
g_test_add_func(
"/evidence-list-model/invalid-records",
test_evidence_list_model_invalid_records
);
return g_test_run();
}