Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ env:
# The exact toolchain used for tests and releases. go.mod declares a
# minimum - this is the pin. Raising it can change generated bytes, so the
# byte stability guard has to be green before it moves.
GO_VERSION: "1.27.3"
GO_VERSION: "1.27.4"

jobs:
test:
Expand Down Expand Up @@ -1460,6 +1460,23 @@ jobs:
echo "TFG_MAILPARSER=$RUNNER_TEMP/mailparser/node_modules/mailparser" >> "$GITHUB_ENV"
node -e "console.log('mailparser', require(process.argv[1] + '/package.json').version)" "$RUNNER_TEMP/mailparser/node_modules/mailparser"

# OTLP is read by the OpenTelemetry Collector, which is asked to
# write every batch back byte for byte (docs/OTLP-2026-11-10.md
# section 5). The contrib distribution is the one that carries the
# file receiver and the file exporter. Its binary is taken out of
# the image the project publishes, pinned by version and by digest,
# put outside the checkout and named to the guard through
# TFG_OTELCOL. Asked for its version, so an image that did not
# arrive stops this step rather than turning the OTLP guard into a
# skip.
otelcol='otel/opentelemetry-collector-contrib:0.165.0@sha256:e40d61f10c9a617cca1ffd1be4d7e2b3a4b0f0001798ff07e58cabe82e618cea'
mkdir -p "$RUNNER_TEMP/otelcol"
docker create --name tfg-otelcol "$otelcol"
docker cp tfg-otelcol:/otelcol-contrib "$RUNNER_TEMP/otelcol/otelcol-contrib"
docker rm tfg-otelcol
echo "TFG_OTELCOL=$RUNNER_TEMP/otelcol/otelcol-contrib" >> "$GITHUB_ENV"
"$RUNNER_TEMP/otelcol/otelcol-contrib" --version

# The distribution's Pillow cannot open an AVIF - measured on this
# runner, PIL.UnidentifiedImageError on a file the structural checker
# had just called sound. Installed for the SAME interpreter the oracle
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/dev-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ concurrency:
cancel-in-progress: true

env:
GO_VERSION: "1.27.3"
GO_VERSION: "1.27.4"
# Fourteen days rather than the default ninety. These are throwaway builds
# handed to one person, and an unsigned binary should not sit for a quarter of
# a year behind a link somebody can pass on as if it were official.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/mutation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ concurrency:
cancel-in-progress: true

env:
GO_VERSION: "1.27.3"
GO_VERSION: "1.27.4"
# Built as the test job builds on Linux, so a verdict here is about the
# guards CI actually runs. Entries on files only a cgo build or only
# Windows compiles come back UNJUDGED, said out loud in the report.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ concurrency:
env:
# The same pin CI carries. A release built on a different toolchain than the
# one the byte stability guards ran under is a release nobody measured.
GO_VERSION: "1.27.3"
GO_VERSION: "1.27.4"

jobs:
check:
Expand Down
30 changes: 27 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ because it turns other people's test suites red.

### Added

- **A new format: `otlp`, OpenTelemetry traces and logs as OTLP JSON.**
`tfg generate --format otlp` writes spans or log records to an exact size,
one batch on each line as the OTLP file exporter writes them, or as one
request for an OTLP endpoint (`formatting`). Settings choose `signal`, the
batch, the start time and rate, trace depth, response codes and severities.
A file of lines comes back from the OpenTelemetry Collector byte for byte,
and none of its lines passes 1 MiB, where the Collector's file receiver
drops a line without saying so. Metrics are not written yet.

- **A file can be sent under a name no disk stores.** `send-as` in a recipe,
`--send-as` on the command line and Send as in the window take a name such
as `../climbed_out.jpg`. The file keeps an ordinary name on disk and the
Expand Down Expand Up @@ -81,6 +90,20 @@ because it turns other people's test suites red.

### Changed

- **`empty-and-minimal` now includes `otlp`.** The preset builds the smallest
file of every format this build has, so with `formats` left at `all` it
gains an `otlp` target of 1266 B, and the recipe `tfg preset eject
empty-and-minimal` prints is one target longer. A manifest from a run of
that preset records the hash of that recipe, so its `recipe_hash` differs
from one written before. Every file the preset wrote before is written as
it was. The files that come after `otlp` in the run take the next `id`,
and a run that names its `formats` is untouched.

- **`zero-head` on a log no longer promises a refusal.** A reader that takes
a log line by line fails on the first line and reads the rest, so the
manifest of such a file now says the outcome depends on the reader. The
file itself is written as before.

- **`upload-validation` is four files longer.** The files sent under a name
that leaves the upload directory are four targets more in the recipe
`tfg preset eject upload-validation` prints. A manifest from a run of that
Expand Down Expand Up @@ -223,9 +246,10 @@ because it turns other people's test suites red.

