diff --git a/android-client/PMA.VaultPatch.pas b/android-client/PMA.VaultPatch.pas index c5bc99e..4016550 100644 --- a/android-client/PMA.VaultPatch.pas +++ b/android-client/PMA.VaultPatch.pas @@ -203,7 +203,8 @@ end; function MergeSnapshots(const ALocalJson, ARemoteJson: string): string; var LLocal, LMerged: TJSONObject; - LLocalEntries, LOutEntries, LLocalStones, LOutStones: TJSONArray; + LLocalEntries, LOutEntries, LLocalStones, LOutStones, + LLocalFolders: TJSONArray; LEntry, LOther: TJSONObject; I, J: Integer; LUuid, LMine, LTheirs, LKilled: string; @@ -223,6 +224,22 @@ begin LMerged.AddPair('tombstones', LOutStones); end; + // 0. LES DOSSIERS DU LOCAL REJOIGNENT CEUX DU DISTANT. + // + // Oubli trouve a la relecture : la fusion ne portait qu'entrees et + // pierres tombales. Un dossier cree ici - hors connexion, ou juste avant + // qu'un autre appareil publie - disparaissait donc de `folders`, tandis + // que les entrees gardaient son nom. Elles se retrouvaient rangees dans + // un dossier que rien n'affiche : introuvables sans le savoir. + // + // Additif, jamais destructif : on n'enleve pas un dossier du distant + // parce qu'il manque ici, c'est aussi la regle du poste. + if LLocal.TryGetValue('folders', LLocalFolders) then + for I := 0 to LLocalFolders.Count - 1 do + if LLocalFolders.Items[I] is TJSONObject then + EnsureFolder(LMerged, + (LLocalFolders.Items[I] as TJSONObject).GetValue('name', '')); + // 1. Les pierres tombales locales rejoignent celles du distant. Avant les // entrees : l'etape suivante s'en sert pour arbitrer. if LLocal.TryGetValue('tombstones', LLocalStones) then diff --git a/android-client/TESTS.md b/android-client/TESTS.md new file mode 100644 index 0000000..a8edda2 --- /dev/null +++ b/android-client/TESTS.md @@ -0,0 +1,140 @@ +# Client Android — relecture et scénario d'essai + +Relecture du 2026-08-22, avant d'ajouter la vérification de fond, les réglages +et les icônes. Le but n'était pas de tout relire mais de chercher là où cette +séance a montré que les défauts se logent : **les frontières entre fichiers, +les chemins d'erreur, et ce qui traverse les threads.** + +--- + +## Ce que la relecture a trouvé + +### 1. La fusion ne transportait pas les dossiers — CORRIGÉ + +`MergeSnapshots` fondait `entries` et `tombstones`, pas `folders`. Un dossier +créé sur le téléphone — hors connexion, ou juste avant qu'un autre appareil +publie — disparaissait donc de la liste, **tandis que les entrées gardaient son +nom**. Elles se retrouvaient rangées dans un dossier que rien n'affiche : +introuvables sans qu'on le sache. + +C'est exactement la panne contre laquelle `EnsureFolder` avait été écrit, et +elle est revenue par une autre porte. Le report est additif — on ne retire pas +un dossier du distant parce qu'il manque ici. + +### 2. Le témoin d'attente n'existait que sur la liste — CORRIGÉ + +Le point orange était posé sur le cadenas, qui n'existe que dans la liste. Or +après un verrouillage on atterrit sur l'accueil — précisément là où l'on décide +de synchroniser. Il est maintenant aussi sur *Sync*. + +### 3. Trois routines ne libèrent pas l'indicateur d'occupation dans un `finally` + +`SaveAttachment`, `RegisterLocal` et `LoginLocal` le font dans le chemin normal +**et** dans leur `except`, ce qui couvre tout ce qui descend d'`Exception`. Une +erreur d'un autre genre laisserait le voile en place. + +**Non corrigé, et assumé** : le minuteur de 25 s le lève de toute façon, et +c'est son rôle. Le signaler ici vaut mieux que d'ajouter trois `finally` qui +donneraient l'illusion d'une garantie que seul le minuteur apporte. + +### 4. `ParseJSONValue(...) as TJSONObject` fuit sur une entrée qui n'est pas un objet + +Le transtypage lève avant l'affectation, donc la valeur analysée n'est jamais +libérée. Dix-sept occurrences. **Non corrigé** : n'arrive que sur un conteneur +corrompu, où l'on a de plus gros ennuis, et le processus est de toute façon en +train d'échouer bruyamment. À traiter le jour où l'on touche à ces fonctions. + +### 5. Ce qui a été vérifié et va bien + +- **Échappement HTML** : `esc()` couvre nom, sous-titre, initiale, URI d'icône, + libellés, valeurs, noms de dossiers, options de liste. Une entrée nommée + `