Documentation
Tout ce que fait l'outil, rangé selon les questions avec lesquelles les gens arrivent vraiment. Le README du dépôt est la référence complète et correspond toujours à la version que vous avez téléchargée.
Quelles commandes existe-t-il ?
Chacune fait une seule chose :
tfg generate produire des fichiers, depuis une recette ou des options
tfg validate vérifier une recette sans rien écrire
tfg verify comparer un répertoire à un manifeste
tfg cleanup supprimer les fichiers qu'un manifeste liste
tfg recipe fmt afficher une recette sous sa forme normalisée
tfg preset construire un jeu de fichiers à partir d'une question de test nommée
tfg formats lister les formats pris en charge par cette version
tfg damage lister les façons dont cette version peut abîmer un fichier exprès
tfg tool de petits outils pour des fichiers que vous avez déjà
tfg version afficher la version de l'outil
tfg license afficher la licence et ce qu'elle implique pour les fichiers générés
Comment générer un seul fichier de taille exacte ?
Nommez le format, la taille et la destination. Les tailles se comptent par 1024, donc
2mb font 2097152 octets. Un nombre d'octets brut fonctionne aussi, donc
--size 10485761 demande exactement ce nombre.
tfg generate --format png --size 2mb --out ./out
Les options utiles de generate :
| Option | Ce qu'elle fait |
|---|---|
--format <id> | format des fichiers, par exemple txt |
--size <size> | taille exacte de chaque fichier, comme 10mb ou un nombre d'octets brut |
--size-range <a-b> | une taille tirée par fichier dans une plage, comme 1kb-8kb. Le tirage vient de la graine |
--boundary <size> | trois fichiers autour d'une limite : un octet en dessous, la limite, un octet au-dessus |
--count <n> | combien de fichiers produire. Par défaut 1 |
--name <template> | modèle de nom, par exemple invoice_{index:04}.txt |
--out <dir> | répertoire où écrire |
--seed <n> | graine de l'exécution. La même graine donne les mêmes octets |
--set <k>=<v> | un réglage de format, répétable |
--damage <name> | abîmer les fichiers exprès, répétable et appliqué dans l'ordre. Lancez tfg damage pour la liste |
--expected <outcome> | accept, reject, sanitize ou unspecified |
--dry-run | compter et montrer, sans rien écrire du tout |
--json | écrire le manifeste sur la sortie standard |
Comment fabriquer un fichier volontairement cassé ?
Tout autre fichier écrit par cet outil est correct par construction, ce qui répond à deux des trois
questions que pose un validateur d'envoi. --damage répond à la troisième - le
fichier s'ouvre-t-il seulement. Le fichier est produit normalement puis abîmé, il garde donc la
taille demandée.
tfg generate --format png --size 2mb --damage zero-head --out ./out
tfg generate --format png --size 2mb --damage zero-head:bytes=16 --out ./out
Les réglages se placent après deux-points. L'option se répète, et l'ordre dans lequel vous les
écrivez est l'ordre dans lequel ils sont appliqués. tfg damage liste ce que cette
version sait faire et ce que chacun accepte.
Dans une recette, la clé est une liste, de noms ou de réglages :
targets:
- id: broken
format: png
count: 5
size: 2mb
damage:
- zero-head
- type: zero-head
bytes: 16
Un fichier abîmé reçoit expected: reject dans le manifeste, avec l'altération notée à
côté. Deux choses sont refusées avant que rien ne soit écrit, car chacune mettrait sinon sur le
disque un fichier que le manifeste décrit à tort :
- un fichier plus petit que ce dont l'altération a besoin, car il ressortirait inchangé
-
expected: acceptà côté d'une altération, car rien ne pourrait y répondre. Écrivezsanitizesi le système testé doit réparer le fichier, ouunspecifiedsi c'est justement la question que vous posez
Une troisième ne peut pas être connue à l'avance. Si une altération s'exécute sans déplacer un seul octet, ce fichier est abandonné plutôt qu'écrit - l'exécution continue, dit de quel fichier il s'agissait et se termine avec le code de sortie partiel.
Pas à pas, avec un test qui lit le manifeste : comment fabriquer un fichier corrompu pour les tests.
À quoi ressemble une recette ?
Une recette est un fichier YAML qui décrit toute une exécution. Commitez-la à côté de vos tests et les fixtures cessent d'être des binaires dans votre dépôt - n'importe qui peut les reconstruire, à l'octet près, à partir d'un fichier de quelques centaines de caractères.
# fixtures.yaml
version: 1
seed: 7741
defaults:
label: true
targets:
- id: invoices
format: pdf
count: 25
size: 300kb
name: invoice_{index:04}.pdf
properties:
pages: 3
page_size: a4
expected: accept
- id: over_the_limit
format: png
count: 2
size: 12mb
expected:
outcome: reject
reason: size_limit
- id: bundle
format: zip
contains:
- format: txt
count: 200
size: 4kb
output:
dir: ./fixtures
manifest: manifest.json
tfg validate fixtures.yaml
tfg generate fixtures.yaml
Chaque cible exige exactement une de ces clés : size, size-range,
boundary ou contains. Deux est une erreur, et aucune aussi. Une
recette invalide n'écrit aucun fichier et signale tous les problèmes d'un coup
plutôt que le premier seulement, chacun nommant le réglage concerné.
Comment déclarer ce que mon système doit faire d'un fichier ?
Forme courte quand le résultat suffit, forme longue quand la raison compte :
expected: accept
expected:
outcome: reject
reason: size_limit
Les résultats sont accept, reject, sanitize et
unspecified. Les raisons forment une liste fermée pour qu'un rapport puisse les
regrouper : content_malformed, count_limit,
dimensions_limit, duplicate, encoding_invalid,
extension_rule, filename_invalid, filename_too_long,
filename_traversal, malware_signature, mime_mismatch,
nesting_depth, none, size_limit et
size_zero.
Une raison nomme la règle en jeu, pas le verdict. C'est pourquoi la même raison
peut se trouver sous l'un ou l'autre résultat - un fichier un octet sous une limite est
accept, et la règle concernée reste size_limit.
Que contient le manifeste ?
Il est écrit à côté des fichiers à la fin de chaque exécution, y compris d'une exécution interrompue. Une entrée par fichier :
{
"manifest_version": "1.0",
"tool": { "name": "testing-files-generator", "version": "0.4.0" },
"run": {
"id": "run_b359aa8d94",
"seed": 0,
"command": "tfg generate --format png --size 2mb --out ./out",
"platform": { "os": "windows", "arch": "amd64" },
"complete": true
},
"summary": {
"file_count": 1,
"total_bytes": 2097152,
"by_format": { "png": 1 },
"by_expected": { "unspecified": 1 }
},
"files": [
{
"path": "files_0001.png",
"bytes": 2097152,
"format": "png",
"fidelity": "full",
"determinism": "byte",
"seed": "8dc2d18c",
"hashes": { "sha256": "1a1f7c..." },
"properties": { "width": 640, "height": 480 },
"expected": {
"outcome": "unspecified",
"detail": "No expectation was declared for this file.",
"confidence": "policy_dependent"
}
}
]
}
Un recipe_hash est ajouté quand l'exécution vient d'une recette, et preset
avec overrides quand elle vient d'un préréglage, de sorte qu'un manifeste peut
toujours être rattaché à ce qui l'a produit.
Chaque entrée porte aussi target_id, l'identifiant de la cible de la recette qui a
produit le fichier, et summary.by_target compte les fichiers de chaque cible. Une
recette à plusieurs cibles peut donc être vérifiée cible par cible sans lire les noms de
fichiers.
Qu'est-ce qu'un préréglage ?
Un jeu de fichiers prêt à l'emploi qui répond à une question de test courante, pour que vous n'ayez
pas à concevoir le jeu vous-même. Les préréglages sont des recettes ordinaires en dessous, et
eject affiche la recette pour que vous puissiez la modifier. Chaque préréglage a
sa propre page qui dit ce qu'il trouve d'habitude, ce que
contient le jeu et chaque réglage qu'il accepte.
-
Un fichier valide et aussi petit que le format le permet passe-t-il ?
empty-and-minimal -
Mon système va-t-il stocker, afficher et restituer un nom de fichier auquel il ne s'attendait pas ?
filename-handling -
Une limite de taille est-elle appliquée exactement là où elle est déclarée ?
size-boundaries -
Mon import de tableaux résiste-t-il à ce qu'exportent les vrais outils ?
tabular-import -
Mon lecteur sait-il dans quel encodage est un fichier, ou devine-t-il ?
text-encoding -
Mon formulaire d'envoi accepte-t-il ce qu'il doit et refuse-t-il le reste ?
upload-validation
tfg preset list
tfg preset show size-boundaries
tfg generate --preset size-boundaries --limit 10mb --out ./edges
tfg preset eject size-boundaries > my.yaml
show vous dit ce que coûterait le jeu avant que vous ne le construisiez, et dit
franchement quand un nombre est un substitut de notre part plutôt qu'une limite de la vôtre.
Que signifient les codes de sortie ?
Chaque fin a son propre code, la sortie lisible par machine va sur la sortie standard, et une exécution échouée n'y écrit rien. Le tableau est un contrat figé - changer le sens d'un code exige une version majeure.
| Code | Signification |
|---|---|
0 |
Tout a fonctionné. |
1 |
Une erreur inattendue dans l'outil. |
2 |
Commande ou option incorrecte. |
3 |
La recette n'est pas valide. |
4 |
Le format ne peut pas faire ce qui était demandé. |
5 |
Une lecture ou une écriture a échoué. |
6 |
Espace disque insuffisant. |
7 |
verify a trouvé une différence. |
8 |
L'exécution s'est terminée mais tout n'a pas été produit. |
130 |
Interrompu avec Ctrl+C. |
143 |
Arrêté par un signal, ce qui ressemble à un délai dépassé en CI. |
- name: build the fixtures
run: tfg generate fixtures.yaml --out ./fixtures
- name: run the tests
run: pytest tests/
- name: nothing moved
run: tfg verify ./fixtures/manifest.json
Une exécution arrêtée avec Ctrl+C laisse quand même un manifeste et ne laisse jamais de fichier à moitié écrit, si bien qu'une tâche annulée peut encore être nettoyée par la suivante.
Des workflows prêts pour GitHub Actions et GitLab CI : comment générer des fichiers de test dans un pipeline CI.
Y a-t-il une fenêtre de bureau ?
Oui, le même moteur avec une fenêtre, pour les tests qui ne sont pas scriptés. Ce n'est pas une version amputée : un test compare les deux interfaces fonctionnalité par fonctionnalité, et tout ce que seule l'une peut faire doit être déclaré et justifié au lieu de diverger discrètement.
Les écrans sont un lot, les préréglages, plusieurs lots à la fois et À propos. Elle montre ce que coûterait une exécution avant d'écrire quoi que ce soit, indique la progression pendant l'exécution et peut être annulée en cours de route sans laisser de fichier à moitié écrit. Elle n'ouvre pas encore de fichier de recette - les recettes sont pour l'instant une affaire de ligne de commande, et la fenêtre construit ses lots dans le formulaire.