Testing Files Generator
Nederlands

Documentatie

Alles wat de tool doet, geordend als de vragen waarmee mensen echt komen. De README in de repository is de volledige referentie en komt altijd overeen met de build die je hebt gedownload.

Welke opdrachten zijn er?

Elke doet precies één ding:

tfg generate    bestanden maken, uit een recept of met opties
tfg validate    een recept controleren en niets schrijven
tfg verify      een map controleren tegen een manifest
tfg cleanup     de bestanden verwijderen die een manifest opsomt
tfg recipe fmt  een recept in zijn vaste vorm afdrukken
tfg preset      een set bestanden bouwen uit een benoemde testvraag
tfg formats     de formaten opsommen die deze build ondersteunt
tfg damage      de manieren opsommen waarop deze build een bestand met opzet kan beschadigen
tfg tool        kleine hulpmiddelen voor bestanden die je al hebt
tfg version     de versie van de tool tonen
tfg license     de licentie tonen en wat die betekent voor gegenereerde bestanden

Hoe genereer ik één bestand van een exacte grootte?

Noem het formaat, de grootte en waar het naartoe moet. Groottes tellen in 1024-tallen, dus 2mb is 2097152 bytes. Een gewoon aantal bytes werkt ook, dus --size 10485761 vraagt precies zoveel.

tfg generate --format png --size 2mb --out ./out

De nuttige opties van generate:

OptieWat het doet
--format <id>formaat van de bestanden, bijvoorbeeld txt
--size <size>exacte grootte van elk bestand, zoals 10mb of een gewoon aantal bytes
--size-range <a-b>een grootte die per bestand uit een bereik wordt getrokken, zoals 1kb-8kb. De trekking komt uit de seed
--boundary <size>drie bestanden rond een limiet: één byte eronder, de limiet, één byte erboven
--count <n>hoeveel bestanden te maken. Standaard 1
--name <template>naamsjabloon, bijvoorbeeld invoice_{index:04}.txt
--out <dir>map om naartoe te schrijven
--seed <n>seed van de run. Dezelfde seed geeft dezelfde bytes
--set <k>=<v>een formaatinstelling, herhaalbaar
--damage <name>de bestanden met opzet beschadigen, herhaalbaar en in volgorde toegepast. Draai tfg damage voor de lijst
--expected <outcome>accept, reject, sanitize of unspecified
--dry-runtellen en tonen, helemaal niets schrijven
--jsonhet manifest naar de standaarduitvoer schrijven

Hoe maak ik een bestand dat met opzet kapot is?

Elk ander bestand dat deze tool schrijft is correct per constructie, wat twee van de drie vragen beantwoordt die een uploadvalidator stelt. --damage beantwoordt de derde - gaat het bestand überhaupt open. Het bestand wordt normaal gemaakt en daarna beschadigd, dus het heeft nog steeds de grootte die je vroeg.

tfg generate --format png --size 2mb --damage zero-head --out ./out
tfg generate --format png --size 2mb --damage zero-head:bytes=16 --out ./out

Instellingen komen na een dubbele punt. De optie herhaalt zich, en de volgorde waarin je ze schrijft is de volgorde waarin ze worden toegepast. tfg damage toont wat deze build kan en wat elke variant accepteert.

In een recept is de sleutel een lijst, van namen of van instellingen:

targets:
  - id: broken
    format: png
    count: 5
    size: 2mb
    damage:
      - zero-head
      - type: zero-head
        bytes: 16

Een beschadigd bestand krijgt expected: reject in het manifest, met de beschadiging ernaast vastgelegd. Twee dingen worden geweigerd voordat er iets wordt geschreven, omdat elk anders een bestand op schijf zou zetten dat het manifest verkeerd beschrijft:

Een derde is vooraf niet te weten. Als een beschadiging draait en geen enkele byte verschuift, wordt dat bestand weggegooid in plaats van geschreven - de run gaat door, zegt om welk bestand het ging en eindigt met de gedeeltelijke afsluitcode.

Stap voor stap, met een test die het manifest leest: hoe maak je een beschadigd bestand om mee te testen.

Hoe ziet een recept eruit?

