diff --git a/docs/tickets/closed/TICKET-017.md b/docs/tickets/closed/TICKET-017.md new file mode 100644 index 0000000..e2c234f --- /dev/null +++ b/docs/tickets/closed/TICKET-017.md @@ -0,0 +1,241 @@ +# Ticket #017 + +## Titre + +Ajouter le chemin complet à `InvestigationNode`. + +--- + +## Objectif + +Faire évoluer `InvestigationNode` afin que chaque nœud connaisse le chemin +complet qu'il représente sur le système de fichiers. + +Cette information permettra ensuite au `Workspace` et aux futurs visualiseurs +de savoir exactement quel fichier ou dossier est sélectionné. + +--- + +## Responsabilités + +Le module `InvestigationNode` doit : + +- mémoriser un chemin complet ; +- posséder sa propre copie de ce chemin ; +- exposer le chemin en lecture seule ; +- libérer correctement cette chaîne lors de sa destruction. + +Le module `InvestigationTreeBuilder` doit : + +- fournir le chemin complet lors de la création de chaque nœud ; +- construire correctement le chemin des enfants ; +- ne jamais reconstruire le chemin dans la GUI. + +--- + +## Architecture + +```text +InvestigationTreeBuilder + │ + ▼ +InvestigationNode +├── name +├── path +├── type +├── parent +└── children +``` + +Le chemin appartient au Core. + +La couche graphique ne fait que le lire. + +--- + +## Fichiers concernés + +```text +include/core/investigation_node.h +src/core/investigation_node.c + +src/core/investigation_tree_builder.c + +tests/test_investigation_node.c +tests/test_investigation_tree_builder.c +``` + +Aucune modification graphique dans ce ticket. + +--- + +## API publique à faire évoluer + +Le constructeur devient : + +```c +InvestigationNode *investigation_node_new( + const char *name, + const char *path, + InvestigationNodeType type +); +``` + +Un nouveau getter est ajouté : + +```c +const char *investigation_node_get_path( + const InvestigationNode *node +); +``` + +--- + +## Principe de propriété + +Le nœud copie le chemin reçu avec une allocation dédiée. + +Après : + +```c +node = investigation_node_new( + "Enquete.sqlite", + "/home/fy59/Enquetes/Test/00_BaseDeDonnees/Enquete.sqlite", + INVESTIGATION_NODE_FILE +); +``` + +le code appelant peut modifier ou libérer ses propres chaînes. + +Le nœud reste propriétaire de ses copies internes. + +--- + +## Structure interne attendue + +```c +struct InvestigationNode +{ + char *name; + char *path; + InvestigationNodeType type; + InvestigationNode *parent; + GPtrArray *children; +}; +``` + +La structure reste opaque. + +--- + +## Comportement attendu + +Pour le dossier racine : + +```text +Nom : +Test + +Chemin : +/home/fy59/Enquetes/Test +``` + +Pour un fichier enfant : + +```text +Nom : +Enquete.sqlite + +Chemin : +/home/fy59/Enquetes/Test/00_BaseDeDonnees/Enquete.sqlite +``` + +--- + +## Règles de validation + +Le constructeur doit refuser : + +- `name == NULL` ; +- un nom vide ; +- `path == NULL` ; +- un chemin vide. + +Le getter doit retourner : + +```c +NULL +``` + +si le nœud vaut `NULL`. + +--- + +## Hors périmètre + +Ce ticket ne doit pas : + +- afficher le chemin dans le `Workspace` ; +- ouvrir un fichier ; +- normaliser les permissions ; +- résoudre les liens symboliques ; +- calculer un chemin relatif ; +- modifier le système de fichiers ; +- ajouter des métadonnées. + +--- + +## Contraintes techniques + +- C17 ; +- GLib autorisée ; +- aucune dépendance GTK ; +- structure opaque ; +- aucun état global ; +- documentation Doxygen ; +- compilation sans warning ; +- aucune fuite mémoire. + +--- + +## Critères d'acceptation + +- [ ] Le projet compile sans warning. +- [ ] `InvestigationNode` possède un chemin. +- [ ] Le chemin est copié. +- [ ] Le getter retourne le bon chemin. +- [ ] Les chemins racine et enfants sont corrects. +- [ ] Les noms invalides sont refusés. +- [ ] Les chemins invalides sont refusés. +- [ ] Le builder construit correctement tous les chemins. +- [ ] Tous les tests existants sont adaptés. +- [ ] `make test` reste entièrement valide. +- [ ] Aucun code GTK n'est modifié. + +--- + +## Tests + +### InvestigationNode + +- créer un nœud avec un chemin valide ; +- lire le chemin ; +- tester `path == NULL` ; +- tester un chemin vide ; +- vérifier que la chaîne est copiée ; +- tester `investigation_node_get_path(NULL)`. + +### InvestigationTreeBuilder + +- vérifier le chemin de la racine ; +- vérifier le chemin de `DirectoryA` ; +- vérifier le chemin de `FileA.txt` ; +- vérifier le chemin de `RootFile.md`. + +--- + +## Commit attendu + +```text +feat(core): add path to investigation nodes +``` diff --git a/include/core/investigation_node.h b/include/core/investigation_node.h index 794fe34..38b3890 100644 --- a/include/core/investigation_node.h +++ b/include/core/investigation_node.h @@ -6,21 +6,16 @@ #ifndef LABFY_INVESTIGATION_INVESTIGATION_NODE_H #define LABFY_INVESTIGATION_INVESTIGATION_NODE_H - #include #include /** - * @brief Représentation opaque d'un nœud d'enquête. - * - * La structure réelle est définie dans investigation_node.c. - * Les autres modules manipulent uniquement un pointeur vers - * InvestigationNode. + * @brief Représentation opaque d'un nœud. */ typedef struct InvestigationNode InvestigationNode; /** - * @brief Type d'un nœud de l'arborescence. + * @brief Type d'un nœud. */ typedef enum { @@ -29,24 +24,26 @@ typedef enum } InvestigationNodeType; /** - * @brief Crée un nouveau nœud d'enquête. + * @brief Crée un nouveau nœud. * - * Le nom fourni est copié. Le code appelant peut donc modifier ou libérer - * sa chaîne après l'appel sans affecter le nœud. + * Le nom et le chemin sont copiés. + * Le code appelant peut donc modifier ou libérer ses chaînes après l'appel. * * @param name Nom du fichier ou du dossier. + * @param path Chemin complet du fichier ou du dossier. * @param type Type du nœud. * - * @return Un nouveau nœud, ou NULL si le nom est invalide ou si la création - * échoue. + * @return Un nouveau nœud, ou NULL si le nom ou le chemin est invalide, + * ou si une allocation échoue. */ InvestigationNode *investigation_node_new( const char *name, + const char *path, InvestigationNodeType type ); /** - * @brief Libère les ressources associées à un nœud. + * @brief Libère un nœud et tous ses enfants. * * Cette fonction accepte NULL. * @@ -57,21 +54,35 @@ void investigation_node_free( ); /** - * @brief Retourne le nom du nœud. + * @brief Retourne le nom d'un nœud. * - * La chaîne retournée appartient au nœud et ne doit pas être modifiée - * ni libérée par le code appelant. + * La chaîne retournée appartient au nœud. + * Elle ne doit être ni modifiée ni libérée. * * @param node Nœud à consulter. * - * @return Le nom en lecture seule, ou NULL si node vaut NULL. + * @return Le nom du nœud, ou NULL si node vaut NULL. */ const char *investigation_node_get_name( const InvestigationNode *node ); /** - * @brief Retourne le type du nœud. + * @brief Retourne le chemin complet d'un nœud. + * + * La chaîne retournée appartient au nœud. + * Elle ne doit être ni modifiée ni libérée. + * + * @param node Nœud à consulter. + * + * @return Le chemin complet, ou NULL si node vaut NULL. + */ +const char *investigation_node_get_path( + const InvestigationNode *node +); + +/** + * @brief Retourne le type d'un nœud. * * @param node Nœud à consulter. * @@ -86,7 +97,10 @@ InvestigationNodeType investigation_node_get_type( * * En cas de succès, le parent devient propriétaire de l'enfant. * - * @return true si l'ajout a réussi. + * @param parent Nœud parent. + * @param child Nœud enfant. + * + * @return true si l'ajout a réussi, sinon false. */ bool investigation_node_add_child( InvestigationNode *parent, @@ -94,9 +108,12 @@ bool investigation_node_add_child( ); /** - * @brief Retourne un enfant. + * @brief Retourne un enfant par son index. * - * @return L'enfant ou NULL. + * @param node Nœud parent. + * @param index Index de l'enfant. + * + * @return L'enfant demandé, ou NULL si l'index est invalide. */ const InvestigationNode *investigation_node_get_child( const InvestigationNode *node, @@ -105,6 +122,10 @@ const InvestigationNode *investigation_node_get_child( /** * @brief Retourne le nombre d'enfants. + * + * @param node Nœud à consulter. + * + * @return Le nombre d'enfants, ou 0 si node vaut NULL. */ size_t investigation_node_get_children_count( const InvestigationNode *node @@ -112,6 +133,11 @@ size_t investigation_node_get_children_count( /** * @brief Retourne le parent d'un nœud. + * + * @param node Nœud à consulter. + * + * @return Le parent, ou NULL si le nœud n'a pas de parent + * ou si node vaut NULL. */ const InvestigationNode *investigation_node_get_parent( const InvestigationNode *node diff --git a/labfy-investigation b/labfy-investigation index f51fc01..5c29528 100755 Binary files a/labfy-investigation and b/labfy-investigation differ diff --git a/src/core/investigation_node.c b/src/core/investigation_node.c index 79c4412..bb8d49c 100644 --- a/src/core/investigation_node.c +++ b/src/core/investigation_node.c @@ -17,6 +17,7 @@ struct InvestigationNode { char *name; + char *path; InvestigationNodeType type; InvestigationNode *parent; GPtrArray *children; @@ -24,6 +25,7 @@ struct InvestigationNode InvestigationNode *investigation_node_new( const char *name, + const char *path, InvestigationNodeType type ) { @@ -34,6 +36,11 @@ InvestigationNode *investigation_node_new( return NULL; } + if (path == NULL || path[0] == '\0') + { + return NULL; + } + node = g_new0(InvestigationNode, 1); if (node == NULL) @@ -49,6 +56,14 @@ InvestigationNode *investigation_node_new( return NULL; } + node->path = g_strdup(path); + + if (node->path == NULL) + { + investigation_node_free(node); + return NULL; + } + node->children = g_ptr_array_new_with_free_func( (GDestroyNotify) investigation_node_free ); @@ -81,11 +96,24 @@ void investigation_node_free( { g_ptr_array_free(node->children, TRUE); } - + + g_free(node->path); g_free(node->name); g_free(node); } +const char *investigation_node_get_path( + const InvestigationNode *node +) +{ + if (node == NULL) + { + return NULL; + } + + return node->path; +} + const char *investigation_node_get_name( const InvestigationNode *node ) diff --git a/src/core/investigation_tree_builder.c b/src/core/investigation_tree_builder.c index 3c76f54..e2e36d1 100644 --- a/src/core/investigation_tree_builder.c +++ b/src/core/investigation_tree_builder.c @@ -64,6 +64,7 @@ static bool investigation_tree_builder_build_children( GFileType child_file_type; GFile *child_file = NULL; InvestigationNode *child_node = NULL; + char *child_path = NULL; InvestigationNodeType child_node_type; file_info = g_file_enumerator_next_file( @@ -118,12 +119,12 @@ static bool investigation_tree_builder_build_children( child_node_type = INVESTIGATION_NODE_FILE; } - child_node = investigation_node_new( - child_name, - child_node_type + child_file = g_file_get_child( + directory, + child_name ); - if (child_node == NULL) + if (child_file == NULL) { success = false; g_object_unref(file_info); @@ -131,6 +132,26 @@ static bool investigation_tree_builder_build_children( break; } + child_path = g_file_get_path(child_file); + + if (child_path == NULL) + { + success = false; + g_object_unref(child_file); + g_object_unref(file_info); + file_info = NULL; + break; + } + + child_node = investigation_node_new( + child_name, + child_path, + child_node_type + ); + + g_free(child_path); + child_path = NULL; + /* * En cas de succès, parent_node devient propriétaire de child_node. */ @@ -138,6 +159,7 @@ static bool investigation_tree_builder_build_children( { investigation_node_free(child_node); success = false; + g_object_unref(child_file); g_object_unref(file_info); file_info = NULL; break; @@ -148,29 +170,17 @@ static bool investigation_tree_builder_build_children( */ if (child_node_type == INVESTIGATION_NODE_DIRECTORY) { - child_file = g_file_get_child( - directory, - child_name - ); - - if (child_file == NULL) - { - success = false; - g_object_unref(file_info); - file_info = NULL; - break; - } - success = investigation_tree_builder_build_children( child_file, child_node, error ); - - g_object_unref(child_file); } + g_object_unref(child_file); g_object_unref(file_info); + + child_file = NULL; file_info = NULL; } @@ -234,6 +244,7 @@ InvestigationTreeModel *investigation_tree_builder_build( root_node = investigation_node_new( root_name, + root_path, INVESTIGATION_NODE_DIRECTORY ); diff --git a/tests/test_investigation_node b/tests/test_investigation_node index 9cb1d29..b7614ef 100755 Binary files a/tests/test_investigation_node and b/tests/test_investigation_node differ diff --git a/tests/test_investigation_node.c b/tests/test_investigation_node.c index 07a3557..6aeb76f 100644 --- a/tests/test_investigation_node.c +++ b/tests/test_investigation_node.c @@ -15,6 +15,7 @@ static void test_directory_node(void) node = investigation_node_new( "01_Preuves_Originales", + "/test/01_Preuves_Originales", INVESTIGATION_NODE_DIRECTORY ); @@ -39,6 +40,7 @@ static void test_file_node(void) node = investigation_node_new( "Chronologie.md", + "/test/Chronologie.md", INVESTIGATION_NODE_FILE ); @@ -64,16 +66,19 @@ static void test_add_children(void) root = investigation_node_new( "Template", + "/test/Template", INVESTIGATION_NODE_DIRECTORY ); database_directory = investigation_node_new( "00_BaseDeDonnees", + "/test/Template/00_BaseDeDonnees", INVESTIGATION_NODE_DIRECTORY ); database_file = investigation_node_new( "Enquete.sqlite", + "/test/Template/00_BaseDeDonnees/Enquete.sqlite", INVESTIGATION_NODE_FILE ); @@ -131,21 +136,25 @@ static void test_invalid_additions(void) directory = investigation_node_new( "Directory", + "/test/Directory", INVESTIGATION_NODE_DIRECTORY ); file = investigation_node_new( "File.txt", + "/test/File.txt", INVESTIGATION_NODE_FILE ); child = investigation_node_new( "Child", + "/test/Directory/Child", INVESTIGATION_NODE_DIRECTORY ); second_parent = investigation_node_new( "SecondParent", + "/test/SecondParent", INVESTIGATION_NODE_DIRECTORY ); @@ -177,6 +186,7 @@ static void test_invalid_index(void) node = investigation_node_new( "Template", + "/test/Template", INVESTIGATION_NODE_DIRECTORY ); @@ -194,12 +204,33 @@ static void test_invalid_names(void) assert( investigation_node_new( NULL, + "/test/file", INVESTIGATION_NODE_FILE ) == NULL ); assert( investigation_node_new( + "", + "/test/file", + INVESTIGATION_NODE_FILE + ) == NULL + ); +} + +static void test_invalid_paths(void) +{ + assert( + investigation_node_new( + "File.txt", + NULL, + INVESTIGATION_NODE_FILE + ) == NULL + ); + + assert( + investigation_node_new( + "File.txt", "", INVESTIGATION_NODE_FILE ) == NULL @@ -211,6 +242,7 @@ static void test_null_behaviour(void) assert(investigation_node_get_name(NULL) == NULL); assert(investigation_node_get_children_count(NULL) == 0); assert(investigation_node_get_parent(NULL) == NULL); + assert(investigation_node_get_path(NULL) == NULL); investigation_node_free(NULL); } @@ -224,6 +256,7 @@ int main(void) test_invalid_index(); test_invalid_names(); test_null_behaviour(); + test_invalid_paths(); printf( "InvestigationNode : tous les tests de hiérarchie sont valides.\n" diff --git a/tests/test_investigation_tree_builder b/tests/test_investigation_tree_builder index fd9d901..d6cd484 100755 Binary files a/tests/test_investigation_tree_builder and b/tests/test_investigation_tree_builder differ diff --git a/tests/test_investigation_tree_model b/tests/test_investigation_tree_model index a439b76..2c823e1 100755 Binary files a/tests/test_investigation_tree_model and b/tests/test_investigation_tree_model differ diff --git a/tests/test_investigation_tree_model.c b/tests/test_investigation_tree_model.c index 5a295e4..e918aea 100644 --- a/tests/test_investigation_tree_model.c +++ b/tests/test_investigation_tree_model.c @@ -21,6 +21,7 @@ static void test_tree_model_creation(void) root_node = investigation_node_new( "Template", + "/test/Template", INVESTIGATION_NODE_DIRECTORY ); @@ -41,6 +42,13 @@ static void test_tree_model_creation(void) ) == 0 ); + assert( + strcmp( + investigation_node_get_path(returned_node), + "/test/Template" + ) == 0 + ); + assert( investigation_node_get_type(returned_node) == INVESTIGATION_NODE_DIRECTORY