Dokumentation
Alles, was das Tool kann, geordnet nach den Fragen, mit denen die Leute tatsächlich kommen. Die README im Repository ist die vollständige Referenz und passt immer zu dem Build, den du heruntergeladen hast.
Welche Befehle gibt es?
Jeder tut genau eine Sache:
tfg generate Dateien erzeugen, aus einem Rezept oder über Flags
tfg validate ein Rezept prüfen und nichts schreiben
tfg verify ein Verzeichnis gegen ein Manifest prüfen
tfg cleanup die Dateien entfernen, die ein Manifest auflistet
tfg recipe fmt ein Rezept in seiner festen Form ausgeben
tfg preset ein Set von Dateien aus einer benannten Testfrage erzeugen
tfg formats die Formate dieses Builds auflisten
tfg damage die Arten auflisten, wie dieser Build eine Datei absichtlich beschädigen kann
tfg tool kleine Helfer für Dateien, die du schon hast
tfg version die Version des Tools ausgeben
tfg license die Lizenz ausgeben und was sie für erzeugte Dateien bedeutet
Wie erzeuge ich eine einzelne Datei in exakter Größe?
Nenne das Format, die Größe und das Ziel. Größen zählen in 1024ern, 2mb sind also
2097152 Bytes. Eine einfache Byte-Zahl funktioniert auch, --size 10485761 verlangt
also genau so viele.
tfg generate --format png --size 2mb --out ./out
Die nützlichen Flags von generate:
| Flag | Was es tut |
|---|---|
--format <id> | Format der Dateien, zum Beispiel txt |
--size <size> | exakte Größe jeder Datei, etwa 10mb oder eine einfache Byte-Zahl |
--size-range <a-b> | eine Größe, die pro Datei aus einem Bereich gezogen wird, etwa 1kb-8kb. Die Ziehung kommt aus dem Seed |
--boundary <size> | drei Dateien rund um ein Limit: ein Byte darunter, das Limit, ein Byte darüber |
--count <n> | wie viele Dateien erzeugt werden. Standard 1 |
--name <template> | Namensvorlage, zum Beispiel invoice_{index:04}.txt |
--out <dir> | Verzeichnis, in das geschrieben wird |
--seed <n> | Seed des Laufs. Derselbe Seed ergibt dieselben Bytes |
--set <k>=<v> | eine Formateinstellung, wiederholbar |
--damage <name> | Dateien absichtlich beschädigen, wiederholbar und der Reihe nach angewendet. Mit tfg damage bekommst du die Liste |
--expected <outcome> | accept, reject, sanitize oder unspecified |
--dry-run | zählen und anzeigen, überhaupt nichts schreiben |
--json | das Manifest auf die Standardausgabe schreiben |
Wie erzeuge ich eine absichtlich kaputte Datei?
Jede andere Datei, die dieses Tool schreibt, ist per Konstruktion korrekt, was zwei der drei Fragen
beantwortet, die ein Upload-Validator stellt. --damage beantwortet die dritte -
lässt sich die Datei überhaupt öffnen. Die Datei wird normal erzeugt und dann beschädigt, sie
hat also weiterhin die verlangte Größ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
Einstellungen stehen hinter einem Doppelpunkt. Das Flag lässt sich wiederholen, und die Reihenfolge,
in der du sie schreibst, ist die Reihenfolge, in der sie angewendet werden. tfg
damage listet auf, was dieser Build kann und was jede Variante annimmt.
In einem Rezept ist der Schlüssel eine Liste, aus Namen oder aus Einstellungen:
targets:
- id: broken
format: png
count: 5
size: 2mb
damage:
- zero-head
- type: zero-head
bytes: 16
Eine beschädigte Datei bekommt im Manifest expected: reject, mit der daneben
festgehaltenen Beschädigung. Zwei Dinge werden abgelehnt, bevor etwas geschrieben wird, weil
sonst jeweils eine Datei auf die Platte käme, die das Manifest falsch beschreibt:
- eine Datei, die kleiner ist, als die Beschädigung braucht, weil sie unverändert herauskäme
-
expected: acceptneben einer Beschädigung, weil es keine Datei geben kann, die das erfüllt. Schreibesanitize, wenn das getestete System die Datei reparieren soll, oderunspecified, wenn genau das die Frage ist, die du stellst
Eine dritte lässt sich nicht im Voraus wissen. Wenn eine Beschädigung läuft und kein Byte verändert, wird diese Datei verworfen statt geschrieben - der Lauf macht weiter, sagt, um welche Datei es ging, und endet mit dem Exit-Code für teilweise erfolgreiche Läufe.
Schritt für Schritt, mit einem Test, der das Manifest liest: So erzeugen Sie eine beschädigte Datei zum Testen.
Wie sieht ein Rezept aus?
Ein Rezept ist eine YAML-Datei, die einen ganzen Lauf beschreibt. Checke sie neben deinen Tests ein, und die Fixtures sind keine Binärdateien mehr in deinem Repository - jeder kann sie Byte für Byte aus einer Datei von wenigen hundert Zeichen neu erzeugen.
# 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
Jedes Target braucht genau eines von size, size-range,
boundary oder contains. Zwei davon sind ein Fehler, und keines
ebenfalls. Ein ungültiges Rezept schreibt überhaupt keine Dateien und meldet
alle Probleme auf einmal statt nur das erste, jedes mit der Einstellung, um die es geht.
Wie lege ich fest, was mein System mit einer Datei tun soll?
Kurzform, wenn das Ergebnis reicht, Langform, wenn der Grund zählt:
expected: accept
expected:
outcome: reject
reason: size_limit
Die Ergebnisse sind accept, reject, sanitize und
unspecified. Die Gründe sind eine geschlossene Liste, damit ein Report danach
gruppieren kann: 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 und
size_zero.
Ein Grund benennt die Regel, um die es geht, nicht das Urteil. Deshalb kann
derselbe Grund unter beiden Ergebnissen stehen - eine Datei ein Byte unter einem Limit ist
accept, und die Regel, um die es geht, ist trotzdem size_limit.
Was steht im Manifest?
Es wird am Ende jedes Laufs neben die Dateien geschrieben, auch bei einem unterbrochenen Lauf. Ein Eintrag pro Datei:
{
"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"
}
}
]
}
Ein recipe_hash kommt hinzu, wenn der Lauf aus einem Rezept stammt, und
preset mit overrides, wenn er aus einem Preset stammt, sodass sich ein
Manifest immer auf das zurückführen lässt, was es erzeugt hat.
Jeder Eintrag trägt außerdem target_id, die ID des Targets im Rezept, das die Datei
erzeugt hat, und summary.by_target zählt, auf wie viele Dateien jedes Target kam.
Ein Rezept mit mehreren Targets lässt sich so Target für Target prüfen, ohne Dateinamen zu
lesen.
Was ist ein Preset?
Ein fertiges Set aus Dateien, das eine gängige Testfrage beantwortet, damit du das Set nicht selbst
entwerfen musst. Presets sind darunter ganz normale Rezepte, und eject gibt das
Rezept aus, damit du es von dort aus bearbeiten kannst. Jedes Preset hat
eine eigene Seite mit dem, was es meist findet, was im Set steckt und
welche Einstellungen es kennt.
-
Kommt eine gültige Datei durch, die so klein ist, wie das Format es erlaubt?
empty-and-minimal -
Speichert, zeigt und liefert mein System einen Dateinamen korrekt, mit dem es nicht gerechnet hat?
filename-handling -
Wird ein Größenlimit genau dort durchgesetzt, wo es angegeben ist?
size-boundaries -
Übersteht mein Tabellenimport das, was echte Tools exportieren?
tabular-import -
Weiß mein Leser, in welcher Kodierung eine Datei vorliegt, oder rät er?
text-encoding -
Nimmt mein Upload-Formular an, was es soll, und weist den Rest ab?
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 sagt dir, was das Set kosten würde, bevor du es baust, und sagt
unmissverständlich, wenn eine Zahl ein Platzhalter von uns statt ein Limit von dir ist.
Was bedeuten die Exit-Codes?
Jedes Ende hat einen eigenen Code, maschinenlesbare Ausgabe geht auf die Standardausgabe, und ein fehlgeschlagener Lauf gibt dort nichts aus. Die Tabelle ist ein eingefrorener Vertrag - die Bedeutung eines Codes zu ändern erfordert einen Major-Versionssprung.
| Code | Bedeutung |
|---|---|
0 |
Alles hat funktioniert. |
1 |
Ein unerwarteter Fehler im Tool. |
2 |
Falscher Befehl oder falsches Flag. |
3 |
Das Rezept ist ungültig. |
4 |
Das Format kann nicht, was verlangt wurde. |
5 |
Lesen oder Schreiben ist fehlgeschlagen. |
6 |
Nicht genug Speicherplatz. |
7 |
verify hat eine Abweichung gefunden. |
8 |
Der Lauf ist beendet, aber nicht alles wurde erzeugt. |
130 |
Mit Strg+C abgebrochen. |
143 |
Durch ein Signal beendet, so sieht ein CI-Timeout aus. |
- 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
Ein mit Strg+C gestoppter Lauf hinterlässt trotzdem ein Manifest und nie eine halb geschriebene Datei, sodass ein abgebrochener Job vom nächsten noch aufgeräumt werden kann.
Fertige Workflows für GitHub Actions und GitLab CI: So erzeugen Sie Testdateien in einer CI-Pipeline.
Gibt es ein Desktop-Fenster?
Ja, dieselbe Engine mit einem Fenster davor, für das Testen, das nicht skriptbar ist. Es ist keine abgespeckte Version: Ein Test vergleicht die beiden Oberflächen Fähigkeit für Fähigkeit, und alles, was nur eine von beiden kann, muss deklariert und begründet werden, statt unbemerkt auseinanderzudriften.
Die Bildschirme sind ein Stapel, Presets, mehrere Stapel gleichzeitig und Info. Es zeigt, was ein Lauf kosten würde, bevor etwas geschrieben wird, meldet den Fortschritt während des Laufs und lässt sich mittendrin abbrechen, ohne eine halb geschriebene Datei zu hinterlassen. Eine Rezeptdatei öffnet es noch nicht - Rezepte gibt es vorerst nur auf der Kommandozeile, und das Fenster baut seine Stapel im Formular.