Aller au contenu

Tests par fichiers de référence

Spec: ISO/IEC/IEEE 29119-4 Spec: ISO/IEC 25010 Evidence: Test-backed

Un fichier de référence est un enregistrement de « voici à quoi ressemble une sortie correcte » auquel un test se confronte à chaque exécution. NextPDF utilise ces fichiers de référence pour détecter les changements que personne n’a voulu introduire : un flux compressé différemment, un paragraphe déplacé, une coordonnée qui a dérivé. Cette page explique comment cela fonctionne et comment un fichier de référence reste fiable au lieu de devenir une référence obsolète que plus personne ne lit.

La génération de PDF est un long pipeline où la dérive peut s’installer silencieusement à de nombreux endroits. Un remaniement qui « ne change rien » peut discrètement réordonner des opérateurs, modifier une matrice de transformation ou décaler une cellule de tableau d’une valeur infime. Les tests unitaires détectent rarement ce type de dérive : ils vérifient une valeur que tu as pensé à contrôler, pas les milliers d’octets que tu as oubliés. Les techniques fondées sur la structure et celles fondées sur la spécification détectent des erreurs différentes, et aucune ne couvre entièrement l’autre (ISO/IEC/IEEE 29119-4, Annex A). Un fichier de référence est la spécification par l’exemple qui fige la sortie entière, et non une seule assertion.

Le risque existe dans les deux sens. Un fichier de référence trop strict échoue au moindre changement anodin et finit par être ré-entériné à l’aveugle, jusqu’à ne plus rien prouver. Un fichier de référence trop permissif laisse passer de vraies régressions. Trouver le juste équilibre, c’est tout le savoir-faire.

  • Un fichier de référence est une sortie de référence figée, générée à partir d’un comportement du moteur reconnu comme correct, puis archivée dans le dépôt.
  • Un test par fichier de référence régénère la sortie et la compare à la référence figée ; toute différence fait échouer le test et exige une décision humaine.
  • NextPDF compare au niveau qui est significatif mais stable : le texte extrait et les opérateurs structurels normalisés, et non les octets bruts, car ceux-ci portent du bruit (horodatages, ordre des sous-ensembles, compression) qui n’est pas une régression.
  • Mettre à jour un fichier de référence est une opération délibérée et relue, derrière un commutateur explicite GOLDEN_UPDATE — jamais un « j’accepte tout ce qui a changé » automatique.
  • Le test par fichier de référence se distingue des tests par instantané et par caractérisation sur un point décisif : un fichier de référence n’est jamais mis à jour automatiquement.

L’infrastructure de fichiers de référence du moteur s’appuie sur un diff à deux couches explicite plutôt que sur une comparaison octet par octet :

  1. Generate Render the fixture input through the current engine.
  2. Layer 1 — text Extract human-readable text from the content stream ; diff against the text golden. Catches dropped or reordered content and encoding regressions.
  3. Layer 2 — structure Extract ordered PDF operators, normalise coordinates to a fixed precision, diff against the operator golden. Catches layout shifts and broken structure.
  4. Decide Any diff fails the test ; a human judges whether it is a regression or an intended change.
Comment se déroule une comparaison de référence NextPDF : générer le PDF, extraire les couches significatives (le texte, puis les opérateurs structurels normalisés), comparer chacune à la référence figée, et échouer avec un rapport lisible par un humain dès la moindre différence.

Ce que NextPDF choisit délibérément de ne pas comparer est aussi important que ce qu’il compare. La sortie en octets bruts est exclue, car les horodatages, l’ordre des sous-ensembles de polices et la compression des flux la rendent fragile sans la rendre plus correcte. La comparaison d’images au niveau du pixel est exclue de ce palier, car elle requiert un moteur de rendu externe et fait entrer la variabilité de l’environnement. Les coordonnées en virgule flottante sont normalisées à une précision fixe, afin qu’un bruit d’arrondi sans signification ne soit pas confondu avec une régression. C’est toute la différence entre un fichier de référence qui teste le comportement et un autre qui teste le bruit environnemental transitoire.

Ce choix définit aussi le profil de reproductibilité de la page : structurel. NextPDF documente trois profils — au bit près (les octets exacts se reproduisent), structurel (le graphe d’objets et la séquence d’opérateurs se reproduisent, en tolérant une variabilité bénigne au niveau des octets) et sémantique (le sens se reproduit). À ce palier, les tests par fichier de référence vérifient le profil structurel par construction. C’est pourquoi leurs références survivent à une montée de version de la bibliothèque de compression, mais échouent encore sur un tableau déplacé.

Evidence: Test-backed La comparaison à deux couches (extraction du texte, puis comparaison des opérateurs structurels normalisés) est la méthodologie de référence documentée propre au moteur, avec la comparaison des octets bruts, le diff d’images et la comparaison exacte en virgule flottante explicitement hors périmètre pour les raisons ci-dessus. La suite de tests par fichier de référence est une suite déclarée, exécutable séparément, distincte des suites par instantané et par caractérisation.

