Testing Files Generator
Français

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 :

OptionCe 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-runcompter 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 :

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.

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.