labfy-investigation/docs/tickets/closed/TICKET-032.md
2026-07-17 10:05:29 +02:00

10 KiB
Raw Blame History

Ticket #032 — Factoriser le chargement et linstallation dune enquête

Contexte

Après le ticket #031.1, lapplication sait :

  • créer une enquête ;
  • ouvrir une enquête existante ;
  • remplacer la session active ;
  • fermer proprement lapplication.

Cependant, les flux « Nouvelle enquête » et « Ouvrir une enquête » dupliquent encore une partie importante de la logique :

investigation_session_open()
→ récupération du projet
→ récupération du chemin racine
→ investigation_tree_builder_build()
→ application_install_session()
→ nettoyage en cas déchec

Cette duplication augmentera avec les futures fonctions :

  • enquêtes récentes ;
  • ouverture depuis la ligne de commande ;
  • restauration de session ;
  • ouverture depuis un rapport ou une archive.

Objectif

Créer dans src/core/application.c une fonction interne unique qui ouvre et installe une enquête à partir de son dossier racine.

Contrat attendu :

static gboolean application_open_and_install_investigation(
    Application *application,
    const char *root_path,
    GError **error
);

Cette fonction doit garantir :

succès :
    Application devient propriétaire de la nouvelle session
    Application devient propriétaire du nouvel arbre
    ancienne session libérée
    ancien arbre libéré
    fenêtre mise à jour

échec :
    ancienne session conservée
    ancien arbre conservé
    nouvelle session libérée
    nouvel arbre libéré
    erreur transmise à lappelant

Travail à réaliser

1. Ajouter un domaine derreur privé

Dans src/core/application.c, ajouter un domaine derreur uniquement utilisé par le contrôleur.

Exemple :

typedef enum
{
    APPLICATION_OPEN_ERROR_INVALID_ARGUMENT,
    APPLICATION_OPEN_ERROR_INVALID_PROJECT,
    APPLICATION_OPEN_ERROR_TREE_BUILD,
    APPLICATION_OPEN_ERROR_INSTALL
} ApplicationOpenError;

Ajouter :

#define APPLICATION_OPEN_ERROR \
    application_open_error_quark()

Puis :

static GQuark application_open_error_quark(void)
{
    return g_quark_from_static_string(
        "labfy-investigation-application-open-error"
    );
}

Le domaine reste privé à application.c.


2. Créer la fonction factorisée

Ajouter :

static gboolean application_open_and_install_investigation(
    Application *application,
    const char *root_path,
    GError **error
);

La fonction doit :

  1. valider application ;
  2. valider application->main_window ;
  3. valider root_path ;
  4. ouvrir une nouvelle InvestigationSession ;
  5. récupérer son InvestigationProject ;
  6. récupérer le chemin racine canonique ;
  7. construire un nouvel InvestigationTreeModel ;
  8. appeler application_install_session() ;
  9. transférer la propriété uniquement en cas de succès.

Règle de propriété

Avant application_install_session() :

la fonction possède new_session
la fonction possède new_tree_model

Après succès :

Application possède new_session
Application possède new_tree_model

Après échec :

la fonction doit libérer les objets quelle possède encore

Validation de GError

La fonction doit respecter la convention GLib :

g_return_val_if_fail(
    error == NULL || *error == NULL,
    FALSE
);

Lutilisation de g_return_val_if_fail() est acceptable ici pour vérifier le contrat du développeur.

Les erreurs utilisateur doivent être produites avec :

g_set_error()
g_set_error_literal()
g_propagate_prefixed_error()

3. Propager lerreur de InvestigationSession

Si :

investigation_session_open()

échoue, la fonction doit conserver lerreur métier dorigine et lui ajouter du contexte.

Exemple conceptuel :

Impossible douvrir lenquête : la base SQLite est absente

Ne pas remplacer lerreur précise par un simple :

Erreur inconnue

lorsquun GError est disponible.


4. Refactoriser louverture dune enquête existante

application_on_folder_selected() ne doit plus contenir directement :

investigation_session_open()
investigation_tree_builder_build()
application_install_session()

Elle doit seulement :

  1. accepter lannulation ;
  2. appeler application_open_and_install_investigation() ;
  3. journaliser lerreur ;
  4. libérer le GError.

Structure attendue :

static void application_on_folder_selected(
    const char *folder_path,
    gpointer user_data
)
{
    Application *application = user_data;
    GError *error = NULL;

    if (application == NULL ||
        folder_path == NULL)
    {
        return;
    }

    if (!application_open_and_install_investigation(
            application,
            folder_path,
            &error
        ))
    {
        g_warning(
            "Impossible d'ouvrir l'enquête '%s' : %s",
            folder_path,
            error != NULL
                ? error->message
                : "erreur inconnue"
        );

        g_clear_error(&error);
    }
}

Lannulation ne doit pas produire de warning.


5. Refactoriser la création dune enquête

