feat(core): add investigation session loader
This commit is contained in:
parent
137e75722d
commit
4efdfe4865
12 changed files with 2382 additions and 24 deletions
21
Makefile
21
Makefile
|
|
@ -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
|
||||
|
|
|
|||
776
docs/tickets/open/TICKET-028.md
Normal file
776
docs/tickets/open/TICKET-028.md
Normal file
|
|
@ -0,0 +1,776 @@
|
|||
# Ticket #028 — Ajouter l’ouverture d’une enquête existante
|
||||
|
||||
## Contexte
|
||||
|
||||
Les tickets précédents ont permis de mettre en place :
|
||||
|
||||
- la création transactionnelle d’une base d’enquê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 l’unique 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 l’enquête ;
|
||||
- le chemin du fichier `Enquete.sqlite`.
|
||||
|
||||
Cependant, l’application ne possède pas encore d’objet 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 d’une enquête ouverte.
|
||||
|
||||
## Objectif
|
||||
|
||||
Créer un type opaque `InvestigationSession` chargé d’ouvrir une enquête existante et de conserver :
|
||||
|
||||
- son contexte de fichiers `InvestigationProject` ;
|
||||
- sa connexion `Database` ;
|
||||
- ses informations persistées `InvestigationRecord`.
|
||||
|
||||
L’ouverture 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 d’ouverture
|
||||
|
||||
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 d’erreur 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
|
||||
```
|
||||
|
||||
Lorsqu’une 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 d’ouverture
|
||||
|
||||
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 d’erreur attendu est :
|
||||
|
||||
```c
|
||||
INVESTIGATION_SESSION_ERROR_INVALID_ARGUMENT
|
||||
```
|
||||
|
||||
Le paramètre `error` peut être `NULL`.
|
||||
|
||||
Si `error` n’est pas `NULL`, il doit respecter les conventions GLib :
|
||||
|
||||
```c
|
||||
*error == NULL
|
||||
```
|
||||
|
||||
au moment de l’appel.
|
||||
|
||||
### 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 l’API 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 l’API 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 l’ouverture 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 n’existe pas, retourner :
|
||||
|
||||
```c
|
||||
INVESTIGATION_SESSION_ERROR_DATABASE_NOT_FOUND
|
||||
```
|
||||
|
||||
L’ouverture d’une 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 l’enquê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 d’erreur 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, l’ouverture doit échouer avec :
|
||||
|
||||
```c
|
||||
INVESTIGATION_SESSION_ERROR_ROOT_MISMATCH
|
||||
```
|
||||
|
||||
Cette vérification évite d’ouvrir une base copiée ou déplacée sans détecter l’incohérence.
|
||||
|
||||
Le déplacement volontaire d’une enquête sera traité dans un ticket distinct.
|
||||
|
||||
### 11. Construire la session
|
||||
|
||||
La session ne doit être créée qu’aprè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 d’allocation, 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 l’appelant.
|
||||
|
||||
Les accesseurs doivent retourner `NULL` si la session reçue est `NULL`.
|
||||
|
||||
`Database` reste non constante car les futurs DAO auront besoin d’une 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 d’ouverture valide
|
||||
|
||||
Créer un dossier temporaire.
|
||||
|
||||
Initialiser une base avec :
|
||||
|
||||
```c
|
||||
database_initialize()
|
||||
```
|
||||
|
||||
Ouvrir ensuite l’enquête avec :
|
||||
|
||||
```c
|
||||
investigation_session_open()
|
||||
```
|
||||
|
||||
Vérifier :
|
||||
|
||||
- la session n’est pas `NULL` ;
|
||||
- aucune erreur n’est produite ;
|
||||
- le projet est disponible ;
|
||||
- la connexion Database est disponible ;
|
||||
- le record est disponible ;
|
||||
- le nom de l’enquête est correct ;
|
||||
- le chemin racine est correct ;
|
||||
- l’UUID est valide ;
|
||||
- `created_at` n’est pas vide ;
|
||||
- `updated_at` n’est 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 d’un dossier inexistant
|
||||
|
||||
Utiliser un chemin inexistant.
|
||||
|
||||
Vérifier :
|
||||
|
||||
```text
|
||||
résultat == NULL
|
||||
erreur == INVESTIGATION_SESSION_ERROR_ROOT_NOT_FOUND
|
||||
message non vide
|
||||
```
|
||||
|
||||
### Test d’un chemin qui n’est 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 d’une 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 d’une 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 d’un chemin racine incohérent
|
||||
|
||||
Créer une base avec un chemin racine enregistré différent du dossier utilisé pour l’ouverture.
|
||||
|
||||
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 l’ouverture valide d’une 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 l’allocation 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 d’acceptation
|
||||
|
||||
- [ ] 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 l’ouverture.
|
||||
- [ ] Le chemin racine est normalisé.
|
||||
- [ ] Le chemin de la base provient de `InvestigationProject`.
|
||||
- [ ] Le fichier SQLite doit exister avant l’appel à `database_open()`.
|
||||
- [ ] L’ouverture 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 l’ouverture.
|
||||
- [ ] 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 n’apparaît dans l’API de la session.
|
||||
- [ ] Aucune dépendance GTK n’est ajoutée.
|
||||
- [ ] Les tests d’ouverture 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 n’est 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 d’une nouvelle enquête ;
|
||||
- le déplacement d’une enquête ;
|
||||
- la réparation automatique d’un chemin racine incohérent ;
|
||||
- la modification de `investigation.root_path` ;
|
||||
- l’intégration dans la fenêtre GTK ;
|
||||
- une boîte de dialogue de sélection ;
|
||||
- l’affichage du nom de l’enquête ;
|
||||
- les DAO des preuves, sources ou entités ;
|
||||
- la fermeture demandée par l’interface 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 l’enquête
|
||||
```
|
||||
|
||||
Cette session deviendra le contexte principal utilisé ultérieurement par l’interface et les futurs DAO.
|
||||
|
||||
## Commit attendu
|
||||
|
||||
Une fois tous les critères d’acceptation 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 s’ils 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é qu’après validation complète de la compilation, des tests et du contenu du commit.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
143
include/core/investigation_session.h
Normal file
143
include/core/investigation_session.h
Normal 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.
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
374
src/core/investigation_session.c
Normal file
374
src/core/investigation_session.c
Normal 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.
|
|
@ -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
BIN
tests/test_investigation_session
Executable file
Binary file not shown.
684
tests/test_investigation_session.c
Normal file
684
tests/test_investigation_session.c
Normal 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;
|
||||
}
|
||||
Loading…
Reference in a new issue