What people use it for
Five jobs that come up in almost every project that accepts files from people, and the command that does each one. Every example below runs as written.
Upload limits
Testing whether a file size limit is enforced where it says it is
A limit is three test cases, not one: just under, exactly on, and just over. Getting those by hand means computing byte counts and hoping you did not fencepost the arithmetic. Ask for the set instead:
tfg generate --preset size-boundaries --limit 1mb --spread 1B --format pdf --out ./edges
You get three real PDFs at 1048575, 1048576 and 1048577 bytes, and a manifest saying the first two
should be accepted and the third rejected for size_limit. Your test reads the
expectation instead of you writing three assertions by hand - and when the limit changes, you
change one number and rerun.
The same works without a preset when you want a single boundary set inline:
tfg generate --format png --boundary 5mb --out ./png-edges
Continuous integration
Keeping fixtures out of the repository without losing them
Large binary fixtures make a repository slow to clone and awkward to review, and nobody can tell what changed when one is replaced. A recipe is a few hundred characters of YAML that rebuilds the identical files - byte for byte, on any machine - because every file is derived from the run seed.
- 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
Every ending has its own exit code, so a pipeline can tell a bad recipe from a full disk from a verification mismatch. A failed run prints nothing on standard output, which keeps a log parser from reading an error as data.
Scale
Finding out what happens when the folder is big
Import routines, nightly jobs and directory listings behave differently at ten thousand files than at ten. Sizes drawn from a range make the set look like real traffic rather than ten thousand identical files, and the draw comes from the seed, so the set is the same tomorrow.
tfg generate --format log --size-range 1kb-8kb --count 10000 --out ./fixtures
Check what a run would cost before it writes anything, which matters when the total is measured in gigabytes:
tfg generate --format log --size-range 1kb-8kb --count 10000 --dry-run
A run larger than the free space on the disk is refused before the first byte is written, rather than filling the disk and failing halfway.
Archives
Testing an unpacker with an archive that really holds files
An empty archive with the right extension proves nothing about code that opens it and walks what is inside. Declare the contents and the archive genuinely holds them:
targets:
- id: bundle
format: zip
contains:
- format: txt
count: 200
size: 4kb
Nesting depth, entry counts and the size of what is inside are all things an import routine has opinions about, and this is how you find out what those opinions are.
Parsers and viewers
Checking that your own code reads a format the way real software does
Every format here is checked against an independent reader before it ships - a PNG is opened and its pixels compared, a DOCX is read back by separate libraries, an archive is extracted. That means a file that your parser rejects is a finding about your parser, not about the generator.
tfg generate --format docx --size 300kb --set paragraphs=120 --out ./documents
tfg generate --format xlsx --size 2mb --set rows=400 --set columns=6 --out ./sheets
The formats page lists the settings each one accepts and the smallest file each can be.
Who this is for
QA engineers, test automation, and anyone whose code has an upload form, an import routine, a parser or a storage quota behind it. It runs on a machine with no network at all, which matters in a closed corporate environment where a browser based generator is not an option.
Free and open source, GPL-3.0. Nothing to sign up for. The binaries are not signed yet, so your system will warn you the first time - the release notes say what to expect.