### Security

- **Built with Go 1.27.3.** It fixes a vulnerability in `crypto/x509` of the
Go standard library, which only the desktop window links. The command line
tool was not affected. Building from source now needs Go 1.27.3.
- **Built with Go 1.27.4.** It fixes vulnerabilities in `crypto/x509` and
`net/http` of the Go standard library, which only the desktop window links.
The command line tool was not affected. Building from source now needs
Go 1.27.4. No generated file changes.
- **The window is built with `golang.org/x/net` 0.62.0.** The version before
had two published vulnerabilities in the package that reads and writes HTML.
The window uses the package only to read the SVG pictures it draws. Neither
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ reached yet.

## Building it

Needs Go 1.27.3 or newer, and nothing else for the command line tool.
Needs Go 1.27.4 or newer, and nothing else for the command line tool.

```
git clone https://github.com/donislawdev/TestingFilesGenerator
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

**Testing Files Generator** is a tool for QA engineers and developers who need real
files to test against - an upload form, a parser, anything that takes a file and
has an opinion about it. You pick one of its 31 formats and the size you want,
has an opinion about it. You pick one of its 32 formats and the size you want,
and you get **exactly that**: ask for a 10 MB PDF and you get a PDF that a reader
will open, at 10 MB to the byte. Every run also leaves a manifest saying **what
your system should do with each file**, which is the part other generators leave
Expand All @@ -36,7 +36,7 @@ needs it finds out it exists.

- **Hit an exact size, to the byte** - ask for 10485761 bytes and get exactly
that, never a silently rounded file.
- **Write 31 real formats** - a generated PNG opens in an image viewer, a DOCX
- **Write 32 real formats** - a generated PNG opens in an image viewer, a DOCX
opens in Word, a ZIP extracts. Not padded zeros with an extension.
- **Say what should happen to each file** - the manifest carries an expected
outcome, so your test reads the assertion instead of you writing it out.
Expand Down Expand Up @@ -145,7 +145,7 @@ macOS on Apple silicon. uv installs it as well.
go install -tags noasm github.com/donislawdev/TestingFilesGenerator/cmd/tfg@latest
```

**From source.** Needs Go 1.27.3 or newer, and nothing else:
**From source.** Needs Go 1.27.4 or newer, and nothing else:

