Documentación
Todo lo que hace la herramienta, ordenado según las preguntas con las que la gente llega de verdad. El README del repositorio es la referencia completa y siempre coincide con la versión que descargaste.
¿Qué comandos hay?
Cada uno hace una sola cosa:
tfg generate producir archivos, desde una receta o desde opciones
tfg validate comprobar una receta sin escribir nada
tfg verify comprobar un directorio contra un manifiesto
tfg cleanup eliminar los archivos que lista un manifiesto
tfg recipe fmt imprimir una receta en su forma normalizada
tfg preset construir un conjunto de archivos a partir de una pregunta de prueba con nombre
tfg formats listar los formatos que admite esta versión
tfg damage listar las formas en que esta versión puede romper un archivo a propósito
tfg tool pequeñas utilidades para archivos que ya tienes
tfg version imprimir la versión de la herramienta
tfg license imprimir la licencia y qué significa para los archivos generados
¿Cómo genero un único archivo de tamaño exacto?
Indica el formato, el tamaño y dónde va. Los tamaños cuentan de 1024 en 1024, así que
2mb son 2097152 bytes. Un número de bytes simple también sirve, de modo que
--size 10485761 pide exactamente esa cantidad.
tfg generate --format png --size 2mb --out ./out
Las opciones útiles de generate:
| Opción | Qué hace |
|---|---|
--format <id> | formato de los archivos, por ejemplo txt |
--size <size> | tamaño exacto de cada archivo, como 10mb o un número de bytes simple |
--size-range <a-b> | un tamaño sacado por archivo de un intervalo, como 1kb-8kb. El sorteo viene de la semilla |
--boundary <size> | tres archivos alrededor de un límite: un byte por debajo, el límite, un byte por encima |
--count <n> | cuántos archivos producir. Por defecto 1 |
--name <template> | plantilla de nombre, por ejemplo invoice_{index:04}.txt |
--out <dir> | directorio donde escribir |
--seed <n> | semilla de la ejecución. La misma semilla da los mismos bytes |
--set <k>=<v> | un ajuste de formato, repetible |
--damage <name> | romper los archivos a propósito, repetible y aplicado en orden. Ejecuta tfg damage para ver la lista |
--expected <outcome> | accept, reject, sanitize o unspecified |
--dry-run | contar y mostrar, sin escribir absolutamente nada |
--json | escribir el manifiesto en la salida estándar |
¿Cómo hago un archivo roto a propósito?
Todos los demás archivos que escribe esta herramienta son correctos por construcción, lo que
responde a dos de las tres preguntas que hace un validador de subidas. --damage
responde a la tercera - si el archivo se abre siquiera. El archivo se produce con normalidad y
luego se rompe, así que conserva el tamaño que pediste.
tfg generate --format png --size 2mb --damage zero-head --out ./out
tfg generate --format png --size 2mb --damage zero-head:bytes=16 --out ./out
Los ajustes van después de dos puntos. La opción se repite, y el orden en que las escribes es el
orden en que se aplican. tfg damage lista lo que puede hacer esta versión y qué
admite cada una.
En una receta la clave es una lista, de nombres o de ajustes:
targets:
- id: broken
format: png
count: 5
size: 2mb
damage:
- zero-head
- type: zero-head
bytes: 16
Un archivo dañado recibe expected: reject en el manifiesto, con el daño registrado a su
lado. Dos cosas se rechazan antes de escribir nada, porque cada una dejaría en disco un archivo
que el manifiesto describe mal:
- un archivo más pequeño de lo que necesita el daño, porque saldría sin cambios
-
expected: acceptjunto a un daño, porque nada podría cumplirlo. Escribesanitizesi el sistema bajo prueba debe reparar el archivo, ounspecifiedsi esa es la pregunta que haces
Una tercera no se puede saber de antemano. Si un daño se ejecuta y no mueve ningún byte, ese archivo se descarta en lugar de escribirse - la ejecución continúa, dice de qué archivo se trató y termina con el código de salida parcial.
Paso a paso, con una prueba que lee el manifiesto: cómo crear un archivo corrupto para pruebas.
¿Qué aspecto tiene una receta?
Una receta es un archivo YAML que describe una ejecución completa. Súbela al repositorio junto a tus pruebas y los fixtures dejan de ser binarios en tu repositorio - cualquiera puede reconstruirlos, byte a byte, a partir de un archivo de unos cientos de caracteres.
# 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
Cada target necesita exactamente una de estas claves: size, size-range,
boundary o contains. Dos es un error y ninguna también. Una receta
inválida escribe ningún archivo e informa de todos los problemas a la vez en
lugar de solo del primero, cada uno nombrando el ajuste al que se refiere.
¿Cómo declaro qué debe hacer mi sistema con un archivo?
Forma corta cuando basta con el resultado, forma larga cuando importa el motivo:
expected: accept
expected:
outcome: reject
reason: size_limit
Los resultados son accept, reject, sanitize y
unspecified. Los motivos son una lista cerrada para que un informe pueda
agruparlos: 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 y size_zero.
Un motivo nombra la regla en juego, no el veredicto. Por eso el mismo motivo puede
estar bajo cualquiera de los dos resultados - un archivo un byte por debajo de un límite es
accept, y la regla de la que trata sigue siendo size_limit.
¿Qué hay en el manifiesto?
Se escribe junto a los archivos al final de cada ejecución, incluida una ejecución interrumpida. Una entrada por archivo:
{
"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"
}
}
]
}
Se añade un recipe_hash cuando la ejecución viene de una receta, y preset
con overrides cuando viene de un preset, de modo que un manifiesto siempre se puede
rastrear hasta lo que lo produjo.
Cada entrada lleva también target_id, el id del target de la receta que produjo el
archivo, y summary.by_target cuenta los archivos a los que llegó cada target. Una
receta con varios targets se puede comprobar así target por target sin leer nombres de archivo.
¿Qué es un preset?
Un conjunto de archivos listo que responde a una pregunta de prueba habitual, para que no tengas que
diseñar el conjunto tú. Los presets son recetas normales por debajo, y eject
imprime la receta para que la edites desde ahí. Cada preset tiene una
página propia con lo que suele encontrar, qué hay en el conjunto y cada ajuste que admite.
-
¿Pasa un archivo válido y tan pequeño como permite el formato?
empty-and-minimal -
¿Mi sistema guardará, mostrará y devolverá un nombre de archivo que no esperaba?
filename-handling -
¿Se aplica un límite de tamaño exactamente donde se declara?
size-boundaries -
¿Sobrevive mi importación de tablas a lo que exportan las herramientas reales?
tabular-import -
¿Sabe mi lector en qué codificación está un archivo, o lo adivina?
text-encoding -
¿Mi formulario de subida acepta lo que debe y rechaza el resto?
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 te dice cuánto costaría el conjunto antes de construirlo, y dice sin rodeos cuándo
un número es un marcador nuestro en lugar de un límite tuyo.
¿Qué significan los códigos de salida?
Cada final tiene su propio código, la salida legible por máquina va a la salida estándar, y una ejecución fallida no imprime nada allí. La tabla es un contrato congelado - cambiar lo que significa un código exige una versión mayor.
| Código | Significado |
|---|---|
0 |
Todo ha funcionado. |
1 |
Un error inesperado dentro de la herramienta. |
2 |
Comando u opción incorrectos. |
3 |
La receta no es válida. |
4 |
El formato no puede hacer lo que se pidió. |
5 |
Ha fallado una lectura o una escritura. |
6 |
No hay espacio suficiente en disco. |
7 |
verify ha encontrado una discrepancia. |
8 |
La ejecución terminó, pero no se produjo todo. |
130 |
Interrumpido con Ctrl+C. |
143 |
Detenido por una señal, que es el aspecto de un tiempo de espera agotado 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
Una ejecución detenida con Ctrl+C sigue dejando un manifiesto y nunca deja un archivo a medio escribir, así que un trabajo cancelado aún puede limpiarlo el siguiente.
Workflows listos para GitHub Actions y GitLab CI: cómo generar archivos de prueba en un pipeline de CI.
¿Hay una ventana de escritorio?
Sí, el mismo motor con una ventana encima, para las pruebas que no se automatizan. No es una versión recortada: una prueba compara las dos interfaces capacidad por capacidad, y todo lo que solo una de ellas puede hacer debe declararse y justificarse en lugar de divergir en silencio.
Las pantallas son un lote, presets, varios lotes a la vez y Acerca de. Muestra lo que costaría una ejecución antes de escribir nada, informa del progreso mientras corre y se puede cancelar a medias sin dejar un archivo a medio escribir. Todavía no abre un archivo de receta - por ahora las recetas son cosa de la línea de comandos, y la ventana construye sus lotes en el formulario.