application_on_create_investigation() doit conserver uniquement :

  1. la création physique du projet ;
  2. lappel à la fonction factorisée ;
  3. le message spécifique indiquant que le projet existe sur le disque si son ouverture échoue.

Le flux devient :

investigation_project_create()
→ application_open_and_install_investigation()

La fonction ne doit plus reconstruire elle-même :

session
projet
chemin canonique
arbre
installation

En cas déchec après création, le dossier créé reste volontairement sur le disque.

Le message doit être explicite :

Lenquête a été créée dans « ... », mais son ouverture a échoué : ...

6. Ne pas modifier application_install_session()

Cette fonction conserve sa responsabilité actuelle :

  • valider les objets prêts à installer ;
  • libérer lancien état ;
  • transférer la propriété ;
  • mettre à jour MainWindow.

Le nouveau helper prépare les objets.

application_install_session() réalise le remplacement final.


Pseudo-code de la fonction factorisée

static gboolean application_open_and_install_investigation(
    Application *application,
    const char *root_path,
    GError **error
)
{
    InvestigationSession *new_session = NULL;
    InvestigationTreeModel *new_tree_model = NULL;
    const InvestigationProject *project = NULL;
    const char *canonical_root_path = NULL;
    GError *session_error = NULL;

    valider les arguments;

    new_session = investigation_session_open(
        root_path,
        &session_error
    );

    si échec :
        propager session_error avec contexte;
        return FALSE;

    project = investigation_session_get_project(
        new_session
    );

    valider project;

    canonical_root_path =
        investigation_project_get_root_path(project);

    valider canonical_root_path;

    new_tree_model =
        investigation_tree_builder_build(
            canonical_root_path
        );

    si échec :
        produire GError;
        fermer new_session;
        return FALSE;

    si application_install_session() échoue :
        produire GError;
        libérer new_tree_model;
        fermer new_session;
        return FALSE;

    return TRUE;
}

Tests manuels

Création valide

  1. lancer lapplication ;
  2. créer une enquête ;
  3. vérifier le titre ;
  4. vérifier la barre détat ;
  5. vérifier larborescence.

Ouverture valide

  1. fermer et relancer ;
  2. ouvrir lenquête créée ;
  3. vérifier le même résultat.

Remplacement valide

  1. ouvrir une enquête A ;
  2. ouvrir une enquête B ;
  3. vérifier que B remplace A.

Échec douverture

  1. ouvrir une enquête valide A ;
  2. tenter douvrir un dossier invalide ;
  3. vérifier que A reste active ;
  4. vérifier que lerreur est journalisée.

Échec après création

Provoquer si possible un échec douverture après création.

Vérifier :

  • le dossier créé reste présent ;
  • lancienne session reste active ;
  • aucun double free ;
  • aucun crash.

Annulation

Annuler le sélecteur de dossier.

Vérifier :

  • aucun warning ;
  • aucun changement détat ;
  • aucun crash.

Critères dacceptation

  • Une seule fonction ouvre une session et construit son arbre.
  • La création utilise cette fonction.
  • Louverture utilise cette fonction.
  • application_install_session() nest pas dupliquée.
  • Lancienne session reste active en cas déchec.
  • Lancien arbre reste actif en cas déchec.
  • Les nouveaux objets sont libérés en cas déchec.
  • Les erreurs de InvestigationSession sont propagées.
  • Lannulation ne produit pas derreur.
  • Aucun code SQLite direct nest ajouté.
  • Aucun comportement GTK visible nest cassé.
  • make réussit.
  • make test réussit.
  • git diff --check ne retourne aucune erreur.

Audit attendu

La logique douverture ne doit apparaître quune seule fois :

rg -n \
    'investigation_session_open|investigation_tree_builder_build' \
    src/core/application.c

Résultat attendu :

une occurrence de investigation_session_open
une occurrence de investigation_tree_builder_build

La création et louverture doivent appeler le helper :

rg -n \
    'application_open_and_install_investigation' \
    src/core/application.c

Vérifier labsence de SQLite direct :

rg -n \
    'sqlite3_|#include <sqlite3.h>' \
    src/core/application.c

Résultat attendu :

aucune sortie

Vérifier le format :

git diff --check

Fichiers concernés

src/core/application.c

Aucune modification publique nest normalement nécessaire dans :

include/core/application.h

Commit attendu

make clean
make
make test
git diff --check
git status --short
git add src/core/application.c
git diff --cached --stat
git diff --cached
git commit -m "refactor(core): centralize investigation loading"
git push

Résultat attendu

Après ce ticket, tous les futurs points dentrée utiliseront le même flux :

Création
Ouverture manuelle
Enquêtes récentes
Ligne de commande
Restauration de session
        ↓
application_open_and_install_investigation()

Le ticket #033 pourra ensuite afficher graphiquement les GError déjà correctement produits par ce flux.