```
git clone https://github.com/donislawdev/TestingFilesGenerator
Expand Down Expand Up @@ -223,6 +223,7 @@ owns it, at the exact size you asked for:
| ⚙️ **Configuration** | `yaml`, `toml` |
| 🗺️ **Maps** | `geojson` (RFC 7946) |
| ✉️ **Mail** | `eml` (RFC 5322 with MIME, real files attached) |
| 📡 **Telemetry** | `otlp` (OpenTelemetry traces and logs as OTLP JSON) |
| 🗜️ **Archives** | `zip`, `targz` (`.tar.gz`) |
| 🔊 **Audio** | `wav` |
| 🎞️ **Video** | `webm`, `mp4` (both AV1, no sound) |
Expand Down Expand Up @@ -712,6 +713,7 @@ recipe. `tfg formats <id>` prints the allowed range or list for each:
| `txt`, `md`, `xml` | `encoding`, `bom` |
| `json` | `formatting` |
| `geojson` | `geometry`, `formatting`, `precision`, `altitude`, `vertices`, `holes`, `winding`, `antimeridian`, `unlocated`, `ids`, `bbox` |
| `otlp` | `signal`, `formatting`, `batch`, `start`, `rate`, `depth`, `status_mix`, `level_mix`, `trace_context` |
| `eml` | `body`, `layout`, `text_encoding`, `charset`, `headers`, `recipients`, `line_endings`, `attachments`, `attachment_format`, `attachment_size`, `filename_style`, `message_depth`, `inline`, `inline_format`, `inline_size` |
| `svg` | `width`, `height` |
| `html` | `structure`, `nesting`, `characters`, `elements`, `links`, `bom`, `formatting`, `styles`, `inline`, `inline_format`, `inline_size` |
Expand Down
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module github.com/donislawdev/TestingFilesGenerator
// which version it is. Kept honest by TestTheBuildFloorIsThePinnedToolchainAndTheReadmeSaysSo,
// which also holds the sentence in README.md against this number. See
// docs/STACK.md.
go 1.27.3
go 1.27.4

// How the line above got to the number it carries. There used to be a separate
// "toolchain" directive here saying which compiler produces our binaries, and
Expand Down Expand Up @@ -81,6 +81,12 @@ go 1.27.3
// pull request went red on it. Only the window binary links crypto/x509 (go
// list -deps, the same day). The byte stability guards were run under 1.27.3
// before the move, as they were for 1.27.2.
//
// Raised to 1.27.4 on 2026-11-11 because govulncheck reported GO-2026-6859 in
// net/http, fixed in 1.27.4, and the known vulnerabilities check of every
// pull request went red on it. Only the window binary links net/http (go list
// -deps, the same day). The byte stability guards were run under 1.27.4
// before the move.

require (
fyne.io/fyne/v2 v2.8.2
Expand Down
4 changes: 4 additions & 0 deletions internal/damage/appendjunk.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ var appendPolicy = []PolicyCase{
Why: "In UTF-16 most random bytes are valid characters, so the file can still be valid text."},
{Format: "md", When: map[string][]string{"encoding": {"utf-16be", "utf-16le"}},
Why: "In UTF-16 most random bytes are valid characters, so the document can still be valid text."},
// Measured 2026-11-11 on the OpenTelemetry Collector 0.165.0, in all three
// layouts and through both of its receivers: 200 over HTTP, and the file
// receiver read every batch (docs/OTLP-2026-11-10.md section 12.3).
{Format: "otlp", Why: "The OpenTelemetry Collector reads every batch and skips the bytes after the last one, and a parser that takes the file as one JSON document refuses them."},
}

// junkBytes is how many bytes this run appends, falling back for the reason
Expand Down
7 changes: 7 additions & 0 deletions internal/damage/truncate.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ var truncatePolicy = []PolicyCase{
{Format: "yaml", Why: "A YAML document cut short can still be valid YAML with fewer or shorter values."},
{Format: "toml", Why: "A TOML document cut short can still be valid TOML, for example when the cut falls between two lines or inside a number."},
{Format: "html", Why: "HTML readers are required to finish a page that is cut short."},
// One layout only, measured 2026-11-11 on the OpenTelemetry Collector
// 0.165.0 (docs/OTLP-2026-11-10.md section 12.3): its file receiver read
// every whole line and said nothing of the cut one, and over HTTP it
// answered 200. Cut short as one request, the same data is refused with
// 400.
{Format: "otlp", When: map[string][]string{"formatting": {"record-per-line"}},
Why: "An OTLP file cut short still holds whole lines, and a reader of lines keeps those and drops the cut one without a word."},
}

// missingShare is the share this run cuts, in percent.
Expand Down
25 changes: 22 additions & 3 deletions internal/damage/zerohead.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,34 @@ func init() {
},
},
Floor: func(v Values) int64 { return int64(headBytes(v)) },
// No policy cases: measured 2026-11-09 and again with every setting
// of every format on 2026-11-10, a zeroed head breaks the rules of
// all 31 formats (docs/DAMAGE-ETAP-2-2026-11-09.md section 2).
// A zeroed head breaks the rules of every format, measured 2026-11-09
// and again with every setting of every format on 2026-11-10
// (docs/DAMAGE-ETAP-2-2026-11-09.md section 2). The policy cases are
// the two formats read a line at a time, where the reader meets the
// damage on one line and carries on with the next.
Policy: zeroHeadPolicy,
Open: func(v Values, _ File, out io.Writer) (Stream, error) {
return &zeroHead{out: out, left: headBytes(v)}, nil
},
})
}

// zeroHeadPolicy is where a zeroed head costs a reader one line rather than
// the file.
//
// Measured 2026-11-11 on the OpenTelemetry Collector 0.165.0
// (docs/OTLP-2026-11-10.md section 12.3). Its file log receiver with a parser
// of access log lines read every entry of a log whose first eight bytes were
// zeroed and failed on the first line alone, which in a labelled log is the
// label. Its OTLP file receiver dropped the first batch of an OTLP file
// without a word and read the others, and sent the same file as a request it
// answered 400. An OTLP file laid out as one request is refused whole.
var zeroHeadPolicy = []PolicyCase{
{Format: "log", Why: "A reader that takes a log line by line fails on the first line and reads the rest."},
{Format: "otlp", When: map[string][]string{"formatting": {"record-per-line"}},
Why: "A reader of lines drops the first batch of an OTLP file without a word and keeps the others, and an endpoint sent the file as a request refuses it."},
}

// headBytes is how many bytes this run zeroes.
//
// The value has been past the declaration by the time it arrives, so a bad one
Expand Down
1 change: 1 addition & 0 deletions internal/format/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
_ "github.com/donislawdev/TestingFilesGenerator/internal/format/logfile"
_ "github.com/donislawdev/TestingFilesGenerator/internal/format/md"
_ "github.com/donislawdev/TestingFilesGenerator/internal/format/mp4"
_ "github.com/donislawdev/TestingFilesGenerator/internal/format/otlp"
_ "github.com/donislawdev/TestingFilesGenerator/internal/format/pdf"
_ "github.com/donislawdev/TestingFilesGenerator/internal/format/png"
_ "github.com/donislawdev/TestingFilesGenerator/internal/format/pptx"
Expand Down
Loading
Loading