feat(core): add path to investigation nodes
This commit is contained in:
parent
33014a5316
commit
68a624f398
10 changed files with 388 additions and 41 deletions
241
docs/tickets/closed/TICKET-017.md
Normal file
241
docs/tickets/closed/TICKET-017.md
Normal file
|
|
@ -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
|
||||
```
|
||||
|
|
@ -6,21 +6,16 @@
|
|||
#ifndef LABFY_INVESTIGATION_INVESTIGATION_NODE_H
|
||||
#define LABFY_INVESTIGATION_INVESTIGATION_NODE_H
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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"
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue