feat(core): add investigation session loader

This commit is contained in:
grayTerminal-sh 2026-07-16 13:38:15 +02:00
parent 137e75722d
commit 4efdfe4865
12 changed files with 2382 additions and 24 deletions

View file

@ -39,6 +39,7 @@ TEST_TRANSACTION = tests/test_transaction
TEST_ERROR = tests/test_error
TEST_INVESTIGATION_RECORD = tests/test_investigation_record
TEST_INVESTIGATION_DAO := tests/test_investigation_dao
TEST_INVESTIGATION_SESSION := tests/test_investigation_session
all: $(TARGET)
@ -123,6 +124,19 @@ $(TEST_INVESTIGATION_DAO): \
src/database/error.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
$(TEST_INVESTIGATION_SESSION): \
tests/test_investigation_session.c \
src/core/investigation_session.c \
src/core/investigation_project.c \
src/dao/investigation_dao.c \
src/models/investigation_record.c \
src/database/database.c \
src/database/schema.c \
src/database/statement.c \
src/database/transaction.c \
src/database/error.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
test: \
$(TEST_NODE) \
$(TEST_TREE_MODEL) \
@ -133,7 +147,8 @@ test: \
$(TEST_TRANSACTION) \
$(TEST_ERROR) \
$(TEST_INVESTIGATION_RECORD) \
$(TEST_INVESTIGATION_DAO)
$(TEST_INVESTIGATION_DAO) \
$(TEST_INVESTIGATION_SESSION)
@echo "Exécution des tests..."
@./$(TEST_NODE)
@./$(TEST_TREE_MODEL)
@ -145,6 +160,7 @@ test: \
@$(TEST_ERROR)
@$(TEST_INVESTIGATION_RECORD)
@$(TEST_INVESTIGATION_DAO)
@$(TEST_INVESTIGATION_SESSION)
@echo "Tous les tests sont valides."
%.o: %.c
@ -164,6 +180,7 @@ clean:
$(TEST_TRANSACTION) \
$(TEST_ERROR) \
$(TEST_INVESTIGATION_RECORD) \
$(TEST_INVESTIGATION_DAO)
$(TEST_INVESTIGATION_DAO) \
$(TEST_INVESTIGATION_SESSION)
.PHONY: clean run test

View file

@ -0,0 +1,776 @@
# Ticket #028 — Ajouter louverture dune enquête existante
## Contexte
Les tickets précédents ont permis de mettre en place :
- la création transactionnelle dune base denquête ;
- la couche `Database` ;
- les requêtes préparées `DatabaseStatement` ;
- la gestion des transactions et des erreurs ;
- le modèle `InvestigationRecord` ;
- le DAO `InvestigationDao` permettant de charger lunique ligne de la table `investigation`.
Le projet possède également un type `InvestigationProject` chargé de représenter les chemins du projet sur le système de fichiers, notamment :
- le dossier racine de lenquête ;
- le chemin du fichier `Enquete.sqlite`.
Cependant, lapplication ne possède pas encore dobjet représentant une enquête réellement ouverte pendant son exécution.
Les différentes ressources sont encore séparées :
```text
InvestigationProject
Database
InvestigationRecord
```
Il faut désormais les regrouper dans un contexte cohérent dont la durée de vie correspond à celle dune enquête ouverte.
## Objectif
Créer un type opaque `InvestigationSession` chargé douvrir une enquête existante et de conserver :
- son contexte de fichiers `InvestigationProject` ;
- sa connexion `Database` ;
- ses informations persistées `InvestigationRecord`.
Louverture doit vérifier que le dossier sélectionné correspond bien aux informations enregistrées dans la base SQLite.
La connexion SQLite doit rester ouverte pendant toute la durée de vie de la session afin de permettre les futurs appels aux DAO.
## Architecture attendue
```text
Application
InvestigationSession
├── InvestigationProject
├── Database
└── InvestigationRecord
InvestigationDao
DatabaseStatement
SQLite
```
## Travail à réaliser
### 1. Créer le type `InvestigationSession`
Créer les fichiers :
```text
include/core/investigation_session.h
src/core/investigation_session.c
```
Le type doit être opaque :
```c
typedef struct InvestigationSession InvestigationSession;
```
Sa représentation privée doit contenir au minimum :
```c
struct InvestigationSession
{
InvestigationProject *project;
Database *database;
InvestigationRecord *record;
};
```
Le header public ne doit pas exposer cette structure.
### 2. Définir les erreurs douverture
Créer une énumération dédiée :
```c
typedef enum
{
INVESTIGATION_SESSION_ERROR_INVALID_ARGUMENT,
INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND,
INVESTIGATION_SESSION_ERROR_DATABASE_NOT_FOUND,
INVESTIGATION_SESSION_ERROR_PROJECT,
INVESTIGATION_SESSION_ERROR_DATABASE,
INVESTIGATION_SESSION_ERROR_RECORD,
INVESTIGATION_SESSION_ERROR_ROOT_MISMATCH,
INVESTIGATION_SESSION_ERROR_MEMORY
} InvestigationSessionError;
```
Définir un domaine derreur GLib :
```c
#define INVESTIGATION_SESSION_ERROR \
investigation_session_error_quark()
GQuark investigation_session_error_quark(void);
```
Les erreurs doivent être transmises avec un paramètre :
```c
GError **error
```
Lorsquune erreur provenant de `Database` doit être propagée, son message doit être copié dans le `GError` avant la fermeture de la connexion.
### 3. Ajouter la fonction douverture
Déclarer :
```c
InvestigationSession *investigation_session_open(
const char *investigation_root_path,
GError **error
);
```
La fonction doit ouvrir une enquête déjà existante à partir de son dossier racine.
Elle ne doit pas créer une nouvelle enquête.
### 4. Valider les paramètres
La fonction doit refuser :
```text
investigation_root_path == NULL
investigation_root_path vide
```
Le code derreur attendu est :
```c
INVESTIGATION_SESSION_ERROR_INVALID_ARGUMENT
```
Le paramètre `error` peut être `NULL`.
Si `error` nest pas `NULL`, il doit respecter les conventions GLib :
```c
*error == NULL
```
au moment de lappel.
### 5. Vérifier le dossier racine
Le chemin fourni doit correspondre à un dossier existant.
La fonction doit vérifier :
```c
G_FILE_TEST_IS_DIR
```
Un chemin inexistant ou qui ne représente pas un dossier doit produire :
```c
INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND
```
Le chemin doit être normalisé avec :
```c
g_canonicalize_filename()
```
La session doit travailler à partir de ce chemin canonique.
### 6. Construire `InvestigationProject`
Réutiliser lAPI existante de `InvestigationProject`.
La logique de construction du chemin de la base ne doit pas être dupliquée dans `InvestigationSession`.
Le chemin attendu reste géré par `InvestigationProject` :
```text
<racine>/00_BaseDeDonnees/Enquete.sqlite
```
Si lAPI actuelle de `InvestigationProject` ne permet pas de représenter un projet existant, elle peut être étendue de manière minimale.
Aucune logique SQLite ne doit être ajoutée dans `InvestigationProject`.
### 7. Vérifier le fichier SQLite
Avant louverture de la connexion, vérifier que le chemin retourné par `InvestigationProject` correspond à un fichier régulier :
```c
G_FILE_TEST_IS_REGULAR
```
Si le fichier nexiste pas, retourner :
```c
INVESTIGATION_SESSION_ERROR_DATABASE_NOT_FOUND
```
Louverture dune enquête existante ne doit jamais créer silencieusement une nouvelle base vide.
### 8. Ouvrir la connexion Database
Utiliser :
```c
database_open()
```
La fonction `investigation_session_open()` ne doit pas appeler directement :
```c
sqlite3_open()
sqlite3_open_v2()
sqlite3_close()
```
Si `database_open()` échoue, retourner :
```c
INVESTIGATION_SESSION_ERROR_DATABASE
```
La connexion doit rester ouverte si la session est créée avec succès.
### 9. Charger lenquête persistée
Utiliser :
```c
investigation_dao_load()
```
Le DAO doit retourner un `InvestigationRecord`.
Si le chargement échoue :
- récupérer le code et le message de la dernière erreur `Database` ;
- copier le message dans un `GError` ;
- retourner `NULL` ;
- fermer proprement la connexion ;
- libérer le projet ;
- ne laisser aucune ressource allouée.
Le code derreur de session attendu est :
```c
INVESTIGATION_SESSION_ERROR_RECORD
```
### 10. Vérifier la cohérence du chemin racine
Le chemin sélectionné doit correspondre au champ persistant :
```text
investigation.root_path
```
Comparer les versions canoniques de :
```text
chemin racine sélectionné
chemin racine enregistré dans InvestigationRecord
```
La comparaison doit être faite après normalisation avec :
```c
g_canonicalize_filename()
```
Si les chemins ne correspondent pas, louverture doit échouer avec :
```c
INVESTIGATION_SESSION_ERROR_ROOT_MISMATCH
```
Cette vérification évite douvrir une base copiée ou déplacée sans détecter lincohérence.
Le déplacement volontaire dune enquête sera traité dans un ticket distinct.
### 11. Construire la session
La session ne doit être créée quaprès validation complète :
```text
dossier racine valide
InvestigationProject valide
fichier SQLite présent
Database ouverte
InvestigationRecord chargé
chemin racine cohérent
InvestigationSession créée
```
En cas déchec dallocation, retourner :
```c
INVESTIGATION_SESSION_ERROR_MEMORY
```
### 12. Ajouter la fonction de fermeture
Déclarer :
```c
void investigation_session_close(
InvestigationSession *session
);
```
Cette fonction doit accepter `NULL`.
Elle doit libérer toutes les ressources possédées par la session :
```text
InvestigationRecord
Database
InvestigationProject
InvestigationSession
```
La session devient propriétaire de ces trois objets dès que son ouverture réussit.
### 13. Ajouter les accesseurs
Ajouter :
```c
const InvestigationProject *investigation_session_get_project(
const InvestigationSession *session
);
const InvestigationRecord *investigation_session_get_record(
const InvestigationSession *session
);
Database *investigation_session_get_database(
InvestigationSession *session
);
```
Les pointeurs retournés appartiennent à la session et ne doivent pas être libérés par lappelant.
Les accesseurs doivent retourner `NULL` si la session reçue est `NULL`.
`Database` reste non constante car les futurs DAO auront besoin dune connexion modifiable.
### 14. Interdire les dépendances SQLite et GTK
Le module `InvestigationSession` ne doit pas inclure :
```c
#include <sqlite3.h>
#include <gtk/gtk.h>
```
Il doit exclusivement utiliser les abstractions existantes :
```text
InvestigationProject
Database
InvestigationDao
InvestigationRecord
GLib
```
## Tests à ajouter
Créer :
```text
tests/test_investigation_session.c
```
### Test douverture valide
Créer un dossier temporaire.
Initialiser une base avec :
```c
database_initialize()
```
Ouvrir ensuite lenquête avec :
```c
investigation_session_open()
```
Vérifier :
- la session nest pas `NULL` ;
- aucune erreur nest produite ;
- le projet est disponible ;
- la connexion Database est disponible ;
- le record est disponible ;
- le nom de lenquête est correct ;
- le chemin racine est correct ;
- lUUID est valide ;
- `created_at` nest pas vide ;
- `updated_at` nest pas vide ;
- la connexion peut encore être utilisée par un DAO ;
- la fermeture libère correctement les ressources.
### Test des paramètres invalides
Vérifier :
```c
investigation_session_open(NULL, &error) == NULL
investigation_session_open("", &error) == NULL
```
Le code attendu est :
```c
INVESTIGATION_SESSION_ERROR_INVALID_ARGUMENT
```
Vérifier également le comportement avec :
```c
error == NULL
```
### Test dun dossier inexistant
Utiliser un chemin inexistant.
Vérifier :
```text
résultat == NULL
erreur == INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND
message non vide
```
### Test dun chemin qui nest pas un dossier
Créer un fichier temporaire et utiliser son chemin comme racine.
Vérifier :
```text
résultat == NULL
erreur == INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND
```
### Test dune base absente
Créer une structure de projet valide sans fichier :
```text
00_BaseDeDonnees/Enquete.sqlite
```
Vérifier :
```text
résultat == NULL
erreur == INVESTIGATION_SESSION_ERROR_DATABASE_NOT_FOUND
```
La fonction ne doit pas créer de nouveau fichier SQLite.
### Test dune base invalide
Créer un fichier SQLite vide ou une base ne contenant pas la table :
```text
investigation
```
Vérifier :
```text
résultat == NULL
erreur == INVESTIGATION_SESSION_ERROR_RECORD
message non vide
```
### Test dun chemin racine incohérent
Créer une base avec un chemin racine enregistré différent du dossier utilisé pour louverture.
Vérifier :
```text
résultat == NULL
erreur == INVESTIGATION_SESSION_ERROR_ROOT_MISMATCH
message non vide
```
### Test des accesseurs avec NULL
Vérifier :
```c
investigation_session_get_project(NULL) == NULL
investigation_session_get_record(NULL) == NULL
investigation_session_get_database(NULL) == NULL
```
Vérifier également :
```c
investigation_session_close(NULL);
```
### Test de réutilisation du DAO
Après louverture valide dune session, appeler de nouveau :
```c
investigation_dao_load(
investigation_session_get_database(session)
);
```
Vérifier que la connexion reste fonctionnelle pendant toute la durée de vie de la session.
## Gestion de la mémoire
Toutes les sorties déchec de `investigation_session_open()` doivent libérer les ressources déjà créées.
Le nettoyage doit couvrir les cas suivants :
```text
échec avant création du projet
échec après création du projet
échec après ouverture de Database
échec après chargement du record
échec lors de la comparaison des chemins
échec lors de lallocation de la session
```
Aucun objet ne doit être libéré deux fois.
Aucune ressource temporaire ne doit rester allouée :
```text
chemins canoniques
messages copiés
GError temporaires
InvestigationProject
Database
InvestigationRecord
```
## Makefile
Ajouter :
```make
TEST_INVESTIGATION_SESSION := tests/test_investigation_session
```
Ajouter une règle compilant au minimum :
```text
tests/test_investigation_session.c
src/core/investigation_session.c
src/core/investigation_project.c
src/dao/investigation_dao.c
src/models/investigation_record.c
src/database/database.c
src/database/schema.c
src/database/statement.c
src/database/transaction.c
src/database/error.c
```
Lier avec :
```text
GLib
SQLite
```
Ajouter le test aux cibles :
```text
test
clean
```
La nouvelle sortie attendue est :
```text
InvestigationSession : tous les tests sont valides.
```
## Critères dacceptation
- [ ] Le type `InvestigationSession` est opaque.
- [ ] Une session possède un `InvestigationProject`.
- [ ] Une session possède une connexion `Database`.
- [ ] Une session possède un `InvestigationRecord`.
- [ ] Le dossier racine est validé avant louverture.
- [ ] Le chemin racine est normalisé.
- [ ] Le chemin de la base provient de `InvestigationProject`.
- [ ] Le fichier SQLite doit exister avant lappel à `database_open()`.
- [ ] Louverture ne crée jamais silencieusement une nouvelle base.
- [ ] Le record est chargé avec `InvestigationDao`.
- [ ] Le chemin enregistré est comparé au chemin sélectionné.
- [ ] Une incohérence de chemin empêche louverture.
- [ ] La connexion reste ouverte pendant la durée de vie de la session.
- [ ] La fermeture libère toutes les ressources.
- [ ] Les accesseurs acceptent une session `NULL`.
- [ ] Les erreurs sont propagées avec `GError`.
- [ ] Aucun type SQLite napparaît dans lAPI de la session.
- [ ] Aucune dépendance GTK nest ajoutée.
- [ ] Les tests douverture valide sont présents.
- [ ] Les tests de paramètres invalides sont présents.
- [ ] Les tests de dossier absent sont présents.
- [ ] Les tests de base absente sont présents.
- [ ] Les tests de base invalide sont présents.
- [ ] Les tests de chemin incohérent sont présents.
- [ ] Les anciens tests restent valides.
- [ ] `make` réussit sans erreur.
- [ ] `make test` réussit.
- [ ] `git diff --check` ne retourne aucune erreur.
## Audit attendu
Les commandes suivantes ne doivent rien afficher :
```bash
rg -n 'sqlite3_|#include <sqlite3.h>|#include <gtk' \
include/core/investigation_session.h \
src/core/investigation_session.c
```
Vérifier également que la logique du chemin SQLite nest pas dupliquée :
```bash
rg -n '00_BaseDeDonnees|Enquete.sqlite' \
src/core/investigation_session.c
```
Le résultat attendu est aucune occurrence, sauf éventuellement dans un commentaire de documentation justifié.
## Hors périmètre
Ce ticket ne doit pas ajouter :
- la création dune nouvelle enquête ;
- le déplacement dune enquête ;
- la réparation automatique dun chemin racine incohérent ;
- la modification de `investigation.root_path` ;
- lintégration dans la fenêtre GTK ;
- une boîte de dialogue de sélection ;
- laffichage du nom de lenquête ;
- les DAO des preuves, sources ou entités ;
- la fermeture demandée par linterface utilisateur ;
- la sauvegarde automatique ;
- une migration de schéma ;
- un verrouillage multi-instance de la base.
## Fichiers principalement concernés
```text
include/core/investigation_session.h
src/core/investigation_session.c
tests/test_investigation_session.c
Makefile
```
Une adaptation limitée de ces fichiers est autorisée si nécessaire :
```text
include/core/investigation_project.h
src/core/investigation_project.c
```
## Résultat attendu
À la fin du ticket, le programme doit pouvoir ouvrir une enquête existante à partir de son dossier racine.
Une session valide doit conserver ensemble :
```text
le projet de fichiers
la connexion SQLite
les informations persistées de lenquête
```
Cette session deviendra le contexte principal utilisé ultérieurement par linterface et les futurs DAO.
## Commit attendu
Une fois tous les critères dacceptation validés :
```text
feat(core): add investigation session loader
```
Avant le commit :
```bash
make clean
make
make test
git diff --check
git status --short
```
Préparer les fichiers :
```bash
git add \
Makefile \
include/core/investigation_session.h \
src/core/investigation_session.c \
tests/test_investigation_session.c
```
Ajouter également les fichiers `InvestigationProject` uniquement sils ont réellement été modifiés :
```bash
git add \
include/core/investigation_project.h \
src/core/investigation_project.c
```
Contrôler le contenu préparé :
```bash
git diff --cached --stat
git diff --cached
```
Créer le commit :
```bash
git commit -m "feat(core): add investigation session loader"
```
Le push ne doit être effectué quaprès validation complète de la compilation, des tests et du contenu du commit.

View file

@ -8,6 +8,11 @@
#include <stdbool.h>
/**
* @brief Contexte opaque représentant les chemins d'un projet d'enquête.
*/
typedef struct InvestigationProject InvestigationProject;
/**
* @brief Crée une nouvelle enquête dans un dossier parent.
*
@ -15,7 +20,7 @@
*
* - le dossier racine de l'enquête ;
* - l'arborescence standard ;
* - le fichier vide 00_BaseDeDonnees/Enquete.sqlite.
* - le fichier 00_BaseDeDonnees/Enquete.sqlite.
*
* Le dossier parent doit déjà exister.
*
@ -28,7 +33,7 @@
* La chaîne retournée appartient au code appelant et doit être libérée
* avec g_free().
*
* @param parent_directory Chemin du dossier parent.
* @param parent_directory Chemin du dossier parent.
* @param investigation_name Nom de la nouvelle enquête.
*
* @return Le chemin complet de l'enquête créée, ou NULL en cas d'échec.
@ -38,7 +43,7 @@ char *investigation_project_create(
const char *investigation_name
);
/******************************************************************************
/**
* @brief Vérifie qu'un dossier est une enquête valide.
*
* La fonction contrôle que tous les éléments obligatoires de la structure
@ -49,9 +54,62 @@ char *investigation_project_create(
* @param investigation_path Chemin de l'enquête à vérifier.
*
* @return true si l'enquête est valide, sinon false.
******************************************************************************/
*/
bool investigation_project_validate(
const char *investigation_path
);
/**
* @brief Crée un contexte de chemins pour une enquête existante.
*
* Le chemin racine est converti en chemin canonique.
*
* Cette fonction ne crée aucun fichier et ne modifie pas le projet.
* Elle construit uniquement les chemins nécessaires à son utilisation.
*
* @param investigation_root_path Chemin racine de l'enquête.
*
* @return Un nouveau contexte InvestigationProject, ou NULL en cas d'échec.
*/
InvestigationProject *investigation_project_open(
const char *investigation_root_path
);
/**
* @brief Libère un contexte InvestigationProject.
*
* Cette fonction accepte NULL.
*
* @param project Contexte à libérer.
*/
void investigation_project_free(
InvestigationProject *project
);
/**
* @brief Retourne le chemin racine canonique de l'enquête.
*
* Le pointeur retourné appartient au contexte et ne doit pas être libéré.
*
* @param project Contexte du projet.
*
* @return Chemin racine, ou NULL si project vaut NULL.
*/
const char *investigation_project_get_root_path(
const InvestigationProject *project
);
/**
* @brief Retourne le chemin du fichier SQLite de l'enquête.
*
* Le pointeur retourné appartient au contexte et ne doit pas être libéré.
*
* @param project Contexte du projet.
*
* @return Chemin du fichier Enquete.sqlite, ou NULL si project vaut NULL.
*/
const char *investigation_project_get_database_path(
const InvestigationProject *project
);
#endif

