feat(core): build investigation tree from filesystem
This commit is contained in:
parent
2bd29e6208
commit
a0e6743ac8
9 changed files with 917 additions and 4 deletions
19
Makefile
19
Makefile
|
|
@ -19,9 +19,9 @@ TEST_CFLAGS = -std=c17 \
|
|||
-Wextra \
|
||||
-Werror \
|
||||
-Iinclude \
|
||||
$(shell $(PKG_CONFIG) --cflags glib-2.0)
|
||||
$(shell $(PKG_CONFIG) --cflags glib-2.0 gio-2.0)
|
||||
|
||||
TEST_LDFLAGS = $(shell $(PKG_CONFIG) --libs glib-2.0)
|
||||
TEST_LDFLAGS = $(shell $(PKG_CONFIG) --libs glib-2.0 gio-2.0)
|
||||
|
||||
SRC := $(shell find src -name "*.c")
|
||||
|
||||
|
|
@ -31,6 +31,7 @@ TARGET = labfy-investigation
|
|||
|
||||
TEST_NODE = tests/test_investigation_node
|
||||
TEST_TREE_MODEL = tests/test_investigation_tree_model
|
||||
TEST_TREE_BUILDER = tests/test_investigation_tree_builder
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
|
|
@ -48,10 +49,19 @@ $(TEST_TREE_MODEL): \
|
|||
src/core/investigation_tree_model.c
|
||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
|
||||
|
||||
test: $(TEST_NODE) $(TEST_TREE_MODEL)
|
||||
$(TEST_TREE_BUILDER): \
|
||||
tests/test_investigation_tree_builder.c \
|
||||
src/core/investigation_node.c \
|
||||
src/core/investigation_tree_model.c \
|
||||
src/core/investigation_tree_builder.c
|
||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) \
|
||||
$(shell $(PKG_CONFIG) --libs gio-2.0)
|
||||
|
||||
test: $(TEST_NODE) $(TEST_TREE_MODEL) $(TEST_TREE_BUILDER)
|
||||
@echo "Exécution des tests..."
|
||||
@./$(TEST_NODE)
|
||||
@./$(TEST_TREE_MODEL)
|
||||
@./$(TEST_TREE_BUILDER)
|
||||
@echo "Tous les tests sont valides."
|
||||
|
||||
%.o: %.c
|
||||
|
|
@ -63,6 +73,7 @@ run: $(TARGET)
|
|||
clean:
|
||||
rm -f $(OBJ) $(TARGET) \
|
||||
$(TEST_NODE) \
|
||||
$(TEST_TREE_MODEL)
|
||||
$(TEST_TREE_MODEL) \
|
||||
$(TEST_TREE_BUILDER)
|
||||
|
||||
.PHONY: clean run test
|
||||
|
|
|
|||
243
docs/tickets/closed/TICKET-010.md
Normal file
243
docs/tickets/closed/TICKET-010.md
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
# Ticket #010
|
||||
|
||||
## Titre
|
||||
|
||||
Construire l'arborescence d'une enquête depuis le système de fichiers.
|
||||
|
||||
---
|
||||
|
||||
## Objectif
|
||||
|
||||
Créer le module `InvestigationTreeBuilder`.
|
||||
|
||||
Ce module doit parcourir récursivement le dossier racine d'une enquête et
|
||||
construire un `InvestigationTreeModel` représentant son contenu.
|
||||
|
||||
---
|
||||
|
||||
## Responsabilités
|
||||
|
||||
Le module `InvestigationTreeBuilder` doit :
|
||||
|
||||
- recevoir le chemin racine d'une enquête ;
|
||||
- vérifier que ce chemin désigne un dossier existant ;
|
||||
- créer le nœud racine ;
|
||||
- parcourir récursivement les dossiers ;
|
||||
- créer un `InvestigationNode` pour chaque dossier ;
|
||||
- créer un `InvestigationNode` pour chaque fichier ;
|
||||
- assembler les relations parent/enfants ;
|
||||
- retourner un `InvestigationTreeModel` complet ;
|
||||
- nettoyer toutes les ressources en cas d'erreur.
|
||||
|
||||
---
|
||||
|
||||
## Hors périmètre
|
||||
|
||||
Ce ticket ne doit pas :
|
||||
|
||||
- afficher l'arborescence avec GTK ;
|
||||
- modifier le système de fichiers ;
|
||||
- créer ou supprimer des fichiers ;
|
||||
- trier les éléments ;
|
||||
- filtrer les fichiers cachés ;
|
||||
- surveiller les changements du disque ;
|
||||
- suivre les liens symboliques ;
|
||||
- communiquer avec SQLite.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
Investigation
|
||||
│
|
||||
▼
|
||||
InvestigationTreeBuilder
|
||||
│
|
||||
▼
|
||||
InvestigationTreeModel
|
||||
│
|
||||
▼
|
||||
InvestigationNode
|
||||
```
|
||||
|
||||
Le module appartient à la couche `core`.
|
||||
|
||||
Il peut utiliser GLib et GIO, mais ne doit jamais dépendre de GTK.
|
||||
|
||||
---
|
||||
|
||||
## Fichiers concernés
|
||||
|
||||
```text
|
||||
include/core/investigation_tree_builder.h
|
||||
src/core/investigation_tree_builder.c
|
||||
```
|
||||
|
||||
Les tests seront placés dans :
|
||||
|
||||
```text
|
||||
tests/test_investigation_tree_builder.c
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Interface publique attendue
|
||||
|
||||
```c
|
||||
InvestigationTreeModel *investigation_tree_builder_build(
|
||||
const char *root_path
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Principe de propriété
|
||||
|
||||
En cas de succès, la fonction retourne un nouveau
|
||||
`InvestigationTreeModel`.
|
||||
|
||||
Le code appelant devient propriétaire du modèle retourné et doit le libérer
|
||||
avec :
|
||||
|
||||
```c
|
||||
investigation_tree_model_free(tree_model);
|
||||
```
|
||||
|
||||
En cas d'échec, la fonction retourne `NULL` et doit avoir libéré toutes les
|
||||
ressources créées pendant la construction.
|
||||
|
||||
---
|
||||
|
||||
## Comportement attendu
|
||||
|
||||
À partir de :
|
||||
|
||||
```text
|
||||
Enquete_Test/
|
||||
├── 00_BaseDeDonnees/
|
||||
│ └── Enquete.sqlite
|
||||
├── 01_Preuves_Originales/
|
||||
│ ├── Captures_Ecran/
|
||||
│ └── Emails/
|
||||
└── README.md
|
||||
```
|
||||
|
||||
le module doit construire en mémoire :
|
||||
|
||||
```text
|
||||
Enquete_Test
|
||||
├── 00_BaseDeDonnees
|
||||
│ └── Enquete.sqlite
|
||||
├── 01_Preuves_Originales
|
||||
│ ├── Captures_Ecran
|
||||
│ └── Emails
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gestion des liens symboliques
|
||||
|
||||
Les liens symboliques ne sont pas suivis.
|
||||
|
||||
Cette règle évite :
|
||||
|
||||
- les boucles récursives ;
|
||||
- la sortie involontaire du dossier d'enquête ;
|
||||
- l'analyse de fichiers extérieurs à l'enquête.
|
||||
|
||||
Leur prise en charge éventuelle fera l'objet d'un ticket distinct.
|
||||
|
||||
---
|
||||
|
||||
## Dépendances
|
||||
|
||||
- C17 ;
|
||||
- GLib ;
|
||||
- GIO.
|
||||
|
||||
Aucune dépendance GTK ou SQLite.
|
||||
|
||||
---
|
||||
|
||||
## Contraintes techniques
|
||||
|
||||
- aucune variable globale ;
|
||||
- aucune modification du système de fichiers ;
|
||||
- parcours récursif ;
|
||||
- utilisation de `GFile` et `GFileEnumerator` ;
|
||||
- libération correcte des `GObject` avec `g_object_unref()` ;
|
||||
- respect de la règle « le propriétaire détruit » ;
|
||||
- documentation Doxygen ;
|
||||
- compilation sans warning ;
|
||||
- aucune fuite mémoire.
|
||||
|
||||
---
|
||||
|
||||
## Cas à gérer
|
||||
|
||||
- chemin valide ;
|
||||
- chemin relatif ;
|
||||
- chemin `NULL` ;
|
||||
- chemin vide ;
|
||||
- chemin inexistant ;
|
||||
- chemin désignant un fichier ;
|
||||
- dossier vide ;
|
||||
- plusieurs niveaux de sous-dossiers ;
|
||||
- fichiers et dossiers mélangés ;
|
||||
- erreur rencontrée pendant le parcours.
|
||||
|
||||
---
|
||||
|
||||
## Critères d'acceptation
|
||||
|
||||
- [ ] Le projet compile sans warning.
|
||||
- [ ] Un dossier valide produit un modèle.
|
||||
- [ ] Le nom du dossier racine est correct.
|
||||
- [ ] Les dossiers produisent des nœuds de type `DIRECTORY`.
|
||||
- [ ] Les fichiers produisent des nœuds de type `FILE`.
|
||||
- [ ] Les relations parent/enfants sont correctes.
|
||||
- [ ] Plusieurs niveaux de profondeur sont pris en charge.
|
||||
- [ ] Un dossier vide est pris en charge.
|
||||
- [ ] Un chemin invalide retourne `NULL`.
|
||||
- [ ] Les liens symboliques ne sont pas suivis.
|
||||
- [ ] Toutes les ressources sont libérées en cas d'erreur.
|
||||
- [ ] Aucun code GTK.
|
||||
- [ ] Aucun code SQLite.
|
||||
- [ ] `make test` exécute le nouveau test.
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
|
||||
Le test doit créer une arborescence temporaire isolée :
|
||||
|
||||
```text
|
||||
TestCase/
|
||||
├── DirectoryA/
|
||||
│ └── FileA.txt
|
||||
├── DirectoryB/
|
||||
└── RootFile.md
|
||||
```
|
||||
|
||||
Il doit vérifier :
|
||||
|
||||
- le nom du nœud racine ;
|
||||
- le nombre d'enfants de la racine ;
|
||||
- la présence des deux dossiers ;
|
||||
- la présence du fichier racine ;
|
||||
- la présence de `FileA.txt` dans `DirectoryA` ;
|
||||
- le type de chaque nœud ;
|
||||
- le parent de chaque enfant ;
|
||||
- la gestion d'un dossier vide ;
|
||||
- les chemins invalides ;
|
||||
- la destruction complète du modèle.
|
||||
|
||||
---
|
||||
|
||||
## Commit attendu
|
||||
|
||||
```text
|
||||
feat(core): build investigation tree from filesystem
|
||||
```
|
||||
36
include/core/investigation_tree_builder.h
Normal file
36
include/core/investigation_tree_builder.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/******************************************************************************
|
||||
* @file investigation_tree_builder.h
|
||||
* @brief Interface publique du constructeur d'arborescence d'une enquête.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LABFY_INVESTIGATION_INVESTIGATION_TREE_BUILDER_H
|
||||
#define LABFY_INVESTIGATION_INVESTIGATION_TREE_BUILDER_H
|
||||
|
||||
#include "core/investigation_tree_model.h"
|
||||
|
||||
/**
|
||||
* @brief Construit le modèle d'arborescence d'une enquête depuis le disque.
|
||||
*
|
||||
* Le chemin fourni doit désigner un dossier existant.
|
||||
*
|
||||
* La fonction parcourt récursivement son contenu et crée :
|
||||
*
|
||||
* - un nœud racine pour le dossier sélectionné ;
|
||||
* - un nœud de type DIRECTORY pour chaque sous-dossier ;
|
||||
* - un nœud de type FILE pour chaque fichier.
|
||||
*
|
||||
* Les liens symboliques ne sont pas suivis.
|
||||
*
|
||||
* Le code appelant devient propriétaire du modèle retourné et doit le
|
||||
* libérer avec investigation_tree_model_free().
|
||||
*
|
||||
* @param root_path Chemin du dossier racine à parcourir.
|
||||
*
|
||||
* @return Un nouveau modèle d'arborescence, ou NULL si le chemin est invalide
|
||||
* ou si une erreur empêche la construction du modèle.
|
||||
*/
|
||||
InvestigationTreeModel *investigation_tree_builder_build(
|
||||
const char *root_path
|
||||
);
|
||||
|
||||
#endif
|
||||
Binary file not shown.
285
src/core/investigation_tree_builder.c
Normal file
285
src/core/investigation_tree_builder.c
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
/******************************************************************************
|
||||
* @file investigation_tree_builder.c
|
||||
* @brief Construction d'une arborescence d'enquête depuis le système de fichiers.
|
||||
******************************************************************************/
|
||||
|
||||
#include "core/investigation_tree_builder.h"
|
||||
|
||||
#include "core/investigation_node.h"
|
||||
#include "core/investigation_tree_model.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
|
||||
/**
|
||||
* @brief Attributs demandés à GIO pendant le parcours d'un dossier.
|
||||
*/
|
||||
#define INVESTIGATION_TREE_FILE_ATTRIBUTES \
|
||||
G_FILE_ATTRIBUTE_STANDARD_NAME "," \
|
||||
G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
|
||||
G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK
|
||||
|
||||
/**
|
||||
* @brief Construit récursivement les enfants d'un nœud dossier.
|
||||
*
|
||||
* @param directory Dossier GIO actuellement parcouru.
|
||||
* @param parent_node Nœud représentant ce dossier dans notre modèle.
|
||||
* @param error Adresse recevant une éventuelle erreur GIO.
|
||||
*
|
||||
* @return true si le parcours s'est terminé correctement, sinon false.
|
||||
*/
|
||||
static bool investigation_tree_builder_build_children(
|
||||
GFile *directory,
|
||||
InvestigationNode *parent_node,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
GFileEnumerator *enumerator = NULL;
|
||||
GFileInfo *file_info = NULL;
|
||||
bool success = true;
|
||||
|
||||
if (directory == NULL || parent_node == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
enumerator = g_file_enumerate_children(
|
||||
directory,
|
||||
INVESTIGATION_TREE_FILE_ATTRIBUTES,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
NULL,
|
||||
error
|
||||
);
|
||||
|
||||
if (enumerator == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while (success)
|
||||
{
|
||||
const char *child_name = NULL;
|
||||
GFileType child_file_type;
|
||||
GFile *child_file = NULL;
|
||||
InvestigationNode *child_node = NULL;
|
||||
InvestigationNodeType child_node_type;
|
||||
|
||||
file_info = g_file_enumerator_next_file(
|
||||
enumerator,
|
||||
NULL,
|
||||
error
|
||||
);
|
||||
|
||||
if (file_info == NULL)
|
||||
{
|
||||
/*
|
||||
* NULL signifie soit que le dossier est entièrement parcouru,
|
||||
* soit qu'une erreur a eu lieu.
|
||||
*/
|
||||
if (error != NULL && *error != NULL)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Les liens symboliques sont volontairement ignorés afin d'éviter
|
||||
* les boucles et les sorties involontaires du dossier d'enquête.
|
||||
*/
|
||||
if (g_file_info_get_is_symlink(file_info))
|
||||
{
|
||||
g_object_unref(file_info);
|
||||
file_info = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
child_name = g_file_info_get_name(file_info);
|
||||
|
||||
if (child_name == NULL || child_name[0] == '\0')
|
||||
{
|
||||
success = false;
|
||||
g_object_unref(file_info);
|
||||
file_info = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
child_file_type = g_file_info_get_file_type(file_info);
|
||||
|
||||
if (child_file_type == G_FILE_TYPE_DIRECTORY)
|
||||
{
|
||||
child_node_type = INVESTIGATION_NODE_DIRECTORY;
|
||||
}
|
||||
else
|
||||
{
|
||||
child_node_type = INVESTIGATION_NODE_FILE;
|
||||
}
|
||||
|
||||
child_node = investigation_node_new(
|
||||
child_name,
|
||||
child_node_type
|
||||
);
|
||||
|
||||
if (child_node == NULL)
|
||||
{
|
||||
success = false;
|
||||
g_object_unref(file_info);
|
||||
file_info = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* En cas de succès, parent_node devient propriétaire de child_node.
|
||||
*/
|
||||
if (!investigation_node_add_child(parent_node, child_node))
|
||||
{
|
||||
investigation_node_free(child_node);
|
||||
success = false;
|
||||
g_object_unref(file_info);
|
||||
file_info = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Seuls les dossiers sont parcourus récursivement.
|
||||
*/
|
||||
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(file_info);
|
||||
file_info = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Si la boucle a été interrompue avant la libération de file_info,
|
||||
* cette vérification garantit un nettoyage correct.
|
||||
*/
|
||||
if (file_info != NULL)
|
||||
{
|
||||
g_object_unref(file_info);
|
||||
}
|
||||
|
||||
g_object_unref(enumerator);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
InvestigationTreeModel *investigation_tree_builder_build(
|
||||
const char *root_path
|
||||
)
|
||||
{
|
||||
GFile *root_file = NULL;
|
||||
GFileType root_file_type;
|
||||
char *root_name = NULL;
|
||||
InvestigationNode *root_node = NULL;
|
||||
InvestigationTreeModel *tree_model = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
if (root_path == NULL || root_path[0] == '\0')
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root_file = g_file_new_for_path(root_path);
|
||||
|
||||
if (root_file == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root_file_type = g_file_query_file_type(
|
||||
root_file,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (root_file_type != G_FILE_TYPE_DIRECTORY)
|
||||
{
|
||||
g_object_unref(root_file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root_name = g_file_get_basename(root_file);
|
||||
|
||||
if (root_name == NULL || root_name[0] == '\0')
|
||||
{
|
||||
g_free(root_name);
|
||||
g_object_unref(root_file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root_node = investigation_node_new(
|
||||
root_name,
|
||||
INVESTIGATION_NODE_DIRECTORY
|
||||
);
|
||||
|
||||
g_free(root_name);
|
||||
|
||||
if (root_node == NULL)
|
||||
{
|
||||
g_object_unref(root_file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!investigation_tree_builder_build_children(
|
||||
root_file,
|
||||
root_node,
|
||||
&error
|
||||
))
|
||||
{
|
||||
if (error != NULL)
|
||||
{
|
||||
g_warning(
|
||||
"Impossible de construire l'arborescence : %s",
|
||||
error->message
|
||||
);
|
||||
|
||||
g_clear_error(&error);
|
||||
}
|
||||
|
||||
investigation_node_free(root_node);
|
||||
g_object_unref(root_file);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_object_unref(root_file);
|
||||
|
||||
tree_model = investigation_tree_model_new(root_node);
|
||||
|
||||
if (tree_model == NULL)
|
||||
{
|
||||
/*
|
||||
* Le transfert de propriété n'a pas eu lieu puisque la création
|
||||
* du modèle a échoué.
|
||||
*/
|
||||
investigation_node_free(root_node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tree_model;
|
||||
}
|
||||
Binary file not shown.
BIN
tests/test_investigation_tree_builder
Executable file
BIN
tests/test_investigation_tree_builder
Executable file
Binary file not shown.
338
tests/test_investigation_tree_builder.c
Normal file
338
tests/test_investigation_tree_builder.c
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
/******************************************************************************
|
||||
* @file test_investigation_tree_builder.c
|
||||
* @brief Tests d'intégration du module InvestigationTreeBuilder.
|
||||
******************************************************************************/
|
||||
|
||||
#include "core/investigation_node.h"
|
||||
#include "core/investigation_tree_builder.h"
|
||||
#include "core/investigation_tree_model.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
/**
|
||||
* @brief Recherche un enfant par son nom.
|
||||
*
|
||||
* @param parent Nœud dans lequel effectuer la recherche.
|
||||
* @param name Nom recherché.
|
||||
*
|
||||
* @return L'enfant correspondant, ou NULL s'il n'existe pas.
|
||||
*/
|
||||
static const InvestigationNode *test_find_child(
|
||||
const InvestigationNode *parent,
|
||||
const char *name
|
||||
)
|
||||
{
|
||||
size_t child_count = 0;
|
||||
|
||||
if (parent == NULL || name == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
child_count = investigation_node_get_children_count(parent);
|
||||
|
||||
for (size_t index = 0; index < child_count; ++index)
|
||||
{
|
||||
const InvestigationNode *child = NULL;
|
||||
const char *child_name = NULL;
|
||||
|
||||
child = investigation_node_get_child(parent, index);
|
||||
|
||||
if (child == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
child_name = investigation_node_get_name(child);
|
||||
|
||||
if (child_name != NULL && strcmp(child_name, name) == 0)
|
||||
{
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Crée un fichier texte utilisé par le test.
|
||||
*/
|
||||
static void test_create_file(const char *file_path)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
assert(file_path != NULL);
|
||||
|
||||
assert(
|
||||
g_file_set_contents(
|
||||
file_path,
|
||||
"test\n",
|
||||
-1,
|
||||
&error
|
||||
)
|
||||
);
|
||||
|
||||
assert(error == NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Vérifie la construction récursive d'une arborescence.
|
||||
*/
|
||||
static void test_build_tree_from_filesystem(void)
|
||||
{
|
||||
char *root_path = NULL;
|
||||
char *root_name = NULL;
|
||||
|
||||
char *directory_a_path = NULL;
|
||||
char *directory_b_path = NULL;
|
||||
char *file_a_path = NULL;
|
||||
char *root_file_path = NULL;
|
||||
|
||||
GError *error = NULL;
|
||||
|
||||
InvestigationTreeModel *tree_model = NULL;
|
||||
const InvestigationNode *root_node = NULL;
|
||||
const InvestigationNode *directory_a_node = NULL;
|
||||
const InvestigationNode *directory_b_node = NULL;
|
||||
const InvestigationNode *file_a_node = NULL;
|
||||
const InvestigationNode *root_file_node = NULL;
|
||||
|
||||
/*
|
||||
* GLib crée un dossier temporaire unique, par exemple :
|
||||
*
|
||||
* /tmp/labfy-investigation-test-ABC123
|
||||
*/
|
||||
root_path = g_dir_make_tmp(
|
||||
"labfy-investigation-test-XXXXXX",
|
||||
&error
|
||||
);
|
||||
|
||||
assert(root_path != NULL);
|
||||
assert(error == NULL);
|
||||
|
||||
directory_a_path = g_build_filename(
|
||||
root_path,
|
||||
"DirectoryA",
|
||||
NULL
|
||||
);
|
||||
|
||||
directory_b_path = g_build_filename(
|
||||
root_path,
|
||||
"DirectoryB",
|
||||
NULL
|
||||
);
|
||||
|
||||
file_a_path = g_build_filename(
|
||||
directory_a_path,
|
||||
"FileA.txt",
|
||||
NULL
|
||||
);
|
||||
|
||||
root_file_path = g_build_filename(
|
||||
root_path,
|
||||
"RootFile.md",
|
||||
NULL
|
||||
);
|
||||
|
||||
assert(g_mkdir(directory_a_path, 0700) == 0);
|
||||
assert(g_mkdir(directory_b_path, 0700) == 0);
|
||||
|
||||
test_create_file(file_a_path);
|
||||
test_create_file(root_file_path);
|
||||
|
||||
/*
|
||||
* Arborescence créée :
|
||||
*
|
||||
* racine temporaire/
|
||||
* ├── DirectoryA/
|
||||
* │ └── FileA.txt
|
||||
* ├── DirectoryB/
|
||||
* └── RootFile.md
|
||||
*/
|
||||
tree_model = investigation_tree_builder_build(root_path);
|
||||
|
||||
assert(tree_model != NULL);
|
||||
|
||||
root_node = investigation_tree_model_get_root(tree_model);
|
||||
|
||||
assert(root_node != NULL);
|
||||
assert(
|
||||
investigation_node_get_type(root_node) ==
|
||||
INVESTIGATION_NODE_DIRECTORY
|
||||
);
|
||||
|
||||
root_name = g_path_get_basename(root_path);
|
||||
|
||||
assert(root_name != NULL);
|
||||
assert(
|
||||
strcmp(
|
||||
investigation_node_get_name(root_node),
|
||||
root_name
|
||||
) == 0
|
||||
);
|
||||
|
||||
assert(investigation_node_get_children_count(root_node) == 3);
|
||||
|
||||
/*
|
||||
* L'ordre retourné par le système de fichiers n'est pas garanti.
|
||||
* On recherche donc chaque enfant par son nom.
|
||||
*/
|
||||
directory_a_node = test_find_child(
|
||||
root_node,
|
||||
"DirectoryA"
|
||||
);
|
||||
|
||||
directory_b_node = test_find_child(
|
||||
root_node,
|
||||
"DirectoryB"
|
||||
);
|
||||
|
||||
root_file_node = test_find_child(
|
||||
root_node,
|
||||
"RootFile.md"
|
||||
);
|
||||
|
||||
assert(directory_a_node != NULL);
|
||||
assert(directory_b_node != NULL);
|
||||
assert(root_file_node != NULL);
|
||||
|
||||
assert(
|
||||
investigation_node_get_type(directory_a_node) ==
|
||||
INVESTIGATION_NODE_DIRECTORY
|
||||
);
|
||||
|
||||
assert(
|
||||
investigation_node_get_type(directory_b_node) ==
|
||||
INVESTIGATION_NODE_DIRECTORY
|
||||
);
|
||||
|
||||
assert(
|
||||
investigation_node_get_type(root_file_node) ==
|
||||
INVESTIGATION_NODE_FILE
|
||||
);
|
||||
|
||||
assert(
|
||||
investigation_node_get_parent(directory_a_node) ==
|
||||
root_node
|
||||
);
|
||||
|
||||
assert(
|
||||
investigation_node_get_parent(directory_b_node) ==
|
||||
root_node
|
||||
);
|
||||
|
||||
assert(
|
||||
investigation_node_get_parent(root_file_node) ==
|
||||
root_node
|
||||
);
|
||||
|
||||
/*
|
||||
* DirectoryB est vide.
|
||||
*/
|
||||
assert(
|
||||
investigation_node_get_children_count(directory_b_node) == 0
|
||||
);
|
||||
|
||||
/*
|
||||
* DirectoryA contient FileA.txt.
|
||||
*/
|
||||
assert(
|
||||
investigation_node_get_children_count(directory_a_node) == 1
|
||||
);
|
||||
|
||||
file_a_node = test_find_child(
|
||||
directory_a_node,
|
||||
"FileA.txt"
|
||||
);
|
||||
|
||||
assert(file_a_node != NULL);
|
||||
|
||||
assert(
|
||||
investigation_node_get_type(file_a_node) ==
|
||||
INVESTIGATION_NODE_FILE
|
||||
);
|
||||
|
||||
assert(
|
||||
investigation_node_get_parent(file_a_node) ==
|
||||
directory_a_node
|
||||
);
|
||||
|
||||
/*
|
||||
* Le modèle possède la racine et toute son arborescence.
|
||||
*/
|
||||
investigation_tree_model_free(tree_model);
|
||||
|
||||
/*
|
||||
* Nettoyage du système de fichiers dans l'ordre inverse
|
||||
* de la création.
|
||||
*/
|
||||
assert(g_remove(file_a_path) == 0);
|
||||
assert(g_remove(root_file_path) == 0);
|
||||
assert(g_rmdir(directory_a_path) == 0);
|
||||
assert(g_rmdir(directory_b_path) == 0);
|
||||
assert(g_rmdir(root_path) == 0);
|
||||
|
||||
g_free(root_file_path);
|
||||
g_free(file_a_path);
|
||||
g_free(directory_b_path);
|
||||
g_free(directory_a_path);
|
||||
g_free(root_name);
|
||||
g_free(root_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Vérifie les chemins invalides.
|
||||
*/
|
||||
static void test_invalid_paths(void)
|
||||
{
|
||||
char *temporary_file_path = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
assert(investigation_tree_builder_build(NULL) == NULL);
|
||||
assert(investigation_tree_builder_build("") == NULL);
|
||||
|
||||
assert(
|
||||
investigation_tree_builder_build(
|
||||
"/chemin/qui/nexiste/pas"
|
||||
) == NULL
|
||||
);
|
||||
|
||||
temporary_file_path = g_build_filename(
|
||||
g_get_tmp_dir(),
|
||||
"labfy-investigation-builder-file-test.txt",
|
||||
NULL
|
||||
);
|
||||
|
||||
test_create_file(temporary_file_path);
|
||||
|
||||
/*
|
||||
* Le chemin existe, mais il désigne un fichier et non un dossier.
|
||||
*/
|
||||
assert(
|
||||
investigation_tree_builder_build(
|
||||
temporary_file_path
|
||||
) == NULL
|
||||
);
|
||||
|
||||
assert(g_remove(temporary_file_path) == 0);
|
||||
|
||||
g_free(temporary_file_path);
|
||||
g_clear_error(&error);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_build_tree_from_filesystem();
|
||||
test_invalid_paths();
|
||||
|
||||
printf(
|
||||
"InvestigationTreeBuilder : tous les tests sont valides.\n"
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
Loading…
Reference in a new issue