From 3a2fbd260dbe1f2ecbfa71b389d4602d4e9f3fd8 Mon Sep 17 00:00:00 2001 From: grayTerminal-sh Date: Mon, 13 Jul 2026 11:44:13 +0200 Subject: [PATCH] feat(core): create investigation tree model --- docs/DEVELOPMENT.md | 35 ++++ docs/tickets/closed/TICKET-008.md | 158 ++++++++++++++++++ .../include/core/investigation_tree_model.h | 60 +++++++ .../src/core/investigation_tree_model.c | 76 +++++++++ .../tests/test_investigation_tree_model | Bin 0 -> 16928 bytes .../tests/test_investigation_tree_model.c | 90 ++++++++++ 6 files changed, 419 insertions(+) create mode 100644 docs/tickets/closed/TICKET-008.md create mode 100644 labfy-investigation/include/core/investigation_tree_model.h create mode 100644 labfy-investigation/src/core/investigation_tree_model.c create mode 100755 labfy-investigation/tests/test_investigation_tree_model create mode 100644 labfy-investigation/tests/test_investigation_tree_model.c diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index c5f76f9..7def7f6 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -639,6 +639,41 @@ Lorsque plusieurs solutions existent, la préférence est donnée à celle qui f --- +## Gestion de la mémoire + +Le projet applique une règle unique concernant la propriété des ressources. + +> Le propriétaire crée. +> Le propriétaire détruit. + +Lorsqu'une ressource est transmise à un objet qui en devient propriétaire, le code appelant ne doit plus la libérer. + +Chaque module est responsable uniquement des ressources qu'il possède. + +Cette règle s'applique à toutes les structures du projet. + +--- + +## Bibliothèques autorisées + +Le projet privilégie les bibliothèques éprouvées plutôt que des réimplémentations. + +### Couche Core + +Autorisé : + +- Langage C17 +- GLib +- SQLite + +Interdit : + +- GTK + +Les structures de données fournies par GLib (GPtrArray, GHashTable, GList, etc.) doivent être privilégiées lorsqu'elles répondent au besoin du projet. + +--- + # Branche principale La branche `main` est toujours stable. diff --git a/docs/tickets/closed/TICKET-008.md b/docs/tickets/closed/TICKET-008.md new file mode 100644 index 0000000..11cb581 --- /dev/null +++ b/docs/tickets/closed/TICKET-008.md @@ -0,0 +1,158 @@ +# Ticket #008 + +## Titre + +Créer le module `InvestigationTreeModel`. + +--- + +## Objectif + +Créer une structure métier représentant le modèle de l'arborescence d'une enquête. + +Le modèle sera propriétaire du nœud racine et servira de base au futur explorateur d'enquête. + +--- + +## Responsabilités + +Le module `InvestigationTreeModel` doit : + +- posséder un nœud racine ; +- gérer le cycle de vie du modèle ; +- exposer le nœud racine en lecture seule. + +--- + +## Hors périmètre + +Ce ticket ne doit pas : + +- parcourir le système de fichiers ; +- créer les enfants d'un nœud ; +- afficher une interface GTK ; +- communiquer avec SQLite ; +- charger une enquête. + +--- + +## Architecture + +```text +Investigation + │ + ▼ +InvestigationTreeModel + │ + ▼ +InvestigationNode +``` + +Le module appartient à la couche **Core**. + +Aucune dépendance vers GTK. + +--- + +## Principe de propriété + +Le module est **propriétaire** du nœud racine. + +À partir du moment où un `InvestigationNode` est transmis au constructeur : + +```c +investigation_tree_model_new(root_node); +``` + +le modèle devient responsable de sa destruction. + +Le code appelant ne doit plus appeler : + +```c +investigation_node_free(root_node); +``` + +La destruction du modèle doit automatiquement détruire son nœud racine. + +--- + +## Fichiers concernés + +```text +include/core/investigation_tree_model.h +src/core/investigation_tree_model.c +``` + +--- + +## Interface publique attendue + +```c +InvestigationTreeModel *investigation_tree_model_new( + InvestigationNode *root_node +); + +void investigation_tree_model_free( + InvestigationTreeModel *tree_model +); + +const InvestigationNode *investigation_tree_model_get_root( + const InvestigationTreeModel *tree_model +); +``` + +--- + +## Comportement attendu + +Le modèle contient uniquement un nœud racine. + +Exemple : + +```text +Template +``` + +Les enfants seront ajoutés dans un ticket ultérieur. + +--- + +## Contraintes techniques + +- C17 +- Structure opaque +- Aucun état global +- Aucune dépendance GTK +- Documentation Doxygen +- Compilation sans warning +- Respect des conventions du projet + +--- + +## Critères d'acceptation + +- [ ] Le projet compile sans warning. +- [ ] Le modèle est opaque. +- [ ] Le modèle possède un nœud racine. +- [ ] Le getter retourne le nœud racine. +- [ ] La destruction du modèle détruit également le nœud racine. +- [ ] Aucun code GTK. +- [ ] Aucun code SQLite. + +--- + +## Tests + +- création d'un modèle valide ; +- lecture du nœud racine ; +- destruction du modèle ; +- création avec un nœud NULL ; +- destruction avec NULL. + +--- + +## Commit attendu + +```text +feat(core): create investigation tree model +``` diff --git a/labfy-investigation/include/core/investigation_tree_model.h b/labfy-investigation/include/core/investigation_tree_model.h new file mode 100644 index 0000000..af5754e --- /dev/null +++ b/labfy-investigation/include/core/investigation_tree_model.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * @file investigation_tree_model.h + * @brief Interface publique du modèle d'arborescence d'une enquête. + ******************************************************************************/ + +#ifndef LABFY_INVESTIGATION_INVESTIGATION_TREE_MODEL_H +#define LABFY_INVESTIGATION_INVESTIGATION_TREE_MODEL_H + +#include "core/investigation_node.h" + +/** + * @brief Représentation opaque du modèle d'arborescence. + * + * La structure réelle est définie dans investigation_tree_model.c. + * Les autres modules manipulent uniquement un pointeur vers + * InvestigationTreeModel. + */ +typedef struct InvestigationTreeModel InvestigationTreeModel; + +/** + * @brief Crée un nouveau modèle d'arborescence. + * + * Le modèle devient propriétaire de root_node après cet appel. + * Le code appelant ne doit donc plus libérer root_node directement. + * + * @param root_node Nœud racine transféré au modèle. + * + * @return Un nouveau modèle, ou NULL si root_node vaut NULL + * ou si la création échoue. + */ +InvestigationTreeModel *investigation_tree_model_new( + InvestigationNode *root_node +); + +/** + * @brief Libère le modèle et le nœud racine qu'il possède. + * + * Cette fonction accepte NULL. + * + * @param tree_model Modèle à libérer. + */ +void investigation_tree_model_free( + InvestigationTreeModel *tree_model +); + +/** + * @brief Retourne le nœud racine du modèle. + * + * Le pointeur retourné appartient toujours au modèle. + * Il ne doit être ni modifié ni libéré par le code appelant. + * + * @param tree_model Modèle à consulter. + * + * @return Nœud racine en lecture seule, ou NULL si tree_model vaut NULL. + */ +const InvestigationNode *investigation_tree_model_get_root( + const InvestigationTreeModel *tree_model +); + +#endif diff --git a/labfy-investigation/src/core/investigation_tree_model.c b/labfy-investigation/src/core/investigation_tree_model.c new file mode 100644 index 0000000..d450cc6 --- /dev/null +++ b/labfy-investigation/src/core/investigation_tree_model.c @@ -0,0 +1,76 @@ +/****************************************************************************** + * @file investigation_tree_model.c + * @brief Implémentation du modèle d'arborescence d'une enquête. + ******************************************************************************/ + +#include "core/investigation_tree_model.h" + +#include + +/** + * @struct InvestigationTreeModel + * @brief Représentation interne du modèle. + */ +struct InvestigationTreeModel +{ + InvestigationNode *root_node; +}; + +InvestigationTreeModel *investigation_tree_model_new( + InvestigationNode *root_node +) +{ + InvestigationTreeModel *tree_model = NULL; + + if (root_node == NULL) + { + return NULL; + } + + tree_model = g_new0( + InvestigationTreeModel, + 1 + ); + + if (tree_model == NULL) + { + return NULL; + } + + /* + * Ownership transferred. + * + * Le modèle devient propriétaire du nœud racine. + */ + tree_model->root_node = root_node; + + return tree_model; +} + +void investigation_tree_model_free( + InvestigationTreeModel *tree_model +) +{ + if (tree_model == NULL) + { + return; + } + + investigation_node_free( + tree_model->root_node + ); + + g_free(tree_model); +} + +const InvestigationNode *investigation_tree_model_get_root( + const InvestigationTreeModel *tree_model +) +{ + if (tree_model == NULL) + { + return NULL; + } + + return tree_model->root_node; +} diff --git a/labfy-investigation/tests/test_investigation_tree_model b/labfy-investigation/tests/test_investigation_tree_model new file mode 100755 index 0000000000000000000000000000000000000000..e88c7dc6b09cca3dc4c4b24b725ca09467e18473 GIT binary patch literal 16928 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 +#include +#include + +/** + * @brief Vérifie la création d'un modèle valide. + */ +static void test_tree_model_creation(void) +{ + InvestigationNode *root_node = NULL; + InvestigationTreeModel *tree_model = NULL; + const InvestigationNode *returned_node = NULL; + + root_node = investigation_node_new( + "Template", + INVESTIGATION_NODE_DIRECTORY + ); + + assert(root_node != NULL); + + tree_model = investigation_tree_model_new(root_node); + + assert(tree_model != NULL); + + returned_node = investigation_tree_model_get_root(tree_model); + + assert(returned_node != NULL); + + assert( + strcmp( + investigation_node_get_name(returned_node), + "Template" + ) == 0 + ); + + assert( + investigation_node_get_type(returned_node) == + INVESTIGATION_NODE_DIRECTORY + ); + + /* + * tree_model est propriétaire de root_node. + */ + investigation_tree_model_free(tree_model); +} + +/** + * @brief Vérifie les paramètres invalides. + */ +static void test_invalid_parameters(void) +{ + assert( + investigation_tree_model_new(NULL) == NULL + ); + + assert( + investigation_tree_model_get_root(NULL) == NULL + ); +} + +/** + * @brief Vérifie que free(NULL) est accepté. + */ +static void test_null_free(void) +{ + investigation_tree_model_free(NULL); +} + +int main(void) +{ + test_tree_model_creation(); + + test_invalid_parameters(); + + test_null_free(); + + printf( + "InvestigationTreeModel : tous les tests sont valides.\n" + ); + + return 0; +}