Een recept is een YAML-bestand dat een hele run beschrijft. Commit het naast je tests en de fixtures zijn geen binaries meer in je repository - iedereen kan ze byte voor byte opnieuw opbouwen uit een bestand van een paar honderd tekens.

# 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

Elk target heeft precies één van deze sleutels nodig: size, size-range, boundary of contains. Twee is een fout en geen ook. Een ongeldig recept schrijft helemaal geen bestanden en meldt alle problemen tegelijk in plaats van alleen het eerste, elk met de instelling waar het over gaat.

Hoe leg ik vast wat mijn systeem met een bestand moet doen?

Korte vorm als de uitkomst genoeg is, lange vorm als de reden ertoe doet:

expected: accept
expected:
  outcome: reject
  reason: size_limit

De uitkomsten zijn accept, reject, sanitize en unspecified. De redenen zijn een gesloten lijst zodat een rapport erop kan groeperen: 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 en size_zero.

Een reden noemt de regel die in het spel is, niet het oordeel. Daarom kan dezelfde reden onder beide uitkomsten staan - een bestand één byte onder een limiet is accept, en de regel waar het om gaat is nog steeds size_limit.

Wat staat er in het manifest?

Het wordt aan het einde van elke run naast de bestanden geschreven, ook bij een onderbroken run. Eén item per bestand:

{
  "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"
      }
    }
  ]
}

Er komt een recipe_hash bij als de run uit een recept kwam, en preset met overrides als hij uit een preset kwam, zodat een manifest altijd te herleiden is tot wat het maakte.

Elk item draagt ook target_id, de id van het target in het recept dat het bestand maakte, en summary.by_target telt de bestanden waar elk target op uitkwam. Een recept met meerdere targets kan zo target voor target worden gecontroleerd zonder bestandsnamen te lezen.

Wat is een preset?

Een kant-en-klare set bestanden die een veelvoorkomende testvraag beantwoordt, zodat je de set niet zelf hoeft te ontwerpen. Presets zijn gewone recepten onder de motorkap, en eject drukt het recept af zodat je het vanaf daar kunt bewerken. Elke preset heeft een eigen pagina met wat hij meestal vindt, wat er in de set zit en welke instellingen hij kent.

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 zegt je wat de set zou kosten voordat je hem bouwt, en zegt zonder omwegen wanneer een getal een tijdelijke waarde van ons is in plaats van een limiet van jou.

Wat betekenen de afsluitcodes?

Elk einde heeft zijn eigen code, machineleesbare uitvoer gaat naar de standaarduitvoer, en een mislukte run drukt daar niets af. De tabel is een bevroren contract - de betekenis van een code wijzigen vereist een major-versie.

Code Betekenis
0 Alles werkte.
1 Een onverwachte fout in de tool.
2 Verkeerde opdracht of optie.
3 Het recept is niet geldig.
4 Het formaat kan niet wat er gevraagd werd.
5 Een lees- of schrijfactie is mislukt.
6 Niet genoeg schijfruimte.
7 verify vond een afwijking.
8 De run is klaar, maar niet alles is gemaakt.
130 Onderbroken met Ctrl+C.
143 Gestopt door een signaal, zo ziet een CI-timeout eruit.
- 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

Een run die met Ctrl+C is gestopt laat nog steeds een manifest achter en nooit een half geschreven bestand, zodat een afgebroken taak door de volgende nog kan worden opgeruimd.

Kant-en-klare workflows voor GitHub Actions en GitLab CI: hoe genereer je testbestanden in een CI-pipeline.

Is er een bureaubladvenster?

Ja, dezelfde engine met een venster erop, voor het testen dat niet geautomatiseerd is. Het is geen uitgeklede versie: een test vergelijkt de twee interfaces mogelijkheid voor mogelijkheid, en alles wat maar één van beide kan moet worden verklaard en gerechtvaardigd in plaats van ongemerkt uit elkaar te drijven.

De schermen zijn één batch, presets, meerdere batches tegelijk en info. Het toont wat een run zou kosten voordat er iets wordt geschreven, meldt de voortgang terwijl het draait en kan halverwege worden afgebroken zonder een half geschreven bestand achter te laten. Het opent nog geen receptbestand - recepten zijn voorlopig iets van de opdrachtregel, en het venster bouwt zijn batches in het formulier.