Testing Files Generator

Dokumentacja

Wszystko, co narzędzie robi, ułożone jako pytania, z którymi ludzie naprawdę przychodzą. README w repozytorium jest pełną dokumentacją i zawsze odpowiada wersji, którą pobrałeś.

Jakie są komendy?

Każda robi jedną rzecz:

tfg generate    tworzy pliki, z przepisu albo z flag
tfg validate    sprawdza przepis i nic nie zapisuje
tfg verify      sprawdza katalog względem manifestu
tfg cleanup     usuwa pliki wypisane w manifeście
tfg recipe fmt  wypisuje przepis w postaci uporządkowanej
tfg preset      buduje zestaw plików z nazwanego pytania testowego
tfg formats     wypisuje formaty, które ta wersja obsługuje
tfg version     wypisuje wersję narzędzia
tfg license     wypisuje licencję i to, co znaczy dla wygenerowanych plików

Jak wygenerować jeden plik o dokładnym rozmiarze?

Podaj format, rozmiar i katalog. Rozmiary liczą się po 1024, więc 2mb to 2097152 bajty. Zwykła liczba bajtów też zadziała, więc --size 10485761 prosi dokładnie o tyle.

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

Przydatne flagi komendy generate:

FlagaCo robi
--format <id>format plików, na przykład txt
--size <rozmiar>dokładny rozmiar każdego pliku, na przykład 10mb albo liczba bajtów
--size-range <a-b>rozmiar losowany dla każdego pliku z zakresu, na przykład 1kb-8kb. Losowanie idzie z ziarna
--boundary <rozmiar>trzy pliki wokół limitu: bajt pod, dokładnie limit, bajt nad
--count <n>ile plików zapisać. Domyślnie 1
--name <szablon>szablon nazwy, na przykład invoice_{index:04}.txt
--out <katalog>katalog, do którego trafiają pliki
--seed <n>ziarno przebiegu. To samo ziarno daje te same bajty
--set <k>=<v>ustawienie formatu, można powtarzać
--expected <wynik>accept, reject, sanitize albo unspecified
--dry-runpolicz i pokaż, nie zapisuj niczego
--jsonwypisz manifest na standardowe wyjście

Jak wygląda przepis?

Przepis to plik YAML opisujący cały przebieg. Commitujesz go obok testów i dane testowe przestają być binariami w repozytorium - każdy może je odtworzyć, co do bajta, z pliku o długości kilkuset znaków.

# 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

Każdy cel potrzebuje dokładnie jednego z: size, size-range, boundary albo contains. Dwa naraz to błąd, brak też. Niepoprawny przepis nie tworzy ani jednego pliku i zgłasza wszystkie problemy naraz, a nie pierwszy z brzegu, przy czym każdy nazywa ustawienie, o które chodzi.

Jak zadeklarować, co system ma zrobić z plikiem?

Krótka forma, gdy wystarczy sam wynik, i długa, gdy liczy się powód:

expected: accept
expected:
  outcome: reject
  reason: size_limit

Wyniki to accept, reject, sanitize i unspecified. Powody są zamkniętą listą, żeby raport mógł po nich grupować: 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 i size_zero.

Powód nazywa regułę, o którą chodzi, a nie werdykt. Dlatego ten sam powód może stać pod jednym i pod drugim wynikiem - plik bajt pod limitem jest accept, a reguła, której dotyczy, to dalej size_limit.

Co zawiera manifest?

Powstaje obok plików na koniec każdego przebiegu, także takiego, który został przerwany. Jedna pozycja na plik:

{
  "manifest_version": "1.0",
  "tool": { "name": "testing-files-generator", "version": "0.1.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"
      }
    }
  ]
}

Do tego dochodzi recipe_hash, gdy przebieg pochodził z przepisu, oraz preset i overrides, gdy pochodził z presetu - żeby manifest zawsze dało się prześledzić z powrotem do tego, co go wyprodukowało.

Czym jest preset?

To gotowy zestaw plików odpowiadający na częste pytanie testowe, żebyś nie musiał projektować zestawu samodzielnie. Presety są pod spodem zwykłymi przepisami, a eject wypisuje ten przepis, więc możesz go od tego miejsca edytować.

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 mówi, ile zestaw będzie kosztował, zanim go zbudujesz, i wprost zaznacza, kiedy liczba jest naszą wartością zastępczą, a nie Twoim limitem.

Co znaczą kody wyjścia?

Każde zakończenie ma własny kod, wyjście czytelne dla maszyny idzie na standardowe wyjście, a nieudany przebieg nie pisze tam nic. Tabela jest zamrożonym kontraktem - zmiana znaczenia kodu wymaga podniesienia wersji głównej.

Kod Znaczenie
0 Wszystko się udało.
1 Nieoczekiwany błąd wewnątrz narzędzia.
2 Zła komenda albo flaga.
3 Przepis jest niepoprawny.
4 Format nie potrafi tego, o co poproszono.
5 Odczyt albo zapis się nie powiódł.
6 Za mało miejsca na dysku.
7 verify znalazło niezgodność.
8 Przebieg się zakończył, ale nie wszystko powstało.
130 Przerwane przez Ctrl+C.
143 Zatrzymane sygnałem - tak wygląda przekroczony czas w 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

Przebieg zatrzymany przez Ctrl+C i tak zostawia manifest i nigdy nie zostawia pliku zapisanego w połowie, więc anulowane zadanie da się posprzątać następnym.

Czy jest okno?

Tak, ten sam silnik z oknem, do testowania, które nie jest skryptowane. Nie jest okrojoną wersją: test porównuje obie powierzchnie możliwość po możliwości, a wszystko, co potrafi tylko jedna z nich, musi być zadeklarowane i uzasadnione, zamiast po cichu się rozjeżdżać.

Ekrany to pojedynczy wsad, presety, kilka wsadów naraz i informacje o programie. Okno pokazuje, ile przebieg będzie kosztował, zanim cokolwiek zapisze, melduje postęp w trakcie i da się je przerwać w połowie bez zostawiania pliku zapisanego do połowy. Nie wczytuje jeszcze przepisu z pliku - przepisy są na razie sprawą wiersza poleceń, a okno buduje swoje wsady w formularzu.