View file

@ -0,0 +1,143 @@
/******************************************************************************
* @file investigation_session.h
* @brief Interface publique d'une session d'enquête ouverte.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_INVESTIGATION_SESSION_H
#define LABFY_INVESTIGATION_INVESTIGATION_SESSION_H
#include <glib.h>
#include "core/investigation_project.h"
#include "database/database.h"
#include "models/investigation_record.h"
/**
* @brief Session opaque représentant une enquête ouverte.
*
* Une session possède :
*
* - le contexte de fichiers InvestigationProject ;
* - la connexion Database ;
* - les informations persistées InvestigationRecord.
*/
typedef struct InvestigationSession InvestigationSession;
/**
* @brief Codes d'erreur produits lors de l'ouverture d'une session.
*/
typedef enum
{
INVESTIGATION_SESSION_ERROR_INVALID_ARGUMENT,
INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND,
INVESTIGATION_SESSION_ERROR_DATABASE_NOT_FOUND,
INVESTIGATION_SESSION_ERROR_PROJECT,
INVESTIGATION_SESSION_ERROR_DATABASE,
INVESTIGATION_SESSION_ERROR_RECORD,
INVESTIGATION_SESSION_ERROR_ROOT_MISMATCH,
INVESTIGATION_SESSION_ERROR_MEMORY
} InvestigationSessionError;
/**
* @brief Domaine d'erreur GLib du module InvestigationSession.
*/
#define INVESTIGATION_SESSION_ERROR \
investigation_session_error_quark()
/**
* @brief Retourne le domaine d'erreur du module.
*
* @return Le GQuark associé aux erreurs InvestigationSession.
*/
GQuark investigation_session_error_quark(void);
/**
* @brief Ouvre une enquête existante.
*
* La fonction :
*
* - vérifie que le chemin racine représente un dossier ;
* - construit un InvestigationProject ;
* - vérifie la présence du fichier SQLite ;
* - ouvre la connexion Database ;
* - charge le record persistant ;
* - vérifie la cohérence du chemin racine.
*
* Cette fonction ne crée aucune enquête et ne modifie pas la base.
*
* En cas de succès, la session devient propriétaire du projet,
* de la connexion Database et du record.
*
* Le paramètre error peut être NULL.
*
* @param investigation_root_path Chemin racine de l'enquête.
* @param error Adresse recevant une éventuelle erreur GLib.
*
* @return Une nouvelle session, ou NULL en cas d'échec.
*/
InvestigationSession *investigation_session_open(
const char *investigation_root_path,
GError **error
);
/**
* @brief Ferme une session d'enquête.
*
* Cette fonction libère :
*
* - le record ;
* - la connexion Database ;
* - le contexte InvestigationProject ;
* - la session.
*
* Cette fonction accepte NULL.
*
* @param session Session à fermer.
*/
void investigation_session_close(
InvestigationSession *session
);
/**
* @brief Retourne le contexte de fichiers de la session.
*
* Le pointeur retourné appartient à la session et ne doit pas être libéré.
*
* @param session Session d'enquête.
*
* @return Le projet associé, ou NULL si session vaut NULL.
*/
const InvestigationProject *investigation_session_get_project(
const InvestigationSession *session
);
/**
* @brief Retourne les informations persistées de l'enquête.
*
* Le pointeur retourné appartient à la session et ne doit pas être libéré.
*
* @param session Session d'enquête.
*
* @return Le record associé, ou NULL si session vaut NULL.
*/
const InvestigationRecord *investigation_session_get_record(
const InvestigationSession *session
);
/**
* @brief Retourne la connexion Database de la session.
*
* Le pointeur retourné appartient à la session et ne doit pas être fermé
* par le code appelant.
*
* La connexion reste modifiable afin d'être utilisée par les futurs DAO.
*
* @param session Session d'enquête.
*
* @return La connexion associée, ou NULL si session vaut NULL.
*/
Database *investigation_session_get_database(
InvestigationSession *session
);
#endif