Evidence: Test-backed Le mécanisme d’honnêteté est concret : les fichiers de référence sont des artefacts générés, et non écrits à la main. Les écraser nécessite un commutateur d’environnement explicite GOLDEN_UPDATE, documenté comme une opération rare et toujours relue. À l’inverse, les tests par instantané du moteur se régénèrent à la première exécution et acceptent la dérive au moyen d’un drapeau de mise à jour. Les tests par caractérisation, eux, figent le comportement hérité sans prétendre qu’il est correct. Les trois sont, à dessein, des outils différents.

Evidence: Standard-backed Un fichier de référence est une spécification par l’exemple. Spec: ISO/IEC/IEEE 29119-4, Annex A indique que les techniques fondées sur la spécification et celles fondées sur la structure détectent des classes d’erreurs différentes et qu’une stratégie devrait les combiner. C’est pourquoi les fichiers de référence s’inscrivent aux côtés des tests unitaires et structurels, et non à leur place, dans la pyramide des tests.

Un test par fichier de référence est mécaniquement simple ; la discipline réside dans le flux de travail qui l’entoure :

<?php
declare(strict_types=1);
// 1. The fixture: a fixed HTML input committed next to the test.
// tests/Golden/fixtures/html-inputs/002-basic-table.html
// 2. The pinned references, generated once from known-good behaviour:
// 002-basic-table.text.golden (Layer 1 — extracted text)
// 002-basic-table.operators.golden (Layer 2 — normalised operators)
// 3. The run compares; ANY difference fails:
// vendor/bin/phpunit --testsuite Golden
// 4. An intended behaviour change is the ONLY time references move,
// and it is explicit and reviewed — never automatic:
// GOLDEN_UPDATE=1 vendor/bin/phpunit --testsuite Golden
//
// The regenerated *.golden files land in the diff of the same change
// that altered behaviour, so a reviewer sees the output delta next to
// the code delta and signs off on both together.

L’exemple, c’est avant tout le processus. Le code de test ne fait que comparer. Ce qui rend le fichier de référence fiable, c’est qu’une référence modifiée est relue en tant que sortie dans le même changement que celui qui a modifié le moteur.

L’erreur la plus courante consiste à assimiler les tests par fichier de référence à des tests octet par octet. Les fichiers de référence de NextPDF ne sont pas les octets du fichier : ce sont son texte extrait et ses opérateurs structurels normalisés. Vérifier les octets bruts échouerait sur une nouvelle version de zlib, une étiquette de sous-ensemble différente ou un horodatage régénéré, dont aucun n’est une régression. Le test finirait alors par être ré-entériné jusqu’à devenir inutile en moins d’une semaine. (Là où les octets exacts doivent réellement se reproduire, il s’agit du profil de reproductibilité au bit près, distinct et plus strict, et non d’un fichier de référence.)

La deuxième erreur consiste à supposer qu’une suite de fichiers de référence au vert prouve que le résultat est correct. Elle prouve l’absence de modification. Un fichier de référence généré à partir d’une sortie boguée protège fidèlement le bogue. Les fichiers de référence protègent contre les régressions par rapport à une base de référence reconnue comme correcte ; ils n’établissent pas que cette base était correcte. C’est à cela que servent les paliers unitaire, structurel et de conformité.

Un test par fichier de référence répond à exactement une question : la sortie a-t-elle changé par rapport à la référence figée ? Il ne dit pas si la référence a un jour été correcte. Il ne mesure pas non plus les performances, la conformité ou la sécurité. Ce sont d’autres paliers. La taille du corpus de fixtures, le taux de réussite de la suite et tout chiffre de couverture sont des signaux de qualité vivants, générés à partir d’artefacts d’intégration continue et publiés avec la build. Ils ne sont volontairement pas indiqués ici, où ils risqueraient de devenir obsolètes.

La disposition exacte des répertoires, les rouages internes du comparateur et le commutateur de mise à jour relèvent de l’infrastructure de test du moteur et peuvent évoluer. La configuration des tests fait autorité si elle venait à contredire cette explication. Cette page ne formule aucune affirmation sur l’outillage par instantané ou par fichier de référence d’une autre bibliothèque.

  • Fichier de référence — une sortie de référence figée, générée à partir d’un comportement du moteur reconnu comme correct puis archivée, et à laquelle un test se confronte à chaque exécution. Jamais mis à jour automatiquement.
  • Diff à deux couches — la comparaison de référence de NextPDF : le texte extrait (couche 1) plus les opérateurs structurels normalisés (couche 2), au lieu des octets bruts.
  • Test par instantané — une technique proche mais distincte, où la référence est régénérée à la première exécution et où la dérive est reconnue via un drapeau de mise à jour.
  • Test par caractérisation — un test qui fige le comportement existant sans affirmer qu’il est correct, généralement pour sécuriser un remaniement.
  • Profil de reproductibilité — le niveau auquel la sortie doit se reproduire : au bit près (octets exacts), structurel (graphe d’objets et séquence d’opérateurs, variabilité bénigne des octets tolérée) ou sémantique (le sens). Ici, les tests par fichier de référence vérifient le profil structurel.
  • GOLDEN_UPDATE — le commutateur d’environnement explicite qui autorise l’écrasement des fichiers de référence ; une opération rare et relue.