Testing Files Generator
Español

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ónQué 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-runcontar y mostrar, sin escribir absolutamente nada
--jsonescribir 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:

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.

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.