Binary file not shown.

View file

@ -12,6 +12,27 @@
#include <glib.h>
#include <glib/gstdio.h>
/**
* @brief Nom du dossier contenant la base de données.
*/
#define INVESTIGATION_PROJECT_DATABASE_DIRECTORY \
"00_BaseDeDonnees"
/**
* @brief Nom du fichier SQLite d'une enquête.
*/
#define INVESTIGATION_PROJECT_DATABASE_FILENAME \
"Enquete.sqlite"
/**
* @brief Représentation privée des chemins d'un projet d'enquête.
*/
struct InvestigationProject
{
char *root_path;
char *database_path;
};
/**
* @brief Type d'un élément de la structure d'une enquête.
*/
@ -436,11 +457,10 @@ char *investigation_project_create(
return NULL;
}
}
database_path = g_build_filename(
investigation_path,
"00_BaseDeDonnees",
"Enquete.sqlite",
INVESTIGATION_PROJECT_DATABASE_DIRECTORY,
INVESTIGATION_PROJECT_DATABASE_FILENAME,
NULL
);
@ -460,14 +480,25 @@ char *investigation_project_create(
return NULL;
}
/*
* Le chemin est enregistré avant l'initialisation.
*
* Ainsi, si database_initialize() crée le fichier puis échoue,
* le nettoyage pourra supprimer ce fichier avant ses dossiers parents.
*
* Le tableau devient propriétaire de database_path.
*/
g_ptr_array_add(
created_paths,
database_path
);
if (!database_initialize(
database_path,
investigation_name,
investigation_path
))
{
g_free(database_path);
investigation_project_cleanup_created_paths(
created_paths
);
@ -482,22 +513,11 @@ char *investigation_project_create(
return NULL;
}
/*
* Le fichier doit également être supprimé avant ses dossiers
* si une évolution future ajoute une étape pouvant encore échouer.
*
* Le tableau devient propriétaire de database_path.
*/
g_ptr_array_add(
created_paths,
database_path
);
/*
* Tout est créé avec succès.
*
* On libère seulement le tableau et ses copies de chemins.
* Les fichiers et dossiers restent sur le disque.
* On libère uniquement le tableau et ses chaînes.
* Les fichiers et dossiers restent présents sur le disque.
*/
g_ptr_array_free(
created_paths,
@ -553,3 +573,109 @@ bool investigation_project_validate(
return true;
}
InvestigationProject *investigation_project_open(
const char *investigation_root_path
)
{
InvestigationProject *project = NULL;
char *canonical_root_path = NULL;
if (investigation_root_path == NULL ||
investigation_root_path[0] == '\0')
{
return NULL;
}
canonical_root_path = g_canonicalize_filename(
investigation_root_path,
NULL
);
if (canonical_root_path == NULL)
{
return NULL;
}
/*
* Le contexte représente une enquête existante.
*
* On vérifie seulement ici que sa racine est un dossier.
* La présence de la base SQLite sera contrôlée séparément par
* InvestigationSession afin de produire une erreur précise.
*/
if (!g_file_test(
canonical_root_path,
G_FILE_TEST_IS_DIR
))
{
g_free(canonical_root_path);
return NULL;
}
project = g_try_new0(
InvestigationProject,
1
);
if (project == NULL)
{
g_free(canonical_root_path);
return NULL;
}
project->root_path = canonical_root_path;
project->database_path = g_build_filename(
project->root_path,
INVESTIGATION_PROJECT_DATABASE_DIRECTORY,
INVESTIGATION_PROJECT_DATABASE_FILENAME,
NULL
);
if (project->database_path == NULL)
{
investigation_project_free(project);
return NULL;
}
return project;
}
void investigation_project_free(
InvestigationProject *project
)
{
if (project == NULL)
{
return;
}
g_free(project->database_path);
g_free(project->root_path);
g_free(project);
}
const char *investigation_project_get_root_path(
const InvestigationProject *project
)
{
if (project == NULL)
{
return NULL;
}
return project->root_path;
}
const char *investigation_project_get_database_path(
const InvestigationProject *project
)
{
if (project == NULL)
{
return NULL;
}
return project->database_path;
}

View file

@ -0,0 +1,374 @@
/******************************************************************************
* @file investigation_session.c
* @brief Gestion d'une session d'enquête ouverte.
******************************************************************************/
#include "core/investigation_session.h"
#include "dao/investigation_dao.h"
#include "database/error.h"
/**
* @brief Représentation privée d'une session d'enquête.
*/
struct InvestigationSession
{
InvestigationProject *project;
Database *database;
InvestigationRecord *record;
};
/**
* @brief Enregistre une erreur littérale si un GError est demandé.
*
* @param error Adresse recevant l'erreur.
* @param error_code Code d'erreur du module.
* @param error_message Message associé.
*/
static void investigation_session_set_error_literal(
GError **error,
InvestigationSessionError error_code,
const char *error_message
)
{
if (error == NULL)
{
return;
}
g_set_error_literal(
error,
INVESTIGATION_SESSION_ERROR,
error_code,
error_message
);
}
GQuark investigation_session_error_quark(void)
{
return g_quark_from_static_string(
"investigation-session-error-quark"
);
}
InvestigationSession *investigation_session_open(
const char *investigation_root_path,
GError **error
)
{
InvestigationSession *session = NULL;
InvestigationProject *project = NULL;
InvestigationRecord *record = NULL;
Database *database = NULL;
char *canonical_root_path = NULL;
char *canonical_record_root_path = NULL;
const char *database_path = NULL;
const char *record_root_path = NULL;
const char *database_error_message = NULL;
/*
* Convention GLib :
*
* un GError existant ne doit pas être remplacé.
*/
g_return_val_if_fail(
error == NULL || *error == NULL,
NULL
);
if (investigation_root_path == NULL ||
investigation_root_path[0] == '\0')
{
investigation_session_set_error_literal(
error,
INVESTIGATION_SESSION_ERROR_INVALID_ARGUMENT,
"Le chemin racine de l'enquête est invalide."
);
return NULL;
}
canonical_root_path = g_canonicalize_filename(
investigation_root_path,
NULL
);
if (canonical_root_path == NULL)
{
investigation_session_set_error_literal(
error,
INVESTIGATION_SESSION_ERROR_MEMORY,
"Impossible d'allouer le chemin canonique de l'enquête."
);
return NULL;
}
if (!g_file_test(
canonical_root_path,
G_FILE_TEST_IS_DIR
))
{
g_set_error(
error,
INVESTIGATION_SESSION_ERROR,
INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND,
"Le chemin racine '%s' n'est pas un dossier existant.",
canonical_root_path
);
goto cleanup;
}
project = investigation_project_open(
canonical_root_path
);
if (project == NULL)
{
investigation_session_set_error_literal(
error,
INVESTIGATION_SESSION_ERROR_PROJECT,
"Impossible de construire le contexte du projet d'enquête."
);
goto cleanup;
}
database_path = investigation_project_get_database_path(
project
);
if (database_path == NULL)
{
investigation_session_set_error_literal(
error,
INVESTIGATION_SESSION_ERROR_PROJECT,
"Le projet ne fournit aucun chemin vers la base de données."
);
goto cleanup;
}
/*
* Cette vérification doit précéder database_open().
*
* SQLite peut créer automatiquement une base absente lors de
* l'ouverture. Une session ne doit jamais créer silencieusement
* une nouvelle base.
*/
if (!g_file_test(
database_path,
G_FILE_TEST_IS_REGULAR
))
{
g_set_error(
error,
INVESTIGATION_SESSION_ERROR,
INVESTIGATION_SESSION_ERROR_DATABASE_NOT_FOUND,
"Le fichier SQLite de l'enquête est absent : '%s'.",
database_path
);
goto cleanup;
}
database = database_open(
database_path
);
if (database == NULL)
{
g_set_error(
error,
INVESTIGATION_SESSION_ERROR,
INVESTIGATION_SESSION_ERROR_DATABASE,
"Impossible d'ouvrir la base de données '%s'.",
database_path
);
goto cleanup;
}
record = investigation_dao_load(
database
);
if (record == NULL)
{
/*
* Le message doit être copié dans le GError avant la fermeture
* de Database, car il appartient à l'objet Database.
*/
database_error_message = database_error_get_message(
database
);
if (database_error_message == NULL ||
database_error_message[0] == '\0')
{
database_error_message =
"Impossible de charger les informations de l'enquête.";
}
g_set_error(
error,
INVESTIGATION_SESSION_ERROR,
INVESTIGATION_SESSION_ERROR_RECORD,
"%s",
database_error_message
);
goto cleanup;
}
record_root_path = investigation_record_get_root_path(
record
);
if (record_root_path == NULL ||
record_root_path[0] == '\0')
{
investigation_session_set_error_literal(
error,
INVESTIGATION_SESSION_ERROR_RECORD,
"Le chemin racine enregistré dans la base est invalide."
);
goto cleanup;
}
canonical_record_root_path = g_canonicalize_filename(
record_root_path,
NULL
);
if (canonical_record_root_path == NULL)
{
investigation_session_set_error_literal(
error,
INVESTIGATION_SESSION_ERROR_MEMORY,
"Impossible d'allouer le chemin canonique enregistré."
);
goto cleanup;
}
if (g_strcmp0(
canonical_root_path,
canonical_record_root_path
) != 0)
{
g_set_error(
error,
INVESTIGATION_SESSION_ERROR,
INVESTIGATION_SESSION_ERROR_ROOT_MISMATCH,
"Le chemin sélectionné '%s' ne correspond pas au chemin "
"enregistré '%s'.",
canonical_root_path,
canonical_record_root_path
);
goto cleanup;
}
session = g_try_new0(
InvestigationSession,
1
);
if (session == NULL)
{
investigation_session_set_error_literal(
error,
INVESTIGATION_SESSION_ERROR_MEMORY,
"Impossible d'allouer la session d'enquête."
);
goto cleanup;
}
/*
* La session devient propriétaire des trois objets.
*/
session->project = project;
session->database = database;
session->record = record;
project = NULL;
database = NULL;
record = NULL;
cleanup:
g_free(canonical_record_root_path);
g_free(canonical_root_path);
investigation_record_free(record);
database_close(database);
investigation_project_free(project);
return session;
}
void investigation_session_close(
InvestigationSession *session
)
{
if (session == NULL)
{
return;
}
investigation_record_free(
session->record
);
database_close(
session->database
);
investigation_project_free(
session->project
);
g_free(session);
}
const InvestigationProject *investigation_session_get_project(
const InvestigationSession *session
)
{
if (session == NULL)
{
return NULL;
}
return session->project;
}
const InvestigationRecord *investigation_session_get_record(
const InvestigationSession *session
)
{
if (session == NULL)
{
return NULL;
}
return session->record;
}
Database *investigation_session_get_database(
InvestigationSession *session
)
{
if (session == NULL)
{
return NULL;
}
return session->database;
}

Binary file not shown.

View file

@ -735,6 +735,181 @@ static void test_validate_directory_replaced_by_file(void)
g_free(temporary_parent);
}
/**
* @brief Vérifie l'ouverture d'un projet existant et ses accesseurs.
*/
static void test_open_existing_project(void)
{
char *temporary_parent = NULL;
char *investigation_path = NULL;
char *path_with_dot = NULL;
char *expected_root_path = NULL;
char *expected_database_path = NULL;
InvestigationProject *project = NULL;
GError *error = NULL;
temporary_parent = g_dir_make_tmp(
"labfy-investigation-project-open-test-XXXXXX",
&error
);
assert(temporary_parent != NULL);
assert(error == NULL);
investigation_path = investigation_project_create(
temporary_parent,
"Enquete_Ouverte"
);
assert(investigation_path != NULL);
/*
* Le segment "." permet de vérifier la canonicalisation du chemin.
*/
path_with_dot = g_build_filename(
investigation_path,
".",
NULL
);
assert(path_with_dot != NULL);
expected_root_path = g_canonicalize_filename(
investigation_path,
NULL
);
assert(expected_root_path != NULL);
expected_database_path = g_build_filename(
expected_root_path,
"00_BaseDeDonnees",
"Enquete.sqlite",
NULL
);
assert(expected_database_path != NULL);
project = investigation_project_open(
path_with_dot
);
assert(project != NULL);
assert(
strcmp(
investigation_project_get_root_path(project),
expected_root_path
) == 0
);
assert(
strcmp(
investigation_project_get_database_path(project),
expected_database_path
) == 0
);
investigation_project_free(project);
assert(
test_remove_path_recursively(
temporary_parent
)
);
g_free(expected_database_path);
g_free(expected_root_path);
g_free(path_with_dot);
g_free(investigation_path);
g_free(temporary_parent);
}
/**
* @brief Vérifie le refus des chemins invalides lors de l'ouverture.
*/
static void test_open_invalid_project_paths(void)
{
char *temporary_directory = NULL;
char *temporary_file = NULL;
GError *error = NULL;
assert(
investigation_project_open(NULL) == NULL
);
assert(
investigation_project_open("") == NULL
);
assert(
investigation_project_open(
"/tmp/labfy-investigation-project-does-not-exist"
) == NULL
);
temporary_directory = g_dir_make_tmp(
"labfy-investigation-project-open-file-test-XXXXXX",
&error
);
assert(temporary_directory != NULL);
assert(error == NULL);
temporary_file = g_build_filename(
temporary_directory,
"not-a-directory.txt",
NULL
);
assert(temporary_file != NULL);
assert(
g_file_set_contents(
temporary_file,
"test\n",
-1,
&error
)
);
assert(error == NULL);
assert(
investigation_project_open(
temporary_file
) == NULL
);
assert(
test_remove_path_recursively(
temporary_directory
)
);
g_free(temporary_file);
g_free(temporary_directory);
}
/**
* @brief Vérifie les fonctions acceptant un projet NULL.
*/
static void test_open_project_null_instance(void)
{
assert(
investigation_project_get_root_path(NULL) == NULL
);
assert(
investigation_project_get_database_path(NULL) == NULL
);
investigation_project_free(NULL);
}
int main(void)
{
test_create_valid_investigation();
@ -749,6 +924,11 @@ int main(void)
test_validate_missing_directory();
test_validate_directory_replaced_by_file();
test_open_existing_project();
test_open_invalid_project_paths();
test_open_project_null_instance();
printf(
"InvestigationProject : tous les tests sont valides.\n"
);

BIN
tests/test_investigation_session Executable file

Binary file not shown.

View file

@ -0,0 +1,684 @@
/******************************************************************************
* @file test_investigation_session.c
* @brief Tests du contexte représentant une enquête ouverte.
******************************************************************************/
#include "core/investigation_session.h"
#include "core/investigation_project.h"
#include "dao/investigation_dao.h"
#include "database/database.h"
#include "models/investigation_record.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <glib.h>
#include <glib/gstdio.h>
static gboolean test_remove_path_recursively(
const char *path
)
{
GDir *directory = NULL;
const char *entry_name = NULL;
if (path == NULL)
{
return FALSE;
}
if (!g_file_test(
path,
G_FILE_TEST_EXISTS
))
{
return TRUE;
}
if (!g_file_test(
path,
G_FILE_TEST_IS_DIR
))
{
return g_remove(path) == 0;
}
directory = g_dir_open(
path,
0,
NULL
);
if (directory == NULL)
{
return FALSE;
}
while ((entry_name = g_dir_read_name(directory)) != NULL)
{
char *entry_path = NULL;
gboolean removed = FALSE;
entry_path = g_build_filename(
path,
entry_name,
NULL
);
if (entry_path == NULL)
{
g_dir_close(directory);
return FALSE;
}
removed = test_remove_path_recursively(
entry_path
);
g_free(entry_path);
if (!removed)
{
g_dir_close(directory);
return FALSE;
}
}
g_dir_close(directory);
return g_rmdir(path) == 0;
}
static void test_assert_session_error(
const GError *error,
InvestigationSessionError expected_code
)
{
assert(error != NULL);
assert(error->domain == INVESTIGATION_SESSION_ERROR);
assert(error->code == (gint) expected_code);
assert(error->message != NULL);
assert(error->message[0] != '\0');
}
static void test_open_valid_session(void)
{
char *temporary_parent = NULL;
char *investigation_path = NULL;
char *canonical_root_path = NULL;
InvestigationSession *session = NULL;
const InvestigationProject *project = NULL;
const InvestigationRecord *record = NULL;
InvestigationRecord *reloaded_record = NULL;
Database *database = NULL;
const char *record_id = NULL;
const char *reloaded_record_id = NULL;
GError *error = NULL;
temporary_parent = g_dir_make_tmp(
"labfy-investigation-session-valid-XXXXXX",
&error
);
assert(temporary_parent != NULL);
assert(error == NULL);
investigation_path = investigation_project_create(
temporary_parent,
"Enquete_Session"
);
assert(investigation_path != NULL);
canonical_root_path = g_canonicalize_filename(
investigation_path,
NULL
);
assert(canonical_root_path != NULL);
session = investigation_session_open(
investigation_path,
&error
);
assert(session != NULL);
assert(error == NULL);
project = investigation_session_get_project(
session
);
database = investigation_session_get_database(
session
);
record = investigation_session_get_record(
session
);
assert(project != NULL);
assert(database != NULL);
assert(record != NULL);
assert(
strcmp(
investigation_project_get_root_path(project),
canonical_root_path
) == 0
);
assert(
strcmp(
investigation_record_get_name(record),
"Enquete_Session"
) == 0
);
assert(
strcmp(
investigation_record_get_root_path(record),
canonical_root_path
) == 0
);
record_id = investigation_record_get_id(
record
);
assert(record_id != NULL);
assert(g_uuid_string_is_valid(record_id));
assert(
investigation_record_get_created_at(record) != NULL
);
assert(
investigation_record_get_created_at(record)[0] != '\0'
);
assert(
investigation_record_get_updated_at(record) != NULL
);
assert(
investigation_record_get_updated_at(record)[0] != '\0'
);
reloaded_record = investigation_dao_load(
database
);
assert(reloaded_record != NULL);
reloaded_record_id = investigation_record_get_id(
reloaded_record
);
assert(reloaded_record_id != NULL);
assert(
strcmp(
record_id,
reloaded_record_id
) == 0
);
investigation_record_free(reloaded_record);
investigation_session_close(session);
assert(
test_remove_path_recursively(
temporary_parent
)
);
g_free(canonical_root_path);
g_free(investigation_path);
g_free(temporary_parent);
}
static void test_open_invalid_parameters(void)
{
InvestigationSession *session = NULL;
GError *error = NULL;
session = investigation_session_open(
NULL,
&error
);
assert(session == NULL);
test_assert_session_error(
error,
INVESTIGATION_SESSION_ERROR_INVALID_ARGUMENT
);
g_clear_error(&error);
session = investigation_session_open(
"",
&error
);
assert(session == NULL);
test_assert_session_error(
error,
INVESTIGATION_SESSION_ERROR_INVALID_ARGUMENT
);
g_clear_error(&error);
assert(
investigation_session_open(
NULL,
NULL
) == NULL
);
assert(
investigation_session_open(
"",
NULL
) == NULL
);
}
static void test_open_missing_root(void)
{
char *temporary_parent = NULL;
char *missing_root_path = NULL;
InvestigationSession *session = NULL;
GError *error = NULL;
temporary_parent = g_dir_make_tmp(
"labfy-investigation-session-missing-root-XXXXXX",
&error
);
assert(temporary_parent != NULL);
assert(error == NULL);
missing_root_path = g_build_filename(
temporary_parent,
"does-not-exist",
NULL
);
assert(missing_root_path != NULL);
session = investigation_session_open(
missing_root_path,
&error
);
assert(session == NULL);
test_assert_session_error(
error,
INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND
);
g_clear_error(&error);
assert(
!g_file_test(
missing_root_path,
G_FILE_TEST_EXISTS
)
);
assert(
test_remove_path_recursively(
temporary_parent
)
);
g_free(missing_root_path);
g_free(temporary_parent);
}
static void test_open_root_is_file(void)
{
char *temporary_parent = NULL;
char *file_path = NULL;
InvestigationSession *session = NULL;
GError *error = NULL;
temporary_parent = g_dir_make_tmp(
"labfy-investigation-session-root-file-XXXXXX",
&error
);
assert(temporary_parent != NULL);
assert(error == NULL);
file_path = g_build_filename(
temporary_parent,
"not-a-directory.txt",
NULL
);
assert(file_path != NULL);
assert(
g_file_set_contents(
file_path,
"test\n",
-1,
&error
)
);
assert(error == NULL);
session = investigation_session_open(
file_path,
&error
);
assert(session == NULL);
test_assert_session_error(
error,
INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND
);
g_clear_error(&error);
assert(
test_remove_path_recursively(
temporary_parent
)
);
g_free(file_path);
g_free(temporary_parent);
}
static void test_open_missing_database(void)
{
char *investigation_root = NULL;
char *database_directory = NULL;
char *database_path = NULL;
InvestigationSession *session = NULL;
GError *error = NULL;
investigation_root = g_dir_make_tmp(
"labfy-investigation-session-missing-database-XXXXXX",
&error
);
assert(investigation_root != NULL);
assert(error == NULL);
database_directory = g_build_filename(
investigation_root,
"00_BaseDeDonnees",
NULL
);
assert(database_directory != NULL);
assert(
g_mkdir(
database_directory,
0700
) == 0
);
database_path = g_build_filename(
database_directory,
"Enquete.sqlite",
NULL
);
assert(database_path != NULL);
assert(
!g_file_test(
database_path,
G_FILE_TEST_EXISTS
)
);
session = investigation_session_open(
investigation_root,
&error
);
assert(session == NULL);
test_assert_session_error(
error,
INVESTIGATION_SESSION_ERROR_DATABASE_NOT_FOUND
);
g_clear_error(&error);
assert(
!g_file_test(
database_path,
G_FILE_TEST_EXISTS
)
);
assert(
test_remove_path_recursively(
investigation_root
)
);
g_free(database_path);
g_free(database_directory);
g_free(investigation_root);
}
static void test_open_invalid_database(void)
{
char *investigation_root = NULL;
char *database_directory = NULL;
char *database_path = NULL;
InvestigationSession *session = NULL;
GError *error = NULL;
investigation_root = g_dir_make_tmp(
"labfy-investigation-session-invalid-database-XXXXXX",
&error
);
assert(investigation_root != NULL);
assert(error == NULL);
database_directory = g_build_filename(
investigation_root,
"00_BaseDeDonnees",
NULL
);
assert(database_directory != NULL);
assert(
g_mkdir(
database_directory,
0700
) == 0
);
database_path = g_build_filename(
database_directory,
"Enquete.sqlite",
NULL
);
assert(database_path != NULL);
assert(
g_file_set_contents(
database_path,
"",
0,
&error
)
);
assert(error == NULL);
session = investigation_session_open(
investigation_root,
&error
);
assert(session == NULL);
test_assert_session_error(
error,
INVESTIGATION_SESSION_ERROR_RECORD
);
g_clear_error(&error);
assert(
test_remove_path_recursively(
investigation_root
)
);
g_free(database_path);
g_free(database_directory);
g_free(investigation_root);
}
static void test_open_root_mismatch(void)
{
char *investigation_root = NULL;
char *database_directory = NULL;
char *database_path = NULL;
char *different_root_path = NULL;
InvestigationSession *session = NULL;
GError *error = NULL;
investigation_root = g_dir_make_tmp(
"labfy-investigation-session-root-mismatch-XXXXXX",
&error
);
assert(investigation_root != NULL);
assert(error == NULL);
database_directory = g_build_filename(
investigation_root,
"00_BaseDeDonnees",
NULL
);
assert(database_directory != NULL);
assert(
g_mkdir(
database_directory,
0700
) == 0
);
database_path = g_build_filename(
database_directory,
"Enquete.sqlite",
NULL
);
assert(database_path != NULL);
different_root_path = g_build_filename(
investigation_root,
"autre-emplacement",
NULL
);
assert(different_root_path != NULL);
assert(
database_initialize(
database_path,
"Enquete_Incoherente",
different_root_path
)
);
session = investigation_session_open(
investigation_root,
&error
);
assert(session == NULL);
test_assert_session_error(
error,
INVESTIGATION_SESSION_ERROR_ROOT_MISMATCH
);
g_clear_error(&error);
assert(
test_remove_path_recursively(
investigation_root
)
);
g_free(different_root_path);
g_free(database_path);
g_free(database_directory);
g_free(investigation_root);
}
static void test_null_session_accessors(void)
{
assert(
investigation_session_get_project(NULL) == NULL
);
assert(
investigation_session_get_record(NULL) == NULL
);
assert(
investigation_session_get_database(NULL) == NULL
);
investigation_session_close(NULL);
}
int main(void)
{
test_open_valid_session();
test_open_invalid_parameters();
test_open_missing_root();
test_open_root_is_file();
test_open_missing_database();
test_open_invalid_database();
test_open_root_mismatch();
test_null_session_accessors();
printf(
"InvestigationSession : tous les tests sont valides.\n"
);
return 0;
}